@cocreate/file 1.19.5 → 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 +24 -0
- package/package.json +1 -1
- package/src/client.js +1 -1
- package/src/server.js +156 -152
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
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
|
+
|
|
16
|
+
## [1.19.6](https://github.com/CoCreate-app/CoCreate-file/compare/v1.19.5...v1.19.6) (2025-09-07)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
### Bug Fixes
|
|
20
|
+
|
|
21
|
+
* correct exclusion logic to check each item in the exclude array ([1fae391](https://github.com/CoCreate-app/CoCreate-file/commit/1fae391f4333813aa30805e87ba7ea28fefda5d3))
|
|
22
|
+
* ensure match is an array by wrapping non-array values ([5c67ec5](https://github.com/CoCreate-app/CoCreate-file/commit/5c67ec592f0217199b2ce965d14e377d6faa9a5b))
|
|
23
|
+
* ensure match is an array of directory paths and await run function ([eb47cf0](https://github.com/CoCreate-app/CoCreate-file/commit/eb47cf00c672ac5110293b3dab38fd8186bb8d64))
|
|
24
|
+
|
|
1
25
|
## [1.19.5](https://github.com/CoCreate-app/CoCreate-file/compare/v1.19.4...v1.19.5) (2025-05-01)
|
|
2
26
|
|
|
3
27
|
|
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,13 +83,21 @@ 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);
|
|
90
95
|
|
|
91
|
-
if (match && !Array.isArray(match))
|
|
92
|
-
|
|
96
|
+
if (match && !Array.isArray(match)) {
|
|
97
|
+
match = [match];
|
|
98
|
+
} else if (!match) {
|
|
99
|
+
match = [];
|
|
100
|
+
}
|
|
93
101
|
|
|
94
102
|
let config = await Config(
|
|
95
103
|
{
|
|
@@ -179,7 +187,7 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
179
187
|
for (let file of files) {
|
|
180
188
|
let skip = false;
|
|
181
189
|
for (let i = 0; i < exclude.length; i++) {
|
|
182
|
-
if (file.includes(exclude)) {
|
|
190
|
+
if (file.includes(exclude[i])) {
|
|
183
191
|
skip = true;
|
|
184
192
|
break;
|
|
185
193
|
}
|
|
@@ -250,7 +258,7 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
250
258
|
|
|
251
259
|
let values = {
|
|
252
260
|
"{{name}}": name || "",
|
|
253
|
-
"{{source}}": source || "",
|
|
261
|
+
"{{source}}": Buffer.isBuffer(source) ? `data:${mimeType};base64,${source.toString('base64')}` : source || "",
|
|
254
262
|
"{{directory}}": directoryName || "",
|
|
255
263
|
"{{path}}": Path || "",
|
|
256
264
|
"{{pathname}}": pathname,
|
|
@@ -277,6 +285,25 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
277
285
|
object
|
|
278
286
|
};
|
|
279
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
|
+
|
|
280
307
|
if (directory.storage) newObject.storage = directory.storage;
|
|
281
308
|
if (directory.database) newObject.database = directory.database;
|
|
282
309
|
if (directory.array) newObject.array = directory.array || "files";
|
|
@@ -288,9 +315,13 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
288
315
|
);
|
|
289
316
|
if (variables) {
|
|
290
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
|
+
}
|
|
291
322
|
newObject.object[key] = newObject.object[
|
|
292
323
|
key
|
|
293
|
-
].replace(variable,
|
|
324
|
+
].replace(variable, replacement);
|
|
294
325
|
}
|
|
295
326
|
}
|
|
296
327
|
}
|
|
@@ -325,144 +356,137 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
325
356
|
// console.log(...errorLog)
|
|
326
357
|
}
|
|
327
358
|
|
|
328
|
-
async function getSource(
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
readType = "utf8";
|
|
332
|
-
} else if (/^(image|audio|video)\/[-+.\w]+/.test(mimeType)) {
|
|
333
|
-
readType = "base64";
|
|
334
|
-
}
|
|
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)/;
|
|
335
362
|
|
|
336
|
-
if
|
|
363
|
+
// We only care if it needs to be Base64-encoded for a Data URI.
|
|
364
|
+
const needsBase64 = base64MimeTypes.test(mimeType);
|
|
337
365
|
|
|
338
|
-
let
|
|
339
|
-
|
|
366
|
+
let resolvedPath = filePath;
|
|
367
|
+
if (isSymlink) {
|
|
368
|
+
// Use promises for realpath
|
|
369
|
+
resolvedPath = await realpathAsync(filePath);
|
|
370
|
+
}
|
|
340
371
|
|
|
341
|
-
|
|
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
|
+
}
|
|
342
390
|
}
|
|
343
391
|
|
|
344
392
|
/**
|
|
345
393
|
* Store files by config sources
|
|
346
394
|
**/
|
|
347
395
|
async function runSources() {
|
|
348
|
-
let
|
|
396
|
+
let newConfig = require(configPath);
|
|
349
397
|
|
|
350
398
|
for (let i = 0; i < sources.length; i++) {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
);
|
|
368
|
-
if (variables) {
|
|
369
|
-
let originalValue = object[key];
|
|
370
|
-
keys.set(key, originalValue);
|
|
371
|
-
let value = "";
|
|
372
|
-
for (let variable of variables) {
|
|
373
|
-
let entry = /{{\s*([\w\W]+)\s*}}/g.exec(
|
|
374
|
-
variable
|
|
375
|
-
);
|
|
376
|
-
entry = entry[1].trim();
|
|
377
|
-
if (entry) {
|
|
378
|
-
if (!fs.existsSync(entry)) continue;
|
|
379
|
-
|
|
380
|
-
if (!isMatch) {
|
|
381
|
-
const filePath = path.resolve(
|
|
382
|
-
configDirectoryPath,
|
|
383
|
-
entry
|
|
384
|
-
);
|
|
385
|
-
for (
|
|
386
|
-
let i = 0;
|
|
387
|
-
i < match.length;
|
|
388
|
-
i++
|
|
389
|
-
) {
|
|
390
|
-
if (
|
|
391
|
-
filePath.startsWith(
|
|
392
|
-
match[i]
|
|
393
|
-
)
|
|
394
|
-
) {
|
|
395
|
-
console.log(
|
|
396
|
-
"Source saved",
|
|
397
|
-
sources[i]
|
|
398
|
-
);
|
|
399
|
-
isMatch = true;
|
|
400
|
-
break;
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
let read_type = "utf8";
|
|
406
|
-
const fileExtension =
|
|
407
|
-
path.extname(entry);
|
|
408
|
-
let mime_type =
|
|
409
|
-
mimeTypes[fileExtension] ||
|
|
410
|
-
"text/html";
|
|
411
|
-
|
|
412
|
-
if (
|
|
413
|
-
/^(image|audio|video)\/[-+.\w]+/.test(
|
|
414
|
-
mime_type
|
|
415
|
-
)
|
|
416
|
-
) {
|
|
417
|
-
read_type = "base64";
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
let binary = fs.readFileSync(entry);
|
|
421
|
-
let content = new Buffer.from(
|
|
422
|
-
binary
|
|
423
|
-
).toString(read_type);
|
|
424
|
-
if (content) value += content;
|
|
425
|
-
// object[key] = object[key].replace(variable, content);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
object[key] = value;
|
|
429
|
-
}
|
|
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);
|
|
430
415
|
}
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (
|
|
441
|
-
|
|
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
|
+
}
|
|
442
428
|
}
|
|
443
|
-
} catch (err) {
|
|
444
|
-
console.log(err);
|
|
445
|
-
process.exit();
|
|
446
429
|
}
|
|
447
430
|
|
|
448
|
-
|
|
449
|
-
response.object &&
|
|
450
|
-
response.object[0] &&
|
|
451
|
-
response.object[0]._id
|
|
452
|
-
) {
|
|
453
|
-
source.object._id = response.object[0]._id;
|
|
454
|
-
}
|
|
431
|
+
}
|
|
455
432
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
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 };
|
|
442
|
+
|
|
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 };
|
|
459
448
|
|
|
460
|
-
|
|
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 };
|
|
461
467
|
}
|
|
462
468
|
|
|
463
|
-
return
|
|
469
|
+
return { value: content, filePath };
|
|
464
470
|
}
|
|
465
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
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
|
|
466
490
|
async function runStore(data) {
|
|
467
491
|
try {
|
|
468
492
|
let response;
|
|
@@ -490,42 +514,22 @@ module.exports = async function file(CoCreateConfig, configPath, match) {
|
|
|
490
514
|
}
|
|
491
515
|
|
|
492
516
|
async function run() {
|
|
493
|
-
if (directories)
|
|
517
|
+
if (directories) {
|
|
518
|
+
await runDirectories();
|
|
519
|
+
}
|
|
494
520
|
|
|
495
521
|
if (sources && sources.length) {
|
|
496
|
-
let
|
|
497
|
-
let newConfig = { ...CoCreateConfig };
|
|
498
|
-
if (directories && directories.length)
|
|
499
|
-
newConfig.directories = directories;
|
|
500
|
-
|
|
501
|
-
newConfig.sources = sources;
|
|
502
|
-
|
|
503
|
-
if (newConfig.repositories)
|
|
504
|
-
newConfig.repositories.forEach((obj) => {
|
|
505
|
-
for (const key in obj) {
|
|
506
|
-
if (!["path", "repo", "exclude"].includes(key)) {
|
|
507
|
-
delete obj[key];
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
});
|
|
511
|
-
|
|
512
|
-
delete newConfig.url;
|
|
513
|
-
delete newConfig.broadcast;
|
|
514
|
-
|
|
522
|
+
let newConfig = await runSources();
|
|
515
523
|
fs.writeFileSync(
|
|
516
524
|
configPath,
|
|
517
525
|
`module.exports = ${JSON.stringify(newConfig, null, 4)};`
|
|
518
526
|
);
|
|
519
527
|
}
|
|
520
|
-
|
|
521
|
-
if (!match.length) {
|
|
522
|
-
console.log("upload complete!");
|
|
523
|
-
|
|
524
|
-
setTimeout(function () {
|
|
525
|
-
process.exit();
|
|
526
|
-
}, 2000);
|
|
527
|
-
}
|
|
528
528
|
}
|
|
529
529
|
|
|
530
|
-
run();
|
|
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
|
+
}
|
|
531
535
|
};
|