@lambdaimg/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/README.md +17 -0
- package/dist/constants.d.ts +7 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +6 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/url.d.ts +22 -0
- package/dist/url.d.ts.map +1 -0
- package/dist/url.js +73 -0
- package/dist/url.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @lambdaimg/core
|
|
2
|
+
|
|
3
|
+
Core URL and responsive image helpers for LambdaImg.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { buildOriginalUrl, buildResizedUrl, buildSrcSet } from "@lambdaimg/core";
|
|
7
|
+
|
|
8
|
+
buildOriginalUrl("products/chair.jpeg");
|
|
9
|
+
buildResizedUrl("products/chair.jpeg", 640);
|
|
10
|
+
buildSrcSet("products/chair.jpeg", { baseUrl: "https://images.example.com" });
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
LambdaImg derivative URLs are canonicalized as:
|
|
14
|
+
|
|
15
|
+
```txt
|
|
16
|
+
/_/w{width}/{originalKey}/{filename}.webp
|
|
17
|
+
```
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const ALLOWED_IMAGE_WIDTHS: readonly [160, 320, 480, 640, 828, 1080, 1440, 1920];
|
|
2
|
+
export type ImageWidth = (typeof ALLOWED_IMAGE_WIDTHS)[number];
|
|
3
|
+
export declare const DERIVED_PREFIX = "_";
|
|
4
|
+
export declare const WEBP_CONTENT_TYPE = "image/webp";
|
|
5
|
+
export declare const RESIZED_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
6
|
+
export declare const NOT_FOUND_CACHE_CONTROL = "public, max-age=60";
|
|
7
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,YAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAEzF,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/D,eAAO,MAAM,cAAc,MAAM,CAAC;AAElC,eAAO,MAAM,iBAAiB,eAAe,CAAC;AAE9C,eAAO,MAAM,qBAAqB,wCAAwC,CAAC;AAE3E,eAAO,MAAM,uBAAuB,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const ALLOWED_IMAGE_WIDTHS = [160, 320, 480, 640, 828, 1080, 1440, 1920];
|
|
2
|
+
export const DERIVED_PREFIX = "_";
|
|
3
|
+
export const WEBP_CONTENT_TYPE = "image/webp";
|
|
4
|
+
export const RESIZED_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
5
|
+
export const NOT_FOUND_CACHE_CONTROL = "public, max-age=60";
|
|
6
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;AAIzF,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAC;AAElC,MAAM,CAAC,MAAM,iBAAiB,GAAG,YAAY,CAAC;AAE9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,qCAAqC,CAAC;AAE3E,MAAM,CAAC,MAAM,uBAAuB,GAAG,oBAAoB,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ALLOWED_IMAGE_WIDTHS, DERIVED_PREFIX, NOT_FOUND_CACHE_CONTROL, RESIZED_CACHE_CONTROL, WEBP_CONTENT_TYPE, type ImageWidth, } from "./constants.js";
|
|
2
|
+
export { buildOriginalUrl, buildResizedUrl, buildSrcSet, canonicalFilename, isAllowedImageWidth, normalizeBaseUrl, normalizeImageKey, parseResizedPath, resizedS3Key, type BuildImageUrlOptions, type BuildSrcSetOptions, type ParsedResizedPath, } from "./url.js";
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,EACjB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,EACZ,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,UAAU,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { ALLOWED_IMAGE_WIDTHS, DERIVED_PREFIX, NOT_FOUND_CACHE_CONTROL, RESIZED_CACHE_CONTROL, WEBP_CONTENT_TYPE, } from "./constants.js";
|
|
2
|
+
export { buildOriginalUrl, buildResizedUrl, buildSrcSet, canonicalFilename, isAllowedImageWidth, normalizeBaseUrl, normalizeImageKey, parseResizedPath, resizedS3Key, } from "./url.js";
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oBAAoB,EACpB,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,iBAAiB,GAElB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,YAAY,GAIb,MAAM,UAAU,CAAC"}
|
package/dist/url.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type ImageWidth } from "./constants.js";
|
|
2
|
+
export interface ParsedResizedPath {
|
|
3
|
+
width: ImageWidth;
|
|
4
|
+
originalKey: string;
|
|
5
|
+
resizedKey: string;
|
|
6
|
+
}
|
|
7
|
+
export interface BuildImageUrlOptions {
|
|
8
|
+
baseUrl?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface BuildSrcSetOptions extends BuildImageUrlOptions {
|
|
11
|
+
widths?: readonly ImageWidth[];
|
|
12
|
+
}
|
|
13
|
+
export declare function canonicalFilename(s3Key: string): string;
|
|
14
|
+
export declare function isAllowedImageWidth(width: number): width is ImageWidth;
|
|
15
|
+
export declare function normalizeImageKey(imageKey: string): string;
|
|
16
|
+
export declare function normalizeBaseUrl(baseUrl?: string): string;
|
|
17
|
+
export declare function buildOriginalUrl(s3Key: string, options?: BuildImageUrlOptions): string;
|
|
18
|
+
export declare function buildResizedUrl(s3Key: string, width: ImageWidth, options?: BuildImageUrlOptions): string;
|
|
19
|
+
export declare function buildSrcSet(s3Key: string, options?: BuildSrcSetOptions): string;
|
|
20
|
+
export declare function resizedS3Key(s3Key: string, width: ImageWidth): string;
|
|
21
|
+
export declare function parseResizedPath(pathname: string): ParsedResizedPath | null;
|
|
22
|
+
//# sourceMappingURL=url.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.d.ts","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwC,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEvF,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,UAAU,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAmB,SAAQ,oBAAoB;IAC9D,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAC;CAChC;AAID,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOvD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,CAEtE;AAED,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,SAAK,GAAG,MAAM,CAErD;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAE1F;AAED,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,UAAU,EACjB,OAAO,GAAE,oBAAyB,GACjC,MAAM,CAIR;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,MAAM,CAGnF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,MAAM,CAErE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAqC3E"}
|
package/dist/url.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { ALLOWED_IMAGE_WIDTHS, DERIVED_PREFIX } from "./constants.js";
|
|
2
|
+
const RESIZED_PATH_RE = /^\/_\/w(\d+)\/(.+)\.webp$/;
|
|
3
|
+
export function canonicalFilename(s3Key) {
|
|
4
|
+
const lastSegment = s3Key.split("/").pop() ?? s3Key;
|
|
5
|
+
const dotIndex = lastSegment.lastIndexOf(".");
|
|
6
|
+
if (dotIndex <= 0) {
|
|
7
|
+
return lastSegment;
|
|
8
|
+
}
|
|
9
|
+
return lastSegment.slice(0, dotIndex);
|
|
10
|
+
}
|
|
11
|
+
export function isAllowedImageWidth(width) {
|
|
12
|
+
return ALLOWED_IMAGE_WIDTHS.includes(width);
|
|
13
|
+
}
|
|
14
|
+
export function normalizeImageKey(imageKey) {
|
|
15
|
+
return imageKey.replace(/^\/+/, "");
|
|
16
|
+
}
|
|
17
|
+
export function normalizeBaseUrl(baseUrl = "") {
|
|
18
|
+
return baseUrl.replace(/\/+$/, "");
|
|
19
|
+
}
|
|
20
|
+
export function buildOriginalUrl(s3Key, options = {}) {
|
|
21
|
+
return withBaseUrl(options.baseUrl, `/${normalizeImageKey(s3Key)}`);
|
|
22
|
+
}
|
|
23
|
+
export function buildResizedUrl(s3Key, width, options = {}) {
|
|
24
|
+
const normalizedKey = normalizeImageKey(s3Key);
|
|
25
|
+
const filename = canonicalFilename(normalizedKey);
|
|
26
|
+
return withBaseUrl(options.baseUrl, `/_/${`w${width}`}/${normalizedKey}/${filename}.webp`);
|
|
27
|
+
}
|
|
28
|
+
export function buildSrcSet(s3Key, options = {}) {
|
|
29
|
+
const widths = options.widths ?? ALLOWED_IMAGE_WIDTHS;
|
|
30
|
+
return widths.map((width) => `${buildResizedUrl(s3Key, width, options)} ${width}w`).join(", ");
|
|
31
|
+
}
|
|
32
|
+
export function resizedS3Key(s3Key, width) {
|
|
33
|
+
return buildResizedUrl(s3Key, width).slice(1);
|
|
34
|
+
}
|
|
35
|
+
export function parseResizedPath(pathname) {
|
|
36
|
+
const match = RESIZED_PATH_RE.exec(pathname);
|
|
37
|
+
if (!match?.[1] || !match[2]) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const width = Number.parseInt(match[1], 10);
|
|
41
|
+
if (!isAllowedImageWidth(width)) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
const middle = match[2];
|
|
45
|
+
const lastSlash = middle.lastIndexOf("/");
|
|
46
|
+
if (lastSlash === -1) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const originalKey = middle.slice(0, lastSlash);
|
|
50
|
+
const filename = middle.slice(lastSlash + 1);
|
|
51
|
+
if (originalKey.length === 0 || filename.length === 0) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (originalKey.startsWith(`${DERIVED_PREFIX}/`)) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
if (canonicalFilename(originalKey) !== filename) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
width,
|
|
62
|
+
originalKey,
|
|
63
|
+
resizedKey: pathname.slice(1),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function withBaseUrl(baseUrl, pathname) {
|
|
67
|
+
const normalizedBaseUrl = normalizeBaseUrl(baseUrl);
|
|
68
|
+
if (!normalizedBaseUrl) {
|
|
69
|
+
return pathname;
|
|
70
|
+
}
|
|
71
|
+
return `${normalizedBaseUrl}${pathname}`;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=url.js.map
|
package/dist/url.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"url.js","sourceRoot":"","sources":["../src/url.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lambdaimg/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Core URL and srcset primitives for LambdaImg.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/jperezrealini/lambdaimg.git",
|
|
9
|
+
"directory": "packages/core"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"type": "module",
|
|
16
|
+
"sideEffects": false,
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"default": "./dist/index.js",
|
|
20
|
+
"types": "./dist/index.d.ts"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -p tsconfig.build.json",
|
|
28
|
+
"check": "oxlint && oxfmt --check && tsc --noEmit -p tsconfig.json",
|
|
29
|
+
"fix": "oxlint --fix && oxfmt",
|
|
30
|
+
"test": "bun test"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/bun": "catalog:",
|
|
34
|
+
"@types/node": "catalog:"
|
|
35
|
+
}
|
|
36
|
+
}
|