@thi.ng/imago 1.2.4 → 1.4.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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Change Log
2
2
 
3
- - **Last updated**: 2025-07-21T08:21:58Z
3
+ - **Last updated**: 2025-07-25T00:04:12Z
4
4
  - **Generator**: [thi.ng/monopub](https://thi.ng/monopub)
5
5
 
6
6
  All notable changes to this project will be documented in this file.
@@ -11,6 +11,20 @@ See [Conventional Commits](https://conventionalcommits.org/) for commit guidelin
11
11
  **Note:** Unlisted _patch_ versions only involve non-code or otherwise excluded changes
12
12
  and/or version bumps of transitive dependencies.
13
13
 
14
+ ## [1.4.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/imago@1.4.0) (2025-07-25)
15
+
16
+ #### 🚀 Features
17
+
18
+ - add support for absolute output paths ([c440e25](https://github.com/thi-ng/umbrella/commit/c440e25))
19
+ - update `formatPath()` to always return abs path
20
+ - simplify `outputProc()` path handling
21
+
22
+ ## [1.3.0](https://github.com/thi-ng/umbrella/tree/@thi.ng/imago@1.3.0) (2025-07-21)
23
+
24
+ #### 🚀 Features
25
+
26
+ - add `dataURL` output option ([eca0ee3](https://github.com/thi-ng/umbrella/commit/eca0ee3))
27
+
14
28
  ### [1.2.1](https://github.com/thi-ng/umbrella/tree/@thi.ng/imago@1.2.1) (2025-07-14)
15
29
 
16
30
  #### 🩹 Bug fixes
package/README.md CHANGED
@@ -375,7 +375,7 @@ For Node.js REPL:
375
375
  const imago = await import("@thi.ng/imago");
376
376
  ```
377
377
 
378
- Package sizes (brotli'd, pre-treeshake): ESM: 5.08 KB
378
+ Package sizes (brotli'd, pre-treeshake): ESM: 5.17 KB
379
379
 
380
380
  ## Dependencies
381
381
 
package/api.d.ts CHANGED
@@ -372,6 +372,19 @@ export interface OutputSpec extends ProcSpec {
372
372
  * WebP output options. See [Sharp docs](https://sharp.pixelplumbing.com/api-output#webp)
373
373
  */
374
374
  webp?: WebpOptions;
375
+ /**
376
+ * Only used if {@link OutputSpec.path} is NOT set. If true, output will be
377
+ * captured as data URL, otherwise as binary data/buffer.
378
+ *
379
+ * @remarks
380
+ * Other conditions:
381
+ *
382
+ * - Requires {@link OutputSpec.format} to be set to a data URL compatible
383
+ * image format.
384
+ * - An error will be thrown during processing if the encoded image size
385
+ * exceeds 32KB.
386
+ */
387
+ dataURL?: boolean;
375
388
  }
376
389
  export interface ResizeSpec extends ProcSpec {
377
390
  op: "resize";
package/ops/output.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  import type { Processor } from "../api.js";
2
2
  export declare const outputProc: Processor;
3
+ export declare const asDataURL: (mime: string, data: Buffer) => string;
3
4
  //# sourceMappingURL=output.d.ts.map
package/ops/output.js CHANGED
@@ -1,19 +1,18 @@
1
1
  import { encode } from "@thi.ng/blurhash";
2
2
  import { isNumber, isPlainObject } from "@thi.ng/checks";
3
+ import { illegalArgs } from "@thi.ng/errors";
3
4
  import { writeFile, writeJSON } from "@thi.ng/file-io";
4
- import { firstNonNullKey } from "@thi.ng/object-utils/first-non-null";
5
- import { join, resolve } from "node:path";
5
+ import { firstNonNullKey } from "@thi.ng/object-utils";
6
6
  import { formatPath } from "../path.js";
7
7
  const outputProc = async (spec, input, ctx) => {
8
8
  const opts = spec;
9
- const outDir = resolve(ctx.opts.outDir || ".");
10
9
  let output = input.clone();
11
10
  if (opts.blurhash) {
12
11
  await __outputBlurHash(opts, output, ctx);
13
12
  return [input, false];
14
13
  }
15
14
  if (opts.raw) {
16
- await __outputRaw(opts, output, ctx, outDir);
15
+ await __outputRaw(opts, output, ctx);
17
16
  return [input, false];
18
17
  }
19
18
  if (ctx.meta.exif && ctx.opts.keepEXIF) {
@@ -72,18 +71,15 @@ const outputProc = async (spec, input, ctx) => {
72
71
  if (format) output = output.toFormat(format);
73
72
  const result = await output.toBuffer();
74
73
  if (opts.path !== void 0) {
75
- const path = join(
76
- outDir,
77
- formatPath(opts.path, ctx, spec, result)
78
- );
74
+ const path = formatPath(opts.path, ctx, spec, result);
79
75
  writeFile(path, result, null, ctx.logger);
80
76
  ctx.outputs[opts.id] = path;
81
77
  } else {
82
- ctx.outputs[opts.id] = result;
78
+ ctx.outputs[opts.id] = format && opts.dataURL ? asDataURL(`image/${format}`, result) : result;
83
79
  }
84
80
  return [input, false];
85
81
  };
86
- const __outputRaw = async (opts, output, ctx, outDir) => {
82
+ const __outputRaw = async (opts, output, ctx) => {
87
83
  const { alpha = false, meta = false } = isPlainObject(opts.raw) ? opts.raw : {};
88
84
  if (alpha) output = output.ensureAlpha();
89
85
  else output = output.removeAlpha();
@@ -92,7 +88,7 @@ const __outputRaw = async (opts, output, ctx, outDir) => {
92
88
  ctx.outputMeta[opts.id] = { ...info, exif: ctx.exif };
93
89
  }
94
90
  if (opts.path !== void 0) {
95
- const path = join(outDir, formatPath(opts.path, ctx, opts, data));
91
+ const path = formatPath(opts.path, ctx, opts, data);
96
92
  writeFile(path, data, null, ctx.logger);
97
93
  ctx.outputs[opts.id] = path;
98
94
  if (meta) {
@@ -122,6 +118,12 @@ const __outputBlurHash = async (opts, output, ctx) => {
122
118
  ctx.logger.debug("computed blurhash:", hash);
123
119
  ctx.outputs[opts.id] = hash;
124
120
  };
121
+ const asDataURL = (mime, data) => {
122
+ if (data.length > 32768)
123
+ illegalArgs("encoded image too large for dataURL (max. 32KB allowed)");
124
+ return `data:${mime};base64,${data.toString("base64")}`;
125
+ };
125
126
  export {
127
+ asDataURL,
126
128
  outputProc
127
129
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thi.ng/imago",
3
- "version": "1.2.4",
3
+ "version": "1.4.0",
4
4
  "description": "JSON & API-based declarative and extensible image processing trees/pipelines",
5
5
  "type": "module",
6
6
  "module": "./index.js",
@@ -54,7 +54,7 @@
54
54
  "sharp": "^0.34.3"
55
55
  },
56
56
  "devDependencies": {
57
- "@thi.ng/vectors": "^8.4.1",
57
+ "@thi.ng/vectors": "^8.5.0",
58
58
  "@types/node": "^24.0.15",
59
59
  "esbuild": "^0.25.8",
60
60
  "typedoc": "^0.28.7",
@@ -189,5 +189,5 @@
189
189
  "status": "alpha",
190
190
  "year": 2024
191
191
  },
192
- "gitHead": "11747c482773d3da03c8b7848b25a56251ccd759\n"
192
+ "gitHead": "b8784efbd8beaa2ff296bb8fb802cb73f6d881ac\n"
193
193
  }
package/path.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  import type { TypedArray } from "@thi.ng/api";
2
2
  import type { ImgProcCtx, OutputSpec } from "./api.js";
3
3
  /**
4
- * Expands/replaces all `{xyz}`-templated identifiers in given file path.
4
+ * Expands/replaces all `{xyz}`-templated identifiers in given file path and
5
+ * makes result an absolute path (if needed).
5
6
  *
6
7
  * @remarks
8
+ * If the input path is not yet absolute, it will be joined with the
9
+ * {@link ImgProcOpts.outDir}.
10
+ *
7
11
  * The following built-in IDs are supported and custom IDs will be looked up via
8
12
  * the {@link ImgProcOpts.pathParts} options provided to {@link processImage}.
9
13
  * Any others will remain as is. Custom IDs take precedence over built-in ones.
package/path.js CHANGED
@@ -12,57 +12,60 @@ import {
12
12
  } from "@thi.ng/date";
13
13
  import { illegalArgs as unsupported } from "@thi.ng/errors";
14
14
  import { createHash } from "node:crypto";
15
- import { basename } from "node:path";
15
+ import { basename, isAbsolute, join, resolve } from "node:path";
16
16
  const _ = void 0;
17
- const formatPath = (path, ctx, spec, buf) => path.replace(/\{(\w+)\}/g, (match, id) => {
18
- const custom = ctx.opts.pathParts?.[id];
19
- if (custom != null) {
20
- return isFunction(custom) ? custom(ctx, spec, buf) : custom;
21
- }
22
- switch (id) {
23
- case "name": {
24
- !path && unsupported(
25
- "cannot format `{name}`, image has no file source"
26
- );
27
- const name = basename(ctx.path);
28
- const idx = name.lastIndexOf(".");
29
- return idx > 0 ? name.substring(0, idx) : name;
17
+ const formatPath = (path, ctx, spec, buf) => {
18
+ path = path.replace(/\{(\w+)\}/g, (match, id) => {
19
+ const custom = ctx.opts.pathParts?.[id];
20
+ if (custom != null) {
21
+ return isFunction(custom) ? custom(ctx, spec, buf) : custom;
30
22
  }
31
- case "sha1":
32
- case "sha224":
33
- case "sha256":
34
- case "sha384":
35
- case "sha512":
36
- return createHash(id).update(buf).digest("hex").substring(0, 8);
37
- case "w":
38
- return String(ctx.size[0]);
39
- case "h":
40
- return String(ctx.size[1]);
41
- case "aspect": {
42
- const [w, h] = ctx.size;
43
- return w > h ? "l" : w < h ? "p" : "sq";
23
+ switch (id) {
24
+ case "name": {
25
+ !path && unsupported(
26
+ "cannot format `{name}`, image has no file source"
27
+ );
28
+ const name = basename(ctx.path);
29
+ const idx = name.lastIndexOf(".");
30
+ return idx > 0 ? name.substring(0, idx) : name;
31
+ }
32
+ case "sha1":
33
+ case "sha224":
34
+ case "sha256":
35
+ case "sha384":
36
+ case "sha512":
37
+ return createHash(id).update(buf).digest("hex").substring(0, 8);
38
+ case "w":
39
+ return String(ctx.size[0]);
40
+ case "h":
41
+ return String(ctx.size[1]);
42
+ case "aspect": {
43
+ const [w, h] = ctx.size;
44
+ return w > h ? "l" : w < h ? "p" : "sq";
45
+ }
46
+ case "date":
47
+ return FMT_yyyyMMdd_ALT(_, true);
48
+ case "time":
49
+ return FMT_HHmmss_ALT(_, true);
50
+ case "year":
51
+ return FMT_yyyy(_, true);
52
+ case "month":
53
+ return FMT_MM(_, true);
54
+ case "week":
55
+ return FMT_ww(_, true);
56
+ case "day":
57
+ return FMT_dd(_, true);
58
+ case "hour":
59
+ return FMT_HH(_, true);
60
+ case "minute":
61
+ return FMT_mm(_, true);
62
+ case "second":
63
+ return FMT_ss(_, true);
44
64
  }
45
- case "date":
46
- return FMT_yyyyMMdd_ALT(_, true);
47
- case "time":
48
- return FMT_HHmmss_ALT(_, true);
49
- case "year":
50
- return FMT_yyyy(_, true);
51
- case "month":
52
- return FMT_MM(_, true);
53
- case "week":
54
- return FMT_ww(_, true);
55
- case "day":
56
- return FMT_dd(_, true);
57
- case "hour":
58
- return FMT_HH(_, true);
59
- case "minute":
60
- return FMT_mm(_, true);
61
- case "second":
62
- return FMT_ss(_, true);
63
- }
64
- return match;
65
- });
65
+ return match;
66
+ });
67
+ return isAbsolute(path) ? path : join(resolve(ctx.opts.outDir || "."), path);
68
+ };
66
69
  export {
67
70
  formatPath
68
71
  };