Skip to content

Commit b202c88

Browse files
Release build 17.15.0 [ci release]
1 parent 5618a78 commit b202c88

10 files changed

Lines changed: 404 additions & 74 deletions

File tree

‎CHANGELOG.txt‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
- [Web-compat] Pass key detection messaging (#2948)
2-
- Document the hotfix release process (#3065)
1+
- Duck.ai data clearing: report the real error and always reply (#3070)

‎Sources/ContentScopeScripts/dist/duckAiDataClearing.js‎

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4034,13 +4034,38 @@
40344034
_ready = new WeakMap();
40354035

40364036
// src/features/duck-ai-data-clearing.js
4037+
function toError(error) {
4038+
return error instanceof Error ? error : new Error(String(error));
4039+
}
4040+
function requestError(request) {
4041+
try {
4042+
return request && typeof request === "object" && "error" in request ? (
4043+
/** @type {IDBRequest} */
4044+
request.error
4045+
) : null;
4046+
} catch {
4047+
return null;
4048+
}
4049+
}
40374050
var DuckAiDataClearing = class extends ContentFeature {
40384051
init() {
40394052
this.messaging.subscribe("duckAiClearData", (params) => {
4040-
void this.clearData(params);
4053+
void this.handleClearData(params);
40414054
});
40424055
this.notify("duckAiClearDataReady");
40434056
}
4057+
/**
4058+
* Always replies, even when clearing throws unexpectedly (e.g. missing settings), so the caller never waits in vain.
4059+
* @param {unknown} [params]
4060+
*/
4061+
async handleClearData(params) {
4062+
try {
4063+
await this.clearData(params);
4064+
} catch (error) {
4065+
this.log.error("Unexpected error while clearing data:", error);
4066+
this.notifyCompletionResult([{ stage: "unexpected", error: toError(error) }]);
4067+
}
4068+
}
40444069
/**
40454070
* @param {unknown} [params]
40464071
*/
@@ -4123,23 +4148,23 @@
41234148
/**
41244149
* Iterates over all configured localStorage keys and performs an operation on each.
41254150
* @param {(key: string) => void} operation - Operation to perform on each localStorage key
4126-
* @param {Error[]} errors - Array to collect any errors
4151+
* @param {ClearFailure[]} errors - Array to collect any errors
41274152
*/
41284153
withLocalStorages(operation, errors) {
41294154
const keys = this.getFeatureSetting("chatsLocalStorageKeys");
41304155
for (const key of keys) {
41314156
try {
41324157
operation(key);
41334158
} catch (error) {
4134-
errors.push(error instanceof Error ? error : new Error(String(error)));
4159+
errors.push({ stage: "localStorage", error: toError(error) });
41354160
this.log.error("Error in localStorage operation:", error);
41364161
}
41374162
}
41384163
}
41394164
/**
41404165
* Iterates over all configured IndexedDB stores and performs an operation on each.
41414166
* @param {(objectStore: IDBObjectStore, transaction: IDBTransaction, dbName: string, storeName: string) => void} operation
4142-
* @param {Error[]} errors - Array to collect any errors
4167+
* @param {ClearFailure[]} errors - Array to collect any errors
41434168
*/
41444169
async withAllIndexedDBs(operation, errors) {
41454170
const pairs = this.getFeatureSetting("chatImagesIndexDbNameObjectStoreNamePairs");
@@ -4149,22 +4174,25 @@
41494174
operation(objectStore, transaction, dbName, storeName);
41504175
});
41514176
} catch (error) {
4152-
errors.push(error instanceof Error ? error : new Error(String(error)));
4177+
errors.push({ stage: "indexedDB", error: toError(error) });
41534178
this.log.error("Error in IndexedDB operation:", error);
41544179
}
41554180
}
41564181
}
41574182
/**
41584183
* Sends the appropriate completion or failure notification based on errors.
4159-
* @param {Error[]} errors - Array of errors that occurred during operations
4184+
* `errorName` (e.g. a `DOMException` name) and `stage` let the native side report the cause without free text.
4185+
* @param {ClearFailure[]} errors - Failures that occurred during operations
41604186
*/
41614187
notifyCompletionResult(errors) {
41624188
if (errors.length === 0) {
41634189
this.notify("duckAiClearDataCompleted");
41644190
} else {
4165-
const lastError = errors[errors.length - 1];
4191+
const { stage, error } = errors[errors.length - 1];
41664192
this.notify("duckAiClearDataFailed", {
4167-
error: lastError?.message
4193+
error: error.message,
4194+
errorName: error.name,
4195+
stage
41684196
});
41694197
}
41704198
}
@@ -4216,7 +4244,7 @@
42164244
const request = window.indexedDB.open(indexDbName);
42174245
request.onerror = (event) => {
42184246
this.log.error("Error opening IndexedDB:", event);
4219-
reject(event);
4247+
reject(requestError(request) ?? new Error("Failed to open IndexedDB"));
42204248
};
42214249
request.onsuccess = (_2) => {
42224250
const db = request.result;
@@ -4241,7 +4269,11 @@
42414269
transaction.addEventListener("error", (err) => {
42424270
this.log.error("Transaction error:", err);
42434271
db.close();
4244-
reject(err);
4272+
reject(requestError(err.target) ?? transaction.error ?? new Error("IndexedDB transaction failed"));
4273+
});
4274+
transaction.addEventListener("abort", () => {
4275+
db.close();
4276+
reject(transaction.error ?? new DOMException("IndexedDB transaction aborted", "AbortError"));
42454277
});
42464278
operation(objectStore, transaction);
42474279
} catch (err) {

‎build/android/duckAiDataClearing.js‎

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4034,13 +4034,38 @@
40344034
_ready = new WeakMap();
40354035

40364036
// src/features/duck-ai-data-clearing.js
4037+
function toError(error) {
4038+
return error instanceof Error ? error : new Error(String(error));
4039+
}
4040+
function requestError(request) {
4041+
try {
4042+
return request && typeof request === "object" && "error" in request ? (
4043+
/** @type {IDBRequest} */
4044+
request.error
4045+
) : null;
4046+
} catch {
4047+
return null;
4048+
}
4049+
}
40374050
var DuckAiDataClearing = class extends ContentFeature {
40384051
init() {
40394052
this.messaging.subscribe("duckAiClearData", (params) => {
4040-
void this.clearData(params);
4053+
void this.handleClearData(params);
40414054
});
40424055
this.notify("duckAiClearDataReady");
40434056
}
4057+
/**
4058+
* Always replies, even when clearing throws unexpectedly (e.g. missing settings), so the caller never waits in vain.
4059+
* @param {unknown} [params]
4060+
*/
4061+
async handleClearData(params) {
4062+
try {
4063+
await this.clearData(params);
4064+
} catch (error) {
4065+
this.log.error("Unexpected error while clearing data:", error);
4066+
this.notifyCompletionResult([{ stage: "unexpected", error: toError(error) }]);
4067+
}
4068+
}
40444069
/**
40454070
* @param {unknown} [params]
40464071
*/
@@ -4123,23 +4148,23 @@
41234148
/**
41244149
* Iterates over all configured localStorage keys and performs an operation on each.
41254150
* @param {(key: string) => void} operation - Operation to perform on each localStorage key
4126-
* @param {Error[]} errors - Array to collect any errors
4151+
* @param {ClearFailure[]} errors - Array to collect any errors
41274152
*/
41284153
withLocalStorages(operation, errors) {
41294154
const keys = this.getFeatureSetting("chatsLocalStorageKeys");
41304155
for (const key of keys) {
41314156
try {
41324157
operation(key);
41334158
} catch (error) {
4134-
errors.push(error instanceof Error ? error : new Error(String(error)));
4159+
errors.push({ stage: "localStorage", error: toError(error) });
41354160
this.log.error("Error in localStorage operation:", error);
41364161
}
41374162
}
41384163
}
41394164
/**
41404165
* Iterates over all configured IndexedDB stores and performs an operation on each.
41414166
* @param {(objectStore: IDBObjectStore, transaction: IDBTransaction, dbName: string, storeName: string) => void} operation
4142-
* @param {Error[]} errors - Array to collect any errors
4167+
* @param {ClearFailure[]} errors - Array to collect any errors
41434168
*/
41444169
async withAllIndexedDBs(operation, errors) {
41454170
const pairs = this.getFeatureSetting("chatImagesIndexDbNameObjectStoreNamePairs");
@@ -4149,22 +4174,25 @@
41494174
operation(objectStore, transaction, dbName, storeName);
41504175
});
41514176
} catch (error) {
4152-
errors.push(error instanceof Error ? error : new Error(String(error)));
4177+
errors.push({ stage: "indexedDB", error: toError(error) });
41534178
this.log.error("Error in IndexedDB operation:", error);
41544179
}
41554180
}
41564181
}
41574182
/**
41584183
* Sends the appropriate completion or failure notification based on errors.
4159-
* @param {Error[]} errors - Array of errors that occurred during operations
4184+
* `errorName` (e.g. a `DOMException` name) and `stage` let the native side report the cause without free text.
4185+
* @param {ClearFailure[]} errors - Failures that occurred during operations
41604186
*/
41614187
notifyCompletionResult(errors) {
41624188
if (errors.length === 0) {
41634189
this.notify("duckAiClearDataCompleted");
41644190
} else {
4165-
const lastError = errors[errors.length - 1];
4191+
const { stage, error } = errors[errors.length - 1];
41664192
this.notify("duckAiClearDataFailed", {
4167-
error: lastError?.message
4193+
error: error.message,
4194+
errorName: error.name,
4195+
stage
41684196
});
41694197
}
41704198
}
@@ -4216,7 +4244,7 @@
42164244
const request = window.indexedDB.open(indexDbName);
42174245
request.onerror = (event) => {
42184246
this.log.error("Error opening IndexedDB:", event);
4219-
reject(event);
4247+
reject(requestError(request) ?? new Error("Failed to open IndexedDB"));
42204248
};
42214249
request.onsuccess = (_2) => {
42224250
const db = request.result;
@@ -4241,7 +4269,11 @@
42414269
transaction.addEventListener("error", (err) => {
42424270
this.log.error("Transaction error:", err);
42434271
db.close();
4244-
reject(err);
4272+
reject(requestError(err.target) ?? transaction.error ?? new Error("IndexedDB transaction failed"));
4273+
});
4274+
transaction.addEventListener("abort", () => {
4275+
db.close();
4276+
reject(transaction.error ?? new DOMException("IndexedDB transaction aborted", "AbortError"));
42454277
});
42464278
operation(objectStore, transaction);
42474279
} catch (err) {

‎build/integration/contentScope.js‎

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15622,13 +15622,38 @@ ul.messages {
1562215622

1562315623
// src/features/duck-ai-data-clearing.js
1562415624
init_define_import_meta_trackerLookup();
15625+
function toError(error) {
15626+
return error instanceof Error ? error : new Error(String(error));
15627+
}
15628+
function requestError(request) {
15629+
try {
15630+
return request && typeof request === "object" && "error" in request ? (
15631+
/** @type {IDBRequest} */
15632+
request.error
15633+
) : null;
15634+
} catch {
15635+
return null;
15636+
}
15637+
}
1562515638
var DuckAiDataClearing = class extends ContentFeature {
1562615639
init() {
1562715640
this.messaging.subscribe("duckAiClearData", (params) => {
15628-
void this.clearData(params);
15641+
void this.handleClearData(params);
1562915642
});
1563015643
this.notify("duckAiClearDataReady");
1563115644
}
15645+
/**
15646+
* Always replies, even when clearing throws unexpectedly (e.g. missing settings), so the caller never waits in vain.
15647+
* @param {unknown} [params]
15648+
*/
15649+
async handleClearData(params) {
15650+
try {
15651+
await this.clearData(params);
15652+
} catch (error) {
15653+
this.log.error("Unexpected error while clearing data:", error);
15654+
this.notifyCompletionResult([{ stage: "unexpected", error: toError(error) }]);
15655+
}
15656+
}
1563215657
/**
1563315658
* @param {unknown} [params]
1563415659
*/
@@ -15711,23 +15736,23 @@ ul.messages {
1571115736
/**
1571215737
* Iterates over all configured localStorage keys and performs an operation on each.
1571315738
* @param {(key: string) => void} operation - Operation to perform on each localStorage key
15714-
* @param {Error[]} errors - Array to collect any errors
15739+
* @param {ClearFailure[]} errors - Array to collect any errors
1571515740
*/
1571615741
withLocalStorages(operation, errors) {
1571715742
const keys = this.getFeatureSetting("chatsLocalStorageKeys");
1571815743
for (const key of keys) {
1571915744
try {
1572015745
operation(key);
1572115746
} catch (error) {
15722-
errors.push(error instanceof Error ? error : new Error(String(error)));
15747+
errors.push({ stage: "localStorage", error: toError(error) });
1572315748
this.log.error("Error in localStorage operation:", error);
1572415749
}
1572515750
}
1572615751
}
1572715752
/**
1572815753
* Iterates over all configured IndexedDB stores and performs an operation on each.
1572915754
* @param {(objectStore: IDBObjectStore, transaction: IDBTransaction, dbName: string, storeName: string) => void} operation
15730-
* @param {Error[]} errors - Array to collect any errors
15755+
* @param {ClearFailure[]} errors - Array to collect any errors
1573115756
*/
1573215757
async withAllIndexedDBs(operation, errors) {
1573315758
const pairs = this.getFeatureSetting("chatImagesIndexDbNameObjectStoreNamePairs");
@@ -15737,22 +15762,25 @@ ul.messages {
1573715762
operation(objectStore, transaction, dbName, storeName);
1573815763
});
1573915764
} catch (error) {
15740-
errors.push(error instanceof Error ? error : new Error(String(error)));
15765+
errors.push({ stage: "indexedDB", error: toError(error) });
1574115766
this.log.error("Error in IndexedDB operation:", error);
1574215767
}
1574315768
}
1574415769
}
1574515770
/**
1574615771
* Sends the appropriate completion or failure notification based on errors.
15747-
* @param {Error[]} errors - Array of errors that occurred during operations
15772+
* `errorName` (e.g. a `DOMException` name) and `stage` let the native side report the cause without free text.
15773+
* @param {ClearFailure[]} errors - Failures that occurred during operations
1574815774
*/
1574915775
notifyCompletionResult(errors) {
1575015776
if (errors.length === 0) {
1575115777
this.notify("duckAiClearDataCompleted");
1575215778
} else {
15753-
const lastError = errors[errors.length - 1];
15779+
const { stage, error } = errors[errors.length - 1];
1575415780
this.notify("duckAiClearDataFailed", {
15755-
error: lastError?.message
15781+
error: error.message,
15782+
errorName: error.name,
15783+
stage
1575615784
});
1575715785
}
1575815786
}
@@ -15804,7 +15832,7 @@ ul.messages {
1580415832
const request = window.indexedDB.open(indexDbName);
1580515833
request.onerror = (event) => {
1580615834
this.log.error("Error opening IndexedDB:", event);
15807-
reject(event);
15835+
reject(requestError(request) ?? new Error("Failed to open IndexedDB"));
1580815836
};
1580915837
request.onsuccess = (_2) => {
1581015838
const db = request.result;
@@ -15829,7 +15857,11 @@ ul.messages {
1582915857
transaction.addEventListener("error", (err) => {
1583015858
this.log.error("Transaction error:", err);
1583115859
db.close();
15832-
reject(err);
15860+
reject(requestError(err.target) ?? transaction.error ?? new Error("IndexedDB transaction failed"));
15861+
});
15862+
transaction.addEventListener("abort", () => {
15863+
db.close();
15864+
reject(transaction.error ?? new DOMException("IndexedDB transaction aborted", "AbortError"));
1583315865
});
1583415866
operation(objectStore, transaction);
1583515867
} catch (err) {

0 commit comments

Comments
 (0)