cipher-switch 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Neeraj
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,116 @@
1
+ # cipher-switch
2
+
3
+ A lightweight TypeScript library for transparently encrypting and decrypting HTTP request/response bodies using [Fernet](https://github.com/fernet/spec/blob/master/Spec.md) symmetric encryption. Drop-in replacement for `fetch` that keeps your API traffic encrypted in transit.
4
+
5
+ ## Features
6
+
7
+ - **Automatic encryption** – JSON request bodies are encrypted before sending
8
+ - **Automatic decryption** – Responses with the `x-encrypted` header are decrypted transparently
9
+ - **Drop-in fetch replacement** – Use `createCryptoFetch` instead of `fetch` with minimal code changes
10
+ - **Path exclusions** – Skip encryption for specific URL paths (e.g. health checks, public endpoints)
11
+ - **Fernet encryption** – Industry-standard symmetric encryption with built-in timestamp validation
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ bun add cipher-switch
17
+ ```
18
+
19
+ Or with npm:
20
+
21
+ ```bash
22
+ npm install cipher-switch
23
+ ```
24
+
25
+ ## Quick Start
26
+
27
+ ```typescript
28
+ import { createCryptoFetch } from 'cipher-switch'
29
+
30
+ // Create a fetch function with your Fernet secret key
31
+ const cryptoFetch = createCryptoFetch({
32
+ key: 'your-base64-fernet-key-here',
33
+ })
34
+
35
+ // Use it exactly like fetch
36
+ const response = await cryptoFetch('https://api.example.com/data', {
37
+ method: 'POST',
38
+ headers: { 'Content-Type': 'application/json' },
39
+ body: JSON.stringify({ email: 'user@example.com', password: 'secret' }),
40
+ })
41
+
42
+ const data = await response.json()
43
+ // Data is automatically decrypted
44
+ ```
45
+
46
+ ## Configuration
47
+
48
+ ```typescript
49
+ createCryptoFetch({
50
+ key: string; // Required: Fernet secret key (base64)
51
+ excludedPaths?: string[]; // Optional: URL paths to skip encryption
52
+ enableEncryption?: boolean; // Optional: Toggle encryption (default: true)
53
+ });
54
+ ```
55
+
56
+ ### Generating a Fernet Key
57
+
58
+ Fernet keys must be 32 bytes, base64url encoded. Generate one with:
59
+
60
+ ```typescript
61
+ import { CryptoClient } from 'cipher-switch'
62
+
63
+ // Using Node.js crypto or Bun
64
+ const key = Buffer.from(crypto.randomBytes(32)).toString('base64url')
65
+ // Or: crypto.randomBytes(32).toString("base64url")
66
+ ```
67
+
68
+ ## API
69
+
70
+ ### `createCryptoFetch(config)`
71
+
72
+ Returns an async function with the same signature as `fetch`. It:
73
+
74
+ 1. **Encrypts** JSON request bodies when `Content-Type: application/json` is set
75
+ 2. **Decrypts** responses that include the `x-encrypted` header
76
+ 3. **Skips** URLs matching any `excludedPaths`
77
+ 4. **Passes through** non-JSON requests and unencrypted responses unchanged
78
+
79
+ ### `CryptoClient`
80
+
81
+ Lower-level API for direct encryption/decryption:
82
+
83
+ ```typescript
84
+ import { CryptoClient } from 'cipher-switch'
85
+
86
+ const crypto = new CryptoClient('your-fernet-key')
87
+
88
+ // Encrypt any JSON-serializable data
89
+ const encrypted = crypto.encrypt({ foo: 'bar' })
90
+
91
+ // Decrypt a Fernet token
92
+ const decrypted = crypto.decrypt(encrypted)
93
+ ```
94
+
95
+ ## Server-Side Requirements
96
+
97
+ For end-to-end encryption, your API server must:
98
+
99
+ 1. **Encrypt responses** using the same Fernet key
100
+ 2. **Set the header** `x-encrypted: true` on encrypted responses
101
+ 3. **Decrypt request bodies** that come in the format `{ encrypted: "<fernet-token>" }`
102
+
103
+ ## Excluding Paths
104
+
105
+ Skip encryption for specific endpoints:
106
+
107
+ ```typescript
108
+ const cryptoFetch = createCryptoFetch({
109
+ key: process.env.FERNET_KEY!,
110
+ excludedPaths: ['/health', '/public', '/metrics'],
111
+ })
112
+ ```
113
+
114
+ ## License
115
+
116
+ MIT
@@ -0,0 +1,15 @@
1
+ type Config = {
2
+ key: string;
3
+ excludedPaths?: string[];
4
+ enableEncryption?: boolean;
5
+ };
6
+ declare function createCryptoFetch(config: Config): (input: Request | string | URL, init?: RequestInit) => Promise<Response>;
7
+
8
+ declare class CryptoClient {
9
+ private secret;
10
+ constructor(key: string);
11
+ encrypt(data: unknown): string;
12
+ decrypt(tokenStr: string): any;
13
+ }
14
+
15
+ export { CryptoClient, createCryptoFetch };
@@ -0,0 +1,15 @@
1
+ type Config = {
2
+ key: string;
3
+ excludedPaths?: string[];
4
+ enableEncryption?: boolean;
5
+ };
6
+ declare function createCryptoFetch(config: Config): (input: Request | string | URL, init?: RequestInit) => Promise<Response>;
7
+
8
+ declare class CryptoClient {
9
+ private secret;
10
+ constructor(key: string);
11
+ encrypt(data: unknown): string;
12
+ decrypt(tokenStr: string): any;
13
+ }
14
+
15
+ export { CryptoClient, createCryptoFetch };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ "use strict";var R=Object.create;var y=Object.defineProperty;var b=Object.getOwnPropertyDescriptor;var C=Object.getOwnPropertyNames;var S=Object.getPrototypeOf,T=Object.prototype.hasOwnProperty;var E=(t,e,n)=>e in t?y(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var J=(t,e)=>{for(var n in e)y(t,n,{get:e[n],enumerable:!0})},u=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of C(e))!T.call(t,o)&&o!==n&&y(t,o,{get:()=>e[o],enumerable:!(r=b(e,o))||r.enumerable});return t};var N=(t,e,n)=>(n=t!=null?R(S(t)):{},u(e||!t||!t.__esModule?y(n,"default",{value:t,enumerable:!0}):n,t)),O=t=>u(y({},"__esModule",{value:!0}),t);var l=(t,e,n)=>E(t,typeof e!="symbol"?e+"":e,n);var q={};J(q,{CryptoClient:()=>a,createCryptoFetch:()=>g});module.exports=O(q);var d=N(require("fernet"));var a=class{constructor(e){l(this,"secret");this.secret=new d.Secret(e)}encrypt(e){return new d.Token({secret:this.secret,time:Date.now(),iv:void 0}).encode(JSON.stringify(e))}decrypt(e){let r=new d.Token({secret:this.secret,token:e,ttl:0}).decode();return JSON.parse(r)}};function g(t){let e=new a(t.key),n=t.excludedPaths??[],r=t.enableEncryption??!0;return async function(s,c={}){let h=typeof s=="string"?s:s instanceof URL?s.pathname:s.url;if(n.some(p=>h.includes(p)))return fetch(s,c);let f={...c};if(r){let p=c.headers?.["Content-Type"]||c.headers?.["content-type"];if(c.body&&p?.includes("application/json"))try{let w=JSON.parse(c.body),x=e.encrypt(w);f.body=JSON.stringify({encrypted:x})}catch{}}let i=await fetch(s,f);if(!r||!i.headers.get("x-encrypted"))return i;let k=await i.text(),m=e.decrypt(k);return new Response(JSON.stringify(m),{status:i.status,headers:{"Content-Type":"application/json"}})}}0&&(module.exports={CryptoClient,createCryptoFetch});
package/dist/index.mjs ADDED
@@ -0,0 +1 @@
1
+ var k=Object.defineProperty;var m=(t,e,n)=>e in t?k(t,e,{enumerable:!0,configurable:!0,writable:!0,value:n}):t[e]=n;var p=(t,e,n)=>m(t,typeof e!="symbol"?e+"":e,n);import*as c from"fernet";var a=class{constructor(e){p(this,"secret");this.secret=new c.Secret(e)}encrypt(e){return new c.Token({secret:this.secret,time:Date.now(),iv:void 0}).encode(JSON.stringify(e))}decrypt(e){let i=new c.Token({secret:this.secret,token:e,ttl:0}).decode();return JSON.parse(i)}};function w(t){let e=new a(t.key),n=t.excludedPaths??[],i=t.enableEncryption??!0;return async function(r,s={}){let f=typeof r=="string"?r:r instanceof URL?r.pathname:r.url;if(n.some(y=>f.includes(y)))return fetch(r,s);let d={...s};if(i){let y=s.headers?.["Content-Type"]||s.headers?.["content-type"];if(s.body&&y?.includes("application/json"))try{let g=JSON.parse(s.body),h=e.encrypt(g);d.body=JSON.stringify({encrypted:h})}catch{}}let o=await fetch(r,d);if(!i||!o.headers.get("x-encrypted"))return o;let u=await o.text(),l=e.decrypt(u);return new Response(JSON.stringify(l),{status:o.status,headers:{"Content-Type":"application/json"}})}}export{a as CryptoClient,w as createCryptoFetch};
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "cipher-switch",
3
+ "version": "1.0.1",
4
+ "description": "A lightweight TypeScript library for transparently encrypting and decrypting HTTP request/response bodies using Fernet symmetric encryption.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/neeraj2713/cipher-switch.git"
8
+ },
9
+ "bugs": "https://github.com/neeraj2713/cipher-switch/issues",
10
+ "homepage": "https://github.com/neeraj2713/cipher-switch#readme",
11
+ "license": "MIT",
12
+ "main": "./dist/index.js",
13
+ "module": "./dist/index.mjs",
14
+ "types": "./dist/index.d.ts",
15
+ "exports": {
16
+ ".": {
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.mjs",
19
+ "require": "./dist/index.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "build": "tsup",
27
+ "format": "prettier --write .",
28
+ "format:check": "prettier --check .",
29
+ "test": "bun test test/",
30
+ "prepublishOnly": "bun run build",
31
+ "pack:dry": "bun run build && bun pm pack",
32
+ "publish:dry": "bun publish --dry-run"
33
+ },
34
+ "keywords": [
35
+ "nextjs",
36
+ "encryption",
37
+ "fernet",
38
+ "symmetric"
39
+ ],
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "dependencies": {
47
+ "fernet": "^0.3.3"
48
+ },
49
+ "devDependencies": {
50
+ "@types/fernet": "^0.4.3",
51
+ "prettier": "^3.8.1",
52
+ "tsup": "^8.5.1",
53
+ "typescript": "^5"
54
+ },
55
+ "author": "Neeraj (https://github.com/neeraj2713)"
56
+ }