@devframes/plugin-assets 0.7.15

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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025-PRESENT Anthony Fu <https://github.com/antfu>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,13 @@
1
+ # `@devframes/plugin-assets`
2
+
3
+ Browse, preview, upload, rename, and delete the files in a directory — a framework-neutral port of Nuxt DevTools' Assets tab, built as a **Vue** SPA on `@antfu/design`.
4
+
5
+ ```sh
6
+ pnpx @devframes/plugin-assets
7
+ ```
8
+
9
+ Manages `<cwd>/public` by default. The package exports `createAssetsDevframe()` for custom definitions and `assetsVitePlugin()` from `@devframes/plugin-assets/vite` for Vite hosts. Pass `{ dir }` to manage a different directory, `{ baseURL }` to match where the host serves it, or `{ write: false }` (`--read-only` on the standalone CLI) for a browse-only deployment.
10
+
11
+ Asset previews load files by their public URL, served by the host the plugin is mounted into (Vite/Nuxt/etc. already serve `public/` at `/`) — the plugin doesn't serve the bytes itself. The standalone CLI is its own host, so it serves the directory for you.
12
+
13
+ The standalone server requires devframe's trust handshake by default because it can read, write, and delete real files. The `build` CLI subcommand is disabled by default — see the docs page for why.
package/bin.mjs ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+ import process from 'node:process'
3
+ import { createAssetsCli } from './dist/cli.mjs'
4
+
5
+ async function main() {
6
+ await createAssetsCli().parse()
7
+ }
8
+
9
+ main().catch((error) => {
10
+ console.error(error)
11
+ process.exit(1)
12
+ })
package/dist/cli.d.mts ADDED
@@ -0,0 +1,5 @@
1
+ import { CacHandle } from "devframe/adapters/cac";
2
+ //#region src/cli.d.ts
3
+ declare function createAssetsCli(): CacHandle;
4
+ //#endregion
5
+ export { createAssetsCli };
package/dist/cli.mjs ADDED
@@ -0,0 +1,8 @@
1
+ import { n as createAssetsDevframe } from "./src-DuJ-iEes.mjs";
2
+ import { createCac } from "devframe/adapters/cac";
3
+ //#region src/cli.ts
4
+ function createAssetsCli() {
5
+ return createCac(createAssetsDevframe({ serveStatic: true }));
6
+ }
7
+ //#endregion
8
+ export { createAssetsCli };
@@ -0,0 +1,6 @@
1
+ import { i as CodeSnippet, n as AssetInfo, r as AssetType, t as AssetImageMeta } from "../types-DPDOYVFh.mjs";
2
+ import { DevframeRpcClientOptions } from "devframe/client";
3
+ //#region src/client/index.d.ts
4
+ declare function connectAssets(options?: DevframeRpcClientOptions): Promise<import("devframe/client").DevframeRpcClient>;
5
+ //#endregion
6
+ export { type AssetImageMeta, type AssetInfo, type AssetType, type CodeSnippet, connectAssets };
@@ -0,0 +1,7 @@
1
+ import { connectDevframe } from "devframe/client";
2
+ //#region src/client/index.ts
3
+ function connectAssets(options) {
4
+ return connectDevframe(options);
5
+ }
6
+ //#endregion
7
+ export { connectAssets };
@@ -0,0 +1,80 @@
1
+ import { a as DEFAULT_ALLOWED_UPLOAD_EXTENSIONS, i as CodeSnippet, n as AssetInfo, r as AssetType, t as AssetImageMeta } from "./types-DPDOYVFh.mjs";
2
+ import { DevframeDefinition } from "devframe/types";
3
+ //#region src/index.d.ts
4
+ interface AssetsDevframeOptions {
5
+ id?: string;
6
+ name?: string;
7
+ icon?: string;
8
+ /**
9
+ * Directory this devframe manages. Defaults to `<cwd>/public` — the
10
+ * conventional static-asset folder for Nuxt, Vite, and most static-site
11
+ * frameworks. Pass an absolute path, or one relative to the devframe's
12
+ * own `cwd`.
13
+ */
14
+ dir?: string;
15
+ /**
16
+ * URL base the **host** serves the managed directory at, used to build
17
+ * each asset's `publicPath`. Defaults to `/` — Vite, Nuxt, and most
18
+ * frameworks serve their `public/` folder at the site root. Set this to
19
+ * match a non-root deployment base (e.g. Nuxt's `app.baseURL`).
20
+ */
21
+ baseURL?: string;
22
+ /**
23
+ * Serve the managed directory's bytes from this devframe itself.
24
+ * Defaults to `false`: when mounted into a host (Vite/Nuxt/…) that host
25
+ * already serves `public/` at {@link baseURL}, so the plugin references
26
+ * those URLs instead of standing up its own route. The standalone CLI
27
+ * sets this to `true` (it is its own host), serving the directory under
28
+ * a dedicated base so it doesn't collide with the SPA at `/`.
29
+ */
30
+ serveStatic?: boolean;
31
+ basePath?: string;
32
+ distDir?: string;
33
+ /** Preferred standalone CLI port (default 9015). */
34
+ port?: number;
35
+ /**
36
+ * Enable upload, rename, delete, and folder creation from the UI.
37
+ * Enabled by default — set `false` (or pass `--read-only` on the
38
+ * standalone CLI) for a browse-only deployment.
39
+ */
40
+ write?: boolean;
41
+ /**
42
+ * Extensions `upload` accepts, or `'*'` to accept any file. Defaults to
43
+ * the same allow-list Nuxt DevTools ships.
44
+ */
45
+ uploadExtensions?: readonly string[] | '*';
46
+ /**
47
+ * Watch the managed directory in dev mode and push live listing updates.
48
+ * Enabled by default. Set `false` to skip the file watcher — useful when a
49
+ * host mounts the devframe in a short-lived or repeatedly-recreated
50
+ * context (tests, some SSR dev servers) where a background watcher isn't
51
+ * wanted.
52
+ */
53
+ watch?: boolean;
54
+ /**
55
+ * Require the trust handshake on the standalone server. Enabled by
56
+ * default — this devframe can read, write, and delete real files.
57
+ */
58
+ auth?: boolean;
59
+ /**
60
+ * Register the `build` CLI subcommand. Disabled by default: a static
61
+ * export can only ever list file metadata from a baked snapshot — there
62
+ * is no live host serving the files, and every write action is inherently
63
+ * excluded from a static dump — so the command would produce a broken,
64
+ * write-less shell of the tool. Opt back in if that degraded export is
65
+ * still useful to you.
66
+ */
67
+ build?: boolean;
68
+ }
69
+ /**
70
+ * Create the assets manager devframe — a framework-neutral port of Nuxt
71
+ * DevTools' Assets tab. Mount it into any host via devframe's adapters, or
72
+ * run it standalone with the bundled CLI (`devframe-assets`).
73
+ *
74
+ * @experimental This plugin is experimental and may change without a major
75
+ * version bump until it stabilizes.
76
+ */
77
+ declare function createAssetsDevframe(options?: AssetsDevframeOptions): DevframeDefinition;
78
+ declare const assetsDevframe: DevframeDefinition;
79
+ //#endregion
80
+ export { type AssetImageMeta, type AssetInfo, type AssetType, AssetsDevframeOptions, type CodeSnippet, DEFAULT_ALLOWED_UPLOAD_EXTENSIONS, createAssetsDevframe, assetsDevframe as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import { n as createAssetsDevframe, r as DEFAULT_ALLOWED_UPLOAD_EXTENSIONS, t as assetsDevframe } from "./src-DuJ-iEes.mjs";
2
+ export { DEFAULT_ALLOWED_UPLOAD_EXTENSIONS, createAssetsDevframe, assetsDevframe as default };
@@ -0,0 +1,96 @@
1
+ import { n as AssetInfo, r as AssetType } from "../types-DPDOYVFh.mjs";
2
+ import { DevframeNodeContext, RpcStreamingChannel } from "devframe/types";
3
+ //#region src/node/context.d.ts
4
+ interface AssetsConfig {
5
+ /** Directory this devframe manages. */
6
+ dir: string;
7
+ /** Whether upload / rename / delete / mkdir are registered. */
8
+ write: boolean;
9
+ /** Extensions `upload` accepts, or `'*'` to accept any. */
10
+ uploadExtensions: readonly string[] | '*';
11
+ /**
12
+ * URL base the managed directory is served at. Normally the host
13
+ * (Vite / Nuxt / …) serves `public/` at `/`, so asset `publicPath`s are
14
+ * `joinURL(baseURL, path)` and the plugin serves nothing itself.
15
+ */
16
+ baseURL: string;
17
+ /** The upload streaming channel, created once in `setupAssets` when `write` is enabled. */
18
+ uploadChannel?: RpcStreamingChannel<Uint8Array>;
19
+ }
20
+ interface AssetsContext extends AssetsConfig {
21
+ /** Resolve a root-relative path to an absolute one, rejecting escapes. */
22
+ resolvePath: (relativePath: string) => string;
23
+ }
24
+ /**
25
+ * Record the managed directory and write policy for a context. Called from
26
+ * the devframe's `setup` before any RPC handler runs.
27
+ */
28
+ declare function configureAssets(ctx: DevframeNodeContext, config: AssetsConfig): void;
29
+ /**
30
+ * Per-`DevframeNodeContext` assets state. Each RPC function pulls its
31
+ * managed directory and path resolver from here instead of re-reading
32
+ * `configureAssets`'s config on every call.
33
+ */
34
+ declare function getAssetsContext(ctx: DevframeNodeContext): AssetsContext;
35
+ //#endregion
36
+ //#region src/node/paths.d.ts
37
+ /**
38
+ * Resolve a client-supplied, root-relative path against the managed
39
+ * directory, rejecting anything that would escape it (`..` traversal, a
40
+ * rogue absolute path, etc.). Every RPC handler that touches the
41
+ * filesystem goes through this — never trust a path from the wire.
42
+ */
43
+ declare function resolveAssetPath(root: string, relativePath: string): string;
44
+ //#endregion
45
+ //#region src/node/scanner.d.ts
46
+ /** Classifies a path by extension, driving preview and icon choice. */
47
+ declare function guessAssetType(path: string): AssetType;
48
+ /** Recursively lists every file under `dir`, sorted alphabetically by path. */
49
+ declare function scanAssets(dir: string, baseURL: string): Promise<AssetInfo[]>;
50
+ //#endregion
51
+ //#region src/node/index.d.ts
52
+ interface SetupAssetsOptions {
53
+ /** Directory this devframe manages. */
54
+ dir: string;
55
+ /** Whether upload / rename / delete / mkdir are registered. */
56
+ write: boolean;
57
+ /** Extensions `upload` accepts, or `'*'` to accept any. */
58
+ uploadExtensions: readonly string[] | '*';
59
+ /**
60
+ * URL base the managed directory is served at — the base every asset's
61
+ * `publicPath` is resolved against.
62
+ */
63
+ baseURL: string;
64
+ /**
65
+ * Whether this devframe should serve the managed directory's bytes
66
+ * itself. Left `false` when mounted into a host (Vite / Nuxt / …) that
67
+ * already serves `public/` at {@link baseURL}; set `true` only for the
68
+ * standalone CLI, which is its own host.
69
+ */
70
+ serveStatic: boolean;
71
+ /** Start the live file watcher (dev mode only). */
72
+ watch: boolean;
73
+ }
74
+ /**
75
+ * Register the assets RPC surface on a devframe node context: ensures the
76
+ * managed directory exists, registers the read RPCs (always) and write
77
+ * RPCs (when enabled), and starts a live file watcher in dev mode.
78
+ *
79
+ * Raw asset bytes (for `<img>`/`<video>`/download previews) are served by
80
+ * the **host** the plugin is attached to — Vite, Nuxt, or any framework
81
+ * already serves its `public/` dir at {@link SetupAssetsOptions.baseURL}.
82
+ * The plugin only serves them itself when `serveStatic` is set (the
83
+ * standalone CLI, which is its own host).
84
+ *
85
+ * Called from the definition's `setup(ctx)` and reusable by host adapters
86
+ * that wire their own context.
87
+ */
88
+ declare function setupAssets(ctx: DevframeNodeContext, options: SetupAssetsOptions): Promise<void>;
89
+ /**
90
+ * Stop the live file watcher started by {@link setupAssets}. Test harnesses
91
+ * wrap their dev-server `close()` with this — see `test/_utils.ts` — so a
92
+ * leaked chokidar watcher doesn't keep the process alive.
93
+ */
94
+ declare function disposeAssetsWatcher(ctx: DevframeNodeContext): Promise<void>;
95
+ //#endregion
96
+ export { SetupAssetsOptions, configureAssets, disposeAssetsWatcher, getAssetsContext, guessAssetType, resolveAssetPath, scanAssets, setupAssets };
@@ -0,0 +1,3 @@
1
+ import { n as setupAssets, t as disposeAssetsWatcher } from "../node-qqFgpP7-.mjs";
2
+ import { b as getAssetsContext, m as scanAssets, p as guessAssetType, x as resolveAssetPath, y as configureAssets } from "../rpc-lfFykVQ_.mjs";
3
+ export { configureAssets, disposeAssetsWatcher, getAssetsContext, guessAssetType, resolveAssetPath, scanAssets, setupAssets };
@@ -0,0 +1,75 @@
1
+ import { _ as UPLOAD_CHANNEL, i as writeFunctions, n as readFunctions, t as alwaysFunctions, y as configureAssets } from "./rpc-lfFykVQ_.mjs";
2
+ import { existsSync } from "node:fs";
3
+ import fsp from "node:fs/promises";
4
+ import { watch } from "chokidar";
5
+ import { debounce } from "perfect-debounce";
6
+ //#region src/constants.ts
7
+ /** Broadcast whenever a file is added, removed, or changed under the managed directory. */
8
+ const CHANGED_EVENT = "devframes:plugin:assets:changed";
9
+ /** Preferred standalone CLI port. */
10
+ const DEFAULT_PORT = 9015;
11
+ //#endregion
12
+ //#region src/node/watcher.ts
13
+ /**
14
+ * Watch the managed directory and broadcast {@link CHANGED_EVENT} (debounced)
15
+ * whenever a file is added, removed, or changed, so connected UIs refresh
16
+ * their listing live. Returns a disposer; call it when the devframe shuts
17
+ * down (tests in particular — a leaked watcher keeps the process alive).
18
+ */
19
+ function watchAssetsDir(ctx, dir) {
20
+ const notify = debounce(async () => {
21
+ await ctx.rpc.broadcast({
22
+ method: CHANGED_EVENT,
23
+ args: []
24
+ });
25
+ }, 300);
26
+ const watcher = watch(dir, {
27
+ ignoreInitial: true,
28
+ depth: 32
29
+ });
30
+ watcher.on("add", () => void notify()).on("unlink", () => void notify()).on("addDir", () => void notify()).on("unlinkDir", () => void notify()).on("change", () => void notify());
31
+ return () => watcher.close();
32
+ }
33
+ //#endregion
34
+ //#region src/node/index.ts
35
+ const watchers = /* @__PURE__ */ new WeakMap();
36
+ /**
37
+ * Register the assets RPC surface on a devframe node context: ensures the
38
+ * managed directory exists, registers the read RPCs (always) and write
39
+ * RPCs (when enabled), and starts a live file watcher in dev mode.
40
+ *
41
+ * Raw asset bytes (for `<img>`/`<video>`/download previews) are served by
42
+ * the **host** the plugin is attached to — Vite, Nuxt, or any framework
43
+ * already serves its `public/` dir at {@link SetupAssetsOptions.baseURL}.
44
+ * The plugin only serves them itself when `serveStatic` is set (the
45
+ * standalone CLI, which is its own host).
46
+ *
47
+ * Called from the definition's `setup(ctx)` and reusable by host adapters
48
+ * that wire their own context.
49
+ */
50
+ async function setupAssets(ctx, options) {
51
+ if (!existsSync(options.dir)) await fsp.mkdir(options.dir, { recursive: true });
52
+ const uploadChannel = options.write ? ctx.rpc.streaming.create(UPLOAD_CHANNEL) : void 0;
53
+ configureAssets(ctx, {
54
+ ...options,
55
+ uploadChannel
56
+ });
57
+ if (options.serveStatic) ctx.views.hostStatic(options.baseURL, options.dir);
58
+ for (const fn of readFunctions) ctx.rpc.register(fn);
59
+ for (const fn of alwaysFunctions) ctx.rpc.register(fn);
60
+ if (options.write) for (const fn of writeFunctions) ctx.rpc.register(fn);
61
+ if (ctx.mode === "dev" && options.watch) watchers.set(ctx, watchAssetsDir(ctx, options.dir));
62
+ }
63
+ /**
64
+ * Stop the live file watcher started by {@link setupAssets}. Test harnesses
65
+ * wrap their dev-server `close()` with this — see `test/_utils.ts` — so a
66
+ * leaked chokidar watcher doesn't keep the process alive.
67
+ */
68
+ async function disposeAssetsWatcher(ctx) {
69
+ const dispose = watchers.get(ctx);
70
+ if (!dispose) return;
71
+ watchers.delete(ctx);
72
+ await dispose();
73
+ }
74
+ //#endregion
75
+ export { setupAssets as n, DEFAULT_PORT as r, disposeAssetsWatcher as t };