@cocreate/file 1.19.6 → 1.20.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 +15 -0
- package/package.json +1 -1
- package/src/client.js +1 -1
- package/src/server.js +149 -148
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
# [1.20.0](https://github.com/CoCreate-app/CoCreate-file/compare/v1.19.6...v1.20.0) (2025-10-11)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* add error handling for translation service in file function ([262dc66](https://github.com/CoCreate-app/CoCreate-file/commit/262dc66d57c13a8948ef1afa863413c203f965bc))
|
|
7
|
+
* update import statement and modify exit behavior in server function ([6e72fde](https://github.com/CoCreate-app/CoCreate-file/commit/6e72fdec3867c031e342b3faef598b661f43a620))
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* add options parameter to file function for enhanced translation support ([7fac13d](https://github.com/CoCreate-app/CoCreate-file/commit/7fac13d10b8abd40584d5abc6b89d110f84db288))
|
|
13
|
+
* enhance file handling by supporting base64 encoding for binary sources and improving file reading logic ([192337c](https://github.com/CoCreate-app/CoCreate-file/commit/192337c3a44d412f8a8d2c6bb1172451661eebf5))
|
|
14
|
+
* enhance file processing with support for base64 and binary types, and add variable processing functionality ([eabfddf](https://github.com/CoCreate-app/CoCreate-file/commit/eabfddf2e1c36e7abd330369c1f28f90290cc1b8))
|
|
15
|
+
|
|
1
16
|
## [1.19.6](https://github.com/CoCreate-app/CoCreate-file/compare/v1.19.5...v1.19.6) (2025-09-07)
|
|
2
17
|
|
|
3
18
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cocreate/file",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "A versatile, configurable headless file uploader supporting local and server operations. Accessible via a JavaScript API and HTML5 attributes, it provides seamless file reading, writing, and uploading with fallbacks to the standard HTML5 file input API. Ideal for developers needing robust file management in headless environments.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"file-uploader",
|
package/src/client.js
CHANGED
|
@@ -28,7 +28,7 @@ import Elements from "@cocreate/elements";
|
|
|
28
28
|
import Actions from "@cocreate/actions";
|
|
29
29
|
import { render } from "@cocreate/render";
|
|
30
30
|
import { queryElements } from "@cocreate/utils";
|
|
31
|
-
import "@cocreate/element-prototype";
|
|
31
|
+
import { setValue } from "@cocreate/element-prototype";
|
|
32
32
|
|
|
33
33
|
const inputs = new Map();
|
|
34
34
|
const Files = new Map();
|
package/src/server.js
CHANGED
|
@@ -83,7 +83,12 @@ const mimeTypes = {
|
|
|
83
83
|
".7z": "application/x-7z-compressed"
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
module.exports = async function file(
|
|
86
|
+
module.exports = async function file(
|
|
87
|
+
CoCreateConfig,
|
|
88
|
+
configPath,
|
|
89
|
+
match,
|
|
90
|
+
options
|
|
91
|
+
) {
|
|
87
92
|
let directories = CoCreateConfig.directories;
|
|
88
93
|
let sources = CoCreateConfig.sources;
|
|
89
94
|
let configDirectoryPath = path.dirname(configPath);
|
|
@@ -253,7 +258,7 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
253
258
|
|
|
254
259
|
let values = {
|
|
255
260
|
"{{name}}": name || "",
|
|
256
|
-
"{{source}}": source || "",
|
|
261
|
+
"{{source}}": Buffer.isBuffer(source) ? `data:${mimeType};base64,${source.toString('base64')}` : source || "",
|
|
257
262
|
"{{directory}}": directoryName || "",
|
|
258
263
|
"{{path}}": Path || "",
|
|
259
264
|
"{{pathname}}": pathname,
|
|
@@ -280,6 +285,25 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
280
285
|
object
|
|
281
286
|
};
|
|
282
287
|
|
|
288
|
+
if (
|
|
289
|
+
options.translate &&
|
|
290
|
+
mimeType === "text/html" &&
|
|
291
|
+
Array.isArray(directory.languages) &&
|
|
292
|
+
!object.translations
|
|
293
|
+
) {
|
|
294
|
+
try {
|
|
295
|
+
// Call your AI translation service
|
|
296
|
+
const translations = await options.translate(
|
|
297
|
+
Buffer.isBuffer(source) ? source.toString('utf-8') : source,
|
|
298
|
+
directory.languages
|
|
299
|
+
);
|
|
300
|
+
newObject.object.translations = translations;
|
|
301
|
+
} catch (err) {
|
|
302
|
+
console.error("Translation error:", err);
|
|
303
|
+
// Continue without translations
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
283
307
|
if (directory.storage) newObject.storage = directory.storage;
|
|
284
308
|
if (directory.database) newObject.database = directory.database;
|
|
285
309
|
if (directory.array) newObject.array = directory.array || "files";
|
|
@@ -291,9 +315,13 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
291
315
|
);
|
|
292
316
|
if (variables) {
|
|
293
317
|
for (let variable of variables) {
|
|
318
|
+
let replacement = values[variable];
|
|
319
|
+
if (key === 'src' && variable === '{{source}}' && Buffer.isBuffer(source)) {
|
|
320
|
+
replacement = `data:${mimeType};base64,${source.toString('base64')}`;
|
|
321
|
+
}
|
|
294
322
|
newObject.object[key] = newObject.object[
|
|
295
323
|
key
|
|
296
|
-
].replace(variable,
|
|
324
|
+
].replace(variable, replacement);
|
|
297
325
|
}
|
|
298
326
|
}
|
|
299
327
|
}
|
|
@@ -328,144 +356,137 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
328
356
|
// console.log(...errorLog)
|
|
329
357
|
}
|
|
330
358
|
|
|
331
|
-
async function getSource(
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
readType = "utf8";
|
|
335
|
-
} else if (/^(image|audio|video)\/[-+.\w]+/.test(mimeType)) {
|
|
336
|
-
readType = "base64";
|
|
337
|
-
}
|
|
359
|
+
async function getSource(filePath, mimeType, isSymlink) {
|
|
360
|
+
// 1. UPDATED: Includes standard font types and uses simpler matching
|
|
361
|
+
const base64MimeTypes = /^(image|audio|video|font\/(woff2?|ttf|otf|eot)|application\/vnd\.ms-fontobject|application\/x-font-.*|application\/octet-stream)/;
|
|
338
362
|
|
|
339
|
-
if
|
|
363
|
+
// We only care if it needs to be Base64-encoded for a Data URI.
|
|
364
|
+
const needsBase64 = base64MimeTypes.test(mimeType);
|
|
340
365
|
|
|
341
|
-
let
|
|
342
|
-
|
|
366
|
+
let resolvedPath = filePath;
|
|
367
|
+
if (isSymlink) {
|
|
368
|
+
// Use promises for realpath
|
|
369
|
+
resolvedPath = await realpathAsync(filePath);
|
|
370
|
+
}
|
|
343
371
|
|
|
344
|
-
|
|
372
|
+
// 2. READ: Always read the file as a raw Buffer (omitting encoding)
|
|
373
|
+
// This gives us the raw bytes, which is the safest start for any file.
|
|
374
|
+
let fileBuffer;
|
|
375
|
+
try {
|
|
376
|
+
fileBuffer = await fs.promises.readFile(resolvedPath);
|
|
377
|
+
} catch (error) {
|
|
378
|
+
console.error(`Error reading file: ${resolvedPath}`, error);
|
|
379
|
+
return ""; // Return empty string or handle error as appropriate
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (needsBase64) {
|
|
383
|
+
// 3. RETURN BUFFER: Return the raw buffer for binary files.
|
|
384
|
+
return fileBuffer;
|
|
385
|
+
} else {
|
|
386
|
+
// 4. HANDLE TEXT/OTHER:
|
|
387
|
+
// For files not intended for Base64, convert the Buffer to a string using 'utf8'.
|
|
388
|
+
return fileBuffer.toString('utf8');
|
|
389
|
+
}
|
|
345
390
|
}
|
|
346
391
|
|
|
347
392
|
/**
|
|
348
393
|
* Store files by config sources
|
|
349
394
|
**/
|
|
350
395
|
async function runSources() {
|
|
351
|
-
let
|
|
396
|
+
let newConfig = require(configPath);
|
|
352
397
|
|
|
353
398
|
for (let i = 0; i < sources.length; i++) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
);
|
|
371
|
-
if (variables) {
|
|
372
|
-
let originalValue = object[key];
|
|
373
|
-
keys.set(key, originalValue);
|
|
374
|
-
let value = "";
|
|
375
|
-
for (let variable of variables) {
|
|
376
|
-
let entry = /{{\s*([\w\W]+)\s*}}/g.exec(
|
|
377
|
-
variable
|
|
378
|
-
);
|
|
379
|
-
entry = entry[1].trim();
|
|
380
|
-
if (entry) {
|
|
381
|
-
if (!fs.existsSync(entry)) continue;
|
|
382
|
-
|
|
383
|
-
if (!isMatch) {
|
|
384
|
-
const filePath = path.resolve(
|
|
385
|
-
configDirectoryPath,
|
|
386
|
-
entry
|
|
387
|
-
);
|
|
388
|
-
for (
|
|
389
|
-
let i = 0;
|
|
390
|
-
i < match.length;
|
|
391
|
-
i++
|
|
392
|
-
) {
|
|
393
|
-
if (
|
|
394
|
-
filePath.startsWith(
|
|
395
|
-
match[i]
|
|
396
|
-
)
|
|
397
|
-
) {
|
|
398
|
-
console.log(
|
|
399
|
-
"Source saved",
|
|
400
|
-
sources[i]
|
|
401
|
-
);
|
|
402
|
-
isMatch = true;
|
|
403
|
-
break;
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
let read_type = "utf8";
|
|
409
|
-
const fileExtension =
|
|
410
|
-
path.extname(entry);
|
|
411
|
-
let mime_type =
|
|
412
|
-
mimeTypes[fileExtension] ||
|
|
413
|
-
"text/html";
|
|
414
|
-
|
|
415
|
-
if (
|
|
416
|
-
/^(image|audio|video)\/[-+.\w]+/.test(
|
|
417
|
-
mime_type
|
|
418
|
-
)
|
|
419
|
-
) {
|
|
420
|
-
read_type = "base64";
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
let binary = fs.readFileSync(entry);
|
|
424
|
-
let content = new Buffer.from(
|
|
425
|
-
binary
|
|
426
|
-
).toString(read_type);
|
|
427
|
-
if (content) value += content;
|
|
428
|
-
// object[key] = object[key].replace(variable, content);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
object[key] = value;
|
|
432
|
-
}
|
|
399
|
+
let data = sources[i];
|
|
400
|
+
|
|
401
|
+
// Handle string values
|
|
402
|
+
if (typeof data === "string") {
|
|
403
|
+
let {value, filePath } = await processVariables(data);
|
|
404
|
+
let response = await runStore(value);
|
|
405
|
+
if (response && response.object && response.object[0]) {
|
|
406
|
+
updateFilePath(filePath, response); // Call the new function to update the file path
|
|
407
|
+
}
|
|
408
|
+
} else if (data.array && data.object) {
|
|
409
|
+
if (typeof data.object === "string") {
|
|
410
|
+
let {value, filePath } = await processVariables(data.object);
|
|
411
|
+
if (value) {
|
|
412
|
+
let response = await runStore(value);
|
|
413
|
+
if (response && response.object && response.object[0]) {
|
|
414
|
+
updateFilePath(filePath, response.object);
|
|
433
415
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
if (
|
|
444
|
-
|
|
416
|
+
}
|
|
417
|
+
} else if (typeof data.object === "object" && data.object !== null) {
|
|
418
|
+
for (const key in data) {
|
|
419
|
+
let {value } = await processVariables(data[key]);
|
|
420
|
+
if (data) {
|
|
421
|
+
data.object[key] = value;
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
let response = await runStore(data);
|
|
425
|
+
if (response && response.object && response.object[0] && response.object[0]._id) {
|
|
426
|
+
newConfig.sources[i].object._id = response.object[0]._id;
|
|
427
|
+
}
|
|
445
428
|
}
|
|
446
|
-
} catch (err) {
|
|
447
|
-
console.log(err);
|
|
448
|
-
process.exit();
|
|
449
429
|
}
|
|
450
430
|
|
|
451
|
-
|
|
452
|
-
response.object &&
|
|
453
|
-
response.object[0] &&
|
|
454
|
-
response.object[0]._id
|
|
455
|
-
) {
|
|
456
|
-
source.object._id = response.object[0]._id;
|
|
457
|
-
}
|
|
431
|
+
}
|
|
458
432
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
433
|
+
return newConfig;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
async function processVariables(value) {
|
|
437
|
+
let variableMatch = /{{\s*([\w\W]+)\s*}}/g.exec(value);
|
|
438
|
+
if (!variableMatch) return { value, filePath: null };
|
|
439
|
+
|
|
440
|
+
let entry = variableMatch[1].trim();
|
|
441
|
+
if (!fs.existsSync(entry)) return { value, filePath: null };
|
|
462
442
|
|
|
463
|
-
|
|
443
|
+
const filePath = path.resolve(configDirectoryPath, entry);
|
|
444
|
+
|
|
445
|
+
// Check if the file path matches any of the provided match patterns
|
|
446
|
+
let isMatched = match.some((pattern) => filePath.startsWith(pattern));
|
|
447
|
+
if (!isMatched) return { value, filePath: null };
|
|
448
|
+
|
|
449
|
+
// Read the file as is
|
|
450
|
+
let content;
|
|
451
|
+
try {
|
|
452
|
+
const fileMimeType = mimeTypes[path.extname(entry)] || "text/plain";
|
|
453
|
+
|
|
454
|
+
if (fileMimeType === "application/json") {
|
|
455
|
+
// Parse JSON files
|
|
456
|
+
content = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
457
|
+
} else if (fileMimeType === "application/javascript" || fileMimeType === "text/javascript") {
|
|
458
|
+
// For JavaScript files, require the file to execute exports
|
|
459
|
+
content = require(filePath);
|
|
460
|
+
} else {
|
|
461
|
+
// For plain strings, read as UTF-8 without conversion
|
|
462
|
+
content = fs.readFileSync(filePath, "utf8");
|
|
463
|
+
}
|
|
464
|
+
} catch (error) {
|
|
465
|
+
console.error(`Failed to process file: ${filePath}`, error);
|
|
466
|
+
return { value, filePath: null };
|
|
464
467
|
}
|
|
465
468
|
|
|
466
|
-
return
|
|
469
|
+
return { value: content, filePath };
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Updates the file at the given file path with the provided data.
|
|
474
|
+
* The data is saved as a JSON string.
|
|
475
|
+
*
|
|
476
|
+
* @param {string} filePath - The path of the file to update.
|
|
477
|
+
* @param {object} data - The data to write to the file.
|
|
478
|
+
*/
|
|
479
|
+
function updateFilePath(filePath, data) {
|
|
480
|
+
try {
|
|
481
|
+
const jsonData = JSON.stringify(data, null, 4); // Format JSON with indentation
|
|
482
|
+
fs.writeFileSync(filePath, jsonData, "utf8");
|
|
483
|
+
console.log(`File updated successfully at: ${filePath}`);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error(`Failed to update file at: ${filePath}`, error);
|
|
486
|
+
}
|
|
467
487
|
}
|
|
468
488
|
|
|
489
|
+
|
|
469
490
|
async function runStore(data) {
|
|
470
491
|
try {
|
|
471
492
|
let response;
|
|
@@ -493,42 +514,22 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
493
514
|
}
|
|
494
515
|
|
|
495
516
|
async function run() {
|
|
496
|
-
if (directories)
|
|
517
|
+
if (directories) {
|
|
518
|
+
await runDirectories();
|
|
519
|
+
}
|
|
497
520
|
|
|
498
521
|
if (sources && sources.length) {
|
|
499
|
-
let
|
|
500
|
-
let newConfig = { ...CoCreateConfig };
|
|
501
|
-
if (directories && directories.length)
|
|
502
|
-
newConfig.directories = directories;
|
|
503
|
-
|
|
504
|
-
newConfig.sources = sources;
|
|
505
|
-
|
|
506
|
-
if (newConfig.repositories)
|
|
507
|
-
newConfig.repositories.forEach((obj) => {
|
|
508
|
-
for (const key in obj) {
|
|
509
|
-
if (!["path", "repo", "exclude"].includes(key)) {
|
|
510
|
-
delete obj[key];
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
delete newConfig.url;
|
|
516
|
-
delete newConfig.broadcast;
|
|
517
|
-
|
|
522
|
+
let newConfig = await runSources();
|
|
518
523
|
fs.writeFileSync(
|
|
519
524
|
configPath,
|
|
520
525
|
`module.exports = ${JSON.stringify(newConfig, null, 4)};`
|
|
521
526
|
);
|
|
522
527
|
}
|
|
523
|
-
|
|
524
|
-
if (!match.length) {
|
|
525
|
-
console.log("upload complete!");
|
|
526
|
-
|
|
527
|
-
setTimeout(function () {
|
|
528
|
-
process.exit();
|
|
529
|
-
}, 2000);
|
|
530
|
-
}
|
|
531
528
|
}
|
|
532
529
|
|
|
533
530
|
await run();
|
|
531
|
+
// Only exit if not in watch mode
|
|
532
|
+
if (!process.argv.includes("--watch") && !process.argv.includes("-w")) {
|
|
533
|
+
process.exit();
|
|
534
|
+
}
|
|
534
535
|
};
|