nuxt-file-storage 0.3.1 → 0.3.2

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
@@ -2,9 +2,10 @@
2
2
 
3
3
  # Nuxt File Storage
4
4
 
5
- [![Visits Badge](https://badges.pufler.dev/visits/nyllre/nuxt-file-storage)](https://badges.pufler.dev)
5
+ <!-- [![Visits Badge](https://badges.pufler.dev/visits/nyllre/nuxt-file-storage)](https://badges.pufler.dev) -->
6
6
  [![npm version][npm-version-src]][npm-version-href]
7
7
  [![npm downloads][npm-downloads-src]][npm-downloads-href]
8
+ [![nuxt.care health](https://img.shields.io/endpoint?url=https://nuxt.care/api/badge/nuxt-file-storage&style=flat&colorA=18181B&colorB=28CF8D)](https://nuxt.care/?search=nuxt-file-storage)
8
9
  [![License][license-src]][license-href]
9
10
  [![Nuxt][nuxt-src]][nuxt-href]
10
11
 
package/dist/module.d.mts CHANGED
@@ -1,32 +1,7 @@
1
1
  import * as _nuxt_schema from '@nuxt/schema';
2
-
3
- interface ServerFile {
4
- name: string
5
- content: string
6
- size: string
7
- type: string
8
- lastModified: string
9
- }
10
-
11
- interface ClientFile extends Blob {
12
- content: string | ArrayBuffer | null | undefined
13
- name: string
14
- lastModified: number
15
- }
16
-
17
- interface ModuleOptions {
18
- mount: string
19
- version: string
20
- }
21
-
22
- /**
23
- * @description Augment the '#imports' module to include useRuntimeConfig
24
- * this is only needed because this package is consumed as a module
25
- */
26
- declare module '#imports' {
27
- export function useRuntimeConfig(): any
28
- }
2
+ import { ModuleOptions } from '../dist/runtime/types.js';
3
+ export * from '../dist/runtime/types.js';
29
4
 
30
5
  declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
31
6
 
32
- export { type ClientFile, type ModuleOptions, type ServerFile, _default as default };
7
+ export { _default as default };
package/dist/module.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "nuxt-file-storage",
3
3
  "configKey": "fileStorage",
4
- "version": "0.3.1",
4
+ "version": "0.3.2",
5
5
  "builder": {
6
- "@nuxt/module-builder": "0.8.4",
7
- "unbuild": "2.0.0"
6
+ "@nuxt/module-builder": "1.0.2",
7
+ "unbuild": "3.6.1"
8
8
  }
9
9
  }
package/dist/module.mjs CHANGED
@@ -1,16 +1,7 @@
1
- import { defineNuxtModule, logger, createResolver, addImportsDir, addServerScanDir } from '@nuxt/kit';
1
+ import { defineNuxtModule, createResolver, logger, addImportsDir, addServerScanDir, addTemplate } from '@nuxt/kit';
2
2
  import { defu } from 'defu';
3
3
 
4
-
5
-
6
- // -- Unbuild CommonJS Shims --
7
- import __cjs_url__ from 'url';
8
- import __cjs_path__ from 'path';
9
- import __cjs_mod__ from 'module';
10
- const __filename = __cjs_url__.fileURLToPath(import.meta.url);
11
- const __dirname = __cjs_path__.dirname(__filename);
12
- const require = __cjs_mod__.createRequire(import.meta.url);
13
- const module = defineNuxtModule({
4
+ const module$1 = defineNuxtModule({
14
5
  meta: {
15
6
  name: "nuxt-file-storage",
16
7
  configKey: "fileStorage"
@@ -21,15 +12,35 @@ const module = defineNuxtModule({
21
12
  // version: '0.0.0',
22
13
  // },
23
14
  setup(options, nuxt) {
15
+ const { resolve } = createResolver(import.meta.url);
24
16
  const config = nuxt.options.runtimeConfig;
25
17
  config.public.fileStorage = defu(config.public.fileStorage, {
26
18
  ...options
27
19
  });
28
20
  logger.ready(`Nuxt File Storage has mounted successfully`);
29
- const resolve = createResolver(__dirname).resolve;
21
+ nuxt.options.alias["#file-storage"] = resolve("./runtime");
30
22
  addImportsDir(resolve("runtime/composables"));
31
23
  addServerScanDir(resolve("./runtime/server"));
24
+ addTemplate({
25
+ filename: "types/nuxt-file-storage.d.ts",
26
+ getContents: () => [
27
+ "declare module '#file-storage' {",
28
+ ` const ServerFile: typeof import('${resolve("./runtime/types")}').ServerFile`,
29
+ ` const ClientFile: typeof import('${resolve("./runtime/types")}').ClientFile`,
30
+ "}",
31
+ "",
32
+ "declare global {",
33
+ ` type ServerFile = import('${resolve("./runtime/types")}').ServerFile`,
34
+ ` type ClientFile = import('${resolve("./runtime/types")}').ClientFile`,
35
+ "}",
36
+ "",
37
+ "export {}"
38
+ ].join("\n")
39
+ });
40
+ nuxt.hook("prepare:types", async (options2) => {
41
+ options2.references.push({ path: resolve(nuxt.options.buildDir, "types/nuxt-file-storage.d.ts") });
42
+ });
32
43
  }
33
44
  });
34
45
 
35
- export { module as default };
46
+ export { module$1 as default };
@@ -1,39 +1,8 @@
1
- import type { ClientFile } from '../../types.js';
2
1
  type Options = {
3
2
  clearOldFiles: boolean;
4
3
  };
5
4
  export default function (options?: Options): {
6
- files: import("vue").Ref<{
7
- content: string | {
8
- readonly byteLength: number;
9
- slice: (begin?: number, end?: number) => ArrayBuffer;
10
- readonly [Symbol.toStringTag]: "ArrayBuffer";
11
- } | null | undefined;
12
- name: string;
13
- lastModified: number;
14
- readonly size: number;
15
- readonly type: string;
16
- arrayBuffer: () => Promise<ArrayBuffer>;
17
- bytes: () => Promise<Uint8Array<ArrayBuffer>>;
18
- slice: (start?: number, end?: number, contentType?: string) => Blob;
19
- stream: () => ReadableStream<Uint8Array<ArrayBuffer>>;
20
- text: () => Promise<string>;
21
- }[], ClientFile[] | {
22
- content: string | {
23
- readonly byteLength: number;
24
- slice: (begin?: number, end?: number) => ArrayBuffer;
25
- readonly [Symbol.toStringTag]: "ArrayBuffer";
26
- } | null | undefined;
27
- name: string;
28
- lastModified: number;
29
- readonly size: number;
30
- readonly type: string;
31
- arrayBuffer: () => Promise<ArrayBuffer>;
32
- bytes: () => Promise<Uint8Array<ArrayBuffer>>;
33
- slice: (start?: number, end?: number, contentType?: string) => Blob;
34
- stream: () => ReadableStream<Uint8Array<ArrayBuffer>>;
35
- text: () => Promise<string>;
36
- }[]>;
5
+ files: import("vue").Ref<any, any>;
37
6
  handleFileInput: (event: any) => Promise<void>;
38
7
  clearFiles: () => void;
39
8
  };
@@ -1,4 +1,4 @@
1
1
  import { defineNuxtPlugin } from "#app";
2
- export default defineNuxtPlugin((nuxtApp) => {
2
+ export default defineNuxtPlugin((_nuxtApp) => {
3
3
  console.log("nuxt-file-storage initialized successfully");
4
4
  });
@@ -2,42 +2,31 @@ import path from "path";
2
2
  import { realpath, stat } from "fs/promises";
3
3
  import { createError } from "#imports";
4
4
  export const normalizeRelative = (p) => {
5
- if (!p)
6
- return "";
5
+ if (!p) return "";
7
6
  return p.replace(/^[/\\]+/, "").replace(/\\/g, "/");
8
7
  };
9
8
  export const isSafeBasename = (name) => {
10
- if (!name)
11
- return false;
12
- if (name !== path.basename(name))
13
- return false;
14
- if (name.includes("\0"))
15
- return false;
16
- if (name === "." || name === "..")
17
- return false;
18
- if (name.includes("/") || name.includes("\\"))
19
- return false;
20
- if (name.split(/[/\\]+/).includes(".."))
21
- return false;
9
+ if (!name) return false;
10
+ if (name !== path.basename(name)) return false;
11
+ if (name.includes("\0")) return false;
12
+ if (name === "." || name === "..") return false;
13
+ if (name.includes("/") || name.includes("\\")) return false;
14
+ if (name.split(/[/\\]+/).includes("..")) return false;
22
15
  return true;
23
16
  };
24
17
  export const ensureSafeBasename = (name) => {
25
- if (!isSafeBasename(name))
26
- throw new Error("Unsafe filename");
18
+ if (!isSafeBasename(name)) throw new Error("Unsafe filename");
27
19
  return name;
28
20
  };
29
21
  export const containsPathTraversal = (p) => {
30
- if (!p)
31
- return false;
32
- if (/^([\\/]|^)?\.\.([\\/]|$)?/.test(p) || /(^|[\\/])\.\.($|[\\/])/.test(p))
33
- return true;
22
+ if (!p) return false;
23
+ if (/^([\\/]|^)?\.\.([\\/]|$)?/.test(p) || /(^|[\\/])\.\.($|[\\/])/.test(p)) return true;
34
24
  const normalized = path.normalize(p);
35
25
  const parts = normalized.split(/[/\\]+/);
36
26
  return parts.includes("..");
37
27
  };
38
28
  export const resolveAndEnsureInside = async (mount, ...parts) => {
39
- if (!mount)
40
- throw new Error("Mount path must be provided");
29
+ if (!mount) throw new Error("Mount path must be provided");
41
30
  const mountResolved = path.resolve(mount);
42
31
  const cleanedParts = parts.map((p) => p.replace(/^[/\\]+/, ""));
43
32
  const targetResolved = path.resolve(mountResolved, ...cleanedParts);
@@ -48,10 +37,9 @@ export const resolveAndEnsureInside = async (mount, ...parts) => {
48
37
  try {
49
38
  await stat(cur);
50
39
  break;
51
- } catch (error) {
40
+ } catch {
52
41
  const parent = path.dirname(cur);
53
- if (parent === cur)
54
- break;
42
+ if (parent === cur) break;
55
43
  cur = parent;
56
44
  }
57
45
  }
@@ -1,4 +1,4 @@
1
- import type { ServerFile } from '../../../types.js';
1
+ import type { ServerFile } from '../../types.js';
2
2
  import type { H3Event, EventHandlerRequest } from 'h3';
3
3
  /**
4
4
  * @description Will store the file in the specified directory
@@ -10,27 +10,50 @@ import { createReadStream, promises as fsPromises } from "fs";
10
10
  const getMount = () => {
11
11
  try {
12
12
  return useRuntimeConfig().public.fileStorage.mount;
13
- } catch (err) {
13
+ } catch {
14
14
  return process.env.FILE_STORAGE_MOUNT || process.env.NUXT_FILE_STORAGE_MOUNT;
15
15
  }
16
16
  };
17
17
  export const storeFileLocally = async (file, fileNameOrIdLength, filelocation = "") => {
18
18
  const { binaryString, ext } = parseDataUrl(file.content);
19
19
  const location = getMount();
20
- if (!location)
21
- throw new Error("fileStorage.mount is not configured");
22
- const originalExt = file.name.toString().split(".").pop() || ext;
23
- const safeExt = originalExt.replace(/[^a-zA-Z0-9]/g, "") || ext;
20
+ if (!location) throw new Error("fileStorage.mount is not configured");
21
+ const nameStr = file.name.toString();
22
+ const originalExt = nameStr.includes(".") ? nameStr.split(".").pop() : ext;
23
+ const safeExt = (originalExt || ext).replace(/[^a-zA-Z0-9]/g, "") || ext;
24
24
  let filename;
25
25
  if (typeof fileNameOrIdLength === "number") {
26
26
  filename = `${generateRandomId(fileNameOrIdLength)}.${safeExt}`;
27
27
  } else {
28
28
  ensureSafeBasename(fileNameOrIdLength);
29
- filename = `${fileNameOrIdLength}.${safeExt}`;
29
+ const extensionFromFileName = fileNameOrIdLength.split(".").pop();
30
+ if (!fileNameOrIdLength.includes(".")) {
31
+ filename = `${fileNameOrIdLength}.${safeExt}`;
32
+ } else if (extensionFromFileName === safeExt) {
33
+ filename = fileNameOrIdLength;
34
+ } else {
35
+ console.warn(
36
+ `[nuxt-file-storage] The provided filename "${fileNameOrIdLength}" does not have the expected extension ".${safeExt}". The correct extension will be appended.`
37
+ );
38
+ filename = `${fileNameOrIdLength.split(".").slice(0, -1).join(".")}.${safeExt}`;
39
+ }
30
40
  }
31
41
  const normalizedFilelocation = normalizeRelative(filelocation);
32
42
  const dirPath = await resolveAndEnsureInside(location, normalizedFilelocation);
33
- await mkdir(dirPath, { recursive: true });
43
+ try {
44
+ await mkdir(dirPath, { recursive: true });
45
+ } catch (err) {
46
+ if (err?.code === "EEXIST") {
47
+ throw new Error(
48
+ `[nuxt-file-storage] EEXIST: A file already exists at "${dirPath}" where a directory was expected. This typically happens when a file was accidentally created at a path meant for a folder. Please remove or rename the conflicting file.`
49
+ );
50
+ } else if (err?.code === "ENOTDIR") {
51
+ throw new Error(
52
+ `[nuxt-file-storage] ENOTDIR: Cannot create directory "${dirPath}" because a parent path component is a file, not a directory. Check if any part of the path "${normalizedFilelocation}" exists as a file instead of a folder. Please remove or rename the conflicting file.`
53
+ );
54
+ }
55
+ throw err;
56
+ }
34
57
  const targetPath = await resolveAndEnsureInside(location, normalizedFilelocation, filename);
35
58
  await writeFile(targetPath, binaryString, {
36
59
  flag: "w"
@@ -39,8 +62,7 @@ export const storeFileLocally = async (file, fileNameOrIdLength, filelocation =
39
62
  };
40
63
  export const getFileLocally = (filename, filelocation = "") => {
41
64
  const location = getMount();
42
- if (!location)
43
- throw new Error("fileStorage.mount is not configured");
65
+ if (!location) throw new Error("fileStorage.mount is not configured");
44
66
  ensureSafeBasename(filename);
45
67
  const normalizedFilelocation = normalizeRelative(filelocation);
46
68
  const resolved = path.resolve(location, normalizedFilelocation, filename);
@@ -56,16 +78,14 @@ export const getFileLocally = (filename, filelocation = "") => {
56
78
  };
57
79
  export const getFilesLocally = async (filelocation = "") => {
58
80
  const location = getMount();
59
- if (!location)
60
- return [];
81
+ if (!location) return [];
61
82
  const normalizedFilelocation = normalizeRelative(filelocation);
62
83
  const dirPath = await resolveAndEnsureInside(location, normalizedFilelocation);
63
84
  return await readdir(dirPath).catch(() => []);
64
85
  };
65
86
  export const deleteFile = async (filename, filelocation = "") => {
66
87
  const location = getMount();
67
- if (!location)
68
- throw new Error("fileStorage.mount is not configured");
88
+ if (!location) throw new Error("fileStorage.mount is not configured");
69
89
  ensureSafeBasename(filename);
70
90
  const normalizedFilelocation = normalizeRelative(filelocation);
71
91
  const targetPath = await resolveAndEnsureInside(location, normalizedFilelocation, filename);
@@ -99,7 +119,7 @@ export const retrieveFileLocally = async (event, filename, filelocation = "") =>
99
119
  if (!stats.isFile()) {
100
120
  throw createError({ statusCode: 404, statusMessage: "Not Found" });
101
121
  }
102
- } catch (err) {
122
+ } catch {
103
123
  throw createError({ statusCode: 404, statusMessage: "Not Found" });
104
124
  }
105
125
  const ext = path.extname(filePath).slice(1).toLowerCase();
@@ -0,0 +1,23 @@
1
+ export interface ServerFile {
2
+ name: string;
3
+ content: string;
4
+ size: string;
5
+ type: string;
6
+ lastModified: string;
7
+ }
8
+ export interface ClientFile extends Blob {
9
+ content: string | ArrayBuffer | null | undefined;
10
+ name: string;
11
+ lastModified: number;
12
+ }
13
+ export interface ModuleOptions {
14
+ mount: string;
15
+ version: string;
16
+ }
17
+ /**
18
+ * @description Augment the '#imports' module to include useRuntimeConfig
19
+ * this is only needed because this package is consumed as a module
20
+ */
21
+ declare module '#imports' {
22
+ function useRuntimeConfig(): any;
23
+ }
File without changes
package/dist/types.d.mts CHANGED
@@ -1 +1,9 @@
1
- export { type useRuntimeConfig } from './module.js'
1
+ import type { NuxtModule } from '@nuxt/schema'
2
+
3
+ import type { default as Module } from './module.mjs'
4
+
5
+ export type ModuleOptions = typeof Module extends NuxtModule<infer O> ? Partial<O> : Record<string, any>
6
+
7
+ export { default } from './module.mjs'
8
+
9
+ export * from '../dist/runtime/types.js'
package/package.json CHANGED
@@ -1,19 +1,17 @@
1
1
  {
2
2
  "name": "nuxt-file-storage",
3
- "version": "0.3.1",
3
+ "version": "0.3.2",
4
4
  "description": "Easy solution to store files in your nuxt apps. Be able to upload files from the frontend and recieve them from the backend to then save the files in your project.",
5
5
  "repository": "NyllRE/nuxt-file-storage",
6
6
  "license": "MIT",
7
7
  "type": "module",
8
8
  "exports": {
9
9
  ".": {
10
- "types": "./dist/types.d.ts",
11
- "import": "./dist/module.mjs",
12
- "require": "./dist/module.cjs"
10
+ "types": "./dist/types.d.mts",
11
+ "import": "./dist/module.mjs"
13
12
  }
14
13
  },
15
- "main": "./dist/module.cjs",
16
- "types": "./dist/types.d.ts",
14
+ "main": "./dist/module.mjs",
17
15
  "files": [
18
16
  "dist"
19
17
  ],
@@ -24,27 +22,29 @@
24
22
  "dev:prepare": "nuxt-module-build build --stub && nuxt-module-build prepare && nuxi prepare playground",
25
23
  "bump": "npm version patch -m \"chore(release): %s\"",
26
24
  "release": "npm run lint && npm run bump && npm run prepack && changelogen && git push --follow-tags && npm publish",
27
- "lint": "eslint .",
25
+ "lint": "oxlint .",
28
26
  "test": "vitest run",
29
- "test:watch": "vitest watch"
27
+ "test:watch": "vitest watch",
28
+ "changelogen": "changelogen"
30
29
  },
31
30
  "dependencies": {
32
- "@nuxt/kit": "^3.15.4",
31
+ "@nuxt/kit": "^4.3.0",
33
32
  "defu": "^6.1.4"
34
33
  },
35
34
  "devDependencies": {
36
- "@nuxt/devtools": "latest",
37
- "@nuxt/eslint-config": "^0.2.0",
38
- "@nuxt/module-builder": "^0.8.4",
39
- "@nuxt/schema": "^3.15.4",
40
- "@nuxt/test-utils": "^3.21.0",
35
+ "@nuxt/devtools": "^3.1.1",
36
+ "@nuxt/eslint-config": "^1.13.0",
37
+ "@nuxt/module-builder": "^1.0.2",
38
+ "@nuxt/schema": "^4.3.0",
39
+ "@nuxt/test-utils": "^3.23.0",
41
40
  "@types/node": "^20.17.19",
42
41
  "@vitest/ui": "4.0.16",
43
42
  "@vue/test-utils": "^2.4.6",
44
43
  "changelogen": "^0.5.7",
45
44
  "eslint": "^8.57.1",
46
45
  "happy-dom": "^20.0.11",
47
- "nuxt": "^3.15.4",
46
+ "nuxt": "^4.3.0",
47
+ "oxlint": "^1.42.0",
48
48
  "playwright-core": "^1.57.0",
49
49
  "typescript": "^5.9.3",
50
50
  "vitest": "^4.0.16"
package/dist/module.cjs DELETED
@@ -1,5 +0,0 @@
1
- module.exports = function(...args) {
2
- return import('./module.mjs').then(m => m.default.call(this, ...args))
3
- }
4
- const _meta = module.exports.meta = require('./module.json')
5
- module.exports.getMeta = () => Promise.resolve(_meta)
package/dist/module.d.ts DELETED
@@ -1,32 +0,0 @@
1
- import * as _nuxt_schema from '@nuxt/schema';
2
-
3
- interface ServerFile {
4
- name: string
5
- content: string
6
- size: string
7
- type: string
8
- lastModified: string
9
- }
10
-
11
- interface ClientFile extends Blob {
12
- content: string | ArrayBuffer | null | undefined
13
- name: string
14
- lastModified: number
15
- }
16
-
17
- interface ModuleOptions {
18
- mount: string
19
- version: string
20
- }
21
-
22
- /**
23
- * @description Augment the '#imports' module to include useRuntimeConfig
24
- * this is only needed because this package is consumed as a module
25
- */
26
- declare module '#imports' {
27
- export function useRuntimeConfig(): any
28
- }
29
-
30
- declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
31
-
32
- export { type ClientFile, type ModuleOptions, type ServerFile, _default as default };
package/dist/types.d.ts DELETED
@@ -1 +0,0 @@
1
- export { type useRuntimeConfig } from './module'