@asterflow/plugin 1.0.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.
package/README.md ADDED
@@ -0,0 +1,155 @@
1
+ <div align="center">
2
+
3
+ # @asterflow/plugin
4
+
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
+
8
+ ![last-commit](https://img.shields.io/github/last-commit/AsterFlow/AsterFlow?style=for-the-badge&colorA=302D41&colorB=b4befe)
9
+ ![commit-activity](https://img.shields.io/github/commit-activity/y/AsterFlow/AsterFlow?style=for-the-badge&colorA=302D41&colorB=f9e2af)
10
+ ![code-size](https://img.shields.io/github/languages/code-size/AsterFlow/AsterFlow?style=for-the-badge&colorA=302D41&colorB=90dceb)
11
+
12
+ ![top-language](https://img.shields.io/github/languages/top/AsterFlow/AsterFlow?style=for-the-badge&colorA=302D41&colorB=90dceb)
13
+ ![bundle-size](https://img.shields.io/bundlejs/size/@asterflow/plugin?style=for-the-badge&colorA=302D41&colorB=3ac97b)
14
+
15
+ </div>
16
+
17
+ > A modular and typed plugin system for extending AsterFlow functionality.
18
+
19
+ ## 📦 Installation
20
+
21
+ ```bash
22
+ npm install @asterflow/plugin
23
+ # or
24
+ bun install @asterflow/plugin
25
+ ```
26
+
27
+ ## 💡 About
28
+
29
+ `@asterflow/plugin` provides a robust and typed system for extending AsterFlow's functionality. It allows developers to create modular plugins that can inject context, manipulate configurations, and react to application lifecycle events, ensuring seamless and type-safe integration.
30
+
31
+ ## ✨ Features
32
+
33
+ - **Extensible Plugin System:** Create modular plugins to add custom functionalities to AsterFlow.
34
+ - **Dynamic Context:** Inject static values into the plugin's context (`decorate`) or derive complex properties based on configuration and existing context (`derive`).
35
+ - **Lifecycle Hooks:** Register handlers for specific AsterFlow application events (such as `beforeInitialize`, `afterInitialize`, `onRequest`, and `onResponse`) to extend behavior at different stages.
36
+ - **Typed Configuration:** Define the plugin's configuration structure and its default values, with automatic type inference.
37
+ - **Type Safety:** Full TypeScript support to ensure your plugin's context, configuration, and hooks are type-safe.
38
+ - **Runtime Optimization:** Hooks are efficiently invoked only when the plugin is registered, allowing for performance optimizations.
39
+
40
+ ## 🚀 Usage
41
+
42
+ `@asterflow/plugin` enables the creation of plugins that extend `AsterFlow` in a modular and type-safe manner. Below are examples of how to create and use plugins.
43
+
44
+ ### Creating a Basic Plugin
45
+
46
+ ```typescript
47
+ import { Plugin } from '@asterflow/plugin'
48
+
49
+ const myPlugin = Plugin.create({ name: 'my-first-plugin' })
50
+ .decorate('appName', 'My AsterFlow Application') // Adds a static value to the plugin's context
51
+ .on('beforeInitialize', (app, context) => {
52
+ console.log(`Initializing ${context.appName}...`)
53
+ // app is the AsterFlow instance
54
+ })
55
+ .on('afterInitialize', (app, context) => {
56
+ console.log(`${context.appName} has been initialized!`)
57
+ })
58
+
59
+ // This plugin can now be registered with `app.use(myPlugin)`
60
+ ```
61
+
62
+ ### Using Configuration and Derivation
63
+
64
+ Plugins can be configured and can derive values based on their configuration or existing context.
65
+
66
+ ```typescript
67
+ import { Plugin } from '@asterflow/plugin'
68
+
69
+ interface FeaturePluginConfig {
70
+ featureEnabled: boolean;
71
+ featureName: string;
72
+ }
73
+
74
+ const featureTogglePlugin = Plugin.create({ name: 'feature-toggle' })
75
+ .withConfig<FeaturePluginConfig>({
76
+ featureEnabled: true,
77
+ featureName: 'Awesome Feature'
78
+ })
79
+ .derive('statusMessage', (context) => {
80
+ return context.featureEnabled
81
+ ? `${context.featureName} is enabled.`
82
+ : `${context.featureName} is disabled.`
83
+ })
84
+ .on('beforeInitialize', (app, context) => {
85
+ console.log(context.statusMessage) // "Awesome Feature is enabled."
86
+ })
87
+
88
+ // This plugin can be configured when registered:
89
+ // app.use(featureTogglePlugin, { featureEnabled: false })
90
+ ```
91
+
92
+ ### Lifecycle Hooks
93
+
94
+ Hooks allow plugins to react to important events in the `AsterFlow` lifecycle.
95
+
96
+ ```typescript
97
+ import { Plugin } from '@asterflow/plugin'
98
+ import { Request } from '@asterflow/request'
99
+ import { Response } from '@asterflow/response'
100
+
101
+ const loggerPlugin = Plugin.create({ name: 'logger-plugin' })
102
+ .on('onRequest', (request, response, context) => {
103
+ // Logs request details
104
+ console.log(`[REQ] ${request.getMethod()} ${request.getPathname()}`)
105
+ })
106
+ .on('onResponse', (request, response, context) => {
107
+ // Logs response details
108
+ console.log(`[RES] ${response.getStatus()} ${request.getPathname()}`)
109
+ })
110
+
111
+ // Register this plugin for global logging
112
+ // app.use(loggerPlugin)
113
+ ```
114
+
115
+ ### Integrating with AsterFlow
116
+
117
+ To use the created plugins, you register them with the `AsterFlow` instance.
118
+
119
+ ```typescript
120
+ import { AsterFlow } from '@asterflow/core'
121
+ import { adapters } from '@asterflow/adapter'
122
+ import fastify from 'fastify'
123
+ // Import your plugins here
124
+ // import { myPlugin, featureTogglePlugin, loggerPlugin } from './your-plugins'
125
+
126
+ const server = fastify()
127
+ const app = new AsterFlow({
128
+ driver: adapters.fastify
129
+ })
130
+
131
+ // Example plugin registration
132
+ app.use(myPlugin) // No additional configuration
133
+ app.use(featureTogglePlugin, { featureEnabled: false }) // With overridden configuration
134
+ app.use(loggerPlugin)
135
+
136
+ app.listen(server, { port: 3000 }, (err) => {
137
+ if (err) {
138
+ console.error(err)
139
+ process.exit(1)
140
+ }
141
+ console.log('AsterFlow server with plugins listening on port 3000!')
142
+ })
143
+ ```
144
+
145
+ ## 🔗 Related Packages
146
+
147
+ - [@asterflow/core](https://www.npmjs.com/package/@asterflow/core) - The heart of the AsterFlow framework.
148
+ - [@asterflow/adapter](https://www.npmjs.com/package/@asterflow/adapter) - HTTP adapters for different runtimes.
149
+ - [@asterflow/router](https://www.npmjs.com/package/@asterflow/router) - Type-safe routing system.
150
+ - [@asterflow/request](https://www.npmjs.com/package/@asterflow/request) - Unified HTTP request system.
151
+ - [@asterflow/response](https://www.npmjs.com/package/@asterflow/response) - Type-safe HTTP response system.
152
+
153
+ ## 📄 License
154
+
155
+ MIT - See [LICENSE](https://github.com/AsterFlow/AsterFlow/blob/main/LICENSE) for more details.
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var r = Object.defineProperty;
3
+ var f = Object.getOwnPropertyDescriptor;
4
+ var u = Object.getOwnPropertyNames;
5
+ var d = Object.prototype.hasOwnProperty;
6
+ var g = (t, e) => {
7
+ for (var o in e)
8
+ r(t, o, { get: e[o], enumerable: !0 });
9
+ }, C = (t, e, o, n) => {
10
+ if (e && typeof e == "object" || typeof e == "function")
11
+ for (let s of u(e))
12
+ !d.call(t, s) && s !== o && r(t, s, { get: () => e[s], enumerable: !(n = f(e, s)) || n.enumerable });
13
+ return t;
14
+ };
15
+ var x = (t) => C(r({}, "__esModule", { value: !0 }), t);
16
+ // packages/plugin/src/index.ts
17
+ var y = {};
18
+ g(y, {
19
+ Plugin: () => a
20
+ });
21
+ module.exports = x(y);
22
+ // packages/plugin/src/controllers/Plugin.ts
23
+ var a = class t {
24
+ name;
25
+ resolvers;
26
+ defaultConfig;
27
+ hooks;
28
+ constructor(e, o, n, s) {
29
+ this.name = e, this.resolvers = o, this.hooks = n, this.defaultConfig = s;
30
+ }
31
+ withConfig(e) {
32
+ return new t(
33
+ this.name,
34
+ this.resolvers,
35
+ this.hooks,
36
+ e
37
+ );
38
+ }
39
+ decorate(e, o) {
40
+ let n = (s, i) => ({
41
+ ...i,
42
+ [e]: o
43
+ });
44
+ return new t(
45
+ this.name,
46
+ [...this.resolvers, n],
47
+ this.hooks,
48
+ this.defaultConfig
49
+ );
50
+ }
51
+ derive(e, o) {
52
+ let n = (s, i) => {
53
+ let l = { ...i, ...s }, h = o(l);
54
+ return {
55
+ ...i,
56
+ [e]: h
57
+ };
58
+ };
59
+ return new t(
60
+ this.name,
61
+ [...this.resolvers, n],
62
+ this.hooks,
63
+ this.defaultConfig
64
+ );
65
+ }
66
+ on(e, o) {
67
+ return this.hooks[e] || (this.hooks[e] = []), this.hooks[e].push(o), new t(
68
+ this.name,
69
+ this.resolvers,
70
+ this.hooks,
71
+ this.defaultConfig
72
+ );
73
+ }
74
+ _build(e) {
75
+ let o = { ...this.defaultConfig, ...e }, n = {};
76
+ for (let s of this.resolvers)
77
+ n = s(o, n);
78
+ return {
79
+ name: this.name,
80
+ context: n,
81
+ hooks: this.hooks
82
+ };
83
+ }
84
+ static create(e) {
85
+ return new t(e.name, [], {}, {});
86
+ }
87
+ };
88
+ 0 && (module.exports = {
89
+ Plugin
90
+ });
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,69 @@
1
+ // packages/plugin/src/controllers/Plugin.ts
2
+ var r = class s {
3
+ name;
4
+ resolvers;
5
+ defaultConfig;
6
+ hooks;
7
+ constructor(e, o, t, n) {
8
+ this.name = e, this.resolvers = o, this.hooks = t, this.defaultConfig = n;
9
+ }
10
+ withConfig(e) {
11
+ return new s(
12
+ this.name,
13
+ this.resolvers,
14
+ this.hooks,
15
+ e
16
+ );
17
+ }
18
+ decorate(e, o) {
19
+ let t = (n, i) => ({
20
+ ...i,
21
+ [e]: o
22
+ });
23
+ return new s(
24
+ this.name,
25
+ [...this.resolvers, t],
26
+ this.hooks,
27
+ this.defaultConfig
28
+ );
29
+ }
30
+ derive(e, o) {
31
+ let t = (n, i) => {
32
+ let a = { ...i, ...n }, l = o(a);
33
+ return {
34
+ ...i,
35
+ [e]: l
36
+ };
37
+ };
38
+ return new s(
39
+ this.name,
40
+ [...this.resolvers, t],
41
+ this.hooks,
42
+ this.defaultConfig
43
+ );
44
+ }
45
+ on(e, o) {
46
+ return this.hooks[e] || (this.hooks[e] = []), this.hooks[e].push(o), new s(
47
+ this.name,
48
+ this.resolvers,
49
+ this.hooks,
50
+ this.defaultConfig
51
+ );
52
+ }
53
+ _build(e) {
54
+ let o = { ...this.defaultConfig, ...e }, t = {};
55
+ for (let n of this.resolvers)
56
+ t = n(o, t);
57
+ return {
58
+ name: this.name,
59
+ context: t,
60
+ hooks: this.hooks
61
+ };
62
+ }
63
+ static create(e) {
64
+ return new s(e.name, [], {}, {});
65
+ }
66
+ };
67
+ export {
68
+ r as Plugin
69
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1,54 @@
1
+ import type { AnyPluginHooks } from '../types/plugin';
2
+ /**
3
+ * A factory for creating plugins that can be configured and attached to AsterFlow.
4
+ * Plugins are defined with a series of resolvers (`decorate`, `derive`) and lifecycle
5
+ * hooks (`on`) that build up its context and runtime behavior.
6
+ */
7
+ export declare class Plugin<Path extends string, Config extends Record<string, any> = {}, Context extends Record<string, any> = {}, THooks extends AnyPluginHooks = {}> {
8
+ readonly name: Path;
9
+ private readonly resolvers;
10
+ readonly defaultConfig: Partial<Config>;
11
+ readonly hooks: THooks;
12
+ private constructor();
13
+ /**
14
+ * Defines the shape of the configuration and its default values for this plugin.
15
+ */
16
+ withConfig<C extends Record<string, any>>(defaultConfig: C): Plugin<Path, C, Context, THooks>;
17
+ /**
18
+ * Adds a new static value to the plugin's context (decoration).
19
+ */
20
+ decorate<Key extends string, Value>(key: Key, value: Value): Plugin<Path, Config, Context & { [K in Key]: Value; }, THooks>;
21
+ /**
22
+ * Adds a new property to the context that is derived from the configuration and the existing context.
23
+ * The resolver function is executed lazily when the plugin is registered via `app.use()`.
24
+ */
25
+ derive<Key extends string, Value>(key: Key, resolverFn: (context: Context & Config) => Value): Plugin<Path, Config, Context & { [K in Key]: Value; }, THooks>;
26
+ /**
27
+ * Registers a handler for a specific lifecycle event.
28
+ * Adding a hook makes the plugin "runtime-aware". AsterFlow can optimize by only
29
+ * invoking plugins that have registered hooks for a given event.
30
+ *
31
+ * @example
32
+ * const routingPlugin = Plugin.create({ name: 'dynamic-routes' })
33
+ * .on('beforeInitialize', (app, context) => {
34
+ * // `app` is the AsterFlow instance
35
+ * const dynamicRouter = createDynamicRouter();
36
+ * app.controller(dynamicRouter);
37
+ * });
38
+ */
39
+ on<Event extends keyof AnyPluginHooks, Handler extends NonNullable<AnyPluginHooks[Event]> extends (infer F)[] ? F : never>(event: Event, handler: Handler): Plugin<Path, Config, Context, THooks>;
40
+ /**
41
+ * Builds the final context and hooks from the provided configuration.
42
+ */
43
+ _build(config: any): {
44
+ name: Path;
45
+ context: Context;
46
+ hooks: THooks;
47
+ };
48
+ /**
49
+ * Creates a new Plugin instance. This is the entry point for building a plugin.
50
+ */
51
+ static create<Path extends string>(options: {
52
+ name: Path;
53
+ }): Plugin<Path, {}, {}, {}>;
54
+ }
@@ -0,0 +1,2 @@
1
+ export * from './controllers/Plugin';
2
+ export * from './types/plugin';
@@ -0,0 +1,29 @@
1
+ import type { AsterFlow } from '@asterflow/core';
2
+ import type { Request } from '@asterflow/request';
3
+ import type { Response } from '@asterflow/response';
4
+ import type { Plugin } from '../controllers/Plugin';
5
+ export type AnyPlugin = Plugin<string>;
6
+ export type AnyPluginHooks = PluginHooks<any, any, any, any>;
7
+ /**
8
+ * Tipo para extrair o objeto de configuração de um plugin.
9
+ * Ele torna as propriedades com valores padrão opcionais.
10
+ */
11
+ export type ConfigArgument<P extends AnyPlugin> = P extends Plugin<any, any, any, infer C> ? Omit<C, keyof P['defaultConfig']> & Partial<Pick<C, keyof P['defaultConfig'] & keyof C>> : never;
12
+ /**
13
+ * Tipo para o plugin após seu contexto ter sido construído.
14
+ */
15
+ export type ResolvedPlugin<P extends AnyPlugin> = ReturnType<P['_build']>;
16
+ /**
17
+ * Defines the available lifecycle hooks a plugin can register.
18
+ */
19
+ export type PluginHooks<Instance extends AsterFlow<any>, Context extends Record<string, any>, Responser extends Response, Requester extends Request<unknown>> = {
20
+ beforeInitialize?: ((app: Instance, context: Context) => void | Promise<void>)[];
21
+ afterInitialize?: ((app: Instance, context: Context) => void | Promise<void>)[];
22
+ onRequest?: ((request: Requester, response: Responser, context: Context) => void | Promise<void>)[];
23
+ onResponse?: ((request: Requester, response: Responser, context: Context) => void | Promise<void>)[];
24
+ };
25
+ /**
26
+ * Represents a function that resolves a part of the plugin's context.
27
+ * @internal
28
+ */
29
+ export type Resolver = (config: any, context: any) => Record<string, any>;
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@asterflow/plugin",
3
+ "version": "1.0.1",
4
+ "main": "dist/cjs/index.cjs",
5
+ "module": "dist/mjs/index.js",
6
+ "types": "dist/types/index.d.ts",
7
+ "typings": "dist/types/index.d.ts",
8
+ "type": "module",
9
+ "license": "MIT",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/types/index.d.ts",
13
+ "import": "./dist/mjs/index.js",
14
+ "require": "./dist/cjs/index.cjs"
15
+ }
16
+ },
17
+ "engines": {
18
+ "node": ">=20"
19
+ },
20
+ "devDependencies": {
21
+ "@asterflow/core": "1.0.5",
22
+ "@asterflow/response": "1.0.2",
23
+ "@asterflow/request": "1.0.5"
24
+ },
25
+ "peerDependencies": {
26
+ "typescript": "^5.8.3"
27
+ }
28
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "lib": [
4
+ "ESNext"
5
+ ],
6
+ "target": "ESNext",
7
+ "module": "ESNext",
8
+ "moduleDetection": "force",
9
+ "jsx": "react-jsx",
10
+ "allowJs": true,
11
+ "moduleResolution": "bundler",
12
+ "allowImportingTsExtensions": true,
13
+ "verbatimModuleSyntax": true,
14
+ "noEmit": true,
15
+ "strict": true,
16
+ "skipLibCheck": true,
17
+ "noFallthroughCasesInSwitch": true,
18
+ "noUncheckedIndexedAccess": true,
19
+ "noUnusedLocals": false,
20
+ "noUnusedParameters": false,
21
+ "noPropertyAccessFromIndexSignature": false
22
+ },
23
+ "include": [
24
+ "dist"
25
+ ]
26
+ }