@cocreate/file 1.20.0 → 1.21.1

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.
@@ -22,13 +22,13 @@ jobs:
22
22
  runs-on: ubuntu-latest
23
23
  steps:
24
24
  - name: Checkout
25
- uses: actions/checkout@v3
25
+ uses: actions/checkout@v4
26
26
  - name: Setup Node.js
27
- uses: actions/setup-node@v3
27
+ uses: actions/setup-node@v4
28
28
  with:
29
- node-version: 14
29
+ node-version: 22 # Required for the latest semantic-release plugins
30
30
  - name: Semantic Release
31
- uses: cycjimmy/semantic-release-action@v3
31
+ uses: cycjimmy/semantic-release-action@v4 # Update to v4 for better Node 20+ support
32
32
  id: semantic
33
33
  with:
34
34
  extra_plugins: |
@@ -36,9 +36,8 @@ jobs:
36
36
  @semantic-release/git
37
37
  @semantic-release/github
38
38
  env:
39
- GITHUB_TOKEN: "${{ secrets.GITHUB }}"
39
+ GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" # Use the built-in token if possible
40
40
  NPM_TOKEN: "${{ secrets.NPM_TOKEN }}"
41
41
  outputs:
42
42
  new_release_published: "${{ steps.semantic.outputs.new_release_published }}"
43
43
  new_release_version: "${{ steps.semantic.outputs.new_release_version }}"
44
-
package/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [1.21.1](https://github.com/CoCreate-app/CoCreate-file/compare/v1.21.0...v1.21.1) (2026-01-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * update worklow ([2d82287](https://github.com/CoCreate-app/CoCreate-file/commit/2d8228735691832aaaf4a6d53b879cc0faa2f326))
7
+
8
+ # [1.21.0](https://github.com/CoCreate-app/CoCreate-file/compare/v1.20.0...v1.21.0) (2025-11-16)
9
+
10
+
11
+ ### Features
12
+
13
+ * enhance file processing by adding support for include/exclude filters and implementing dynamic data retrieval for various file types ([d8a2185](https://github.com/CoCreate-app/CoCreate-file/commit/d8a2185ce6f9554aa929f285e24526a281bc0b96))
14
+
1
15
  # [1.20.0](https://github.com/CoCreate-app/CoCreate-file/compare/v1.19.6...v1.20.0) (2025-10-11)
2
16
 
3
17
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cocreate/file",
3
- "version": "1.20.0",
3
+ "version": "1.21.1",
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/server.js CHANGED
@@ -4,6 +4,7 @@ const fs = require("fs");
4
4
  const realpathAsync = fs.promises.realpath;
5
5
 
6
6
  const path = require("path");
7
+ const { pathToFileURL } = require("url");
7
8
  const mimeTypes = {
8
9
  ".aac": "audio/aac",
9
10
  ".abw": "application/x-abiword",
@@ -174,46 +175,60 @@ module.exports = async function file(
174
175
  async function runDirectories() {
175
176
  for (const directory of directories) {
176
177
  const entry = directory.entry;
177
- const exclude = directory.exclude || [];
178
- await runFiles(directory, entry, exclude);
178
+ await runFiles(directory, entry);
179
179
  }
180
180
  return;
181
181
  }
182
182
 
183
- async function runFiles(directory, entry, exclude, Path, directoryName) {
183
+ async function runFiles(directory, entry, Path, directoryName) {
184
184
  const entryPath = path.resolve(configDirectoryPath, entry);
185
185
  let files = fs.readdirSync(entryPath);
186
-
186
+ let exclude = directory.exclude || [];
187
+ let include = directory.include || [];
187
188
  for (let file of files) {
188
- let skip = false;
189
- for (let i = 0; i < exclude.length; i++) {
190
- if (file.includes(exclude[i])) {
191
- skip = true;
192
- break;
193
- }
194
- }
195
- if (skip) continue;
189
+ const filePath = path.resolve(entryPath, file);
196
190
 
197
191
  let isDirectory;
198
192
  let isSymlink = fs
199
- .lstatSync(`${entryPath}/${file}`)
193
+ .lstatSync(filePath)
200
194
  .isSymbolicLink();
201
195
  if (isSymlink) {
202
- let symlinkPath = await realpathAsync(`${entryPath}/${file}`);
196
+ let symlinkPath = await realpathAsync(filePath);
203
197
  isDirectory =
204
198
  fs.existsSync(symlinkPath) &&
205
199
  fs.lstatSync(symlinkPath).isDirectory();
206
200
  } else
207
201
  isDirectory =
208
- fs.existsSync(`${entryPath}/${file}`) &&
209
- fs.lstatSync(`${entryPath}/${file}`).isDirectory();
202
+ fs.existsSync(filePath) &&
203
+ fs.lstatSync(filePath).isDirectory();
204
+
205
+ let skip = false;
206
+ for (let i = 0; i < exclude.length; i++) {
207
+ if (filePath.includes(exclude[i])) {
208
+ skip = true;
209
+ break;
210
+ }
211
+ }
212
+
213
+ for (let i = 0; i < include.length; i++) {
214
+ if (filePath.includes(include[i])) {
215
+ skip = false;
216
+ break;
217
+ } else if (isDirectory) {
218
+ skip = "directory";
219
+ break;
220
+ } else {
221
+ skip = true;
222
+ }
223
+ }
224
+
225
+ if (skip === true) continue;
210
226
 
211
227
  let name = file;
212
228
  let source = "";
213
229
 
214
230
  for (let i = 0; i < match.length; i++) {
215
231
  skip = true;
216
- const filePath = path.resolve(entryPath, file);
217
232
  if (filePath.startsWith(match[i])) {
218
233
  skip = false;
219
234
  break;
@@ -237,7 +252,7 @@ module.exports = async function file(
237
252
  } else directoryName = "/";
238
253
  }
239
254
 
240
- if (exclude && exclude.includes(directoryName)) continue;
255
+ // if (exclude && exclude.includes(directoryName)) continue;
241
256
 
242
257
  if (!Path) {
243
258
  if (directoryName === "/") Path = directoryName;
@@ -248,13 +263,15 @@ module.exports = async function file(
248
263
  if (Path === "/") pathname = Path + name;
249
264
  else pathname = Path + "/" + name;
250
265
 
251
- if (isDirectory) mimeType = "text/directory";
252
- else
266
+ if (isDirectory) {
267
+ mimeType = "text/directory";
268
+ } else {
253
269
  source = await getSource(
254
270
  `${entryPath}/${file}`,
255
271
  mimeType,
256
272
  isSymlink
257
273
  );
274
+ }
258
275
 
259
276
  let values = {
260
277
  "{{name}}": name || "",
@@ -262,85 +279,120 @@ module.exports = async function file(
262
279
  "{{directory}}": directoryName || "",
263
280
  "{{path}}": Path || "",
264
281
  "{{pathname}}": pathname,
265
- "{{content-type}}": mimeType || ""
282
+ "{{content-type}}": mimeType || "",
266
283
  };
267
284
 
268
- let object = { ...directory.object };
269
- if (!object.name) object.name = "{{name}}";
270
- if (!object.src) object.src = "{{source}}";
271
- if (!object.directory) object.directory = "{{directory}}";
272
- if (!object.path) object.path = "{{path}}";
273
- if (!object.pathname) object.pathname = "{{pathname}}";
274
- if (!object["content-type"])
275
- object["content-type"] = "{{content-type}}";
276
- if (
277
- !object.public &&
278
- object.public != false &&
279
- object.public != "false"
280
- )
281
- object.public = "true";
282
-
283
- let newObject = {
284
- array: directory.array || "files",
285
- object
285
+ let data = {
286
+ array: directory.array || "files"
286
287
  };
287
288
 
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
289
+ let isData = false;
290
+ if ( typeof directory.$data === "string") {
291
+ if (isDirectory) {
292
+ skip = "directory";
293
+ } else {
294
+ isData = true;
295
+ data = await getData(
296
+ `${entryPath}/${file}`,
297
+ mimeType,
298
+ isSymlink
299
+ );
300
+ if (!data) continue
301
+ }
302
+ } else if ( typeof directory.object === "string") {
303
+ if (isDirectory) {
304
+ skip = "directory";
305
+ } else {
306
+ isData = true;
307
+ data.object = await getData(
308
+ `${entryPath}/${file}`,
309
+ mimeType,
310
+ isSymlink
299
311
  );
300
- newObject.object.translations = translations;
301
- } catch (err) {
302
- console.error("Translation error:", err);
303
- // Continue without translations
312
+ if (!data.object) continue
313
+ }
314
+ } else if (typeof directory.object === "object" && directory.object !== null) {
315
+ data.array = directory.array || "files";
316
+ let object = { ...directory.object };
317
+ if (!object.name) object.name = "{{name}}";
318
+ if (!object.src) object.src = "{{source}}";
319
+ if (!object.directory) object.directory = "{{directory}}";
320
+ if (!object.path) object.path = "{{path}}";
321
+ if (!object.pathname) object.pathname = "{{pathname}}";
322
+ if (!object["content-type"])
323
+ object["content-type"] = "{{content-type}}";
324
+ if (
325
+ !object.public &&
326
+ object.public != false &&
327
+ object.public != "false"
328
+ )
329
+ object.public = "true";
330
+
331
+ data.object = object;
332
+
333
+ if (!data.object._id) {
334
+ data.$filter = {
335
+ query: {
336
+ pathname
337
+ }
338
+ };
304
339
  }
305
- }
306
340
 
307
- if (directory.storage) newObject.storage = directory.storage;
308
- if (directory.database) newObject.database = directory.database;
309
- if (directory.array) newObject.array = directory.array || "files";
341
+ if (
342
+ options.translate &&
343
+ mimeType === "text/html" &&
344
+ Array.isArray(directory.languages) &&
345
+ !object.translations
346
+ ) {
347
+ try {
348
+ // Call your AI translation service
349
+ const translations = await options.translate(
350
+ Buffer.isBuffer(source) ? source.toString('utf-8') : source,
351
+ directory.languages
352
+ );
353
+ data.object.translations = translations;
354
+ } catch (err) {
355
+ console.error("Translation error:", err);
356
+ // Continue without translations
357
+ }
358
+ }
310
359
 
311
- for (const key of Object.keys(directory.object)) {
312
- if (typeof directory.object[key] == "string") {
313
- let variables = directory.object[key].match(
314
- /{{([A-Za-z0-9_.,\[\]\-\/ ]*)}}/g
315
- );
316
- if (variables) {
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')}`;
360
+ if (directory.storage) data.storage = directory.storage;
361
+ if (directory.database) data.database = directory.database;
362
+ if (directory.array) data.array = directory.array || "files";
363
+
364
+ for (const key of Object.keys(directory.object)) {
365
+ if (typeof directory.object[key] == "string") {
366
+ let variables = directory.object[key].match(
367
+ /{{([A-Za-z0-9_.,\[\]\-\/ ]*)}}/g
368
+ );
369
+ if (variables) {
370
+ for (let variable of variables) {
371
+ let replacement = values[variable];
372
+ if (key === 'src' && variable === '{{source}}' && Buffer.isBuffer(source)) {
373
+ replacement = `data:${mimeType};base64,${source.toString('base64')}`;
374
+ }
375
+ data.object[key] = data.object[
376
+ key
377
+ ].replace(variable, replacement);
321
378
  }
322
- newObject.object[key] = newObject.object[
323
- key
324
- ].replace(variable, replacement);
325
379
  }
326
380
  }
327
381
  }
328
382
  }
329
383
 
330
384
  if (skip !== "directory") {
331
- if (!newObject.object._id)
332
- newObject.$filter = {
333
- query: {
334
- pathname
335
- }
336
- };
337
-
338
- response = await runStore(newObject);
339
- console.log(
340
- `Uploaded: ${entryPath}/${file}`,
341
- `To: ${pathname}`
342
- );
343
-
385
+ response = await runStore(data);
386
+ if (isData) {
387
+ console.log(
388
+ `Saved: ${entryPath}/${file}`
389
+ );
390
+ } else {
391
+ console.log(
392
+ `Uploaded: ${entryPath}/${file}`,
393
+ `To: ${pathname}`
394
+ );
395
+ }
344
396
  if (response.error) errorLog.push(response.error);
345
397
  }
346
398
 
@@ -349,7 +401,7 @@ module.exports = async function file(
349
401
  if (entry.endsWith("/")) newEntry = entry + name;
350
402
  else newEntry = entry + "/" + name;
351
403
 
352
- await runFiles(directory, newEntry, exclude, pathname, name);
404
+ await runFiles(directory, newEntry, pathname, name);
353
405
  }
354
406
  }
355
407
  // if (errorLog.length)
@@ -389,6 +441,57 @@ module.exports = async function file(
389
441
  }
390
442
  }
391
443
 
444
+ async function getData(filePath, mimeType, isSymlink) {
445
+ let resolvedPath = filePath;
446
+ if (isSymlink) {
447
+ resolvedPath = await realpathAsync(filePath);
448
+ }
449
+
450
+ try {
451
+ const fileMimeType = mimeTypes[path.extname(resolvedPath)] || "text/plain";
452
+ if (fileMimeType === "application/json") {
453
+ // Parse JSON files
454
+ return JSON.parse(fs.readFileSync(resolvedPath, "utf8"));
455
+ } else if (
456
+ fileMimeType === "application/javascript" ||
457
+ fileMimeType === "text/javascript"
458
+ ) {
459
+ // Try CommonJS require first (fast path)
460
+ try {
461
+ // clear require cache to ensure fresh load
462
+ delete require.cache[require.resolve(resolvedPath)];
463
+ return require(resolvedPath);
464
+ } catch (err) {
465
+ // If require fails due to ESM syntax (export / import), fall back to dynamic import
466
+ const isESMSyntaxError =
467
+ err instanceof SyntaxError ||
468
+ /Unexpected token 'export'/.test(err.message) ||
469
+ /Cannot use import statement outside a module/.test(err.message) ||
470
+ /Unexpected token 'import'/.test(err.message);
471
+
472
+ if (isESMSyntaxError) {
473
+ try {
474
+ const module = await import(pathToFileURL(resolvedPath).href);
475
+ // return default export when present otherwise return full module
476
+ return module && module.default ? module.default : module;
477
+ } catch (impErr) {
478
+ console.error(`Failed to dynamic-import module: ${resolvedPath}`, impErr);
479
+ throw impErr;
480
+ }
481
+ }
482
+ // rethrow original require error if it's not an ESM issue
483
+ throw err;
484
+ }
485
+ } else {
486
+ return fs.readFileSync(resolvedPath, "utf8");
487
+ }
488
+ } catch (error) {
489
+ console.error(`Failed to process file: ${resolvedPath}`, error);
490
+ return "";
491
+ }
492
+
493
+ }
494
+
392
495
  /**
393
496
  * Store files by config sources
394
497
  **/