@asterflow/fs 1.0.9 → 2.0.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/README.md CHANGED
@@ -2,120 +2,70 @@
2
2
 
3
3
  # @asterflow/fs
4
4
 
5
- ![license-info](https://img.shields.io/github/license/AsterFlow/plugins?style=for-the-badge&colorA=302D41&colorB=f9e2af&logoColor=f9e2af)
6
- ![stars-info](https://img.shields.io/github/stars/AsterFlow/plugins?colorA=302D41&colorB=f9e2af&style=for-the-badge)
5
+ ![license-info](https://img.shields.io/github/license/AsterFlow/AsterFlow?style=for-the-badge&colorA=302D41&colorB=f9e2af&logoColor=f9e2af)
6
+ ![stars-info](https://img.shields.io/github/stars/AsterFlow/AsterFlow?colorA=302D41&colorB=f9e2af&style=for-the-badge)
7
+ ![last-commit](https://img.shields.io/github/last-commit/AsterFlow/AsterFlow?path=plugins%2Ffs&style=for-the-badge&colorA=302D41&colorB=b4befe)
8
+
7
9
  ![bundle-size](https://img.shields.io/bundlejs/size/@asterflow/fs?style=for-the-badge&colorA=302D41&colorB=3ac97b)
8
10
 
9
11
  </div>
10
12
 
11
- > Roteamento baseado em convenções de sistema de arquivos para o AsterFlow.
13
+ > Generates a static route manifest from a file-based `routes/` directory and registers it on an AsterFlow app as a plugin.
12
14
 
13
15
  ## 📦 Installation
14
16
 
15
17
  ```bash
16
- # You can use any package manager
17
- npm install @asterflow/fs
18
- ```
19
-
20
- ## 💡 About
21
-
22
- `@asterflow/fs` brings the convenience of file system-based routing to your AsterFlow projects. Inspired by modern web frameworks, this plugin automatically discovers and registers your routes based on the file and directory structure, allowing you to focus on writing your API logic instead of manual route configuration.
23
-
24
- ## ✨ Features
25
-
26
- - **Convention over Configuration:** Automatically generates API routes from your file structure.
27
- - **Dynamic Parameters:** Support for dynamic segments in filenames (e.g., `$id.ts`).
28
- - **Index Routes:** `index.ts` files are treated as the base route of a directory.
29
- - **Type-Safe:** Fully integrated with AsterFlow's type system.
30
- - **Seamless Integration:** Automatically registers all discovered routes before the server starts.
31
-
32
- ## 🚀 Usage
33
-
34
- ### 1\. Project Structure
35
-
36
- Create a directory to store your route files. By convention, this directory is usually `routes/` or `src/routes/`.
37
-
18
+ bun install @asterflow/fs
38
19
  ```
39
- .
40
- ├── routes/
41
- │ ├── index.ts # Handles GET /
42
- │ ├── users/
43
- │ │ ├── index.ts # Handles GET /users
44
- │ │ └── $id.ts # Handles GET /users/:id
45
- ├── src
46
- │ └── index.ts # Your main application file
47
- └── package.json
48
- ```
49
-
50
- ### 2\. Define Your Routes
51
20
 
52
- Each route file must have a `default export` of an AsterFlow `Method` or `Router`.
21
+ Register the plugin with a generated route manifest:
53
22
 
54
- **`src/routes/users/$id.ts`**
23
+ ```ts
24
+ import { AsterFlow } from 'asterflow'
25
+ import { fsRoutingPlugin } from '@asterflow/fs'
26
+ import routes from './routes.gen'
55
27
 
56
- ```typescript
57
- import { Method } from '@asterflow/router';
58
-
59
- export default new Method({
60
- // The 'path' property will be overwritten by the plugin,
61
- // but can be useful for isolated testing.
62
- path: '/',
63
- method: 'get',
64
- handler({ response, url }) {
65
- // 'id' will be available at runtime
66
- const params = url.getParams();
67
- return response.success({ user: { id: params.id } });
68
- }
69
- });
28
+ const app = new AsterFlow()
29
+ .use(fsRoutingPlugin, { routes })
70
30
  ```
71
31
 
72
- ### 3\. Register the Plugin
32
+ ### Features
73
33
 
74
- In your main application file, import and register the `fsRouting` plugin.
34
+ - **Static manifest, not runtime scanning**: `generateRouteManifest` walks a routes directory once and writes a file with one literal `import` per route plus a default-exported array - a bundler can follow these imports, unlike the old `await import(dynamicPath)` approach.
35
+ - **File-to-URL conventions**: `index.ts` becomes `/`, `users/index.ts` becomes `/users`, and a `$`-prefixed segment like `$id.ts` becomes a `:id` param.
36
+ - **Auto path assignment**: in the generated manifest, any route whose `default export` has no explicit `path` gets one assigned from its file location.
37
+ - **Safe registration**: `fsRoutingPlugin` registers every entry in `routes` with `instance.controller(route)` on `beforeInitialize`, and skips (with a warning) any entry that isn't a `Method`/`Router` instance instead of throwing.
75
38
 
76
- **`src/index.ts`**
39
+ ## ❓ How to Use
77
40
 
78
- ```typescript
79
- import { AsterFlow } from 'asterflow';
80
- import { fsRouting } from '@asterflow/fs';
81
- import { join } from 'path';
41
+ Generate the manifest ahead of time, usually via the `asterflow generate` CLI command, or by calling the generator directly from a build script:
82
42
 
83
- // Register the plugin and point it to your routes directory
84
- export const app = new AsterFlow();
85
- .use(fsRouting, {
86
- path: join(process.cwd(), 'src', 'routes')
87
- });
43
+ ```ts
44
+ import { generateRouteManifest } from '@asterflow/fs'
88
45
 
89
- // Start the server
90
- app.listen({ port: 3333 }, () => {
91
- console.log('Server running with file system routing!');
92
- });
46
+ await generateRouteManifest({
47
+ routesDir: './src/routes',
48
+ outFile: './src/routes.gen.ts'
49
+ })
93
50
  ```
94
51
 
95
- That's it\! The plugin will scan the `src/routes` directory and register all valid route files when the application starts.
96
-
97
- ## 🗺️ Routing Conventions
52
+ Route files just export a `Method` or `Router` - the `path` is filled in by the generator from the file's location, so `src/routes/users/$id.ts` becomes `/users/:id`:
98
53
 
99
- The plugin transforms file paths into URL routes based on the following rules:
54
+ ```ts
55
+ // src/routes/users/$id.ts
56
+ import { Method } from '@asterflow/router'
100
57
 
101
- | File Path | Generated Route |
102
- | ----------------- | ------------------ |
103
- | `index.ts` | `/` |
104
- | `users.ts` | `/users` |
105
- | `users/index.ts` | `/users` |
106
- | `$id.ts` | `/:id` |
107
- | `products/$id.ts` | `/products/:id` |
108
- | `categories/$categoryId/products/$productId.ts` | `/categories/:categoryId/products/:productId` |
109
-
110
- - Files named `index` become the root of their directory.
111
- - Filenames prefixed with `$` (e.g., `$id.ts`) are converted to dynamic URL parameters (e.g., `/:id`).
58
+ export default new Method(Method.GET, {
59
+ handler: ({ url, response }) => response.success({ id: url.getParams().id })
60
+ })
61
+ ```
112
62
 
113
63
  ## 🔗 Related Packages
114
64
 
115
- - [asterflow](https://www.npmjs.com/package/asterflow) - The core of the AsterFlow framework.
116
- - [@asterflow/plugin](https://www.npmjs.com/package/@asterflow/plugin) - The main plugin system.
117
- - [@asterflow/router](https://www.npmjs.com/package/@asterflow/router) - The type-safe routing system used by this plugin.
65
+ - [@asterflow/plugin](https://www.npmjs.com/package/@asterflow/plugin) - builds `fsRoutingPlugin` via `Plugin.create()`.
66
+ - [@asterflow/router](https://www.npmjs.com/package/@asterflow/router) - provides the `Method`/`Router` classes that route files export and that the plugin registers.
67
+ - Depended on by `@asterflow/cli` - its `generate` command calls this package's `generateRouteManifest` to produce the route manifest.
118
68
 
119
69
  ## 📄 License
120
70
 
121
- MIT - See the main project [LICENSE](https://github.com/AsterFlow/AsterFlow/blob/main/LICENSE) for more details.
71
+ This project is licensed under the [MIT License](../../LICENSE).
@@ -1,52 +1,31 @@
1
1
  "use strict";
2
- var p = Object.defineProperty;
3
- var h = Object.getOwnPropertyDescriptor;
4
- var w = Object.getOwnPropertyNames;
5
- var R = Object.prototype.hasOwnProperty;
6
- var v = (t, o) => {
7
- for (var r in o)
8
- p(t, r, { get: o[r], enumerable: !0 });
9
- }, F = (t, o, r, e) => {
10
- if (o && typeof o == "object" || typeof o == "function")
11
- for (let s of w(o))
12
- !R.call(t, s) && s !== r && p(t, s, { get: () => o[s], enumerable: !(e = h(o, s)) || e.enumerable });
13
- return t;
2
+ var f = Object.defineProperty;
3
+ var T = Object.getOwnPropertyDescriptor;
4
+ var P = Object.getOwnPropertyNames;
5
+ var S = Object.prototype.hasOwnProperty;
6
+ var O = (e, r) => {
7
+ for (var s in r)
8
+ f(e, s, { get: r[s], enumerable: !0 });
9
+ }, I = (e, r, s, t) => {
10
+ if (r && typeof r == "object" || typeof r == "function")
11
+ for (let o of P(r))
12
+ !S.call(e, o) && o !== s && f(e, o, { get: () => r[o], enumerable: !(t = T(r, o)) || t.enumerable });
13
+ return e;
14
14
  };
15
- var j = (t) => F(p({}, "__esModule", { value: !0 }), t);
16
-
15
+ var M = (e) => I(f({}, "__esModule", { value: !0 }), e);
17
16
  // plugins/fs/src/index.ts
18
- var P = {};
19
- v(P, {
20
- default: () => T,
21
- fsRoutingPlugin: () => y,
22
- getFilesRecursively: () => n,
23
- transformPathToUrl: () => u
17
+ var _ = {};
18
+ O(_, {
19
+ default: () => D,
20
+ fsRoutingPlugin: () => F,
21
+ generateRouteManifest: () => U,
22
+ getFilesRecursively: () => c,
23
+ transformPathToUrl: () => m
24
24
  });
25
- module.exports = j(P);
26
- var x = require("@asterflow/plugin"), a = require("@asterflow/router");
27
-
25
+ module.exports = M(_);
26
+ var $ = require("@asterflow/plugin"), u = require("@asterflow/router");
28
27
  // plugins/fs/package.json
29
- var f = "1.0.9";
30
-
31
- // plugins/fs/src/utils/format.ts
32
- var d = require("path");
33
- function u(t, o) {
34
- let e = (0, d.relative)(o, t).replace(/\.(ts|js)$/, "");
35
- return e.endsWith("/index") ? e = e.slice(0, -6) : e === "index" && (e = ""), e = e.replace(/\$/g, ":"), `/${e}`;
36
- }
37
-
38
- // plugins/fs/src/utils/glob.ts
39
- var g = require("fs/promises"), m = require("path");
40
- async function n(t) {
41
- let o = await (0, g.readdir)(t, { withFileTypes: !0 });
42
- return (await Promise.all(
43
- o.map(async (e) => {
44
- let s = (0, m.join)(t, e.name);
45
- return e.isDirectory() ? n(s) : s;
46
- })
47
- )).flat();
48
- }
49
-
28
+ var y = "2.0.0";
50
29
  // plugins/fs/src/utils/log.ts
51
30
  var l = {
52
31
  reset: "\x1B[0m",
@@ -54,42 +33,75 @@ var l = {
54
33
  yellow: "\x1B[33m",
55
34
  cyan: "\x1B[36m"
56
35
  };
57
- function c(t, o) {
58
- console.warn(`${l.yellow}%s %s${l.reset}`, "[AsterFlow]", t), console.group();
59
- for (let [r, e] of Object.entries(o))
60
- console.log(`${l.cyan}%s:${l.reset} %s`, r, e);
36
+ function w(e, r) {
37
+ console.warn(`${l.yellow}%s %s${l.reset}`, "[AsterFlow]", e), console.group();
38
+ for (let [s, t] of Object.entries(r))
39
+ console.log(`${l.cyan}%s:${l.reset} %s`, s, t);
61
40
  console.groupEnd(), console.log();
62
41
  }
63
-
42
+ // plugins/fs/src/utils/codegen.ts
43
+ var p = require("fs/promises"), i = require("path");
44
+ // plugins/fs/src/utils/format.ts
45
+ var h = require("path");
46
+ function m(e, r) {
47
+ let t = (0, h.relative)(r, e).replace(/\.(ts|js)$/, "");
48
+ return t.endsWith("/index") ? t = t.slice(0, -6) : t === "index" && (t = ""), t = t.replace(/\$/g, ":"), `/${t}`;
49
+ }
50
+ // plugins/fs/src/utils/glob.ts
51
+ var R = require("fs/promises"), j = require("path");
52
+ async function c(e) {
53
+ let r = await (0, R.readdir)(e, { withFileTypes: !0 });
54
+ return (await Promise.all(
55
+ r.map(async (t) => {
56
+ let o = (0, j.join)(e, t.name);
57
+ return t.isDirectory() ? c(o) : o;
58
+ })
59
+ )).flat();
60
+ }
61
+ // plugins/fs/src/utils/codegen.ts
62
+ var N = new Set([".ts", ".tsx", ".js", ".jsx"]);
63
+ async function U({ routesDir: e, outFile: r }) {
64
+ let t = (await c(e)).filter((n) => N.has((0, i.extname)(n)) && !n.endsWith(".d.ts")).sort(), o = (0, i.dirname)(r), g = [], d = [], x = [];
65
+ t.forEach((n, v) => {
66
+ let a = `Route${v}`, E = A((0, i.relative)(o, n)), k = m(n, e);
67
+ g.push(`import ${a} from '${E}'`), d.push(`if (!${a}.path) ${a}.path = ${JSON.stringify(k)}`), x.push(a);
68
+ });
69
+ let b = [
70
+ "
71
+ `
72
+ ...g,
73
+ "",
74
+ ...d,
75
+ "",
76
+ `export default [${x.join(", ")}]`,
77
+ ""
78
+ ].join(`
79
+ `);
80
+ await (0, p.mkdir)(o, { recursive: !0 }), await (0, p.writeFile)(r, b);
81
+ }
82
+ function A(e) {
83
+ let s = e.replace(/\.(ts|tsx|js|jsx)$/, "").split(i.sep).join("/");
84
+ return s.startsWith(".") ? s : `./${s}`;
85
+ }
64
86
  // plugins/fs/src/index.ts
65
- var y = x.Plugin.create({ name: "fs-routing" }).decorate("creator", "Ashu11-A").decorate("version", f).config({ path: "" }).derive("files", async (t) => await n(t.path)).extends((t, o) => ({
66
- async registerRoutes(r) {
67
- if (r.files.length !== 0)
68
- for (let e of r.files) {
69
- let i = (await import(e)).default;
70
- if (!i) {
71
- c("File Skipped: No Default Export", {
72
- File: e,
73
- Reason: "The file does not contain any default exported routes (export default).",
74
- Solution: 'Ensure the file has a "export default new Router()" or similar.'
75
- });
76
- continue;
77
- }
78
- if (!(i instanceof a.Method || i instanceof a.Router)) {
79
- c("File Skipped: Invalid Export Type", {
80
- File: e,
81
- "Exported Type": typeof i,
82
- Reason: "The file does not export a known class instance (e.g., Router, Method)."
83
- });
84
- continue;
85
- }
86
- i.path || (i.path = u(e, r.path)), t.controller(i);
87
+ var F = $.Plugin.create({ name: "fs-routing" }).decorate("creator", "Ashu11-A").decorate("version", y).config({ routes: [] }).extends((e, r) => ({
88
+ registerRoutes(s) {
89
+ for (let t of s.routes) {
90
+ if (!(t instanceof u.Method || t instanceof u.Router)) {
91
+ w("Route Skipped: Invalid Entry", {
92
+ "Exported Type": typeof t,
93
+ Reason: "An entry in the generated route manifest is not a Router/Method instance.",
94
+ Solution: "Re-run `asterflow generate` - if the problem persists, check the route file's default export."
95
+ });
96
+ continue;
87
97
  }
98
+ e.controller(t);
99
+ }
88
100
  }
89
- })).on("beforeInitialize", async (t, o) => await t.registerRoutes(o)), T = y;
90
- // Annotate the CommonJS export names for ESM import in node:
101
+ })).on("beforeInitialize", (e, r) => e.registerRoutes(r)), D = F;
91
102
  0 && (module.exports = {
92
103
  fsRoutingPlugin,
104
+ generateRouteManifest,
93
105
  getFilesRecursively,
94
106
  transformPathToUrl
95
107
  });
package/dist/mjs/index.js CHANGED
@@ -1,73 +1,87 @@
1
1
  // plugins/fs/src/index.ts
2
- import { Plugin as x } from "@asterflow/plugin";
3
- import { Method as y, Router as h } from "@asterflow/router";
4
-
2
+ import { Plugin as O } from "@asterflow/plugin";
3
+ import { Method as I, Router as M } from "@asterflow/router";
5
4
  // plugins/fs/package.json
6
- var p = "1.0.9";
7
-
5
+ var f = "2.0.0";
6
+ // plugins/fs/src/utils/log.ts
7
+ var a = {
8
+ reset: "\x1B[0m",
9
+ red: "\x1B[31m",
10
+ yellow: "\x1B[33m",
11
+ cyan: "\x1B[36m"
12
+ };
13
+ function m(t, r) {
14
+ console.warn(`${a.yellow}%s %s${a.reset}`, "[AsterFlow]", t), console.group();
15
+ for (let [s, e] of Object.entries(r))
16
+ console.log(`${a.cyan}%s:${a.reset} %s`, s, e);
17
+ console.groupEnd(), console.log();
18
+ }
19
+ // plugins/fs/src/utils/codegen.ts
20
+ import { mkdir as v, writeFile as E } from "fs/promises";
21
+ import { dirname as k, extname as T, relative as d, sep as P } from "path";
8
22
  // plugins/fs/src/utils/format.ts
9
- import { relative as d } from "path";
10
- function u(t, o) {
11
- let e = d(o, t).replace(/\.(ts|js)$/, "");
23
+ import { relative as $ } from "path";
24
+ function g(t, r) {
25
+ let e = $(r, t).replace(/\.(ts|js)$/, "");
12
26
  return e.endsWith("/index") ? e = e.slice(0, -6) : e === "index" && (e = ""), e = e.replace(/\$/g, ":"), `/${e}`;
13
27
  }
14
-
15
28
  // plugins/fs/src/utils/glob.ts
16
- import { readdir as g } from "fs/promises";
17
- import { join as m } from "path";
29
+ import { readdir as F } from "fs/promises";
30
+ import { join as b } from "path";
18
31
  async function l(t) {
19
- let o = await g(t, { withFileTypes: !0 });
32
+ let r = await F(t, { withFileTypes: !0 });
20
33
  return (await Promise.all(
21
- o.map(async (e) => {
22
- let n = m(t, e.name);
23
- return e.isDirectory() ? l(n) : n;
34
+ r.map(async (e) => {
35
+ let o = b(t, e.name);
36
+ return e.isDirectory() ? l(o) : o;
24
37
  })
25
38
  )).flat();
26
39
  }
27
-
28
- // plugins/fs/src/utils/log.ts
29
- var i = {
30
- reset: "\x1B[0m",
31
- red: "\x1B[31m",
32
- yellow: "\x1B[33m",
33
- cyan: "\x1B[36m"
34
- };
35
- function a(t, o) {
36
- console.warn(`${i.yellow}%s %s${i.reset}`, "[AsterFlow]", t), console.group();
37
- for (let [r, e] of Object.entries(o))
38
- console.log(`${i.cyan}%s:${i.reset} %s`, r, e);
39
- console.groupEnd(), console.log();
40
+ // plugins/fs/src/utils/codegen.ts
41
+ var S = new Set([".ts", ".tsx", ".js", ".jsx"]);
42
+ async function B({ routesDir: t, outFile: r }) {
43
+ let e = (await l(t)).filter((i) => S.has(T(i)) && !i.endsWith(".d.ts")).sort(), o = k(r), c = [], p = [], u = [];
44
+ e.forEach((i, w) => {
45
+ let n = `Route${w}`, h = x(d(o, i)), R = g(i, t);
46
+ c.push(`import ${n} from '${h}'`), p.push(`if (!${n}.path) ${n}.path = ${JSON.stringify(R)}`), u.push(n);
47
+ });
48
+ let y = [
49
+ "
50
+ `
51
+ ...c,
52
+ "",
53
+ ...p,
54
+ "",
55
+ `export default [${u.join(", ")}]`,
56
+ ""
57
+ ].join(`
58
+ `);
59
+ await v(o, { recursive: !0 }), await E(r, y);
60
+ }
61
+ function x(t) {
62
+ let s = t.replace(/\.(ts|tsx|js|jsx)$/, "").split(P).join("/");
63
+ return s.startsWith(".") ? s : `./${s}`;
40
64
  }
41
-
42
65
  // plugins/fs/src/index.ts
43
- var w = x.create({ name: "fs-routing" }).decorate("creator", "Ashu11-A").decorate("version", p).config({ path: "" }).derive("files", async (t) => await l(t.path)).extends((t, o) => ({
44
- async registerRoutes(r) {
45
- if (r.files.length !== 0)
46
- for (let e of r.files) {
47
- let s = (await import(e)).default;
48
- if (!s) {
49
- a("File Skipped: No Default Export", {
50
- File: e,
51
- Reason: "The file does not contain any default exported routes (export default).",
52
- Solution: 'Ensure the file has a "export default new Router()" or similar.'
53
- });
54
- continue;
55
- }
56
- if (!(s instanceof y || s instanceof h)) {
57
- a("File Skipped: Invalid Export Type", {
58
- File: e,
59
- "Exported Type": typeof s,
60
- Reason: "The file does not export a known class instance (e.g., Router, Method)."
61
- });
62
- continue;
63
- }
64
- s.path || (s.path = u(e, r.path)), t.controller(s);
66
+ var W = O.create({ name: "fs-routing" }).decorate("creator", "Ashu11-A").decorate("version", f).config({ routes: [] }).extends((t, r) => ({
67
+ registerRoutes(s) {
68
+ for (let e of s.routes) {
69
+ if (!(e instanceof I || e instanceof M)) {
70
+ m("Route Skipped: Invalid Entry", {
71
+ "Exported Type": typeof e,
72
+ Reason: "An entry in the generated route manifest is not a Router/Method instance.",
73
+ Solution: "Re-run `asterflow generate` - if the problem persists, check the route file's default export."
74
+ });
75
+ continue;
65
76
  }
77
+ t.controller(e);
78
+ }
66
79
  }
67
- })).on("beforeInitialize", async (t, o) => await t.registerRoutes(o)), M = w;
80
+ })).on("beforeInitialize", (t, r) => t.registerRoutes(r)), V = W;
68
81
  export {
69
- M as default,
70
- w as fsRoutingPlugin,
82
+ V as default,
83
+ W as fsRoutingPlugin,
84
+ B as generateRouteManifest,
71
85
  l as getFilesRecursively,
72
- u as transformPathToUrl
86
+ g as transformPathToUrl
73
87
  };
@@ -1,74 +1,32 @@
1
- // Generated by dts-bundle-generator v9.5.1
2
-
3
1
  import { Plugin } from '@asterflow/plugin';
4
- import { AnySchema, MethodHandler, MethodKeys, MethodOptions, Middleware, MiddlewareOutput, RouteHandler, RouterOptions, SchemaDynamic } from '@asterflow/router';
5
- import { AnyAsterflow } from 'asterflow';
6
-
7
- declare module "@asterflow/router" {
8
- type MethodOptionsFS<Responder extends Responders, Path extends string = string, Drive extends Runtime = Runtime, Method extends MethodKeys = MethodKeys, Schema extends AnySchema = AnySchema, Middlewares extends readonly Middleware<Responder, Schema, string, Record<string, unknown>>[] = [
9
- ], Context extends MiddlewareOutput<Middlewares> = MiddlewareOutput<Middlewares>, Instance extends AnyAsterflow = AnyAsterflow, Handler extends MethodHandler<Path, Drive, Responder, Schema, Middlewares, Context, Instance> = MethodHandler<Path, Drive, Responder, Schema, Middlewares, Context, Instance>> = Omit<MethodOptions<Responder, Path, Drive, Method, Schema, Middlewares, Context, Instance, Handler>, "path"> & {
10
- path?: Path;
11
- param?: Path;
12
- };
13
- type RouterOptionsFS<Path extends string, Schema extends SchemaDynamic<MethodKeys>, Responder extends Responders, Middlewares extends readonly Middleware<Responder, AnySchema, string, Record<string, unknown>>[], Context extends MiddlewareOutput<Middlewares>, Routers extends {
14
- [Method in MethodKeys]?: RouteHandler<Path, Responder, Method, Schema, Middlewares, Context>;
15
- }> = Omit<RouterOptions<Path, Schema, Responder, Middlewares, Context, Routers>, "path"> & {
16
- path?: Path;
17
- param?: Path;
18
- };
19
- export class Method<Responder extends Responders, const Path extends string = string, const Drive extends Runtime = Runtime, const Method extends MethodKeys = MethodKeys, const Schema extends AnySchema = AnySchema, const Middlewares extends readonly Middleware<Responder, Schema, string, Record<string, unknown>>[] = [
20
- ], const Context extends MiddlewareOutput<Middlewares> = MiddlewareOutput<Middlewares>, const Instance extends AnyAsterflow = AnyAsterflow, const Handler extends MethodHandler<Path, Drive, Responder, Schema, Middlewares, Context, Instance> = MethodHandler<Path, Drive, Responder, Schema, Middlewares, Context, Instance>> extends OriginalMethod<Responder, Path, Drive, Method, Schema, Middlewares, Context, Instance, Handler> {
21
- constructor(options: MethodOptionsFS<Responder, Path, Drive, Method, Schema, Middlewares, Context, Instance, Handler>);
22
- }
23
- export class Router<Responder extends Responders, const Path extends string = string, const Schema extends SchemaDynamic<MethodKeys> = SchemaDynamic<MethodKeys>, const Middlewares extends readonly Middleware<Responder, AnySchema, string, Record<string, unknown>>[] = [
24
- ], const Context extends MiddlewareOutput<Middlewares> = MiddlewareOutput<Middlewares>, const Routers extends {
25
- [Method in MethodKeys]?: RouteHandler<Path, Responder, Method, Schema, Middlewares, Context>;
26
- } = {
27
- [Method in MethodKeys]?: RouteHandler<Path, Responder, Method, Schema, Middlewares, Context>;
28
- }> extends OriginalRouter<Responder, Path, Schema, Middlewares, Context, Routers> {
29
- constructor(options: RouterOptionsFS<Path, Schema, Responder, Middlewares, Context, Routers>);
30
- }
31
- }
32
- export declare const fsRoutingPlugin: Plugin<"fs-routing", import("asterflow").AnyAsterflow, {
33
- path: string;
34
- defaultConfig: {
35
- path: string;
36
- };
37
- }, {
38
- creator: string;
39
- version: string;
40
- }, {
41
- files: string[];
42
- }, {
43
- registerRoutes: (context: {
44
- creator: string;
45
- version: string;
46
- path: string;
47
- defaultConfig: {
48
- path: string;
49
- };
50
- files: string[];
51
- }) => Promise<void>;
2
+ import { type AnyRouter } from '@asterflow/router';
3
+ export * from './utils/codegen';
4
+ export * from './utils/format';
5
+ export * from './utils/glob';
6
+ export * from './types/asterflow.d';
7
+ export declare const fsRoutingPlugin: Plugin<{
8
+ decorate: {
9
+ creator: string;
10
+ version: string;
11
+ };
12
+ config: {
13
+ routes: AnyRouter[];
14
+ defaultConfig: {
15
+ routes: AnyRouter[];
16
+ };
17
+ };
18
+ path: "fs-routing";
19
+ instance: import("asterflow").AnyAsterflow;
20
+ derive: {};
21
+ extension: {
22
+ registerRoutes: (context: {
23
+ routes: AnyRouter[];
24
+ defaultConfig: {
25
+ routes: AnyRouter[];
26
+ };
27
+ creator: string;
28
+ version: string;
29
+ }) => void;
30
+ };
52
31
  }>;
53
- /**
54
- * Recursively lists files from a starting directory.
55
- * @param dirPath The path to the starting directory.
56
- * @returns A promise that resolves to an array of file paths.
57
- */
58
- export declare function getFilesRecursively(dirPath: string): Promise<string[]>;
59
- /**
60
- * Transforms a file path into a robustly formatted URL route.
61
- */
62
- export declare function transformPathToUrl(filePath: string, rootDir: string): string;
63
- export type FSRoutingContext = {
64
- path: string;
65
- files: string[];
66
- creator: string;
67
- version: string;
68
- };
69
-
70
- export {
71
- fsRoutingPlugin as default,
72
- };
73
-
74
- export {};
32
+ export default fsRoutingPlugin;
@@ -0,0 +1,48 @@
1
+ import type { Runtime } from '@asterflow/adapter'
2
+ import type { AnyAsterflow } from 'asterflow'
3
+ import type { Responders } from '@asterflow/response'
4
+ import type {
5
+ AnySchema,
6
+ MethodHandler, MethodKeys,
7
+ Middleware,
8
+ MiddlewareOutput,
9
+ MethodOptions,
10
+ RouterOptions,
11
+ RouteHandler,
12
+ SchemaDynamic
13
+ } from '@asterflow/router'
14
+
15
+ // FS plugin augmentation: core router now supports optional path/param directly,
16
+ // this module augmentation is kept for backward compatibility and type helpers.
17
+ declare module '@asterflow/router' {
18
+ type MethodOptionsFS<
19
+ Responder extends Responders,
20
+ Path extends string = string,
21
+ Drive extends Runtime = Runtime,
22
+ Method extends MethodKeys = MethodKeys,
23
+ Schema extends AnySchema = AnySchema,
24
+ Middlewares extends readonly Middleware<Responder, Schema, string, Record<string, unknown>>[] = [],
25
+ Context extends MiddlewareOutput<Middlewares> = MiddlewareOutput<Middlewares>,
26
+ Instance extends AnyAsterflow = AnyAsterflow,
27
+ Handler extends MethodHandler<Path, Drive, Responder, Schema, Middlewares, Context, Instance> = MethodHandler<Path, Drive, Responder, Schema, Middlewares, Context, Instance>,
28
+ > = Omit<MethodOptions<Responder, Path, Drive, Method, Schema, Middlewares, Context, Instance, Handler>, 'path'>
29
+ & {
30
+ path?: Path
31
+ param?: Path
32
+ }
33
+
34
+ type RouterOptionsFS<
35
+ Path extends string,
36
+ Schema extends SchemaDynamic<MethodKeys>,
37
+ Responder extends Responders,
38
+ Middlewares extends readonly Middleware<Responder, AnySchema, string, Record<string, unknown>>[],
39
+ Context extends MiddlewareOutput<Middlewares>,
40
+ Routers extends {
41
+ [Method in MethodKeys]?: RouteHandler<Path, Responder, Method, Schema, Middlewares, Context>;
42
+ }
43
+ > = Omit<RouterOptions<Path, Schema, Responder, Middlewares, Context, Routers>, 'path'>
44
+ & {
45
+ path?: Path
46
+ param?: Path
47
+ }
48
+ }
@@ -0,0 +1,15 @@
1
+ export interface GenerateRouteManifestOptions {
2
+ /** Directory scanned for route files (e.g. `src/routes`). */
3
+ routesDir: string;
4
+ /** Path of the manifest file to write (e.g. `src/routes.gen.ts`). */
5
+ outFile: string;
6
+ }
7
+ /**
8
+ * Scans `routesDir` once and writes a manifest file at `outFile` containing a
9
+ * static `import` per route file plus a default-exported `AnyRouter[]`. Meant
10
+ * to run at build/dev time (the `asterflow generate` CLI command), not at
11
+ * request time - the resulting imports are literal specifiers a bundler can
12
+ * follow, unlike `fsRoutingPlugin`'s old `await import(file)` with a
13
+ * fully-dynamic path, which bundlers can't statically resolve at all.
14
+ */
15
+ export declare function generateRouteManifest({ routesDir, outFile }: GenerateRouteManifestOptions): Promise<void>;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Transforms a file path into a robustly formatted URL route.
3
+ */
4
+ export declare function transformPathToUrl(filePath: string, rootDir: string): string;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Recursively lists files from a starting directory.
3
+ * @param dirPath The path to the starting directory.
4
+ * @returns A promise that resolves to an array of file paths.
5
+ */
6
+ export declare function getFilesRecursively(dirPath: string): Promise<string[]>;
@@ -0,0 +1 @@
1
+ export declare function logWarning(title: string, details: Record<string, string>): void;
package/package.json CHANGED
@@ -1,6 +1,13 @@
1
1
  {
2
2
  "name": "@asterflow/fs",
3
- "version": "1.0.9",
3
+ "version": "2.0.0",
4
+ "description": "Generates a static route manifest from a file-based routes/ directory and registers it on an AsterFlow app as a plugin.",
5
+ "keywords": [
6
+ "asterflow",
7
+ "plugin",
8
+ "file-based-routing",
9
+ "fs-routing"
10
+ ],
4
11
  "main": "dist/cjs/index.cjs",
5
12
  "module": "dist/mjs/index.js",
6
13
  "types": "dist/types/index.d.ts",
@@ -10,12 +17,12 @@
10
17
  "author": "Ashu11-A",
11
18
  "repository": {
12
19
  "type": "git",
13
- "url": "git+https://github.com/AsterFlow/plugins.git"
20
+ "url": "git+https://github.com/AsterFlow/AsterFlow.git"
14
21
  },
15
22
  "bugs": {
16
- "url": "https://github.com/AsterFlow/plugins/issues"
23
+ "url": "https://github.com/AsterFlow/AsterFlow/issues"
17
24
  },
18
- "homepage": "https://github.com/AsterFlow/plugins",
25
+ "homepage": "https://github.com/AsterFlow/AsterFlow",
19
26
  "exports": {
20
27
  ".": {
21
28
  "types": "./dist/types/index.d.ts",
@@ -27,11 +34,11 @@
27
34
  "node": ">=20"
28
35
  },
29
36
  "dependencies": {
30
- "@asterflow/plugin": "^1.0.10",
31
- "@asterflow/router": "^1.0.13",
32
- "@asterflow/url-parser": "^2.0.3"
37
+ "@asterflow/router": "^2.0.0",
38
+ "@asterflow/url-parser": "^4.1.1"
33
39
  },
34
40
  "devDependencies": {
35
- "asterflow": "^0.0.2"
41
+ "@asterflow/plugin": "^1.0.9",
42
+ "asterflow": "^1.0.0"
36
43
  }
37
- }
44
+ }
package/tsconfig.json CHANGED
@@ -18,23 +18,9 @@
18
18
  "noUncheckedIndexedAccess": true,
19
19
  "noUnusedLocals": false,
20
20
  "noUnusedParameters": false,
21
- "noPropertyAccessFromIndexSignature": false,
22
- "paths": {
23
- "@asterflow/fs": [
24
- "./types"
25
- ],
26
- "@asterflow/fs/*": [
27
- "./types/*"
28
- ]
29
- },
30
- "declaration": true,
31
- "declarationMap": true
21
+ "noPropertyAccessFromIndexSignature": false
32
22
  },
33
23
  "include": [
34
- "types"
35
- ],
36
- "exclude": [
37
- "node_modules",
38
- "**/*.spec.ts"
24
+ "dist"
39
25
  ]
40
26
  }