@lens-image/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +244 -0
- package/dist/cjs/adapters/memory.d.ts +55 -0
- package/dist/cjs/adapters/memory.d.ts.map +1 -0
- package/dist/cjs/adapters/memory.js +84 -0
- package/dist/cjs/adapters/memory.js.map +1 -0
- package/dist/cjs/browser.d.ts +46 -0
- package/dist/cjs/browser.d.ts.map +1 -0
- package/dist/cjs/browser.js +79 -0
- package/dist/cjs/browser.js.map +1 -0
- package/dist/cjs/bytes.d.ts +18 -0
- package/dist/cjs/bytes.d.ts.map +1 -0
- package/dist/cjs/bytes.js +31 -0
- package/dist/cjs/bytes.js.map +1 -0
- package/dist/cjs/cache.d.ts +43 -0
- package/dist/cjs/cache.d.ts.map +1 -0
- package/dist/cjs/cache.js +77 -0
- package/dist/cjs/cache.js.map +1 -0
- package/dist/cjs/concurrency.d.ts +35 -0
- package/dist/cjs/concurrency.d.ts.map +1 -0
- package/dist/cjs/concurrency.js +82 -0
- package/dist/cjs/concurrency.js.map +1 -0
- package/dist/cjs/engines/index.d.ts +20 -0
- package/dist/cjs/engines/index.d.ts.map +1 -0
- package/dist/cjs/engines/index.js +38 -0
- package/dist/cjs/engines/index.js.map +1 -0
- package/dist/cjs/engines/passthrough.d.ts +41 -0
- package/dist/cjs/engines/passthrough.d.ts.map +1 -0
- package/dist/cjs/engines/passthrough.js +66 -0
- package/dist/cjs/engines/passthrough.js.map +1 -0
- package/dist/cjs/engines/sharp.d.ts +58 -0
- package/dist/cjs/engines/sharp.d.ts.map +1 -0
- package/dist/cjs/engines/sharp.js +243 -0
- package/dist/cjs/engines/sharp.js.map +1 -0
- package/dist/cjs/errors.d.ts +90 -0
- package/dist/cjs/errors.d.ts.map +1 -0
- package/dist/cjs/errors.js +97 -0
- package/dist/cjs/errors.js.map +1 -0
- package/dist/cjs/format.d.ts +33 -0
- package/dist/cjs/format.d.ts.map +1 -0
- package/dist/cjs/format.js +102 -0
- package/dist/cjs/format.js.map +1 -0
- package/dist/cjs/hash.d.ts +19 -0
- package/dist/cjs/hash.d.ts.map +1 -0
- package/dist/cjs/hash.js +43 -0
- package/dist/cjs/hash.js.map +1 -0
- package/dist/cjs/index.d.ts +56 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +110 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/naming.d.ts +48 -0
- package/dist/cjs/naming.d.ts.map +1 -0
- package/dist/cjs/naming.js +95 -0
- package/dist/cjs/naming.js.map +1 -0
- package/dist/cjs/optimizer.d.ts +89 -0
- package/dist/cjs/optimizer.d.ts.map +1 -0
- package/dist/cjs/optimizer.js +667 -0
- package/dist/cjs/optimizer.js.map +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/retry.d.ts +55 -0
- package/dist/cjs/retry.d.ts.map +1 -0
- package/dist/cjs/retry.js +129 -0
- package/dist/cjs/retry.js.map +1 -0
- package/dist/cjs/server/handler.d.ts +80 -0
- package/dist/cjs/server/handler.d.ts.map +1 -0
- package/dist/cjs/server/handler.js +155 -0
- package/dist/cjs/server/handler.js.map +1 -0
- package/dist/cjs/sniff.d.ts +37 -0
- package/dist/cjs/sniff.d.ts.map +1 -0
- package/dist/cjs/sniff.js +442 -0
- package/dist/cjs/sniff.js.map +1 -0
- package/dist/cjs/source.d.ts +45 -0
- package/dist/cjs/source.d.ts.map +1 -0
- package/dist/cjs/source.js +221 -0
- package/dist/cjs/source.js.map +1 -0
- package/dist/cjs/srcset.d.ts +66 -0
- package/dist/cjs/srcset.d.ts.map +1 -0
- package/dist/cjs/srcset.js +81 -0
- package/dist/cjs/srcset.js.map +1 -0
- package/dist/cjs/types.d.ts +441 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +12 -0
- package/dist/cjs/types.js.map +1 -0
- package/dist/cjs/validation.d.ts +38 -0
- package/dist/cjs/validation.d.ts.map +1 -0
- package/dist/cjs/validation.js +79 -0
- package/dist/cjs/validation.js.map +1 -0
- package/dist/esm/adapters/memory.d.ts +55 -0
- package/dist/esm/adapters/memory.d.ts.map +1 -0
- package/dist/esm/adapters/memory.js +80 -0
- package/dist/esm/adapters/memory.js.map +1 -0
- package/dist/esm/browser.d.ts +46 -0
- package/dist/esm/browser.d.ts.map +1 -0
- package/dist/esm/browser.js +49 -0
- package/dist/esm/browser.js.map +1 -0
- package/dist/esm/bytes.d.ts +18 -0
- package/dist/esm/bytes.d.ts.map +1 -0
- package/dist/esm/bytes.js +28 -0
- package/dist/esm/bytes.js.map +1 -0
- package/dist/esm/cache.d.ts +43 -0
- package/dist/esm/cache.d.ts.map +1 -0
- package/dist/esm/cache.js +73 -0
- package/dist/esm/cache.js.map +1 -0
- package/dist/esm/concurrency.d.ts +35 -0
- package/dist/esm/concurrency.d.ts.map +1 -0
- package/dist/esm/concurrency.js +78 -0
- package/dist/esm/concurrency.js.map +1 -0
- package/dist/esm/engines/index.d.ts +20 -0
- package/dist/esm/engines/index.d.ts.map +1 -0
- package/dist/esm/engines/index.js +30 -0
- package/dist/esm/engines/index.js.map +1 -0
- package/dist/esm/engines/passthrough.d.ts +41 -0
- package/dist/esm/engines/passthrough.d.ts.map +1 -0
- package/dist/esm/engines/passthrough.js +63 -0
- package/dist/esm/engines/passthrough.js.map +1 -0
- package/dist/esm/engines/sharp.d.ts +58 -0
- package/dist/esm/engines/sharp.d.ts.map +1 -0
- package/dist/esm/engines/sharp.js +206 -0
- package/dist/esm/engines/sharp.js.map +1 -0
- package/dist/esm/errors.d.ts +90 -0
- package/dist/esm/errors.d.ts.map +1 -0
- package/dist/esm/errors.js +91 -0
- package/dist/esm/errors.js.map +1 -0
- package/dist/esm/format.d.ts +33 -0
- package/dist/esm/format.d.ts.map +1 -0
- package/dist/esm/format.js +93 -0
- package/dist/esm/format.js.map +1 -0
- package/dist/esm/hash.d.ts +19 -0
- package/dist/esm/hash.d.ts.map +1 -0
- package/dist/esm/hash.js +38 -0
- package/dist/esm/hash.js.map +1 -0
- package/dist/esm/index.d.ts +56 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +54 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/naming.d.ts +48 -0
- package/dist/esm/naming.d.ts.map +1 -0
- package/dist/esm/naming.js +88 -0
- package/dist/esm/naming.js.map +1 -0
- package/dist/esm/optimizer.d.ts +89 -0
- package/dist/esm/optimizer.d.ts.map +1 -0
- package/dist/esm/optimizer.js +663 -0
- package/dist/esm/optimizer.js.map +1 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/retry.d.ts +55 -0
- package/dist/esm/retry.d.ts.map +1 -0
- package/dist/esm/retry.js +123 -0
- package/dist/esm/retry.js.map +1 -0
- package/dist/esm/server/handler.d.ts +80 -0
- package/dist/esm/server/handler.d.ts.map +1 -0
- package/dist/esm/server/handler.js +151 -0
- package/dist/esm/server/handler.js.map +1 -0
- package/dist/esm/sniff.d.ts +37 -0
- package/dist/esm/sniff.d.ts.map +1 -0
- package/dist/esm/sniff.js +435 -0
- package/dist/esm/sniff.js.map +1 -0
- package/dist/esm/source.d.ts +45 -0
- package/dist/esm/source.d.ts.map +1 -0
- package/dist/esm/source.js +183 -0
- package/dist/esm/source.js.map +1 -0
- package/dist/esm/srcset.d.ts +66 -0
- package/dist/esm/srcset.d.ts.map +1 -0
- package/dist/esm/srcset.js +76 -0
- package/dist/esm/srcset.js.map +1 -0
- package/dist/esm/types.d.ts +441 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +11 -0
- package/dist/esm/types.js.map +1 -0
- package/dist/esm/validation.d.ts +38 -0
- package/dist/esm/validation.d.ts.map +1 -0
- package/dist/esm/validation.js +74 -0
- package/dist/esm/validation.js.map +1 -0
- package/package.json +77 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Lens contributors
|
|
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
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://lens-image.vercel.app">
|
|
3
|
+
<img src="https://raw.githubusercontent.com/Sthabiso10/lens-image/main/docs/media/logo.png" alt="Lens" width="300" />
|
|
4
|
+
</a>
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<a href="https://www.npmjs.com/package/@lens-image/core"><img alt="npm" src="https://img.shields.io/npm/v/@lens-image%2Fcore.svg?color=cb3837" /></a>
|
|
9
|
+
<img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-strict-3178c6.svg" />
|
|
10
|
+
<img alt="MIT license" src="https://img.shields.io/badge/license-MIT-blue.svg" />
|
|
11
|
+
</p>
|
|
12
|
+
|
|
13
|
+
# @lens-image/core
|
|
14
|
+
|
|
15
|
+
**Zero-dependency image optimization engine for Node.js.** Resize, compress, convert formats, and hand the results to any storage backend.
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install @lens-image/core sharp
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`sharp` is an [optional peer dependency](#the-engine). `@lens-image/core` itself installs **zero packages**.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Quick start
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { ImageOptimizer, MemoryAdapter } from '@lens-image/core';
|
|
29
|
+
|
|
30
|
+
const optimizer = new ImageOptimizer({ adapter: new MemoryAdapter() });
|
|
31
|
+
|
|
32
|
+
const result = await optimizer.optimize({
|
|
33
|
+
source: './photo.jpg',
|
|
34
|
+
formats: ['webp', 'jpg'],
|
|
35
|
+
sizes: [{ width: 1200 }, { width: 600 }, { width: 300 }],
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
result.formats.webp.srcset;
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Swap `MemoryAdapter` for [`@lens-image/adapter-s3`](../adapter-s3), [`@lens-image/adapter-local`](../adapter-local) or [`@lens-image/adapter-cloudinary`](../adapter-cloudinary) when you want the bytes to go somewhere real.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## `new ImageOptimizer(options)`
|
|
46
|
+
|
|
47
|
+
| Option | Type | Default | |
|
|
48
|
+
|---|---|---|---|
|
|
49
|
+
| `adapter` | `StorageAdapter` |, | Where output goes. Omit for process-only mode. |
|
|
50
|
+
| `engine` | `ImageEngine` | auto | Codec backend. Defaults to sharp if installed. |
|
|
51
|
+
| `quality` | `number` | per-format | 1-100. |
|
|
52
|
+
| `formats` | `ImageFormat[]` | `['webp']` | Default output formats. |
|
|
53
|
+
| `sizes` | `SizeSpec[]` | original | Default responsive sizes. |
|
|
54
|
+
| `fallbackFormat` | `ImageFormat \| null` | `'jpeg'` | Used when everything else fails. `null` disables. |
|
|
55
|
+
| `key` | `string \| fn` | `'{hash}/{name}-{label}.{ext}'` | Output key template. |
|
|
56
|
+
| `prefix` | `string` | `''` | Prepended to every key. |
|
|
57
|
+
| `preserveMetadata` | `boolean` | `false` | Keep EXIF/ICC/XMP. |
|
|
58
|
+
| `autoOrient` | `boolean` | `true` | Apply EXIF orientation before resizing. |
|
|
59
|
+
| `concurrency` | `number` | `4` | Parallel encode+upload pipelines. |
|
|
60
|
+
| `validate` | `ValidationOptions \| false` | see below | Input limits. |
|
|
61
|
+
| `retry` | `RetryOptions` | 3 attempts | Adapter write retries. |
|
|
62
|
+
| `cache` | `false \| 'memory' \| 'storage' \| CacheStore` | `false` | Result caching. |
|
|
63
|
+
| `cacheControl` | `string` | `public, max-age=31536000, immutable` | Applied to uploads. |
|
|
64
|
+
| `allowRemote` | `boolean` | `false` | Permit `http(s):` sources. |
|
|
65
|
+
| `onWarning` | `(w) => void` |, | Called per degradation. |
|
|
66
|
+
|
|
67
|
+
Everything except `adapter`, `engine`, `cache` and `allowRemote` can be overridden per call.
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## `optimizer.optimize(options)`
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
await optimizer.optimize({
|
|
75
|
+
source: './photo.jpg',
|
|
76
|
+
formats: ['avif', 'webp', 'jpg'],
|
|
77
|
+
sizes: [{ width: 1200 }, { width: 600, fit: 'contain' }],
|
|
78
|
+
thumbnail: { width: 128, format: 'webp' },
|
|
79
|
+
metadata: { uploadedBy: 'user-42' },
|
|
80
|
+
signal: controller.signal,
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `source` accepts
|
|
85
|
+
|
|
86
|
+
| | |
|
|
87
|
+
|---|---|
|
|
88
|
+
| `string` | Filesystem path |
|
|
89
|
+
| `Uint8Array` / `Buffer` | Raw bytes |
|
|
90
|
+
| `URL` | `file:`, or `http(s):` with `allowRemote` |
|
|
91
|
+
| `AsyncIterable<Uint8Array>` | Node or web stream, multipart part |
|
|
92
|
+
| `{ data, filename }` | Bytes plus a name for key generation |
|
|
93
|
+
|
|
94
|
+
### Sizes
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
sizes: [
|
|
98
|
+
{ width: 1200 }, // → label '1200w'
|
|
99
|
+
{ width: 400, height: 400, fit: 'cover' }, // → label '400x400'
|
|
100
|
+
{ width: 800, label: 'hero', quality: 90 }, // explicit label
|
|
101
|
+
]
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`fit` is `'cover' | 'contain' | 'fill' | 'inside' | 'outside'`. The familiar CSS names. Images are never upscaled unless you set `withoutEnlargement: false`.
|
|
105
|
+
|
|
106
|
+
> Two sizes that produce the same label within one format throw `INVALID_OPTIONS`, because they'd map to the same key and one would silently overwrite the other.
|
|
107
|
+
|
|
108
|
+
---
|
|
109
|
+
|
|
110
|
+
## The result
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
interface OptimizeResult {
|
|
114
|
+
id: string; // stable per (source, options), also the cache key
|
|
115
|
+
runId: string; // unique per call, for log correlation
|
|
116
|
+
source: ImageMetadata & { filename: string; checksum: string };
|
|
117
|
+
formats: { webp?: FormatResult; jpeg?: FormatResult; /* … */ };
|
|
118
|
+
variants: Variant[]; // flat, in plan order
|
|
119
|
+
thumbnail?: Variant;
|
|
120
|
+
warnings: LensWarning[];
|
|
121
|
+
totalSize: number;
|
|
122
|
+
savings: number; // 0-1, vs the largest single output
|
|
123
|
+
durationMs: number;
|
|
124
|
+
cached: boolean;
|
|
125
|
+
engine: string;
|
|
126
|
+
adapter: string | null;
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Each `FormatResult` carries `urls` (keyed by label), `variants`, `srcset`, `size`, `largest` and `smallest`.
|
|
131
|
+
|
|
132
|
+
> **Note on `jpg`.** It's accepted everywhere as an input alias and normalised to `jpeg`, so results are always keyed `result.formats.jpeg`. File extensions still come out as `.jpg`.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Other methods
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
await optimizer.inspect(source); // metadata only, no processing
|
|
140
|
+
await optimizer.optimizeMany([a, b], opts); // sequential, bounded memory
|
|
141
|
+
await optimizer.delete(result); // remove every variant
|
|
142
|
+
await optimizer.dispose(); // release adapter resources
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Validation
|
|
148
|
+
|
|
149
|
+
Runs against the **file header**, before any decoder sees the bytes.
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
validate: {
|
|
153
|
+
maxBytes: 25 * 1024 * 1024, // default
|
|
154
|
+
maxPixels: 100_000_000, // default, decompression-bomb guard
|
|
155
|
+
allowedFormats: ['jpeg', 'png', 'webp', 'avif', 'gif', 'tiff'],
|
|
156
|
+
minWidth: 200,
|
|
157
|
+
minHeight: 200,
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Set `validate: false` to disable. The `maxPixels` guard matters: a 40,000 × 40,000 PNG compresses to a few hundred KB and expands to roughly 6 GB in memory.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## The engine
|
|
166
|
+
|
|
167
|
+
The codec is an interface, not a hard dependency:
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
interface ImageEngine {
|
|
171
|
+
readonly name: string;
|
|
172
|
+
supports(format: ImageFormat): boolean | Promise<boolean>;
|
|
173
|
+
probe(input: Uint8Array): Promise<ImageMetadata>;
|
|
174
|
+
transform(input: Uint8Array, op: TransformOp): Promise<EncodedImage>;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
| Engine | |
|
|
179
|
+
|---|---|
|
|
180
|
+
| `createSharpEngine(opts)` | sharp-backed. Lazy dynamic import, nothing loads until you encode. |
|
|
181
|
+
| `createPassthroughEngine()` | No codec. Validates, hashes, keys and stores originals. Ideal for tests. |
|
|
182
|
+
| `detectEngine()` | sharp if available, otherwise passthrough. The default. |
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { createSharpEngine } from '@lens-image/core';
|
|
186
|
+
|
|
187
|
+
new ImageOptimizer({
|
|
188
|
+
engine: createSharpEngine({
|
|
189
|
+
threads: 1, // we already parallelise; 4 Lens jobs × 4 libvips threads = 16
|
|
190
|
+
cache: false, // libvips' global cache is a leak in a long-lived server
|
|
191
|
+
}),
|
|
192
|
+
});
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Upload handler
|
|
198
|
+
|
|
199
|
+
A `Request → Response` function built only on web standards, so it drops into Next.js, Remix, Hono, Bun or Deno unmodified.
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
export const POST = createUploadHandler({
|
|
203
|
+
optimizer,
|
|
204
|
+
fieldName: 'file',
|
|
205
|
+
maxFiles: 5,
|
|
206
|
+
authorize: async (req) => (await auth(req)) ? undefined : new Response('no', { status: 401 }),
|
|
207
|
+
options: async (req) => ({ prefix: `users/${(await auth(req)).id}` }),
|
|
208
|
+
onError: (error) => logger.error(error),
|
|
209
|
+
});
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Errors map to their `LensError.status` automatically. A too-large file is a 422, not a 500. Unexpected errors return a generic message; the real one goes to `onError`.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## HTML helpers
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { toPicture, pickVariant, buildSrcset } from '@lens-image/core';
|
|
220
|
+
|
|
221
|
+
const pic = toPicture(result, { alt: 'Cabin', sizes: '(max-width: 768px) 100vw, 50vw' });
|
|
222
|
+
const best = pickVariant(result, 800, 'webp'); // smallest variant ≥ 800px
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Exports for adapter authors
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import {
|
|
231
|
+
sniff, mimeTypeFor, extensionFor, // format detection
|
|
232
|
+
buildKey, joinKey, slugify, // key templating
|
|
233
|
+
withRetry, isRetryableError, // retry
|
|
234
|
+
mapLimit, // bounded concurrency
|
|
235
|
+
sha256, shortHash, // hashing
|
|
236
|
+
LensError, // error taxonomy
|
|
237
|
+
} from '@lens-image/core';
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## License
|
|
243
|
+
|
|
244
|
+
MIT
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An in-memory {@link StorageAdapter}.
|
|
3
|
+
*
|
|
4
|
+
* Ships in core on purpose: it is the reference implementation of the adapter
|
|
5
|
+
* contract (about 40 lines of real logic), and it makes every example in the
|
|
6
|
+
* README runnable without credentials or a filesystem.
|
|
7
|
+
*/
|
|
8
|
+
import type { StorageAdapter, StorageContext, StorageFile, StorageObject } from './../types.js';
|
|
9
|
+
/** Options for {@link MemoryAdapter}. */
|
|
10
|
+
export interface MemoryAdapterOptions {
|
|
11
|
+
/** URL scheme used for generated URLs. Default `'memory://'`. */
|
|
12
|
+
readonly baseUrl?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Inject a failure for the first N attempts on any key. Test-only, and the
|
|
15
|
+
* reason retry behaviour is easy to verify without mocking a network.
|
|
16
|
+
*/
|
|
17
|
+
readonly failAttempts?: number;
|
|
18
|
+
}
|
|
19
|
+
/** A stored object. */
|
|
20
|
+
export interface MemoryEntry {
|
|
21
|
+
readonly key: string;
|
|
22
|
+
readonly data: Uint8Array;
|
|
23
|
+
readonly contentType: string;
|
|
24
|
+
readonly cacheControl?: string;
|
|
25
|
+
readonly metadata?: Readonly<Record<string, string>>;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @example
|
|
29
|
+
* ```ts
|
|
30
|
+
* const adapter = new MemoryAdapter();
|
|
31
|
+
* const optimizer = new ImageOptimizer({ adapter });
|
|
32
|
+
*
|
|
33
|
+
* await optimizer.optimize({ source: './photo.jpg', formats: ['webp'] });
|
|
34
|
+
* adapter.keys(); // ['a1b2c3d4e5/photo-original.webp']
|
|
35
|
+
* adapter.get(key)?.data; // the encoded bytes
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare class MemoryAdapter implements StorageAdapter {
|
|
39
|
+
#private;
|
|
40
|
+
readonly name = "memory";
|
|
41
|
+
constructor(options?: MemoryAdapterOptions);
|
|
42
|
+
upload(file: StorageFile, _ctx: StorageContext): Promise<StorageObject>;
|
|
43
|
+
exists(key: string): Promise<boolean>;
|
|
44
|
+
remove(key: string): Promise<void>;
|
|
45
|
+
getUrl(key: string): string;
|
|
46
|
+
/** Reads back a stored object. */
|
|
47
|
+
get(key: string): MemoryEntry | undefined;
|
|
48
|
+
/** Every stored key, in insertion order. */
|
|
49
|
+
keys(): string[];
|
|
50
|
+
/** Number of stored objects. */
|
|
51
|
+
get size(): number;
|
|
52
|
+
/** Drops every object and resets the injected-failure counters. */
|
|
53
|
+
clear(): void;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/adapters/memory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEhG,yCAAyC;AACzC,MAAM,WAAW,oBAAoB;IACnC,iEAAiE;IACjE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAChC;AAED,uBAAuB;AACvB,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACtD;AAED;;;;;;;;;;GAUG;AACH,qBAAa,aAAc,YAAW,cAAc;;IAClD,QAAQ,CAAC,IAAI,YAAY;gBAOb,OAAO,GAAE,oBAAyB;IAKxC,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IA4BvE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIxC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAI3B,kCAAkC;IAClC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAIzC,4CAA4C;IAC5C,IAAI,IAAI,MAAM,EAAE;IAIhB,gCAAgC;IAChC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,mEAAmE;IACnE,KAAK,IAAI,IAAI;CAId"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* An in-memory {@link StorageAdapter}.
|
|
4
|
+
*
|
|
5
|
+
* Ships in core on purpose: it is the reference implementation of the adapter
|
|
6
|
+
* contract (about 40 lines of real logic), and it makes every example in the
|
|
7
|
+
* README runnable without credentials or a filesystem.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.MemoryAdapter = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* const adapter = new MemoryAdapter();
|
|
15
|
+
* const optimizer = new ImageOptimizer({ adapter });
|
|
16
|
+
*
|
|
17
|
+
* await optimizer.optimize({ source: './photo.jpg', formats: ['webp'] });
|
|
18
|
+
* adapter.keys(); // ['a1b2c3d4e5/photo-original.webp']
|
|
19
|
+
* adapter.get(key)?.data; // the encoded bytes
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
class MemoryAdapter {
|
|
23
|
+
name = 'memory';
|
|
24
|
+
#files = new Map();
|
|
25
|
+
#baseUrl;
|
|
26
|
+
#failAttempts;
|
|
27
|
+
#attempts = new Map();
|
|
28
|
+
constructor(options = {}) {
|
|
29
|
+
this.#baseUrl = options.baseUrl ?? 'memory://';
|
|
30
|
+
this.#failAttempts = options.failAttempts ?? 0;
|
|
31
|
+
}
|
|
32
|
+
async upload(file, _ctx) {
|
|
33
|
+
const seen = (this.#attempts.get(file.key) ?? 0) + 1;
|
|
34
|
+
this.#attempts.set(file.key, seen);
|
|
35
|
+
if (seen <= this.#failAttempts) {
|
|
36
|
+
// Shaped like a transient network error so the default retry predicate
|
|
37
|
+
// treats it as retryable.
|
|
38
|
+
const error = new Error(`Simulated transient failure ${seen}/${this.#failAttempts}`);
|
|
39
|
+
error.code = 'ECONNRESET';
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
this.#files.set(file.key, {
|
|
43
|
+
key: file.key,
|
|
44
|
+
data: file.data,
|
|
45
|
+
contentType: file.contentType,
|
|
46
|
+
...(file.cacheControl ? { cacheControl: file.cacheControl } : {}),
|
|
47
|
+
...(file.metadata ? { metadata: file.metadata } : {}),
|
|
48
|
+
});
|
|
49
|
+
return {
|
|
50
|
+
key: file.key,
|
|
51
|
+
url: this.getUrl(file.key),
|
|
52
|
+
size: file.data.byteLength,
|
|
53
|
+
...(file.checksum ? { etag: file.checksum } : {}),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async exists(key) {
|
|
57
|
+
return this.#files.has(key);
|
|
58
|
+
}
|
|
59
|
+
async remove(key) {
|
|
60
|
+
this.#files.delete(key);
|
|
61
|
+
}
|
|
62
|
+
getUrl(key) {
|
|
63
|
+
return `${this.#baseUrl}${key}`;
|
|
64
|
+
}
|
|
65
|
+
/** Reads back a stored object. */
|
|
66
|
+
get(key) {
|
|
67
|
+
return this.#files.get(key);
|
|
68
|
+
}
|
|
69
|
+
/** Every stored key, in insertion order. */
|
|
70
|
+
keys() {
|
|
71
|
+
return [...this.#files.keys()];
|
|
72
|
+
}
|
|
73
|
+
/** Number of stored objects. */
|
|
74
|
+
get size() {
|
|
75
|
+
return this.#files.size;
|
|
76
|
+
}
|
|
77
|
+
/** Drops every object and resets the injected-failure counters. */
|
|
78
|
+
clear() {
|
|
79
|
+
this.#files.clear();
|
|
80
|
+
this.#attempts.clear();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
exports.MemoryAdapter = MemoryAdapter;
|
|
84
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/adapters/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@lens-image/core/browser` - the part of Lens that runs anywhere.
|
|
3
|
+
*
|
|
4
|
+
* Everything re-exported here is pure computation over bytes and strings, with
|
|
5
|
+
* no `node:` imports at all. It works in a browser, a Worker, an edge runtime
|
|
6
|
+
* or React Native.
|
|
7
|
+
*
|
|
8
|
+
* ### Why this exists
|
|
9
|
+
*
|
|
10
|
+
* The main entry point imports `node:crypto` and `node:fs`, so a bundler
|
|
11
|
+
* targeting the browser either fails or ships a polyfill for them. But the
|
|
12
|
+
* *useful* half for a client is Node-free: you can identify an image and read
|
|
13
|
+
* its dimensions from its first few kilobytes, which means you can reject a
|
|
14
|
+
* 40,000 x 40,000 PNG **before** uploading it rather than after.
|
|
15
|
+
*
|
|
16
|
+
* @example Validate before the upload starts
|
|
17
|
+
* ```ts
|
|
18
|
+
* import { sniff, resolveValidation, validateImage, LensError } from '@lens-image/core/browser';
|
|
19
|
+
*
|
|
20
|
+
* const policy = resolveValidation({ maxBytes: 10_000_000, allowedFormats: ['jpeg', 'png', 'webp'] });
|
|
21
|
+
* const header = new Uint8Array(await file.slice(0, 4096).arrayBuffer());
|
|
22
|
+
*
|
|
23
|
+
* try {
|
|
24
|
+
* validateImage({ ...sniff(header, file.size) }, policy, file.name);
|
|
25
|
+
* } catch (error) {
|
|
26
|
+
* if (LensError.is(error)) showToast(error.message); // the same message the server would give
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Note the `file.slice(0, 4096)` - only the header is read, so validating a
|
|
31
|
+
* 40 MB file costs 4 KB.
|
|
32
|
+
*
|
|
33
|
+
* @packageDocumentation
|
|
34
|
+
*/
|
|
35
|
+
export { sniff, sniffFormat, mimeTypeFor, extensionFor, SNIFF_HEADER_BYTES } from './sniff.js';
|
|
36
|
+
export { validateImage, resolveValidation, DEFAULT_MAX_BYTES, DEFAULT_MAX_PIXELS, DEFAULT_ALLOWED_FORMATS, } from './validation.js';
|
|
37
|
+
export type { ResolvedValidation } from './validation.js';
|
|
38
|
+
export { LensError } from './errors.js';
|
|
39
|
+
export type { LensErrorCode, LensErrorDetails } from './errors.js';
|
|
40
|
+
export { normalizeFormat, normalizeFormats, supportsAlpha, isLossy, defaultQuality, defaultEffort, ENCODABLE_FORMATS, } from './format.js';
|
|
41
|
+
export { buildKey, joinKey, slugify, defaultLabel, DEFAULT_KEY_TEMPLATE } from './naming.js';
|
|
42
|
+
export { buildSrcset, toPicture, pickVariant } from './srcset.js';
|
|
43
|
+
export type { PictureData, PictureSource } from './srcset.js';
|
|
44
|
+
export { formatBytes } from './bytes.js';
|
|
45
|
+
export type { DetectedFormat, FitMode, FormatResult, ImageFormat, ImageFormatInput, ImageMetadata, KeyContext, LensWarning, OptimizeResult, Position, ResizeSpec, SizeSpec, ValidationOptions, Variant, } from './types.js';
|
|
46
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/browser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAGH,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAG/F,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,uBAAuB,GACxB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG1D,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAGnE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,OAAO,EACP,cAAc,EACd,aAAa,EACb,iBAAiB,GAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAG7F,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAClE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG9D,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,YAAY,EACV,cAAc,EACd,OAAO,EACP,YAAY,EACZ,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,UAAU,EACV,WAAW,EACX,cAAc,EACd,QAAQ,EACR,UAAU,EACV,QAAQ,EACR,iBAAiB,EACjB,OAAO,GACR,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* `@lens-image/core/browser` - the part of Lens that runs anywhere.
|
|
4
|
+
*
|
|
5
|
+
* Everything re-exported here is pure computation over bytes and strings, with
|
|
6
|
+
* no `node:` imports at all. It works in a browser, a Worker, an edge runtime
|
|
7
|
+
* or React Native.
|
|
8
|
+
*
|
|
9
|
+
* ### Why this exists
|
|
10
|
+
*
|
|
11
|
+
* The main entry point imports `node:crypto` and `node:fs`, so a bundler
|
|
12
|
+
* targeting the browser either fails or ships a polyfill for them. But the
|
|
13
|
+
* *useful* half for a client is Node-free: you can identify an image and read
|
|
14
|
+
* its dimensions from its first few kilobytes, which means you can reject a
|
|
15
|
+
* 40,000 x 40,000 PNG **before** uploading it rather than after.
|
|
16
|
+
*
|
|
17
|
+
* @example Validate before the upload starts
|
|
18
|
+
* ```ts
|
|
19
|
+
* import { sniff, resolveValidation, validateImage, LensError } from '@lens-image/core/browser';
|
|
20
|
+
*
|
|
21
|
+
* const policy = resolveValidation({ maxBytes: 10_000_000, allowedFormats: ['jpeg', 'png', 'webp'] });
|
|
22
|
+
* const header = new Uint8Array(await file.slice(0, 4096).arrayBuffer());
|
|
23
|
+
*
|
|
24
|
+
* try {
|
|
25
|
+
* validateImage({ ...sniff(header, file.size) }, policy, file.name);
|
|
26
|
+
* } catch (error) {
|
|
27
|
+
* if (LensError.is(error)) showToast(error.message); // the same message the server would give
|
|
28
|
+
* }
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* Note the `file.slice(0, 4096)` - only the header is read, so validating a
|
|
32
|
+
* 40 MB file costs 4 KB.
|
|
33
|
+
*
|
|
34
|
+
* @packageDocumentation
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.formatBytes = exports.pickVariant = exports.toPicture = exports.buildSrcset = exports.DEFAULT_KEY_TEMPLATE = exports.defaultLabel = exports.slugify = exports.joinKey = exports.buildKey = exports.ENCODABLE_FORMATS = exports.defaultEffort = exports.defaultQuality = exports.isLossy = exports.supportsAlpha = exports.normalizeFormats = exports.normalizeFormat = exports.LensError = exports.DEFAULT_ALLOWED_FORMATS = exports.DEFAULT_MAX_PIXELS = exports.DEFAULT_MAX_BYTES = exports.resolveValidation = exports.validateImage = exports.SNIFF_HEADER_BYTES = exports.extensionFor = exports.mimeTypeFor = exports.sniffFormat = exports.sniff = void 0;
|
|
38
|
+
// --- Format detection -------------------------------------------------------
|
|
39
|
+
var sniff_js_1 = require("./sniff.js");
|
|
40
|
+
Object.defineProperty(exports, "sniff", { enumerable: true, get: function () { return sniff_js_1.sniff; } });
|
|
41
|
+
Object.defineProperty(exports, "sniffFormat", { enumerable: true, get: function () { return sniff_js_1.sniffFormat; } });
|
|
42
|
+
Object.defineProperty(exports, "mimeTypeFor", { enumerable: true, get: function () { return sniff_js_1.mimeTypeFor; } });
|
|
43
|
+
Object.defineProperty(exports, "extensionFor", { enumerable: true, get: function () { return sniff_js_1.extensionFor; } });
|
|
44
|
+
Object.defineProperty(exports, "SNIFF_HEADER_BYTES", { enumerable: true, get: function () { return sniff_js_1.SNIFF_HEADER_BYTES; } });
|
|
45
|
+
// --- Validation -------------------------------------------------------------
|
|
46
|
+
var validation_js_1 = require("./validation.js");
|
|
47
|
+
Object.defineProperty(exports, "validateImage", { enumerable: true, get: function () { return validation_js_1.validateImage; } });
|
|
48
|
+
Object.defineProperty(exports, "resolveValidation", { enumerable: true, get: function () { return validation_js_1.resolveValidation; } });
|
|
49
|
+
Object.defineProperty(exports, "DEFAULT_MAX_BYTES", { enumerable: true, get: function () { return validation_js_1.DEFAULT_MAX_BYTES; } });
|
|
50
|
+
Object.defineProperty(exports, "DEFAULT_MAX_PIXELS", { enumerable: true, get: function () { return validation_js_1.DEFAULT_MAX_PIXELS; } });
|
|
51
|
+
Object.defineProperty(exports, "DEFAULT_ALLOWED_FORMATS", { enumerable: true, get: function () { return validation_js_1.DEFAULT_ALLOWED_FORMATS; } });
|
|
52
|
+
// --- Errors -----------------------------------------------------------------
|
|
53
|
+
var errors_js_1 = require("./errors.js");
|
|
54
|
+
Object.defineProperty(exports, "LensError", { enumerable: true, get: function () { return errors_js_1.LensError; } });
|
|
55
|
+
// --- Formats ----------------------------------------------------------------
|
|
56
|
+
var format_js_1 = require("./format.js");
|
|
57
|
+
Object.defineProperty(exports, "normalizeFormat", { enumerable: true, get: function () { return format_js_1.normalizeFormat; } });
|
|
58
|
+
Object.defineProperty(exports, "normalizeFormats", { enumerable: true, get: function () { return format_js_1.normalizeFormats; } });
|
|
59
|
+
Object.defineProperty(exports, "supportsAlpha", { enumerable: true, get: function () { return format_js_1.supportsAlpha; } });
|
|
60
|
+
Object.defineProperty(exports, "isLossy", { enumerable: true, get: function () { return format_js_1.isLossy; } });
|
|
61
|
+
Object.defineProperty(exports, "defaultQuality", { enumerable: true, get: function () { return format_js_1.defaultQuality; } });
|
|
62
|
+
Object.defineProperty(exports, "defaultEffort", { enumerable: true, get: function () { return format_js_1.defaultEffort; } });
|
|
63
|
+
Object.defineProperty(exports, "ENCODABLE_FORMATS", { enumerable: true, get: function () { return format_js_1.ENCODABLE_FORMATS; } });
|
|
64
|
+
// --- Key templating ---------------------------------------------------------
|
|
65
|
+
var naming_js_1 = require("./naming.js");
|
|
66
|
+
Object.defineProperty(exports, "buildKey", { enumerable: true, get: function () { return naming_js_1.buildKey; } });
|
|
67
|
+
Object.defineProperty(exports, "joinKey", { enumerable: true, get: function () { return naming_js_1.joinKey; } });
|
|
68
|
+
Object.defineProperty(exports, "slugify", { enumerable: true, get: function () { return naming_js_1.slugify; } });
|
|
69
|
+
Object.defineProperty(exports, "defaultLabel", { enumerable: true, get: function () { return naming_js_1.defaultLabel; } });
|
|
70
|
+
Object.defineProperty(exports, "DEFAULT_KEY_TEMPLATE", { enumerable: true, get: function () { return naming_js_1.DEFAULT_KEY_TEMPLATE; } });
|
|
71
|
+
// --- HTML helpers -----------------------------------------------------------
|
|
72
|
+
var srcset_js_1 = require("./srcset.js");
|
|
73
|
+
Object.defineProperty(exports, "buildSrcset", { enumerable: true, get: function () { return srcset_js_1.buildSrcset; } });
|
|
74
|
+
Object.defineProperty(exports, "toPicture", { enumerable: true, get: function () { return srcset_js_1.toPicture; } });
|
|
75
|
+
Object.defineProperty(exports, "pickVariant", { enumerable: true, get: function () { return srcset_js_1.pickVariant; } });
|
|
76
|
+
// --- Formatting -------------------------------------------------------------
|
|
77
|
+
var bytes_js_1 = require("./bytes.js");
|
|
78
|
+
Object.defineProperty(exports, "formatBytes", { enumerable: true, get: function () { return bytes_js_1.formatBytes; } });
|
|
79
|
+
//# sourceMappingURL=browser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/browser.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;;;AAEH,+EAA+E;AAC/E,uCAA+F;AAAtF,iGAAA,KAAK,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,uGAAA,WAAW,OAAA;AAAE,wGAAA,YAAY,OAAA;AAAE,8GAAA,kBAAkB,OAAA;AAE1E,+EAA+E;AAC/E,iDAMyB;AALvB,8GAAA,aAAa,OAAA;AACb,kHAAA,iBAAiB,OAAA;AACjB,kHAAA,iBAAiB,OAAA;AACjB,mHAAA,kBAAkB,OAAA;AAClB,wHAAA,uBAAuB,OAAA;AAIzB,+EAA+E;AAC/E,yCAAwC;AAA/B,sGAAA,SAAS,OAAA;AAGlB,+EAA+E;AAC/E,yCAQqB;AAPnB,4GAAA,eAAe,OAAA;AACf,6GAAA,gBAAgB,OAAA;AAChB,0GAAA,aAAa,OAAA;AACb,oGAAA,OAAO,OAAA;AACP,2GAAA,cAAc,OAAA;AACd,0GAAA,aAAa,OAAA;AACb,8GAAA,iBAAiB,OAAA;AAGnB,+EAA+E;AAC/E,yCAA6F;AAApF,qGAAA,QAAQ,OAAA;AAAE,oGAAA,OAAO,OAAA;AAAE,oGAAA,OAAO,OAAA;AAAE,yGAAA,YAAY,OAAA;AAAE,iHAAA,oBAAoB,OAAA;AAEvE,+EAA+E;AAC/E,yCAAkE;AAAzD,wGAAA,WAAW,OAAA;AAAE,sGAAA,SAAS,OAAA;AAAE,wGAAA,WAAW,OAAA;AAG5C,+EAA+E;AAC/E,uCAAyC;AAAhC,uGAAA,WAAW,OAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Byte formatting.
|
|
3
|
+
*
|
|
4
|
+
* Its own module so that `validation.ts` - and therefore the whole `./browser`
|
|
5
|
+
* entry point - does not have to import `source.ts`, which pulls in `node:fs`
|
|
6
|
+
* and `node:path` at the top level.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Human-readable byte count, using binary units.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* formatBytes(900); // '900 B'
|
|
14
|
+
* formatBytes(5_242_880); // '5.0 MiB'
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function formatBytes(bytes: number): string;
|
|
18
|
+
//# sourceMappingURL=bytes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bytes.d.ts","sourceRoot":"","sources":["../../src/bytes.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMjD"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Byte formatting.
|
|
4
|
+
*
|
|
5
|
+
* Its own module so that `validation.ts` - and therefore the whole `./browser`
|
|
6
|
+
* entry point - does not have to import `source.ts`, which pulls in `node:fs`
|
|
7
|
+
* and `node:path` at the top level.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.formatBytes = formatBytes;
|
|
11
|
+
/**
|
|
12
|
+
* Human-readable byte count, using binary units.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* formatBytes(900); // '900 B'
|
|
17
|
+
* formatBytes(5_242_880); // '5.0 MiB'
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
function formatBytes(bytes) {
|
|
21
|
+
if (!Number.isFinite(bytes) || bytes < 0)
|
|
22
|
+
return '0 B';
|
|
23
|
+
if (bytes < 1024)
|
|
24
|
+
return `${Math.round(bytes)} B`;
|
|
25
|
+
if (bytes < 1024 * 1024)
|
|
26
|
+
return `${(bytes / 1024).toFixed(1)} KiB`;
|
|
27
|
+
if (bytes < 1024 * 1024 * 1024)
|
|
28
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MiB`;
|
|
29
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GiB`;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=bytes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bytes.js","sourceRoot":"","sources":["../../src/bytes.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAWH,kCAMC;AAfD;;;;;;;;GAQG;AACH,SAAgB,WAAW,CAAC,KAAa;IACvC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACvD,IAAI,KAAK,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC;IAClD,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACnE,IAAI,KAAK,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI;QAAE,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IACnF,OAAO,GAAG,CAAC,KAAK,GAAG,CAAC,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AAC5D,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Result caching.
|
|
3
|
+
*
|
|
4
|
+
* The expensive part of an optimize run is the encode, and the result is a pure
|
|
5
|
+
* function of (source bytes, options). So the cache key is a hash of both, and
|
|
6
|
+
* a hit skips encoding *and* uploading entirely.
|
|
7
|
+
*
|
|
8
|
+
* Three strategies, in increasing order of how much they trust the world:
|
|
9
|
+
*
|
|
10
|
+
* - `'memory'` - process-lifetime LRU. Fast, but a fresh deploy starts cold and
|
|
11
|
+
* a second instance does the work again.
|
|
12
|
+
* - `'storage'` - memory, plus `adapter.exists()` so a warm bucket short-circuits
|
|
13
|
+
* a cold process.
|
|
14
|
+
* - a {@link CacheStore} - your own Redis/SQLite, shared across instances.
|
|
15
|
+
*/
|
|
16
|
+
import type { CacheStore, OptimizeResult } from './types.js';
|
|
17
|
+
/**
|
|
18
|
+
* A bounded LRU map.
|
|
19
|
+
*
|
|
20
|
+
* Insertion-ordered `Map` iteration gives us the eviction order for free: the
|
|
21
|
+
* first key `keys().next()` yields is the least recently used, because reads
|
|
22
|
+
* re-insert.
|
|
23
|
+
*/
|
|
24
|
+
export declare class MemoryCacheStore implements CacheStore {
|
|
25
|
+
#private;
|
|
26
|
+
/** @param max - Entries to keep before evicting the least recently used. */
|
|
27
|
+
constructor(max?: number);
|
|
28
|
+
get(key: string): OptimizeResult | undefined;
|
|
29
|
+
set(key: string, value: OptimizeResult): void;
|
|
30
|
+
delete(key: string): void;
|
|
31
|
+
/** Drops every entry. */
|
|
32
|
+
clear(): void;
|
|
33
|
+
/** Current entry count. Handy in tests and metrics. */
|
|
34
|
+
get size(): number;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A cache store that never stores anything.
|
|
38
|
+
*
|
|
39
|
+
* Lets the optimizer treat "caching off" as just another store, rather than
|
|
40
|
+
* branching on null at every call site.
|
|
41
|
+
*/
|
|
42
|
+
export declare const nullCacheStore: CacheStore;
|
|
43
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,UAAU;;IAIjD,4EAA4E;gBAChE,GAAG,SAAM;IAIrB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAS5C,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,cAAc,GAAG,IAAI;IAW7C,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,yBAAyB;IACzB,KAAK,IAAI,IAAI;IAIb,uDAAuD;IACvD,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF;AAED;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,UAI5B,CAAC"}
|