mutate a copy of data without changing the original source

Overview

immutability-helper

NPM version Build status Test coverage Downloads Minified size Gzip size

Mutate a copy of data without changing the original source

Setup via NPM

npm install immutability-helper --save

This is a drop-in replacement for react-addons-update:

// import update from 'react-addons-update';
import update from 'immutability-helper';

const state1 = ['x'];
const state2 = update(state1, {$push: ['y']}); // ['x', 'y']

Note that this module has nothing to do with React. However, since this module is most commonly used with React, the docs will focus on how it can be used with React.

Overview

React lets you use whatever style of data management you want, including mutation. However, if you can use immutable data in performance-critical parts of your application it's easy to implement a fast shouldComponentUpdate() method to significantly speed up your app.

Dealing with immutable data in JavaScript is more difficult than in languages designed for it, like Clojure. However, we've provided a simple immutability helper, update(), that makes dealing with this type of data much easier, without fundamentally changing how your data is represented. You can also take a look at Facebook's Immutable.js and React’s Using Immutable Data Structures section for more detail on Immutable.js.

The Main Idea

If you mutate data like this:

myData.x.y.z = 7;
// or...
myData.a.b.push(9);

You have no way of determining which data has changed since the previous copy has been overwritten. Instead, you need to create a new copy of myData and change only the parts of it that need to be changed. Then you can compare the old copy of myData with the new one in shouldComponentUpdate() using triple-equals:

const newData = deepCopy(myData);
newData.x.y.z = 7;
newData.a.b.push(9);

Unfortunately, deep copies are expensive, and sometimes impossible. You can alleviate this by only copying objects that need to be changed and by reusing the objects that haven't changed. Unfortunately, in today's JavaScript this can be cumbersome:

const newData = Object.assign({}, myData, {
  x: Object.assign({}, myData.x, {
    y: Object.assign({}, myData.x.y, {z: 7}),
  }),
  a: Object.assign({}, myData.a, {b: myData.a.b.concat(9)})
});

While this is fairly performant (since it only makes a shallow copy of log n objects and reuses the rest), it's a big pain to write. Look at all the repetition! This is not only annoying, but also provides a large surface area for bugs.

update()

update() provides simple syntactic sugar around this pattern to make writing this code easier. This code becomes:

import update from 'immutability-helper';

const newData = update(myData, {
  x: {y: {z: {$set: 7}}},
  a: {b: {$push: [9]}}
});

While the syntax takes a little getting used to (though it's inspired by MongoDB's query language) there's no redundancy, it's statically analyzable and it's not much more typing than the mutative version.

The $-prefixed keys are called commands. The data structure they are "mutating" is called the target.

Available Commands

  • {$push: array} push() all the items in array on the target.
  • {$unshift: array} unshift() all the items in array on the target.
  • {$splice: array of arrays} for each item in arrays call splice() on the target with the parameters provided by the item. Note: The items in the array are applied sequentially, so the order matters. The indices of the target may change during the operation.
  • {$set: any} replace the target entirely.
  • {$toggle: array of strings} toggles a list of boolean fields from the target object.
  • {$unset: array of strings} remove the list of keys in array from the target object.
  • {$merge: object} merge the keys of object with the target.
  • {$apply: function} passes in the current value to the function and updates it with the new returned value.
  • {$add: array of objects} add a value to a Map or Set. When adding to a Set you pass in an array of objects to add, when adding to a Map, you pass in [key, value] arrays like so: update(myMap, {$add: [['foo', 'bar'], ['baz', 'boo']]})
  • {$remove: array of strings} remove the list of keys in array from a Map or Set.

Shorthand $apply syntax

Additionally, instead of a command object, you can pass a function, and it will be treated as if it was a command object with the $apply command: update({a: 1}, {a: function}). That example would be equivalent to update({a: 1}, {a: {$apply: function}}).

Limitations

⚠️ update only works for data properties, not for accessor properties defined with Object.defineProperty. It just does not see the latter, and therefore might create shadowing data properties which could break application logic depending on setter side effects. Therefore update should only be used on plain data objects that only contain data properties as descendants.

Examples

Simple push

const initialArray = [1, 2, 3];
const newArray = update(initialArray, {$push: [4]}); // => [1, 2, 3, 4]

initialArray is still [1, 2, 3].

Nested collections

const collection = [1, 2, {a: [12, 17, 15]}];
const newCollection = update(collection, {2: {a: {$splice: [[1, 1, 13, 14]]}}});
// => [1, 2, {a: [12, 13, 14, 15]}]

This accesses collection's index 2, key a, and does a splice of one item starting from index 1 (to remove 17) while inserting 13 and 14.

Updating a value based on its current one

const obj = {a: 5, b: 3};
const newObj = update(obj, {b: {$apply: function(x) {return x * 2;}}});
// => {a: 5, b: 6}
// This is equivalent, but gets verbose for deeply nested collections:
const newObj2 = update(obj, {b: {$set: obj.b * 2}});

(Shallow) Merge

const obj = {a: 5, b: 3};
const newObj = update(obj, {$merge: {b: 6, c: 7}}); // => {a: 5, b: 6, c: 7}

Computed Property Names

Arrays can be indexed into with runtime variables via the ES2015 Computed Property Names feature. An object property name expression may be wrapped in brackets [] which will be evaluated at runtime to form the final property name.

const collection = {children: ['zero', 'one', 'two']};
const index = 1;
const newCollection = update(collection, {children: {[index]: {$set: 1}}});
// => {children: ['zero', 1, 'two']}

Removing an element from an array

// Delete at a specific index, no matter what value is in it
update(state, { items: { $splice: [[index, 1]] } });

Autovivification

Autovivification is the auto creation of new arrays and objects when needed. In the context of javascript that would mean something like this

const state = {}
state.a.b.c = 1; // state would equal { a: { b: { c: 1 } } }

Since javascript doesn't have this "feature", the same applies to immutability-helper. The reason why this is practically impossible in javascript and by extension immutability-helper is the following:

var state = {}
state.thing[0] = 'foo' // What type should state.thing have? Should it be an object or array?
state.thing2[1] = 'foo2' // What about thing2? This must be an object!
state.thing3 = ['thing3'] // This is regular js, this works without autovivification
state.thing3[1] = 'foo3' // Hmm, notice that state.thing2 is an object, yet this is an array
state.thing2.slice // should be undefined
state.thing3.slice // should be a function

If you need to set something deeply nested and don't want to have to set each layer down the line, consider using this technique which is shown with a contrived example:

var state = {}
var desiredState = {
  foo: [
    {
      bar: ['x', 'y', 'z']
    },
  ],
};

const state2 = update(state, {
  foo: foo =>
    update(foo || [], {
      0: fooZero =>
        update(fooZero || {}, {
          bar: bar => update(bar || [], { $push: ["x", "y", "z"] })
        })
    })
});

console.log(JSON.stringify(state2) === JSON.stringify(desiredState)) // true
// note that state could have been declared as any of the following and it would still output true:
// var state = { foo: [] }
// var state = { foo: [ {} ] }
// var state = { foo: [ {bar: []} ] }

You can also choose to use the extend functionality to add an $auto and $autoArray command:

import update, { extend } from 'immutability-helper';

extend('$auto', function(value, object) {
  return object ?
    update(object, value):
    update({}, value);
});
extend('$autoArray', function(value, object) {
  return object ?
    update(object, value):
    update([], value);
});

var state = {}
var desiredState = {
  foo: [
    {
      bar: ['x', 'y', 'z']
    },
  ],
};
var state2 = update(state, {
  foo: {$autoArray: {
    0: {$auto: {
      bar: {$autoArray: {$push: ['x', 'y', 'z']}}
    }}
  }}
});
console.log(JSON.stringify(state2) === JSON.stringify(desiredState)) // true

Adding your own commands

The main difference this module has with react-addons-update is that you can extend this to give it more functionality:

import update, { extend } from 'immutability-helper';

extend('$addtax', function(tax, original) {
  return original + (tax * original);
});
const state = { price: 123 };
const withTax = update(state, {
  price: {$addtax: 0.8},
});
assert(JSON.stringify(withTax) === JSON.stringify({ price: 221.4 }));

Note that original in the function above is the original object, so if you plan making a mutation, you must first shallow clone the object. Another option is to use update to make the change return update(original, { foo: {$set: 'bar'} })

If you don't want to mess around with the globally exported update function you can make a copy and work with that copy:

import { Context } from 'immutability-helper';

const myContext = new Context();

myContext.extend('$foo', function(value, original) {
  return 'foo!';
});

myContext.update(/* args */);
Comments
  • Convert project into TypeScript

    Convert project into TypeScript

    Fixes #122 Fixes #117 Fixes #127

    Based on and fixes #126

    I've separated into 4 commits for an easier review.

    Concerning the last commit, Microsoft says the following:

    If you are writing the library in TypeScript, don't use dtslint. Use --declaration to have type definitions generated for you.

    opened by jednano 23
  • Typescript Error TS2304: Cannot find name 'ReadonlySet' / 'ReadonlyMap'

    Typescript Error TS2304: Cannot find name 'ReadonlySet' / 'ReadonlyMap'

    Hi,

    I'm using Immutability-helper with a sample of SharePoint Webparts (it's called react-collapsible-textboxio). Since a few days everytime I start my Webpart I get the Errors:

    [18:11:22] Error - typescript - node_modules\immutability-helper\index.d.ts(12,8): error TS2304: Cannot find name 'ReadonlySet'. [18:11:22] Error - typescript - node_modules\immutability-helper\index.d.ts(14,3): error TS2304: Cannot find name 'ReadonlySet'. [18:11:22] Error - typescript - node_modules\immutability-helper\index.d.ts(17,8): error TS2304: Cannot find name 'ReadonlyMap'. [18:11:22] Error - typescript - node_modules\immutability-helper\index.d.ts(19,3): error TS2304: Cannot find name 'ReadonlyMap'.

    I don't thinks the error is in the webpart itself, because Typescript links directly to the node_module a detects the error in there. I can not say how this error appeared, it was just there I think as I reinstalled npm cause of a sync problem with git-repository...

    I hope someone here can help me with my problem and thats the bes place to post it.

    Thank you for your answers,

    • Effi
    opened by DerEffi 15
  • Use conditional types to make types significantly safer and more robust.

    Use conditional types to make types significantly safer and more robust.

    CustomCommands

    Due to the nature of update.extend, update itself cannot be effectively typed in such a way as to get type safety with the default commands while also allowing arbitrary custom commands. The reason for this is that (1) extend is side-effecting, so we can't capture a new type from the return value and (2) custom commands are entirely arbitrary, so in order to permit them we would have to have extremely permissive typings along the lines of (any) => any, which are so broad as to be almost useless.

    In order to get around this, the types require a type parameter for custom commands. This allows normal usage to proceed unhindered and well-typed, but custom usage will have to be specially-notated. The type that represents this concept is CustomCommands.

    The reason that CustomCommands is a separate, branded type rather than simply extends object is due to type inference. If update's argument were typed as type Spec<T, C extends object>, the type parameter C would behave as a catch-all. Any fields you provide anywhere in the nested spec would be lumped together under some crazy inferred type for C, thereby undermining type safety by effectively being any. Instead, CustomCommands' branding represents an un-inferrable, opt-in type that can only be explicitly specified by the user.

    You never need to declare an object of type CustomCommands<T>, i.e., the brand doesn't exist at runtime. For typing purposes the branding is "unwrapped" and doesn't add extra nesting or the branded field itself to any type. In normal usage, it is invisible.

    The typings file itself includes a brief example of how to use CustomCommands, reproduced here for convenience:

    // Usage with custom commands is as follows:
    
    interface MyCommands {
      $foo: string;
    }
    
    update<Foo, CustomCommands<MyCommands>>(..., { $foo: "bar" });
    
    // It is suggested that if you use custom commands frequently, you wrap and re-export a
    // properly-typed version of `update`:
    
    function myUpdate<T>(object: T, spec: Spec<T, CustomCommands<MyCommands>>) {
      return update(object, spec);
    }
    

    About this PR

    This PR uses conditional types from Typescript 2.9 to inspect the arguments passed to update and provide very precise typings. It comes at the cost of demoting the ease of use of custom commands, as described above, but I consider that justifiable because as the explanation notes, types were barely checked (if at all) before due to too-permissive custom command support!

    The improvements from this PR extend not just to type safety when calling update, but also to operations like renaming refactors! This means that in many cases if you rename a field on some type, calls to update that would modify the value of that field are also detected and refactored. This is a huge devex improvement from before, where those fields would be left dangling and pointing to nothing, causing subtle issues in the future.

    opened by seansfkelley 14
  • Add $toggle

    Add $toggle

    Sometimes, it's useful to be able to just toggle a boolean field. Right now I'd have to write something like this:

    const obj = { a: false };
    update(obj, {a: {$set: !obj.a}});
    

    This PR makes it possible to go like:

    const obj = {a: false};
    update(obj, {$toggle: ['a']});
    
    opened by vladtsf 12
  • TypeScript error on update method:  TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof

    TypeScript error on update method: TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof "/immutability-helper/index"' has no compatible call signatures.

    I am currently getting the following issue: TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'typeof "~/node_modules/immutability-helper/index"' has no compatible call signatures.

    This issue does not happen on the 2.6.4 version but as soon as the solution pulled in the 2.6.5 version i started getting this issue. For now I have made the version constant at 2.6.4, but wanted to notify you about this issue.

    Thanks.

    opened by ajalgaonkar 11
  • fix($types): support array index

    fix($types): support array index

    Temporary fix for #84.

    Here's a VERY bad way of allowing nested array indexing in queries. Here's a very rough reproduction of the problem for the TypeScript experts that may be able to help us get a more type-safe solution for this problem.


    There definitely should be a way of not loosing the type data here. I imagine the solution to look like (imaginary syntax that doesn't exist in TypeScript :) ):

    type Tree<T> = 
      // If is object
      {[K in keyof T]?: Query<T[K]>}
      |
      // if is array
      {[I in indexesOf T]?: Query<T[I]>}
    

    Is there something like I in indexesOf T in TypeScript? I imagine there's no way of getting such information from just static analysis.

    cc. @kolodny @andy-ms

    bug 
    opened by andreiglingeanu 11
  • Do not update when data is the same

    Do not update when data is the same

    It would be good if update function could return previous data if changed variable is the same

    i mean let a = {b: {c: {d : 5}}}; let z = update(a, {b: {c: {d : {$set: 5}}}); here 'z' changes even though value is the same. Right now i have a performance issue with react application, cause after update i receive new object most of the times with the same value as previous.

    I believe Implementing this feature would speed up a react application

    opened by danziamo 11
  • Types broken for TypeScript < 3

    Types broken for TypeScript < 3

    When we upgraded immutability-helper to version 2.9.0, our TypeScript build broke with the following error:

    node_modules/immutability-helper/index.d.ts:25:57 - error TS1110: Type expected.
    
    25     $splice: Array<[number, number?] | [number, number, ...T[]]>;
                                                               ~~~
    

    I'm wondering if this is a syntax that is was added in TypeScript 3, as we're still on 2.8.3.

    opened by mockdeep 9
  • Arrays are not copied properly - keys are lost

    Arrays are not copied properly - keys are lost

    Consider:

    a = { items: [ { name: 'Superman', strength: 1000 }, { name: 'Jim', strength: 2 } ]
    a.items.top = 0
    a
    // =>
    // { items: 
    //    [ { name: 'Superman', strength: 1000 },
    //      { name: 'Jim', strength: 2 },
    //      top: 0 ] }
    c
    =>
     { items: [
        { name: "Superman", strength: 1000 },
        { name: "Jim", strength: 2 }
      ] 
    }
    cmd = { items: { 1: { strength: { $set: 3 } } } }
    b = update(a, cmd)
    b
    // =>
    // { items: 
    //    [ { name: 'Superman', strength: 1000 },
    //      { name: 'Jim', strength: 3 } ] }
    

    Notice the "top" key was lost. The culprit is (I believe) line 22 where it should be:

    function copy(object) {
      if (object instanceof Array) {
    -     return object.slice();
    +     return assign(object.constructor(), object)
      } else if (object && typeof object === 'object') {
        var prototype = object.constructor && object.constructor.prototype
    

    Keys should not be lost.

    opened by arturog 9
  • node_modules/immutability-helper/index

    node_modules/immutability-helper/index"' has no default export.

    Hi Guys a question. I'm having an annoying problem here using immutability-helper with react and typescript. In particular in this page https://github.com/kolodny/immutability-helper you say to use the following to import the update module:

    import update from 'immutability-helper'.

    Unfortunately recently I see that it's no longer working.The error I receive is the following:

    ./ClientApp/store/Counter.ts:2:8 TS1192: Module '"../node_modules/immutability-helper/index"' has no default export.

    If I change it to import * as update from 'immutability-helper' I receive the error on the update.

    Any idea on this?

    Thanks Alessandro

    opened by Less97 8
  • Add positional operator

    Add positional operator

    Add positional operator in order to update items in collection in simple and declarative manner.

    const users = [
      { id: 1, name: 'Andrej', surname: 'Badin' },
      { id: 2, name: 'John', surname: 'Doe' },
    ];
    
    const patchedUsers = { id: 2, surname: 'Appleseed' };
    
    const updatedUsers = update(users, { $: { $merge: patchedUsers } });
    
    // Expected output
    // const updatedUsers = [
    //   { id: 1, name: 'Andrej', surname: 'Badin' },
    //   { id: 2, name: 'John', surname: 'Appleseed' },
    // ];
    

    I am open to operator name, it could be $, $_, $pos, $idx or whatever.

    Prop used to find item position in collection could be specified if not default (id prop?), e.g.

    const updatedUsers = update(users, { $: { $merge: patchedUsers, identifier: 'key' } });
    

    Nice to have:

    • update multiple items in collection at once (could be possible if identifier is function which return boolean when update searches for matched items)
    opened by Andreyco 7
  • Update Broke - Weird Behaviour

    Update Broke - Weird Behaviour

    Hi

    Struggling with this weird behaviour

    It's simply understand my situation by reading the few lines of code and the few lines of console logs.

    How is this happening, what on Earth am I missing here.

                console.log("Trying");
                console.log({ index });
                console.log(account.access);
                console.log(account.access[index]);
                const _access = updateHelper(account.access, {
                    [0]: {
                        status: {
                            $set: "three"
                        }
                    }
                });
                console.log({ _access });
    
    Trying
    { index: 0 }
    [{"userDBID":"61e92c4d82661c026e8c7fcd","type":"owner","status":"pending","position":"Managing Director"}]
    {
      userDBID: '61e92c4d82661c026e8c7fcd',
      type: 'owner',
      status: 'pending',
      position: 'Managing Director'
    }
    error - TypeError: Class constructor CoreMongooseArray cannot be invoked without 'new'
        at copy (/Users/gaddbox/Documents/rentdodo/node_modules/immutability-helper/index.js:41:25)
        at /Users/gaddbox/Documents/rentdodo/node_modules/immutability-helper/index.js:104:38
        at Array.forEach (<anonymous>)
        at Context.update (/Users/gaddbox/Documents/rentdodo/node_modules/immutability-helper/index.js:85:26)
        at onAgencyVerified (webpack-internal:///./utils/server/contact.js:717:82)
        at runMicrotasks (<anonymous>)
        at processTicksAndRejections (internal/process/task_queues.js:97:5)
        at async onVerifyCheckup (webpack-internal:///./utils/server/contact.js:557:34)
        at async handler (webpack-internal:///./pages/api/account/update/address.js:38:5)
        at async Object.apiResolver (/Users/gaddbox/Documents/rentdodo/node_modules/next/dist/server/api-utils.js:101:9)
        at async DevServer.handleApiRequest (/Users/gaddbox/Documents/rentdodo/node_modules/next/dist/server/next-server.js:770:9)
        at async Object.fn (/Users/gaddbox/Documents/rentdodo/node_modules/next/dist/server/next-server.js:661:37)
        at async Router.execute (/Users/gaddbox/Documents/rentdodo/node_modules/next/dist/server/router.js:205:32)
        at async DevServer.run (/Users/gaddbox/Documents/rentdodo/node_modules/next/dist/server/next-server.js:841:29)
        at async DevServer.run (/Users/gaddbox/Documents/rentdodo/node_modules/next/dist/server/dev/next-dev-server.js:355:20)
        at async DevServer.handleRequest (/Users/gaddbox/Documents/rentdodo/node_modules/next/dist/server/next-server.js:292:20) {
      page: '/api/account/update/address'
    }
    

    Daniel

    opened by GaddMaster 1
  • Numeric keys don't work for Maps

    Numeric keys don't work for Maps

    The following test fails with TypeError: Cannot read property 'apple' of undefined:

      it('supports nested objects inside Maps with numeric keys', () => {
        const state = new Map([
          [42, { banana: 'yellow', apple: ['red'], blueberry: 'purple' }],
        ]);
    
        const updatedState = update(state, {
          [42]: { apple: { $set: ['green', 'red'] } },
        } as any);
    
        expect(updatedState).toEqual(
          new Map([
            [
              42,
              { banana: 'yellow', apple: ['green', 'red'], blueberry: 'purple' },
            ],
          ]),
        );
      });
    
    opened by infojunkie 2
  • TS2589: Type instantiation is excessively deep and possibly infinite

    TS2589: Type instantiation is excessively deep and possibly infinite

    After upgrading from TypeScript 3.5 to 3.8, I started getting error TS2589 on code that calls update with a Spec parameter. Here's a repro:

    import update, { Spec } from "immutability-helper";
    
    interface Widget {
    	name: string;
    }
    
    function f(widget: Widget, spec: Spec<Widget>): Widget {
    	return update(widget, spec); // <== TS2589
    }
    

    Compiler output:

    repro.ts:8:9 - error TS2589: Type instantiation is excessively deep and possibly infinite.
    
    8  return update(widget, spec);
              ~~~~~~~~~~~~~~~~~~~~
    

    The error seems to occur only when strict null checks are enabled.

    Is there something wrong with my code? Or is this a new limitation of the TypeScript compiler?

    opened by MichaelJLiu 4
  • Performance

    Performance

    Seems imutability-helper far away from Immutable.js when mutating. Are there any benchmarks or comparing imutability-helper with alternatives (seemless-immutable\timm\etc..)?

    opened by deser 18
  • $toggle support for Sets

    $toggle support for Sets

    For lists of checkboxes like this:

    image

    I like to store the IDs of the checked elements in a Set.

    To "toggle" a checkbox right now I have to do this:

    checkOccasion = id => ev => {
        const spec = {
            enabled: {
                [ev.target.checked ? '$add' : '$remove']: [id],
            }
        };
        this::updateState(spec);
    }
    
    <CheckboxLabel value={occ.id} checked={enabled.has(occ.id)} onChange={this.checkOccasion(occ.id)}>{occ.name}</CheckboxLabel>
    

    Would be nicer if instead I could just do:

    const spec = {
        enabled: {
            $toggle: [id],
        }
    };
    

    i.e. enabled is a Set, so if id is already contained in the set, it would be removed, otherwise it would be added.


    Actually, alternatively $toggle could accept an object [too] which would explicitly let you add or remove specific elements:

    const spec = {
        enabled: {
            $toggle: {
                [id]: ev.target.checked
            },
        }
    };
    

    That'd let us skip the .has check too.

    opened by mnpenner 1
Releases(v3.1.1)
Owner
Moshe Kolodny
Moshe Kolodny
ClojureScript's persistent data structures and supporting API from the comfort of vanilla JavaScript

mori A simple bridge to ClojureScript's persistent data structures and supporting APIs for vanilla JavaScript. Pull requests welcome. Breaking changes

David Nolen 3.4k Dec 31, 2022
A complete, fully tested and documented data structure library written in pure JavaScript.

Buckets A JavaScript Data Structure Library Buckets is a complete, fully tested and documented data structure library written in pure JavaScript. Incl

Mauricio 1.2k Jan 4, 2023
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Jan 7, 2023
A simulation data generator

Mock.js Mock.js is a simulation data generator to help the front-end to develop and prototype separate from the back-end progress and reduce some mono

高云 墨智 18.7k Jan 4, 2023
JSON-Schema + fake data generators

Use JSON Schema along with fake generators to provide consistent and meaningful fake data for your system. What's next? Breaking-changes towards v0.5.

JSON Schema Faker 2.9k Jan 4, 2023
🛠️construct-js is a library for creating byte level data structures.

??️construct-js is a library for creating byte level data structures.

Francis Stokes 1.3k Dec 14, 2022
⚡️The Fullstack React Framework — built on Next.js

The Fullstack React Framework "Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails Read the Documentation “Zero-API” data layer lets y

⚡️Blitz 12.5k Jan 4, 2023
Minecraft-classic - A working copy of the original classic.minecraft.net website.

minecraft-classic Just a copy of https://classic.minecraft.net/ (or at least as much as I could). This is a working copy of the Minecraft Classic webs

null 4 Oct 19, 2022
A2er - Fun browser extension, changing all words ending with `a` to end with `er`.

a2er Fun browser extension, changing all words ending with a to end with er. This started as a joke between friends and me, pronouncing words ending w

Sebastian Schicho 1 Jan 10, 2022
This is a simple tool to allow you to create WebM files with changing aspect ratios.

WackyWebM WackyWebM is a tool that allows you to create WebM video files with changing aspect ratios. If you're having issues, want to share your cust

OIRNOIR 564 Jan 4, 2023
This is a simple tool to allow you to create WebM files with changing aspect ratios.

WackyWebM WackyWebM is a tool that allows you to create WebM video files with changing aspect ratios. If you're having issues, want to share your cust

OIRNOIR 435 Aug 18, 2022
Your whole team, changing the world one stroke at a time.

Collanvas — Your whole team, changing the world one stroke at a time ?? With an online whiteboard, you can brainstorm ?? , draw art ??️ , and even pla

Eluda 12 Dec 29, 2022
🫥 Copy repository without the git information for self-managed gitlab, very fast.

English | 简体中文 degitlab ?? degitlab -> de-git-lab Copy repository without the git information for self-managed gitlab, very fast. Why? In self-managed

東澔 6 Oct 16, 2022
Copy/paste detecting GitHub Action for programming source code (jscpd)

dry-code Copy/paste detecting GitHub Action for programming source code with jscpd Action inputs Action input Description Default Value Required optio

null 5 Dec 14, 2022
JSON Visio is data visualization tool for your json data which seamlessly illustrates your data on graphs without having to restructure anything, paste directly or import file.

JSON Visio is data visualization tool for your json data which seamlessly illustrates your data on graphs without having to restructure anything, paste directly or import file.

Aykut Saraç 20.6k Jan 4, 2023
Low-level CSS Toolkit – the original Functional/Utility/Atomic CSS library

Basscss Low-level CSS toolkit – the original Functional CSS library https://basscss.com Lightning-Fast Modular CSS with No Side Effects Basscss is a l

Basscss 5.8k Dec 31, 2022
Our original Web Component library.

Polymer ℹ️ Note: This is the current stable version of the Polymer library. At Google I/O 2018 we announced a new Web Component base class, LitElement

Polymer Project 21.9k Jan 3, 2023
THE original Lightbox script (v2).

Lightbox2 The original lightbox script. Lightbox is small javascript library used to overlay images on top of the current page. It's a snap to setup a

Lokesh Dhakar 5.8k Jan 3, 2023
:clock8: The original jQuery plugin that makes it easy to support automatically updating fuzzy timestamps (e.g. "4 minutes ago").

timeago: a jQuery plugin Timeago is a jQuery plugin that makes it easy to support automatically updating fuzzy timestamps (e.g. "4 minutes ago" or "ab

Ryan McGeary 3.8k Dec 25, 2022
A flatbed solution - original version by V4M0N0S

Flatbed - Athena Framework Plugin Just for testing...no support! Installation Copy Folder client-plugins/gpFlatbed to your athena project under src/co

j0n4s 8 Dec 8, 2022