pixeli 1.0.3 → 1.0.4
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 +21 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/jobs/batchRunner.d.ts +44 -0
- package/dist/core/jobs/batchRunner.js +90 -0
- package/dist/core/jobs/types.d.ts +10 -0
- package/dist/core/jobs/types.js +1 -0
- package/dist/core/merges/index.d.ts +2 -0
- package/dist/core/merges/index.js +1 -0
- package/dist/core/merges/types.d.ts +23 -2
- package/dist/core/modules/typedEventEmitter.d.ts +7 -0
- package/dist/core/modules/typedEventEmitter.js +9 -0
- package/dist/core/schemas/mergeJob.d.ts +11 -0
- package/dist/core/schemas/mergeJob.js +6 -0
- package/dist/validators/outputFile.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,6 +51,7 @@ The tool currently supports four main layout modes: ***Grid***, ***Masonry*** (h
|
|
|
51
51
|
* [All Supported Input Formats](#all-supported-input-formats)
|
|
52
52
|
* [All Supported Output Formats](#all-supported-output-formats)
|
|
53
53
|
* [Pixel Limits](#pixel-limits)
|
|
54
|
+
* [Error Handling](#error-handling)
|
|
54
55
|
* [Colors and Transparency](#colors-and-transparency)
|
|
55
56
|
* [CLI](#cli)
|
|
56
57
|
* [Library](#library)
|
|
@@ -471,6 +472,26 @@ Generating extremely large images significantly reduces speed, and may also lead
|
|
|
471
472
|
| **BMP** | `.bmp` | **32,767×32,767 or ~2,147,483,647×2,147,483,647 px** | Depends on version/fields. |
|
|
472
473
|
| **TIFF** | `.tif`, `.tiff` | **4,294,967,295 × 4,294,967,295 px** (theoretical) | Very large; may be limited by software or memory. |
|
|
473
474
|
|
|
475
|
+
### Error Handling
|
|
476
|
+
All errors produced by this library are instances of the `MergeError` class, which extends the `Error` class.
|
|
477
|
+
|
|
478
|
+
In addition to the inherited `message` property, the `MergeError` class also has a `context` object parameter attached to it, where there is a `type` and an optional `cause` property. Consider the following:
|
|
479
|
+
|
|
480
|
+
```typescript
|
|
481
|
+
throw new MergeError("Some error has occurred", {
|
|
482
|
+
type: 'internal',
|
|
483
|
+
cause: 'helper "trimmedMedian" failed'
|
|
484
|
+
});
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
The `cause` property is only used when an internal error has occurred, meaning that there is something wrong with the API.
|
|
488
|
+
|
|
489
|
+
The `type` property can be one of the following:
|
|
490
|
+
- `validation`: One of the options provided to the merge function is incorrect or is required but not provided.
|
|
491
|
+
- `image`: An invalid, corrupt, or empty image input list has been provided.
|
|
492
|
+
- `internal`: An error with the interal library. Should not occur.
|
|
493
|
+
|
|
494
|
+
|
|
474
495
|
### Colors and Transparency
|
|
475
496
|
|
|
476
497
|
#### CLI
|
package/dist/core/index.d.ts
CHANGED
package/dist/core/index.js
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { MergeError } from '../mergeError.js';
|
|
2
|
+
import { TypedEventEmitter } from '../modules/typedEventEmitter.js';
|
|
3
|
+
import type { BatchOptions, MergeJob } from './types.js';
|
|
4
|
+
type MergeResult = {
|
|
5
|
+
index: number;
|
|
6
|
+
success: true;
|
|
7
|
+
buffer: Buffer;
|
|
8
|
+
} | {
|
|
9
|
+
index: number;
|
|
10
|
+
success: false;
|
|
11
|
+
error: MergeError;
|
|
12
|
+
};
|
|
13
|
+
export interface BatchEvents {
|
|
14
|
+
start: {
|
|
15
|
+
totalJobs: number;
|
|
16
|
+
};
|
|
17
|
+
'job:start': {
|
|
18
|
+
job: MergeJob;
|
|
19
|
+
index: number;
|
|
20
|
+
};
|
|
21
|
+
'job:complete': {
|
|
22
|
+
job: MergeJob;
|
|
23
|
+
index: number;
|
|
24
|
+
buffer: Buffer;
|
|
25
|
+
};
|
|
26
|
+
'job:error': {
|
|
27
|
+
job: MergeJob;
|
|
28
|
+
index: number;
|
|
29
|
+
error: MergeError;
|
|
30
|
+
};
|
|
31
|
+
complete: {
|
|
32
|
+
results: MergeResult[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export declare class BatchRunner extends TypedEventEmitter<BatchEvents> {
|
|
36
|
+
private jobs;
|
|
37
|
+
private results;
|
|
38
|
+
private validatedJobs;
|
|
39
|
+
constructor(jobs: MergeJob[]);
|
|
40
|
+
run(options?: BatchOptions): Promise<MergeResult[]>;
|
|
41
|
+
private runJob;
|
|
42
|
+
private validateJob;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Merges
|
|
2
|
+
import * as mergeFunctions from '../merges/index.js';
|
|
3
|
+
// Errors
|
|
4
|
+
import { MergeError } from '../mergeError.js';
|
|
5
|
+
import { MESSAGES } from '../modules/messages.js';
|
|
6
|
+
// Other
|
|
7
|
+
import { TypedEventEmitter } from '../modules/typedEventEmitter.js';
|
|
8
|
+
import { mergeJobSchema } from '../schemas/mergeJob.js';
|
|
9
|
+
export class BatchRunner extends TypedEventEmitter {
|
|
10
|
+
jobs;
|
|
11
|
+
results = [];
|
|
12
|
+
validatedJobs = [];
|
|
13
|
+
constructor(jobs) {
|
|
14
|
+
super();
|
|
15
|
+
this.jobs = jobs;
|
|
16
|
+
// Ensure all jobs are valid
|
|
17
|
+
for (const job of jobs) {
|
|
18
|
+
const validatedJob = this.validateJob(job);
|
|
19
|
+
this.validatedJobs.push(validatedJob);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async run(options = {}) {
|
|
23
|
+
const { stopOnError = false } = options;
|
|
24
|
+
// Run jobs
|
|
25
|
+
for (let i = 0; i < this.validatedJobs.length; i++) {
|
|
26
|
+
const job = this.validatedJobs[i];
|
|
27
|
+
try {
|
|
28
|
+
// Emit job start event
|
|
29
|
+
this.emit('job:start', {
|
|
30
|
+
index: i,
|
|
31
|
+
job: this.jobs[i],
|
|
32
|
+
});
|
|
33
|
+
// Run job
|
|
34
|
+
const buffer = await this.runJob(job);
|
|
35
|
+
// Emit job completion event
|
|
36
|
+
this.emit('job:complete', {
|
|
37
|
+
index: i,
|
|
38
|
+
job: this.jobs[i],
|
|
39
|
+
buffer,
|
|
40
|
+
});
|
|
41
|
+
this.results.push({ index: i, success: true, buffer: buffer });
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
// Emit job error event
|
|
45
|
+
this.emit('job:error', {
|
|
46
|
+
index: i,
|
|
47
|
+
job: this.jobs[i],
|
|
48
|
+
error: error,
|
|
49
|
+
});
|
|
50
|
+
// Throw error if needed
|
|
51
|
+
if (stopOnError)
|
|
52
|
+
throw error;
|
|
53
|
+
// Store error for later
|
|
54
|
+
this.results.push({ index: i, success: false, error: error });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// Emit batch job completion event
|
|
58
|
+
this.emit('complete', {
|
|
59
|
+
results: this.results,
|
|
60
|
+
});
|
|
61
|
+
return this.results;
|
|
62
|
+
}
|
|
63
|
+
async runJob(job) {
|
|
64
|
+
// Run job and return resulting buffer
|
|
65
|
+
switch (job.type) {
|
|
66
|
+
case 'grid':
|
|
67
|
+
return mergeFunctions.gridMerge(job.inputs, job.options);
|
|
68
|
+
case 'masonry':
|
|
69
|
+
return mergeFunctions.masonryMerge(job.inputs, job.options);
|
|
70
|
+
case 'collage':
|
|
71
|
+
return mergeFunctions.collageMerge(job.inputs, job.options);
|
|
72
|
+
case 'template':
|
|
73
|
+
return mergeFunctions.templateMerge(job.inputs, job.options);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
validateJob(job) {
|
|
77
|
+
// Validate job format
|
|
78
|
+
const { success, data, error } = mergeJobSchema.safeParse(job);
|
|
79
|
+
if (!success) {
|
|
80
|
+
const path = error.issues[0]?.path;
|
|
81
|
+
const err = error.issues[0]?.message;
|
|
82
|
+
if (!path || !err) {
|
|
83
|
+
throw new MergeError(MESSAGES.ERROR.INTERNAL.message, { type: 'internal', cause: error });
|
|
84
|
+
}
|
|
85
|
+
const errorText = path.length > 0 ? `Invalid value at ${path.join('/')}: ${err}` : `Error: ${err}`;
|
|
86
|
+
throw new MergeError(errorText, { type: 'validation' });
|
|
87
|
+
}
|
|
88
|
+
return data;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type sharp from 'sharp';
|
|
2
|
+
import type { MergeTypeOptions } from '../merges/types.js';
|
|
3
|
+
export type MergeJob = {
|
|
4
|
+
/** File paths to all of the images to load. */
|
|
5
|
+
inputs: sharp.SharpInput[];
|
|
6
|
+
} & MergeTypeOptions;
|
|
7
|
+
export interface BatchOptions {
|
|
8
|
+
/** Throws a `MergeError` instantly and stops the process if true. */
|
|
9
|
+
stopOnError?: boolean;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export { gridMerge } from './grid/index.js';
|
|
2
2
|
export { masonryMerge } from './masonry/index.js';
|
|
3
3
|
export { templateMerge } from './template/index.js';
|
|
4
|
+
export { collageMerge } from './collage/index.js';
|
|
5
|
+
export type { GridMergeOptions, MasonryMergeOptions, TemplateMergeOptions, CollageMergeOptions } from './types.js';
|
|
@@ -72,10 +72,10 @@ export interface MasonryMergeOptions extends BaseMergeOptions {
|
|
|
72
72
|
/** The vertical alignment of each column. Only applied in vertical layouts. */
|
|
73
73
|
vAlign?: 'top' | 'middle' | 'bottom' | 'justified';
|
|
74
74
|
}
|
|
75
|
-
interface TemplateMergeOptions extends BaseMergeOptions {
|
|
75
|
+
export interface TemplateMergeOptions extends BaseMergeOptions {
|
|
76
76
|
template: Template;
|
|
77
77
|
}
|
|
78
|
-
interface CollageMergeOptions extends Omit<BaseMergeOptions, 'gap'> {
|
|
78
|
+
export interface CollageMergeOptions extends Omit<BaseMergeOptions, 'gap'> {
|
|
79
79
|
/** Width of each image cell in pixels, uses the median image width if undefined. */
|
|
80
80
|
imageWidth?: number | undefined;
|
|
81
81
|
/** The aspect ratio of each image. Used to calculate image height based on given width.
|
|
@@ -95,6 +95,27 @@ interface CollageMergeOptions extends Omit<BaseMergeOptions, 'gap'> {
|
|
|
95
95
|
* For example, a value of `10` will result in a random degree being picked from `-10` to `+10` degrees. */
|
|
96
96
|
rotationRange?: number;
|
|
97
97
|
}
|
|
98
|
+
export type MergeTypeOptions = {
|
|
99
|
+
/** Use the grid layout engine. */
|
|
100
|
+
type: 'grid';
|
|
101
|
+
/** Options specific to grid merges. */
|
|
102
|
+
options: GridMergeOptions;
|
|
103
|
+
} | {
|
|
104
|
+
/** Use the masonry layout engine. */
|
|
105
|
+
type: 'masonry';
|
|
106
|
+
/** Options specific to masonry merges. */
|
|
107
|
+
options: MasonryMergeOptions;
|
|
108
|
+
} | {
|
|
109
|
+
/** Use the template-based merge engine. */
|
|
110
|
+
type: 'template';
|
|
111
|
+
/** Options specific to template merges. */
|
|
112
|
+
options: TemplateMergeOptions;
|
|
113
|
+
} | {
|
|
114
|
+
/** Use the free-form collage merge engine. */
|
|
115
|
+
type: 'collage';
|
|
116
|
+
/** Options specific to collage merges. */
|
|
117
|
+
options: CollageMergeOptions;
|
|
118
|
+
};
|
|
98
119
|
export type GridMerge = MergeCommand<GridMergeOptions>;
|
|
99
120
|
export type MasonryMerge = MergeCommand<MasonryMergeOptions>;
|
|
100
121
|
export type TemplateMerge = MergeCommand<TemplateMergeOptions>;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import EventEmitter from 'node:events';
|
|
2
|
+
type EventKey<T> = keyof T & string;
|
|
3
|
+
export declare class TypedEventEmitter<TEvents> extends EventEmitter {
|
|
4
|
+
on<K extends EventKey<TEvents>>(event: K, listener: (payload: TEvents[K]) => void): this;
|
|
5
|
+
emit<K extends EventKey<TEvents>>(event: K, payload: TEvents[K]): boolean;
|
|
6
|
+
}
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
export declare const mergeJobSchema: z.ZodObject<{
|
|
3
|
+
type: z.ZodEnum<{
|
|
4
|
+
template: "template";
|
|
5
|
+
grid: "grid";
|
|
6
|
+
masonry: "masonry";
|
|
7
|
+
collage: "collage";
|
|
8
|
+
}>;
|
|
9
|
+
inputs: z.ZodArray<z.ZodString>;
|
|
10
|
+
options: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
|
|
11
|
+
}, z.z.core.$strict>;
|
|
@@ -4,4 +4,4 @@ import { SUPPORTED_OUTPUT_FORMATS } from '../core/helpers.js';
|
|
|
4
4
|
export const outputFileValidator = z.string().refine((outputPath) => {
|
|
5
5
|
const extension = path.extname(outputPath).replace('.', '');
|
|
6
6
|
return SUPPORTED_OUTPUT_FORMATS.includes(extension);
|
|
7
|
-
}, '
|
|
7
|
+
}, 'output image format is invalid');
|
package/package.json
CHANGED