@think-grid-labs/snapbolt 0.1.3

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 ADDED
@@ -0,0 +1,36 @@
1
+ # @think-grid-labs/snapbolt
2
+
3
+ A high-performance image optimization toolkit powered by Rust and WebAssembly.
4
+
5
+ ## Overview
6
+ This toolkit provides professional-grade image optimization (resizing and JPEG/WebP encoding) that runs entirely on the client side.
7
+
8
+ ## Quick Start
9
+
10
+ ### 1. Install
11
+ ```bash
12
+ npm install @think-grid-labs/snapbolt
13
+ ```
14
+
15
+ ### 2. Sync WASM Binary
16
+ You must ensure the `.wasm` file is available in your project's `public` folder:
17
+ ```bash
18
+ npx @think-grid-labs/snapbolt-cli sync ./public
19
+ ```
20
+
21
+ ### 3. Use with React
22
+ ```tsx
23
+ import { useImageOptimizer } from '@think-grid-labs/snapbolt';
24
+
25
+ const SmartImage = ({ src }) => {
26
+ const { optimizedUrl, loading } = useImageOptimizer(src, 75, 300);
27
+ return <img src={optimizedUrl || src} alt="Optimized" />;
28
+ };
29
+ ```
30
+
31
+ ## Features
32
+ - **Client-Side Optimization**: Zero server cost.
33
+ - **Blazing Fast**: Powered by a Rust core.
34
+ - **React Ready**: Easy-to-use hooks.
35
+
36
+ For full documentation, visit our [GitHub Repository](https://github.com/ThinkGrid-Labs/snapbolt).
@@ -0,0 +1,6 @@
1
+ export interface UseImageOptimizerResult {
2
+ optimizedUrl: string | null;
3
+ loading: boolean;
4
+ error: string | null;
5
+ }
6
+ export declare const useImageOptimizer: (src: string | Blob, quality?: number) => UseImageOptimizerResult;
package/dist/index.js ADDED
@@ -0,0 +1,69 @@
1
+ import { useState, useEffect } from 'react';
2
+ // Dynamic import for the Wasm module
3
+ const loadWasm = async () => {
4
+ // We import from the local pkg directory (built via wasm-pack)
5
+ return import('../pkg/snapbolt.js');
6
+ };
7
+ export const useImageOptimizer = (src, quality = 80) => {
8
+ const [state, setState] = useState({
9
+ optimizedUrl: null,
10
+ loading: false,
11
+ error: null,
12
+ });
13
+ useEffect(() => {
14
+ let mounted = true;
15
+ let currentUrl = null;
16
+ const process = async () => {
17
+ if (!src)
18
+ return;
19
+ setState(prev => ({ ...prev, loading: true, error: null }));
20
+ try {
21
+ // 1. Get Blob
22
+ let blob;
23
+ if (typeof src === 'string') {
24
+ const resp = await fetch(src);
25
+ if (!resp.ok)
26
+ throw new Error(`Failed to fetch image: ${resp.statusText}`);
27
+ blob = await resp.blob();
28
+ }
29
+ else {
30
+ blob = src;
31
+ }
32
+ // 2. Load bytes
33
+ const buffer = await blob.arrayBuffer();
34
+ const bytes = new Uint8Array(buffer);
35
+ // 3. Wasm Optimize
36
+ const wasm = await loadWasm();
37
+ const optimizedBytes = wasm.optimize_image_sync(bytes, quality);
38
+ // 4. Create Object URL
39
+ const optimizedBlob = new Blob([optimizedBytes], { type: 'image/webp' });
40
+ const url = URL.createObjectURL(optimizedBlob);
41
+ currentUrl = url; // Store locally for cleanup
42
+ if (mounted) {
43
+ setState({
44
+ optimizedUrl: url,
45
+ loading: false,
46
+ error: null
47
+ });
48
+ }
49
+ }
50
+ catch (err) {
51
+ if (mounted) {
52
+ setState(prev => ({
53
+ ...prev,
54
+ loading: false,
55
+ error: err.message || 'Unknown error'
56
+ }));
57
+ }
58
+ }
59
+ };
60
+ process();
61
+ return () => {
62
+ mounted = false;
63
+ if (currentUrl) {
64
+ URL.revokeObjectURL(currentUrl);
65
+ }
66
+ };
67
+ }, [src, quality]);
68
+ return state;
69
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,56 @@
1
+ /** @vitest-environment jsdom */
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import { renderHook, waitFor } from '@testing-library/react';
4
+ import { useImageOptimizer } from './index';
5
+ // Mock Fetch
6
+ global.fetch = vi.fn();
7
+ // Mock URL.createObjectURL/revokeObjectURL
8
+ if (typeof window !== 'undefined') {
9
+ window.URL.createObjectURL = vi.fn(() => 'blob:mock-url');
10
+ window.URL.revokeObjectURL = vi.fn();
11
+ }
12
+ // Mock the Wasm Module import
13
+ vi.mock('../pkg/snapbolt.js', () => ({
14
+ init: vi.fn().mockResolvedValue({}),
15
+ optimize_image_sync: vi.fn().mockReturnValue(new Uint8Array([1, 2, 3])),
16
+ }));
17
+ describe('useImageOptimizer', () => {
18
+ beforeEach(() => {
19
+ vi.clearAllMocks();
20
+ global.fetch.mockReset();
21
+ });
22
+ it('should optimize image smoothly', async () => {
23
+ const mockBlob = new Blob(['test'], { type: 'image/png' });
24
+ mockBlob.arrayBuffer = vi.fn().mockResolvedValue(new ArrayBuffer(4));
25
+ global.fetch.mockResolvedValue({
26
+ ok: true,
27
+ blob: () => Promise.resolve(mockBlob),
28
+ });
29
+ const { result } = renderHook(() => useImageOptimizer('test.png', 80));
30
+ await waitFor(() => expect(result.current.loading).toBe(false), { timeout: 4000 });
31
+ expect(result.current.optimizedUrl).toBe('blob:mock-url');
32
+ expect(result.current.error).toBeNull();
33
+ });
34
+ it('should handle fetch errors', async () => {
35
+ global.fetch.mockResolvedValue({
36
+ ok: false,
37
+ statusText: 'Not Found',
38
+ });
39
+ const { result } = renderHook(() => useImageOptimizer('invalid.png', 80));
40
+ await waitFor(() => expect(result.current.loading).toBe(false), { timeout: 4000 });
41
+ expect(result.current.error).toContain('Failed to fetch image');
42
+ expect(result.current.optimizedUrl).toBeNull();
43
+ });
44
+ it('should cleanup on unmount', async () => {
45
+ const mockBlob = new Blob(['test'], { type: 'image/png' });
46
+ mockBlob.arrayBuffer = vi.fn().mockResolvedValue(new ArrayBuffer(4));
47
+ global.fetch.mockResolvedValue({
48
+ ok: true,
49
+ blob: () => Promise.resolve(mockBlob),
50
+ });
51
+ const { unmount } = renderHook(() => useImageOptimizer('cleanup.png', 80));
52
+ await waitFor(() => expect(window.URL.createObjectURL).toHaveBeenCalled(), { timeout: 3000 });
53
+ unmount();
54
+ expect(window.URL.revokeObjectURL).toHaveBeenCalled();
55
+ });
56
+ });
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@think-grid-labs/snapbolt",
3
+ "version": "0.1.3",
4
+ "description": "Unified image optimization toolkit for the browser and React",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "pkg",
11
+ "README.md"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./browser": {
20
+ "types": "./pkg/snapbolt.d.ts",
21
+ "import": "./pkg/snapbolt.js",
22
+ "default": "./pkg/snapbolt.js"
23
+ }
24
+ },
25
+ "scripts": {
26
+ "build": "npm run build:wasm && npm run build:ts",
27
+ "build:wasm": "npx wasm-pack build --target web --out-dir pkg --release",
28
+ "build:ts": "tsc",
29
+ "test": "vitest"
30
+ },
31
+ "keywords": [
32
+ "react",
33
+ "react-component",
34
+ "nextjs",
35
+ "nextjs-component",
36
+ "image-optimization",
37
+ "wasm",
38
+ "webp"
39
+ ],
40
+ "author": "DennisP <dennis@thinkgrid-labs.com>",
41
+ "license": "MIT",
42
+ "peerDependencies": {
43
+ "react": ">=16.8.0"
44
+ },
45
+ "devDependencies": {
46
+ "typescript": "^5.0.0",
47
+ "@types/react": "^18.0.0",
48
+ "concurrently": "^8.0.0",
49
+ "vitest": "^0.34.0",
50
+ "@testing-library/react": "^14.0.0",
51
+ "jsdom": "^22.0.0",
52
+ "@types/node": "^20.0.0"
53
+ },
54
+ "publishConfig": {
55
+ "access": "public"
56
+ }
57
+ }