@draftlab/auth 0.2.2 → 0.2.3

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/dist/core.d.ts CHANGED
@@ -81,7 +81,7 @@ interface IssuerInput<Providers extends Record<string, Provider<unknown>>, Subje
81
81
  /** Client authorization check function */
82
82
  allow?(input: AllowCheckInput, req: Request): Promise<boolean>;
83
83
  /** Plugin configuration */
84
- plugins?: readonly Plugin[];
84
+ plugins?: Plugin[];
85
85
  /**
86
86
  * Refresh callback for updating user claims.
87
87
  *
package/dist/core.js CHANGED
@@ -258,7 +258,7 @@ const issuer = (input) => {
258
258
  const app = new Router({ basePath: input.basePath });
259
259
  if (input.plugins && input.plugins.length > 0) {
260
260
  const manager = new PluginManager(input.storage);
261
- for (const plugin of input.plugins) manager.register(plugin);
261
+ manager.registerAll(input.plugins);
262
262
  manager.setupRoutes(app);
263
263
  }
264
264
  for (const [name, value] of Object.entries(input.providers)) {
@@ -3,32 +3,46 @@
3
3
  * Create a new plugin
4
4
  */
5
5
  const plugin = (id) => {
6
+ if (!id || typeof id !== "string") throw new Error("Plugin id must be a non-empty string");
6
7
  const routes = [];
7
- const builder = {
8
+ const registeredPaths = /* @__PURE__ */ new Set();
9
+ const validatePath = (path) => {
10
+ if (!path || typeof path !== "string") throw new Error("Route path must be a non-empty string");
11
+ if (!path.startsWith("/")) throw new Error("Route path must start with '/'");
12
+ };
13
+ return {
8
14
  get(path, handler) {
15
+ validatePath(path);
16
+ const routeKey = `GET ${path}`;
17
+ if (registeredPaths.has(routeKey)) throw new Error(`Route ${routeKey} already registered in plugin '${id}'`);
18
+ registeredPaths.add(routeKey);
9
19
  routes.push({
10
20
  method: "GET",
11
21
  path,
12
22
  handler
13
23
  });
14
- return builder;
24
+ return this;
15
25
  },
16
26
  post(path, handler) {
27
+ validatePath(path);
28
+ const routeKey = `POST ${path}`;
29
+ if (registeredPaths.has(routeKey)) throw new Error(`Route ${routeKey} already registered in plugin '${id}'`);
30
+ registeredPaths.add(routeKey);
17
31
  routes.push({
18
32
  method: "POST",
19
33
  path,
20
34
  handler
21
35
  });
22
- return builder;
36
+ return this;
23
37
  },
24
38
  build() {
39
+ if (routes.length === 0) throw new Error(`Plugin '${id}' has no routes defined`);
25
40
  return {
26
41
  id,
27
42
  routes
28
43
  };
29
44
  }
30
45
  };
31
- return builder;
32
46
  };
33
47
 
34
48
  //#endregion
@@ -12,6 +12,18 @@ declare class PluginManager {
12
12
  * Register a plugin
13
13
  */
14
14
  register(plugin: Plugin): void;
15
+ /**
16
+ * Register multiple plugins at once
17
+ */
18
+ registerAll(plugins: Plugin[]): void;
19
+ /**
20
+ * Get all registered plugins
21
+ */
22
+ getAll(): Plugin[];
23
+ /**
24
+ * Get plugin by id
25
+ */
26
+ get(id: string): Plugin | undefined;
15
27
  /**
16
28
  * Setup plugin routes on a router
17
29
  */
@@ -15,30 +15,49 @@ var PluginManager = class {
15
15
  this.plugins.set(plugin.id, plugin);
16
16
  }
17
17
  /**
18
+ * Register multiple plugins at once
19
+ */
20
+ registerAll(plugins) {
21
+ for (const plugin of plugins) this.register(plugin);
22
+ }
23
+ /**
24
+ * Get all registered plugins
25
+ */
26
+ getAll() {
27
+ return Array.from(this.plugins.values());
28
+ }
29
+ /**
30
+ * Get plugin by id
31
+ */
32
+ get(id) {
33
+ return this.plugins.get(id);
34
+ }
35
+ /**
18
36
  * Setup plugin routes on a router
19
37
  */
20
38
  setupRoutes(router) {
39
+ const registeredPaths = /* @__PURE__ */ new Set();
21
40
  for (const plugin of this.plugins.values()) {
22
41
  if (!plugin.routes) continue;
23
- for (const route of plugin.routes) switch (route.method) {
24
- case "GET":
25
- router.get(route.path, async (ctx) => {
26
- const pluginCtx = {
27
- ...ctx,
28
- storage: this.storage
29
- };
30
- return await route.handler(pluginCtx);
31
- });
32
- break;
33
- case "POST":
34
- router.post(route.path, async (ctx) => {
35
- const pluginCtx = {
36
- ...ctx,
37
- storage: this.storage
38
- };
39
- return await route.handler(pluginCtx);
40
- });
41
- break;
42
+ for (const route of plugin.routes) {
43
+ const fullPath = `/${plugin.id}${route.path}`;
44
+ if (registeredPaths.has(fullPath)) throw new PluginError(`Route conflict: ${fullPath} already registered`, plugin.id);
45
+ registeredPaths.add(fullPath);
46
+ const handler = async (ctx) => {
47
+ const pluginCtx = {
48
+ ...ctx,
49
+ storage: this.storage
50
+ };
51
+ return await route.handler(pluginCtx);
52
+ };
53
+ switch (route.method) {
54
+ case "GET":
55
+ router.get(fullPath, handler);
56
+ break;
57
+ case "POST":
58
+ router.post(fullPath, handler);
59
+ break;
60
+ }
42
61
  }
43
62
  }
44
63
  }
@@ -3,7 +3,7 @@ import { Plugin, PluginRouteHandler } from "./types.js";
3
3
  //#region src/plugin/plugin.d.ts
4
4
 
5
5
  /**
6
- * Plugin builder interface for fluent API
6
+ * Plugin builder interface
7
7
  */
8
8
  interface PluginBuilder {
9
9
  /** Add GET route */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftlab/auth",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "description": "Core implementation for @draftlab/auth",
6
6
  "author": "Matheus Pergoli",
@@ -37,8 +37,8 @@
37
37
  ],
38
38
  "license": "MIT",
39
39
  "devDependencies": {
40
- "@types/node": "^24.0.15",
41
- "tsdown": "^0.12.9",
40
+ "@types/node": "^24.1.0",
41
+ "tsdown": "^0.13.0",
42
42
  "typescript": "^5.8.3",
43
43
  "@draftlab/tsconfig": "0.1.0"
44
44
  },