@e-mc/compress 0.13.10 → 0.14.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @e-mc/compress
2
2
 
3
- * NodeJS 18.20 LTS
3
+ * NodeJS 20 (Minimum 18)
4
4
  * ES2022
5
5
 
6
6
  ## General Usage
@@ -9,7 +9,7 @@
9
9
 
10
10
  ## Interface
11
11
 
12
- * [View Source](https://www.unpkg.com/@e-mc/types@0.13.10/lib/index.d.ts)
12
+ * [View Source](https://www.unpkg.com/@e-mc/types@0.14.0/lib/index.d.ts)
13
13
 
14
14
  ```typescript
15
15
  import type { IModule, ModuleConstructor } from "./index";
@@ -61,13 +61,10 @@ interface CompressConstructor extends ModuleConstructor {
61
61
  import type { CacheDirAction } from "./settings";
62
62
 
63
63
  import type { BrotliOptions, ZlibOptions } from "node:zlib";
64
- import type { Options as ZopfliOptions } from "node-zopfli";
65
64
 
66
65
  interface CompressModule {
67
66
  gzip?: ZlibOptions;
68
67
  brotli?: BrotliOptions;
69
- /** @deprecated */
70
- zopfli?: ZopfliOptions;
71
68
  settings?: {
72
69
  broadcast_id?: string | string[];
73
70
  cache?: boolean | CacheDirAction & { font?: string | number | boolean; image?: string | number | boolean; };
@@ -75,8 +72,6 @@ interface CompressModule {
75
72
  cache_expires?: number | string;
76
73
  gzip_level?: number;
77
74
  brotli_quality?: number;
78
- /** @deprecated */
79
- zopfli_iterations?: number;
80
75
  chunk_size?: number | string;
81
76
  };
82
77
  }
@@ -125,11 +120,10 @@ instance.tryImage("/tmp/image.png", "/path/output/compressed.png", options)
125
120
 
126
121
  ## References
127
122
 
128
- - https://www.unpkg.com/@e-mc/types@0.13.10/lib/compress.d.ts
129
- - https://www.unpkg.com/@e-mc/types@0.13.10/lib/settings.d.ts
123
+ - https://www.unpkg.com/@e-mc/types@0.14.0/lib/compress.d.ts
124
+ - https://www.unpkg.com/@e-mc/types@0.14.0/lib/settings.d.ts
130
125
 
131
126
  * https://www.npmjs.com/package/@types/node
132
- * https://www.npmjs.com/package/@types/node-zopfli
133
127
 
134
128
  ## LICENSE
135
129
 
package/index.js CHANGED
@@ -1,24 +1,26 @@
1
1
  "use strict";
2
- const path = require("node:path");
3
- const fs = require("node:fs");
4
- const stream = require("node:stream");
5
- const zlib = require("node:zlib");
6
- const wawoff2 = require("wawoff2");
7
- const types_1 = require("@e-mc/types");
8
- const core_1 = require("@e-mc/core");
2
+
3
+ const path = require('node:path');
4
+ const fs = require('node:fs');
5
+ const stream = require('node:stream');
6
+ const zlib = require('node:zlib');
7
+ const { compress, decompress } = require('wawoff2');
8
+ const { IMPORT_MAP, alignSize, asExt, cloneObject, errorMessage, errorValue, formatSize, getTempDir, importESM, isErrorCode, isObject, isPlainObject, isString, parseExpires, renameExt, supported } = require('@e-mc/types');
9
+ const { Module, WorkerChannel, WorkerGroup } = require('@e-mc/core');
9
10
  const { toSfnt, toWoff } = require('woff2sfnt-sfnt2woff');
10
11
  const kCompress = Symbol.for('compress:constructor');
11
12
  function createWorker(filename) {
12
- return core_1.WorkerChannel.create(path.join(__dirname, 'worker', filename), 'EMC_COMPRESS');
13
+ return WorkerChannel.create(path.join(__dirname, 'worker', filename), 'EMC_COMPRESS');
13
14
  }
14
- const SUPPORTED_NODE21 = (0, types_1.supported)(21);
15
+ const SUPPORTED_NODE21 = supported(21);
16
+ const SUPPORTED_ZSTD = supported(23, 8) || supported(22, 15, true);
15
17
  const CACHE_IMAGE = Object.create(null);
16
18
  const CACHE_FONT = new Map();
17
19
  const CACHE_FONTFROM = Object.create(null);
18
20
  const CACHE_FONTTO = Object.create(null);
19
21
  const CACHE_TEMP = { font: undefined, image: undefined };
20
22
  let SINGLETON_INSTANCE;
21
- const WORKER = new core_1.WorkerGroup(parseInt(process.env.EMC_COMPRESS_WORKER_GROUP_MAX || '0'));
23
+ const WORKER = new WorkerGroup(parseInt(process.env.EMC_COMPRESS_WORKER_GROUP_MAX || '0'));
22
24
  WORKER
23
25
  .add('otf', createWorker('sfnt2woff.js'))
24
26
  .add('woff', createWorker('woff2sfnt.js'))
@@ -26,13 +28,25 @@ WORKER
26
28
  .add('ttf', createWorker('wawoff2-compress.js'))
27
29
  .add('image', createWorker('plugin-image.js'))
28
30
  .add('gz', createWorker('system-gzip.js'))
29
- .add('br', createWorker('system-brotli.js'));
31
+ .add('br', createWorker('system-brotli.js'))
32
+ .add('zst', createWorker('system-zstd.js'));
30
33
  function getOutputSize(startLength, data, worker) {
31
34
  if (startLength === 0 || !data) {
32
35
  return worker ? ' (worker)' : '';
33
36
  }
34
37
  const offset = Buffer.byteLength(data) - startLength;
35
- return ` (${(worker ? 'worker: ' : '') + (offset > 0 ? '+' : '') + (0, types_1.formatSize)(offset)})`;
38
+ return ` (${(worker ? 'worker: ' : '') + (offset > 0 ? '+' : '') + formatSize(offset)})`;
39
+ }
40
+ function applySettings(options, params) {
41
+ if (isPlainObject(options)) {
42
+ if (isPlainObject(options.params)) {
43
+ options = cloneObject(options, true);
44
+ Object.assign(options.params, params);
45
+ return options;
46
+ }
47
+ return { ...options, params };
48
+ }
49
+ return { params };
36
50
  }
37
51
  function applyBrotliMode(params, value) {
38
52
  if (value) {
@@ -55,8 +69,8 @@ function applyZlibOptions(base, options, ...params) {
55
69
  }
56
70
  }
57
71
  const sanitizePath = (value) => value.replace(/[\\/]/g, '_');
58
- const getWorkerTimeout = (options, value) => core_1.WorkerGroup.checkTimeout(typeof options.worker === 'number' && options.worker > 0 ? options.worker : value, true);
59
- class Compress extends core_1.Module {
72
+ const getWorkerTimeout = (options, value) => WorkerGroup.checkTimeout(typeof options.worker === 'number' && options.worker > 0 ? options.worker : value, true);
73
+ class Compress extends Module {
60
74
  module;
61
75
  static [kCompress] = true;
62
76
  static singleton() {
@@ -64,9 +78,12 @@ class Compress extends core_1.Module {
64
78
  SINGLETON_INSTANCE = new Compress();
65
79
  Object.defineProperty(SINGLETON_INSTANCE, "_logEnabled", { value: false, enumerable: true, writable: false });
66
80
  Object.defineProperty(SINGLETON_INSTANCE, "_logFlushed", { value: true, enumerable: true, writable: false });
67
- Object.defineProperty(SINGLETON_INSTANCE, "host", { set(value) { }, get() {
81
+ Object.defineProperty(SINGLETON_INSTANCE, "host", {
82
+ set(value) { },
83
+ get() {
68
84
  return null;
69
- } });
85
+ }
86
+ });
70
87
  }
71
88
  return SINGLETON_INSTANCE;
72
89
  }
@@ -76,7 +93,7 @@ class Compress extends core_1.Module {
76
93
  level = {
77
94
  gz: 9,
78
95
  br: 11,
79
- zopfli: 15
96
+ zst: 3
80
97
  };
81
98
  compressors = {};
82
99
  _moduleName = 'compress';
@@ -86,15 +103,15 @@ class Compress extends core_1.Module {
86
103
  this.module = module;
87
104
  }
88
105
  init(...args) {
89
- let { gzip_level, brotli_quality, zopfli_iterations, chunk_size } = (0, types_1.isPlainObject)(args[0]) && args[0].settings || this.settings;
106
+ let { gzip_level, brotli_quality, zstd_compression_level, chunk_size } = isPlainObject(args[0]) && args[0].settings || this.settings;
90
107
  if (gzip_level !== undefined && (gzip_level = Math.floor(+gzip_level)) >= -1 && gzip_level <= 9) {
91
108
  this.level.gz = Math.floor(gzip_level);
92
109
  }
93
110
  if (brotli_quality !== undefined && (brotli_quality = Math.floor(+brotli_quality)) >= 0 && brotli_quality <= 11) {
94
111
  this.level.br = brotli_quality;
95
112
  }
96
- if (zopfli_iterations && (zopfli_iterations = Math.floor(+zopfli_iterations)) > 0) {
97
- this.level.zopfli = zopfli_iterations;
113
+ if (zstd_compression_level !== undefined && (zstd_compression_level = Math.floor(+zstd_compression_level)) >= 1 && zstd_compression_level <= 9) {
114
+ this.level.zstd = zstd_compression_level;
98
115
  }
99
116
  if (chunk_size !== undefined) {
100
117
  this.chunkSize = chunk_size;
@@ -108,7 +125,7 @@ class Compress extends core_1.Module {
108
125
  }
109
126
  }
110
127
  getLevel(value, fallback) {
111
- const result = this.level[value = value.toLowerCase()] ?? this.level[value = path.extname(value).substring(1)];
128
+ const result = this.level[value = value.toLowerCase()] ?? this.level[value = asExt(value)];
112
129
  if (!isNaN(result)) {
113
130
  return result;
114
131
  }
@@ -120,6 +137,8 @@ class Compress extends core_1.Module {
120
137
  return zlib.constants.Z_DEFAULT_LEVEL;
121
138
  case 'br':
122
139
  return zlib.constants.BROTLI_DEFAULT_QUALITY;
140
+ case 'zst':
141
+ return zlib.constants.ZSTD_CLEVEL_DEFAULT;
123
142
  }
124
143
  }
125
144
  getReadable(file, options) {
@@ -131,44 +150,28 @@ class Compress extends core_1.Module {
131
150
  return fs.createReadStream(file);
132
151
  }
133
152
  options = undefined;
134
- throw (0, types_1.errorValue)("Operation not permitted", file.toString());
153
+ throw errorValue("Operation not permitted", file.toString());
135
154
  }
136
155
  catch (err) {
137
- if (!options || options.throwsPermission && (0, types_1.isErrorCode)(err, 'EACCES') || options.throwsDoesNotExist && (0, types_1.isErrorCode)(err, 'ENOENT')) {
156
+ if (!options || options.throwsPermission && isErrorCode(err, 'EACCES') || options.throwsDoesNotExist && isErrorCode(err, 'ENOENT')) {
138
157
  throw err;
139
158
  }
140
159
  }
141
160
  return stream.Readable.from(Buffer.alloc(0));
142
161
  }
143
162
  createGzip(file, options) {
144
- let algorithm, level, chunkSize;
163
+ let level, chunkSize;
145
164
  if (options) {
146
- ({ algorithm, level, chunkSize } = options);
147
- }
148
- if (algorithm === 'zopfli') {
149
- try {
150
- const lib = require('node-zopfli');
151
- const opts = (0, types_1.isPlainObject)(this.module.zopfli) ? { ...this.module.zopfli } : {};
152
- if (typeof level === 'number') {
153
- opts.numiterations = level;
154
- }
155
- else {
156
- opts.numiterations ??= this.level.zopfli;
157
- }
158
- return this.getReadable(file, options).pipe(lib.createGzip(opts));
159
- }
160
- catch {
161
- }
162
- level = undefined;
165
+ ({ level, chunkSize } = options);
163
166
  }
164
167
  const gzip = { ...this.module.gzip };
165
168
  if (typeof level === 'number') {
166
169
  gzip.level = level;
167
170
  }
168
- else {
171
+ else if (typeof this.level.gz === 'number') {
169
172
  gzip.level ??= this.level.gz;
170
173
  }
171
- if (!isNaN(chunkSize = (0, types_1.alignSize)(chunkSize, 1))) {
174
+ if (!isNaN(chunkSize = alignSize(chunkSize, 1))) {
172
175
  gzip.chunkSize = chunkSize;
173
176
  }
174
177
  else {
@@ -184,20 +187,7 @@ class Compress extends core_1.Module {
184
187
  if (options) {
185
188
  ({ level, chunkSize, params, mimeType } = options);
186
189
  }
187
- params ||= {};
188
- let brotli = this.module.brotli;
189
- if ((0, types_1.isPlainObject)(brotli)) {
190
- if ((0, types_1.isPlainObject)(brotli.params)) {
191
- brotli = (0, types_1.cloneObject)(brotli, true);
192
- params = Object.assign(brotli.params, params);
193
- }
194
- else {
195
- brotli = { ...brotli, params };
196
- }
197
- }
198
- else {
199
- brotli = { params };
200
- }
190
+ const brotli = applySettings(this.module.brotli, params ||= {});
201
191
  applyBrotliMode(params, mimeType);
202
192
  try {
203
193
  params[zlib.constants.BROTLI_PARAM_SIZE_HINT] = typeof file === 'string' ? fs.statSync(file).size : Buffer.byteLength(file);
@@ -205,12 +195,12 @@ class Compress extends core_1.Module {
205
195
  catch {
206
196
  }
207
197
  if (typeof level === 'number') {
208
- brotli.params[zlib.constants.BROTLI_PARAM_QUALITY] = level;
198
+ params[zlib.constants.BROTLI_PARAM_QUALITY] = level;
209
199
  }
210
- else {
211
- brotli.params[zlib.constants.BROTLI_PARAM_QUALITY] ??= this.level.br;
200
+ else if (typeof this.level.br === 'number') {
201
+ params[zlib.constants.BROTLI_PARAM_QUALITY] ??= this.level.br;
212
202
  }
213
- if (!isNaN(chunkSize = (0, types_1.alignSize)(chunkSize, 1))) {
203
+ if (!isNaN(chunkSize = alignSize(chunkSize, 1))) {
214
204
  brotli.chunkSize = chunkSize;
215
205
  }
216
206
  else {
@@ -248,21 +238,21 @@ class Compress extends core_1.Module {
248
238
  });
249
239
  }
250
240
  async tryFile(file, output, options) {
251
- if ((0, types_1.isObject)(output)) {
241
+ if (isObject(output)) {
252
242
  options = output;
253
243
  output = '';
254
244
  }
255
- if (!(0, types_1.isString)(output)) {
245
+ if (!isString(output)) {
256
246
  output = typeof file === 'string' ? file : '';
257
247
  }
258
248
  if (output && !this.canWrite(output, { ownPermissionOnly: true })) {
259
- return Promise.reject((0, types_1.errorValue)("Operation not permitted", output));
249
+ return Promise.reject(errorValue("Operation not permitted", output));
260
250
  }
261
251
  options ||= {};
262
252
  const { filename, startTime = process.hrtime.bigint(), timeout = 0 } = options;
263
253
  let format = options.format, hash = options.etag;
264
- if (!(0, types_1.isString)(format)) {
265
- return Promise.reject((0, types_1.errorValue)("Missing option \"format\"", output || filename));
254
+ if (!isString(format)) {
255
+ return Promise.reject(errorValue("Missing option \"format\"", output || filename));
266
256
  }
267
257
  format = format.toLowerCase();
268
258
  return new Promise((resolve, reject) => {
@@ -288,12 +278,12 @@ class Compress extends core_1.Module {
288
278
  fromCache = result;
289
279
  result = null;
290
280
  }
291
- if ((0, types_1.isString)(data)) {
281
+ if (isString(data)) {
292
282
  result = data;
293
283
  data = null;
294
284
  }
295
285
  if (!fromCache) {
296
- const status = (0, types_1.isString)(result) ? path.basename(result) : filename || "Completed";
286
+ const status = isString(result) ? path.basename(result) : filename || "Completed";
297
287
  this.writeTimeProcess(ext || format, status + getOutputSize(startLength, data, !!worker), startTime, { type: 8, sessionId: options.sessionId, broadcastId: options.broadcastId });
298
288
  }
299
289
  if (result) {
@@ -310,10 +300,10 @@ class Compress extends core_1.Module {
310
300
  if (worker) {
311
301
  void worker.terminate();
312
302
  }
313
- endProcess((0, types_1.errorValue)(worker ? "Worker did not finish" : "Timeout was exceeded", format));
303
+ endProcess(errorValue(worker ? "Worker did not finish" : "Timeout was exceeded", format));
314
304
  }, worker ? getWorkerTimeout(options, timeout) : timeout);
315
305
  }
316
- this.formatMessage(8, format, [compressing ? 'Compressing file...' : 'Decompressing file...', filename || (output ? path.basename(output) : '')], (0, types_1.isString)(file) ? file : '', { titleColor: 'magenta', sessionId: options.sessionId, broadcastId: options.broadcastId });
306
+ this.formatMessage(8, format, [compressing ? 'Compressing file...' : 'Decompressing file...', filename || (output ? path.basename(output) : '')], isString(file) ? file : '', { titleColor: 'magenta', sessionId: options.sessionId, broadcastId: options.broadcastId });
317
307
  };
318
308
  try {
319
309
  const writeFont = (data, ext, from) => {
@@ -321,7 +311,7 @@ class Compress extends core_1.Module {
321
311
  return;
322
312
  }
323
313
  if (output) {
324
- const pathname = (0, types_1.renameExt)(output, ext);
314
+ const pathname = renameExt(output, ext);
325
315
  fs.writeFile(pathname, data, err => {
326
316
  if (!err) {
327
317
  endProcess(null, data, ext, pathname);
@@ -337,7 +327,7 @@ class Compress extends core_1.Module {
337
327
  if (tempDir && hash) {
338
328
  const pathname = path.join(from, ext);
339
329
  let tempFont;
340
- if (core_1.Module.createDir(tempFont = path.join(tempDir, pathname))) {
330
+ if (Module.createDir(tempFont = path.join(tempDir, pathname))) {
341
331
  tempFont = path.join(tempFont, hash);
342
332
  }
343
333
  else {
@@ -358,10 +348,10 @@ class Compress extends core_1.Module {
358
348
  return false;
359
349
  }
360
350
  if (hash) {
361
- hash = core_1.Module.asHash(hash);
351
+ hash = Module.asHash(hash);
362
352
  }
363
353
  else if (Buffer.isBuffer(file)) {
364
- hash = core_1.Module.asHash(file);
354
+ hash = Module.asHash(file);
365
355
  }
366
356
  else {
367
357
  return false;
@@ -372,18 +362,18 @@ class Compress extends core_1.Module {
372
362
  const cacheKey = from + `_${to}_` + hash;
373
363
  const tempFont = CACHE_FONT.get(cacheKey);
374
364
  if (tempFont) {
375
- if (core_1.Module.isPath(tempFont[0])) {
365
+ if (Module.isPath(tempFont[0])) {
376
366
  try {
377
367
  const data = fs.readFileSync(tempFont[0]);
378
368
  let pathname;
379
369
  if (output) {
380
- fs.writeFileSync(pathname = (0, types_1.renameExt)(output, to), data);
370
+ fs.writeFileSync(pathname = renameExt(output, to), data);
381
371
  endProcess(null, data, to, pathname, true);
382
372
  }
383
373
  else {
384
374
  endProcess(null, data, to, true);
385
375
  }
386
- this.formatMessage(8, from, [pathname ? path.basename(pathname) : "Completed" + ` -> font/${to}`, 'cache'], tempFont[1].toLocaleString(), { ...core_1.Module.LOG_STYLE_NOTICE, hintBold: true, sessionId: options.sessionId, broadcastId: options.broadcastId });
376
+ this.formatMessage(8, from, [pathname ? path.basename(pathname) : "Completed" + ` -> font/${to}`, 'cache'], tempFont[1].toLocaleString(), { ...Module.LOG_STYLE_NOTICE, hintBold: true, sessionId: options.sessionId, broadcastId: options.broadcastId });
387
377
  return true;
388
378
  }
389
379
  catch {
@@ -395,7 +385,7 @@ class Compress extends core_1.Module {
395
385
  }
396
386
  };
397
387
  const errorResponse = (font, message = "Unsupported MIME") => {
398
- endProcess((0, types_1.errorValue)(message, (font?.mime || "Unknown") + ': ' + (output || filename || "Unknown")));
388
+ endProcess(errorValue(message, (font?.mime || "Unknown") + ': ' + (output || filename || "Unknown")));
399
389
  };
400
390
  const shared = Buffer.isBuffer(file);
401
391
  let data;
@@ -406,28 +396,38 @@ class Compress extends core_1.Module {
406
396
  data = fs.readFileSync(file);
407
397
  }
408
398
  else {
409
- reject((0, types_1.errorValue)("Operation not permitted", file));
399
+ reject(errorValue("Operation not permitted", file));
410
400
  return;
411
401
  }
412
402
  startLength = Buffer.byteLength(data);
413
403
  switch (format) {
404
+ case 'zst':
405
+ if (!SUPPORTED_ZSTD) {
406
+ endProcess(errorMessage(format, "Not implemented by runtime application", '22.15.0'));
407
+ break;
408
+ }
414
409
  case 'gz':
415
410
  case 'br': {
416
411
  if (this.hasPermission('worker', options)) {
417
412
  const workerOptions = { chunkSize: options.chunkSize ?? this.chunkSize };
418
- if (format === 'gz') {
419
- workerOptions.level = options.level ?? this.level.gz;
420
- }
421
- else {
422
- workerOptions.params = { [zlib.constants.BROTLI_PARAM_QUALITY]: options.level ?? this.level.br };
423
- applyBrotliMode(workerOptions.params, options.mimeType);
413
+ switch (format) {
414
+ case 'br':
415
+ workerOptions.params = { [zlib.constants.BROTLI_PARAM_QUALITY]: options.level ?? this.level.br };
416
+ applyBrotliMode(workerOptions.params, options.mimeType);
417
+ break;
418
+ case 'zst':
419
+ workerOptions.params = { [zlib.constants.ZSTD_c_compressionLevel]: options.level ?? this.level.zst };
420
+ break;
421
+ default:
422
+ workerOptions.level = options.level ?? this.level.gz;
423
+ break;
424
424
  }
425
425
  const endWorker = (result) => {
426
426
  if (result) {
427
427
  endProcess(null, result, format);
428
428
  }
429
429
  else {
430
- endProcess((0, types_1.errorValue)("Worker did not finish", output));
430
+ endProcess(errorValue("Worker did not finish", output));
431
431
  }
432
432
  };
433
433
  if (worker = WORKER.get(format).sendObject({ data, options: workerOptions, output }, shared || SUPPORTED_NODE21 ? [] : [data.buffer], endWorker)) {
@@ -438,11 +438,21 @@ class Compress extends core_1.Module {
438
438
  startProcess(true);
439
439
  let transform, chunks;
440
440
  if (output) {
441
- transform = this[format === 'gz' ? 'createWriteStreamAsGzip' : 'createWriteStreamAsBrotli'](data, output, options);
441
+ if (format === 'zst') {
442
+ transform = this.#createWriteStreamAsZstd(data, output, options);
443
+ }
444
+ else {
445
+ transform = this[format === 'gz' ? 'createWriteStreamAsGzip' : 'createWriteStreamAsBrotli'](data, output, options);
446
+ }
442
447
  }
443
448
  else {
444
449
  chunks = [];
445
- transform = this[format === 'gz' ? 'createGzip' : 'createBrotliCompress'](data, options);
450
+ if (format === 'zst') {
451
+ transform = this.#createZstdCompress(data, options);
452
+ }
453
+ else {
454
+ transform = this[format === 'gz' ? 'createGzip' : 'createBrotliCompress'](data, options);
455
+ }
446
456
  transform.on('data', chunk => {
447
457
  chunks.push(chunk);
448
458
  });
@@ -464,7 +474,7 @@ class Compress extends core_1.Module {
464
474
  }
465
475
  const checkResult = (result, from) => {
466
476
  if (result) {
467
- void core_1.Module.resolveMime(result).then(font => {
477
+ void Module.resolveMime(result).then(font => {
468
478
  switch (font?.mime) {
469
479
  case "font/ttf":
470
480
  case "font/otf":
@@ -480,7 +490,7 @@ class Compress extends core_1.Module {
480
490
  errorResponse(null, "Worker did not finish");
481
491
  }
482
492
  };
483
- void core_1.Module.resolveMime(data)
493
+ void Module.resolveMime(data)
484
494
  .then(font => {
485
495
  switch (font?.mime) {
486
496
  case "font/woff":
@@ -504,7 +514,7 @@ class Compress extends core_1.Module {
504
514
  }
505
515
  else {
506
516
  startProcess(false);
507
- void wawoff2.decompress(data).then(woff => checkResult(woff, 'woff2'));
517
+ void decompress(data).then(woff => checkResult(woff, 'woff2'));
508
518
  }
509
519
  break;
510
520
  default:
@@ -522,7 +532,7 @@ class Compress extends core_1.Module {
522
532
  }
523
533
  const checkResult = (result, from, mimeType) => {
524
534
  if (result) {
525
- void core_1.Module.resolveMime(result).then(font => {
535
+ void Module.resolveMime(result).then(font => {
526
536
  if (font?.mime === mimeType) {
527
537
  writeFont(result, font.ext, from);
528
538
  }
@@ -536,7 +546,7 @@ class Compress extends core_1.Module {
536
546
  }
537
547
  };
538
548
  startProcess(true);
539
- void core_1.Module.resolveMime(data)
549
+ void Module.resolveMime(data)
540
550
  .then(font => {
541
551
  switch (font?.mime) {
542
552
  case "font/ttf":
@@ -548,7 +558,7 @@ class Compress extends core_1.Module {
548
558
  }
549
559
  else {
550
560
  startProcess(false);
551
- void wawoff2.compress(data).then(result => checkResult(result, 'ttf', "font/woff2"));
561
+ void compress(data).then(result => checkResult(result, 'ttf', "font/woff2"));
552
562
  }
553
563
  break;
554
564
  case "font/otf":
@@ -574,7 +584,7 @@ class Compress extends core_1.Module {
574
584
  default: {
575
585
  const compressor = this.compressors[format];
576
586
  if (typeof compressor !== 'function') {
577
- throw (0, types_1.errorValue)("Missing compression plugin", format);
587
+ throw errorValue("Missing compression plugin", format);
578
588
  }
579
589
  startProcess(true);
580
590
  if (compressor.toString().startsWith('async')) {
@@ -597,7 +607,7 @@ class Compress extends core_1.Module {
597
607
  });
598
608
  }
599
609
  async tryImage(file, output, options) {
600
- if ((0, types_1.isObject)(output)) {
610
+ if (isObject(output)) {
601
611
  options = output;
602
612
  output = '';
603
613
  }
@@ -605,22 +615,22 @@ class Compress extends core_1.Module {
605
615
  options ||= {};
606
616
  }
607
617
  const { filename, startTime = process.hrtime.bigint(), timeout = 0 } = options;
608
- if (!(0, types_1.isString)(output)) {
618
+ if (!isString(output)) {
609
619
  output = typeof file === 'string' ? file : '';
610
620
  }
611
621
  if (output && !this.canWrite(output, { ownPermissionOnly: true })) {
612
- return Promise.reject((0, types_1.errorValue)("Operation not permitted", output));
622
+ return Promise.reject(errorValue("Operation not permitted", output));
613
623
  }
614
624
  let plugin = options.plugin;
615
- if ((0, types_1.isString)(plugin)) {
616
- if (types_1.IMPORT_MAP[plugin]) {
617
- plugin = types_1.IMPORT_MAP[plugin];
625
+ if (isString(plugin)) {
626
+ if (IMPORT_MAP[plugin]) {
627
+ plugin = IMPORT_MAP[plugin];
618
628
  }
619
629
  }
620
630
  else {
621
- return Promise.reject((0, types_1.errorValue)("Missing compression plugin", output));
631
+ return Promise.reject(errorValue("Missing compression plugin", output));
622
632
  }
623
- const ext = output ? path.extname(output).substring(1) : options.format || "Unknown";
633
+ const ext = output ? asExt(output) : options.format || "Unknown";
624
634
  const getFormat = () => output ? path.basename(output) : ext;
625
635
  let data, shared = false;
626
636
  if (Buffer.isBuffer(file)) {
@@ -636,10 +646,10 @@ class Compress extends core_1.Module {
636
646
  }
637
647
  }
638
648
  else {
639
- return Promise.reject((0, types_1.errorValue)("Operation not permitted", file));
649
+ return Promise.reject(errorValue("Operation not permitted", file));
640
650
  }
641
651
  if (!options.mimeType) {
642
- const out = await core_1.Module.resolveMime(data);
652
+ const out = await Module.resolveMime(data);
643
653
  if (out) {
644
654
  options.mimeType = out.mime;
645
655
  }
@@ -659,17 +669,17 @@ class Compress extends core_1.Module {
659
669
  reject(err);
660
670
  return;
661
671
  }
662
- const value = output ? path.basename(output) : filename || (0, types_1.isString)(file) && path.basename(file);
672
+ const value = output ? path.basename(output) : filename || isString(file) && path.basename(file);
663
673
  const status = value ? plugin + ': ' + value : "Completed";
664
674
  if (ctime) {
665
- this.formatMessage(8, value ? path.extname(value).substring(1) : '', [status, 'cache'], ctime.toLocaleString(), { ...core_1.Module.LOG_STYLE_NOTICE, hintBold: true, sessionId: options.sessionId, broadcastId: options.broadcastId });
675
+ this.formatMessage(8, value ? asExt(value) : '', [status, 'cache'], ctime.toLocaleString(), { ...Module.LOG_STYLE_NOTICE, hintBold: true, sessionId: options.sessionId, broadcastId: options.broadcastId });
666
676
  }
667
677
  else {
668
678
  this.writeTimeProcess(ext, status + getOutputSize(startLength, result, worker), startTime, { type: 8, sessionId: options.sessionId, broadcastId: options.broadcastId });
669
679
  }
670
680
  if (!ctime && hash) {
671
681
  const pathname = path.join(tempDir, sanitizePath(plugin), hash);
672
- if (core_1.Module.createDir(pathname)) {
682
+ if (Module.createDir(pathname)) {
673
683
  fs.writeFile(path.join(pathname, cacheKey), result, error => {
674
684
  if (!error) {
675
685
  ((CACHE_IMAGE[plugin] ||= {})[hash] ||= {})[cacheKey] = new Date();
@@ -699,9 +709,9 @@ class Compress extends core_1.Module {
699
709
  if (tempDir) {
700
710
  let stored;
701
711
  try {
702
- hash = core_1.Module.asHash(data);
712
+ hash = Module.asHash(data);
703
713
  stored = (CACHE_IMAGE[plugin] ||= {})[hash];
704
- cacheKey = plugin + (options.options ? ':' + core_1.Module.asHash(core_1.Module.asString(options.options)) : '') + (options.metadata ? ':' + core_1.Module.asHash(core_1.Module.asString(options.metadata)) : '');
714
+ cacheKey = plugin + (options.options ? ':' + Module.asHash(Module.asString(options.options)) : '') + (options.metadata ? ':' + Module.asHash(Module.asString(options.metadata)) : '');
705
715
  const ctime = stored?.[cacheKey];
706
716
  if (ctime) {
707
717
  success(fs.readFileSync(path.join(tempDir, sanitizePath(plugin), hash, cacheKey)), false, ctime);
@@ -723,7 +733,7 @@ class Compress extends core_1.Module {
723
733
  success(result, !!worker);
724
734
  }
725
735
  else {
726
- failed((0, types_1.errorMessage)(plugin, "Unsupported MIME", getFormat()));
736
+ failed(errorMessage(plugin, "Unsupported MIME", getFormat()));
727
737
  }
728
738
  };
729
739
  const startTimeout = (value, message) => {
@@ -732,7 +742,7 @@ class Compress extends core_1.Module {
732
742
  if (worker) {
733
743
  void worker.terminate();
734
744
  }
735
- failed((0, types_1.errorMessage)(plugin, message, options.mimeType));
745
+ failed(errorMessage(plugin, message, options.mimeType));
736
746
  }, value);
737
747
  }
738
748
  };
@@ -751,7 +761,7 @@ class Compress extends core_1.Module {
751
761
  }
752
762
  let transform;
753
763
  try {
754
- transform = await (0, types_1.importESM)(plugin, true);
764
+ transform = await importESM(plugin, true);
755
765
  }
756
766
  catch {
757
767
  transform = require(plugin);
@@ -767,10 +777,39 @@ class Compress extends core_1.Module {
767
777
  }
768
778
  hasPermission(type, options) {
769
779
  if (super.hasPermission(type)) {
770
- return type === 'worker' ? core_1.WorkerChannel.hasPermission(options) : true;
780
+ return type === 'worker' ? WorkerChannel.hasPermission(options) : true;
771
781
  }
772
782
  return false;
773
783
  }
784
+ #createZstdCompress(file, options) {
785
+ let level, chunkSize, params, strategy;
786
+ if (options) {
787
+ ({ level, chunkSize, params, strategy } = options);
788
+ }
789
+ const zstd = applySettings(this.module.zstd, params ||= {});
790
+ if (typeof level === 'number') {
791
+ params[zlib.constants.ZSTD_c_compressionLevel] = level;
792
+ }
793
+ else if (typeof this.level.zst === 'number') {
794
+ params[zlib.constants.ZSTD_c_compressionLevel] ??= this.level.zst;
795
+ }
796
+ if (typeof strategy === 'number') {
797
+ params[zlib.constants.ZSTD_c_strategy] = strategy;
798
+ }
799
+ if (!isNaN(chunkSize = alignSize(chunkSize, 1))) {
800
+ zstd.chunkSize = chunkSize;
801
+ }
802
+ else {
803
+ zstd.chunkSize ??= this.chunkSize;
804
+ }
805
+ if (options) {
806
+ applyZlibOptions(zstd, options, 'memLevel', 'windowBits');
807
+ }
808
+ return this.getReadable(file, options).pipe(zlib.createZstdCompress(zstd));
809
+ }
810
+ #createWriteStreamAsZstd(file, output, options) {
811
+ return this.#createZstdCompress(file, options).pipe(fs.createWriteStream(output));
812
+ }
774
813
  #getCacheDir(type) {
775
814
  let tempDir = CACHE_TEMP[type];
776
815
  if (tempDir !== undefined) {
@@ -778,13 +817,13 @@ class Compress extends core_1.Module {
778
817
  }
779
818
  const { cache, cache_expires = 0 } = this.settings;
780
819
  let expires;
781
- if ((0, types_1.isPlainObject)(cache)) {
820
+ if (isPlainObject(cache)) {
782
821
  const value = cache[type];
783
822
  if (!cache.enabled || value === false) {
784
823
  return CACHE_TEMP[type] = null;
785
824
  }
786
825
  expires = value !== undefined ? value === true ? 0 : value : cache.expires;
787
- if (cache.dir && core_1.Module.createDir(tempDir = path.resolve(cache.dir, this.moduleName, type))) {
826
+ if (cache.dir && Module.createDir(tempDir = path.resolve(cache.dir, this.moduleName, type))) {
788
827
  this.addLog(5, 'Cache directory: ' + tempDir);
789
828
  }
790
829
  else {
@@ -794,11 +833,11 @@ class Compress extends core_1.Module {
794
833
  else if (cache !== true) {
795
834
  return CACHE_TEMP[type] = null;
796
835
  }
797
- expires = (0, types_1.parseExpires)(expires ?? cache_expires);
798
- tempDir ||= this.getTempDir({ pathname: type, moduleDir: true, createDir: true }) || (0, types_1.getTempDir)(true, this.moduleName, type);
836
+ expires = parseExpires(expires ?? cache_expires);
837
+ tempDir ||= this.getTempDir({ pathname: type, moduleDir: true, createDir: true }) || getTempDir(true, this.moduleName, type);
799
838
  CACHE_TEMP[type] = tempDir;
800
839
  if (expires === 0) {
801
- core_1.Module.removeDir(tempDir, true);
840
+ Module.removeDir(tempDir, true);
802
841
  }
803
842
  else {
804
843
  const current = Date.now();
@@ -841,7 +880,7 @@ class Compress extends core_1.Module {
841
880
  return tempDir;
842
881
  }
843
882
  set chunkSize(value) {
844
- this.#chunkSize = !isNaN(value = (0, types_1.alignSize)(value, 1)) ? value : undefined;
883
+ this.#chunkSize = !isNaN(value = alignSize(value, 1)) ? value : undefined;
845
884
  }
846
885
  get chunkSize() {
847
886
  return this.#chunkSize;
@@ -850,4 +889,5 @@ class Compress extends core_1.Module {
850
889
  return this.module.settings ||= {};
851
890
  }
852
891
  }
892
+
853
893
  module.exports = Compress;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e-mc/compress",
3
- "version": "0.13.10",
3
+ "version": "0.14.0",
4
4
  "description": "Compress constructor for E-mc.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -22,8 +22,8 @@
22
22
  "license": "BSD-3-Clause",
23
23
  "homepage": "https://github.com/anpham6/e-mc#readme",
24
24
  "dependencies": {
25
- "@e-mc/core": "0.13.10",
26
- "@e-mc/types": "0.13.10",
25
+ "@e-mc/core": "0.14.0",
26
+ "@e-mc/types": "0.14.0",
27
27
  "wawoff2": "^2.0.1",
28
28
  "woff2sfnt-sfnt2woff": "^1.0.0"
29
29
  }
@@ -1,19 +1,20 @@
1
1
  "use strict";
2
- const node_worker_threads_1 = require("node:worker_threads");
3
- const types_1 = require("@e-mc/types");
4
- const index_1 = require("@e-mc/compress");
5
- const PORT = node_worker_threads_1.workerData[0];
6
- node_worker_threads_1.parentPort.on('message', async (value) => {
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
4
+ const { importESM } = require('@e-mc/types');
5
+ const Compress = require('@e-mc/compress');
6
+ const PORT = workerData[0];
7
+ parentPort.on('message', async (value) => {
7
8
  try {
8
9
  const { plugin, data, options, metadata } = value;
9
10
  let transform;
10
11
  try {
11
- transform = await (0, types_1.importESM)(plugin, true);
12
+ transform = await importESM(plugin, true);
12
13
  }
13
14
  catch {
14
15
  transform = require(plugin);
15
16
  }
16
- void transform(options, metadata)(index_1.asBuffer(data))
17
+ void transform(options, metadata)(Compress.asBuffer(data))
17
18
  .then(result => {
18
19
  try {
19
20
  PORT.postMessage(result, [result.buffer]);
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
- const node_worker_threads_1 = require("node:worker_threads");
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
3
4
  const { toWoff } = require('woff2sfnt-sfnt2woff');
4
- const PORT = node_worker_threads_1.workerData[0];
5
- node_worker_threads_1.parentPort.on('message', (value) => {
5
+ const PORT = workerData[0];
6
+ parentPort.on('message', (value) => {
6
7
  let data = null;
7
8
  try {
8
9
  data = toWoff(value);
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
- const node_worker_threads_1 = require("node:worker_threads");
3
- const fs = require("node:fs");
4
- const stream = require("node:stream");
5
- const zlib = require("node:zlib");
6
- const index_1 = require("@e-mc/compress");
7
- const PORT = node_worker_threads_1.workerData[0];
8
- node_worker_threads_1.parentPort.on('message', async (value) => {
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
4
+ const fs = require('node:fs');
5
+ const stream = require('node:stream');
6
+ const zlib = require('node:zlib');
7
+ const Compress = require('@e-mc/compress');
8
+ const PORT = workerData[0];
9
+ parentPort.on('message', async (value) => {
9
10
  try {
10
11
  const { data, options, output } = value;
11
12
  const chunks = [];
12
- let transform = stream.Readable.from(index_1.asBuffer(data)).pipe(zlib.createBrotliCompress(options));
13
+ let transform = stream.Readable.from(Compress.asBuffer(data)).pipe(zlib.createBrotliCompress(options));
13
14
  if (output) {
14
15
  transform = transform.pipe(fs.createWriteStream(output));
15
16
  }
@@ -1,15 +1,16 @@
1
1
  "use strict";
2
- const node_worker_threads_1 = require("node:worker_threads");
3
- const fs = require("node:fs");
4
- const stream = require("node:stream");
5
- const zlib = require("node:zlib");
6
- const index_1 = require("@e-mc/compress");
7
- const PORT = node_worker_threads_1.workerData[0];
8
- node_worker_threads_1.parentPort.on('message', async (value) => {
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
4
+ const fs = require('node:fs');
5
+ const stream = require('node:stream');
6
+ const zlib = require('node:zlib');
7
+ const Compress = require('@e-mc/compress');
8
+ const PORT = workerData[0];
9
+ parentPort.on('message', async (value) => {
9
10
  try {
10
11
  const { data, options, output } = value;
11
12
  const chunks = [];
12
- let transform = stream.Readable.from(index_1.asBuffer(data)).pipe(zlib.createGzip(options));
13
+ let transform = stream.Readable.from(Compress.asBuffer(data)).pipe(zlib.createGzip(options));
13
14
  if (output) {
14
15
  transform = transform.pipe(fs.createWriteStream(output));
15
16
  }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
4
+ const fs = require('node:fs');
5
+ const stream = require('node:stream');
6
+ const zlib = require('node:zlib');
7
+ const Compress = require('@e-mc/compress');
8
+ const PORT = workerData[0];
9
+ parentPort.on('message', async (value) => {
10
+ try {
11
+ const { data, options, output } = value;
12
+ const chunks = [];
13
+ let transform = stream.Readable.from(Compress.asBuffer(data)).pipe(zlib.createZstdCompress(options));
14
+ if (output) {
15
+ transform = transform.pipe(fs.createWriteStream(output));
16
+ }
17
+ else {
18
+ transform.on('data', chunk => {
19
+ chunks.push(chunk);
20
+ });
21
+ }
22
+ transform
23
+ .on('finish', () => {
24
+ if (chunks.length > 0) {
25
+ const result = Buffer.concat(chunks);
26
+ PORT.postMessage(result, [result.buffer]);
27
+ }
28
+ else {
29
+ PORT.postMessage(output || null);
30
+ }
31
+ })
32
+ .on('error', (err) => {
33
+ transform.destroy();
34
+ throw err;
35
+ });
36
+ }
37
+ catch (err) {
38
+ console.error(err);
39
+ PORT.postMessage(null);
40
+ }
41
+ });
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
- const node_worker_threads_1 = require("node:worker_threads");
3
- const wawoff2 = require("wawoff2");
4
- const PORT = node_worker_threads_1.workerData[0];
5
- node_worker_threads_1.parentPort.on('message', (value) => {
6
- wawoff2.compress(value)
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
4
+ const { compress } = require('wawoff2');
5
+ const PORT = workerData[0];
6
+ parentPort.on('message', (value) => {
7
+ compress(value)
7
8
  .then(data => {
8
9
  PORT.postMessage(data);
9
10
  })
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
- const node_worker_threads_1 = require("node:worker_threads");
3
- const wawoff2 = require("wawoff2");
4
- const PORT = node_worker_threads_1.workerData[0];
5
- node_worker_threads_1.parentPort.on('message', (value) => {
6
- wawoff2.decompress(value)
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
4
+ const { decompress } = require('wawoff2');
5
+ const PORT = workerData[0];
6
+ parentPort.on('message', (value) => {
7
+ decompress(value)
7
8
  .then(data => {
8
9
  PORT.postMessage(data);
9
10
  })
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
- const node_worker_threads_1 = require("node:worker_threads");
2
+
3
+ const { parentPort, workerData } = require('node:worker_threads');
3
4
  const { toSfnt } = require('woff2sfnt-sfnt2woff');
4
- const PORT = node_worker_threads_1.workerData[0];
5
- node_worker_threads_1.parentPort.on('message', (value) => {
5
+ const PORT = workerData[0];
6
+ parentPort.on('message', (value) => {
6
7
  let data = null;
7
8
  try {
8
9
  data = toSfnt(value);