@addon-core/inject-css 0.3.1 → 0.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/index.cjs CHANGED
@@ -20,7 +20,19 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
- default: () => index_default
23
+ InjectCssBaseError: () => InjectCssBaseError,
24
+ InjectCssDeliveryError: () => InjectCssDeliveryError,
25
+ InjectCssFrameDeliveryError: () => InjectCssFrameDeliveryError,
26
+ InjectCssTimeoutError: () => InjectCssTimeoutError,
27
+ InvalidInjectCssCodeError: () => InvalidInjectCssCodeError,
28
+ InvalidInjectCssFilesError: () => InvalidInjectCssFilesError,
29
+ InvalidInjectCssOptionsError: () => InvalidInjectCssOptionsError,
30
+ InvalidInjectCssTargetError: () => InvalidInjectCssTargetError,
31
+ UnsupportedInjectCssOperationError: () => UnsupportedInjectCssOperationError,
32
+ UnsupportedInjectCssOptionError: () => UnsupportedInjectCssOptionError,
33
+ UnsupportedInjectCssTargetError: () => UnsupportedInjectCssTargetError,
34
+ default: () => index_default,
35
+ injectCss: () => injectCss
24
36
  });
25
37
  module.exports = __toCommonJS(index_exports);
26
38
  var import_browser3 = require("@addon-core/browser");
@@ -28,120 +40,587 @@ var import_browser3 = require("@addon-core/browser");
28
40
  // src/InjectCssV2.ts
29
41
  var import_browser = require("@addon-core/browser");
30
42
 
43
+ // src/errors.ts
44
+ var InjectCssBaseError = class extends Error {
45
+ code;
46
+ cause;
47
+ constructor(name, code, message, cause) {
48
+ super(message);
49
+ this.name = name;
50
+ this.code = code;
51
+ if (cause !== void 0) {
52
+ this.cause = cause;
53
+ }
54
+ }
55
+ };
56
+ var InvalidInjectCssTargetError = class extends InjectCssBaseError {
57
+ constructor(message) {
58
+ super("InvalidInjectCssTargetError", "ERR_INJECT_CSS_INVALID_TARGET", `Invalid InjectCss target: ${message}`);
59
+ }
60
+ };
61
+ var UnsupportedInjectCssTargetError = class extends InjectCssBaseError {
62
+ constructor(message, cause) {
63
+ super(
64
+ "UnsupportedInjectCssTargetError",
65
+ "ERR_INJECT_CSS_UNSUPPORTED_TARGET",
66
+ `Unsupported InjectCss target: ${message}`,
67
+ cause
68
+ );
69
+ }
70
+ };
71
+ var InvalidInjectCssOptionsError = class extends InjectCssBaseError {
72
+ constructor(message) {
73
+ super(
74
+ "InvalidInjectCssOptionsError",
75
+ "ERR_INJECT_CSS_INVALID_OPTIONS",
76
+ `Invalid InjectCss options: ${message}`
77
+ );
78
+ }
79
+ };
80
+ var UnsupportedInjectCssOptionError = class extends InjectCssBaseError {
81
+ constructor(message, cause) {
82
+ super(
83
+ "UnsupportedInjectCssOptionError",
84
+ "ERR_INJECT_CSS_UNSUPPORTED_OPTION",
85
+ `Unsupported InjectCss option: ${message}`,
86
+ cause
87
+ );
88
+ }
89
+ };
90
+ var UnsupportedInjectCssOperationError = class extends InjectCssBaseError {
91
+ operation;
92
+ constructor(operation, message, cause) {
93
+ super(
94
+ "UnsupportedInjectCssOperationError",
95
+ "ERR_INJECT_CSS_UNSUPPORTED_OPERATION",
96
+ `Unsupported InjectCss operation "${operation}": ${message}`,
97
+ cause
98
+ );
99
+ this.operation = operation;
100
+ }
101
+ };
102
+ var InvalidInjectCssCodeError = class extends InjectCssBaseError {
103
+ constructor(message) {
104
+ super("InvalidInjectCssCodeError", "ERR_INJECT_CSS_INVALID_CODE", `Invalid InjectCss code: ${message}`);
105
+ }
106
+ };
107
+ var InvalidInjectCssFilesError = class extends InjectCssBaseError {
108
+ constructor(message) {
109
+ super("InvalidInjectCssFilesError", "ERR_INJECT_CSS_INVALID_FILES", `Invalid InjectCss files: ${message}`);
110
+ }
111
+ };
112
+ var InjectCssTimeoutError = class extends InjectCssBaseError {
113
+ target;
114
+ timeoutMs;
115
+ operation;
116
+ constructor(target, timeoutMs, operation = "insert") {
117
+ const action = operation === "insert" ? "injection" : "removal";
118
+ super("InjectCssTimeoutError", "ERR_INJECT_CSS_TIMEOUT", `CSS ${action} timed out after ${timeoutMs} ms.`);
119
+ this.target = target;
120
+ this.timeoutMs = timeoutMs;
121
+ this.operation = operation;
122
+ }
123
+ };
124
+ var InjectCssFrameDeliveryError = class extends InjectCssBaseError {
125
+ tabId;
126
+ frameId;
127
+ operation;
128
+ constructor(tabId, frameId, cause, operation = "insert") {
129
+ const message = cause instanceof Error ? cause.message : String(cause);
130
+ const action = operation === "insert" ? "injection" : "removal";
131
+ super(
132
+ "InjectCssFrameDeliveryError",
133
+ "ERR_INJECT_CSS_FRAME_DELIVERY",
134
+ `CSS ${action} failed in frame ${frameId} of tab ${tabId}: ${message}`,
135
+ cause
136
+ );
137
+ this.tabId = tabId;
138
+ this.frameId = frameId;
139
+ this.operation = operation;
140
+ }
141
+ };
142
+ var InjectCssDeliveryError = class extends InjectCssBaseError {
143
+ target;
144
+ operation;
145
+ constructor(target, cause, operation = "insert") {
146
+ const causeMessage = cause instanceof Error ? cause.message : String(cause);
147
+ const action = operation === "insert" ? "injection" : "removal";
148
+ const message = cause instanceof InjectCssFrameDeliveryError ? causeMessage : `CSS ${action} failed: ${causeMessage}`;
149
+ super("InjectCssDeliveryError", "ERR_INJECT_CSS_DELIVERY", message, cause);
150
+ this.target = target;
151
+ this.operation = operation;
152
+ }
153
+ };
154
+
155
+ // src/validation.ts
156
+ var TARGET_KEYS = /* @__PURE__ */ new Set(["tabId", "allFrames", "frameIds", "documentIds"]);
157
+ var EXECUTION_OPTION_KEYS = /* @__PURE__ */ new Set(["matchAboutBlank", "runAt", "origin", "timeoutMs"]);
158
+ var INJECT_CSS_OPTION_KEYS = /* @__PURE__ */ new Set(["target", ...EXECUTION_OPTION_KEYS]);
159
+ var RUN_AT_VALUES = /* @__PURE__ */ new Set(["document_start", "document_end", "document_idle"]);
160
+ var ORIGIN_VALUES = /* @__PURE__ */ new Set(["AUTHOR", "USER"]);
161
+ var isObject = (value) => {
162
+ return typeof value === "object" && value !== null && !Array.isArray(value);
163
+ };
164
+ var assertKnownKeys = (value, keys, subject) => {
165
+ const unknownKeys = Object.keys(value).filter((key) => !keys.has(key));
166
+ if (unknownKeys.length > 0) {
167
+ throw new InvalidInjectCssOptionsError(
168
+ `${subject} contains unknown ${unknownKeys.length === 1 ? "field" : "fields"}: ${unknownKeys.map((key) => `"${key}"`).join(", ")}.`
169
+ );
170
+ }
171
+ };
172
+ var cloneTarget = (target) => {
173
+ if ("frameIds" in target && target.frameIds !== void 0) {
174
+ return { tabId: target.tabId, frameIds: [...target.frameIds] };
175
+ }
176
+ if ("documentIds" in target && target.documentIds !== void 0) {
177
+ return { tabId: target.tabId, documentIds: [...target.documentIds] };
178
+ }
179
+ if ("allFrames" in target && target.allFrames === true) {
180
+ return { tabId: target.tabId, allFrames: true };
181
+ }
182
+ return { tabId: target.tabId };
183
+ };
184
+ var validateInjectCssTarget = (value) => {
185
+ if (!isObject(value)) {
186
+ throw new InvalidInjectCssTargetError("target must be an object.");
187
+ }
188
+ const unknownKeys = Object.keys(value).filter((key) => !TARGET_KEYS.has(key));
189
+ if (unknownKeys.length > 0) {
190
+ throw new InvalidInjectCssTargetError(
191
+ `target contains unknown ${unknownKeys.length === 1 ? "field" : "fields"}: ${unknownKeys.map((key) => `"${key}"`).join(", ")}.`
192
+ );
193
+ }
194
+ if (!Number.isInteger(value.tabId) || value.tabId < 0) {
195
+ throw new InvalidInjectCssTargetError('"tabId" must be a non-negative integer.');
196
+ }
197
+ const selectors = ["allFrames", "frameIds", "documentIds"].filter((key) => value[key] !== void 0);
198
+ if (selectors.length > 1) {
199
+ throw new InvalidInjectCssTargetError('"allFrames", "frameIds", and "documentIds" are mutually exclusive.');
200
+ }
201
+ if (value.allFrames !== void 0 && value.allFrames !== true) {
202
+ throw new InvalidInjectCssTargetError('"allFrames" must be exactly true when provided.');
203
+ }
204
+ if (value.frameIds !== void 0) {
205
+ if (!Array.isArray(value.frameIds) || value.frameIds.length === 0) {
206
+ throw new InvalidInjectCssTargetError('"frameIds" must contain at least one frame ID.');
207
+ }
208
+ if (value.frameIds.some((frameId) => !Number.isInteger(frameId) || frameId < 0)) {
209
+ throw new InvalidInjectCssTargetError("frame ID must be a non-negative integer.");
210
+ }
211
+ if (new Set(value.frameIds).size !== value.frameIds.length) {
212
+ throw new InvalidInjectCssTargetError('"frameIds" must not contain duplicate frame IDs.');
213
+ }
214
+ }
215
+ if (value.documentIds !== void 0) {
216
+ if (!Array.isArray(value.documentIds) || value.documentIds.length === 0) {
217
+ throw new InvalidInjectCssTargetError('"documentIds" must contain at least one document ID.');
218
+ }
219
+ if (value.documentIds.some((documentId) => typeof documentId !== "string" || documentId.trim().length === 0)) {
220
+ throw new InvalidInjectCssTargetError("document ID must be a non-empty string.");
221
+ }
222
+ if (new Set(value.documentIds).size !== value.documentIds.length) {
223
+ throw new InvalidInjectCssTargetError('"documentIds" must not contain duplicate document IDs.');
224
+ }
225
+ }
226
+ return cloneTarget(value);
227
+ };
228
+ var validateInjectCssExecutionOptions = (value) => {
229
+ if (!isObject(value)) {
230
+ throw new InvalidInjectCssOptionsError("execution options must be an object.");
231
+ }
232
+ assertKnownKeys(value, EXECUTION_OPTION_KEYS, "execution options");
233
+ if (value.matchAboutBlank !== void 0 && typeof value.matchAboutBlank !== "boolean") {
234
+ throw new InvalidInjectCssOptionsError('"matchAboutBlank" must be a boolean.');
235
+ }
236
+ if (value.runAt !== void 0 && (typeof value.runAt !== "string" || !RUN_AT_VALUES.has(value.runAt))) {
237
+ throw new InvalidInjectCssOptionsError('"runAt" must be "document_start", "document_end", or "document_idle".');
238
+ }
239
+ if (value.origin !== void 0 && (typeof value.origin !== "string" || !ORIGIN_VALUES.has(value.origin))) {
240
+ throw new InvalidInjectCssOptionsError('"origin" must be "AUTHOR" or "USER".');
241
+ }
242
+ if (value.timeoutMs !== void 0 && (typeof value.timeoutMs !== "number" || !Number.isInteger(value.timeoutMs) || value.timeoutMs <= 0)) {
243
+ throw new InvalidInjectCssOptionsError('"timeoutMs" must be a positive integer.');
244
+ }
245
+ return { ...value };
246
+ };
247
+ var validateInjectCssOptions = (value) => {
248
+ if (!isObject(value)) {
249
+ throw new InvalidInjectCssOptionsError("options must be an object.");
250
+ }
251
+ assertKnownKeys(value, INJECT_CSS_OPTION_KEYS, "options");
252
+ const { target, ...execution } = value;
253
+ return {
254
+ target: validateInjectCssTarget(target),
255
+ execution: validateInjectCssExecutionOptions(execution)
256
+ };
257
+ };
258
+ var validateInjectCssCode = (value) => {
259
+ if (typeof value !== "string" || value.trim().length === 0) {
260
+ throw new InvalidInjectCssCodeError("code must be a non-empty string.");
261
+ }
262
+ return value;
263
+ };
264
+ var validateInjectCssFiles = (files) => {
265
+ const fileList = typeof files === "string" ? [files] : files;
266
+ if (!Array.isArray(fileList) || fileList.length === 0) {
267
+ throw new InvalidInjectCssFilesError("at least one file is required.");
268
+ }
269
+ if (fileList.some((file) => typeof file !== "string" || file.trim().length === 0)) {
270
+ throw new InvalidInjectCssFilesError("each file must be a non-empty string.");
271
+ }
272
+ return [...fileList];
273
+ };
274
+
31
275
  // src/AbstractInjectCss.ts
276
+ var DEFAULT_TIMEOUT_MS = 4e3;
32
277
  var AbstractInjectCss_default = class {
33
- constructor(_options) {
34
- this._options = _options;
278
+ _target;
279
+ _execution;
280
+ constructor(options) {
281
+ const normalized = validateInjectCssOptions(options);
282
+ this._target = normalized.target;
283
+ this._execution = normalized.execution;
284
+ }
285
+ target(target) {
286
+ const normalizedTarget = validateInjectCssTarget(target);
287
+ this.assertAdapterSupport(normalizedTarget, this._execution);
288
+ this._target = normalizedTarget;
289
+ return this;
35
290
  }
36
291
  options(options) {
37
- this._options = { ...this._options, ...options, tabId: options.tabId ?? this._options.tabId };
292
+ const normalizedOptions = validateInjectCssExecutionOptions(options);
293
+ const nextExecution = { ...this._execution, ...normalizedOptions };
294
+ this.assertAdapterSupport(this._target, nextExecution);
295
+ this._execution = nextExecution;
38
296
  return this;
39
297
  }
40
- get frameIds() {
41
- const { frameId } = this._options;
42
- return typeof frameId === "number" ? [frameId] : typeof frameId !== "boolean" ? frameId : void 0;
298
+ validateCode(css) {
299
+ return validateInjectCssCode(css);
300
+ }
301
+ normalizeFiles(files) {
302
+ return validateInjectCssFiles(files);
303
+ }
304
+ snapshotTarget() {
305
+ return validateInjectCssTarget(this._target);
43
306
  }
44
- get allFrames() {
45
- const { frameId } = this._options;
46
- return typeof frameId === "boolean" ? frameId : void 0;
307
+ snapshotExecution() {
308
+ return { ...this._execution };
47
309
  }
48
- get matchAboutBlank() {
49
- const { matchAboutBlank } = this._options;
50
- return typeof matchAboutBlank === "boolean" ? matchAboutBlank : true;
310
+ get timeoutMs() {
311
+ return this._execution.timeoutMs ?? DEFAULT_TIMEOUT_MS;
312
+ }
313
+ async withTimeout(task, target, timeoutMs, operation = "insert") {
314
+ return new Promise((resolve, reject) => {
315
+ let settled = false;
316
+ const finish = (callback) => {
317
+ if (settled) return;
318
+ settled = true;
319
+ clearTimeout(timeoutId);
320
+ callback();
321
+ };
322
+ const timeoutId = setTimeout(() => {
323
+ finish(() => reject(new InjectCssTimeoutError(target, timeoutMs, operation)));
324
+ }, timeoutMs);
325
+ task.then(
326
+ (value) => finish(() => resolve(value)),
327
+ (error) => finish(() => reject(error))
328
+ );
329
+ });
330
+ }
331
+ deliveryError(target, error, operation = "insert") {
332
+ if (error instanceof InjectCssDeliveryError || error instanceof InjectCssTimeoutError) {
333
+ return error;
334
+ }
335
+ return new InjectCssDeliveryError(target, error, operation);
51
336
  }
52
337
  };
53
338
 
339
+ // src/native-errors.ts
340
+ var UNSUPPORTED_API_PATTERN = /(not supported|unsupported|not (?:available|implemented)|is not a function)\b/i;
341
+ var UNSUPPORTED_FIELD_PATTERN = /(not supported|unsupported|unexpected|unknown|unrecognized|invalid|not (?:a )?valid)\b/i;
342
+ var UNSUPPORTED_TARGET_FIELD_PATTERN = /(not supported|unsupported|unexpected|unknown|unrecognized)\b/i;
343
+ var getNativeErrorMessage = (error) => {
344
+ return error instanceof Error ? error.message : String(error);
345
+ };
346
+ var isUnsupportedRemovalCapabilityError = (error) => {
347
+ const message = getNativeErrorMessage(error);
348
+ return /remove\s*css/i.test(message) && UNSUPPORTED_API_PATTERN.test(message);
349
+ };
350
+ var isUnsupportedOriginCapabilityError = (error) => {
351
+ const message = getNativeErrorMessage(error);
352
+ return /\b(?:(?:css|style)[\s_-]*)?origin\b/i.test(message) && UNSUPPORTED_FIELD_PATTERN.test(message);
353
+ };
354
+ var isUnsupportedDocumentTargetCapabilityError = (error) => {
355
+ const message = getNativeErrorMessage(error);
356
+ return /documentIds?/i.test(message) && UNSUPPORTED_TARGET_FIELD_PATTERN.test(message);
357
+ };
358
+
54
359
  // src/InjectCssV2.ts
55
360
  var InjectCssV2_default = class extends AbstractInjectCss_default {
56
- async insert(code) {
57
- const { tabId, runAt } = this._options;
58
- const details = {
59
- code,
60
- runAt,
61
- cssOrigin: this.cssOrigin,
62
- matchAboutBlank: this.matchAboutBlank
63
- };
64
- if (this.allFrames) {
65
- await (0, import_browser.insertCssTab)(tabId, { ...details, allFrames: true });
66
- } else if (this.frameIds) {
67
- await Promise.all(this.frameIds.map((frameId) => (0, import_browser.insertCssTab)(tabId, { ...details, frameId })));
68
- } else {
69
- await (0, import_browser.insertCssTab)(tabId, details);
361
+ constructor(options) {
362
+ super(options);
363
+ this.assertAdapterSupport(this._target, this._execution);
364
+ }
365
+ async insert(css) {
366
+ const code = this.validateCode(css);
367
+ const target = this.snapshotTarget();
368
+ const execution = this.snapshotExecution();
369
+ const timeoutMs = this.timeoutMs;
370
+ const details = this.createDetails("insert", execution, { code });
371
+ try {
372
+ await this.withTimeout(this.execute("insert", target, details), target, timeoutMs);
373
+ } catch (error) {
374
+ this.throwDeliveryError("insert", target, execution, error);
70
375
  }
71
376
  }
72
377
  async file(files) {
73
- const { tabId, runAt } = this._options;
74
- const fileList = typeof files === "string" ? [files] : files;
75
- const injectTasks = [];
76
- for (const file of fileList) {
77
- const details = {
78
- file,
79
- runAt,
80
- cssOrigin: this.cssOrigin,
81
- matchAboutBlank: this.matchAboutBlank
82
- };
83
- if (this.allFrames) {
84
- injectTasks.push((0, import_browser.insertCssTab)(tabId, { ...details, allFrames: true }));
85
- } else if (this.frameIds) {
86
- injectTasks.push(...this.frameIds.map((frameId) => (0, import_browser.insertCssTab)(tabId, { ...details, frameId })));
87
- } else {
88
- injectTasks.push((0, import_browser.insertCssTab)(tabId, details));
378
+ const fileList = this.normalizeFiles(files);
379
+ await this.deliverFiles("insert", fileList);
380
+ }
381
+ async remove(css) {
382
+ const code = this.validateCode(css);
383
+ await this.removeSource({ code });
384
+ }
385
+ async removeFile(files) {
386
+ const fileList = this.normalizeFiles(files);
387
+ await this.deliverFiles("remove", fileList);
388
+ }
389
+ assertAdapterSupport(target, _execution) {
390
+ if ("documentIds" in target && target.documentIds !== void 0) {
391
+ throw new UnsupportedInjectCssTargetError('"documentIds" are not supported by the MV2 adapter.');
392
+ }
393
+ }
394
+ createDetails(operation, execution, source) {
395
+ return {
396
+ ...source,
397
+ ...operation === "insert" && execution.runAt !== void 0 ? { runAt: execution.runAt } : {},
398
+ ...execution.origin !== void 0 ? { cssOrigin: execution.origin.toLowerCase() } : {},
399
+ ...execution.matchAboutBlank !== void 0 ? { matchAboutBlank: execution.matchAboutBlank } : {}
400
+ };
401
+ }
402
+ async execute(operation, target, details) {
403
+ const deliver = operation === "insert" ? import_browser.insertCssTab : import_browser.removeCssTab;
404
+ if ("allFrames" in target && target.allFrames === true) {
405
+ await deliver(target.tabId, { ...details, allFrames: true });
406
+ return;
407
+ }
408
+ if ("frameIds" in target && target.frameIds !== void 0) {
409
+ await Promise.all(
410
+ target.frameIds.map(
411
+ (frameId) => this.executeFrame(operation, target.tabId, frameId, { ...details, frameId })
412
+ )
413
+ );
414
+ return;
415
+ }
416
+ await deliver(target.tabId, details);
417
+ }
418
+ async executeFrame(operation, tabId, frameId, details) {
419
+ try {
420
+ const deliver = operation === "insert" ? import_browser.insertCssTab : import_browser.removeCssTab;
421
+ await deliver(tabId, details);
422
+ } catch (error) {
423
+ throw new InjectCssFrameDeliveryError(tabId, frameId, error, operation);
424
+ }
425
+ }
426
+ async removeSource(source) {
427
+ const target = this.snapshotTarget();
428
+ const execution = this.snapshotExecution();
429
+ const timeoutMs = this.timeoutMs;
430
+ this.assertRemovalSupport();
431
+ try {
432
+ await this.withTimeout(
433
+ this.execute("remove", target, this.createDetails("remove", execution, source)),
434
+ target,
435
+ timeoutMs,
436
+ "remove"
437
+ );
438
+ } catch (error) {
439
+ this.throwDeliveryError("remove", target, execution, error);
440
+ }
441
+ }
442
+ async deliverFiles(operation, fileList) {
443
+ const target = this.snapshotTarget();
444
+ const execution = this.snapshotExecution();
445
+ const timeoutMs = this.timeoutMs;
446
+ let stopped = false;
447
+ if (operation === "remove") {
448
+ this.assertRemovalSupport();
449
+ }
450
+ const task = (async () => {
451
+ for (const file of fileList) {
452
+ if (stopped) return;
453
+ await this.execute(operation, target, this.createDetails(operation, execution, { file }));
89
454
  }
455
+ })();
456
+ try {
457
+ await this.withTimeout(task, target, timeoutMs, operation);
458
+ } catch (error) {
459
+ stopped = true;
460
+ this.throwDeliveryError(operation, target, execution, error);
90
461
  }
91
- await Promise.all(injectTasks);
92
462
  }
93
- get cssOrigin() {
94
- const { origin } = this._options;
95
- return origin && origin.toLowerCase();
463
+ assertRemovalSupport() {
464
+ if (typeof (0, import_browser.browser)().tabs?.removeCSS !== "function") {
465
+ throw new UnsupportedInjectCssOperationError(
466
+ "remove",
467
+ 'the current MV2 browser does not expose "tabs.removeCSS".'
468
+ );
469
+ }
470
+ }
471
+ throwDeliveryError(operation, target, execution, error) {
472
+ if (operation === "remove" && isUnsupportedRemovalCapabilityError(error)) {
473
+ throw new UnsupportedInjectCssOperationError(
474
+ "remove",
475
+ 'the current MV2 browser does not support "tabs.removeCSS".',
476
+ error
477
+ );
478
+ }
479
+ if (execution.origin !== void 0 && isUnsupportedOriginCapabilityError(error)) {
480
+ throw new UnsupportedInjectCssOptionError('"origin" is not supported by the current browser.', error);
481
+ }
482
+ throw this.deliveryError(target, error, operation);
96
483
  }
97
484
  };
98
485
 
99
486
  // src/InjectCssV3.ts
100
487
  var import_browser2 = require("@addon-core/browser");
101
488
  var InjectCssV3_default = class extends AbstractInjectCss_default {
489
+ constructor(options) {
490
+ super(options);
491
+ this.assertAdapterSupport(this._target, this._execution);
492
+ }
102
493
  async insert(css) {
103
- await (0, import_browser2.insertCss)({
104
- target: this.target,
105
- origin: this._options.origin,
106
- css
107
- });
494
+ const code = this.validateCode(css);
495
+ const target = this.snapshotTarget();
496
+ const execution = this.snapshotExecution();
497
+ const timeoutMs = this.timeoutMs;
498
+ await this.execute(
499
+ "insert",
500
+ target,
501
+ execution,
502
+ {
503
+ target: this.toNativeTarget(target),
504
+ css: code,
505
+ ...execution.origin !== void 0 ? { origin: execution.origin } : {}
506
+ },
507
+ timeoutMs
508
+ );
108
509
  }
109
- async file(fileList) {
110
- await (0, import_browser2.insertCss)({
111
- target: this.target,
112
- origin: this._options.origin,
113
- files: typeof fileList === "string" ? [fileList] : fileList
114
- });
510
+ async file(files) {
511
+ const fileList = this.normalizeFiles(files);
512
+ const target = this.snapshotTarget();
513
+ const execution = this.snapshotExecution();
514
+ const timeoutMs = this.timeoutMs;
515
+ await this.execute(
516
+ "insert",
517
+ target,
518
+ execution,
519
+ {
520
+ target: this.toNativeTarget(target),
521
+ files: fileList,
522
+ ...execution.origin !== void 0 ? { origin: execution.origin } : {}
523
+ },
524
+ timeoutMs
525
+ );
526
+ }
527
+ async remove(css) {
528
+ const code = this.validateCode(css);
529
+ await this.removeInjection({ css: code });
115
530
  }
116
- get target() {
117
- const target = { tabId: this._options.tabId };
118
- if (this.frameIds && this.frameIds.length > 0) {
119
- return { ...target, frameIds: this.frameIds };
531
+ async removeFile(files) {
532
+ const fileList = this.normalizeFiles(files);
533
+ await this.removeInjection({ files: fileList });
534
+ }
535
+ assertAdapterSupport(_target, execution) {
536
+ if (execution.matchAboutBlank !== void 0) {
537
+ throw new UnsupportedInjectCssOptionError('"matchAboutBlank" is not supported by the MV3 adapter.');
120
538
  }
121
- if (this.allFrames === true) {
122
- return { ...target, allFrames: true };
539
+ if (execution.runAt !== void 0) {
540
+ throw new UnsupportedInjectCssOptionError('"runAt" is not supported by the MV3 adapter.');
123
541
  }
124
- let isFirefox = false;
542
+ }
543
+ async execute(operation, target, execution, injection, timeoutMs) {
125
544
  try {
126
- isFirefox = !!browser().runtime.getBrowserInfo;
127
- } catch (_e) {
128
- }
129
- if (!isFirefox) {
130
- const documentIds = this.documentIds;
131
- if (documentIds && documentIds.length > 0) {
132
- return { ...target, documentIds };
545
+ const task = operation === "insert" ? (0, import_browser2.insertCss)(injection) : (0, import_browser2.removeCss)(injection);
546
+ await this.withTimeout(task, target, timeoutMs, operation);
547
+ } catch (error) {
548
+ if ("documentIds" in target && target.documentIds !== void 0 && isUnsupportedDocumentTargetCapabilityError(error)) {
549
+ throw new UnsupportedInjectCssTargetError(
550
+ '"documentIds" are not supported by the current browser.',
551
+ error
552
+ );
553
+ }
554
+ if (operation === "remove" && isUnsupportedRemovalCapabilityError(error)) {
555
+ throw new UnsupportedInjectCssOperationError(
556
+ "remove",
557
+ 'the current MV3 browser does not support "scripting.removeCSS".',
558
+ error
559
+ );
560
+ }
561
+ if (execution.origin !== void 0 && isUnsupportedOriginCapabilityError(error)) {
562
+ throw new UnsupportedInjectCssOptionError('"origin" is not supported by the current browser.', error);
133
563
  }
564
+ throw this.deliveryError(target, error, operation);
134
565
  }
135
- return target;
136
566
  }
137
- get documentIds() {
138
- const { documentId } = this._options;
139
- return typeof documentId === "string" ? [documentId] : documentId;
567
+ async removeInjection(source) {
568
+ const target = this.snapshotTarget();
569
+ const execution = this.snapshotExecution();
570
+ const timeoutMs = this.timeoutMs;
571
+ this.assertRemovalSupport();
572
+ await this.execute(
573
+ "remove",
574
+ target,
575
+ execution,
576
+ {
577
+ target: this.toNativeTarget(target),
578
+ ...source,
579
+ ...execution.origin !== void 0 ? { origin: execution.origin } : {}
580
+ },
581
+ timeoutMs
582
+ );
583
+ }
584
+ assertRemovalSupport() {
585
+ if (typeof (0, import_browser2.browser)().scripting?.removeCSS !== "function") {
586
+ throw new UnsupportedInjectCssOperationError(
587
+ "remove",
588
+ 'the current MV3 browser does not expose "scripting.removeCSS".'
589
+ );
590
+ }
591
+ }
592
+ toNativeTarget(target) {
593
+ if ("frameIds" in target && target.frameIds !== void 0) {
594
+ return { tabId: target.tabId, frameIds: [...target.frameIds] };
595
+ }
596
+ if ("documentIds" in target && target.documentIds !== void 0) {
597
+ return { tabId: target.tabId, documentIds: [...target.documentIds] };
598
+ }
599
+ if ("allFrames" in target && target.allFrames === true) {
600
+ return { tabId: target.tabId, allFrames: true };
601
+ }
602
+ return { tabId: target.tabId };
140
603
  }
141
604
  };
142
605
 
143
606
  // src/index.ts
144
- var index_default = (options) => {
607
+ var injectCss = (options) => {
145
608
  return (0, import_browser3.isManifestVersion3)() ? new InjectCssV3_default(options) : new InjectCssV2_default(options);
146
609
  };
610
+ var index_default = injectCss;
611
+ // Annotate the CommonJS export names for ESM import in node:
612
+ 0 && (module.exports = {
613
+ InjectCssBaseError,
614
+ InjectCssDeliveryError,
615
+ InjectCssFrameDeliveryError,
616
+ InjectCssTimeoutError,
617
+ InvalidInjectCssCodeError,
618
+ InvalidInjectCssFilesError,
619
+ InvalidInjectCssOptionsError,
620
+ InvalidInjectCssTargetError,
621
+ UnsupportedInjectCssOperationError,
622
+ UnsupportedInjectCssOptionError,
623
+ UnsupportedInjectCssTargetError,
624
+ injectCss
625
+ });
147
626
  //# sourceMappingURL=index.cjs.map