+20
| # Changelog | ||
| All notable changes to easy-stack are documented here. | ||
| ## 2.0.0 — 2026-08-16 | ||
| - Add native ESM, CommonJS, modern classic-browser, and ES5 entry points with explicit package exports. | ||
| - Preserve synchronous cooperative LIFO execution, callback `this` binding, and the established `require('easy-stack')` path. | ||
| - Make `stack`, `contents()`, `size`, and read-only `running` consistent across every shipped build. | ||
| - Validate callback batches and replacement arrays before mutation. | ||
| - Return the stack from `add()` for chaining and return the live empty array from `clear()`. | ||
| - Recover the runner after a callback throws while rethrowing the original error. | ||
| - Repair the legacy browser build's CommonJS resolution and inherited-argument enqueue defects. | ||
| - Add shared Node and Chrome contract tests powered by `vanilla-test@2.1.1` with 100% coverage gates. | ||
| - Prove the dependency-free runtime on Node 12.22.12 and the test toolchain on Node 22.12 and 24. | ||
| - Add packed-package verification, a curated nine-page GitHub Pages site, and CI-gated deployment. | ||
| ## 1.0.1 — 2020-11-11 | ||
| - Update MIT license metadata and package version. |
Sorry, the diff of this file is not supported yet
+64
| # Migrating from easy-stack 1.x to 2.0 | ||
| Version 2 keeps the package's central behavior: callbacks execute synchronously in last-in, first-out order, each callback receives the stack as `this`, and a callback calls `this.next()` when the following item should run. | ||
| ## Runtime and imports | ||
| The shipped runtime supports Node.js 12.22 and newer. The `vanilla-test` development toolchain requires Node.js 22.12 or newer, but it is not a runtime dependency. | ||
| CommonJS remains available: | ||
| ```javascript | ||
| const Stack = require('easy-stack'); | ||
| ``` | ||
| Native ESM is now a first-class entry point: | ||
| ```javascript | ||
| import Stack, { Stack as NamedStack } from 'easy-stack'; | ||
| ``` | ||
| The compatibility paths `easy-stack/stack.js` and `easy-stack/es5.js` remain exported. For browsers, `stack-vanilla.js` provides the modern classic-script global and `es5.js` provides the legacy-syntax global. | ||
| ## Contents and state | ||
| `stack.stack` is the live callback array. `stack.contents()` remains the package-root-compatible method: | ||
| ```javascript | ||
| const pending = stack.stack; | ||
| stack.stack = [firstTask, secondTask]; | ||
| stack.contents(); // current live array | ||
| stack.contents([firstTask]); // validate and replace the live array | ||
| ``` | ||
| The old browser file exposed `contents` as an array property while the package root exposed it as a method. Version 2 resolves that mismatch in favor of the established package-root method across all builds. Browser code that used `stack.contents` as an array should move to `stack.stack`. | ||
| `running` is now a read-only view of the runner's internal state. `autoRun` and `stop` remain public truthy/falsy control fields for compatibility. | ||
| ## Validation and errors | ||
| `add()` and replacement arrays now accept functions only. Invalid batches fail before any item is added. Direct mutation remains possible through the live `stack` array, but `next()` rejects a nonfunction before invoking it. | ||
| When a callback throws, the same error still reaches the caller. The runner now also returns to an idle state, allowing a later `add()` or `next()` call to recover. | ||
| ## Return values | ||
| `add()` now returns the stack for chaining. `clear()` now returns the new live empty array. `next()` continues to ignore callback return values and returns `undefined`. | ||
| ## What intentionally did not change | ||
| - Execution is synchronous. | ||
| - The newest callback runs first. | ||
| - A callback must call `this.next()` to continue. | ||
| - Changing `stop` or `autoRun` does not automatically resume pending work; call `next()` explicitly. | ||
| - The live callback array remains intentionally mutable for advanced scheduling. | ||
| ## Upgrade checklist | ||
| 1. Keep existing `require('easy-stack')` imports or move to the native ESM default export. | ||
| 2. Replace browser code that treated `contents` as an array with the `stack` property. | ||
| 3. Ensure every enqueued value is a function. | ||
| 4. Check code that depended on `add()` returning `undefined`. | ||
| 5. Remove direct assignments to `stack.running`; it is now a read-only status view. | ||
| 6. Run the suite on the oldest Node version your application supports. |
| (function exposeStack(global) { | ||
| 'use strict'; | ||
| const stacks = new WeakMap(); | ||
| const states = new WeakMap(); | ||
| function validateTasks(tasks) { | ||
| const invalidIndex = tasks.findIndex((task) => typeof task !== 'function'); | ||
| if (invalidIndex !== -1) { | ||
| throw new TypeError(`Stack task at index ${invalidIndex} must be a function.`); | ||
| } | ||
| } | ||
| class Stack { | ||
| constructor() { | ||
| stacks.set(this, []); | ||
| states.set(this, { running: false }); | ||
| this.autoRun = true; | ||
| this.stop = false; | ||
| } | ||
| add(...tasks) { | ||
| validateTasks(tasks); | ||
| stacks.get(this).push(...tasks); | ||
| if (!this.running && !this.stop && this.autoRun) { | ||
| this.next(); | ||
| } | ||
| return this; | ||
| } | ||
| next() { | ||
| const stack = stacks.get(this); | ||
| const state = states.get(this); | ||
| if (this.stop || stack.length === 0) { | ||
| state.running = false; | ||
| return; | ||
| } | ||
| state.running = true; | ||
| const task = stack.pop(); | ||
| if (typeof task !== 'function') { | ||
| state.running = false; | ||
| throw new TypeError('The next Stack task must be a function.'); | ||
| } | ||
| try { | ||
| task.call(this); | ||
| } catch (error) { | ||
| state.running = false; | ||
| throw error; | ||
| } | ||
| } | ||
| clear() { | ||
| const stack = []; | ||
| stacks.set(this, stack); | ||
| return stack; | ||
| } | ||
| contents(tasks) { | ||
| if (arguments.length > 0) { | ||
| this.stack = tasks; | ||
| } | ||
| return stacks.get(this); | ||
| } | ||
| get stack() { | ||
| return stacks.get(this); | ||
| } | ||
| set stack(tasks) { | ||
| if (!Array.isArray(tasks)) { | ||
| throw new TypeError('Stack contents must be an array of functions.'); | ||
| } | ||
| validateTasks(tasks); | ||
| stacks.set(this, tasks); | ||
| } | ||
| get running() { | ||
| return states.get(this).running; | ||
| } | ||
| get size() { | ||
| return stacks.get(this).length; | ||
| } | ||
| } | ||
| global.Stack = Stack; | ||
| }(typeof globalThis === 'undefined' ? this : globalThis)); |
+95
| 'use strict'; | ||
| const stacks = new WeakMap(); | ||
| const states = new WeakMap(); | ||
| function validateTasks(tasks) { | ||
| const invalidIndex = tasks.findIndex((task) => typeof task !== 'function'); | ||
| if (invalidIndex !== -1) { | ||
| throw new TypeError(`Stack task at index ${invalidIndex} must be a function.`); | ||
| } | ||
| } | ||
| class Stack { | ||
| constructor() { | ||
| stacks.set(this, []); | ||
| states.set(this, { running: false }); | ||
| this.autoRun = true; | ||
| this.stop = false; | ||
| } | ||
| add(...tasks) { | ||
| validateTasks(tasks); | ||
| stacks.get(this).push(...tasks); | ||
| if (!this.running && !this.stop && this.autoRun) { | ||
| this.next(); | ||
| } | ||
| return this; | ||
| } | ||
| next() { | ||
| const stack = stacks.get(this); | ||
| const state = states.get(this); | ||
| if (this.stop || stack.length === 0) { | ||
| state.running = false; | ||
| return; | ||
| } | ||
| state.running = true; | ||
| const task = stack.pop(); | ||
| if (typeof task !== 'function') { | ||
| state.running = false; | ||
| throw new TypeError('The next Stack task must be a function.'); | ||
| } | ||
| try { | ||
| task.call(this); | ||
| } catch (error) { | ||
| state.running = false; | ||
| throw error; | ||
| } | ||
| } | ||
| clear() { | ||
| const stack = []; | ||
| stacks.set(this, stack); | ||
| return stack; | ||
| } | ||
| contents(tasks) { | ||
| if (arguments.length > 0) { | ||
| this.stack = tasks; | ||
| } | ||
| return stacks.get(this); | ||
| } | ||
| get stack() { | ||
| return stacks.get(this); | ||
| } | ||
| set stack(tasks) { | ||
| if (!Array.isArray(tasks)) { | ||
| throw new TypeError('Stack contents must be an array of functions.'); | ||
| } | ||
| validateTasks(tasks); | ||
| stacks.set(this, tasks); | ||
| } | ||
| get running() { | ||
| return states.get(this).running; | ||
| } | ||
| get size() { | ||
| return stacks.get(this).length; | ||
| } | ||
| } | ||
| module.exports = Stack; | ||
| module.exports.Stack = Stack; |
+123
-64
@@ -1,74 +0,133 @@ | ||
| function Stack(){ | ||
| Object.defineProperties( | ||
| this, | ||
| { | ||
| add:{ | ||
| enumerable:true, | ||
| writable:false, | ||
| value:addToStack | ||
| }, | ||
| next:{ | ||
| enumerable:true, | ||
| writable:false, | ||
| value:run | ||
| }, | ||
| clear:{ | ||
| enumerable:true, | ||
| writable:false, | ||
| value:clearStack | ||
| }, | ||
| contents:{ | ||
| enumerable:false, | ||
| get:getStack, | ||
| set:setStack | ||
| }, | ||
| autoRun:{ | ||
| enumerable:true, | ||
| writable:true, | ||
| value:true | ||
| }, | ||
| stop:{ | ||
| enumerable:true, | ||
| writable:true, | ||
| value:false | ||
| } | ||
| } | ||
| ); | ||
| (function exposeStack(global) { | ||
| 'use strict'; | ||
| var stack=[]; | ||
| var running=false; | ||
| var stop=false; | ||
| function validateTasks(tasks) { | ||
| var index; | ||
| function clearStack(){ | ||
| stack=[]; | ||
| return stack; | ||
| } | ||
| if (!Array.isArray(tasks)) { | ||
| throw new TypeError('Stack contents must be an array of functions.'); | ||
| } | ||
| function getStack(){ | ||
| return stack; | ||
| for (index = 0; index < tasks.length; index += 1) { | ||
| if (typeof tasks[index] !== 'function') { | ||
| throw new TypeError('Stack task at index ' + index + ' must be a function.'); | ||
| } | ||
| } | ||
| } | ||
| function setStack(val){ | ||
| stack=val; | ||
| return stack; | ||
| } | ||
| function Stack() { | ||
| var callbacks = []; | ||
| var running = false; | ||
| var self; | ||
| function addToStack(){ | ||
| for(var i in arguments){ | ||
| stack.unshift(arguments[i]); | ||
| if (!(this instanceof Stack)) { | ||
| return new Stack(); | ||
| } | ||
| if(!running && !this.stop && this.autoRun){ | ||
| this.next(); | ||
| } | ||
| } | ||
| function run(){ | ||
| running=true; | ||
| if(stack.length<1 || this.stop){ | ||
| running=false; | ||
| return; | ||
| } | ||
| self = this; | ||
| this.autoRun = true; | ||
| this.stop = false; | ||
| stack.shift().bind(this)(); | ||
| Object.defineProperties(this, { | ||
| add: { | ||
| enumerable: true, | ||
| writable: false, | ||
| value: function add() { | ||
| var tasks = []; | ||
| var index; | ||
| for (index = 0; index < arguments.length; index += 1) { | ||
| tasks.push(arguments[index]); | ||
| } | ||
| validateTasks(tasks); | ||
| for (index = 0; index < tasks.length; index += 1) { | ||
| callbacks.push(tasks[index]); | ||
| } | ||
| if (!running && !self.stop && self.autoRun) { | ||
| self.next(); | ||
| } | ||
| return self; | ||
| } | ||
| }, | ||
| next: { | ||
| enumerable: true, | ||
| writable: false, | ||
| value: function next() { | ||
| var task; | ||
| if (self.stop || callbacks.length === 0) { | ||
| running = false; | ||
| return; | ||
| } | ||
| running = true; | ||
| task = callbacks.pop(); | ||
| if (typeof task !== 'function') { | ||
| running = false; | ||
| throw new TypeError('The next Stack task must be a function.'); | ||
| } | ||
| try { | ||
| task.call(self); | ||
| } catch (error) { | ||
| running = false; | ||
| throw error; | ||
| } | ||
| } | ||
| }, | ||
| clear: { | ||
| enumerable: true, | ||
| writable: false, | ||
| value: function clear() { | ||
| callbacks = []; | ||
| return callbacks; | ||
| } | ||
| }, | ||
| contents: { | ||
| enumerable: true, | ||
| writable: false, | ||
| value: function contents(tasks) { | ||
| if (arguments.length > 0) { | ||
| self.stack = tasks; | ||
| } | ||
| return callbacks; | ||
| } | ||
| }, | ||
| stack: { | ||
| enumerable: true, | ||
| get: function getStack() { | ||
| return callbacks; | ||
| }, | ||
| set: function setStack(tasks) { | ||
| validateTasks(tasks); | ||
| callbacks = tasks; | ||
| } | ||
| }, | ||
| running: { | ||
| enumerable: true, | ||
| get: function getRunning() { | ||
| return running; | ||
| } | ||
| }, | ||
| size: { | ||
| enumerable: true, | ||
| get: function getSize() { | ||
| return callbacks.length; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| } | ||
| if (typeof module === 'object' && module.exports) { | ||
| module.exports = Stack; | ||
| module.exports.Stack = Stack; | ||
| } else { | ||
| global.Stack = Stack; | ||
| } | ||
| }(typeof globalThis === 'undefined' ? this : globalThis)); |
+64
-16
| { | ||
| "name": "easy-stack", | ||
| "version": "1.0.1", | ||
| "description": "Simple JS stack with auto run for node and browsers", | ||
| "main": "stack.js", | ||
| "scripts": { | ||
| "test": "echo \"Error: no test specified\" && exit 1" | ||
| "version": "2.0.0", | ||
| "description": "Zero-dependency cooperative LIFO execution for Node.js and browsers", | ||
| "type": "module", | ||
| "main": "./stack.cjs", | ||
| "module": "./stack.js", | ||
| "browser": "./stack.js", | ||
| "exports": { | ||
| ".": { | ||
| "import": "./stack.js", | ||
| "require": "./stack.cjs", | ||
| "default": "./stack.js" | ||
| }, | ||
| "./stack.js": { | ||
| "import": "./stack.js", | ||
| "require": "./stack.cjs", | ||
| "default": "./stack.js" | ||
| }, | ||
| "./stack-vanilla.js": "./stack-vanilla.js", | ||
| "./es5.js": { | ||
| "import": "./stack.js", | ||
| "require": "./stack.cjs", | ||
| "default": "./es5.js" | ||
| }, | ||
| "./package.json": "./package.json" | ||
| }, | ||
| "engines" : { | ||
| "node" : ">=6.0.0" | ||
| "files": [ | ||
| "stack.js", | ||
| "stack.cjs", | ||
| "stack-vanilla.js", | ||
| "es5.js", | ||
| "CHANGELOG.md", | ||
| "MIGRATION.md", | ||
| "licence" | ||
| ], | ||
| "sideEffects": [ | ||
| "./stack-vanilla.js", | ||
| "./es5.js" | ||
| ], | ||
| "engines": { | ||
| "node": ">=12.22.0" | ||
| }, | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/RIAEvangelist/easy-stack.git" | ||
| "scripts": { | ||
| "test": "node ./scripts/test.js", | ||
| "test:core": "node ./scripts/test.js core", | ||
| "test:package": "node ./scripts/test.js package", | ||
| "test:server": "node ./scripts/test.js server", | ||
| "test:docs": "node ./scripts/test.js docs", | ||
| "test:legacy": "node ./scripts/test.js legacy", | ||
| "coverage": "vanilla-test coverage", | ||
| "coverage:node": "vanilla-test coverage node", | ||
| "coverage:chrome": "vanilla-test coverage chrome", | ||
| "check": "npm test && npm run coverage:node", | ||
| "start": "node ./scripts/serve.js" | ||
| }, | ||
| "keywords": [ | ||
| "stack", | ||
| "lifo", | ||
| "scheduler", | ||
| "priority", | ||
| "javascript", | ||
| "node", | ||
| "js", | ||
| "auto", | ||
| "run", | ||
| "execute", | ||
| "browser", | ||
| "react" | ||
| "zero-dependency" | ||
| ], | ||
| "author": "Brandon Nozaki Miller", | ||
| "license": "MIT", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "git+https://github.com/RIAEvangelist/easy-stack.git" | ||
| }, | ||
| "bugs": { | ||
| "url": "https://github.com/RIAEvangelist/easy-stack/issues" | ||
| }, | ||
| "homepage": "https://github.com/RIAEvangelist/easy-stack#readme" | ||
| "homepage": "https://riaevangelist.github.io/easy-stack/", | ||
| "devDependencies": { | ||
| "vanilla-test": "2.1.1" | ||
| } | ||
| } |
+63
-228
@@ -1,261 +0,96 @@ | ||
| # easy-stack Is Great for any javascript stack (LIFO) | ||
| [](https://riaevangelist.github.io/easy-stack/) | ||
| JS Stacks are different from queues because they are LIFO (last in first out) unlike a queue which is FIFO (first in first out). While a Queue executes in order, a stack executes whatever was most recently added to the stack, much like a reading a stack of papers on your desk. If you read half the papers and someone puts more on the top you start with the new ones first. | ||
| # easy-stack | ||
| 1. socket messages | ||
| 2. priority async operations | ||
| 3. priority synchronous operations | ||
| 4. stacks you want to start running automatically when you add new items | ||
| 5. any simple or complex stack operations | ||
| 6. base class to extend | ||
| 7. anything else that needs a stack | ||
| Zero-dependency cooperative LIFO execution for Node.js and browsers. | ||
| [Overview](https://riaevangelist.github.io/easy-stack/) · [Guide](https://riaevangelist.github.io/easy-stack/guide/) · [API](https://riaevangelist.github.io/easy-stack/api/) · [Patterns](https://riaevangelist.github.io/easy-stack/patterns/) · [Browser](https://riaevangelist.github.io/easy-stack/browser/) · [Examples](https://riaevangelist.github.io/easy-stack/examples/) · [Stack vs queue](https://riaevangelist.github.io/easy-stack/queue/) · [Migration](https://riaevangelist.github.io/easy-stack/migration/) · [Testing](https://riaevangelist.github.io/easy-stack/testing/) | ||
| # Stable and easy to use | ||
| Works great in node.js, webpack, browserify, or any other commonjs loader or compiler. To use in plain old vanilla browser javascript without common js just replace the requires in the examples with script tags. We show that below too. Any time you need a JS stack easy-stack is there for you. | ||
| [](https://www.npmjs.com/package/easy-stack) | ||
| [](https://www.npmjs.com/package/easy-stack) | ||
| [](https://riaevangelist.github.io/easy-stack/testing/) | ||
| [](https://github.com/RIAEvangelist/easy-stack/actions/workflows/ci.yml) | ||
| [](https://riaevangelist.github.io/easy-stack/testing/) | ||
| [](https://riaevangelist.github.io/easy-stack/testing/) | ||
| [](https://www.npmjs.com/package/easy-stack?activeTab=dependencies) | ||
| [](./licence) | ||
| ` require('easy-stack'); ` for ES6 node. | ||
| ` require('easy-stack/es5.js'); ` for ES5 node and browser. | ||
| ## Quick start | ||
| **npm install easy-stack** | ||
| ```sh | ||
| npm install easy-stack | ||
| ``` | ||
| npm info : [See npm trends and stats for easy-stack](http://npm-stat.com/charts.html?package=easy-stack&author=&from=&to=) | ||
|      | ||
| [](https://github.com/RIAEvangelist) | ||
| GitHub info : | ||
|    | ||
| Package details websites : | ||
| * [GitHub.io site](http://riaevangelist.github.io/easy-stack/ "easy-stack documentation"). A prettier version of this site. | ||
| * [NPM Module](https://www.npmjs.org/package/easy-stack "easy-stack npm module"). The npm page for the easy-stack module. | ||
| This work is licenced via the [DBAD Public Licence](http://www.dbad-license.org/). | ||
| ## Exposed methods and values | ||
| |key|type|parameters|default|description| | ||
| |----|----|----|----|----| | ||
| |add|function|any number of functions| |adds all parameter functions to stack and starts execution if autoRun is true, stack is not already running and stack is not forcibly stopped | | ||
| |next|function| | |executes next item in stack if stack is not forcibly stopped| | ||
| |clear|function| | |removes remaining items in the stack| | ||
| |contents|Array| | | stack instance contents | | ||
| |autoRun|Bool| | true |should autoRun stack when new item added| | ||
| |stop|Bool| | false |setting this to true will forcibly prevent the stack from executing| | ||
| ### Basic stack use in node, react, browserify, webpack or any other commonjs implementation | ||
| ```javascript | ||
| import Stack from 'easy-stack'; | ||
| var Stack=require('easy-stack'); | ||
| //create a new Stack instance | ||
| var stack=new Stack; | ||
| const stack = new Stack(); | ||
| const order = []; | ||
| for(var i=0; i<50; i++){ | ||
| //add a bunch of stuff to the stack | ||
| stack.add(makeRequest); | ||
| } | ||
| function makeRequest(){ | ||
| //do stuff | ||
| console.log('making some request'); | ||
| stack.autoRun = false; | ||
| stack.add( | ||
| function first() { | ||
| order.push('first'); | ||
| this.next(); | ||
| }, | ||
| function newest() { | ||
| order.push('newest'); | ||
| this.next(); | ||
| } | ||
| ); | ||
| stack.next(); | ||
| console.log(order); // ['newest', 'first'] | ||
| ``` | ||
| ### Basic browser use | ||
| CommonJS remains supported: | ||
| The only difference is including via a script tag instead of using require. | ||
| ```html | ||
| <html> | ||
| <head> | ||
| <!-- this is the only difference --> | ||
| <script src='./es5.js'></script> | ||
| <script> | ||
| console.log('my awesome app script'); | ||
| var stack=new Stack; | ||
| for(var i=0; i<50; i++){ | ||
| stack.add(makeRequest); | ||
| } | ||
| function makeRequest(){ | ||
| console.log('making some request'); | ||
| this.next(); | ||
| } | ||
| </script> | ||
| </head> | ||
| <body> | ||
| </body> | ||
| </html> | ||
| ```javascript | ||
| const Stack = require('easy-stack'); | ||
| ``` | ||
| ### Basic use with websockets in node, react, browserify, webpack or any other commonjs implementation | ||
| Each task receives the stack as `this` and explicitly continues the flow with `this.next()`. Execution begins synchronously when `autoRun` is truthy; set it to `false` while assembling a batch. | ||
| This allows you to start adding requests immediately and only execute if the websocket is connected. To use in plain browser based JS without webpack or browserify just replace the requires with the script tag. | ||
| ## Contract at a glance | ||
| ```javascript | ||
| | API | Result | | ||
| | --- | --- | | ||
| | `new Stack()` | Creates an isolated stack with `autoRun = true` and `stop = false`. | | ||
| | `add(...tasks)` | Validates and appends functions, starts eligible work, and returns the stack. | | ||
| | `next()` | Runs the newest pending task and returns `undefined`. | | ||
| | `clear()` | Removes pending work and returns the new empty live array. | | ||
| | `contents()` | Returns the live pending array. | | ||
| | `contents(tasks)` | Replaces pending work after validating the complete array. | | ||
| | `stack` | Gets or replaces the live pending array. | | ||
| | `size` | Reports the pending task count. | | ||
| | `running` | Reports whether a task has started and not yet yielded or drained. | | ||
| var Stack=require('easy-stack'); | ||
| The runner is intentionally cooperative. A task that does not call `this.next()` keeps the stack active until another part of the program calls `next()`. New tasks added while active take priority over older pending work. | ||
| //ws-share just makes it easier to share websocket code and ensure you don't open a websocket more than once | ||
| var WS=require('ws-share'); | ||
| ## Browser entry points | ||
| //js-message makes it easy to create and parse normalized JSON messages. | ||
| var Message=require('js-message'); | ||
| Use `stack.js` as a native module, `stack-vanilla.js` as a modern classic script, or `es5.js` for legacy syntax: | ||
| //create a new Stack instance | ||
| var stack=new Stack; | ||
| //force stop until websocket opened | ||
| stack.stop=true; | ||
| var ws=null; | ||
| function startWS(){ | ||
| //websocket.org rocks | ||
| ws=new WS('wss://echo.websocket.org/?encoding=text'); | ||
| ws.on( | ||
| 'open', | ||
| function(){ | ||
| ws.on( | ||
| 'message', | ||
| handleResponse | ||
| ); | ||
| //now that websocket is opened allow auto execution | ||
| stack.stop=false; | ||
| stack.next(); | ||
| } | ||
| ); | ||
| ws.on( | ||
| 'error', | ||
| function(err){ | ||
| //stop execution of stack if there is an error because the websocket is likely closed | ||
| stack.stop=true; | ||
| //remove remaining items in the stack | ||
| stack.clear(); | ||
| throw(err); | ||
| } | ||
| ); | ||
| ws.on( | ||
| 'close', | ||
| function(){ | ||
| //stop execution of stack when the websocket closed | ||
| stack.stop=true; | ||
| } | ||
| ); | ||
| } | ||
| //simulate a lot of requests being stackd up for the websocket | ||
| for(var i=0; i<50; i++){ | ||
| stack.add(makeRequest); | ||
| } | ||
| var messageID=0; | ||
| function handleResponse(e){ | ||
| var message=new Message; | ||
| message.load(e.data); | ||
| console.log(message.type,message.data); | ||
| } | ||
| function makeRequest(){ | ||
| messageID++; | ||
| var message=new Message; | ||
| message.type='testMessage'; | ||
| message.data=messageID; | ||
| ws.send(message.JSON); | ||
| this.next(); | ||
| } | ||
| startWS(); | ||
| ```html | ||
| <script src="https://unpkg.com/easy-stack@2.0.0/stack-vanilla.js"></script> | ||
| <script> | ||
| const stack = new Stack(); | ||
| </script> | ||
| ``` | ||
| # Extending ES6 stack.js | ||
| See the focused [browser guide](https://riaevangelist.github.io/easy-stack/browser/) for module and classic-script examples. | ||
| ```javascript | ||
| ## Verification | ||
| const Stack=require('easy-stack'); | ||
| The shared behavior suite runs through `vanilla-test@2.1.1` in Node and headless Chrome. A dependency-free fallback proves the shipped runtime on the declared Node 12.22 floor. CI also verifies both module systems, browser globals, the packed npm artifact, documentation links, and 100% statement, branch, function, and line coverage. | ||
| class MyAwesomestack extends Stack{ | ||
| isStopped(){ | ||
| return this.stop; | ||
| } | ||
| removeThirdItem(){ | ||
| this.contents.splice(2,1); | ||
| return this.contents; | ||
| } | ||
| }; | ||
| ```sh | ||
| npm test | ||
| npm run coverage | ||
| ``` | ||
| Read the [testing evidence](https://riaevangelist.github.io/easy-stack/testing/) or the [v2 migration guide](./MIGRATION.md). | ||
| # Extending stack node es5 or browser | ||
| ## License | ||
| ```javascript | ||
| var Stack=require('easy-stack'); | ||
| //MyAwesomestack inherits from stack | ||
| MyAwesomestack.prototype = new Stack; | ||
| //Constructor will extend stack | ||
| MyAwesomestack.prototype.constructor = MyAwesomestack; | ||
| function MyAwesomestack(){ | ||
| //extend with some stuff your app needs, | ||
| //maybe npm publish your extention with easy-stack as a dependancy? | ||
| Object.defineProperties( | ||
| this, | ||
| { | ||
| isStopped:{ | ||
| enumerable:true, | ||
| get:checkStopped, | ||
| set:checkStopped | ||
| }, | ||
| removeThirdItem:{ | ||
| enumerable:true, | ||
| writable:false, | ||
| value:removeThird | ||
| } | ||
| } | ||
| ); | ||
| //enforce Object.assign for extending by locking down Class structure | ||
| //no willy nilly cowboy coding | ||
| Object.seal(this); | ||
| function checkStopped(){ | ||
| return this.stop; | ||
| } | ||
| function removeThird(){ | ||
| //get the stack content | ||
| var list=this.contents; | ||
| //modify the stack content | ||
| list.splice(2,1); | ||
| //save the modified stack content | ||
| this.contents=list; | ||
| return this.contents; | ||
| } | ||
| } | ||
| ``` | ||
| [MIT](./licence) © Brandon Nozaki Miller |
+80
-24
@@ -1,39 +0,95 @@ | ||
| class Stack{ | ||
| constructor(){ | ||
| this.stack=[]; | ||
| this.autoRun=true; | ||
| this.running=false; | ||
| this.stop=false; | ||
| } | ||
| const stacks = new WeakMap(); | ||
| const states = new WeakMap(); | ||
| clear(){ | ||
| this.stack=[]; | ||
| return this.stack; | ||
| function validateTasks(tasks) { | ||
| const invalidIndex = tasks.findIndex((task) => typeof task !== 'function'); | ||
| if (invalidIndex !== -1) { | ||
| throw new TypeError(`Stack task at index ${invalidIndex} must be a function.`); | ||
| } | ||
| } | ||
| contents(val){ | ||
| if(val){ | ||
| this.stack=val; | ||
| } | ||
| return this.stack; | ||
| class Stack { | ||
| constructor() { | ||
| stacks.set(this, []); | ||
| states.set(this, { running: false }); | ||
| this.autoRun = true; | ||
| this.stop = false; | ||
| } | ||
| add(...callbacks){ | ||
| this.stack.push(...callbacks); | ||
| if(!this.running && !this.stop && this.autoRun){ | ||
| add(...tasks) { | ||
| validateTasks(tasks); | ||
| stacks.get(this).push(...tasks); | ||
| if (!this.running && !this.stop && this.autoRun) { | ||
| this.next(); | ||
| } | ||
| return this; | ||
| } | ||
| next(){ | ||
| this.running=true; | ||
| if(this.stack.length<1 || this.stop){ | ||
| this.running=false; | ||
| next() { | ||
| const stack = stacks.get(this); | ||
| const state = states.get(this); | ||
| if (this.stop || stack.length === 0) { | ||
| state.running = false; | ||
| return; | ||
| } | ||
| this.stack.pop().bind(this)(); | ||
| state.running = true; | ||
| const task = stack.pop(); | ||
| if (typeof task !== 'function') { | ||
| state.running = false; | ||
| throw new TypeError('The next Stack task must be a function.'); | ||
| } | ||
| try { | ||
| task.call(this); | ||
| } catch (error) { | ||
| state.running = false; | ||
| throw error; | ||
| } | ||
| } | ||
| clear() { | ||
| const stack = []; | ||
| stacks.set(this, stack); | ||
| return stack; | ||
| } | ||
| contents(tasks) { | ||
| if (arguments.length > 0) { | ||
| this.stack = tasks; | ||
| } | ||
| return stacks.get(this); | ||
| } | ||
| get stack() { | ||
| return stacks.get(this); | ||
| } | ||
| set stack(tasks) { | ||
| if (!Array.isArray(tasks)) { | ||
| throw new TypeError('Stack contents must be an array of functions.'); | ||
| } | ||
| validateTasks(tasks); | ||
| stacks.set(this, tasks); | ||
| } | ||
| get running() { | ||
| return states.get(this).running; | ||
| } | ||
| get size() { | ||
| return stacks.get(this).length; | ||
| } | ||
| } | ||
| module.exports=Stack; | ||
| export { | ||
| Stack as default, | ||
| Stack | ||
| }; |
| var Stack=require('../stack'); | ||
| //create a new Stack instance | ||
| var stack=new Stack; | ||
| stack.autoRun=false; | ||
| for(var i=0; i<50; i++){ | ||
| //add a bunch of stuff to the stack | ||
| stack.add(makeRequest.bind(stack,i)); | ||
| } | ||
| stack.next(); | ||
| function makeRequest(index){ | ||
| //do stuff | ||
| console.log(`making LIFO request ${index}`); | ||
| this.next(); | ||
| } |
-21
| MIT License | ||
| Copyright (c) 2020 Brandon Nozaki Miller | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
21905
66.92%9
50%339
197.37%0
-100%Yes
NaN1
Infinity%97
-62.98%1
Infinity%