
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
Limbs is a tiny object system for JavaScript.
Limbs is written in ECMAScript 5.1, so it can be used in any environment, including older browsers and embedded platforms, without requiring extra build steps.
Lims uses the magic of lexical scoping (closures) to implement features that ES6 (ES2015+) classes still do not provide, such as trait-based multiple inheritance (a.k.a. mixins), true private members, and methods that do not lose their binding when separated from their object.
Limbs is currently available on NPM, and can be installed with:
npm i limbs
If you are using Limbs in a non-NPM environment, please feel free to port it and submit a pull request :-)
The default export of the limbs module is its entry point, called New.
(Capitalized, so as not to clash with JavaScript's built-in new.)
It is a is a very simple function. Each call to it creates a plain object of the
form { traits: [], private: {}, public: {} }, called the core; applies any
passed traits to it, and returns its public member:
var New = require('limbs')
var myObject = New() // {}
var myObject = New(function iAmATrait (core) {
core.public = core.traits }) // ['iAmATrait']
To pre-populate the returned object with useful functionality, you pass traits
to the factory. Two traits come pre-packaged with Limbs; those are the Private
trait and the Public trait.
var Private = New.Private
, Public = New.Public
Or, in ES6, simply:
import New, { Private, Public } from 'limbs'
The Private and Public traits modify core.private and core.public.
Other than that distinction, they work in the same way:
myObject = New(
Private({ X: 1 }),
Public({ Y: 2 })
)
// { Y: 2 }
Of course, private values are no good if you have no way to access them, and
this is where closures really shine: you can pass functions to Public and
Private. Each function gets passed the entire core (with all modifications
prior to that point already applied), so you can e.g. define getters and setters:
myObject = New(
Private({ X: 1 }),
Public(function (core) {
core.public.Y = 'plain value'
Object.defineProperty(core.public, 'X', {
enumerable: true,
get: function () { return core.private.X }
set: function (v) {
var t = typeof v
if (t !== 'number') throw new TypeError('X can only be a number, received: ' + t)
core.private.X = v } }) })
myObject // { X: [Getter/Setter], Y: 'plain value' }
myObject.X // 1
myObject.X = 2 // 2
myObject.X // 2
myObject.X = 'foo' // TypeError thrown
In the above example:
myObject.X (equivalent to core.public.X) is a custom property acting as a
type guard for core.private.X.core.private.X is a separate variable, which is only accessible from the
function that is passed to Public, but not from the outside.Of course, passing the entire core means you can also modify core.public
from a function passed to Private, and vice versa. It is your responsibility
to only do this where it makes sense.
Let's try rewriting that example with a little more composability:
function typeGuarded (propertyName, expectedType) {
var errorMessage = propertyName + ' can only be a ' + expectedType + ', received: '
return function (core) {
Object.defineProperty(core.public, propertyName, {
enumerable: true,
get: function () { return core.private[propertyName] }
set: function (newValue) {
var typeOfNewValue = typeof newValue
if (typeOfNewValue !== expectedType) throw new TypeError(errorMessage + typeOfNewValue)
core.private[propertyName] = newValue } }) } }
myObject = New(
Private({
X: 1,
Y: 'foo'}),
Public(
typeGuarded('X', 'number'),
typeGuarded('Y', 'string')))
Hey presto! We've factored out the validation into a separate function. Thanks to closures, the names and expected types of private properrties (defined at one time) and their actual values and memory locations (defined at another time) end up in the same scope, letting you implement your logic in small, manageable chunks. We've also memoized part of the error message.
As you saw above, you can pass as many arguments as you like to Private and
Public. If you prefix any argument passed to Private, Public or New with
a Boolean conditional that evaluates to false, it will simply be ignored.
This gives you convenient way to quickly enable or disable layers of functionality.
The last thing you need to know is that if you return an object from a function
that you've passed to New, Private, or Public, that object will replace
core, core.private, and core.public entirely. This is to enable
immutable-style operations, such as using ES6 Object.assign/object spread
syntax, or to patch in ImmutableJS.
FAQs
Closure-powered trait-based OOP system for ES5+
We found that limbs demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.