@stacksjs/objects 0.75.451 → 0.75.457

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -83,3 +83,18 @@ lengths throw without changing the original. A plain source record replaces a ne
83
83
  Date, Map, Set or class target during `deepMerge`; those targets are not containers
84
84
  for JSON fields. The package ships both its root and the narrow `objects/transform`
85
85
  entry as JavaScript and declarations.
86
+
87
+ `omit` normalizes numeric exclusions. `objectMap` returns a partial record because
88
+ callbacks may remove entries. `setPath` preserves sparse slots and own enumerable
89
+ array siblings; its `SetPathResult` type follows known paths, with `unknown` for
90
+ dynamic paths and the replacement type for an empty path.
91
+
92
+ `deepMerge` tracks plain-record graphs per source overlay, preserving cycles and
93
+ sharing each source node's first destination. It updates the same target; arrays
94
+ and non-plain values remain atomic. Later sources use fresh graph tracking.
95
+
96
+ Path inference normalizes numeric/string aliases and distributes finite union
97
+ steps. Broad string/number/template steps and open index-signature object sources
98
+ return `unknown`; narrow before using them. Length changes drop fixed tuple index
99
+ guarantees, and generic array results include possible holes. Numeric-looking
100
+ named siblings such as `-1` and `1.5` retain their own value types.
package/dist/index.d.ts CHANGED
@@ -26,7 +26,7 @@ import type { DeepMerge } from '@stacksjs/types';
26
26
  * // { b: 2 }
27
27
  * ```
28
28
  */
29
- export declare function objectMap<K extends string, V, NK extends PropertyKey = K, NV = V>(obj: Record<K, V>, fn: (key: K, value: V) => [NK, NV] | undefined): Record<NK, NV>;
29
+ export declare function objectMap<K extends string, V, NK extends PropertyKey = K, NV = V>(obj: Record<K, V>, fn: (key: K, value: V) => [NK, NV] | undefined): Partial<Record<NK, NV>>;
30
30
  /**
31
31
  * Type guard for any key, `k`.
32
32
  * Marks `k` as a key of `T` if `k` is in `obj`.
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{notNullish}from"@stacksjs/utils";import{isObject}from"@stacksjs/validation";export*from"./transform";export function objectMap(obj,fn){return Object.fromEntries(Object.entries(obj).map(([k,v])=>fn(k,v)).filter(notNullish))}export function isKeyOf(obj,k){return k in obj}export function objectKeys(obj){return Object.keys(obj)}export function objectEntries(obj){return Object.entries(obj)}export function deepMerge(target,...sources){if(!sources.length)return target;const[source,...remaining]=sources;if(source===void 0)return target;if(isMergeableObject(target)&&isMergeableObject(source)){const into=target,from=source;for(const key of Object.keys(from)){if(key==="__proto__")continue;if(isPlainObject(from[key])){if(!Object.hasOwn(into,key)||!isPlainObject(into[key]))into[key]={};deepMerge(into[key],from[key])}else into[key]=from[key]}}return deepMerge(target,...remaining)}function isMergeableObject(item){return isObject(item)&&!Array.isArray(item)}function isPlainObject(item){if(typeof item!=="object"||item===null)return!1;const proto=Object.getPrototypeOf(item);return proto===null||proto===Object.prototype||Object.getPrototypeOf(proto)===null}export function objectPick(obj,keys,omitUndefined=!1){return Object.fromEntries(keys.filter((key)=>(key in obj)&&(!omitUndefined||obj[key]!==void 0)).map((key)=>[key,obj[key]]))}export function clearUndefined(obj){Object.keys(obj).forEach((key)=>obj[key]===void 0&&delete obj[key]);return obj}export function hasOwnProperty(obj,v){return obj==null?!1:Object.prototype.hasOwnProperty.call(obj,v)}
1
+ import{notNullish}from"@stacksjs/utils";import{isObject}from"@stacksjs/validation";export*from"./transform";export function objectMap(obj,fn){return Object.fromEntries(Object.entries(obj).map(([k,v])=>fn(k,v)).filter(notNullish))}export function isKeyOf(obj,k){return k in obj}export function objectKeys(obj){return Object.keys(obj)}export function objectEntries(obj){return Object.entries(obj)}export function deepMerge(target,...sources){for(const source of sources)if(isMergeableObject(target)&&isMergeableObject(source))mergeGraph(target,source,new WeakMap);return target}function mergeGraph(into,from,seen){seen.set(from,into);for(const key of Object.keys(from)){if(key==="__proto__")continue;const value=from[key];let merged=value;if(isPlainObject(value)){const mapped=seen.get(value);if(mapped)merged=mapped;else{const existing=Object.hasOwn(into,key)?into[key]:void 0,child=isPlainObject(existing)?existing:{};mergeGraph(child,value,seen);merged=child}}if(Object.hasOwn(into,key))into[key]=merged;else Object.defineProperty(into,key,{value:merged,writable:!0,enumerable:!0,configurable:!0})}}function isMergeableObject(item){return isObject(item)&&!Array.isArray(item)}function isPlainObject(item){if(typeof item!=="object"||item===null)return!1;const proto=Object.getPrototypeOf(item);if(proto===null||proto===Object.prototype)return!0;if(Object.getPrototypeOf(proto)!==null)return!1;const constructor=Object.getOwnPropertyDescriptor(proto,"constructor")?.value;return typeof constructor==="function"&&Function.prototype.toString.call(constructor)===Function.prototype.toString.call(Object)}export function objectPick(obj,keys,omitUndefined=!1){return Object.fromEntries(keys.filter((key)=>(key in obj)&&(!omitUndefined||obj[key]!==void 0)).map((key)=>[key,obj[key]]))}export function clearUndefined(obj){Object.keys(obj).forEach((key)=>obj[key]===void 0&&delete obj[key]);return obj}export function hasOwnProperty(obj,v){return obj==null?!1:Object.prototype.hasOwnProperty.call(obj,v)}
@@ -118,23 +118,7 @@ export declare function isEmptyObject(obj: object): boolean;
118
118
  * ```
119
119
  */
120
120
  export declare function getPath(source: unknown, path: readonly PathStep[], fallback?: unknown): unknown;
121
- /**
122
- * A copy with the value at `path` replaced, creating missing steps on the way.
123
- *
124
- * Only the objects along the path are copied - siblings are shared with the
125
- * original, which is what makes this cheap enough to use on every update. A
126
- * numeric step creates an array where a step is missing, so
127
- * `setPath({}, ['items', 0], x)` produces `{ items: [x] }` rather than
128
- * `{ items: { 0: x } }`.
129
- *
130
- * @category Object
131
- * @example
132
- * ```ts
133
- * setPath({ a: { b: 1 } }, ['a', 'b'], 2) // { a: { b: 2 } }
134
- * setPath({}, ['a', 'b'], 1) // { a: { b: 1 } }
135
- * ```
136
- */
137
- export declare function setPath<O extends object>(source: O, path: readonly PathStep[], value: unknown): O;
121
+ export declare function setPath<O extends object, const Path extends readonly PathStep[], V>(source: O, path: Path, value: V): SetPathResult<O, Path, V>;
138
122
  /**
139
123
  * A copy with every value replaced by what `transform` returns.
140
124
  *
@@ -151,3 +135,48 @@ export declare function setPath<O extends object>(source: O, path: readonly Path
151
135
  export type MappedStringValues<O extends object, R> = { [K in keyof O as K extends string | number ? K : never]: R }
152
136
  /** One step of a path: an object key or an array index. */
153
137
  export type PathStep = string | number;
138
+ /**
139
+ * A copy with the value at `path` replaced, creating missing steps on the way.
140
+ *
141
+ * Only the objects along the path are copied - siblings are shared with the
142
+ * original, which is what makes this cheap enough to use on every update. A
143
+ * numeric step creates an array where a step is missing, so
144
+ * `setPath({}, ['items', 0], x)` produces `{ items: [x] }` rather than
145
+ * `{ items: { 0: x } }`.
146
+ *
147
+ * @category Object
148
+ * @example
149
+ * ```ts
150
+ * setPath({ a: { b: 1 } }, ['a', 'b'], 2) // { a: { b: 2 } }
151
+ * setPath({}, ['a', 'b'], 1) // { a: { b: 1 } }
152
+ * ```
153
+ */
154
+ declare type KeyName<K> = K extends number ? `${K}` : K;
155
+ declare type MatchingKeys<S, K> = { [P in keyof S]-?: KeyName<P> extends KeyName<K> ? P : never }[keyof S];
156
+ declare type PathChild<S, K extends PropertyKey> = [MatchingKeys<S, K>] extends [never] ? undefined : S[MatchingKeys<S, K>];
157
+ declare type ArraySiblings<S extends readonly unknown[]> = Omit<S, keyof [...S]>;
158
+ declare type UpdatedArray<S extends readonly unknown[], K extends PathStep, Rest extends readonly PathStep[], V> = Array<S[number] | SetPathResult<PathChild<S, K>, Rest, V> | undefined>
159
+ & Omit<ArraySiblings<S>, MatchingKeys<ArraySiblings<S>, K>>
160
+ & { [P in K]: SetPathResult<PathChild<S, P>, Rest, V> }
161
+ declare type ArrayPathResult<S extends readonly unknown[], K extends PathStep, Rest extends readonly PathStep[], V> = K extends 'length' ? Array<S[number] | undefined> & Partial<ArraySiblings<S>>
162
+ : K extends number | `${number}`
163
+ ? number extends S['length'] ? UpdatedArray<S, K, Rest, V>
164
+ : `${K}` extends keyof S
165
+ ? { [I in keyof S]: I extends `${K}` ? SetPathResult<S[I], Rest, V> : S[I] }
166
+ : UpdatedArray<S, K, Rest, V>
167
+ : Omit<S, MatchingKeys<S, K>> & { [P in K]: SetPathResult<PathChild<S, P>, Rest, V> }
168
+ /** Known paths describe their updated shape; arbitrary or patterned steps return unknown. */
169
+ export type SetPathResult<S, Path extends readonly PathStep[], V> = number extends Path['length'] ? unknown
170
+ : Path extends readonly [] ? V
171
+ : Path extends readonly [infer K extends PathStep, ...infer Rest extends readonly PathStep[]]
172
+ ? K extends unknown
173
+ // An empty object satisfies open index signatures, but not a required literal key.
174
+ ? {} extends Record<K, never> ? unknown
175
+ : S extends readonly unknown[] ? ArrayPathResult<S, K, Rest, V>
176
+ : S extends object ? string extends keyof S ? unknown
177
+ : number extends keyof S ? unknown
178
+ : Omit<S, MatchingKeys<S, K>> & { [P in K]: SetPathResult<PathChild<S, P>, Rest, V> }
179
+ : K extends number ? ArrayPathResult<never[], K, Rest, V>
180
+ : { [P in K]: SetPathResult<undefined, Rest, V> }
181
+ : never
182
+ : unknown;
package/dist/transform.js CHANGED
@@ -1 +1 @@
1
- export function omit(obj,keys){const excluded=new Set(keys);return Object.fromEntries(Object.entries(obj).filter(([key])=>!excluded.has(key)))}export function pickBy(obj,predicate){return Object.fromEntries(Object.entries(obj).filter(([key,value])=>predicate(value,key)))}export function omitBy(obj,predicate){return pickBy(obj,(value,key)=>!predicate(value,key))}export function mapValues(obj,transform){return Object.fromEntries(Object.entries(obj).map(([key,value])=>[key,transform(value,key)]))}export function mapKeys(obj,transform){return Object.fromEntries(Object.entries(obj).map(([key,value])=>[transform(key,value),value]))}export function invert(obj){return Object.fromEntries(Object.entries(obj).map(([key,value])=>[String(value),key]))}export function isEmptyObject(obj){for(const _key in obj)if(Object.hasOwn(obj,_key))return!1;return!0}export function getPath(source,path,fallback){let current=source;for(const step of path){if(current===null||current===void 0)return fallback;current=current[step]}return current===void 0?fallback:current}export function setPath(source,path,value){if(path.length===0)return value;const[step,...rest]=path,container=Array.isArray(source)?[...source]:{...source};if(rest.length===0){defineKey(container,step,value);return container}const existing=Object.hasOwn(container,step)?container[step]:void 0,child=existing===null||typeof existing!=="object"?typeof rest[0]==="number"?[]:{}:existing;defineKey(container,step,setPath(child,rest,value));return container}function defineKey(target,key,value){if(Array.isArray(target)&&key==="length"){Object.defineProperty(target,key,{value});return}Object.defineProperty(target,key,{value,writable:!0,enumerable:!0,configurable:!0})}
1
+ export function omit(obj,keys){const excluded=new Set(keys.map((key)=>typeof key==="number"?String(key):key));return Object.fromEntries(Object.entries(obj).filter(([key])=>!excluded.has(key)))}export function pickBy(obj,predicate){return Object.fromEntries(Object.entries(obj).filter(([key,value])=>predicate(value,key)))}export function omitBy(obj,predicate){return pickBy(obj,(value,key)=>!predicate(value,key))}export function mapValues(obj,transform){return Object.fromEntries(Object.entries(obj).map(([key,value])=>[key,transform(value,key)]))}export function mapKeys(obj,transform){return Object.fromEntries(Object.entries(obj).map(([key,value])=>[transform(key,value),value]))}export function invert(obj){return Object.fromEntries(Object.entries(obj).map(([key,value])=>[String(value),key]))}export function isEmptyObject(obj){for(const _key in obj)if(Object.hasOwn(obj,_key))return!1;return!0}export function getPath(source,path,fallback){let current=source;for(const step of path){if(current===null||current===void 0)return fallback;current=current[step]}return current===void 0?fallback:current}export function setPath(source,path,value){if(path.length===0)return value;const[step,...rest]=path,container=Array.isArray(source)?copyArray(source):{...source};if(rest.length===0){defineKey(container,step,value);return container}const existing=Object.hasOwn(container,step)?container[step]:void 0,child=existing===null||typeof existing!=="object"?typeof rest[0]==="number"?[]:{}:existing;defineKey(container,step,setPath(child,rest,value));return container}function copyArray(source){const copy=Array(source.length);for(const key of Reflect.ownKeys(source)){if(!Object.prototype.propertyIsEnumerable.call(source,key))continue;defineKey(copy,key,source[key])}return copy}function defineKey(target,key,value){if(Array.isArray(target)&&key==="length"){Object.defineProperty(target,key,{value});return}Object.defineProperty(target,key,{value,writable:!0,enumerable:!0,configurable:!0})}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stacksjs/objects",
3
3
  "type": "module",
4
- "version": "0.75.451",
4
+ "version": "0.75.457",
5
5
  "description": "The Stacks objects.",
6
6
  "author": "Chris Breuer",
7
7
  "contributors": [
@@ -56,12 +56,12 @@
56
56
  "prepublishOnly": "bun run build"
57
57
  },
58
58
  "dependencies": {
59
- "@stacksjs/utils": "0.75.451",
60
- "@stacksjs/validation": "0.75.451"
59
+ "@stacksjs/utils": "0.75.457",
60
+ "@stacksjs/validation": "0.75.457"
61
61
  },
62
62
  "devDependencies": {
63
- "@stacksjs/collections": "0.75.451",
64
- "@stacksjs/types": "0.75.451",
63
+ "@stacksjs/collections": "0.75.457",
64
+ "@stacksjs/types": "0.75.457",
65
65
  "better-dx": "^0.2.24"
66
66
  },
67
67
  "sideEffects": false