All files main.js

98.05% Statements 101/103
91.66% Branches 11/12
100% Functions 1/1
98.05% Lines 101/103

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 1041x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 17x 17x 17x 17x 17x 17x 17x 17x 17x 3x 3x 14x 14x 14x 14x 14x 17x 11x     11x 11x 11x 4x 4x 7x 7x 7x 7x 10x 10x 17x 3x 3x 7x 7x 7x 7x 17x 1x 1x 1x 1x 1x  
/**
* @license Apache-2.0
*
* Copyright (c) 2026 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
 
'use strict';
 
// MODULES //
 
var isArray = require( '@stdlib/assert/is-array' );
var isPlainObject = require( '@stdlib/assert/is-plain-object' );
var format = require( '@stdlib/string/format' );
var copy = require( '@stdlib/utils/copy' );
var isSafeToken = require( '@stdlib/utils/json-pointer/base/assert/is-safe-token' );
var resolveToken = require( '@stdlib/utils/json-pointer/base/resolve-token' );
 
 
// MAIN //
 
/**
* Immutably sets a value at a path in a document using structural sharing.
*
* @param {(Object|Array)} document - source document
* @param {Array<string>} tokens - array of reference tokens
* @param {*} value - the value to set
* @throws {Error} tokens must not contain unsafe tokens
* @throws {TypeError} intermediate path segments must not reference non-traversable value
* @returns {(Object|Array)} new document with the value set at the specified path
*
* @example
* var doc = {
*     'a': {
*         'b': {
*             'c': 1
*         }
*     }
* };
* var out = set( doc, [ 'a', 'b', 'c' ], 99 );
* // returns { 'a': { 'b': { 'c': 99 } } }
*
* var bool = ( doc.a.b === out.a.b );
* // returns false
*
* bool = ( doc === out );
* // returns false
*/
function set( document, tokens, value ) {
	var current;
	var child;
	var root;
	var key;
	var i;
 
	// Root replacement:
	if ( tokens.length === 0 ) {
		return value;
	}
 
	root = copy( document, 1 );
	current = root;
 
	// Walk all tokens except the last, cloning each level:
	for ( i = 0; i < tokens.length - 1; i++ ) {
		if ( !isSafeToken( tokens[ i ] ) ) {
			throw new Error( format( 'invalid argument. Token contains an unsafe value. Value: `%s`.', tokens[ i ] ) );
		}
		key = resolveToken( current, tokens[ i ] );
 
		if ( !isPlainObject( current[ key ] ) && !isArray( current[ key ] ) ) {
			throw new TypeError( format( 'invalid argument. Path references a non-traversable value at token `%s`.', tokens[ i ] ) );
		}
		child = copy( current[ key ], 1 );
		current[ key ] = child;
		current = child;
	}
 
	// Set the value at the final token:
	if ( !isSafeToken( tokens[ tokens.length - 1 ] ) ) {
		throw new Error( format( 'invalid argument. Token contains an unsafe value. Value: `%s`.', tokens[ tokens.length - 1 ] ) );
	}
	key = resolveToken( current, tokens[ tokens.length - 1 ] );
	current[ key ] = value;
 
	return root;
}
 
 
// EXPORTS //
 
module.exports = set;