pixel-serve-server 0.0.5 β 0.0.7
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/LICENSE +21 -21
- package/README.md +212 -212
- package/dist/assets/noavatar.avif +0 -0
- package/dist/assets/noavatar.png +0 -0
- package/dist/assets/noavatar.webp +0 -0
- package/dist/assets/noimage.avif +0 -0
- package/dist/assets/noimage.jpg +0 -0
- package/dist/assets/noimage.webp +0 -0
- package/dist/index.d.mts +14 -1
- package/dist/index.d.ts +14 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +48 -48
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025 Hiprax
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Hiprax
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,212 +1,212 @@
|
|
|
1
|
-
# Image Server Middleware
|
|
2
|
-
|
|
3
|
-
A powerful and customizable middleware for processing, resizing, and serving images in Node.js applications. Built with **TypeScript** and powered by **Sharp**, this package allows you to handle local and network images with robust error handling, fallback images, and customizable options.
|
|
4
|
-
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
## Features
|
|
8
|
-
|
|
9
|
-
- πΌοΈ **Dynamic Image Resizing and Formatting**
|
|
10
|
-
|
|
11
|
-
- Supports various formats: `jpeg`, `png`, `webp`, `gif`, `tiff`, `avif`, and `svg`.
|
|
12
|
-
- Adjustable dimensions with constraints for safety.
|
|
13
|
-
|
|
14
|
-
- π **Network and Local File Handling**
|
|
15
|
-
|
|
16
|
-
- Fetches images from allowed network domains.
|
|
17
|
-
- Processes images stored locally with safe path validation.
|
|
18
|
-
|
|
19
|
-
- π **Fallback Images**
|
|
20
|
-
|
|
21
|
-
- Provides fallback images for invalid or missing sources.
|
|
22
|
-
|
|
23
|
-
- π§ **Highly Configurable**
|
|
24
|
-
|
|
25
|
-
- Flexible option to set base directories, private folders, and user-specific paths.
|
|
26
|
-
- Supports user-defined ID handlers and folder logic.
|
|
27
|
-
|
|
28
|
-
- π **Efficient and Scalable**
|
|
29
|
-
- Built on **Sharp** for high-performance image processing.
|
|
30
|
-
- Handles concurrent requests with ease.
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Installation
|
|
35
|
-
|
|
36
|
-
Install the package using npm or yarn:
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npm install pixel-serve-server
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## Usage
|
|
45
|
-
|
|
46
|
-
### Basic Setup
|
|
47
|
-
|
|
48
|
-
Hereβs how to integrate the middleware with an Express application:
|
|
49
|
-
|
|
50
|
-
```typescript
|
|
51
|
-
import express from "express";
|
|
52
|
-
import { registerServe } from "pixel-serve-server";
|
|
53
|
-
import path, { dirname } from "node:path";
|
|
54
|
-
import { fileURLToPath } from "node:url";
|
|
55
|
-
|
|
56
|
-
const app = express();
|
|
57
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
58
|
-
const __dirname = dirname(__filename);
|
|
59
|
-
|
|
60
|
-
const BASE_IMAGE_DIR = path.join(__dirname, "../assets/images/public");
|
|
61
|
-
const PRIVATE_IMAGE_DIR = path.join(__dirname, "../assets/images/private");
|
|
62
|
-
|
|
63
|
-
const serveImage = registerServe({
|
|
64
|
-
baseDir: BASE_IMAGE_DIR, // Base directory for local images
|
|
65
|
-
idHandler: (id: string) => `user-${id}`, // Custom handler for user IDs
|
|
66
|
-
getUserFolder: async (id: string) => `/private/users/${id}`, // Logic for user-specific folder paths
|
|
67
|
-
websiteURL: "example.com", // Your website's base URL
|
|
68
|
-
apiRegex: /^\/api\/v1\//, // Regex for removing API prefixes
|
|
69
|
-
allowedNetworkList: ["trusted.com"], // List of allowed network domains
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
app.get("/api/v1/pixel/serve", serveImage);
|
|
73
|
-
|
|
74
|
-
app.listen(3000, () => {
|
|
75
|
-
console.log("Server is running on http://localhost:3000");
|
|
76
|
-
});
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
---
|
|
80
|
-
|
|
81
|
-
### Options
|
|
82
|
-
|
|
83
|
-
The `serveImage` middleware accepts the following options:
|
|
84
|
-
|
|
85
|
-
| Option | Type | Description |
|
|
86
|
-
| -------------------- | ---------- | ----------------------------------------------------------- |
|
|
87
|
-
| `baseDir` | `string` | Base directory for local image files. |
|
|
88
|
-
| `idHandler` | `Function` | Function to handle and format user IDs. |
|
|
89
|
-
| `getUserFolder` | `Function` | Async function to resolve a user-specific folder path. |
|
|
90
|
-
| `websiteURL` | `string` | Your website's base URL for identifying internal resources. |
|
|
91
|
-
| `apiRegex` | `RegExp` | Regex to strip API prefixes from internal paths. |
|
|
92
|
-
| `allowedNetworkList` | `string[]` | List of allowed domains for network images. |
|
|
93
|
-
|
|
94
|
-
---
|
|
95
|
-
|
|
96
|
-
### Example Requests
|
|
97
|
-
|
|
98
|
-
#### Fetching a Local Image
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
GET http://localhost:3000/images?src=/uploads/image1.jpg&width=300&height=300
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
#### Fetching a Network Image
|
|
105
|
-
|
|
106
|
-
```bash
|
|
107
|
-
GET http://localhost:3000/images?src=https://trusted.com/image2.jpg&format=webp
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
#### Handling Private User Folders
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
GET http://localhost:3000/images?src=/avatar.jpg&folder=private&userId=12345
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
---
|
|
117
|
-
|
|
118
|
-
### User Data Parameters
|
|
119
|
-
|
|
120
|
-
The middleware uses the following `UserData` query parameters:
|
|
121
|
-
|
|
122
|
-
| Parameter | Type | Description |
|
|
123
|
-
| --------- | ----------------------- | ---------------------------------------------------- |
|
|
124
|
-
| `src` | `string` | Path or URL to the image source. |
|
|
125
|
-
| `format` | `ImageFormat` | Desired output format (e.g., `jpeg`, `png`, `webp`). |
|
|
126
|
-
| `width` | `number` | Desired width of the output image. |
|
|
127
|
-
| `height` | `number` | Desired height of the output image. |
|
|
128
|
-
| `quality` | `number` | Image quality (1-100, default: 80). |
|
|
129
|
-
| `folder` | `'public' \| 'private'` | Image folder type (default: `public`). |
|
|
130
|
-
| `userId` | `string \| null` | User ID for private folder access. |
|
|
131
|
-
| `type` | `'normal' \| 'avatar'` | Image type (default: `normal`). |
|
|
132
|
-
|
|
133
|
-
---
|
|
134
|
-
|
|
135
|
-
### Image Formats
|
|
136
|
-
|
|
137
|
-
The following image formats are supported:
|
|
138
|
-
|
|
139
|
-
- `jpeg`
|
|
140
|
-
- `jpg`
|
|
141
|
-
- `png`
|
|
142
|
-
- `webp`
|
|
143
|
-
- `gif`
|
|
144
|
-
- `tiff`
|
|
145
|
-
- `avif`
|
|
146
|
-
- `svg`
|
|
147
|
-
|
|
148
|
-
Each format is processed with the specified quality settings.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
### Advanced Configuration
|
|
153
|
-
|
|
154
|
-
#### Custom ID Handler
|
|
155
|
-
|
|
156
|
-
Use the `idHandler` option to customize how user IDs are formatted.
|
|
157
|
-
|
|
158
|
-
```typescript
|
|
159
|
-
const options = {
|
|
160
|
-
idHandler: (id) => `user-${id.toUpperCase()}`, // Converts ID to uppercase with "user-" prefix
|
|
161
|
-
};
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
#### Resolving User Folders
|
|
165
|
-
|
|
166
|
-
The `getUserFolder` function dynamically resolves private folder paths for users.
|
|
167
|
-
|
|
168
|
-
```typescript
|
|
169
|
-
const options = {
|
|
170
|
-
getUserFolder: async (id) => `/private/data/users/${id}`, // Returns a private directory path
|
|
171
|
-
};
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
#### Allowed Network Domains
|
|
175
|
-
|
|
176
|
-
Whitelist trusted domains for fetching network images.
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const options = {
|
|
180
|
-
allowedNetworkList: ["example.com", "cdn.example.com"], // Only allows images from these domains
|
|
181
|
-
};
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
---
|
|
185
|
-
|
|
186
|
-
### Error Handling
|
|
187
|
-
|
|
188
|
-
The middleware automatically falls back to pre-defined images for errors:
|
|
189
|
-
|
|
190
|
-
| Error Condition | Fallback Behavior |
|
|
191
|
-
| ----------------------------- | ------------------------------- |
|
|
192
|
-
| Invalid local path | Returns a fallback image. |
|
|
193
|
-
| Unsupported network domain | Returns a fallback image. |
|
|
194
|
-
| Invalid or missing parameters | Defaults to placeholder values. |
|
|
195
|
-
|
|
196
|
-
### Dependencies
|
|
197
|
-
|
|
198
|
-
This package uses the following dependencies:
|
|
199
|
-
|
|
200
|
-
- **Express**: HTTP server framework.
|
|
201
|
-
- **Sharp**: High-performance image processing.
|
|
202
|
-
- **Axios**: HTTP client for fetching network images.
|
|
203
|
-
|
|
204
|
-
---
|
|
205
|
-
|
|
206
|
-
### License
|
|
207
|
-
|
|
208
|
-
This package is licensed under the [MIT License](LICENSE).
|
|
209
|
-
|
|
210
|
-
### Feedback
|
|
211
|
-
|
|
212
|
-
If you encounter issues or have suggestions, feel free to open an [issue](https://github.com/Hiprax/pixel-serve-server/issues).
|
|
1
|
+
# Image Server Middleware
|
|
2
|
+
|
|
3
|
+
A powerful and customizable middleware for processing, resizing, and serving images in Node.js applications. Built with **TypeScript** and powered by **Sharp**, this package allows you to handle local and network images with robust error handling, fallback images, and customizable options.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- πΌοΈ **Dynamic Image Resizing and Formatting**
|
|
10
|
+
|
|
11
|
+
- Supports various formats: `jpeg`, `png`, `webp`, `gif`, `tiff`, `avif`, and `svg`.
|
|
12
|
+
- Adjustable dimensions with constraints for safety.
|
|
13
|
+
|
|
14
|
+
- π **Network and Local File Handling**
|
|
15
|
+
|
|
16
|
+
- Fetches images from allowed network domains.
|
|
17
|
+
- Processes images stored locally with safe path validation.
|
|
18
|
+
|
|
19
|
+
- π **Fallback Images**
|
|
20
|
+
|
|
21
|
+
- Provides fallback images for invalid or missing sources.
|
|
22
|
+
|
|
23
|
+
- π§ **Highly Configurable**
|
|
24
|
+
|
|
25
|
+
- Flexible option to set base directories, private folders, and user-specific paths.
|
|
26
|
+
- Supports user-defined ID handlers and folder logic.
|
|
27
|
+
|
|
28
|
+
- π **Efficient and Scalable**
|
|
29
|
+
- Built on **Sharp** for high-performance image processing.
|
|
30
|
+
- Handles concurrent requests with ease.
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
Install the package using npm or yarn:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install pixel-serve-server
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
### Basic Setup
|
|
47
|
+
|
|
48
|
+
Hereβs how to integrate the middleware with an Express application:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import express from "express";
|
|
52
|
+
import { registerServe } from "pixel-serve-server";
|
|
53
|
+
import path, { dirname } from "node:path";
|
|
54
|
+
import { fileURLToPath } from "node:url";
|
|
55
|
+
|
|
56
|
+
const app = express();
|
|
57
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
58
|
+
const __dirname = dirname(__filename);
|
|
59
|
+
|
|
60
|
+
const BASE_IMAGE_DIR = path.join(__dirname, "../assets/images/public");
|
|
61
|
+
const PRIVATE_IMAGE_DIR = path.join(__dirname, "../assets/images/private");
|
|
62
|
+
|
|
63
|
+
const serveImage = registerServe({
|
|
64
|
+
baseDir: BASE_IMAGE_DIR, // Base directory for local images
|
|
65
|
+
idHandler: (id: string) => `user-${id}`, // Custom handler for user IDs
|
|
66
|
+
getUserFolder: async (id: string) => `/private/users/${id}`, // Logic for user-specific folder paths
|
|
67
|
+
websiteURL: "example.com", // Your website's base URL
|
|
68
|
+
apiRegex: /^\/api\/v1\//, // Regex for removing API prefixes
|
|
69
|
+
allowedNetworkList: ["trusted.com"], // List of allowed network domains
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
app.get("/api/v1/pixel/serve", serveImage);
|
|
73
|
+
|
|
74
|
+
app.listen(3000, () => {
|
|
75
|
+
console.log("Server is running on http://localhost:3000");
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
### Options
|
|
82
|
+
|
|
83
|
+
The `serveImage` middleware accepts the following options:
|
|
84
|
+
|
|
85
|
+
| Option | Type | Description |
|
|
86
|
+
| -------------------- | ---------- | ----------------------------------------------------------- |
|
|
87
|
+
| `baseDir` | `string` | Base directory for local image files. |
|
|
88
|
+
| `idHandler` | `Function` | Function to handle and format user IDs. |
|
|
89
|
+
| `getUserFolder` | `Function` | Async function to resolve a user-specific folder path. |
|
|
90
|
+
| `websiteURL` | `string` | Your website's base URL for identifying internal resources. |
|
|
91
|
+
| `apiRegex` | `RegExp` | Regex to strip API prefixes from internal paths. |
|
|
92
|
+
| `allowedNetworkList` | `string[]` | List of allowed domains for network images. |
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
### Example Requests
|
|
97
|
+
|
|
98
|
+
#### Fetching a Local Image
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
GET http://localhost:3000/images?src=/uploads/image1.jpg&width=300&height=300
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Fetching a Network Image
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
GET http://localhost:3000/images?src=https://trusted.com/image2.jpg&format=webp
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### Handling Private User Folders
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
GET http://localhost:3000/images?src=/avatar.jpg&folder=private&userId=12345
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### User Data Parameters
|
|
119
|
+
|
|
120
|
+
The middleware uses the following `UserData` query parameters:
|
|
121
|
+
|
|
122
|
+
| Parameter | Type | Description |
|
|
123
|
+
| --------- | ----------------------- | ---------------------------------------------------- |
|
|
124
|
+
| `src` | `string` | Path or URL to the image source. |
|
|
125
|
+
| `format` | `ImageFormat` | Desired output format (e.g., `jpeg`, `png`, `webp`). |
|
|
126
|
+
| `width` | `number` | Desired width of the output image. |
|
|
127
|
+
| `height` | `number` | Desired height of the output image. |
|
|
128
|
+
| `quality` | `number` | Image quality (1-100, default: 80). |
|
|
129
|
+
| `folder` | `'public' \| 'private'` | Image folder type (default: `public`). |
|
|
130
|
+
| `userId` | `string \| null` | User ID for private folder access. |
|
|
131
|
+
| `type` | `'normal' \| 'avatar'` | Image type (default: `normal`). |
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
### Image Formats
|
|
136
|
+
|
|
137
|
+
The following image formats are supported:
|
|
138
|
+
|
|
139
|
+
- `jpeg`
|
|
140
|
+
- `jpg`
|
|
141
|
+
- `png`
|
|
142
|
+
- `webp`
|
|
143
|
+
- `gif`
|
|
144
|
+
- `tiff`
|
|
145
|
+
- `avif`
|
|
146
|
+
- `svg`
|
|
147
|
+
|
|
148
|
+
Each format is processed with the specified quality settings.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
### Advanced Configuration
|
|
153
|
+
|
|
154
|
+
#### Custom ID Handler
|
|
155
|
+
|
|
156
|
+
Use the `idHandler` option to customize how user IDs are formatted.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const options = {
|
|
160
|
+
idHandler: (id) => `user-${id.toUpperCase()}`, // Converts ID to uppercase with "user-" prefix
|
|
161
|
+
};
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### Resolving User Folders
|
|
165
|
+
|
|
166
|
+
The `getUserFolder` function dynamically resolves private folder paths for users.
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
const options = {
|
|
170
|
+
getUserFolder: async (id) => `/private/data/users/${id}`, // Returns a private directory path
|
|
171
|
+
};
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
#### Allowed Network Domains
|
|
175
|
+
|
|
176
|
+
Whitelist trusted domains for fetching network images.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const options = {
|
|
180
|
+
allowedNetworkList: ["example.com", "cdn.example.com"], // Only allows images from these domains
|
|
181
|
+
};
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
### Error Handling
|
|
187
|
+
|
|
188
|
+
The middleware automatically falls back to pre-defined images for errors:
|
|
189
|
+
|
|
190
|
+
| Error Condition | Fallback Behavior |
|
|
191
|
+
| ----------------------------- | ------------------------------- |
|
|
192
|
+
| Invalid local path | Returns a fallback image. |
|
|
193
|
+
| Unsupported network domain | Returns a fallback image. |
|
|
194
|
+
| Invalid or missing parameters | Defaults to placeholder values. |
|
|
195
|
+
|
|
196
|
+
### Dependencies
|
|
197
|
+
|
|
198
|
+
This package uses the following dependencies:
|
|
199
|
+
|
|
200
|
+
- **Express**: HTTP server framework.
|
|
201
|
+
- **Sharp**: High-performance image processing.
|
|
202
|
+
- **Axios**: HTTP client for fetching network images.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
### License
|
|
207
|
+
|
|
208
|
+
This package is licensed under the [MIT License](LICENSE).
|
|
209
|
+
|
|
210
|
+
### Feedback
|
|
211
|
+
|
|
212
|
+
If you encounter issues or have suggestions, feel free to open an [issue](https://github.com/Hiprax/pixel-serve-server/issues).
|
|
File without changes
|
package/dist/assets/noavatar.png
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/assets/noimage.avif
CHANGED
|
File without changes
|
package/dist/assets/noimage.jpg
CHANGED
|
File without changes
|
package/dist/assets/noimage.webp
CHANGED
|
File without changes
|
package/dist/index.d.mts
CHANGED
|
@@ -29,4 +29,17 @@ type UserData = {
|
|
|
29
29
|
*/
|
|
30
30
|
declare const registerServe: (options: Options) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {("avatar" | "normal")} ImageType
|
|
34
|
+
* @description Defines the type of image being processed.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Checks if a specified path is valid within a base path.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} basePath - The base directory to resolve paths.
|
|
40
|
+
* @param {string} specifiedPath - The path to check.
|
|
41
|
+
* @returns {boolean} True if the path is valid, false otherwise.
|
|
42
|
+
*/
|
|
43
|
+
declare const isValidPath: (basePath: string, specifiedPath: string) => Promise<boolean>;
|
|
44
|
+
|
|
45
|
+
export { type ImageFormat, type ImageType, type Options, type UserData, isValidPath, registerServe };
|
package/dist/index.d.ts
CHANGED
|
@@ -29,4 +29,17 @@ type UserData = {
|
|
|
29
29
|
*/
|
|
30
30
|
declare const registerServe: (options: Options) => (req: Request, res: Response, next: NextFunction) => Promise<void>;
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
/**
|
|
33
|
+
* @typedef {("avatar" | "normal")} ImageType
|
|
34
|
+
* @description Defines the type of image being processed.
|
|
35
|
+
*/
|
|
36
|
+
/**
|
|
37
|
+
* Checks if a specified path is valid within a base path.
|
|
38
|
+
*
|
|
39
|
+
* @param {string} basePath - The base directory to resolve paths.
|
|
40
|
+
* @param {string} specifiedPath - The path to check.
|
|
41
|
+
* @returns {boolean} True if the path is valid, false otherwise.
|
|
42
|
+
*/
|
|
43
|
+
declare const isValidPath: (basePath: string, specifiedPath: string) => Promise<boolean>;
|
|
44
|
+
|
|
45
|
+
export { type ImageFormat, type ImageType, type Options, type UserData, isValidPath, registerServe };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var S=Object.create;var h=Object.defineProperty;var _=Object.getOwnPropertyDescriptor;var B=Object.getOwnPropertyNames;var C=Object.getPrototypeOf,z=Object.prototype.hasOwnProperty;var G=(e,r)=>{for(var a in r)h(e,a,{get:r[a],enumerable:!0})},O=(e,r,a,s)=>{if(r&&typeof r=="object"||typeof r=="function")for(let t of B(r))!z.call(e,t)&&t!==a&&h(e,t,{get:()=>r[t],enumerable:!(s=_(r,t))||s.enumerable});return e};var c=(e,r,a)=>(a=e!=null?S(C(e)):{},O(r||!e||!e.__esModule?h(a,"default",{value:e,enumerable:!0}):a,e)),$=e=>O(h({},"__esModule",{value:!0}),e);var Q={};G(Q,{isValidPath:()=>b,registerServe:()=>D});module.exports=$(Q);var k=()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.src||new URL("main.js",document.baseURI).href,l=k();var v=c(require("path")),U=c(require("sharp"));var x=require("fs/promises"),R=c(require("path")),T=require("url"),H=R.default.dirname((0,T.fileURLToPath)(l)),L=e=>R.default.join(H,"assets",e),V=L("noimage.jpg"),W=L("noavatar.png"),u={normal:async()=>(0,x.readFile)(V),avatar:async()=>(0,x.readFile)(W)},w=/^\/api\/v1\//,j=["jpeg","jpg","png","webp","gif","tiff","avif","svg"],I={jpeg:"image/jpeg",jpg:"image/jpeg",png:"image/png",webp:"image/webp",gif:"image/gif",tiff:"image/tiff",avif:"image/avif",svg:"image/svg+xml"};var o=c(require("path")),g=c(require("fs/promises")),A=c(require("axios"));var b=async(e,r)=>{try{if(!e||!r||r.includes("\0")||o.default.isAbsolute(r)||!/^[^\x00-\x1F]+$/.test(r))return!1;let a=o.default.resolve(e),s=o.default.resolve(a,r),[t,i]=await Promise.all([g.realpath(a),g.realpath(s)]);if(!(await g.stat(t)).isDirectory())return!1;let f=t+o.default.sep,y=(i+o.default.sep).startsWith(f)||i===t,d=o.default.relative(t,i);return!d.startsWith("..")&&!o.default.isAbsolute(d)&&y}catch{return!1}},X=async(e,r="normal")=>{try{let a=await A.default.get(e,{responseType:"arraybuffer",timeout:5e3}),s=a.headers["content-type"]?.toLowerCase();return Object.values(I).includes(s??"")?Buffer.from(a.data):await u[r]()}catch{return await u[r]()}},F=async(e,r,a="normal")=>{if(!await b(r,e))return await u[a]();try{return await g.readFile(o.default.resolve(r,e))}catch{return await u[a]()}},N=(e,r,a,s="normal",t=w,i=[])=>{let n=new URL(e);if([a,`www.${a}`].includes(n.host)){let m=n.pathname.replace(t,"");return F(m,r,s)}else return i.includes(n.host)?X(e,s):u[s]()};var q=e=>({...{baseDir:"",idHandler:a=>a,getUserFolder:async()=>"",websiteURL:"",apiRegex:w,allowedNetworkList:[]},...e}),E=e=>({...{quality:80,format:"jpeg",src:"/placeholder/noimage.jpg",folder:"public",type:"normal",width:void 0,height:void 0,userId:void 0},...e,quality:e.quality?Math.min(Math.max(Number(e.quality)||80,1),100):100,width:e.width?Math.min(Math.max(Number(e.width),50),2e3):void 0,height:e.height?Math.min(Math.max(Number(e.height),50),2e3):void 0});var K=async(e,r,a,s)=>{try{let t=E(e.query),i=q(s),n,f=i.baseDir,m;if(t.userId){let p=typeof t.userId=="object"?String(Object.values(t.userId)[0]):String(t.userId);i.idHandler?m=i.idHandler(p):m=p}if(t.folder==="private"){let p=await i?.getUserFolder?.(e,m);p&&(f=p)}let y=j.includes(t?.format?.toLowerCase())?t?.format?.toLowerCase():"jpeg";t?.src?.startsWith("http")?n=await N(t?.src??"",f,i?.websiteURL??"",t?.type,i?.apiRegex,i?.allowedNetworkList):n=await F(t?.src??"",f,t?.type);let d=(0,U.default)(n);if(t?.width||t?.height){let p={width:t?.width??void 0,height:t?.height??void 0,fit:U.default.fit.cover};d=d.resize(p)}let M=await d.toFormat(y,{quality:t?.quality?Number(t?.quality):80}).toBuffer(),P=`${v.default.basename(t?.src??"",v.default.extname(t?.src??""))}.${y}`;r.type(I[y]),r.setHeader("Content-Disposition",`inline; filename="${P}"`),r.send(M)}catch(t){a(t)}},J=e=>async(r,a,s)=>K(r,a,s,e),D=J;0&&(module.exports={isValidPath,registerServe});
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../node_modules/tsup/assets/cjs_shims.js","../src/pixel.ts","../src/variables.ts","../src/functions.ts","../src/renders.ts"],"sourcesContent":["/**\n * @module ImageService\n * @description A module to serve, process, and manage image delivery for web applications.\n */\n\nexport { default as registerServe } from \"./pixel\";\nexport * from \"./types\";\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import path from \"node:path\";\nimport sharp, { FormatEnum, ResizeOptions } from \"sharp\";\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { Options, UserData, ImageFormat, ImageType } from \"./types\";\nimport { allowedFormats, mimeTypes } from \"./variables\";\nimport { fetchImage, readLocalImage } from \"./functions\";\nimport { renderOptions, renderUserData } from \"./renders\";\n\n/**\n * @typedef {Object} Options\n * @property {string} baseDir - The base directory for public image files.\n * @property {function(string): string} idHandler - A function to handle user IDs.\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\n */\n\n/**\n * @function serveImage\n * @description Processes and serves an image based on user data and options.\n * @param {Request} req - The Express request object.\n * @param {Response} res - The Express response object.\n * @param {NextFunction} next - The Express next function.\n * @param {Options} options - The options object for image processing.\n * @returns {Promise<void>}\n */\nconst serveImage = async (\n req: Request,\n res: Response,\n next: NextFunction,\n options: Options\n) => {\n try {\n const userData = renderUserData(req.query as UserData);\n const parsedOptions = renderOptions(options);\n\n let imageBuffer;\n let baseDir = parsedOptions.baseDir;\n let parsedUserId;\n\n if (userData.userId) {\n const userIdStr =\n typeof userData.userId === \"object\"\n ? String(Object.values(userData.userId)[0])\n : String(userData.userId);\n if (parsedOptions.idHandler) {\n parsedUserId = parsedOptions.idHandler(userIdStr);\n } else {\n parsedUserId = userIdStr;\n }\n }\n\n if (userData.folder === \"private\") {\n const dir = await parsedOptions?.getUserFolder?.(req, parsedUserId);\n if (dir) {\n baseDir = dir;\n }\n }\n\n const outputFormat = allowedFormats.includes(\n userData?.format?.toLowerCase() as ImageFormat\n )\n ? userData?.format?.toLowerCase()\n : \"jpeg\";\n\n if (userData?.src?.startsWith(\"http\")) {\n imageBuffer = await fetchImage(\n userData?.src ?? \"\",\n baseDir,\n parsedOptions?.websiteURL ?? \"\",\n userData?.type as ImageType,\n parsedOptions?.apiRegex,\n parsedOptions?.allowedNetworkList\n );\n } else {\n imageBuffer = await readLocalImage(\n userData?.src ?? \"\",\n baseDir,\n userData?.type as ImageType\n );\n }\n\n let image = sharp(imageBuffer);\n\n if (userData?.width || userData?.height) {\n const resizeOptions = {\n width: userData?.width ?? undefined,\n height: userData?.height ?? undefined,\n fit: sharp.fit.cover,\n };\n image = image.resize(resizeOptions as ResizeOptions);\n }\n\n const processedImage = await image\n .toFormat(outputFormat as keyof FormatEnum, {\n quality: userData?.quality ? Number(userData?.quality) : 80,\n })\n .toBuffer();\n\n const processedFileName = `${path.basename(\n userData?.src ?? \"\",\n path.extname(userData?.src ?? \"\")\n )}.${outputFormat}`;\n\n res.type(mimeTypes[outputFormat]);\n res.setHeader(\n \"Content-Disposition\",\n `inline; filename=\"${processedFileName}\"`\n );\n res.send(processedImage);\n } catch (error) {\n next(error);\n }\n};\n\n/**\n * @function registerServe\n * @description A function to register the serveImage function as middleware for Express.\n * @param {Options} options - The options object for image processing.\n * @returns {function(Request, Response, NextFunction): Promise<void>} The middleware function.\n */\nconst registerServe = (options: Options) => {\n return async (req: Request, res: Response, next: NextFunction) =>\n serveImage(req, res, next, options);\n};\n\nexport default registerServe;\n","import type { ImageFormat } from \"./types\";\nimport { readFile } from \"node:fs/promises\";\n\nconst NOT_FOUND_IMAGE = new URL(\"./assets/noimage.jpg\", import.meta.url)\n .pathname;\n\nconst NOT_FOUND_AVATAR = new URL(\"./assets/noavatar.png\", import.meta.url)\n .pathname;\n\nexport const FALLBACKIMAGES = {\n normal: async () => readFile(NOT_FOUND_IMAGE),\n avatar: async () => readFile(NOT_FOUND_AVATAR),\n};\n\nexport const API_REGEX: RegExp = /^\\/api\\/v1\\//;\n\nexport const allowedFormats: ImageFormat[] = [\n \"jpeg\",\n \"jpg\",\n \"png\",\n \"webp\",\n \"gif\",\n \"tiff\",\n \"avif\",\n \"svg\",\n];\n\nexport const mimeTypes: Readonly<Record<string, string>> = {\n jpeg: \"image/jpeg\",\n jpg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n tiff: \"image/tiff\",\n avif: \"image/avif\",\n svg: \"image/svg+xml\",\n};\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport axios from \"axios\";\nimport { mimeTypes, API_REGEX, FALLBACKIMAGES } from \"./variables\";\nimport type { ImageType } from \"./types\";\n\n/**\n * @typedef {(\"avatar\" | \"normal\")} ImageType\n * @description Defines the type of image being processed.\n */\n\n/**\n * Checks if a specified path is valid within a base path.\n *\n * @param {string} basePath - The base directory to resolve paths.\n * @param {string} specifiedPath - The path to check.\n * @returns {boolean} True if the path is valid, false otherwise.\n */\nconst isValidPath = (basePath: string, specifiedPath: string): boolean => {\n if (!basePath || !specifiedPath) return false;\n const resolvedBase = path.resolve(basePath);\n const resolvedPath = path.resolve(resolvedBase, specifiedPath);\n return resolvedPath.startsWith(resolvedBase) && existsSync(resolvedPath);\n};\n\n/**\n * Fetches an image from a network source.\n *\n * @param {string} src - The URL of the image.\n * @param {ImageType} [type=\"normal\"] - Type of fallback image in case of an error.\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\n */\nconst fetchFromNetwork = async (\n src: string,\n type: ImageType = \"normal\"\n): Promise<Buffer> => {\n try {\n const response = await axios.get(src, {\n responseType: \"arraybuffer\",\n timeout: 5000,\n });\n\n const contentType = response.headers[\"content-type\"]?.toLowerCase();\n const allowedMimeTypes = Object.values(mimeTypes);\n\n if (allowedMimeTypes.includes(contentType ?? \"\")) {\n return Buffer.from(response.data);\n }\n return await FALLBACKIMAGES[type]();\n } catch (error) {\n return await FALLBACKIMAGES[type]();\n }\n};\n\n/**\n * Reads an image from the local file system.\n *\n * @param {string} filePath - Path to the image file.\n * @param {string} baseDir - Base directory to resolve paths.\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\n * @returns {Promise<Buffer>} A buffer containing the image data.\n */\nexport const readLocalImage = async (\n filePath: string,\n baseDir: string,\n type: ImageType = \"normal\"\n) => {\n if (!isValidPath(baseDir, filePath)) {\n return await FALLBACKIMAGES[type]();\n }\n try {\n return await fs.readFile(path.resolve(baseDir, filePath));\n } catch (error) {\n return await FALLBACKIMAGES[type]();\n }\n};\n\n/**\n * Fetches an image from either a local file or a network source.\n *\n * @param {string} src - The URL or local path of the image.\n * @param {string} baseDir - Base directory to resolve local paths.\n * @param {string} websiteURL - The URL of the website.\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\n * @param {RegExp} [apiRegex=API_REGEX] - Regular expression to match API routes.\n * @param {string[]} [allowedNetworkList=[]] - List of allowed network hosts.\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\n */\nexport const fetchImage = (\n src: string,\n baseDir: string,\n websiteURL: string,\n type: ImageType = \"normal\",\n apiRegex: RegExp = API_REGEX,\n allowedNetworkList: string[] = []\n) => {\n const url = new URL(src);\n const isInternal = [websiteURL, `www.${websiteURL}`].includes(url.host);\n if (isInternal) {\n const localPath = url.pathname.replace(apiRegex, \"\");\n return readLocalImage(localPath, baseDir, type);\n } else {\n const allowedCondition = allowedNetworkList.includes(url.host);\n if (!allowedCondition) {\n return FALLBACKIMAGES[type]();\n }\n return fetchFromNetwork(src, type);\n }\n};\n","import { API_REGEX } from \"./variables\";\nimport type { Options, UserData } from \"./types\";\n\n/**\n * @typedef {(\"avatar\" | \"normal\")} ImageType\n * @description Defines the type of image being processed.\n */\n\n/**\n * @typedef {(\"jpeg\" | \"jpg\" | \"png\" | \"webp\" | \"gif\" | \"tiff\" | \"avif\" | \"svg\")} ImageFormat\n * @description Supported formats for image processing.\n */\n\n/**\n * @typedef {Object} Options\n * @property {string} baseDir - The base directory for public image files.\n * @property {function(string): string} idHandler - A function to handle user IDs.\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\n */\n\n/**\n * @typedef {Object} UserData\n * @property {number|string} quality - Quality of the image (1β100).\n * @property {ImageFormat} format - Desired format of the image.\n * @property {string} [src] - Source path or URL for the image.\n * @property {string} [folder] - The folder type (\"public\" or \"private\").\n * @property {ImageType} [type] - Type of the image (\"avatar\" or \"normal\").\n * @property {string|null} [userId] - Optional user identifier.\n * @property {number|string} [width] - Desired image width.\n * @property {number|string} [height] - Desired image height.\n */\n\n/**\n * Renders the options object with default values and user-provided values.\n *\n * @param {Partial<Options>} options - The user-provided options.\n * @returns {Options} The rendered options object.\n */\nexport const renderOptions = (options: Partial<Options>): Options => {\n const initialOptions: Options = {\n baseDir: \"\",\n idHandler: (id: string) => id,\n getUserFolder: async () => \"\",\n websiteURL: \"\",\n apiRegex: API_REGEX,\n allowedNetworkList: [],\n };\n return {\n ...initialOptions,\n ...options,\n };\n};\n\n/**\n * Renders the user data object with default values and user-provided values.\n *\n * @param {Partial<UserData>} userData - The user-provided data.\n * @returns {UserData} The rendered user data object.\n */\nexport const renderUserData = (userData: Partial<UserData>): UserData => {\n const initialUserData: UserData = {\n quality: 80,\n format: \"jpeg\",\n src: \"/placeholder/noimage.jpg\",\n folder: \"public\",\n type: \"normal\",\n width: undefined,\n height: undefined,\n userId: undefined,\n };\n return {\n ...initialUserData,\n ...userData,\n quality: userData.quality\n ? Math.min(Math.max(Number(userData.quality) || 80, 1), 100)\n : 100,\n width: userData.width\n ? Math.min(Math.max(Number(userData.width), 50), 2000)\n : undefined,\n height: userData.height\n ? Math.min(Math.max(Number(userData.height), 50), 2000)\n : undefined,\n };\n};\n"],"mappings":"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","names":["index_exports","__export","pixel_default","__toCommonJS","getImportMetaUrl","importMetaUrl","import_node_path","import_sharp","import_promises","NOT_FOUND_IMAGE","importMetaUrl","NOT_FOUND_AVATAR","FALLBACKIMAGES","API_REGEX","allowedFormats","mimeTypes","import_node_path","import_node_fs","fs","import_axios","isValidPath","basePath","specifiedPath","resolvedBase","path","resolvedPath","fetchFromNetwork","src","type","response","axios","contentType","mimeTypes","FALLBACKIMAGES","readLocalImage","filePath","baseDir","fetchImage","websiteURL","apiRegex","API_REGEX","allowedNetworkList","url","localPath","renderOptions","options","id","API_REGEX","renderUserData","userData","serveImage","req","res","next","options","userData","renderUserData","parsedOptions","renderOptions","imageBuffer","baseDir","parsedUserId","userIdStr","dir","outputFormat","allowedFormats","fetchImage","readLocalImage","image","sharp","resizeOptions","processedImage","processedFileName","path","mimeTypes","error","registerServe","pixel_default"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../node_modules/tsup/assets/cjs_shims.js","../src/pixel.ts","../src/variables.ts","../src/functions.ts","../src/renders.ts"],"sourcesContent":["/**\r\n * @module ImageService\r\n * @description A module to serve, process, and manage image delivery for web applications.\r\n */\r\n\r\nexport { default as registerServe } from \"./pixel\";\r\nexport * from \"./types\";\r\nexport { isValidPath } from \"./functions\";\r\n","// Shim globals in cjs bundle\n// There's a weird bug that esbuild will always inject importMetaUrl\n// if we export it as `const importMetaUrl = ... __filename ...`\n// But using a function will not cause this issue\n\nconst getImportMetaUrl = () =>\n typeof document === 'undefined'\n ? new URL(`file:${__filename}`).href\n : (document.currentScript && document.currentScript.src) ||\n new URL('main.js', document.baseURI).href\n\nexport const importMetaUrl = /* @__PURE__ */ getImportMetaUrl()\n","import path from \"node:path\";\r\nimport sharp, { FormatEnum, ResizeOptions } from \"sharp\";\r\nimport type { Request, Response, NextFunction } from \"express\";\r\nimport type { Options, UserData, ImageFormat, ImageType } from \"./types\";\r\nimport { allowedFormats, mimeTypes } from \"./variables\";\r\nimport { fetchImage, readLocalImage } from \"./functions\";\r\nimport { renderOptions, renderUserData } from \"./renders\";\r\n\r\n/**\r\n * @typedef {Object} Options\r\n * @property {string} baseDir - The base directory for public image files.\r\n * @property {function(string): string} idHandler - A function to handle user IDs.\r\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\r\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\r\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\r\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\r\n */\r\n\r\n/**\r\n * @function serveImage\r\n * @description Processes and serves an image based on user data and options.\r\n * @param {Request} req - The Express request object.\r\n * @param {Response} res - The Express response object.\r\n * @param {NextFunction} next - The Express next function.\r\n * @param {Options} options - The options object for image processing.\r\n * @returns {Promise<void>}\r\n */\r\nconst serveImage = async (\r\n req: Request,\r\n res: Response,\r\n next: NextFunction,\r\n options: Options\r\n) => {\r\n try {\r\n const userData = renderUserData(req.query as UserData);\r\n const parsedOptions = renderOptions(options);\r\n\r\n let imageBuffer;\r\n let baseDir = parsedOptions.baseDir;\r\n let parsedUserId;\r\n\r\n if (userData.userId) {\r\n const userIdStr =\r\n typeof userData.userId === \"object\"\r\n ? String(Object.values(userData.userId)[0])\r\n : String(userData.userId);\r\n if (parsedOptions.idHandler) {\r\n parsedUserId = parsedOptions.idHandler(userIdStr);\r\n } else {\r\n parsedUserId = userIdStr;\r\n }\r\n }\r\n\r\n if (userData.folder === \"private\") {\r\n const dir = await parsedOptions?.getUserFolder?.(req, parsedUserId);\r\n if (dir) {\r\n baseDir = dir;\r\n }\r\n }\r\n\r\n const outputFormat = allowedFormats.includes(\r\n userData?.format?.toLowerCase() as ImageFormat\r\n )\r\n ? userData?.format?.toLowerCase()\r\n : \"jpeg\";\r\n\r\n if (userData?.src?.startsWith(\"http\")) {\r\n imageBuffer = await fetchImage(\r\n userData?.src ?? \"\",\r\n baseDir,\r\n parsedOptions?.websiteURL ?? \"\",\r\n userData?.type as ImageType,\r\n parsedOptions?.apiRegex,\r\n parsedOptions?.allowedNetworkList\r\n );\r\n } else {\r\n imageBuffer = await readLocalImage(\r\n userData?.src ?? \"\",\r\n baseDir,\r\n userData?.type as ImageType\r\n );\r\n }\r\n\r\n let image = sharp(imageBuffer);\r\n\r\n if (userData?.width || userData?.height) {\r\n const resizeOptions = {\r\n width: userData?.width ?? undefined,\r\n height: userData?.height ?? undefined,\r\n fit: sharp.fit.cover,\r\n };\r\n image = image.resize(resizeOptions as ResizeOptions);\r\n }\r\n\r\n const processedImage = await image\r\n .toFormat(outputFormat as keyof FormatEnum, {\r\n quality: userData?.quality ? Number(userData?.quality) : 80,\r\n })\r\n .toBuffer();\r\n\r\n const processedFileName = `${path.basename(\r\n userData?.src ?? \"\",\r\n path.extname(userData?.src ?? \"\")\r\n )}.${outputFormat}`;\r\n\r\n res.type(mimeTypes[outputFormat]);\r\n res.setHeader(\r\n \"Content-Disposition\",\r\n `inline; filename=\"${processedFileName}\"`\r\n );\r\n res.send(processedImage);\r\n } catch (error) {\r\n next(error);\r\n }\r\n};\r\n\r\n/**\r\n * @function registerServe\r\n * @description A function to register the serveImage function as middleware for Express.\r\n * @param {Options} options - The options object for image processing.\r\n * @returns {function(Request, Response, NextFunction): Promise<void>} The middleware function.\r\n */\r\nconst registerServe = (options: Options) => {\r\n return async (req: Request, res: Response, next: NextFunction) =>\r\n serveImage(req, res, next, options);\r\n};\r\n\r\nexport default registerServe;\r\n","import type { ImageFormat } from \"./types\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\nconst getAssetPath = (filename: string) => {\r\n return path.join(__dirname, \"assets\", filename);\r\n};\r\n\r\nconst NOT_FOUND_IMAGE = getAssetPath(\"noimage.jpg\");\r\nconst NOT_FOUND_AVATAR = getAssetPath(\"noavatar.png\");\r\n\r\nexport const FALLBACKIMAGES = {\r\n normal: async () => readFile(NOT_FOUND_IMAGE),\r\n avatar: async () => readFile(NOT_FOUND_AVATAR),\r\n};\r\n\r\nexport const API_REGEX: RegExp = /^\\/api\\/v1\\//;\r\n\r\nexport const allowedFormats: ImageFormat[] = [\r\n \"jpeg\",\r\n \"jpg\",\r\n \"png\",\r\n \"webp\",\r\n \"gif\",\r\n \"tiff\",\r\n \"avif\",\r\n \"svg\",\r\n];\r\n\r\nexport const mimeTypes: Readonly<Record<string, string>> = {\r\n jpeg: \"image/jpeg\",\r\n jpg: \"image/jpeg\",\r\n png: \"image/png\",\r\n webp: \"image/webp\",\r\n gif: \"image/gif\",\r\n tiff: \"image/tiff\",\r\n avif: \"image/avif\",\r\n svg: \"image/svg+xml\",\r\n};\r\n","import path from \"node:path\";\r\nimport * as fs from \"node:fs/promises\";\r\nimport axios from \"axios\";\r\nimport { mimeTypes, API_REGEX, FALLBACKIMAGES } from \"./variables\";\r\nimport type { ImageType } from \"./types\";\r\n\r\n/**\r\n * @typedef {(\"avatar\" | \"normal\")} ImageType\r\n * @description Defines the type of image being processed.\r\n */\r\n\r\n/**\r\n * Checks if a specified path is valid within a base path.\r\n *\r\n * @param {string} basePath - The base directory to resolve paths.\r\n * @param {string} specifiedPath - The path to check.\r\n * @returns {boolean} True if the path is valid, false otherwise.\r\n */\r\nexport const isValidPath = async (\r\n basePath: string,\r\n specifiedPath: string\r\n): Promise<boolean> => {\r\n try {\r\n if (!basePath || !specifiedPath) return false;\r\n if (specifiedPath.includes(\"\\0\")) return false;\r\n if (path.isAbsolute(specifiedPath)) return false;\r\n if (!/^[^\\x00-\\x1F]+$/.test(specifiedPath)) return false;\r\n\r\n const resolvedBase = path.resolve(basePath);\r\n const resolvedPath = path.resolve(resolvedBase, specifiedPath);\r\n\r\n const [realBase, realPath] = await Promise.all([\r\n fs.realpath(resolvedBase),\r\n fs.realpath(resolvedPath),\r\n ]);\r\n\r\n const baseStats = await fs.stat(realBase);\r\n if (!baseStats.isDirectory()) return false;\r\n\r\n const normalizedBase = realBase + path.sep;\r\n const normalizedPath = realPath + path.sep;\r\n\r\n const isInside =\r\n normalizedPath.startsWith(normalizedBase) || realPath === realBase;\r\n\r\n const relative = path.relative(realBase, realPath);\r\n return !relative.startsWith(\"..\") && !path.isAbsolute(relative) && isInside;\r\n } catch {\r\n return false;\r\n }\r\n};\r\n\r\n/**\r\n * Fetches an image from a network source.\r\n *\r\n * @param {string} src - The URL of the image.\r\n * @param {ImageType} [type=\"normal\"] - Type of fallback image in case of an error.\r\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\r\n */\r\nconst fetchFromNetwork = async (\r\n src: string,\r\n type: ImageType = \"normal\"\r\n): Promise<Buffer> => {\r\n try {\r\n const response = await axios.get(src, {\r\n responseType: \"arraybuffer\",\r\n timeout: 5000,\r\n });\r\n\r\n const contentType = response.headers[\"content-type\"]?.toLowerCase();\r\n const allowedMimeTypes = Object.values(mimeTypes);\r\n\r\n if (allowedMimeTypes.includes(contentType ?? \"\")) {\r\n return Buffer.from(response.data);\r\n }\r\n return await FALLBACKIMAGES[type]();\r\n } catch (error) {\r\n return await FALLBACKIMAGES[type]();\r\n }\r\n};\r\n\r\n/**\r\n * Reads an image from the local file system.\r\n *\r\n * @param {string} filePath - Path to the image file.\r\n * @param {string} baseDir - Base directory to resolve paths.\r\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\r\n * @returns {Promise<Buffer>} A buffer containing the image data.\r\n */\r\nexport const readLocalImage = async (\r\n filePath: string,\r\n baseDir: string,\r\n type: ImageType = \"normal\"\r\n) => {\r\n const isValid = await isValidPath(baseDir, filePath);\r\n if (!isValid) {\r\n return await FALLBACKIMAGES[type]();\r\n }\r\n try {\r\n return await fs.readFile(path.resolve(baseDir, filePath));\r\n } catch (error) {\r\n return await FALLBACKIMAGES[type]();\r\n }\r\n};\r\n\r\n/**\r\n * Fetches an image from either a local file or a network source.\r\n *\r\n * @param {string} src - The URL or local path of the image.\r\n * @param {string} baseDir - Base directory to resolve local paths.\r\n * @param {string} websiteURL - The URL of the website.\r\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\r\n * @param {RegExp} [apiRegex=API_REGEX] - Regular expression to match API routes.\r\n * @param {string[]} [allowedNetworkList=[]] - List of allowed network hosts.\r\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\r\n */\r\nexport const fetchImage = (\r\n src: string,\r\n baseDir: string,\r\n websiteURL: string,\r\n type: ImageType = \"normal\",\r\n apiRegex: RegExp = API_REGEX,\r\n allowedNetworkList: string[] = []\r\n) => {\r\n const url = new URL(src);\r\n const isInternal = [websiteURL, `www.${websiteURL}`].includes(url.host);\r\n if (isInternal) {\r\n const localPath = url.pathname.replace(apiRegex, \"\");\r\n return readLocalImage(localPath, baseDir, type);\r\n } else {\r\n const allowedCondition = allowedNetworkList.includes(url.host);\r\n if (!allowedCondition) {\r\n return FALLBACKIMAGES[type]();\r\n }\r\n return fetchFromNetwork(src, type);\r\n }\r\n};\r\n","import { API_REGEX } from \"./variables\";\r\nimport type { Options, UserData } from \"./types\";\r\n\r\n/**\r\n * @typedef {(\"avatar\" | \"normal\")} ImageType\r\n * @description Defines the type of image being processed.\r\n */\r\n\r\n/**\r\n * @typedef {(\"jpeg\" | \"jpg\" | \"png\" | \"webp\" | \"gif\" | \"tiff\" | \"avif\" | \"svg\")} ImageFormat\r\n * @description Supported formats for image processing.\r\n */\r\n\r\n/**\r\n * @typedef {Object} Options\r\n * @property {string} baseDir - The base directory for public image files.\r\n * @property {function(string): string} idHandler - A function to handle user IDs.\r\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\r\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\r\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\r\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\r\n */\r\n\r\n/**\r\n * @typedef {Object} UserData\r\n * @property {number|string} quality - Quality of the image (1β100).\r\n * @property {ImageFormat} format - Desired format of the image.\r\n * @property {string} [src] - Source path or URL for the image.\r\n * @property {string} [folder] - The folder type (\"public\" or \"private\").\r\n * @property {ImageType} [type] - Type of the image (\"avatar\" or \"normal\").\r\n * @property {string|null} [userId] - Optional user identifier.\r\n * @property {number|string} [width] - Desired image width.\r\n * @property {number|string} [height] - Desired image height.\r\n */\r\n\r\n/**\r\n * Renders the options object with default values and user-provided values.\r\n *\r\n * @param {Partial<Options>} options - The user-provided options.\r\n * @returns {Options} The rendered options object.\r\n */\r\nexport const renderOptions = (options: Partial<Options>): Options => {\r\n const initialOptions: Options = {\r\n baseDir: \"\",\r\n idHandler: (id: string) => id,\r\n getUserFolder: async () => \"\",\r\n websiteURL: \"\",\r\n apiRegex: API_REGEX,\r\n allowedNetworkList: [],\r\n };\r\n return {\r\n ...initialOptions,\r\n ...options,\r\n };\r\n};\r\n\r\n/**\r\n * Renders the user data object with default values and user-provided values.\r\n *\r\n * @param {Partial<UserData>} userData - The user-provided data.\r\n * @returns {UserData} The rendered user data object.\r\n */\r\nexport const renderUserData = (userData: Partial<UserData>): UserData => {\r\n const initialUserData: UserData = {\r\n quality: 80,\r\n format: \"jpeg\",\r\n src: \"/placeholder/noimage.jpg\",\r\n folder: \"public\",\r\n type: \"normal\",\r\n width: undefined,\r\n height: undefined,\r\n userId: undefined,\r\n };\r\n return {\r\n ...initialUserData,\r\n ...userData,\r\n quality: userData.quality\r\n ? Math.min(Math.max(Number(userData.quality) || 80, 1), 100)\r\n : 100,\r\n width: userData.width\r\n ? Math.min(Math.max(Number(userData.width), 50), 2000)\r\n : undefined,\r\n height: userData.height\r\n ? Math.min(Math.max(Number(userData.height), 50), 2000)\r\n : undefined,\r\n };\r\n};\r\n"],"mappings":"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","names":["index_exports","__export","isValidPath","pixel_default","__toCommonJS","getImportMetaUrl","importMetaUrl","import_node_path","import_sharp","import_promises","import_node_path","import_node_url","__dirname","path","importMetaUrl","getAssetPath","filename","NOT_FOUND_IMAGE","NOT_FOUND_AVATAR","FALLBACKIMAGES","API_REGEX","allowedFormats","mimeTypes","import_node_path","fs","import_axios","isValidPath","basePath","specifiedPath","path","resolvedBase","resolvedPath","realBase","realPath","normalizedBase","isInside","relative","fetchFromNetwork","src","type","response","axios","contentType","mimeTypes","FALLBACKIMAGES","readLocalImage","filePath","baseDir","fetchImage","websiteURL","apiRegex","API_REGEX","allowedNetworkList","url","localPath","renderOptions","options","id","API_REGEX","renderUserData","userData","serveImage","req","res","next","options","userData","renderUserData","parsedOptions","renderOptions","imageBuffer","baseDir","parsedUserId","userIdStr","dir","outputFormat","allowedFormats","fetchImage","readLocalImage","image","sharp","resizeOptions","processedImage","processedFileName","path","mimeTypes","error","registerServe","pixel_default"]}
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import
|
|
1
|
+
import j from"node:path";import A from"sharp";import{readFile as x}from"node:fs/promises";import F from"node:path";import{fileURLToPath as q}from"node:url";var E=F.dirname(q(import.meta.url)),b=t=>F.join(E,"assets",t),D=b("noimage.jpg"),P=b("noavatar.png"),d={normal:async()=>x(D),avatar:async()=>x(P)},y=/^\/api\/v1\//,v=["jpeg","jpg","png","webp","gif","tiff","avif","svg"],h={jpeg:"image/jpeg",jpg:"image/jpeg",png:"image/png",webp:"image/webp",gif:"image/gif",tiff:"image/tiff",avif:"image/avif",svg:"image/svg+xml"};import n from"node:path";import*as l from"node:fs/promises";import M from"axios";var R=async(t,r)=>{try{if(!t||!r||r.includes("\0")||n.isAbsolute(r)||!/^[^\x00-\x1F]+$/.test(r))return!1;let a=n.resolve(t),i=n.resolve(a,r),[e,s]=await Promise.all([l.realpath(a),l.realpath(i)]);if(!(await l.stat(e)).isDirectory())return!1;let g=e+n.sep,c=(s+n.sep).startsWith(g)||s===e,f=n.relative(e,s);return!f.startsWith("..")&&!n.isAbsolute(f)&&c}catch{return!1}},B=async(t,r="normal")=>{try{let a=await M.get(t,{responseType:"arraybuffer",timeout:5e3}),i=a.headers["content-type"]?.toLowerCase();return Object.values(h).includes(i??"")?Buffer.from(a.data):await d[r]()}catch{return await d[r]()}},I=async(t,r,a="normal")=>{if(!await R(r,t))return await d[a]();try{return await l.readFile(n.resolve(r,t))}catch{return await d[a]()}},O=(t,r,a,i="normal",e=y,s=[])=>{let o=new URL(t);if([a,`www.${a}`].includes(o.host)){let m=o.pathname.replace(e,"");return I(m,r,i)}else return s.includes(o.host)?B(t,i):d[i]()};var U=t=>({...{baseDir:"",idHandler:a=>a,getUserFolder:async()=>"",websiteURL:"",apiRegex:y,allowedNetworkList:[]},...t}),T=t=>({...{quality:80,format:"jpeg",src:"/placeholder/noimage.jpg",folder:"public",type:"normal",width:void 0,height:void 0,userId:void 0},...t,quality:t.quality?Math.min(Math.max(Number(t.quality)||80,1),100):100,width:t.width?Math.min(Math.max(Number(t.width),50),2e3):void 0,height:t.height?Math.min(Math.max(Number(t.height),50),2e3):void 0});var S=async(t,r,a,i)=>{try{let e=T(t.query),s=U(i),o,g=s.baseDir,m;if(e.userId){let p=typeof e.userId=="object"?String(Object.values(e.userId)[0]):String(e.userId);s.idHandler?m=s.idHandler(p):m=p}if(e.folder==="private"){let p=await s?.getUserFolder?.(t,m);p&&(g=p)}let c=v.includes(e?.format?.toLowerCase())?e?.format?.toLowerCase():"jpeg";e?.src?.startsWith("http")?o=await O(e?.src??"",g,s?.websiteURL??"",e?.type,s?.apiRegex,s?.allowedNetworkList):o=await I(e?.src??"",g,e?.type);let f=A(o);if(e?.width||e?.height){let p={width:e?.width??void 0,height:e?.height??void 0,fit:A.fit.cover};f=f.resize(p)}let L=await f.toFormat(c,{quality:e?.quality?Number(e?.quality):80}).toBuffer(),N=`${j.basename(e?.src??"",j.extname(e?.src??""))}.${c}`;r.type(h[c]),r.setHeader("Content-Disposition",`inline; filename="${N}"`),r.send(L)}catch(e){a(e)}},_=t=>async(r,a,i)=>S(r,a,i,t),C=_;export{R as isValidPath,C as registerServe};
|
|
2
2
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/pixel.ts","../src/variables.ts","../src/functions.ts","../src/renders.ts"],"sourcesContent":["import path from \"node:path\";\nimport sharp, { FormatEnum, ResizeOptions } from \"sharp\";\nimport type { Request, Response, NextFunction } from \"express\";\nimport type { Options, UserData, ImageFormat, ImageType } from \"./types\";\nimport { allowedFormats, mimeTypes } from \"./variables\";\nimport { fetchImage, readLocalImage } from \"./functions\";\nimport { renderOptions, renderUserData } from \"./renders\";\n\n/**\n * @typedef {Object} Options\n * @property {string} baseDir - The base directory for public image files.\n * @property {function(string): string} idHandler - A function to handle user IDs.\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\n */\n\n/**\n * @function serveImage\n * @description Processes and serves an image based on user data and options.\n * @param {Request} req - The Express request object.\n * @param {Response} res - The Express response object.\n * @param {NextFunction} next - The Express next function.\n * @param {Options} options - The options object for image processing.\n * @returns {Promise<void>}\n */\nconst serveImage = async (\n req: Request,\n res: Response,\n next: NextFunction,\n options: Options\n) => {\n try {\n const userData = renderUserData(req.query as UserData);\n const parsedOptions = renderOptions(options);\n\n let imageBuffer;\n let baseDir = parsedOptions.baseDir;\n let parsedUserId;\n\n if (userData.userId) {\n const userIdStr =\n typeof userData.userId === \"object\"\n ? String(Object.values(userData.userId)[0])\n : String(userData.userId);\n if (parsedOptions.idHandler) {\n parsedUserId = parsedOptions.idHandler(userIdStr);\n } else {\n parsedUserId = userIdStr;\n }\n }\n\n if (userData.folder === \"private\") {\n const dir = await parsedOptions?.getUserFolder?.(req, parsedUserId);\n if (dir) {\n baseDir = dir;\n }\n }\n\n const outputFormat = allowedFormats.includes(\n userData?.format?.toLowerCase() as ImageFormat\n )\n ? userData?.format?.toLowerCase()\n : \"jpeg\";\n\n if (userData?.src?.startsWith(\"http\")) {\n imageBuffer = await fetchImage(\n userData?.src ?? \"\",\n baseDir,\n parsedOptions?.websiteURL ?? \"\",\n userData?.type as ImageType,\n parsedOptions?.apiRegex,\n parsedOptions?.allowedNetworkList\n );\n } else {\n imageBuffer = await readLocalImage(\n userData?.src ?? \"\",\n baseDir,\n userData?.type as ImageType\n );\n }\n\n let image = sharp(imageBuffer);\n\n if (userData?.width || userData?.height) {\n const resizeOptions = {\n width: userData?.width ?? undefined,\n height: userData?.height ?? undefined,\n fit: sharp.fit.cover,\n };\n image = image.resize(resizeOptions as ResizeOptions);\n }\n\n const processedImage = await image\n .toFormat(outputFormat as keyof FormatEnum, {\n quality: userData?.quality ? Number(userData?.quality) : 80,\n })\n .toBuffer();\n\n const processedFileName = `${path.basename(\n userData?.src ?? \"\",\n path.extname(userData?.src ?? \"\")\n )}.${outputFormat}`;\n\n res.type(mimeTypes[outputFormat]);\n res.setHeader(\n \"Content-Disposition\",\n `inline; filename=\"${processedFileName}\"`\n );\n res.send(processedImage);\n } catch (error) {\n next(error);\n }\n};\n\n/**\n * @function registerServe\n * @description A function to register the serveImage function as middleware for Express.\n * @param {Options} options - The options object for image processing.\n * @returns {function(Request, Response, NextFunction): Promise<void>} The middleware function.\n */\nconst registerServe = (options: Options) => {\n return async (req: Request, res: Response, next: NextFunction) =>\n serveImage(req, res, next, options);\n};\n\nexport default registerServe;\n","import type { ImageFormat } from \"./types\";\nimport { readFile } from \"node:fs/promises\";\n\nconst NOT_FOUND_IMAGE = new URL(\"./assets/noimage.jpg\", import.meta.url)\n .pathname;\n\nconst NOT_FOUND_AVATAR = new URL(\"./assets/noavatar.png\", import.meta.url)\n .pathname;\n\nexport const FALLBACKIMAGES = {\n normal: async () => readFile(NOT_FOUND_IMAGE),\n avatar: async () => readFile(NOT_FOUND_AVATAR),\n};\n\nexport const API_REGEX: RegExp = /^\\/api\\/v1\\//;\n\nexport const allowedFormats: ImageFormat[] = [\n \"jpeg\",\n \"jpg\",\n \"png\",\n \"webp\",\n \"gif\",\n \"tiff\",\n \"avif\",\n \"svg\",\n];\n\nexport const mimeTypes: Readonly<Record<string, string>> = {\n jpeg: \"image/jpeg\",\n jpg: \"image/jpeg\",\n png: \"image/png\",\n webp: \"image/webp\",\n gif: \"image/gif\",\n tiff: \"image/tiff\",\n avif: \"image/avif\",\n svg: \"image/svg+xml\",\n};\n","import path from \"node:path\";\nimport { existsSync } from \"node:fs\";\nimport * as fs from \"node:fs/promises\";\nimport axios from \"axios\";\nimport { mimeTypes, API_REGEX, FALLBACKIMAGES } from \"./variables\";\nimport type { ImageType } from \"./types\";\n\n/**\n * @typedef {(\"avatar\" | \"normal\")} ImageType\n * @description Defines the type of image being processed.\n */\n\n/**\n * Checks if a specified path is valid within a base path.\n *\n * @param {string} basePath - The base directory to resolve paths.\n * @param {string} specifiedPath - The path to check.\n * @returns {boolean} True if the path is valid, false otherwise.\n */\nconst isValidPath = (basePath: string, specifiedPath: string): boolean => {\n if (!basePath || !specifiedPath) return false;\n const resolvedBase = path.resolve(basePath);\n const resolvedPath = path.resolve(resolvedBase, specifiedPath);\n return resolvedPath.startsWith(resolvedBase) && existsSync(resolvedPath);\n};\n\n/**\n * Fetches an image from a network source.\n *\n * @param {string} src - The URL of the image.\n * @param {ImageType} [type=\"normal\"] - Type of fallback image in case of an error.\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\n */\nconst fetchFromNetwork = async (\n src: string,\n type: ImageType = \"normal\"\n): Promise<Buffer> => {\n try {\n const response = await axios.get(src, {\n responseType: \"arraybuffer\",\n timeout: 5000,\n });\n\n const contentType = response.headers[\"content-type\"]?.toLowerCase();\n const allowedMimeTypes = Object.values(mimeTypes);\n\n if (allowedMimeTypes.includes(contentType ?? \"\")) {\n return Buffer.from(response.data);\n }\n return await FALLBACKIMAGES[type]();\n } catch (error) {\n return await FALLBACKIMAGES[type]();\n }\n};\n\n/**\n * Reads an image from the local file system.\n *\n * @param {string} filePath - Path to the image file.\n * @param {string} baseDir - Base directory to resolve paths.\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\n * @returns {Promise<Buffer>} A buffer containing the image data.\n */\nexport const readLocalImage = async (\n filePath: string,\n baseDir: string,\n type: ImageType = \"normal\"\n) => {\n if (!isValidPath(baseDir, filePath)) {\n return await FALLBACKIMAGES[type]();\n }\n try {\n return await fs.readFile(path.resolve(baseDir, filePath));\n } catch (error) {\n return await FALLBACKIMAGES[type]();\n }\n};\n\n/**\n * Fetches an image from either a local file or a network source.\n *\n * @param {string} src - The URL or local path of the image.\n * @param {string} baseDir - Base directory to resolve local paths.\n * @param {string} websiteURL - The URL of the website.\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\n * @param {RegExp} [apiRegex=API_REGEX] - Regular expression to match API routes.\n * @param {string[]} [allowedNetworkList=[]] - List of allowed network hosts.\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\n */\nexport const fetchImage = (\n src: string,\n baseDir: string,\n websiteURL: string,\n type: ImageType = \"normal\",\n apiRegex: RegExp = API_REGEX,\n allowedNetworkList: string[] = []\n) => {\n const url = new URL(src);\n const isInternal = [websiteURL, `www.${websiteURL}`].includes(url.host);\n if (isInternal) {\n const localPath = url.pathname.replace(apiRegex, \"\");\n return readLocalImage(localPath, baseDir, type);\n } else {\n const allowedCondition = allowedNetworkList.includes(url.host);\n if (!allowedCondition) {\n return FALLBACKIMAGES[type]();\n }\n return fetchFromNetwork(src, type);\n }\n};\n","import { API_REGEX } from \"./variables\";\nimport type { Options, UserData } from \"./types\";\n\n/**\n * @typedef {(\"avatar\" | \"normal\")} ImageType\n * @description Defines the type of image being processed.\n */\n\n/**\n * @typedef {(\"jpeg\" | \"jpg\" | \"png\" | \"webp\" | \"gif\" | \"tiff\" | \"avif\" | \"svg\")} ImageFormat\n * @description Supported formats for image processing.\n */\n\n/**\n * @typedef {Object} Options\n * @property {string} baseDir - The base directory for public image files.\n * @property {function(string): string} idHandler - A function to handle user IDs.\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\n */\n\n/**\n * @typedef {Object} UserData\n * @property {number|string} quality - Quality of the image (1β100).\n * @property {ImageFormat} format - Desired format of the image.\n * @property {string} [src] - Source path or URL for the image.\n * @property {string} [folder] - The folder type (\"public\" or \"private\").\n * @property {ImageType} [type] - Type of the image (\"avatar\" or \"normal\").\n * @property {string|null} [userId] - Optional user identifier.\n * @property {number|string} [width] - Desired image width.\n * @property {number|string} [height] - Desired image height.\n */\n\n/**\n * Renders the options object with default values and user-provided values.\n *\n * @param {Partial<Options>} options - The user-provided options.\n * @returns {Options} The rendered options object.\n */\nexport const renderOptions = (options: Partial<Options>): Options => {\n const initialOptions: Options = {\n baseDir: \"\",\n idHandler: (id: string) => id,\n getUserFolder: async () => \"\",\n websiteURL: \"\",\n apiRegex: API_REGEX,\n allowedNetworkList: [],\n };\n return {\n ...initialOptions,\n ...options,\n };\n};\n\n/**\n * Renders the user data object with default values and user-provided values.\n *\n * @param {Partial<UserData>} userData - The user-provided data.\n * @returns {UserData} The rendered user data object.\n */\nexport const renderUserData = (userData: Partial<UserData>): UserData => {\n const initialUserData: UserData = {\n quality: 80,\n format: \"jpeg\",\n src: \"/placeholder/noimage.jpg\",\n folder: \"public\",\n type: \"normal\",\n width: undefined,\n height: undefined,\n userId: undefined,\n };\n return {\n ...initialUserData,\n ...userData,\n quality: userData.quality\n ? Math.min(Math.max(Number(userData.quality) || 80, 1), 100)\n : 100,\n width: userData.width\n ? Math.min(Math.max(Number(userData.width), 50), 2000)\n : undefined,\n height: userData.height\n ? Math.min(Math.max(Number(userData.height), 50), 2000)\n : undefined,\n };\n};\n"],"mappings":"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","names":["path","sharp","readFile","NOT_FOUND_IMAGE","NOT_FOUND_AVATAR","FALLBACKIMAGES","API_REGEX","allowedFormats","mimeTypes","path","existsSync","fs","axios","isValidPath","basePath","specifiedPath","resolvedBase","path","resolvedPath","existsSync","fetchFromNetwork","src","type","response","axios","contentType","mimeTypes","FALLBACKIMAGES","readLocalImage","filePath","baseDir","fetchImage","websiteURL","apiRegex","API_REGEX","allowedNetworkList","url","localPath","renderOptions","options","id","API_REGEX","renderUserData","userData","serveImage","req","res","next","options","userData","renderUserData","parsedOptions","renderOptions","imageBuffer","baseDir","parsedUserId","userIdStr","dir","outputFormat","allowedFormats","fetchImage","readLocalImage","image","sharp","resizeOptions","processedImage","processedFileName","path","mimeTypes","error","registerServe","pixel_default"]}
|
|
1
|
+
{"version":3,"sources":["../src/pixel.ts","../src/variables.ts","../src/functions.ts","../src/renders.ts"],"sourcesContent":["import path from \"node:path\";\r\nimport sharp, { FormatEnum, ResizeOptions } from \"sharp\";\r\nimport type { Request, Response, NextFunction } from \"express\";\r\nimport type { Options, UserData, ImageFormat, ImageType } from \"./types\";\r\nimport { allowedFormats, mimeTypes } from \"./variables\";\r\nimport { fetchImage, readLocalImage } from \"./functions\";\r\nimport { renderOptions, renderUserData } from \"./renders\";\r\n\r\n/**\r\n * @typedef {Object} Options\r\n * @property {string} baseDir - The base directory for public image files.\r\n * @property {function(string): string} idHandler - A function to handle user IDs.\r\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\r\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\r\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\r\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\r\n */\r\n\r\n/**\r\n * @function serveImage\r\n * @description Processes and serves an image based on user data and options.\r\n * @param {Request} req - The Express request object.\r\n * @param {Response} res - The Express response object.\r\n * @param {NextFunction} next - The Express next function.\r\n * @param {Options} options - The options object for image processing.\r\n * @returns {Promise<void>}\r\n */\r\nconst serveImage = async (\r\n req: Request,\r\n res: Response,\r\n next: NextFunction,\r\n options: Options\r\n) => {\r\n try {\r\n const userData = renderUserData(req.query as UserData);\r\n const parsedOptions = renderOptions(options);\r\n\r\n let imageBuffer;\r\n let baseDir = parsedOptions.baseDir;\r\n let parsedUserId;\r\n\r\n if (userData.userId) {\r\n const userIdStr =\r\n typeof userData.userId === \"object\"\r\n ? String(Object.values(userData.userId)[0])\r\n : String(userData.userId);\r\n if (parsedOptions.idHandler) {\r\n parsedUserId = parsedOptions.idHandler(userIdStr);\r\n } else {\r\n parsedUserId = userIdStr;\r\n }\r\n }\r\n\r\n if (userData.folder === \"private\") {\r\n const dir = await parsedOptions?.getUserFolder?.(req, parsedUserId);\r\n if (dir) {\r\n baseDir = dir;\r\n }\r\n }\r\n\r\n const outputFormat = allowedFormats.includes(\r\n userData?.format?.toLowerCase() as ImageFormat\r\n )\r\n ? userData?.format?.toLowerCase()\r\n : \"jpeg\";\r\n\r\n if (userData?.src?.startsWith(\"http\")) {\r\n imageBuffer = await fetchImage(\r\n userData?.src ?? \"\",\r\n baseDir,\r\n parsedOptions?.websiteURL ?? \"\",\r\n userData?.type as ImageType,\r\n parsedOptions?.apiRegex,\r\n parsedOptions?.allowedNetworkList\r\n );\r\n } else {\r\n imageBuffer = await readLocalImage(\r\n userData?.src ?? \"\",\r\n baseDir,\r\n userData?.type as ImageType\r\n );\r\n }\r\n\r\n let image = sharp(imageBuffer);\r\n\r\n if (userData?.width || userData?.height) {\r\n const resizeOptions = {\r\n width: userData?.width ?? undefined,\r\n height: userData?.height ?? undefined,\r\n fit: sharp.fit.cover,\r\n };\r\n image = image.resize(resizeOptions as ResizeOptions);\r\n }\r\n\r\n const processedImage = await image\r\n .toFormat(outputFormat as keyof FormatEnum, {\r\n quality: userData?.quality ? Number(userData?.quality) : 80,\r\n })\r\n .toBuffer();\r\n\r\n const processedFileName = `${path.basename(\r\n userData?.src ?? \"\",\r\n path.extname(userData?.src ?? \"\")\r\n )}.${outputFormat}`;\r\n\r\n res.type(mimeTypes[outputFormat]);\r\n res.setHeader(\r\n \"Content-Disposition\",\r\n `inline; filename=\"${processedFileName}\"`\r\n );\r\n res.send(processedImage);\r\n } catch (error) {\r\n next(error);\r\n }\r\n};\r\n\r\n/**\r\n * @function registerServe\r\n * @description A function to register the serveImage function as middleware for Express.\r\n * @param {Options} options - The options object for image processing.\r\n * @returns {function(Request, Response, NextFunction): Promise<void>} The middleware function.\r\n */\r\nconst registerServe = (options: Options) => {\r\n return async (req: Request, res: Response, next: NextFunction) =>\r\n serveImage(req, res, next, options);\r\n};\r\n\r\nexport default registerServe;\r\n","import type { ImageFormat } from \"./types\";\r\nimport { readFile } from \"node:fs/promises\";\r\nimport path from \"node:path\";\r\nimport { fileURLToPath } from \"node:url\";\r\n\r\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\r\n\r\nconst getAssetPath = (filename: string) => {\r\n return path.join(__dirname, \"assets\", filename);\r\n};\r\n\r\nconst NOT_FOUND_IMAGE = getAssetPath(\"noimage.jpg\");\r\nconst NOT_FOUND_AVATAR = getAssetPath(\"noavatar.png\");\r\n\r\nexport const FALLBACKIMAGES = {\r\n normal: async () => readFile(NOT_FOUND_IMAGE),\r\n avatar: async () => readFile(NOT_FOUND_AVATAR),\r\n};\r\n\r\nexport const API_REGEX: RegExp = /^\\/api\\/v1\\//;\r\n\r\nexport const allowedFormats: ImageFormat[] = [\r\n \"jpeg\",\r\n \"jpg\",\r\n \"png\",\r\n \"webp\",\r\n \"gif\",\r\n \"tiff\",\r\n \"avif\",\r\n \"svg\",\r\n];\r\n\r\nexport const mimeTypes: Readonly<Record<string, string>> = {\r\n jpeg: \"image/jpeg\",\r\n jpg: \"image/jpeg\",\r\n png: \"image/png\",\r\n webp: \"image/webp\",\r\n gif: \"image/gif\",\r\n tiff: \"image/tiff\",\r\n avif: \"image/avif\",\r\n svg: \"image/svg+xml\",\r\n};\r\n","import path from \"node:path\";\r\nimport * as fs from \"node:fs/promises\";\r\nimport axios from \"axios\";\r\nimport { mimeTypes, API_REGEX, FALLBACKIMAGES } from \"./variables\";\r\nimport type { ImageType } from \"./types\";\r\n\r\n/**\r\n * @typedef {(\"avatar\" | \"normal\")} ImageType\r\n * @description Defines the type of image being processed.\r\n */\r\n\r\n/**\r\n * Checks if a specified path is valid within a base path.\r\n *\r\n * @param {string} basePath - The base directory to resolve paths.\r\n * @param {string} specifiedPath - The path to check.\r\n * @returns {boolean} True if the path is valid, false otherwise.\r\n */\r\nexport const isValidPath = async (\r\n basePath: string,\r\n specifiedPath: string\r\n): Promise<boolean> => {\r\n try {\r\n if (!basePath || !specifiedPath) return false;\r\n if (specifiedPath.includes(\"\\0\")) return false;\r\n if (path.isAbsolute(specifiedPath)) return false;\r\n if (!/^[^\\x00-\\x1F]+$/.test(specifiedPath)) return false;\r\n\r\n const resolvedBase = path.resolve(basePath);\r\n const resolvedPath = path.resolve(resolvedBase, specifiedPath);\r\n\r\n const [realBase, realPath] = await Promise.all([\r\n fs.realpath(resolvedBase),\r\n fs.realpath(resolvedPath),\r\n ]);\r\n\r\n const baseStats = await fs.stat(realBase);\r\n if (!baseStats.isDirectory()) return false;\r\n\r\n const normalizedBase = realBase + path.sep;\r\n const normalizedPath = realPath + path.sep;\r\n\r\n const isInside =\r\n normalizedPath.startsWith(normalizedBase) || realPath === realBase;\r\n\r\n const relative = path.relative(realBase, realPath);\r\n return !relative.startsWith(\"..\") && !path.isAbsolute(relative) && isInside;\r\n } catch {\r\n return false;\r\n }\r\n};\r\n\r\n/**\r\n * Fetches an image from a network source.\r\n *\r\n * @param {string} src - The URL of the image.\r\n * @param {ImageType} [type=\"normal\"] - Type of fallback image in case of an error.\r\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\r\n */\r\nconst fetchFromNetwork = async (\r\n src: string,\r\n type: ImageType = \"normal\"\r\n): Promise<Buffer> => {\r\n try {\r\n const response = await axios.get(src, {\r\n responseType: \"arraybuffer\",\r\n timeout: 5000,\r\n });\r\n\r\n const contentType = response.headers[\"content-type\"]?.toLowerCase();\r\n const allowedMimeTypes = Object.values(mimeTypes);\r\n\r\n if (allowedMimeTypes.includes(contentType ?? \"\")) {\r\n return Buffer.from(response.data);\r\n }\r\n return await FALLBACKIMAGES[type]();\r\n } catch (error) {\r\n return await FALLBACKIMAGES[type]();\r\n }\r\n};\r\n\r\n/**\r\n * Reads an image from the local file system.\r\n *\r\n * @param {string} filePath - Path to the image file.\r\n * @param {string} baseDir - Base directory to resolve paths.\r\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\r\n * @returns {Promise<Buffer>} A buffer containing the image data.\r\n */\r\nexport const readLocalImage = async (\r\n filePath: string,\r\n baseDir: string,\r\n type: ImageType = \"normal\"\r\n) => {\r\n const isValid = await isValidPath(baseDir, filePath);\r\n if (!isValid) {\r\n return await FALLBACKIMAGES[type]();\r\n }\r\n try {\r\n return await fs.readFile(path.resolve(baseDir, filePath));\r\n } catch (error) {\r\n return await FALLBACKIMAGES[type]();\r\n }\r\n};\r\n\r\n/**\r\n * Fetches an image from either a local file or a network source.\r\n *\r\n * @param {string} src - The URL or local path of the image.\r\n * @param {string} baseDir - Base directory to resolve local paths.\r\n * @param {string} websiteURL - The URL of the website.\r\n * @param {ImageType} [type=\"normal\"] - Type of fallback image if the path is invalid.\r\n * @param {RegExp} [apiRegex=API_REGEX] - Regular expression to match API routes.\r\n * @param {string[]} [allowedNetworkList=[]] - List of allowed network hosts.\r\n * @returns {Promise<Buffer>} A buffer containing the image data or a fallback image.\r\n */\r\nexport const fetchImage = (\r\n src: string,\r\n baseDir: string,\r\n websiteURL: string,\r\n type: ImageType = \"normal\",\r\n apiRegex: RegExp = API_REGEX,\r\n allowedNetworkList: string[] = []\r\n) => {\r\n const url = new URL(src);\r\n const isInternal = [websiteURL, `www.${websiteURL}`].includes(url.host);\r\n if (isInternal) {\r\n const localPath = url.pathname.replace(apiRegex, \"\");\r\n return readLocalImage(localPath, baseDir, type);\r\n } else {\r\n const allowedCondition = allowedNetworkList.includes(url.host);\r\n if (!allowedCondition) {\r\n return FALLBACKIMAGES[type]();\r\n }\r\n return fetchFromNetwork(src, type);\r\n }\r\n};\r\n","import { API_REGEX } from \"./variables\";\r\nimport type { Options, UserData } from \"./types\";\r\n\r\n/**\r\n * @typedef {(\"avatar\" | \"normal\")} ImageType\r\n * @description Defines the type of image being processed.\r\n */\r\n\r\n/**\r\n * @typedef {(\"jpeg\" | \"jpg\" | \"png\" | \"webp\" | \"gif\" | \"tiff\" | \"avif\" | \"svg\")} ImageFormat\r\n * @description Supported formats for image processing.\r\n */\r\n\r\n/**\r\n * @typedef {Object} Options\r\n * @property {string} baseDir - The base directory for public image files.\r\n * @property {function(string): string} idHandler - A function to handle user IDs.\r\n * @property {function(string, Request): Promise<string>} getUserFolder - Asynchronous function to retrieve user-specific folders.\r\n * @property {string} websiteURL - The base URL of the website for internal link resolution.\r\n * @property {RegExp} apiRegex - Regex to parse API endpoints from URLs.\r\n * @property {string[]} allowedNetworkList - List of allowed network domains for external image fetching.\r\n */\r\n\r\n/**\r\n * @typedef {Object} UserData\r\n * @property {number|string} quality - Quality of the image (1β100).\r\n * @property {ImageFormat} format - Desired format of the image.\r\n * @property {string} [src] - Source path or URL for the image.\r\n * @property {string} [folder] - The folder type (\"public\" or \"private\").\r\n * @property {ImageType} [type] - Type of the image (\"avatar\" or \"normal\").\r\n * @property {string|null} [userId] - Optional user identifier.\r\n * @property {number|string} [width] - Desired image width.\r\n * @property {number|string} [height] - Desired image height.\r\n */\r\n\r\n/**\r\n * Renders the options object with default values and user-provided values.\r\n *\r\n * @param {Partial<Options>} options - The user-provided options.\r\n * @returns {Options} The rendered options object.\r\n */\r\nexport const renderOptions = (options: Partial<Options>): Options => {\r\n const initialOptions: Options = {\r\n baseDir: \"\",\r\n idHandler: (id: string) => id,\r\n getUserFolder: async () => \"\",\r\n websiteURL: \"\",\r\n apiRegex: API_REGEX,\r\n allowedNetworkList: [],\r\n };\r\n return {\r\n ...initialOptions,\r\n ...options,\r\n };\r\n};\r\n\r\n/**\r\n * Renders the user data object with default values and user-provided values.\r\n *\r\n * @param {Partial<UserData>} userData - The user-provided data.\r\n * @returns {UserData} The rendered user data object.\r\n */\r\nexport const renderUserData = (userData: Partial<UserData>): UserData => {\r\n const initialUserData: UserData = {\r\n quality: 80,\r\n format: \"jpeg\",\r\n src: \"/placeholder/noimage.jpg\",\r\n folder: \"public\",\r\n type: \"normal\",\r\n width: undefined,\r\n height: undefined,\r\n userId: undefined,\r\n };\r\n return {\r\n ...initialUserData,\r\n ...userData,\r\n quality: userData.quality\r\n ? Math.min(Math.max(Number(userData.quality) || 80, 1), 100)\r\n : 100,\r\n width: userData.width\r\n ? Math.min(Math.max(Number(userData.width), 50), 2000)\r\n : undefined,\r\n height: userData.height\r\n ? Math.min(Math.max(Number(userData.height), 50), 2000)\r\n : undefined,\r\n };\r\n};\r\n"],"mappings":"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","names":["path","sharp","readFile","path","fileURLToPath","__dirname","getAssetPath","filename","NOT_FOUND_IMAGE","NOT_FOUND_AVATAR","FALLBACKIMAGES","API_REGEX","allowedFormats","mimeTypes","path","fs","axios","isValidPath","basePath","specifiedPath","path","resolvedBase","resolvedPath","realBase","realPath","normalizedBase","isInside","relative","fetchFromNetwork","src","type","response","axios","contentType","mimeTypes","FALLBACKIMAGES","readLocalImage","filePath","baseDir","fetchImage","websiteURL","apiRegex","API_REGEX","allowedNetworkList","url","localPath","renderOptions","options","id","API_REGEX","renderUserData","userData","serveImage","req","res","next","options","userData","renderUserData","parsedOptions","renderOptions","imageBuffer","baseDir","parsedUserId","userIdStr","dir","outputFormat","allowedFormats","fetchImage","readLocalImage","image","sharp","resizeOptions","processedImage","processedFileName","path","mimeTypes","error","registerServe","pixel_default"]}
|
package/package.json
CHANGED
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pixel-serve-server",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "A robust Node.js utility for handling and processing images. This package provides features like resizing, format conversion and etc.",
|
|
5
|
-
"main": "./dist/index.js",
|
|
6
|
-
"module": "./dist/index.mjs",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
8
|
-
"scripts": {
|
|
9
|
-
"build": "tsup"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist",
|
|
13
|
-
"dist/assets"
|
|
14
|
-
],
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "git+https://github.com/Hiprax/pixel-serve-server.git"
|
|
18
|
-
},
|
|
19
|
-
"keywords": [
|
|
20
|
-
"Pixel Serve",
|
|
21
|
-
"Image",
|
|
22
|
-
"Image Serving",
|
|
23
|
-
"Image Optimization",
|
|
24
|
-
"Image Resizing",
|
|
25
|
-
"Image Formatting",
|
|
26
|
-
"Image Transformation"
|
|
27
|
-
],
|
|
28
|
-
"author": "Hiprax",
|
|
29
|
-
"license": "MIT",
|
|
30
|
-
"bugs": {
|
|
31
|
-
"url": "https://github.com/Hiprax/pixel-serve-server/issues"
|
|
32
|
-
},
|
|
33
|
-
"homepage": "https://github.com/Hiprax/pixel-serve-server#readme",
|
|
34
|
-
"devDependencies": {
|
|
35
|
-
"@types/express": "^5.0.0",
|
|
36
|
-
"@types/node": "^22.10.5",
|
|
37
|
-
"tsup": "^8.3.5",
|
|
38
|
-
"typescript": "^5.7.3"
|
|
39
|
-
},
|
|
40
|
-
"engines": {
|
|
41
|
-
"node": ">=8.x"
|
|
42
|
-
},
|
|
43
|
-
"dependencies": {
|
|
44
|
-
"axios": "^1.7.9",
|
|
45
|
-
"express": "^4.21.2",
|
|
46
|
-
"sharp": "^0.33.5"
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pixel-serve-server",
|
|
3
|
+
"version": "0.0.7",
|
|
4
|
+
"description": "A robust Node.js utility for handling and processing images. This package provides features like resizing, format conversion and etc.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"module": "./dist/index.mjs",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsup"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"dist/assets"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/Hiprax/pixel-serve-server.git"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"Pixel Serve",
|
|
21
|
+
"Image",
|
|
22
|
+
"Image Serving",
|
|
23
|
+
"Image Optimization",
|
|
24
|
+
"Image Resizing",
|
|
25
|
+
"Image Formatting",
|
|
26
|
+
"Image Transformation"
|
|
27
|
+
],
|
|
28
|
+
"author": "Hiprax",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/Hiprax/pixel-serve-server/issues"
|
|
32
|
+
},
|
|
33
|
+
"homepage": "https://github.com/Hiprax/pixel-serve-server#readme",
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/express": "^5.0.0",
|
|
36
|
+
"@types/node": "^22.10.5",
|
|
37
|
+
"tsup": "^8.3.5",
|
|
38
|
+
"typescript": "^5.7.3"
|
|
39
|
+
},
|
|
40
|
+
"engines": {
|
|
41
|
+
"node": ">=8.x"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"axios": "^1.7.9",
|
|
45
|
+
"express": "^4.21.2",
|
|
46
|
+
"sharp": "^0.33.5"
|
|
47
|
+
}
|
|
48
|
+
}
|