@stless/modify-js 1.0.0 → 1.1.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
@@ -7,17 +7,37 @@
7
7
  # @stless/modify-js
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@stless/modify-js.svg)](https://www.npmjs.com/package/@stless/modify-js)
10
- ![Node.js](https://img.shields.io/badge/Node.js-%3E%3D20-green)
10
+ ![Node.js](https://img.shields.io/badge/Node.js-%3E%3D16.11.0-green)
11
11
  [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://github.com/harnuma9/modify-js/blob/main/LICENSE)
12
12
  ![Verify Status](https://github.com/harnuma9/modify-js/actions/workflows/verify.yml/badge.svg)
13
- [![Socket Badge](https://badge.socket.dev/npm/package/@stless/modify-js/1.0.0)](https://badge.socket.dev/npm/package/@stless/modify-js/1.0.0)
13
+ [![Socket Badge](https://badge.socket.dev/npm/package/@stless/modify-js/1.1.0)](https://badge.socket.dev/npm/package/@stless/modify-js/1.1.0)
14
14
  [![Donate](https://img.shields.io/badge/@Harnuma9-Donate-FF4D4D)](https://harnuma9.github.io/donate/)
15
15
 
16
16
  <br />
17
17
 
18
- **Lightweight functional chaining for JavaScript.**
19
- `modify-js` is a zero-dependency utility that provides a user-land polyfill for the proposed **[TC39](https://github.com/tc39/proposal-pipeline-operator) Pipeline Operator**.
20
- It safely extends native prototypes, and it allows you to transform data through readable, linear pipelines without the syntactic callback hell” of nested functions.
18
+ `@stless/modify-js` is a lightweight, zero-dependency utility that brings functional piping to JavaScript. It provides a secure, high-performance alternative to the proposed **[TC39 Pipeline Operator](https://github.com/tc39/proposal-pipeline-operator)**.
19
+
20
+ Values wrapped in a “Hardened Pipe” can transform data through readable, linear pipelines. This eliminates the “Callback Hell” of nested functions and ensures your data remains encapsulated and secure throughout its lifecycle.
21
+
22
+ <br />
23
+
24
+ ---
25
+
26
+ <br />
27
+
28
+ ## ✨ Why `[modify](JS)`?
29
+
30
+ While standard JavaScript requires nested function calls or intermediate variables, [modify-js](https://www.npmjs.com/package/@stless/modify-js) allows for a clean, top-down data flow.
31
+
32
+ <br />
33
+
34
+ * **🔒 Hardened Security**: Uses **[ES2022 Private Class Fields](https://dev.to/smitterhane/private-class-fields-in-javascript-es2022-3b8)** (`#value`) to keep your data invisible to external scripts and loggers.
35
+
36
+ * **🧼 Auto-Sanitization**: Optionally wipes internal state and freezes the pipe instance upon exit.
37
+
38
+ * **🎯 Zero Side-Effects**: This version does not touch native prototypes or lead to syntax errors, making it 100% safe for production environments and compatible for modern use.
39
+
40
+ * **⚡ High Performance**: Optimized for the V8 engine with a single-allocation design that is friendly to the Garbage Collector.
21
41
 
22
42
  <br />
23
43
 
@@ -50,6 +70,14 @@ yarn add @stless/modify-js
50
70
  pnpm add @stless/modify-js
51
71
  ```
52
72
 
73
+ <br />
74
+
75
+ ### Requirements
76
+
77
+ * **Node.js**: `>=16.11.0` (For stable [ES2022 Private Field](https://dev.to/smitterhane/private-class-fields-in-javascript-es2022-3b8) support)
78
+
79
+ * **Browser**: Modern evergreen browsers (Chrome 91+, Firefox 90+, Safari 15+)
80
+
53
81
  <br />
54
82
  <br />
55
83
 
@@ -59,20 +87,19 @@ pnpm add @stless/modify-js
59
87
 
60
88
  ### Initialize the Environment
61
89
 
62
- `modify-js` follows the [Principle of Least Power](https://en.wikipedia.org/wiki/Rule_of_least_power). It does not patch anything until you tell it to. Use `discover()` to find all global constructors (Array, String, Map, URL, etc.) or pass an explicit list.
90
+ Use `chain_` (or the shorthand `chain$`) to start a pipeline. The data is immediately encapsulated in a private class field.
63
91
 
64
92
  ```javascript
65
93
  // ESM
66
- import applyModify, { discover, __F0V0 } from '@stless/modify-js';
94
+ import chain_ from '@stless/modify-js';
67
95
  // or CJS
68
- const { default: applyModify, discover, __F0V0 } = require('@stless/modify-js');
96
+ const { default: chain_, chain$ } = require('@stless/modify-js');
69
97
 
70
98
 
71
- // Option A: Explicit injection
72
- applyModify([String, Array, Promise]);
73
-
74
- // Option B: Global discovery and silent injection
75
- applyModify(discover(), __F0V0);
99
+ const result = chain_(" hello world ")
100
+ ._p(s => s.trim())
101
+ .$p(s => s.toUpperCase())
102
+ .out(); // "HELLO WORLD"
76
103
  ```
77
104
 
78
105
  <br />
@@ -82,56 +109,55 @@ applyModify(discover(), __F0V0);
82
109
  Transform data from left-to-right. Use `._p()`, `.$p()`, or `.modify()` to chain functions.
83
110
 
84
111
  ```javascript
85
- const result = " hello world "
86
- ._p(s => s.trim())
87
- ._p(s => s.toUpperCase())
88
- ._p(s => s + "!!!")
89
- .out();
90
-
91
- console.log(result); // "HELLO WORLD!!!"
112
+ const user = chain$(apiResponse)
113
+ ._p(res => res.data)
114
+ ._p(data => ({ ...data, timestamp: Date.now() }))
115
+ .out({ error: "No data found" }); // Safe fallback
92
116
  ```
93
117
 
94
118
  <br />
95
119
 
96
- ### Tapping Mid-chain
120
+ ### Tapping & Side Effects
97
121
 
98
- You can also “tap” into the pipe, mid-chain without interruption.
122
+ You can “tap” into the pipe mid-chain for logging or cleanup without breaking the flow.
99
123
 
100
124
  ```javascript
101
- const result = "100"
102
- ._p(s => parseInt(s, 10))
103
- ._p(n => Math.pow(n, 2))
104
- ._p(u => (console.log(u), u)) // console and return the same value
105
- ._p(v => ({ value: v }))
106
- .out();
107
- ```
125
+ const a = chain$(100)
126
+ ._p(n => n * 2)
127
+ // log then return value
128
+ ._p(v => (console.log(`Value is: ${v}`), v));
108
129
 
109
- <br />
130
+ const val1 = a.out(null, false); // set to false
110
131
 
111
- ### Standalone Chaining (No Prototype Pollution)
132
+ // Reuse the same chain
133
+ const val2 = a
134
+ .modify(n => n - 20)
135
+ .out(null, true); // lock the pipe
112
136
 
113
- If you prefer not to touch native prototypes, use `chain_` (or shorthand `chain$`) to wrap values manually.
114
-
115
- ```javascript
116
- import { chain$ } from '@stless/modify-js';
117
-
118
- const val = chain$("data")
119
- .$p(d => d.split(""))
120
- .out();
137
+ console.log(val1); // Output: 200
138
+ console.log(val2); // Output: 180
121
139
  ```
122
140
 
123
141
  <br />
124
142
 
125
- ### Restore the Environment
143
+ ### The “Security Exit”
126
144
 
127
- You can completely remove `modify-js` from the environment at runtime.
145
+ The `.out()` method is the most powerful part of the library. It **handles extraction and cleanup** simultaneously.
128
146
 
129
147
  ```javascript
130
- import { ejectModify } from '@stless/modify-js';
148
+ // Default:
149
+ // .out(fallbackValue, shouldLock=true)
131
150
 
132
- ejectModify({ verbose: true }); // Restores all prototypes to factory state
151
+ const data = pipe.out("Unknown", true);
152
+ console.log(pipe.isLocked()); // Output: true
133
153
  ```
134
154
 
155
+ * **Defaulting**: If the pipe resulted in `null` or `undefined`, it returns your default.
156
+
157
+ * **Wiping**: It clears the internal private state.
158
+
159
+ * **Locking**: It freezes the pipe object, making it impossible to reuse or hijack.
160
+
135
161
  <br />
136
162
 
137
163
  ### Shortcut one-liner
@@ -140,15 +166,13 @@ It is possible to achieve a functional code with almost no `const`, `let`, or `v
140
166
 
141
167
  <br />
142
168
 
143
- **Before:**
169
+ **Before (Imperative):**
144
170
 
145
171
  ```javascript
146
172
  function encryptMsg(algo, data, key, iv) {
147
173
  const cipher = crypto.createCipheriv(algo, key, iv);
148
- const part1 = cipher.update(data);
149
- const part2 = cipher.final();
150
- const encrypted = Buffer.concat([part1, part2]);
151
- key.fill(0);
174
+ const encrypted = Buffer.concat([cipher.update(data), cipher.final()]);
175
+ key.fill(0); // Sensitive cleanup
152
176
  iv.fill(0);
153
177
  return encrypted;
154
178
  }
@@ -156,11 +180,11 @@ function encryptMsg(algo, data, key, iv) {
156
180
 
157
181
  <br />
158
182
 
159
- **After (Modify-JS applied):**
183
+ **After (With Hardened Pipe):**
160
184
 
161
185
  ```javascript
162
186
  function encryptMsg(algo, data, key, iv) {
163
- return [algo, key, iv]
187
+ return chain_([algo, key, iv])
164
188
  ._p(a => crypto.createCipheriv(...a)) // Create cipher
165
189
  ._p(c => Buffer.concat([c.update(data), c.final()])) // Process data
166
190
  ._p(enc => (key.fill(0), iv.fill(0), enc)) // Cleanup & return
@@ -184,64 +208,6 @@ function encryptMsg(algo, data, key, iv) {
184
208
  <br />
185
209
  <br />
186
210
 
187
- ## Advanced Injection Options
188
-
189
- Control how `modify-js` handles collisions with existing methods using pre-defined shorthand constants:
190
-
191
- | Constant | Force | Verbose | Description |
192
- |--------------|--------------|--------------|--------------|
193
- |`__F0V1` | `false` | `true` | **Standard:** Only patch new methods, log collisions. |
194
- |`__F1V1` | `true` | `true` | **Debug:** Overwrite everything and log actions. |
195
- |`__F0V0` | `false` | `false` | **Silent:** Patch if possible, no logs. |
196
- |`__F1V0` | `true` | `false` | **Override:** Overwrite everything silently. |
197
-
198
- <br />
199
- <br />
200
-
201
- ## 🔩 API Reference
202
-
203
- <br />
204
-
205
- ### `applyModify(targets, options)`
206
-
207
- Injects pipeline methods into prototype chains.
208
-
209
- * `targets`: `Array<Function>` - List of constructors (e.g. `[String, Array]`).
210
- * `options`: `ModifyOptions` - `{ force: boolean, verbose: boolean }`.
211
-
212
- <br />
213
-
214
- ### `discover()`
215
-
216
- Reflectively scans `globalThis` for all [PascalCase](https://convertcase.help/blog/what-is-pascalcase/) functions with prototypes. Includes `Buffer` in Node environments.
217
-
218
- <br />
219
-
220
- ### `hasBeenApplied()`
221
-
222
- Returns a `boolean` indicating if the library has been initialized. Useful for conditional logic in environments with [hot-module reloading (HMR)](https://webpack.js.org/guides/hot-module-replacement/) or complex entry points.
223
-
224
- <br />
225
-
226
- ### `getAppliedTargets()`
227
-
228
- Returns an `Array` of all currently patched constructors. This provides full transparency into what native objects have been modified by the library.
229
-
230
- <br />
231
-
232
- ### `chain_(val)` / `chain$(val)`
233
-
234
- Wraps a value in a `ChainBox` for processing without prototype extensions.
235
-
236
- <br />
237
-
238
- ### `ejectModify(options)`
239
-
240
- Reverses all changes made by `applyModify` and resets internal library state.
241
-
242
- <br />
243
- <br />
244
-
245
211
  ---
246
212
 
247
213
  <br />
package/dist/index.min.js CHANGED
@@ -1,9 +1,8 @@
1
1
  /**
2
- * @file Prototype extension utility for functional chaining.
3
- * @summary A user-land polyfill for the proposed TC39 Pipeline Operator.
2
+ * @file Optimized utility for functional chaining and data transformation.
3
+ * @summary Lightweight, high-performance, zero-dependency, hardened polyfill for the TC39 Pipeline Operator.
4
4
  * @author Aries Harbinger
5
5
  * @license Apache-2.0
6
- * @module modify-js
7
6
  */
8
- const appliedTargets=new Set;let isApplied=!1;export const __F0V0={force:!1,verbose:!1};export const __F1V1={force:!0,verbose:!0};export const __F0V1={force:!1,verbose:!0};export const __F1V0={force:!0,verbose:!1};export const hasBeenApplied=()=>isApplied;export const getAppliedTargets=()=>Array.from(appliedTargets);export function discover(){const e=Object.getOwnPropertyNames(globalThis).filter(e=>{try{const o=globalThis[e];return/^[A-Z]/.test(e)&&"function"==typeof o&&o.prototype}catch(e){return!1}}).map(e=>globalThis[e]);return"undefined"==typeof Buffer||e.includes(Buffer)||e.push(Buffer),e}export const chain_=e=>{const o=e=>chain_(e);return Object.freeze({modify:t=>o(t(e)),_p:t=>o(t(e)),$p:t=>o(t(e)),out:o=>null==e?o:e})};export const chain$=chain_;const modifyDefinition=Object.freeze({enumerable:!1,configurable:!0,writable:!0,value:function(e){if("function"!=typeof e)throw new TypeError("Expected a function");return chain_(e(this))}});export function ejectModify({verbose:e=!0}={}){getAppliedTargets().forEach(o=>{try{const e=o.prototype;delete e.modify,delete e._p,delete e.$p,appliedTargets.delete(o)}catch(t){e&&console.warn(`[modify-js] Skipping ${o.name||"Anonymous"}: Unable to eject. Reason: ${t.message}`)}}),appliedTargets.size<=0&&(e&&console.log("[modify-js] Environment fully restored."),isApplied=!1)}export function applyModify(e=[],{force:o=!1,verbose:t=!0}={}){if(!(isApplied&&!o&&e.length<=0)){if(!Array.isArray(e))throw new Error("applyModify requires an array of constructors. Use discover() to get all available.");e.forEach(e=>{if(appliedTargets.has(e)&&!o)return;const r=e?.prototype;if(!r)return void(t&&console.warn(`[modify-js] Skipping ${e}: No prototype available.`));["modify","_p","$p"].forEach(n=>{if(!Object.prototype.hasOwnProperty.call(r,n)||o)try{Object.defineProperty(r,n,modifyDefinition)}catch(o){t&&console.warn(`[modify-js] Skipping ${e.name||"Anonymous"}: Unable to inject .${n}(). Reason: ${o.message}`)}else t&&console.warn(`[modify-js] Skipping ${e.name||"Anonymous"}: .${n}() already exists.`)}),appliedTargets.add(e)}),isApplied=!0}}export default applyModify;
7
+ export class Pipe{#t;constructor(t){this.#t=t}modify(t){return this.#t=t(this.#t),this}_p(t){return this.modify(t)}$p(t){return this.modify(t)}out(t,e=!0){try{return null==this.#t?t:this.#t}finally{e&&(this.#t=null,Object.freeze(this))}}_o(t,e){return this.out(t,e)}$o(t,e){return this.out(t,e)}isLocked(){return Object.isFrozen(this)}_l(){return this.isLocked()}$l(){return this.isLocked()}}export const chain_=t=>new Pipe(t);export const chain$=t=>new Pipe(t);export default chain_;
9
8
  //# sourceMappingURL=index.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"dist/index.min.js.map","names":["appliedTargets","Set","isApplied","__F0V0","force","verbose","__F1V1","__F0V1","__F1V0","hasBeenApplied","getAppliedTargets","Array","from","discover","selection","Object","getOwnPropertyNames","globalThis","filter","name","item","test","prototype","_","map","Buffer","includes","push","chain_","val","inner","v","freeze","modify","fn","_p","$p","out","def","chain$","modifyDefinition","enumerable","configurable","writable","value","TypeError","this","ejectModify","forEach","target","proto","delete","e","console","warn","message","size","log","applyModify","targets","length","isArray","Error","has","methodName","hasOwnProperty","call","defineProperty","add"],"sources":["src/index.js"],"mappings":";;;;;;;AA8BA,MAAMA,eAAiB,IAAIC,IAO3B,IAAIC,WAAY,SAcT,MAAMC,OAAS,CAAEC,OAAO,EAAOC,SAAS,UAMxC,MAAMC,OAAS,CAAEF,OAAO,EAAOC,SAAS,UAMxC,MAAME,OAAS,CAAEH,OAAO,EAAOC,SAAS,UAMxC,MAAMG,OAAS,CAAEJ,OAAO,EAAOC,SAAS,UAQxC,MAAMI,eAAiB,IAAMP,iBAQ7B,MAAMQ,kBAAoB,IAAMC,MAAMC,KAAKZ,uBAS3C,SAASa,WACd,MAAMC,EAAYC,OAAOC,oBAAoBC,YAC1CC,OAAOC,IACN,IACE,MAAMC,EAAOH,WAAWE,GAExB,MAAO,SAASE,KAAKF,IACK,mBAATC,GACPA,EAAKE,SACjB,CAEA,MAAOC,GAAK,OAAO,CAAO,IAE3BC,IAAIL,GAAQF,WAAWE,IAM1B,MAHsB,oBAAXM,QAA2BX,EAAUY,SAASD,SACvDX,EAAUa,KAAKF,QAEVX,CACT,QAwBO,MAAMc,OAAUC,IAErB,MAAMC,EAASC,GAAMH,OAAOG,GAE5B,OAAOhB,OAAOiB,OAAO,CAOnBC,OAASC,GAAOJ,EAAMI,EAAGL,IAQzBM,GAAKD,GAAOJ,EAAMI,EAAGL,IAQrBO,GAAKF,GAAOJ,EAAMI,EAAGL,IAQrBQ,IAAMC,GAAgB,MAAPT,EAAcS,EAAMT,YAShC,MAAMU,OAAgBX,OAe7B,MAAMY,iBAAmBzB,OAAOiB,OAAO,CAErCS,YAAY,EAGZC,cAAc,EAGdC,UAAU,EAOVC,MACS,SAASV,GACd,GAAkB,mBAAPA,EAAmB,MAAM,IAAIW,UAAU,uBAGlD,OAAOjB,OAAOM,EAAGY,MACnB,WAiBG,SAASC,aAAY1C,QAAEA,GAAU,GAAS,CAAC,GAEhCK,oBAERsC,QAAQC,IACd,IACE,MAAMC,EAAQD,EAAO3B,iBAId4B,EAAMjB,cACNiB,EAAMf,UACNe,EAAMd,GAGbpC,eAAemD,OAAOF,EACxB,CAEA,MAAOG,GACD/C,GACFgD,QAAQC,KACN,wBAAwBL,EAAO9B,MAAQ,yCACXiC,EAAEG,UAEpC,IAIEvD,eAAewD,MAAQ,IACrBnD,GACFgD,QAAQI,IAAI,2CACdvD,WAAY,EAEhB,QA0BO,SAASwD,YAAYC,EAAU,IAAIvD,MAAEA,GAAQ,EAAKC,QAAEA,GAAU,GAAS,CAAC,GAE7E,KAAIH,YAAcE,GAASuD,EAAQC,QAAU,GAA7C,CAEA,IAAKjD,MAAMkD,QAAQF,GACjB,MAAM,IAAIG,MAAM,uFAElBH,EAAQX,QAAQC,IAEd,GAAIjD,eAAe+D,IAAId,KAAY7C,EAAO,OAE1C,MAAM8C,EAAQD,GAAQ3B,UAGtB,IAAK4B,EAEH,YADI7C,GAASgD,QAAQC,KAAK,wBAAwBL,+BAIpC,CAAC,SAAU,KAAM,MAEzBD,QAAQgB,IAGd,IAFejD,OAAOO,UAAU2C,eAAeC,KAAKhB,EAAOc,IAE5C5D,EACb,IACEW,OAAOoD,eAAejB,EAAOc,EAAYxB,iBAC3C,CACA,MAAOY,GACD/C,GACFgD,QAAQC,KACN,wBAAwBL,EAAO9B,MAAQ,kCAClB6C,gBAAyBZ,EAAEG,UAEtD,MAESlD,GACTgD,QAAQC,KAAK,wBAAwBL,EAAO9B,MAAQ,iBAAiB6C,yBAIzEhE,eAAeoE,IAAInB,KAGrB/C,WAAY,CA1C0C,CA2CxD,gBAEewD","ignoreList":[]}
1
+ {"version":3,"file":"dist/index.min.js.map","names":["Pipe","value","constructor","val","this","modify","fn","_p","$p","out","def","lock","Object","freeze","_o","$o","isLocked","isFrozen","_l","$l","chain_","chain$"],"sources":["src/index.js"],"mappings":";;;;;;OAkCO,MAAMA,KAMTC,GAMA,WAAAC,CAAYC,GAAOC,MAAKH,EAASE,CAAK,CAUtC,MAAAE,CAAOC,GAEH,OADAF,MAAKH,EAASK,EAAGF,MAAKH,GACfG,IACX,CAOA,EAAAG,CAAGD,GAAM,OAAOF,KAAKC,OAAOC,EAAK,CAOjC,EAAAE,CAAGF,GAAM,OAAOF,KAAKC,OAAOC,EAAK,CAWjC,GAAAG,CAAIC,EAAKC,GAAO,GACZ,IACI,OAAsB,MAAfP,MAAKH,EACNS,EACAN,MAAKH,CAEf,CAAE,QACMU,IACAP,MAAKH,EAAS,KACdW,OAAOC,OAAOT,MAEtB,CACJ,CAQA,EAAAU,CAAGJ,EAAKC,GAAQ,OAAOP,KAAKK,IAAIC,EAAKC,EAAO,CAQ5C,EAAAI,CAAGL,EAAKC,GAAQ,OAAOP,KAAKK,IAAIC,EAAKC,EAAO,CAS5C,QAAAK,GAAa,OAAOJ,OAAOK,SAASb,KAAO,CAG3C,EAAAc,GAAO,OAAOd,KAAKY,UAAY,CAG/B,EAAAG,GAAO,OAAOf,KAAKY,UAAY,SAc5B,MAAMI,OAAUjB,GAAQ,IAAIH,KAAKG,UAQjC,MAAMkB,OAAUlB,GAAQ,IAAIH,KAAKG,kBAEzBiB","ignoreList":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stless/modify-js",
3
- "version": "1.0.0",
4
- "description": "A zero-dependency prototype extension utility for functional chaining and null-safe pipelines.",
3
+ "version": "1.1.0",
4
+ "description": "Lightweight, high-performance, zero-dependency, hardened polyfill for the TC39 Pipeline Operator.",
5
5
  "author": "Aries Harbinger",
6
6
  "license": "Apache-2.0",
7
7
  "homepage": "https://github.com/harnuma9/modify-js#readme",
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "engines": {
31
- "node": ">=20"
31
+ "node": ">=16.11.0"
32
32
  },
33
33
  "scripts": {
34
34
  "build": "bash update.sh",
@@ -39,10 +39,12 @@
39
39
  },
40
40
  "keywords": [
41
41
  "pipeline",
42
+ "lightweight",
42
43
  "functional",
43
44
  "chaining",
44
45
  "utility",
45
- "prototype",
46
+ "polyfill",
47
+ "hardened",
46
48
  "tc39",
47
49
  "fluent-interface"
48
50
  ],
package/src/index.d.ts CHANGED
@@ -1,110 +1,100 @@
1
1
  /**
2
- * Utility: Scans the global environment for valid constructors with prototypes.
3
- * This identifies native objects (Array, String, Map, etc.) available for patching.
4
- * @function discover
5
- * @returns {Array<Function>} A list of discoverable global constructors.
6
- */
7
- export function discover(): Array<Function>;
8
- /**
9
- * Removes .modify() and shorthand pipeline methods from all patched constructors.
10
- * This restores the environment to its original state by deleting the injected
11
- * prototype properties.
12
- * @function ejectModify
13
- * @memberof module:modify-js
14
- * @param {ModifyOptions} [options={}] - Configuration for the ejection process.
15
- * @param {boolean} [options.verbose=true] - If true, logs restoration status and warnings.
16
- * @returns {void}
17
- * @example
18
- * ejectModify({ verbose: false }); // Clean up the prototypes
19
- */
20
- export function ejectModify({ verbose }?: ModifyOptions): void;
21
- /**
22
- * Injects .modify() and shorthands into specified prototype chains.
23
- * Follows the Principle of Least Power; it does not patch globally
24
- * unless explicitly passed the results of {@link module:modify-js.discover}.
25
- * These methods return a {@link ChainBox} to ensure subsequent calls are null-safe.
2
+ * Copyright 2026 Aries Harbinger
26
3
  *
27
- * @function applyModify
28
- * @example
29
- * // Manual explicit injection
30
- * applyModify([Array, String, Boolean]);
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
31
7
  *
32
- * @example
33
- * // Intentional global injection
34
- * applyModify(discover(), __F0V0);
8
+ * http://www.apache.org/licenses/LICENSE-2.0
35
9
  *
36
- * @example
37
- * // Usage in code
38
- * "hello"._p(s => s.toUpperCase()).modify(s => s + "!!!").out(); // "HELLO!!!"
39
- *
40
- * @param {Array<Function>} [targets=[]] - An array of constructors to patch.
41
- * @param {ModifyOptions} [options={}] - Configuration for injection behavior.
42
- * @returns {void}
43
- */
44
- export function applyModify(targets?: Array<Function>, { force, verbose }?: ModifyOptions): void;
45
- /**
46
- * Configuration options for injection behavior.
47
- * @typedef {Object} ModifyOptions
48
- * @property {boolean} [force=false] - If true, overwrites existing .modify() or shorthand methods.
49
- * @property {boolean} [verbose=true] - If true, logs warnings when collisions occur.
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
50
15
  */
51
16
  /**
52
- * Shorthand: Force=False, Verbose=False (Silent Mode)
53
- * @type {ModifyOptions}
17
+ * @file Optimized utility for functional chaining and data transformation.
18
+ * @summary Lightweight, high-performance, zero-dependency, hardened polyfill for the TC39 Pipeline Operator.
19
+ * @author Aries Harbinger
20
+ * @license Apache-2.0
54
21
  */
55
- export const __F0V0: ModifyOptions;
56
22
  /**
57
- * Shorthand: Force=True, Verbose=True (Debug/Aggressive Mode)
58
- * @type {ModifyOptions}
23
+ * A transformation function used within the pipeline.
24
+ * @callback ModifyCallback
25
+ * @param {any} value - The current value in the pipeline.
26
+ * @returns {any} The transformed value.
59
27
  */
60
- export const __F1V1: ModifyOptions;
61
28
  /**
62
- * Shorthand: Force=False, Verbose=True (Standard Mode)
63
- * @type {ModifyOptions}
29
+ * Represents a secure, stateful container for functional transformations.
30
+ * @class Pipe
64
31
  */
65
- export const __F0V1: ModifyOptions;
66
- /**
67
- * Shorthand: Force=True, Verbose=False (Override Mode)
68
- * @type {ModifyOptions}
69
- */
70
- export const __F1V0: ModifyOptions;
71
- export function hasBeenApplied(): boolean;
72
- export function getAppliedTargets(): Array<Function>;
73
- export function chain_(val: any): ChainBox;
74
- export function chain$(val: any): ChainBox;
75
- export default applyModify;
76
- /**
77
- * Configuration options for injection behavior.
78
- */
79
- export type ModifyOptions = {
32
+ export class Pipe {
33
+ /**
34
+ * Creates an instance of Pipe.
35
+ * @param {any} val - The initial value to wrap.
36
+ */
37
+ constructor(val: any);
38
+ /**
39
+ * Passes the internal value through a transformation function.
40
+ * Updates the internal state and returns the same Pipe instance.
41
+ * @method modify
42
+ * @param {ModifyCallback} fn - The transformation function to execute.
43
+ * @returns {this} The current Pipe instance for further chaining.
44
+ */
45
+ modify(fn: ModifyCallback): this;
80
46
  /**
81
- * - If true, overwrites existing .modify() or shorthand methods.
47
+ * Alias for {@link modify}.
48
+ * @param {ModifyCallback} fn
49
+ * @returns {this}
82
50
  */
83
- force?: boolean | undefined;
51
+ _p(fn: ModifyCallback): this;
84
52
  /**
85
- * - If true, logs warnings when collisions occur.
53
+ * Alias for {@link modify}.
54
+ * @param {ModifyCallback} fn
55
+ * @returns {this}
86
56
  */
87
- verbose?: boolean | undefined;
88
- };
89
- export type ChainBox = {
57
+ $p(fn: ModifyCallback): this;
90
58
  /**
91
- * - Shorthand pipeline method.
59
+ * Extracts the value from the pipe and terminates the chain.
60
+ * If the internal value is null or undefined, the provided default is returned.
61
+ * @method out
62
+ * @param {any} [def] - Optional fallback value if the pipe's value is null/undefined.
63
+ * @param {boolean} [lock=true] - If true, wipes the internal state and freezes the object.
64
+ * @returns {any} The final transformed value or the default fallback.
92
65
  */
93
- _p: (arg0: ModifyCallback) => ChainBox;
66
+ out(def?: any, lock?: boolean): any;
94
67
  /**
95
- * - Shorthand pipeline method.
68
+ * Alias for {@link out}.
69
+ * @param {any} [def]
70
+ * @param {boolean} [lock=true]
71
+ * @returns {any}
96
72
  */
97
- $p: (arg0: ModifyCallback) => ChainBox;
73
+ _o(def?: any, lock?: boolean): any;
98
74
  /**
99
- * - Explicit pipeline method.
75
+ * Alias for {@link out}.
76
+ * @param {any} [def]
77
+ * @param {boolean} [lock=true]
78
+ * @returns {any}
100
79
  */
101
- modify: (arg0: ModifyCallback) => ChainBox;
80
+ $o(def?: any, lock?: boolean): any;
102
81
  /**
103
- * - Unwraps the value and exits the chain.
82
+ * Checks if the pipe instance has been locked and frozen.
83
+ * A locked pipe can no longer be modified or used to extract values.
84
+ * @method isLocked
85
+ * @returns {boolean} True if the instance is frozen, false otherwise.
104
86
  */
105
- out: (arg0: any | undefined) => any;
106
- };
87
+ isLocked(): boolean;
88
+ /** Alias for {@link isLocked} */
89
+ _l(): boolean;
90
+ /** Alias for {@link isLocked} */
91
+ $l(): boolean;
92
+ #private;
93
+ }
94
+ export function chain_(val: any): Pipe;
95
+ export function chain$(val: any): Pipe;
96
+ export default chain_;
107
97
  /**
108
- * The transformation callback passed to .modify() or shorthands.
98
+ * A transformation function used within the pipeline.
109
99
  */
110
- export type ModifyCallback = (instance: any) => any;
100
+ export type ModifyCallback = (value: any) => any;
package/src/index.js CHANGED
@@ -15,328 +15,138 @@
15
15
  */
16
16
 
17
17
  /**
18
- * @file Prototype extension utility for functional chaining.
19
- * @summary A user-land polyfill for the proposed TC39 Pipeline Operator.
18
+ * @file Optimized utility for functional chaining and data transformation.
19
+ * @summary Lightweight, high-performance, zero-dependency, hardened polyfill for the TC39 Pipeline Operator.
20
20
  * @author Aries Harbinger
21
21
  * @license Apache-2.0
22
- * @module modify-js
23
22
  */
24
23
 
25
-
26
- /**
27
- * Internal state to track patched constructors.
28
- * @type {Set<Function>}
29
- * @private
30
- */
31
- const appliedTargets = new Set();
32
-
33
- /**
34
- * Internal state to track if the utility has been initialized at least once.
35
- * @type {boolean}
36
- * @private
37
- */
38
- let isApplied = false;
39
-
40
-
41
- /**
42
- * Configuration options for injection behavior.
43
- * @typedef {Object} ModifyOptions
44
- * @property {boolean} [force=false] - If true, overwrites existing .modify() or shorthand methods.
45
- * @property {boolean} [verbose=true] - If true, logs warnings when collisions occur.
46
- */
47
-
48
- /**
49
- * Shorthand: Force=False, Verbose=False (Silent Mode)
50
- * @type {ModifyOptions}
51
- */
52
- export const __F0V0 = { force: false, verbose: false };
53
-
54
- /**
55
- * Shorthand: Force=True, Verbose=True (Debug/Aggressive Mode)
56
- * @type {ModifyOptions}
57
- */
58
- export const __F1V1 = { force: true, verbose: true };
59
-
60
- /**
61
- * Shorthand: Force=False, Verbose=True (Standard Mode)
62
- * @type {ModifyOptions}
63
- */
64
- export const __F0V1 = { force: false, verbose: true };
65
-
66
- /**
67
- * Shorthand: Force=True, Verbose=False (Override Mode)
68
- * @type {ModifyOptions}
69
- */
70
- export const __F1V0 = { force: true, verbose: false };
71
-
72
-
73
- /**
74
- * Utility: Checks if the library has been executed at least once.
75
- * @function hasBeenApplied
76
- * @returns {boolean} True if applyModify has been called successfully.
77
- */
78
- export const hasBeenApplied = () => isApplied;
79
-
80
-
81
24
  /**
82
- * Utility: Returns an array of all currently patched constructors.
83
- * @function getAppliedTargets
84
- * @returns {Array<Function>} An array containing the constructors currently modified.
25
+ * A transformation function used within the pipeline.
26
+ * @callback ModifyCallback
27
+ * @param {any} value - The current value in the pipeline.
28
+ * @returns {any} The transformed value.
85
29
  */
86
- export const getAppliedTargets = () => Array.from(appliedTargets);
87
-
88
30
 
89
31
  /**
90
- * Utility: Scans the global environment for valid constructors with prototypes.
91
- * This identifies native objects (Array, String, Map, etc.) available for patching.
92
- * @function discover
93
- * @returns {Array<Function>} A list of discoverable global constructors.
32
+ * Represents a secure, stateful container for functional transformations.
33
+ * @class Pipe
94
34
  */
95
- export function discover() {
96
- const selection = Object.getOwnPropertyNames(globalThis)
97
- .filter(name => {
98
- try {
99
- const item = globalThis[name];
100
- // Filter for PascalCase functions that have a prototype (standard JS classes)
101
- return /^[A-Z]/.test(name)
102
- && typeof item === 'function'
103
- && item.prototype;
104
- }
105
-
106
- catch (_) { return false; }
107
- })
108
- .map(name => globalThis[name]);
109
-
110
- // Include Buffer if in a Node-like environment
111
- if (typeof Buffer !== 'undefined' && !selection.includes(Buffer))
112
- selection.push(Buffer);
113
-
114
- return selection;
115
- }
116
-
35
+ export class Pipe {
36
+ /**
37
+ * The internal value held by the pipe.
38
+ * @type {any}
39
+ * @private
40
+ */
41
+ #value;
117
42
 
118
- /**
119
- * @typedef {Object} ChainBox
120
- * @property {function(ModifyCallback): ChainBox} _p - Shorthand pipeline method.
121
- * @property {function(ModifyCallback): ChainBox} $p - Shorthand pipeline method.
122
- * @property {function(ModifyCallback): ChainBox} modify - Explicit pipeline method.
123
- * @property {function(any=): any} out - Unwraps the value and exits the chain.
124
- */
43
+ /**
44
+ * Creates an instance of Pipe.
45
+ * @param {any} val - The initial value to wrap.
46
+ */
47
+ constructor(val) { this.#value = val; }
125
48
 
126
- /**
127
- * A safe wrapper for functional chaining that handles null/undefined values.
128
- * Once inside a chain_, all subsequent calls are null-safe.
129
- * @function chain_
130
- * @param {any} val - The initial value to wrap.
131
- * @returns {ChainBox} A chainable object containing transformation methods.
132
- * @example
133
- * const result = chain_(" hello ")
134
- * .$p(s => s.trim())
135
- * .$p(s => null) // Pipeline continues safely
136
- * .$p(s => console.log(s)) // Output mid-chain
137
- * .out("DEFAULT"); // Returns "DEFAULT"
138
- */
139
- export const chain_ = (val) => {
140
- /** @private */
141
- const inner = (v) => chain_(v);
142
49
 
143
- return Object.freeze({
144
50
  /**
51
+ * Passes the internal value through a transformation function.
52
+ * Updates the internal state and returns the same Pipe instance.
145
53
  * @method modify
146
- * @memberof module:modify-js.ChainBox
147
- * @param {ModifyCallback} fn - Transformation function.
148
- * @returns {ChainBox}
54
+ * @param {ModifyCallback} fn - The transformation function to execute.
55
+ * @returns {this} The current Pipe instance for further chaining.
149
56
  */
150
- modify: (fn) => inner(fn(val)),
57
+ modify(fn) {
58
+ this.#value = fn(this.#value);
59
+ return this;
60
+ }
151
61
 
152
62
  /**
153
- * @method _p
154
- * @memberof module:modify-js.ChainBox
155
- * @param {ModifyCallback} fn - Transformation function.
156
- * @returns {ChainBox}
63
+ * Alias for {@link modify}.
64
+ * @param {ModifyCallback} fn
65
+ * @returns {this}
157
66
  */
158
- _p: (fn) => inner(fn(val)),
67
+ _p(fn) { return this.modify(fn); }
159
68
 
160
69
  /**
161
- * @method $p
162
- * @memberof module:modify-js.ChainBox
163
- * @param {ModifyCallback} fn - Transformation function.
164
- * @returns {ChainBox}
70
+ * Alias for {@link modify}.
71
+ * @param {ModifyCallback} fn
72
+ * @returns {this}
165
73
  */
166
- $p: (fn) => inner(fn(val)),
74
+ $p(fn) { return this.modify(fn); }
75
+
167
76
 
168
77
  /**
78
+ * Extracts the value from the pipe and terminates the chain.
79
+ * If the internal value is null or undefined, the provided default is returned.
169
80
  * @method out
170
- * @memberof module:modify-js.ChainBox
171
- * @param {any} [def] - Optional fallback value if the result is null/undefined.
172
- * @returns {any}
81
+ * @param {any} [def] - Optional fallback value if the pipe's value is null/undefined.
82
+ * @param {boolean} [lock=true] - If true, wipes the internal state and freezes the object.
83
+ * @returns {any} The final transformed value or the default fallback.
173
84
  */
174
- out: (def) => (val == null ? def : val)
175
- });
176
- };
177
-
178
- /**
179
- * A shorthand method for chain_ function.
180
- * @function chain$
181
- * @returns {ChainBox} A chainable object containing transformation methods.
182
- */
183
- export const chain$ = (() => chain_)();
184
-
185
-
186
- /**
187
- * The transformation callback passed to .modify() or shorthands.
188
- * @callback ModifyCallback
189
- * @param {any} instance - The object instance the method was called upon.
190
- * @returns {any} - The result of the transformation.
191
- */
192
-
193
- /**
194
- * Property descriptor for the utility methods.
195
- * @type {PropertyDescriptor}
196
- * @private
197
- */
198
- const modifyDefinition = Object.freeze({
199
- // This prevents the new methods from breaking other libraries that loop over objects.
200
- enumerable: false,
201
-
202
- // Allows the property to be deleted or changed later.
203
- configurable: true,
204
-
205
- // Allows the value (the function itself) to be changed using an assignment operator (=).
206
- writable: true,
207
-
208
- /**
209
- * @param {ModifyCallback} fn - The transformation function.
210
- * @throws {TypeError} If fn is not a function.
211
- * @returns {any} The result of fn(this).
212
- */
213
- value: (function () {
214
- return function(fn) {
215
- if (typeof fn !== 'function') throw new TypeError('Expected a function');
216
- // Once the user uses .modify() or shorthands, they get a chain_ back.
217
- // This may "solve" the friction of mid-chain null values.
218
- return chain_(fn(this));
85
+ out(def, lock = true) {
86
+ try {
87
+ return this.#value == null
88
+ ? def
89
+ : this.#value;
90
+
91
+ } finally {
92
+ if (lock) {
93
+ this.#value = null;
94
+ Object.freeze(this);
95
+ }
96
+ }
219
97
  }
220
- })()
221
- });
222
-
223
98
 
224
- /**
225
- * Removes .modify() and shorthand pipeline methods from all patched constructors.
226
- * This restores the environment to its original state by deleting the injected
227
- * prototype properties.
228
- * @function ejectModify
229
- * @memberof module:modify-js
230
- * @param {ModifyOptions} [options={}] - Configuration for the ejection process.
231
- * @param {boolean} [options.verbose=true] - If true, logs restoration status and warnings.
232
- * @returns {void}
233
- * @example
234
- * ejectModify({ verbose: false }); // Clean up the prototypes
235
- */
236
- export function ejectModify({ verbose = true } = {}) {
237
- /** @type {Array<Function>} */
238
- const targets = getAppliedTargets();
99
+ /**
100
+ * Alias for {@link out}.
101
+ * @param {any} [def]
102
+ * @param {boolean} [lock=true]
103
+ * @returns {any}
104
+ */
105
+ _o(def, lock) { return this.out(def, lock); }
239
106
 
240
- targets.forEach(target => {
241
- try {
242
- const proto = target.prototype;
107
+ /**
108
+ * Alias for {@link out}.
109
+ * @param {any} [def]
110
+ * @param {boolean} [lock=true]
111
+ * @returns {any}
112
+ */
113
+ $o(def, lock) { return this.out(def, lock); }
243
114
 
244
- // Remove the methods from the prototype chain
245
- // These correspond to the methods defined in applyModify
246
- delete proto.modify;
247
- delete proto._p;
248
- delete proto.$p;
249
115
 
250
- // Remove specifically this target from the tracking Set
251
- appliedTargets.delete(target);
252
- }
116
+ /**
117
+ * Checks if the pipe instance has been locked and frozen.
118
+ * A locked pipe can no longer be modified or used to extract values.
119
+ * @method isLocked
120
+ * @returns {boolean} True if the instance is frozen, false otherwise.
121
+ */
122
+ isLocked() { return Object.isFrozen(this); }
253
123
 
254
- catch (e) {
255
- if (verbose)
256
- console.warn(
257
- `[modify-js] Skipping ${target.name || 'Anonymous'}: ` +
258
- `Unable to eject. Reason: ${e.message}`
259
- );
260
- }
261
- });
124
+ /** Alias for {@link isLocked} */
125
+ _l() { return this.isLocked(); }
262
126
 
263
- // Final reset of global internal state
264
- if (appliedTargets.size <= 0) {
265
- if (verbose)
266
- console.log("[modify-js] Environment fully restored.");
267
- isApplied = false;
268
- }
127
+ /** Alias for {@link isLocked} */
128
+ $l() { return this.isLocked(); }
269
129
  }
270
130
 
271
-
272
131
  /**
273
- * Injects .modify() and shorthands into specified prototype chains.
274
- * Follows the Principle of Least Power; it does not patch globally
275
- * unless explicitly passed the results of {@link module:modify-js.discover}.
276
- * These methods return a {@link ChainBox} to ensure subsequent calls are null-safe.
277
- *
278
- * @function applyModify
279
- * @example
280
- * // Manual explicit injection
281
- * applyModify([Array, String, Boolean]);
282
- *
283
- * @example
284
- * // Intentional global injection
285
- * applyModify(discover(), __F0V0);
286
- *
132
+ * Entry point to create a new Pipe instance.
133
+ * @function chain_
134
+ * @param {any} val - The value to start the pipeline with.
135
+ * @returns {Pipe} A new Pipe container.
287
136
  * @example
288
- * // Usage in code
289
- * "hello"._p(s => s.toUpperCase()).modify(s => s + "!!!").out(); // "HELLO!!!"
290
- *
291
- * @param {Array<Function>} [targets=[]] - An array of constructors to patch.
292
- * @param {ModifyOptions} [options={}] - Configuration for injection behavior.
293
- * @returns {void}
137
+ * const result = chain_(" hello ")
138
+ * ._p(s => s.trim())
139
+ * ._p(s => s.toUpperCase())
140
+ * .out(); // "HELLO"
294
141
  */
295
- export function applyModify(targets = [], { force = false, verbose = true } = {}) {
296
- // Exit early if no work is requested and library was already used
297
- if (isApplied && !force && targets.length <= 0) return;
298
-
299
- if (!Array.isArray(targets))
300
- throw new Error('applyModify requires an array of constructors. Use discover() to get all available.');
301
-
302
- targets.forEach(target => {
303
- // Idempotency check
304
- if (appliedTargets.has(target) && !force) return;
305
-
306
- const proto = target?.prototype;
307
-
308
- // Validate target has a prototype to extend
309
- if (!proto) {
310
- if (verbose) console.warn(`[modify-js] Skipping ${target}: No prototype available.`);
311
- return;
312
- }
313
-
314
- const methods = ['modify', '_p', '$p'];
142
+ export const chain_ = (val) => new Pipe(val);
315
143
 
316
- methods.forEach(methodName => {
317
- const exists = Object.prototype.hasOwnProperty.call(proto, methodName);
318
-
319
- if (!exists || force) {
320
- try {
321
- Object.defineProperty(proto, methodName, modifyDefinition);
322
- }
323
- catch (e) {
324
- if (verbose)
325
- console.warn(
326
- `[modify-js] Skipping ${target.name || 'Anonymous'}: ` +
327
- `Unable to inject .${methodName}(). Reason: ${e.message}`
328
- );
329
- }
330
-
331
- } else if (verbose) {
332
- console.warn(`[modify-js] Skipping ${target.name || 'Anonymous'}: .${methodName}() already exists.`);
333
- }
334
- });
335
-
336
- appliedTargets.add(target);
337
- });
338
-
339
- isApplied = true;
340
- }
144
+ /**
145
+ * Shorthand alias for {@link chain_}.
146
+ * @function chain$
147
+ * @param {any} val
148
+ * @returns {Pipe}
149
+ */
150
+ export const chain$ = (val) => new Pipe(val);
341
151
 
342
- export default applyModify;
152
+ export default chain_;