Full Code of fwilkerson/clean-set for AI

master 741c142a92cd cached
12 files
14.7 KB
4.6k tokens
4 symbols
1 requests
Download .txt
Repository: fwilkerson/clean-set
Branch: master
Commit: 741c142a92cd
Files: 12
Total size: 14.7 KB

Directory structure:
gitextract_bm5mcim6/

├── .gitignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── README.md
├── dist/
│   ├── clean-set.es.js
│   ├── clean-set.js
│   └── clean-set.modern.js
├── index.d.ts
├── lib/
│   └── index.js
├── package.json
└── tests/
    └── index.js

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# nyc test coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# TypeScript v1 declaration files
typings/

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variables file
.env

# next.js build output
.next

# Editor files
.vscode

# Testing area for myself
sandbox

coverage.lcov

================================================
FILE: .prettierrc
================================================
{
  "singleQuote": true,
  "trailingComma": "es5"
}


================================================
FILE: .travis.yml
================================================
after_success: npm run coverage
language: node_js
node_js:
  - "8"
cache:
  directories:
    - "node_modules"

================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 Frank Wilkerson

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.


================================================
FILE: README.md
================================================
# clean-set

[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier)
[![Build Status](https://travis-ci.org/fwilkerson/clean-set.svg?branch=master)](https://travis-ci.org/fwilkerson/clean-set)
[![codecov](https://codecov.io/gh/fwilkerson/clean-set/branch/master/graph/badge.svg)](https://codecov.io/gh/fwilkerson/clean-set)

> Quickly update a value in a deeply nested object and clone each node touched for simple change tracking `===`.

Check out [dset](https://github.com/lukeed/dset) if you just want to do an in place mutation on a deeply nested value.

## Install

> npm i clean-set

Includes builds for commonjs, umd, and esm and is less than ~~200b~~ 182b gzip (thanks to [@lukeed](https://github.com/lukeed))

## Usage

```javascript
let current = {
  a: { b: [], c: true },
  d: [],
  e: {
    f: { g: 'hello' },
    h: { i: 0 },
  },
};

let next = cleanSet(current, 'e.h.i', 1);

/**
 * Alternatively you can provide a function for the final parameter to
 * receive the current value of that node.
 *
 * let next = cleanSet(current, 'e.h.i', i => i + 1);
 */

// The value is assigned
console.log(next.e.h.i !== current.e.h.i); // true

// Each parent node touched is a new reference
console.log(next.e.h !== current.e.h); // true
console.log(next.e !== current.e); // true
console.log(next !== current); // true

// Untouched references remain the same
console.log(next.e.f === current.e.f); // true
console.log(next.a === current.a); // true
console.log(next.a.b === current.a.b); // true
console.log(next.d === current.d); // true
```

Here's what an object spread equivalent would look like.

```javascript
let next = {
  ...current,
  e: {
    ...current.e,
    h: { ...current.e.h, i: 1 },
  },
};
```

## Benchmarks

Check out the [es bench link](https://esbench.com/bench/5b16f1cbf2949800a0f61cf2) to run the benchmarks yourself.

> Note: YMMV canary and firefox dev have some impressive improvements for object assign and object spread respectively.

Chrome 67

<img src="./assets/chrome_67.png">


================================================
FILE: dist/clean-set.es.js
================================================
function r(r){var t=r&&r.pop?[]:{};for(var n in r)t[n]=r[n];return t}export default function(t,n,l){n.split&&(n=n.split("."));for(var o=r(t),a=o,e=0,f=n.length;e<f;e++)a=a[n[e]]=e===f-1?l&&l.call?l(a[n[e]]):l:r(a[n[e]]);return o}
//# sourceMappingURL=clean-set.es.js.map


================================================
FILE: dist/clean-set.js
================================================
function r(r){var n=r&&r.pop?[]:{};for(var t in r)n[t]=r[t];return n}module.exports=function(n,t,o){t.split&&(t=t.split("."));for(var l=r(n),e=l,i=0,p=t.length;i<p;i++)e=e[t[i]]=i===p-1?o&&o.call?o(e[t[i]]):o:r(e[t[i]]);return l};
//# sourceMappingURL=clean-set.js.map


================================================
FILE: dist/clean-set.modern.js
================================================
function t(t){let l=t&&t.pop?[]:{};for(let e in t)l[e]=t[e];return l}export default function(l,e,n){e.split&&(e=e.split("."));let r=t(l),o=r,f=0,i=e.length;for(;f<i;f++)o=o[e[f]]=f===i-1?n&&n.call?n(o[e[f]]):n:t(o[e[f]]);return r}
//# sourceMappingURL=clean-set.modern.js.map


================================================
FILE: index.d.ts
================================================
declare module 'clean-set' {
  function cleanSet<A>(
    source: A,
    keys: string | string[],
    update: <B>(value: B) => B
  ): A;
  function cleanSet<A>(source: A, keys: string | string[], update: any): A;
  /**
   * Update a value in a deeply nested object and clone each node touched
   * @param source The object to be updated
   * @param location The dot notation or array key path to the property you wish to update
   * @param update Either a function that will receive the current value and return a new value OR the new value for the node
   * @returns The new object
   */
  export default cleanSet;
}


================================================
FILE: lib/index.js
================================================
export default function(source, keys, update) {
  keys.split && (keys = keys.split('.'));

  let next = copy(source),
    last = next,
    i = 0,
    l = keys.length;

  for (; i < l; i++) {
    last = last[keys[i]] =
      i === l - 1
        ? update && !!update.call
          ? update(last[keys[i]])
          : update
        : copy(last[keys[i]]);
  }

  return next;
}

function copy(source) {
  let to = source && !!source.pop ? [] : {};
  for (let i in source) to[i] = source[i];
  return to;
}


================================================
FILE: package.json
================================================
{
  "name": "clean-set",
  "version": "1.1.2",
  "description": "A fast deep assignment alternative to the object spread operator and Object.assign",
  "umd:main": "dist/clean-set.min.js",
  "module": "dist/clean-set.es.js",
  "main": "dist/clean-set.js",
  "source": "lib/index.js",
  "typings": "index.d.ts",
  "scripts": {
    "build": "microbundle",
    "coverage": "nyc report --reporter=text-lcov > coverage.lcov && codecov",
    "tap": "tape -r esm tests/**/*.js | tap-difflet",
    "test": "nyc --reporter=text npm run tap"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fwilkerson/clean-set.git"
  },
  "keywords": [],
  "author": "Frank A. Wilkerson",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/fwilkerson/clean-set/issues"
  },
  "homepage": "https://github.com/fwilkerson/clean-set#readme",
  "devDependencies": {
    "codecov": "^3.2.0",
    "esm": "^3.2.18",
    "microbundle": "^0.12.0",
    "nyc": "^15.0.1",
    "prettier": "^1.16.4",
    "tap-difflet": "^0.7.1",
    "tape": "^4.10.1"
  }
}


================================================
FILE: tests/index.js
================================================
import test from 'tape';

import cleanSet from '../lib';

let data = {
  a: { b: [], c: true },
  d: [{ m: [{ n: 2 }] }],
  e: {
    f: { g: 'hello' },
    h: { i: 0, j: [] },
  },
};

test('clean-set: basic functionality', tap => {
  let next = cleanSet(data, 'e.h.i', 1);

  tap.assert(next != null, 'next has a value');
  tap.assert(next !== data, 'next has a new reference');

  tap.assert(next.e.h.i === 1, 'value was updated');
  tap.assert(next.e.h !== data.e.h, 'touched node has a new reference');
  tap.assert(next.e !== data.e, 'touched node has a new reference');

  tap.assert(next.a === data.a, 'untouched node kept their reference');
  tap.assert(next.a.b === data.a.b, 'untouched node kept their reference');
  tap.assert(next.d === data.d, 'untouched node kept their reference');
  tap.assert(
    next.d[0].m === data.d[0].m,
    'untouched node kept their reference'
  );
  tap.assert(
    next.d[0].m[0] === data.d[0].m[0],
    'untouched node kept their reference'
  );
  tap.assert(next.e.f === data.e.f, 'untouched node kept their reference');
  tap.assert(next.e.h.j === data.e.h.j, 'untouched node kept their reference');

  tap.end();
});

test('clean-set: update value as a function', tap => {
  let next = cleanSet(data, 'e.h.j', j => j.concat('some-item'));

  tap.assert(next != null, 'next has a value');
  tap.assert(next !== data, 'next has a new reference');

  tap.assert(next.e.h.j[0] === 'some-item', 'value was updated');
  tap.assert(next.e.h.j !== data.e.h.j, 'touched node has a new reference');
  tap.assert(next.e.h !== data.e.h, 'touched node has a new reference');
  tap.assert(next.e !== data.e, 'touched node has a new reference');

  tap.assert(next.a === data.a, 'untouched node kept their reference');
  tap.assert(next.a.b === data.a.b, 'untouched node kept their reference');
  tap.assert(next.d === data.d, 'untouched node kept their reference');
  tap.assert(
    next.d[0].m === data.d[0].m,
    'untouched node kept their reference'
  );
  tap.assert(
    next.d[0].m[0] === data.d[0].m[0],
    'untouched node kept their reference'
  );
  tap.assert(next.e.f === data.e.f, 'untouched node kept their reference');

  tap.end();
});

test('clean-set: array key', tap => {
  let next = cleanSet(data, ['e', 'h', 'i'], i => i + 1);

  tap.assert(next != null, 'next has a value');
  tap.assert(next !== data, 'next has a new reference');

  tap.assert(next.e.h.i === 1, 'value was updated');
  tap.assert(next.e.h !== data.e.h, 'touched node has a new reference');
  tap.assert(next.e !== data.e, 'touched node has a new reference');

  tap.assert(next.a === data.a, 'untouched node kept their reference');
  tap.assert(next.a.b === data.a.b, 'untouched node kept their reference');
  tap.assert(next.d === data.d, 'untouched node kept their reference');
  tap.assert(
    next.d[0].m === data.d[0].m,
    'untouched node kept their reference'
  );
  tap.assert(
    next.d[0].m[0] === data.d[0].m[0],
    'untouched node kept their reference'
  );
  tap.assert(next.e.f === data.e.f, 'untouched node kept their reference');
  tap.assert(next.e.h.j === data.e.h.j, 'untouched node kept their reference');

  tap.end();
});

test('clean-set: creates an object if none exists', tap => {
  let next = cleanSet(data, 'e.h.k.l', true);

  tap.assert(next != null, 'next has a value');
  tap.assert(next !== data, 'next has a new reference');

  tap.same(next.e.h.k, { l: true }, 'object was created and value was added');
  tap.assert(next.e.h !== data.e.h, 'touched node has a new reference');
  tap.assert(next.e !== data.e, 'touched node has a new reference');

  tap.assert(next.a === data.a, 'untouched node kept their reference');
  tap.assert(next.a.b === data.a.b, 'untouched node kept their reference');
  tap.assert(next.d === data.d, 'untouched node kept their reference');
  tap.assert(
    next.d[0].m === data.d[0].m,
    'untouched node kept their reference'
  );
  tap.assert(
    next.d[0].m[0] === data.d[0].m[0],
    'untouched node kept their reference'
  );
  tap.assert(next.e.f === data.e.f, 'untouched node kept their reference');
  tap.assert(next.e.h.j === data.e.h.j, 'untouched node kept their reference');

  tap.end();
});

test('clean-set: supports accessing an index of an array', tap => {
  // let next = cleanSet(data, 'd.0.m.0.n', n => n + 1); // either will work for this scenario
  let next = cleanSet(data, ['d', 0, 'm', 0, 'n'], n => n + 1);

  tap.assert(next != null, 'next has a value');
  tap.assert(next !== data, 'next has a new reference');

  tap.assert(next.d[0].m[0].n === 3, 'value was updated');
  tap.assert(
    next.d[0].m[0] !== data.d[0].m[0],
    'touched node has a new reference'
  );
  tap.assert(next.d[0].m !== data.d[0].m, 'touched node has a new reference');
  tap.assert(next.d[0] !== data.d[0], 'touched node has a new reference');
  tap.assert(next.d !== data.d, 'touched node has a new reference');

  tap.assert(next.a === data.a, 'untouched node kept their reference');
  tap.assert(next.a.b === data.a.b, 'untouched node kept their reference');
  tap.assert(next.e.f === data.e.f, 'untouched node kept their reference');
  tap.assert(next.e.h.i === data.e.h.i, 'untouched node kept their reference');
  tap.assert(next.e.h.j === data.e.h.j, 'untouched node kept their reference');

  tap.end();
});

test('clean-set: creates a record at the index if none exists', tap => {
  let next = cleanSet(data, ['d', 2, 'o'], { p: 'creates at index' });

  tap.assert(next != null, 'next has a value');
  tap.assert(next !== data, 'next has a new reference');
  tap.same(next.d[2].o, { p: 'creates at index' }, 'value was created');
  tap.assert(next.d !== data.d, 'touched node has a new reference');

  tap.assert(next.a === data.a, 'untouched node kept their reference');
  tap.assert(next.a.b === data.a.b, 'untouched node kept their reference');
  tap.assert(
    next.d[0].m === data.d[0].m,
    'untouched node kept their reference'
  );
  tap.assert(
    next.d[0].m[0] === data.d[0].m[0],
    'untouched node kept their reference'
  );
  tap.assert(next.e.f === data.e.f, 'untouched node kept their reference');
  tap.assert(next.e.h.i === data.e.h.i, 'untouched node kept their reference');
  tap.assert(next.e.h.j === data.e.h.j, 'untouched node kept their reference');

  tap.end();
});

test('clean-set: allows setting to null', tap => {
  let next = cleanSet(data, 'q.r', null);

  tap.assert(next != null, 'next has a value');
  tap.assert(next !== data, 'next has a new reference');

  tap.same(next.q, { r: null } , 'object was created and value set to null');

  tap.end();
});


test('clean-set: performance benchmark', tap => {
  let cData = data,
    n = 0,
    start = +Date.now();
  for (; n < 100000; n++) {
    cData = cleanSet(cData, 'e.h.i', i => i + 1);
  }
  let timeTaken = +Date.now() - start;

  tap.assert(timeTaken < 300, `cleanSet benchmark ran in ${timeTaken}ms`);
  tap.end();
});

test('Object.assign: performance benchmark', tap => {
  let cData = data,
    n = 0,
    start = +Date.now();

  for (; n < 100000; n++) {
    cData = Object.assign({}, cData, {
      e: Object.assign({}, cData.e, {
        h: Object.assign({}, cData.e.h, { i: cData.e.h.i + 1 }),
      }),
    });
  }
  let timeTaken = +Date.now() - start;

  tap.assert(timeTaken < 300, `Object.assign benchmark ran in ${timeTaken}ms`);

  tap.end();
});

test('Object spread: performance benchmark', tap => {
  let cData = data,
    n = 0,
    start = +Date.now();

  for (; n < 100000; n++) {
    cData = {
      ...cData,
      e: { ...cData.e, h: { ...cData.e.h, i: cData.e.h.i + 1 } },
    };
  }
  let timeTaken = +Date.now() - start;

  tap.assert(timeTaken < 300, `Object spread benchmark ran in ${timeTaken}ms`);

  tap.end();
});
Download .txt
gitextract_bm5mcim6/

├── .gitignore
├── .prettierrc
├── .travis.yml
├── LICENSE
├── README.md
├── dist/
│   ├── clean-set.es.js
│   ├── clean-set.js
│   └── clean-set.modern.js
├── index.d.ts
├── lib/
│   └── index.js
├── package.json
└── tests/
    └── index.js
Download .txt
SYMBOL INDEX (4 symbols across 4 files)

FILE: dist/clean-set.es.js
  function r (line 1) | function r(r){var t=r&&r.pop?[]:{};for(var n in r)t[n]=r[n];return t}

FILE: dist/clean-set.js
  function r (line 1) | function r(r){var n=r&&r.pop?[]:{};for(var t in r)n[t]=r[t];return n}

FILE: dist/clean-set.modern.js
  function t (line 1) | function t(t){let l=t&&t.pop?[]:{};for(let e in t)l[e]=t[e];return l}

FILE: lib/index.js
  function copy (line 21) | function copy(source) {
Condensed preview — 12 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (16K chars).
[
  {
    "path": ".gitignore",
    "chars": 987,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Directo"
  },
  {
    "path": ".prettierrc",
    "chars": 52,
    "preview": "{\n  \"singleQuote\": true,\n  \"trailingComma\": \"es5\"\n}\n"
  },
  {
    "path": ".travis.yml",
    "chars": 109,
    "preview": "after_success: npm run coverage\nlanguage: node_js\nnode_js:\n  - \"8\"\ncache:\n  directories:\n    - \"node_modules\""
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2018 Frank Wilkerson\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "README.md",
    "chars": 2105,
    "preview": "# clean-set\n\n[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](ht"
  },
  {
    "path": "dist/clean-set.es.js",
    "chars": 271,
    "preview": "function r(r){var t=r&&r.pop?[]:{};for(var n in r)t[n]=r[n];return t}export default function(t,n,l){n.split&&(n=n.split("
  },
  {
    "path": "dist/clean-set.js",
    "chars": 269,
    "preview": "function r(r){var n=r&&r.pop?[]:{};for(var t in r)n[t]=r[t];return n}module.exports=function(n,t,o){t.split&&(t=t.split("
  },
  {
    "path": "dist/clean-set.modern.js",
    "chars": 276,
    "preview": "function t(t){let l=t&&t.pop?[]:{};for(let e in t)l[e]=t[e];return l}export default function(l,e,n){e.split&&(e=e.split("
  },
  {
    "path": "index.d.ts",
    "chars": 617,
    "preview": "declare module 'clean-set' {\n  function cleanSet<A>(\n    source: A,\n    keys: string | string[],\n    update: <B>(value: "
  },
  {
    "path": "lib/index.js",
    "chars": 504,
    "preview": "export default function(source, keys, update) {\n  keys.split && (keys = keys.split('.'));\n\n  let next = copy(source),\n  "
  },
  {
    "path": "package.json",
    "chars": 1059,
    "preview": "{\n  \"name\": \"clean-set\",\n  \"version\": \"1.1.2\",\n  \"description\": \"A fast deep assignment alternative to the object spread"
  },
  {
    "path": "tests/index.js",
    "chars": 7754,
    "preview": "import test from 'tape';\n\nimport cleanSet from '../lib';\n\nlet data = {\n  a: { b: [], c: true },\n  d: [{ m: [{ n: 2 }] }]"
  }
]

About this extraction

This page contains the full source code of the fwilkerson/clean-set GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 12 files (14.7 KB), approximately 4.6k tokens, and a symbol index with 4 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!