fusion-framework 1.1.2 → 1.2.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/LICENSE ADDED
@@ -0,0 +1,28 @@
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2026, Fusion Framework
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package/README.md CHANGED
@@ -2,26 +2,24 @@
2
2
 
3
3
  Class-based HTTP APIs on a shared Rust core (`fusion-core`).
4
4
 
5
- Handlers use `this.params` / `this.query` / `this.body` / `this.state` (no signature param injection — that is Python-only DX).
5
+ Handlers use `this.params` / `this.query` / `this.body` / `this.state` (no signature param injection — that is Python-only).
6
6
 
7
- ## Links
7
+ ## Install
8
8
 
9
- - **Docs:** [fusion.cipherunit.xyz](https://fusion.cipherunit.xyz/)
10
- - **GitHub:** [cipherunits/fusion-framework](https://github.com/cipherunits/fusion-framework)
11
- - **CLI:** [cipherunits/fusion-tool](https://github.com/cipherunits/fusion-tool)
9
+ ```bash
10
+ npm i fusion-framework
11
+ ```
12
12
 
13
- ## Install
13
+ From this repo:
14
14
 
15
15
  ```bash
16
- cd crates/fusion-node
17
- npm install
18
- npm run build:debug
16
+ cd crates/fusion-node && npm install && npm run build:debug
19
17
  ```
20
18
 
21
- Or after publish:
19
+ Scaffold with [Fusion Tool](https://github.com/cipherunits/fusion-tool):
22
20
 
23
21
  ```bash
24
- npm i fusion-framework
22
+ fusion init --lang typescript --name my-app
25
23
  ```
26
24
 
27
25
  ## Quick start
@@ -37,7 +35,7 @@ export const ItemModule = route('/api/[module]/{id}')(
37
35
  },
38
36
  )
39
37
 
40
- const MIDDLEWARE = [] // optional framework has no defaults
38
+ const MIDDLEWARE = [] // your middleware; Fusion already adds frameworkHeaders() by default
41
39
 
42
40
  settings.ensureLoaded()
43
41
  const app = new FusionApp(getSettings())
@@ -45,32 +43,14 @@ for (const mw of MIDDLEWARE) app.use(mw)
45
43
  await app.listen()
46
44
  ```
47
45
 
48
- ## Middleware
49
-
50
- ```js
51
- import { bearerJwt, requireRoles, route } from 'fusion-framework'
52
-
53
- const MIDDLEWARE = [bearerJwt()] // or bearerJwt({ verify })
54
-
55
- route('/api/admin', { roles: ['admin', 'super_admin'] })(
56
- class AdminModule extends FusionBaseApi {
57
- get() {
58
- return this.response({ user: this.state.jwt?.sub })
59
- }
60
- },
61
- )
62
- ```
63
-
64
- Sync or async: `(request, callNext) => …` / `async (request, callNext) => await callNext(request)`.
46
+ ## Docs
65
47
 
66
- ## Status codes
48
+ Full guides (router, config, middleware):
49
+ **https://fusion.cipherunit.xyz/en/docs/typescript/v1**
67
50
 
68
- ```js
69
- import { status } from 'fusion-framework'
70
- status.HTTP_SUCCESS // 200
71
- status.HTTP_404_NOT_FOUND
72
- ```
51
+ - Site: [fusion.cipherunit.xyz](https://fusion.cipherunit.xyz/)
52
+ - GitHub: [cipherunits/fusion-framework](https://github.com/cipherunits/fusion-framework)
73
53
 
74
54
  ## License
75
55
 
76
- MIT
56
+ BSD 3-Clause
Binary file
Binary file
Binary file
Binary file
package/index.d.ts CHANGED
@@ -94,8 +94,27 @@ export function coerceParamJs(raw: string, kind?: string): unknown
94
94
 
95
95
  export const settings: Settings
96
96
  export const status: Record<string, number>
97
+ export const header: HeaderModule
97
98
  export const HTTP_METHODS: string[]
98
99
 
100
+ export interface HeaderModule {
101
+ [name: string]: string | ((...args: any[]) => Record<string, string>)
102
+ CONTENT_TYPE: string
103
+ CONTENT_DISPOSITION: string
104
+ LOCATION: string
105
+ AUTHORIZATION: string
106
+ APPLICATION_JSON: string
107
+ APPLICATION_OCTET_STREAM: string
108
+ APPLICATION_PDF: string
109
+ attachment(filename: string): Record<string, string>
110
+ inline(filename?: string | null): Record<string, string>
111
+ contentType(mediaType: string, charset?: string | null): Record<string, string>
112
+ location(url: string): Record<string, string>
113
+ cacheControl(value: string): Record<string, string>
114
+ download(filename: string, mediaType?: string | null): Record<string, string>
115
+ fingerprint(): Record<string, string>
116
+ }
117
+
99
118
  export interface FusionSettings {
100
119
  host: string
101
120
  port: number
@@ -118,6 +137,8 @@ export type FusionMiddleware = (
118
137
  callNext: (request: FusionRequest) => unknown | Promise<unknown>,
119
138
  ) => unknown | Promise<unknown>
120
139
 
140
+ export function frameworkHeaders(): FusionMiddleware
141
+
121
142
  export type FusionResponse =
122
143
  | string
123
144
  | {
package/index.js CHANGED
@@ -63,6 +63,98 @@ if (typeof native.getHttpStatusCodes === 'function') {
63
63
  })
64
64
  }
65
65
 
66
+ const header = Object.create(null)
67
+ if (typeof native.getHttpHeaderConstants === 'function') {
68
+ for (const entry of native.getHttpHeaderConstants()) {
69
+ header[entry.name] = entry.value
70
+ }
71
+ } else {
72
+ Object.assign(header, {
73
+ CONTENT_TYPE: 'Content-Type',
74
+ CONTENT_DISPOSITION: 'Content-Disposition',
75
+ LOCATION: 'Location',
76
+ AUTHORIZATION: 'Authorization',
77
+ APPLICATION_JSON: 'application/json',
78
+ APPLICATION_OCTET_STREAM: 'application/octet-stream',
79
+ APPLICATION_PDF: 'application/pdf',
80
+ })
81
+ }
82
+
83
+ function mergeHeaderMap(map) {
84
+ return map && typeof map === 'object' ? { ...map } : {}
85
+ }
86
+
87
+ header.attachment = (filename) =>
88
+ typeof native.headerAttachment === 'function'
89
+ ? mergeHeaderMap(native.headerAttachment(String(filename)))
90
+ : { [header.CONTENT_DISPOSITION]: `attachment; filename="${filename}"` }
91
+
92
+ header.inline = (filename) =>
93
+ typeof native.headerInline === 'function'
94
+ ? mergeHeaderMap(native.headerInline(filename == null ? null : String(filename)))
95
+ : filename
96
+ ? { [header.CONTENT_DISPOSITION]: `inline; filename="${filename}"` }
97
+ : { [header.CONTENT_DISPOSITION]: 'inline' }
98
+
99
+ header.contentType = (mediaType, charset) =>
100
+ typeof native.headerContentType === 'function'
101
+ ? mergeHeaderMap(native.headerContentType(String(mediaType), charset == null ? null : String(charset)))
102
+ : {
103
+ [header.CONTENT_TYPE]: charset
104
+ ? `${mediaType}; charset=${charset}`
105
+ : String(mediaType),
106
+ }
107
+
108
+ header.location = (url) =>
109
+ typeof native.headerLocation === 'function'
110
+ ? mergeHeaderMap(native.headerLocation(String(url)))
111
+ : { [header.LOCATION]: String(url) }
112
+
113
+ header.cacheControl = (value) =>
114
+ typeof native.headerCacheControl === 'function'
115
+ ? mergeHeaderMap(native.headerCacheControl(String(value)))
116
+ : { 'Cache-Control': String(value) }
117
+
118
+ header.download = (filename, mediaType) =>
119
+ typeof native.headerDownload === 'function'
120
+ ? mergeHeaderMap(
121
+ native.headerDownload(
122
+ String(filename),
123
+ mediaType == null ? null : String(mediaType),
124
+ ),
125
+ )
126
+ : {
127
+ ...header.contentType(mediaType || header.APPLICATION_OCTET_STREAM),
128
+ ...header.attachment(filename),
129
+ }
130
+
131
+ header.fingerprint = () =>
132
+ typeof native.getFingerprintHeaders === 'function'
133
+ ? mergeHeaderMap(native.getFingerprintHeaders())
134
+ : {
135
+ 'X-Powered-By': 'Fusion Framework',
136
+ 'X-Framework': 'Fusion',
137
+ 'X-Fusion-Version': '1.2.0',
138
+ }
139
+
140
+ function frameworkHeaders() {
141
+ const extra = header.fingerprint()
142
+ return (request, callNext) => {
143
+ const result = callNext(request)
144
+ const apply = (value) => {
145
+ if (value && typeof value.then === 'function') {
146
+ return value.then(apply)
147
+ }
148
+ if (!value || typeof value !== 'object') {
149
+ return { status: 200, body: value, headers: { ...extra } }
150
+ }
151
+ const headers = { ...extra, ...(value.headers || {}) }
152
+ return { ...value, headers }
153
+ }
154
+ return apply(result)
155
+ }
156
+ }
157
+
66
158
  class FusionBaseApi {
67
159
  constructor(request) {
68
160
  this.request = request
@@ -488,7 +580,8 @@ class FusionApp {
488
580
  this.settings = getSettings()
489
581
  this.engine = new NativeApp()
490
582
  this.mounted = false
491
- this._middleware = []
583
+ // Default: advertise Fusion to clients / Wappalyzer-style detectors.
584
+ this._middleware = [frameworkHeaders()]
492
585
  }
493
586
 
494
587
  use(middleware) {
@@ -639,10 +732,12 @@ module.exports = {
639
732
  getSettings,
640
733
  settings,
641
734
  status,
735
+ header,
642
736
  HTTP_METHODS,
643
737
  run,
644
738
  bearerJwt,
645
739
  requireRoles,
740
+ frameworkHeaders,
646
741
  runMiddlewareChain,
647
742
  coerceParam,
648
743
  getHttpMethods: () => HTTP_METHODS,
package/package.json CHANGED
@@ -1,25 +1,21 @@
1
1
  {
2
2
  "name": "fusion-framework",
3
- "version": "1.1.2",
4
- "description": "Build high-performance class-based HTTP APIs in Node.js on a shared Rust core routing, middleware, JWT auth, and TypeScript types",
3
+ "version": "1.2.0",
4
+ "description": "Class-based HTTP framework for Node.js, powered by a shared Rust core via N-API",
5
5
  "keywords": [
6
6
  "http",
7
7
  "framework",
8
8
  "nodejs",
9
- "node",
10
- "rust",
11
- "napi",
12
9
  "http-server",
13
10
  "web-framework",
14
- "rest-api",
11
+ "rest",
15
12
  "api",
16
13
  "routing",
17
14
  "middleware",
18
15
  "jwt",
19
16
  "typescript",
20
- "openapi",
21
- "swagger",
22
- "hyper",
17
+ "napi",
18
+ "rust",
23
19
  "fusion"
24
20
  ],
25
21
  "homepage": "https://fusion.cipherunit.xyz/",
@@ -32,21 +28,33 @@
32
28
  "directory": "crates/fusion-node"
33
29
  },
34
30
  "author": "CipherUnits <cipherunit.dev@gmail.com>",
35
- "license": "MIT",
36
- "main": "index.js",
37
- "types": "index.d.ts",
31
+ "license": "BSD-3-Clause",
32
+ "type": "commonjs",
33
+ "main": "./index.js",
34
+ "types": "./index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "types": "./index.d.ts",
38
+ "require": "./index.js",
39
+ "default": "./index.js"
40
+ },
41
+ "./package.json": "./package.json"
42
+ },
38
43
  "files": [
39
44
  "index.js",
40
45
  "index.d.ts",
41
46
  "README.md",
47
+ "LICENSE",
42
48
  "*.node"
43
49
  ],
44
50
  "napi": {
45
51
  "name": "fusion-node",
46
52
  "triples": {
47
- "defaults": true,
53
+ "defaults": false,
48
54
  "additional": [
55
+ "x86_64-unknown-linux-gnu",
49
56
  "aarch64-unknown-linux-gnu",
57
+ "x86_64-pc-windows-msvc",
50
58
  "aarch64-apple-darwin"
51
59
  ]
52
60
  }
@@ -55,7 +63,7 @@
55
63
  "artifacts": "napi artifacts",
56
64
  "build": "napi build --platform --release",
57
65
  "build:debug": "napi build --platform",
58
- "prepublishOnly": "napi prepublish -t npm --skip-gh-release"
66
+ "prepublishOnly": "node -e \"const fs=require('fs');const ok=fs.readdirSync('.').some(f=>f.endsWith('.node'));if(!ok){console.error('Missing native .node binaries before publish');process.exit(1)}\""
59
67
  },
60
68
  "publishConfig": {
61
69
  "access": "public",
@@ -63,16 +71,14 @@
63
71
  "provenance": true
64
72
  },
65
73
  "engines": {
66
- "node": ">=16"
74
+ "node": ">=18"
67
75
  },
76
+ "os": [
77
+ "linux",
78
+ "darwin",
79
+ "win32"
80
+ ],
68
81
  "devDependencies": {
69
82
  "@napi-rs/cli": "^2.18.4"
70
- },
71
- "optionalDependencies": {
72
- "fusion-framework-win32-x64-msvc": "1.1.2",
73
- "fusion-framework-darwin-x64": "1.1.2",
74
- "fusion-framework-linux-x64-gnu": "1.1.2",
75
- "fusion-framework-linux-arm64-gnu": "1.1.2",
76
- "fusion-framework-darwin-arm64": "1.1.2"
77
83
  }
78
- }
84
+ }