@anuma/sdk 7.1.0 → 7.1.1
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/README.md +10 -0
- package/dist/react/index.cjs +37 -16
- package/dist/react/index.d.mts +3 -0
- package/dist/react/index.d.ts +3 -0
- package/dist/react/index.mjs +37 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,6 +74,16 @@ The SDK provides entry points for different platforms:
|
|
|
74
74
|
- `@anuma/sdk/expo` — React Native / Expo
|
|
75
75
|
- `@anuma/sdk/client` — Generated API client and types
|
|
76
76
|
|
|
77
|
+
### iCloud backups
|
|
78
|
+
|
|
79
|
+
Add a `backupRevision` field with type `String` to the `ConversationBackup`
|
|
80
|
+
record type in the development schema.
|
|
81
|
+
[Deploy the schema to production](https://developer.apple.com/documentation/cloudkit/deploying-an-icloud-container-s-schema)
|
|
82
|
+
before you release a client that uses iCloud backup.
|
|
83
|
+
|
|
84
|
+
The SDK compares this saved revision with the local conversation and message
|
|
85
|
+
data. The next backup replaces older files that have no saved revision.
|
|
86
|
+
|
|
77
87
|
## Features
|
|
78
88
|
|
|
79
89
|
The SDK gives you access to a unified API across multiple LLM providers through
|
package/dist/react/index.cjs
CHANGED
|
@@ -30968,7 +30968,7 @@ async function requestICloudSignIn() {
|
|
|
30968
30968
|
}
|
|
30969
30969
|
return container.whenUserSignsIn();
|
|
30970
30970
|
}
|
|
30971
|
-
async function uploadFileToICloud(filename, content) {
|
|
30971
|
+
async function uploadFileToICloud(filename, content, backupRevision) {
|
|
30972
30972
|
const container = await getContainer();
|
|
30973
30973
|
const database = container.privateCloudDatabase;
|
|
30974
30974
|
const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
@@ -30981,7 +30981,8 @@ async function uploadFileToICloud(filename, content) {
|
|
|
30981
30981
|
filename: { value: filename },
|
|
30982
30982
|
data: { value: base64Data },
|
|
30983
30983
|
size: { value: content.size },
|
|
30984
|
-
contentType: { value: content.type || "application/json" }
|
|
30984
|
+
contentType: { value: content.type || "application/json" },
|
|
30985
|
+
...backupRevision ? { backupRevision: { value: backupRevision } } : {}
|
|
30985
30986
|
}
|
|
30986
30987
|
};
|
|
30987
30988
|
const response = await database.saveRecords(record2);
|
|
@@ -30993,7 +30994,8 @@ async function uploadFileToICloud(filename, content) {
|
|
|
30993
30994
|
recordName: savedRecord.recordName,
|
|
30994
30995
|
filename,
|
|
30995
30996
|
modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
|
|
30996
|
-
size: content.size
|
|
30997
|
+
size: content.size,
|
|
30998
|
+
backupRevision: savedRecord.fields.backupRevision?.value
|
|
30997
30999
|
};
|
|
30998
31000
|
}
|
|
30999
31001
|
async function listICloudFiles() {
|
|
@@ -31014,6 +31016,7 @@ async function listICloudFiles() {
|
|
|
31014
31016
|
recordName: record2.recordName,
|
|
31015
31017
|
filename: record2.fields.filename?.value ?? "",
|
|
31016
31018
|
modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
|
|
31019
|
+
backupRevision: record2.fields.backupRevision?.value,
|
|
31017
31020
|
size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
|
|
31018
31021
|
}));
|
|
31019
31022
|
return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
@@ -31067,35 +31070,53 @@ async function findICloudFile(filename) {
|
|
|
31067
31070
|
recordName: record2.recordName,
|
|
31068
31071
|
filename: record2.fields.filename?.value ?? "",
|
|
31069
31072
|
modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
|
|
31073
|
+
backupRevision: record2.fields.backupRevision?.value,
|
|
31070
31074
|
size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
|
|
31071
31075
|
};
|
|
31072
31076
|
}
|
|
31073
31077
|
|
|
31074
31078
|
// src/lib/backup/icloud/backup.ts
|
|
31075
31079
|
var isAuthError = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
|
|
31080
|
+
var UNREVISIONED_COLUMNS = /* @__PURE__ */ new Set([
|
|
31081
|
+
"updated_at",
|
|
31082
|
+
"vector",
|
|
31083
|
+
"embedding_model",
|
|
31084
|
+
"chunks",
|
|
31085
|
+
"origin"
|
|
31086
|
+
]);
|
|
31087
|
+
async function getBackupRevision(database, conversationId) {
|
|
31088
|
+
const serialized = await database.read(async () => {
|
|
31089
|
+
const { Q: Q20 } = await import("@nozbe/watermelondb");
|
|
31090
|
+
const tables = await Promise.all(
|
|
31091
|
+
["conversations", "history"].map(
|
|
31092
|
+
(table) => database.get(table).query(Q20.where("conversation_id", conversationId), Q20.sortBy("id", Q20.asc)).unsafeFetchRaw()
|
|
31093
|
+
)
|
|
31094
|
+
);
|
|
31095
|
+
return JSON.stringify(
|
|
31096
|
+
tables.map(
|
|
31097
|
+
(rows) => rows.map(
|
|
31098
|
+
(row) => Object.entries(row).filter(([key]) => !key.startsWith("_") && !UNREVISIONED_COLUMNS.has(key)).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
|
|
31099
|
+
)
|
|
31100
|
+
)
|
|
31101
|
+
);
|
|
31102
|
+
});
|
|
31103
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(serialized));
|
|
31104
|
+
return "v1:" + Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
31105
|
+
}
|
|
31076
31106
|
async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
|
|
31077
31107
|
try {
|
|
31078
31108
|
await deps.requestEncryptionKey(userAddress);
|
|
31079
31109
|
const filename = `${conversationId}.json`;
|
|
31080
31110
|
const existingFile = await findICloudFile(filename);
|
|
31081
|
-
|
|
31082
|
-
|
|
31083
|
-
|
|
31084
|
-
const records = await conversationsCollection.query(Q20.where("conversation_id", conversationId)).fetch();
|
|
31085
|
-
if (records.length > 0) {
|
|
31086
|
-
const conversation = conversationToStoredRaw(records[0]);
|
|
31087
|
-
const localUpdated = conversation.updatedAt.getTime();
|
|
31088
|
-
const remoteModified = existingFile.modifiedAt.getTime();
|
|
31089
|
-
if (localUpdated <= remoteModified) {
|
|
31090
|
-
return "skipped";
|
|
31091
|
-
}
|
|
31092
|
-
}
|
|
31111
|
+
const backupRevision = await getBackupRevision(database, conversationId);
|
|
31112
|
+
if (existingFile?.backupRevision === backupRevision) {
|
|
31113
|
+
return "skipped";
|
|
31093
31114
|
}
|
|
31094
31115
|
const exportResult = await deps.exportConversation(conversationId, userAddress);
|
|
31095
31116
|
if (!exportResult.success || !exportResult.blob) {
|
|
31096
31117
|
return "failed";
|
|
31097
31118
|
}
|
|
31098
|
-
await uploadFileToICloud(filename, exportResult.blob);
|
|
31119
|
+
await uploadFileToICloud(filename, exportResult.blob, backupRevision);
|
|
31099
31120
|
return "uploaded";
|
|
31100
31121
|
} catch (err) {
|
|
31101
31122
|
if (isAuthError(err) && !_retried) {
|
package/dist/react/index.d.mts
CHANGED
package/dist/react/index.d.ts
CHANGED
package/dist/react/index.mjs
CHANGED
|
@@ -29112,7 +29112,7 @@ async function requestICloudSignIn() {
|
|
|
29112
29112
|
}
|
|
29113
29113
|
return container.whenUserSignsIn();
|
|
29114
29114
|
}
|
|
29115
|
-
async function uploadFileToICloud(filename, content) {
|
|
29115
|
+
async function uploadFileToICloud(filename, content, backupRevision) {
|
|
29116
29116
|
const container = await getContainer();
|
|
29117
29117
|
const database = container.privateCloudDatabase;
|
|
29118
29118
|
const recordName = `backup_${filename.replace(/[^a-zA-Z0-9]/g, "_")}`;
|
|
@@ -29125,7 +29125,8 @@ async function uploadFileToICloud(filename, content) {
|
|
|
29125
29125
|
filename: { value: filename },
|
|
29126
29126
|
data: { value: base64Data },
|
|
29127
29127
|
size: { value: content.size },
|
|
29128
|
-
contentType: { value: content.type || "application/json" }
|
|
29128
|
+
contentType: { value: content.type || "application/json" },
|
|
29129
|
+
...backupRevision ? { backupRevision: { value: backupRevision } } : {}
|
|
29129
29130
|
}
|
|
29130
29131
|
};
|
|
29131
29132
|
const response = await database.saveRecords(record2);
|
|
@@ -29137,7 +29138,8 @@ async function uploadFileToICloud(filename, content) {
|
|
|
29137
29138
|
recordName: savedRecord.recordName,
|
|
29138
29139
|
filename,
|
|
29139
29140
|
modifiedAt: new Date(savedRecord.modified?.timestamp ?? Date.now()),
|
|
29140
|
-
size: content.size
|
|
29141
|
+
size: content.size,
|
|
29142
|
+
backupRevision: savedRecord.fields.backupRevision?.value
|
|
29141
29143
|
};
|
|
29142
29144
|
}
|
|
29143
29145
|
async function listICloudFiles() {
|
|
@@ -29158,6 +29160,7 @@ async function listICloudFiles() {
|
|
|
29158
29160
|
recordName: record2.recordName,
|
|
29159
29161
|
filename: record2.fields.filename?.value ?? "",
|
|
29160
29162
|
modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
|
|
29163
|
+
backupRevision: record2.fields.backupRevision?.value,
|
|
29161
29164
|
size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
|
|
29162
29165
|
}));
|
|
29163
29166
|
return files.sort((a, b) => b.modifiedAt.getTime() - a.modifiedAt.getTime());
|
|
@@ -29211,35 +29214,53 @@ async function findICloudFile(filename) {
|
|
|
29211
29214
|
recordName: record2.recordName,
|
|
29212
29215
|
filename: record2.fields.filename?.value ?? "",
|
|
29213
29216
|
modifiedAt: new Date(record2.modified?.timestamp ?? Date.now()),
|
|
29217
|
+
backupRevision: record2.fields.backupRevision?.value,
|
|
29214
29218
|
size: typeof record2.fields.data?.value === "object" && record2.fields.data?.value !== null ? record2.fields.data.value.size : 0
|
|
29215
29219
|
};
|
|
29216
29220
|
}
|
|
29217
29221
|
|
|
29218
29222
|
// src/lib/backup/icloud/backup.ts
|
|
29219
29223
|
var isAuthError = (err) => err instanceof Error && (err.message.includes("AUTHENTICATION") || err.message.includes("NOT_AUTHENTICATED") || err.message.includes("sign in"));
|
|
29224
|
+
var UNREVISIONED_COLUMNS = /* @__PURE__ */ new Set([
|
|
29225
|
+
"updated_at",
|
|
29226
|
+
"vector",
|
|
29227
|
+
"embedding_model",
|
|
29228
|
+
"chunks",
|
|
29229
|
+
"origin"
|
|
29230
|
+
]);
|
|
29231
|
+
async function getBackupRevision(database, conversationId) {
|
|
29232
|
+
const serialized = await database.read(async () => {
|
|
29233
|
+
const { Q: Q20 } = await import("@nozbe/watermelondb");
|
|
29234
|
+
const tables = await Promise.all(
|
|
29235
|
+
["conversations", "history"].map(
|
|
29236
|
+
(table) => database.get(table).query(Q20.where("conversation_id", conversationId), Q20.sortBy("id", Q20.asc)).unsafeFetchRaw()
|
|
29237
|
+
)
|
|
29238
|
+
);
|
|
29239
|
+
return JSON.stringify(
|
|
29240
|
+
tables.map(
|
|
29241
|
+
(rows) => rows.map(
|
|
29242
|
+
(row) => Object.entries(row).filter(([key]) => !key.startsWith("_") && !UNREVISIONED_COLUMNS.has(key)).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0)
|
|
29243
|
+
)
|
|
29244
|
+
)
|
|
29245
|
+
);
|
|
29246
|
+
});
|
|
29247
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(serialized));
|
|
29248
|
+
return "v1:" + Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
29249
|
+
}
|
|
29220
29250
|
async function pushConversationToICloud(database, conversationId, userAddress, deps, _retried = false) {
|
|
29221
29251
|
try {
|
|
29222
29252
|
await deps.requestEncryptionKey(userAddress);
|
|
29223
29253
|
const filename = `${conversationId}.json`;
|
|
29224
29254
|
const existingFile = await findICloudFile(filename);
|
|
29225
|
-
|
|
29226
|
-
|
|
29227
|
-
|
|
29228
|
-
const records = await conversationsCollection.query(Q20.where("conversation_id", conversationId)).fetch();
|
|
29229
|
-
if (records.length > 0) {
|
|
29230
|
-
const conversation = conversationToStoredRaw(records[0]);
|
|
29231
|
-
const localUpdated = conversation.updatedAt.getTime();
|
|
29232
|
-
const remoteModified = existingFile.modifiedAt.getTime();
|
|
29233
|
-
if (localUpdated <= remoteModified) {
|
|
29234
|
-
return "skipped";
|
|
29235
|
-
}
|
|
29236
|
-
}
|
|
29255
|
+
const backupRevision = await getBackupRevision(database, conversationId);
|
|
29256
|
+
if (existingFile?.backupRevision === backupRevision) {
|
|
29257
|
+
return "skipped";
|
|
29237
29258
|
}
|
|
29238
29259
|
const exportResult = await deps.exportConversation(conversationId, userAddress);
|
|
29239
29260
|
if (!exportResult.success || !exportResult.blob) {
|
|
29240
29261
|
return "failed";
|
|
29241
29262
|
}
|
|
29242
|
-
await uploadFileToICloud(filename, exportResult.blob);
|
|
29263
|
+
await uploadFileToICloud(filename, exportResult.blob, backupRevision);
|
|
29243
29264
|
return "uploaded";
|
|
29244
29265
|
} catch (err) {
|
|
29245
29266
|
if (isAuthError(err) && !_retried) {
|