web-background 1.0.4 → 1.0.5

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 CHANGED
@@ -1,68 +1,149 @@
1
- # Web Background
2
- This module provides background runtime available on the web.
3
- 웹에서 사용할 있는 백그라운드 실행환경을 제공합니다.
4
-
5
- ## API
6
- ### background(fn)
7
-
8
- ```tsx
9
- type FunctionInBackground<Fn> = (...params: Parameters<Fn>) => Promise<ReturnType<Fn>>;
10
-
11
- background<Fn>(fn: Fn): FunctionInBackground<Fn>
12
- ```
13
-
14
- ## Example
15
- [CodeSandbox](https://8pj3sf.csb.app/)
16
-
17
- ```ts
18
- async function runLongTask () {
19
- const getVertexCoordinates = background((imageData: ImageData) => {
20
- const { data, width } = imageData;
21
- let [minX, minY, maxX, maxY] = [Infinity, Infinity, 0, 0];
22
-
23
- for(let i = 0, leng = data.length; i < leng; i += 4) {
24
- const [r, g, b, a] = data.slice(i, i+4);
25
- const isFilled = Math.max(r, g, b, a) > 0;
26
- if(isFilled) {
27
- const y = Math.floor(i / 4 / width);
28
- const x = Math.floor(i / 4 - width * y);
29
-
30
- minX = Math.min(minX, x);
31
- minY = Math.min(minY, y);
32
- maxX = Math.max(maxX, x);
33
- maxY = Math.max(maxY, y);
34
- }
35
- }
36
-
37
- return {
38
- x: minX === Infinity ? 0 : minX,
39
- y: minY === Infinity ? 0 : minY,
40
- width: maxX,
41
- height: maxY
42
- };
43
- })
44
-
45
- const coordinate = await getVertexCoordinates(
46
- canvas.getContext('2d').getImageData(0, 0, 1000, 1000)
47
- )
48
- }
1
+ # 🚀 Web Background
2
+
3
+ **Simple Web Worker Library - Run complex tasks in the background!**
4
+
5
+ Web Background is a simple yet powerful library that enables you to execute heavy computations in the background without blocking the main thread in web browsers.
6
+
7
+ ## ✨ Features
8
+
9
+ - 🎯 **Simple API**: Execute workers instantly with a single function
10
+ - 🧠 **Automatic Context Capture**: Automatically detects and transfers external variables
11
+ - 🚀 **High-Performance Caching**: Cache identical functions for faster re-execution
12
+ - 🧹 **Automatic Memory Management**: Auto-cleanup workers and cache on GC
13
+ - 💪 **Full TypeScript Support**: Complete type safety
14
+ - 📦 **Zero Dependencies**: No external dependencies
15
+
16
+ ## 📦 Installation
17
+
18
+ ```bash
19
+ npm install web-background
20
+ # or
21
+ yarn add web-background
22
+ # or
23
+ pnpm add web-background
24
+ ```
25
+
26
+ ## 🚀 Quick Start
27
+
28
+ ### Basic Usage
29
+
30
+ ```typescript
31
+ import { background } from "web-background";
32
+
33
+ // Simple background execution
34
+ const heavyCalculation = background((numbers: number[]) => {
35
+ // CPU intensive task
36
+ return numbers.reduce((sum, num) => sum + Math.sqrt(num), 0);
37
+ });
38
+
39
+ // Execute without blocking main thread
40
+ const result = await heavyCalculation([1, 4, 9, 16, 25]);
41
+ console.log(result); // 15
42
+
43
+ // When external variables are needed (optional context passing)
44
+ const multiplier = 10;
45
+ const processWithContext = background(
46
+ (numbers: number[]) => numbers.map((x) => x * multiplier),
47
+ { multiplier } // Explicit passing when needed
48
+ );
49
+ ```
50
+
51
+ ### Complex Real-world Example
52
+
53
+ ```typescript
54
+ // Image data processing
55
+ const processImage = background((imageData: ImageData) => {
56
+ const { data, width, height } = imageData;
57
+ const processed = new Uint8ClampedArray(data.length);
58
+
59
+ // Apply Gaussian blur
60
+ for (let i = 0; i < data.length; i += 4) {
61
+ const y = Math.floor(i / 4 / width);
62
+ const x = Math.floor(i / 4 - width * y);
63
+
64
+ processed[i] = data[i] * 0.8; // R
65
+ processed[i + 1] = data[i + 1] * 0.8; // G
66
+ processed[i + 2] = data[i + 2] * 0.8; // B
67
+ processed[i + 3] = data[i + 3]; // A
68
+ }
69
+
70
+ return new ImageData(processed, width, height);
71
+ });
72
+
73
+ // Usage
74
+ const canvas = document.querySelector("canvas");
75
+ const ctx = canvas.getContext("2d");
76
+ const originalData = ctx.getImageData(0, 0, canvas.width, canvas.height);
77
+
78
+ const blurredData = await processImage(originalData);
79
+ ctx.putImageData(blurredData, 0, 0);
49
80
  ```
50
81
 
82
+ ## 🛠️ API Reference
83
+
84
+ ### `background<T>(fn: T, context?: Record<string, any>): FunctionInBackground<T>`
85
+
86
+ Converts a function to run in a web worker.
87
+
88
+ **Parameters:**
89
+
90
+ - `fn`: Function to execute in the background
91
+ - `context` (optional): Explicitly pass external variables when needed
92
+
93
+ **Returns:**
94
+
95
+ - Function with the same signature as the original but returns a `Promise`
96
+
97
+ **Type:**
98
+
99
+ ```typescript
100
+ type FunctionInBackground<Fn> = (
101
+ ...params: Parameters<Fn>
102
+ ) => Promise<ReturnType<Fn>>;
103
+ ```
104
+
105
+ ## 📋 Constraints & Guidelines
106
+
107
+ ### ✅ What's Available
108
+
109
+ - Pure computation logic
110
+ - JSON serializable data (strings, numbers, arrays, objects, etc.)
111
+ - Built-in global objects like `Math`, `Date`, `JSON`
112
+ - Explicitly passed functions and variables
113
+
114
+ ### ❌ What's Not Available
115
+
116
+ - DOM APIs (`document`, `window`, etc.)
117
+ - Web APIs (`fetch`, `localStorage`, etc. have separate worker support)
118
+ - Non-serializable data (functions, Symbols, undefined, etc.)
119
+ - ES6 module imports (context passing required in bundler environments)
120
+
121
+ ### 💡 Best Practices
122
+
123
+ 1. **Context is optional**:
124
+
125
+ ```typescript
126
+ // ✅ Pure function without external variables
127
+ const pureWorker = background((data) => complexCalculation(data));
51
128
 
52
- ## Tip
53
- - Web Background is implemented as a Web Worker and is available in web browsers.
54
- Web Background는 웹 워커로 구현되며 브라우저에서 사용할 수 있습니다.
129
+ // ✅ Pass context when needed (more stable)
130
+ const contextWorker = background((data) => processWithConfig(data, config), {
131
+ config,
132
+ });
133
+ ```
55
134
 
56
- - In most cases, you don't need to use it. It's recommended to use it for long working
57
- 대부분의 경우 이 모듈을 사용할 필요는 없으나, 비용이 큰 작업을 처리해야 할 경우 사용할 것을 추천합니다.
135
+ 2. **Use only for heavy computations**:
58
136
 
59
- - Most WebAPIs including DOM APIs are cannot be used.
60
- DOM API를 포함한 대부분의 WebAPI를 사용하지 못합니다.
61
- [Read More](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
137
+ ```typescript
138
+ // Simple operations
139
+ const add = background((a, b) => a + b);
62
140
 
63
- - To send arguments, you must use data from structured clone algorithm types.
64
- 인자로 전송 가능한 데이터 유형은 복사 가능한 타입의 데이터만 가능합니다.
65
- [Read More](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm)
141
+ // CPU intensive operations
142
+ const processLargeArray = background((data) => {
143
+ // Complex processing logic...
144
+ });
145
+ ```
66
146
 
67
- - It runs in isolation, cannot be access external variables and modules
68
- 격리된 환경에서 실행되므로 외부 모듈 외부 변수에 접근할 수 없습니다.
147
+ 3. **Keep data size appropriate**:
148
+ - Large objects or complex contexts cause serialization overhead
149
+ - Pass only necessary data for performance optimization
@@ -1,2 +1,3 @@
1
1
  export type FunctionInBackground<Callback extends (params: any) => any> = (...params: Parameters<Callback>) => Promise<ReturnType<Callback>>;
2
2
  export declare function background<Payload, ReturnValue>(fn: (payload: Payload) => ReturnValue): FunctionInBackground<typeof fn>;
3
+ export declare function background<Payload, ReturnValue>(fn: (payload: Payload) => ReturnValue, context?: Record<string, any>): FunctionInBackground<typeof fn>;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- class e extends Worker{postMessage(e){super.postMessage(e)}addEventListener(e,t){super.addEventListener(e,t)}}class t extends e{constructor(e,t){super(e,t)}request(e){return new Promise((t=>{const r=({data:e})=>{t(e),this.removeEventListener("message",r)};this.postMessage(e),this.addEventListener("message",r)}))}}"function"==typeof SuppressedError&&SuppressedError;class r{static fromModule(e,t){var{module:r}=t,n=function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var s=0;for(n=Object.getOwnPropertySymbols(e);s<n.length;s++)t.indexOf(n[s])<0&&Object.prototype.propertyIsEnumerable.call(e,n[s])&&(r[n[s]]=e[n[s]])}return r}(t,["module"]);const s=`\n self.addEventListener('message', async event => {\n const result = await (${r.toString()})(${r.length>1?"...event.data":"event.data"});\n \n self.postMessage(result);\n });\n `.trim(),o=new Blob([s]),a=URL.createObjectURL(o),l=new e(a,n),u=l.terminate;return l.terminate=()=>{u.call(l),URL.revokeObjectURL(a)},l}}const n=new FinalizationRegistry((e=>e.terminate()));function s(e){let s=null;return function o(...a){if("undefined"==typeof window)throw new Error("[web-background] You must use background in browser");return null==s&&(s=r.fromModule(t,{module:e}),n.register(o,s)),s.request.call(s,a.length>1?a:a[0])}}export{s as background};
1
+ class e extends Worker{postMessage(e){super.postMessage(e)}addEventListener(e,t){super.addEventListener(e,t)}}class t extends e{constructor(e,t){super(e,t)}request(e){return new Promise((t=>{const n=({data:e})=>{t(e),this.removeEventListener("message",n)};this.postMessage(e),this.addEventListener("message",n)}))}}"function"==typeof SuppressedError&&SuppressedError;class n{constructor(){this.store=new Map}get(e){return this.store.get(e)}set(e,t){this.store.set(e,t)}delete(e){this.store.delete(e)}clear(){this.store.clear()}size(){return this.store.size}}const o=new n,r=new class extends n{deleteByPrefix(e){for(const[t,n]of this.store)t.startsWith(e)&&this.delete(t)}},s=new n,i=new n;class c{static increment(e){const t=i.get(e)||0;i.set(e,t+1)}static decrement(e){const t=i.get(e)||0;return t<=1?(i.delete(e),!0):(i.set(e,t-1),!1)}static getCount(e){return i.get(e)||0}}class a{static getContext(e){return o.get(e)}static setContext(e,t){o.set(e,t),c.increment(e)}static getSerialization(e){return r.get(e)}static setSerialization(e,t){r.set(e,t);const n=e.split(":")[0];c.increment(n)}static getIdentifiers(e){return s.get(e)}static setIdentifiers(e,t){s.set(e,t),c.increment(e)}static decrementUsage(e){c.decrement(e)&&(o.delete(e),s.delete(e),r.deleteByPrefix(e+":"),console.log(`'${e.substring(0,50)}...' 함수 캐시 정리 완료`))}static getCacheStats(){return{contextCacheSize:o.size(),serializationCacheSize:r.size(),identifierCacheSize:s.size(),activeFunctions:i.size()}}static clearAllCaches(){o.clear(),r.clear(),s.clear(),i.clear(),console.log("모든 캐시가 정리되었습니다")}}const l=new Set(["function","return","var","let","const","if","else","for","while","do","break","continue","switch","case","default","try","catch","finally","throw","new","this","typeof","instanceof","in","of","class","extends","super","async","await","yield","true","false","null","undefined","delete","void","with","export","import","from"]),f=new Set(["console","Math","Date","JSON","Array","Object","String","Number","Boolean","RegExp","Error","Promise","setTimeout","setInterval","clearTimeout","clearInterval","parseInt","parseFloat","isNaN","isFinite","decodeURIComponent","encodeURIComponent","window","document","global","process","self","URL","URLSearchParams","fetch","XMLHttpRequest","localStorage","sessionStorage","location","history","navigator","Blob","File","FileReader","FormData","Headers","Request","Response","AbortController","AbortSignal","crypto","performance","requestAnimationFrame","cancelAnimationFrame","requestIdleCallback","cancelIdleCallback"]);function u(e){const t=a.getIdentifiers(e);if(t)return t;const n=new Set,o=/\b[a-zA-Z_$][a-zA-Z0-9_$]*\b/g;let r;for(;null!==(r=o.exec(e));){const t=r[0];d(t)||p(t,e)||y(t,e)||n.add(t)}const s=Array.from(n);return a.setIdentifiers(e,s),s}function d(e){return l.has(e)}function g(e){return f.has(e)}function p(e,t){return[new RegExp(`function\\s*\\([^)]*\\b${e}\\b[^)]*\\)`),new RegExp(`\\([^)]*\\b${e}\\b[^)]*\\)\\s*=>`),new RegExp(`^\\s*${e}\\s*=>`),new RegExp(`async\\s+\\([^)]*\\b${e}\\b[^)]*\\)\\s*=>`),new RegExp(`async\\s+${e}\\s*=>`)].some((e=>e.test(t)))}function y(e,t){return[new RegExp(`(?:var|let|const)\\s+${e}\\b`),new RegExp(`(?:var|let|const)\\s+\\{[^}]*\\b${e}\\b[^}]*\\}`),new RegExp(`function\\s+${e}\\b`)].some((e=>e.test(t)))}function w(e){if(null==e)return!0;if("boolean"==typeof e||"number"==typeof e||"string"==typeof e)return!0;if("function"==typeof e||"symbol"==typeof e||"bigint"==typeof e)return!1;if(Array.isArray(e))return e.every((e=>w(e)));if("object"==typeof e)try{return JSON.stringify(e),!0}catch(e){return!1}return!1}function h(e,t){const n=`${e.toString()}:${JSON.stringify(t||{})}`,o=a.getSerialization(n);if(o)return o;const r=function(e,t){const n=e.toString();let o="(function() {\n";const r=Object.entries(t),s=r.filter((([,e])=>"function"!=typeof e)),i=r.filter((([,e])=>"function"==typeof e));s.forEach((([e,t])=>{try{o+=` const ${e} = ${JSON.stringify(t)};\n`}catch(t){o+=` const ${e} = undefined;\n`}})),i.forEach((([e,t])=>{"function"==typeof t&&(o+=` const ${e} = ${t.toString()};\n`)})),r.length>0&&(o+="\n");return o+=` return ${n};\n`,o+="})()",o}(e,t||{});return a.setSerialization(n,r),r}class b{static fromModule(e,t){var{module:n,context:o}=t,r=function(e,t){var n={};for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&t.indexOf(o)<0&&(n[o]=e[o]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var r=0;for(o=Object.getOwnPropertySymbols(e);r<o.length;r++)t.indexOf(o[r])<0&&Object.prototype.propertyIsEnumerable.call(e,o[r])&&(n[o[r]]=e[o[r]])}return n}(t,["module","context"]);const s=o||function(e){const t=u(e.toString()),n={};return t.forEach((e=>{if(!g(e))try{const t=globalThis[e]||("undefined"!=typeof window?window[e]:void 0);void 0!==t&&("function"==typeof t||w(t))&&(n[e]=t)}catch(e){}})),n}(n),i=h(n,s);console.log(i);const c=`\n self.addEventListener('message', async event => {\n const func = ${i};\n const result = await func(${n.length>1?"...event.data":"event.data"});\n self.postMessage(result);\n });\n `.trim(),a=new Blob([c]),l=URL.createObjectURL(a),f=new e(l,r),d=f.terminate;return f.terminate=()=>{d.call(f),URL.revokeObjectURL(l)},f}}const m=new FinalizationRegistry((e=>{e.worker.terminate(),a.decrementUsage(e.funcStr),console.log("워커 정리 및 캐시 정리 완료")}));function S(e){const t=e.toString(),n=a.getContext(t);if(n)return console.log("캐시된 컨텍스트 사용:",n),n;const o={};console.log("함수 문자열:",t);const r=u(t),s=function(e){const t=e.match(/function[^(]*\(([^)]*)\)|^\s*\(([^)]*)\)\s*=>|^\s*([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=>/),n=new Set;t&&(t[1]||t[2]||t[3]||"").split(",").forEach((e=>{const t=e.trim().split("=")[0].trim();t&&/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(t)&&n.add(t)}));return n}(t),i=function(e){const t=new Set,n=/(?:var|let|const)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;let o;for(;null!==(o=n.exec(e));)t.add(o[1]);return t}(t),c=function(){const e={};return"undefined"!=typeof window&&Object.getOwnPropertyNames(window).forEach((t=>{try{if(!g(t)){const n=Object.getOwnPropertyDescriptor(window,t);if(n&&n.configurable){const n=window[t];void 0!==n&&(e[t]=n)}}}catch(e){}})),"undefined"!=typeof globalThis&&Object.getOwnPropertyNames(globalThis).forEach((t=>{try{g(t)||t in e||(e[t]=globalThis[t])}catch(e){}})),e}();return console.log("추출된 식별자들:",r),console.log("매개변수들:",Array.from(s)),console.log("로컬 변수들:",Array.from(i)),console.log("호출자 스코프:",Object.keys(c)),r.forEach((e=>{if(!(d(e)||g(e)||s.has(e)||i.has(e))){if(console.log(`'${e}' 검사 중...`),e in c){const t=c[e];return console.log(`호출자 스코프에서 '${e}' 발견:`,typeof t),void(("function"==typeof t||w(t))&&(o[e]=t))}try{let t;"undefined"!=typeof window&&e in window?(t=window[e],console.log(`window에서 '${e}' 발견:`,typeof t)):"undefined"!=typeof globalThis&&e in globalThis&&(t=globalThis[e],console.log(`globalThis에서 '${e}' 발견:`,typeof t)),void 0!==t&&("function"==typeof t||w(t))&&(o[e]=t)}catch(t){console.log(`'${e}' 접근 실패:`,t)}}})),console.log("최종 컨텍스트:",o),a.setContext(t,o),o}function $(e,n){const o=void 0!==n?n:S(e);console.log("최종 사용될 컨텍스트:",o);let r=null;return function n(...s){if("undefined"==typeof window)throw new Error("[web-background] You must use background in browser");return null==r&&(r=b.fromModule(t,{module:e,context:o}),m.register(n,{worker:r,funcStr:e.toString()})),r.request.call(r,s.length>1?s:s[0])}}export{$ as background};
@@ -4,8 +4,9 @@ interface WorkerConstructor<Worker> {
4
4
  }
5
5
  interface Options extends WorkerOptions {
6
6
  module: (...args: any[]) => any;
7
+ context?: Record<string, any>;
7
8
  }
8
9
  export declare class WorkerBuilder {
9
- static fromModule<W extends Worker>(Worker: WorkerConstructor<W>, { module, ...options }: Options): W;
10
+ static fromModule<W extends Worker>(Worker: WorkerConstructor<W>, { module, context, ...options }: Options): W;
10
11
  }
11
12
  export {};
@@ -0,0 +1,16 @@
1
+ export declare class CacheManager {
2
+ static getContext(funcStr: string): Record<string, any> | undefined;
3
+ static setContext(funcStr: string, context: Record<string, any>): void;
4
+ static getSerialization(cacheKey: string): string | undefined;
5
+ static setSerialization(cacheKey: string, result: string): void;
6
+ static getIdentifiers(funcStr: string): string[] | undefined;
7
+ static setIdentifiers(funcStr: string, identifiers: string[]): void;
8
+ static decrementUsage(funcStr: string): void;
9
+ static getCacheStats(): {
10
+ contextCacheSize: number;
11
+ serializationCacheSize: number;
12
+ identifierCacheSize: number;
13
+ activeFunctions: number;
14
+ };
15
+ static clearAllCaches(): void;
16
+ }
@@ -0,0 +1 @@
1
+ export declare function captureCallerContext(fn: Function): Record<string, any>;
@@ -0,0 +1,9 @@
1
+ export declare function extractIdentifiers(funcStr: string): string[];
2
+ export declare function isKeyword(identifier: string): boolean;
3
+ export declare function isGlobalBuiltIn(identifier: string): boolean;
4
+ export declare function isParameter(identifier: string, funcStr: string): boolean;
5
+ export declare function isLocalVariable(identifier: string, funcStr: string): boolean;
6
+ export declare function isSerializable(value: any): boolean;
7
+ export declare function getGlobalScope(): Record<string, any>;
8
+ export declare function extractParametersFromFunction(funcStr: string): Set<string>;
9
+ export declare function extractLocalVariables(funcStr: string): Set<string>;
@@ -0,0 +1,3 @@
1
+ export declare function serializeFunction(func: Function, providedContext?: Record<string, any>): string;
2
+ export declare function deserializeFunction(serializedFunc: string): Function;
3
+ export declare function extractClosureContext(func: Function): Record<string, any>;
@@ -1 +1,4 @@
1
- export declare const workerCleanupRegistry: FinalizationRegistry<Worker>;
1
+ export declare const workerCleanupRegistry: FinalizationRegistry<{
2
+ worker: Worker;
3
+ funcStr: string;
4
+ }>;
package/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "Background",
8
8
  "Web Background"
9
9
  ],
10
- "version": "1.0.4",
10
+ "version": "1.0.5",
11
11
  "main": "dist/index.js",
12
12
  "module": "dist/index.js",
13
13
  "type": "module",
@@ -24,10 +24,11 @@
24
24
  "@babel/preset-env": "^7.23.3",
25
25
  "@rollup/plugin-babel": "^6.0.4",
26
26
  "@rollup/plugin-node-resolve": "^15.2.3",
27
+ "@rollup/plugin-strip": "^3.0.4",
27
28
  "@rollup/plugin-terser": "^0.4.4",
28
29
  "rollup": "^4.4.0",
29
30
  "rollup-plugin-typescript2": "^0.36.0",
30
31
  "typescript": "5.2"
31
32
  },
32
- "dependencies": {}
33
- }
33
+ "files": ["dist"]
34
+ }
package/www/README.md DELETED
@@ -1,69 +0,0 @@
1
- # React + TypeScript + Vite
2
-
3
- This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
4
-
5
- Currently, two official plugins are available:
6
-
7
- - [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh
8
- - [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
9
-
10
- ## Expanding the ESLint configuration
11
-
12
- If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:
13
-
14
- ```js
15
- export default tseslint.config([
16
- globalIgnores(['dist']),
17
- {
18
- files: ['**/*.{ts,tsx}'],
19
- extends: [
20
- // Other configs...
21
-
22
- // Remove tseslint.configs.recommended and replace with this
23
- ...tseslint.configs.recommendedTypeChecked,
24
- // Alternatively, use this for stricter rules
25
- ...tseslint.configs.strictTypeChecked,
26
- // Optionally, add this for stylistic rules
27
- ...tseslint.configs.stylisticTypeChecked,
28
-
29
- // Other configs...
30
- ],
31
- languageOptions: {
32
- parserOptions: {
33
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
34
- tsconfigRootDir: import.meta.dirname,
35
- },
36
- // other options...
37
- },
38
- },
39
- ])
40
- ```
41
-
42
- You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules:
43
-
44
- ```js
45
- // eslint.config.js
46
- import reactX from 'eslint-plugin-react-x'
47
- import reactDom from 'eslint-plugin-react-dom'
48
-
49
- export default tseslint.config([
50
- globalIgnores(['dist']),
51
- {
52
- files: ['**/*.{ts,tsx}'],
53
- extends: [
54
- // Other configs...
55
- // Enable lint rules for React
56
- reactX.configs['recommended-typescript'],
57
- // Enable lint rules for React DOM
58
- reactDom.configs.recommended,
59
- ],
60
- languageOptions: {
61
- parserOptions: {
62
- project: ['./tsconfig.node.json', './tsconfig.app.json'],
63
- tsconfigRootDir: import.meta.dirname,
64
- },
65
- // other options...
66
- },
67
- },
68
- ])
69
- ```
@@ -1,23 +0,0 @@
1
- import js from '@eslint/js'
2
- import globals from 'globals'
3
- import reactHooks from 'eslint-plugin-react-hooks'
4
- import reactRefresh from 'eslint-plugin-react-refresh'
5
- import tseslint from 'typescript-eslint'
6
- import { globalIgnores } from 'eslint/config'
7
-
8
- export default tseslint.config([
9
- globalIgnores(['dist']),
10
- {
11
- files: ['**/*.{ts,tsx}'],
12
- extends: [
13
- js.configs.recommended,
14
- tseslint.configs.recommended,
15
- reactHooks.configs['recommended-latest'],
16
- reactRefresh.configs.vite,
17
- ],
18
- languageOptions: {
19
- ecmaVersion: 2020,
20
- globals: globals.browser,
21
- },
22
- },
23
- ])
package/www/index.html DELETED
@@ -1,13 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
- <title>Vite + React + TS</title>
8
- </head>
9
- <body>
10
- <div id="root"></div>
11
- <script type="module" src="/src/main.tsx"></script>
12
- </body>
13
- </html>
package/www/package.json DELETED
@@ -1,29 +0,0 @@
1
- {
2
- "name": "www",
3
- "private": true,
4
- "version": "0.0.0",
5
- "type": "module",
6
- "scripts": {
7
- "dev": "vite",
8
- "build": "tsc -b && vite build",
9
- "lint": "eslint .",
10
- "preview": "vite preview"
11
- },
12
- "dependencies": {
13
- "react": "^19.1.1",
14
- "react-dom": "^19.1.1"
15
- },
16
- "devDependencies": {
17
- "@eslint/js": "^9.33.0",
18
- "@types/react": "^19.1.10",
19
- "@types/react-dom": "^19.1.7",
20
- "@vitejs/plugin-react": "^5.0.0",
21
- "eslint": "^9.33.0",
22
- "eslint-plugin-react-hooks": "^5.2.0",
23
- "eslint-plugin-react-refresh": "^0.4.20",
24
- "globals": "^16.3.0",
25
- "typescript": "~5.8.3",
26
- "typescript-eslint": "^8.39.1",
27
- "vite": "^7.1.2"
28
- }
29
- }
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
package/www/src/App.css DELETED
@@ -1,42 +0,0 @@
1
- #root {
2
- max-width: 1280px;
3
- margin: 0 auto;
4
- padding: 2rem;
5
- text-align: center;
6
- }
7
-
8
- .logo {
9
- height: 6em;
10
- padding: 1.5em;
11
- will-change: filter;
12
- transition: filter 300ms;
13
- }
14
- .logo:hover {
15
- filter: drop-shadow(0 0 2em #646cffaa);
16
- }
17
- .logo.react:hover {
18
- filter: drop-shadow(0 0 2em #61dafbaa);
19
- }
20
-
21
- @keyframes logo-spin {
22
- from {
23
- transform: rotate(0deg);
24
- }
25
- to {
26
- transform: rotate(360deg);
27
- }
28
- }
29
-
30
- @media (prefers-reduced-motion: no-preference) {
31
- a:nth-of-type(2) .logo {
32
- animation: logo-spin infinite 20s linear;
33
- }
34
- }
35
-
36
- .card {
37
- padding: 2em;
38
- }
39
-
40
- .read-the-docs {
41
- color: #888;
42
- }
package/www/src/App.tsx DELETED
@@ -1,41 +0,0 @@
1
- import { useState } from 'react'
2
- import reactLogo from './assets/react.svg'
3
- import viteLogo from '/vite.svg'
4
- import './App.css'
5
- import { background } from '../../src/background';
6
-
7
- const log = background((x: string) => x + 'background');
8
-
9
- function App() {
10
- const [count, setCount] = useState(0)
11
-
12
- return (
13
- <>
14
- <div>
15
- <a href="https://vite.dev" target="_blank">
16
- <img src={viteLogo} className="logo" alt="Vite logo" />
17
- </a>
18
- <a href="https://react.dev" target="_blank">
19
- <img src={reactLogo} className="logo react" alt="React logo" />
20
- </a>
21
- </div>
22
- <h1 onClick={async () => {
23
- const result = await log('dd');
24
- console.log(result)
25
- }}>Vite + React</h1>
26
- <div className="card">
27
- <button onClick={() => setCount((count) => count + 1)}>
28
- count is {count}
29
- </button>
30
- <p>
31
- Edit <code>src/App.tsx</code> and save to test HMR
32
- </p>
33
- </div>
34
- <p className="read-the-docs">
35
- Click on the Vite and React logos to learn more
36
- </p>
37
- </>
38
- )
39
- }
40
-
41
- export default App
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="35.93" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 228"><path fill="#00D8FF" d="M210.483 73.824a171.49 171.49 0 0 0-8.24-2.597c.465-1.9.893-3.777 1.273-5.621c6.238-30.281 2.16-54.676-11.769-62.708c-13.355-7.7-35.196.329-57.254 19.526a171.23 171.23 0 0 0-6.375 5.848a155.866 155.866 0 0 0-4.241-3.917C100.759 3.829 77.587-4.822 63.673 3.233C50.33 10.957 46.379 33.89 51.995 62.588a170.974 170.974 0 0 0 1.892 8.48c-3.28.932-6.445 1.924-9.474 2.98C17.309 83.498 0 98.307 0 113.668c0 15.865 18.582 31.778 46.812 41.427a145.52 145.52 0 0 0 6.921 2.165a167.467 167.467 0 0 0-2.01 9.138c-5.354 28.2-1.173 50.591 12.134 58.266c13.744 7.926 36.812-.22 59.273-19.855a145.567 145.567 0 0 0 5.342-4.923a168.064 168.064 0 0 0 6.92 6.314c21.758 18.722 43.246 26.282 56.54 18.586c13.731-7.949 18.194-32.003 12.4-61.268a145.016 145.016 0 0 0-1.535-6.842c1.62-.48 3.21-.974 4.76-1.488c29.348-9.723 48.443-25.443 48.443-41.52c0-15.417-17.868-30.326-45.517-39.844Zm-6.365 70.984c-1.4.463-2.836.91-4.3 1.345c-3.24-10.257-7.612-21.163-12.963-32.432c5.106-11 9.31-21.767 12.459-31.957c2.619.758 5.16 1.557 7.61 2.4c23.69 8.156 38.14 20.213 38.14 29.504c0 9.896-15.606 22.743-40.946 31.14Zm-10.514 20.834c2.562 12.94 2.927 24.64 1.23 33.787c-1.524 8.219-4.59 13.698-8.382 15.893c-8.067 4.67-25.32-1.4-43.927-17.412a156.726 156.726 0 0 1-6.437-5.87c7.214-7.889 14.423-17.06 21.459-27.246c12.376-1.098 24.068-2.894 34.671-5.345a134.17 134.17 0 0 1 1.386 6.193ZM87.276 214.515c-7.882 2.783-14.16 2.863-17.955.675c-8.075-4.657-11.432-22.636-6.853-46.752a156.923 156.923 0 0 1 1.869-8.499c10.486 2.32 22.093 3.988 34.498 4.994c7.084 9.967 14.501 19.128 21.976 27.15a134.668 134.668 0 0 1-4.877 4.492c-9.933 8.682-19.886 14.842-28.658 17.94ZM50.35 144.747c-12.483-4.267-22.792-9.812-29.858-15.863c-6.35-5.437-9.555-10.836-9.555-15.216c0-9.322 13.897-21.212 37.076-29.293c2.813-.98 5.757-1.905 8.812-2.773c3.204 10.42 7.406 21.315 12.477 32.332c-5.137 11.18-9.399 22.249-12.634 32.792a134.718 134.718 0 0 1-6.318-1.979Zm12.378-84.26c-4.811-24.587-1.616-43.134 6.425-47.789c8.564-4.958 27.502 2.111 47.463 19.835a144.318 144.318 0 0 1 3.841 3.545c-7.438 7.987-14.787 17.08-21.808 26.988c-12.04 1.116-23.565 2.908-34.161 5.309a160.342 160.342 0 0 1-1.76-7.887Zm110.427 27.268a347.8 347.8 0 0 0-7.785-12.803c8.168 1.033 15.994 2.404 23.343 4.08c-2.206 7.072-4.956 14.465-8.193 22.045a381.151 381.151 0 0 0-7.365-13.322Zm-45.032-43.861c5.044 5.465 10.096 11.566 15.065 18.186a322.04 322.04 0 0 0-30.257-.006c4.974-6.559 10.069-12.652 15.192-18.18ZM82.802 87.83a323.167 323.167 0 0 0-7.227 13.238c-3.184-7.553-5.909-14.98-8.134-22.152c7.304-1.634 15.093-2.97 23.209-3.984a321.524 321.524 0 0 0-7.848 12.897Zm8.081 65.352c-8.385-.936-16.291-2.203-23.593-3.793c2.26-7.3 5.045-14.885 8.298-22.6a321.187 321.187 0 0 0 7.257 13.246c2.594 4.48 5.28 8.868 8.038 13.147Zm37.542 31.03c-5.184-5.592-10.354-11.779-15.403-18.433c4.902.192 9.899.29 14.978.29c5.218 0 10.376-.117 15.453-.343c-4.985 6.774-10.018 12.97-15.028 18.486Zm52.198-57.817c3.422 7.8 6.306 15.345 8.596 22.52c-7.422 1.694-15.436 3.058-23.88 4.071a382.417 382.417 0 0 0 7.859-13.026a347.403 347.403 0 0 0 7.425-13.565Zm-16.898 8.101a358.557 358.557 0 0 1-12.281 19.815a329.4 329.4 0 0 1-23.444.823c-7.967 0-15.716-.248-23.178-.732a310.202 310.202 0 0 1-12.513-19.846h.001a307.41 307.41 0 0 1-10.923-20.627a310.278 310.278 0 0 1 10.89-20.637l-.001.001a307.318 307.318 0 0 1 12.413-19.761c7.613-.576 15.42-.876 23.31-.876H128c7.926 0 15.743.303 23.354.883a329.357 329.357 0 0 1 12.335 19.695a358.489 358.489 0 0 1 11.036 20.54a329.472 329.472 0 0 1-11 20.722Zm22.56-122.124c8.572 4.944 11.906 24.881 6.52 51.026c-.344 1.668-.73 3.367-1.15 5.09c-10.622-2.452-22.155-4.275-34.23-5.408c-7.034-10.017-14.323-19.124-21.64-27.008a160.789 160.789 0 0 1 5.888-5.4c18.9-16.447 36.564-22.941 44.612-18.3ZM128 90.808c12.625 0 22.86 10.235 22.86 22.86s-10.235 22.86-22.86 22.86s-22.86-10.235-22.86-22.86s10.235-22.86 22.86-22.86Z"></path></svg>
package/www/src/index.css DELETED
@@ -1,68 +0,0 @@
1
- :root {
2
- font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
3
- line-height: 1.5;
4
- font-weight: 400;
5
-
6
- color-scheme: light dark;
7
- color: rgba(255, 255, 255, 0.87);
8
- background-color: #242424;
9
-
10
- font-synthesis: none;
11
- text-rendering: optimizeLegibility;
12
- -webkit-font-smoothing: antialiased;
13
- -moz-osx-font-smoothing: grayscale;
14
- }
15
-
16
- a {
17
- font-weight: 500;
18
- color: #646cff;
19
- text-decoration: inherit;
20
- }
21
- a:hover {
22
- color: #535bf2;
23
- }
24
-
25
- body {
26
- margin: 0;
27
- display: flex;
28
- place-items: center;
29
- min-width: 320px;
30
- min-height: 100vh;
31
- }
32
-
33
- h1 {
34
- font-size: 3.2em;
35
- line-height: 1.1;
36
- }
37
-
38
- button {
39
- border-radius: 8px;
40
- border: 1px solid transparent;
41
- padding: 0.6em 1.2em;
42
- font-size: 1em;
43
- font-weight: 500;
44
- font-family: inherit;
45
- background-color: #1a1a1a;
46
- cursor: pointer;
47
- transition: border-color 0.25s;
48
- }
49
- button:hover {
50
- border-color: #646cff;
51
- }
52
- button:focus,
53
- button:focus-visible {
54
- outline: 4px auto -webkit-focus-ring-color;
55
- }
56
-
57
- @media (prefers-color-scheme: light) {
58
- :root {
59
- color: #213547;
60
- background-color: #ffffff;
61
- }
62
- a:hover {
63
- color: #747bff;
64
- }
65
- button {
66
- background-color: #f9f9f9;
67
- }
68
- }
package/www/src/main.tsx DELETED
@@ -1,10 +0,0 @@
1
- import { StrictMode } from 'react'
2
- import { createRoot } from 'react-dom/client'
3
- import './index.css'
4
- import App from './App.tsx'
5
-
6
- createRoot(document.getElementById('root')!).render(
7
- <StrictMode>
8
- <App />
9
- </StrictMode>,
10
- )
@@ -1 +0,0 @@
1
- /// <reference types="vite/client" />
@@ -1,27 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4
- "target": "ES2022",
5
- "useDefineForClassFields": true,
6
- "lib": ["ES2022", "DOM", "DOM.Iterable"],
7
- "module": "ESNext",
8
- "skipLibCheck": true,
9
-
10
- /* Bundler mode */
11
- "moduleResolution": "bundler",
12
- "allowImportingTsExtensions": true,
13
- "verbatimModuleSyntax": true,
14
- "moduleDetection": "force",
15
- "noEmit": true,
16
- "jsx": "react-jsx",
17
-
18
- /* Linting */
19
- "strict": true,
20
- "noUnusedLocals": true,
21
- "noUnusedParameters": true,
22
- "erasableSyntaxOnly": true,
23
- "noFallthroughCasesInSwitch": true,
24
- "noUncheckedSideEffectImports": true
25
- },
26
- "include": ["src"]
27
- }
@@ -1,25 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
4
- "target": "ES2023",
5
- "lib": ["ES2023"],
6
- "module": "ESNext",
7
- "skipLibCheck": true,
8
-
9
- /* Bundler mode */
10
- "moduleResolution": "bundler",
11
- "allowImportingTsExtensions": true,
12
- "verbatimModuleSyntax": true,
13
- "moduleDetection": "force",
14
- "noEmit": true,
15
-
16
- /* Linting */
17
- "strict": true,
18
- "noUnusedLocals": true,
19
- "noUnusedParameters": true,
20
- "erasableSyntaxOnly": true,
21
- "noFallthroughCasesInSwitch": true,
22
- "noUncheckedSideEffectImports": true
23
- },
24
- "include": ["vite.config.ts"]
25
- }
@@ -1,7 +0,0 @@
1
- import { defineConfig } from 'vite'
2
- import react from '@vitejs/plugin-react'
3
-
4
- // https://vite.dev/config/
5
- export default defineConfig({
6
- plugins: [react()],
7
- })