patch-recorder 0.0.1 → 0.1.0

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/src/utils.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type {PatchesOptions} from './types.js';
1
+ import type {PatchesOptions, GetItemIdConfig, GetItemIdFunction} from './types.js';
2
2
 
3
3
  /**
4
4
  * Type guard to check if a value is a plain object (not null, not array, not Map/Set)
@@ -46,15 +46,13 @@ export function formatPath(
46
46
  typeof options.internalPatchesOptions === 'object' &&
47
47
  options.internalPatchesOptions.pathAsArray === false
48
48
  ) {
49
- // Convert to JSON Pointer string format
50
- return path
51
- .map((part) => {
52
- if (typeof part === 'number') {
53
- return String(part);
54
- }
55
- return '/' + String(part).replace(/~/g, '~0').replace(/\//g, '~1');
56
- })
57
- .join('');
49
+ // Convert to JSON Pointer string format (RFC 6901)
50
+ if (path.length === 0) {
51
+ return '';
52
+ }
53
+ return '/' + path
54
+ .map((part) => String(part).replace(/~/g, '~0').replace(/\//g, '~1'))
55
+ .join('/');
58
56
  }
59
57
 
60
58
  return path;
@@ -148,3 +146,57 @@ export function cloneIfNeeded<T>(value: T): T {
148
146
  }
149
147
  return cloned;
150
148
  }
149
+
150
+ /**
151
+ * Find a getItemId function for a given path.
152
+ * The function is looked up by traversing the getItemId config object
153
+ * using the parent path (all elements except the last one).
154
+ *
155
+ * @example
156
+ * // For path ['items', 3] with config { items: (item) => item.id }
157
+ * // Returns the function (item) => item.id
158
+ */
159
+ export function findGetItemIdFn(
160
+ path: (string | number)[],
161
+ getItemIdConfig: GetItemIdConfig | undefined,
162
+ ): GetItemIdFunction | undefined {
163
+ if (!getItemIdConfig || path.length === 0) {
164
+ return undefined;
165
+ }
166
+
167
+ // We want to match the parent path (all elements except the last one)
168
+ // For path ['items', 3], we want to find config at 'items'
169
+ // For path ['user', 'settings', 'darkMode'], we want to find config at ['user', 'settings']
170
+ const parentPath = path.slice(0, -1);
171
+
172
+ if (parentPath.length === 0) {
173
+ // The path is directly under root (e.g., ['items'])
174
+ // In this case, there's no parent to match against
175
+ return undefined;
176
+ }
177
+
178
+ // Navigate the config object using the parent path
179
+ let current: GetItemIdConfig | GetItemIdFunction | undefined = getItemIdConfig;
180
+
181
+ for (let i = 0; i < parentPath.length; i++) {
182
+ const key = parentPath[i];
183
+
184
+ // Skip numeric indices (array positions) in the path
185
+ if (typeof key === 'number') {
186
+ continue;
187
+ }
188
+
189
+ if (current === undefined || typeof current !== 'object') {
190
+ return undefined;
191
+ }
192
+
193
+ current = (current as GetItemIdConfig)[key];
194
+ }
195
+
196
+ // current should now be a function or undefined
197
+ if (typeof current === 'function') {
198
+ return current as GetItemIdFunction;
199
+ }
200
+
201
+ return undefined;
202
+ }