@web-ts-toolkit/access-router 0.44.0 → 0.46.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/processors.d.mts CHANGED
@@ -47,6 +47,97 @@ interface CopyAndDepopulateOptions {
47
47
  */
48
48
  idField?: string;
49
49
  }
50
+ /**
51
+ * A path copy operation consumed by {@link copyPaths} and the `COPY` task.
52
+ * Reuses the same `{ src, dest }` shape as {@link ProcessCopy}.
53
+ */
54
+ type ProcessPathCopy = ProcessCopy;
55
+ /**
56
+ * A path move operation consumed by {@link movePaths} and the `MOVE` task.
57
+ * Reuses the same `{ src, dest }` shape as {@link ProcessCopy}.
58
+ */
59
+ type ProcessPathMove = ProcessCopy;
60
+ /**
61
+ * A slice operation consumed by {@link sliceArrays} and the `SLICE` task.
62
+ */
63
+ interface ProcessSlice {
64
+ /**
65
+ * Dotted source path of the array to slice. Intermediate segments fan out
66
+ * across arrays like {@link copyAndDepopulate}. Missing, null, or
67
+ * non-array leaves are safe no-ops.
68
+ */
69
+ src: string;
70
+ /**
71
+ * Dotted destination path (relative to the leaf parent) where the sliced
72
+ * window is written. When omitted, the slice is applied in place at `src`.
73
+ * Empty-string destinations are safe no-ops.
74
+ */
75
+ dest?: string;
76
+ /**
77
+ * Number of leading array members to skip. Defaults to `0`. Must be a
78
+ * non-negative safe integer when present.
79
+ */
80
+ skip?: number;
81
+ /**
82
+ * Maximum number of array members to keep from `skip`. When omitted, the
83
+ * tail from `skip` to the end is kept. When present it is clamped to
84
+ * `maxSlice` (default `1000`). Must be a non-negative safe integer.
85
+ */
86
+ limit?: number;
87
+ }
88
+ /**
89
+ * A count operation consumed by {@link countPaths} and the `COUNT` task.
90
+ */
91
+ interface ProcessCount {
92
+ /**
93
+ * Dotted source path whose size is measured. Arrays measure length, plain
94
+ * objects measure key count, present scalars count as `1`, and
95
+ * null/undefined leaves count as `0`. Missing leaves are safe no-ops.
96
+ */
97
+ src: string;
98
+ /**
99
+ * Dotted destination path (relative to the leaf parent) where the numeric
100
+ * count is written. Empty destinations are safe no-ops.
101
+ */
102
+ dest: string;
103
+ }
104
+ /**
105
+ * A mask operation consumed by {@link maskPaths} and the `MASK` task.
106
+ */
107
+ interface ProcessMask {
108
+ /**
109
+ * Dotted source path to redact. Each resolved leaf present on its parent
110
+ * is replaced with `replacement`. Missing leaves are safe no-ops.
111
+ */
112
+ src: string;
113
+ /**
114
+ * Replacement value written at `src`. Defaults to `'***'` when the key is
115
+ * absent. An explicitly provided value (including `null`) is used as-is.
116
+ */
117
+ replacement?: unknown;
118
+ }
119
+ /**
120
+ * Shared mutable/clone option for path processors.
121
+ */
122
+ interface ProcessorOptions {
123
+ /**
124
+ * When `true` (default), `docObject` is mutated in place and returned.
125
+ * When `false`, a deep clone is produced via `cloneDeep` and the input is
126
+ * left untouched.
127
+ */
128
+ mutable?: boolean;
129
+ }
130
+ /**
131
+ * Options for {@link sliceArrays}.
132
+ */
133
+ interface SliceProcessorOptions extends ProcessorOptions {
134
+ /**
135
+ * Upper bound applied to a client-requested `limit`. Defaults to `1000`,
136
+ * matching the `listHardLimit` default. Must be a non-negative safe
137
+ * integer when present.
138
+ */
139
+ maxSlice?: number;
140
+ }
50
141
  /**
51
142
  * Conservative default output for {@link copyAndDepopulate}.
52
143
  *
@@ -90,5 +181,64 @@ type CopyAndDepopulateOutput = Record<string, unknown>;
90
181
  * `constructor`, or when a populated record is missing the configured id field.
91
182
  */
92
183
  declare const copyAndDepopulate: <Output extends object = CopyAndDepopulateOutput>(docObject: object, operations: ProcessCopy[], options?: CopyAndDepopulateOptions) => Output;
184
+ /**
185
+ * Conservative default output for path processors.
186
+ */
187
+ type ProcessorOutput = Record<string, unknown>;
188
+ /**
189
+ * Copy the value at `src` to `dest` without modifying `src`.
190
+ *
191
+ * Semantics mirror {@link copyAndDepopulate} traversal: intermediate segments
192
+ * fan out across arrays, missing/null/scalar intermediates are safe no-ops,
193
+ * and unsafe (`__proto__`, `prototype`, `constructor`) or empty paths are
194
+ * handled the same way. The copied value is deep-cloned so later tasks in
195
+ * the chain cannot alias `src` through `dest`. Copying a path onto itself
196
+ * is a safe no-op.
197
+ *
198
+ * @throws Error when a path contains an unsafe segment.
199
+ */
200
+ declare const copyPaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessCopy[], options?: ProcessorOptions) => Output;
201
+ /**
202
+ * Move the value at `src` to `dest` and delete `src`.
203
+ *
204
+ * Traversal and safety semantics match {@link copyPaths}. Moving a path onto
205
+ * itself is a safe no-op. A `dest` nested under the moved leaf (for example
206
+ * `src: 'a'`, `dest: 'a.b'`) copies without deleting `src` so the freshly
207
+ * written destination is not removed.
208
+ *
209
+ * @throws Error when a path contains an unsafe segment.
210
+ */
211
+ declare const movePaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessCopy[], options?: ProcessorOptions) => Output;
212
+ /**
213
+ * Slice the array at `src` to `[skip, skip + limit)` and write the window to
214
+ * `dest` (default: in place at `src`).
215
+ *
216
+ * Missing, null, or non-array leaves are safe no-ops. `skip` defaults to `0`;
217
+ * an omitted `limit` keeps the tail from `skip`. A present `limit` is clamped
218
+ * to `maxSlice` (default `1000`) so client-requested windows stay bounded.
219
+ *
220
+ * @throws Error when a path contains an unsafe segment or when
221
+ * `skip`/`limit`/`maxSlice` is not a non-negative safe integer.
222
+ */
223
+ declare const sliceArrays: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessSlice[], options?: SliceProcessorOptions) => Output;
224
+ /**
225
+ * Write the size of `src` to `dest`: array length, plain-object key count,
226
+ * `0` for null/undefined leaves, and `1` for other present scalars.
227
+ *
228
+ * Missing leaves are safe no-ops (no destination is created).
229
+ *
230
+ * @throws Error when a path contains an unsafe segment.
231
+ */
232
+ declare const countPaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessCount[], options?: ProcessorOptions) => Output;
233
+ /**
234
+ * Replace the value at `src` with `replacement` (default `'***'`).
235
+ *
236
+ * Only existing leaves are redacted; missing paths are safe no-ops and no
237
+ * new keys are created. Intermediate segments fan out across arrays like
238
+ * {@link copyAndDepopulate}.
239
+ *
240
+ * @throws Error when a path contains an unsafe segment.
241
+ */
242
+ declare const maskPaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessMask[], options?: ProcessorOptions) => Output;
93
243
 
94
- export { type CopyAndDepopulateOptions, type CopyAndDepopulateOutput, type ProcessCopy, copyAndDepopulate };
244
+ export { type CopyAndDepopulateOptions, type CopyAndDepopulateOutput, type ProcessCopy, type ProcessCount, type ProcessMask, type ProcessPathCopy, type ProcessPathMove, type ProcessSlice, type ProcessorOptions, type ProcessorOutput, type SliceProcessorOptions, copyAndDepopulate, copyPaths, countPaths, maskPaths, movePaths, sliceArrays };
package/processors.d.ts CHANGED
@@ -47,6 +47,97 @@ interface CopyAndDepopulateOptions {
47
47
  */
48
48
  idField?: string;
49
49
  }
50
+ /**
51
+ * A path copy operation consumed by {@link copyPaths} and the `COPY` task.
52
+ * Reuses the same `{ src, dest }` shape as {@link ProcessCopy}.
53
+ */
54
+ type ProcessPathCopy = ProcessCopy;
55
+ /**
56
+ * A path move operation consumed by {@link movePaths} and the `MOVE` task.
57
+ * Reuses the same `{ src, dest }` shape as {@link ProcessCopy}.
58
+ */
59
+ type ProcessPathMove = ProcessCopy;
60
+ /**
61
+ * A slice operation consumed by {@link sliceArrays} and the `SLICE` task.
62
+ */
63
+ interface ProcessSlice {
64
+ /**
65
+ * Dotted source path of the array to slice. Intermediate segments fan out
66
+ * across arrays like {@link copyAndDepopulate}. Missing, null, or
67
+ * non-array leaves are safe no-ops.
68
+ */
69
+ src: string;
70
+ /**
71
+ * Dotted destination path (relative to the leaf parent) where the sliced
72
+ * window is written. When omitted, the slice is applied in place at `src`.
73
+ * Empty-string destinations are safe no-ops.
74
+ */
75
+ dest?: string;
76
+ /**
77
+ * Number of leading array members to skip. Defaults to `0`. Must be a
78
+ * non-negative safe integer when present.
79
+ */
80
+ skip?: number;
81
+ /**
82
+ * Maximum number of array members to keep from `skip`. When omitted, the
83
+ * tail from `skip` to the end is kept. When present it is clamped to
84
+ * `maxSlice` (default `1000`). Must be a non-negative safe integer.
85
+ */
86
+ limit?: number;
87
+ }
88
+ /**
89
+ * A count operation consumed by {@link countPaths} and the `COUNT` task.
90
+ */
91
+ interface ProcessCount {
92
+ /**
93
+ * Dotted source path whose size is measured. Arrays measure length, plain
94
+ * objects measure key count, present scalars count as `1`, and
95
+ * null/undefined leaves count as `0`. Missing leaves are safe no-ops.
96
+ */
97
+ src: string;
98
+ /**
99
+ * Dotted destination path (relative to the leaf parent) where the numeric
100
+ * count is written. Empty destinations are safe no-ops.
101
+ */
102
+ dest: string;
103
+ }
104
+ /**
105
+ * A mask operation consumed by {@link maskPaths} and the `MASK` task.
106
+ */
107
+ interface ProcessMask {
108
+ /**
109
+ * Dotted source path to redact. Each resolved leaf present on its parent
110
+ * is replaced with `replacement`. Missing leaves are safe no-ops.
111
+ */
112
+ src: string;
113
+ /**
114
+ * Replacement value written at `src`. Defaults to `'***'` when the key is
115
+ * absent. An explicitly provided value (including `null`) is used as-is.
116
+ */
117
+ replacement?: unknown;
118
+ }
119
+ /**
120
+ * Shared mutable/clone option for path processors.
121
+ */
122
+ interface ProcessorOptions {
123
+ /**
124
+ * When `true` (default), `docObject` is mutated in place and returned.
125
+ * When `false`, a deep clone is produced via `cloneDeep` and the input is
126
+ * left untouched.
127
+ */
128
+ mutable?: boolean;
129
+ }
130
+ /**
131
+ * Options for {@link sliceArrays}.
132
+ */
133
+ interface SliceProcessorOptions extends ProcessorOptions {
134
+ /**
135
+ * Upper bound applied to a client-requested `limit`. Defaults to `1000`,
136
+ * matching the `listHardLimit` default. Must be a non-negative safe
137
+ * integer when present.
138
+ */
139
+ maxSlice?: number;
140
+ }
50
141
  /**
51
142
  * Conservative default output for {@link copyAndDepopulate}.
52
143
  *
@@ -90,5 +181,64 @@ type CopyAndDepopulateOutput = Record<string, unknown>;
90
181
  * `constructor`, or when a populated record is missing the configured id field.
91
182
  */
92
183
  declare const copyAndDepopulate: <Output extends object = CopyAndDepopulateOutput>(docObject: object, operations: ProcessCopy[], options?: CopyAndDepopulateOptions) => Output;
184
+ /**
185
+ * Conservative default output for path processors.
186
+ */
187
+ type ProcessorOutput = Record<string, unknown>;
188
+ /**
189
+ * Copy the value at `src` to `dest` without modifying `src`.
190
+ *
191
+ * Semantics mirror {@link copyAndDepopulate} traversal: intermediate segments
192
+ * fan out across arrays, missing/null/scalar intermediates are safe no-ops,
193
+ * and unsafe (`__proto__`, `prototype`, `constructor`) or empty paths are
194
+ * handled the same way. The copied value is deep-cloned so later tasks in
195
+ * the chain cannot alias `src` through `dest`. Copying a path onto itself
196
+ * is a safe no-op.
197
+ *
198
+ * @throws Error when a path contains an unsafe segment.
199
+ */
200
+ declare const copyPaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessCopy[], options?: ProcessorOptions) => Output;
201
+ /**
202
+ * Move the value at `src` to `dest` and delete `src`.
203
+ *
204
+ * Traversal and safety semantics match {@link copyPaths}. Moving a path onto
205
+ * itself is a safe no-op. A `dest` nested under the moved leaf (for example
206
+ * `src: 'a'`, `dest: 'a.b'`) copies without deleting `src` so the freshly
207
+ * written destination is not removed.
208
+ *
209
+ * @throws Error when a path contains an unsafe segment.
210
+ */
211
+ declare const movePaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessCopy[], options?: ProcessorOptions) => Output;
212
+ /**
213
+ * Slice the array at `src` to `[skip, skip + limit)` and write the window to
214
+ * `dest` (default: in place at `src`).
215
+ *
216
+ * Missing, null, or non-array leaves are safe no-ops. `skip` defaults to `0`;
217
+ * an omitted `limit` keeps the tail from `skip`. A present `limit` is clamped
218
+ * to `maxSlice` (default `1000`) so client-requested windows stay bounded.
219
+ *
220
+ * @throws Error when a path contains an unsafe segment or when
221
+ * `skip`/`limit`/`maxSlice` is not a non-negative safe integer.
222
+ */
223
+ declare const sliceArrays: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessSlice[], options?: SliceProcessorOptions) => Output;
224
+ /**
225
+ * Write the size of `src` to `dest`: array length, plain-object key count,
226
+ * `0` for null/undefined leaves, and `1` for other present scalars.
227
+ *
228
+ * Missing leaves are safe no-ops (no destination is created).
229
+ *
230
+ * @throws Error when a path contains an unsafe segment.
231
+ */
232
+ declare const countPaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessCount[], options?: ProcessorOptions) => Output;
233
+ /**
234
+ * Replace the value at `src` with `replacement` (default `'***'`).
235
+ *
236
+ * Only existing leaves are redacted; missing paths are safe no-ops and no
237
+ * new keys are created. Intermediate segments fan out across arrays like
238
+ * {@link copyAndDepopulate}.
239
+ *
240
+ * @throws Error when a path contains an unsafe segment.
241
+ */
242
+ declare const maskPaths: <Output extends object = ProcessorOutput>(docObject: object, operations: ProcessMask[], options?: ProcessorOptions) => Output;
93
243
 
94
- export { type CopyAndDepopulateOptions, type CopyAndDepopulateOutput, type ProcessCopy, copyAndDepopulate };
244
+ export { type CopyAndDepopulateOptions, type CopyAndDepopulateOutput, type ProcessCopy, type ProcessCount, type ProcessMask, type ProcessPathCopy, type ProcessPathMove, type ProcessSlice, type ProcessorOptions, type ProcessorOutput, type SliceProcessorOptions, copyAndDepopulate, copyPaths, countPaths, maskPaths, movePaths, sliceArrays };
package/processors.js CHANGED
@@ -19,28 +19,68 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
19
19
  // src/processors.ts
20
20
  var processors_exports = {};
21
21
  __export(processors_exports, {
22
- copyAndDepopulate: () => copyAndDepopulate
22
+ copyAndDepopulate: () => copyAndDepopulate,
23
+ copyPaths: () => copyPaths,
24
+ countPaths: () => countPaths,
25
+ maskPaths: () => maskPaths,
26
+ movePaths: () => movePaths,
27
+ sliceArrays: () => sliceArrays
23
28
  });
24
29
  module.exports = __toCommonJS(processors_exports);
25
30
  var import_utils = require("@web-ts-toolkit/utils");
26
31
  var UNSAFE_PATH_KEYS = /* @__PURE__ */ new Set(["__proto__", "prototype", "constructor"]);
27
- function failUnsafeSegment(path) {
32
+ var DEFAULT_MAX_SLICE = 1e3;
33
+ var DEFAULT_MASK_REPLACEMENT = "***";
34
+ function failUnsafeSegment(path, fn = "copyAndDepopulate") {
28
35
  throw new Error(
29
- `copyAndDepopulate: refusing path '${path}' because it contains an unsafe segment (__proto__, prototype, or constructor).`
36
+ `${fn}: refusing path '${path}' because it contains an unsafe segment (__proto__, prototype, or constructor).`
30
37
  );
31
38
  }
32
39
  function failMissingId(path) {
33
40
  throw new Error(`copyAndDepopulate: path '${path}' resolved to a record missing the configured id field.`);
34
41
  }
35
- function assertSafePath(path) {
42
+ function failInvalidSlice(value, field, fn) {
43
+ throw new Error(`${fn}: '${field}' must be a non-negative safe integer, received ${String(value)}.`);
44
+ }
45
+ function assertSafePath(path, fn = "copyAndDepopulate") {
36
46
  const segs = path.split(".");
37
47
  for (const seg of segs) {
38
48
  if (seg.length === 0) continue;
39
49
  if (UNSAFE_PATH_KEYS.has(seg)) {
40
- failUnsafeSegment(path);
50
+ failUnsafeSegment(path, fn);
41
51
  }
42
52
  }
43
53
  }
54
+ function assertNonNegativeSafeInteger(value, field, fn) {
55
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
56
+ failInvalidSlice(value, field, fn);
57
+ }
58
+ }
59
+ function resolveLeafParents(obj, src) {
60
+ const segs = src.split(".");
61
+ let targets = [obj];
62
+ for (let ind = 0; ind < segs.length - 1; ind += 1) {
63
+ const seg = segs[ind];
64
+ targets = targets.reduce((ret, target) => {
65
+ if (!(0, import_utils.isPlainObject)(target)) return ret;
66
+ const targetObject = target;
67
+ const next = targetObject[seg];
68
+ if ((0, import_utils.isArray)(next)) {
69
+ for (const item of next) {
70
+ ret.push(item);
71
+ }
72
+ } else if (next !== null && next !== void 0) ret.push(next);
73
+ return ret;
74
+ }, []);
75
+ }
76
+ const leaf = segs[segs.length - 1];
77
+ const pairs = [];
78
+ (0, import_utils.forEach)(targets, (target) => {
79
+ if (!(0, import_utils.isPlainObject)(target)) return;
80
+ pairs.push({ parent: target, leaf });
81
+ });
82
+ return pairs;
83
+ }
44
84
  var copyAndDepopulate = (docObject, operations, options = { mutable: true, idField: "_id" }) => {
45
85
  const mutable = options.mutable !== false;
46
86
  const idField = options.idField ?? "_id";
@@ -104,7 +144,131 @@ var copyAndDepopulate = (docObject, operations, options = { mutable: true, idFie
104
144
  });
105
145
  return obj;
106
146
  };
147
+ var copyPaths = (docObject, operations, options = { mutable: true }) => {
148
+ const mutable = options.mutable !== false;
149
+ const obj = mutable ? docObject : (0, import_utils.cloneDeep)(docObject);
150
+ (0, import_utils.forEach)(Array.isArray(operations) ? operations : [], (op) => {
151
+ if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
152
+ const src = op.src;
153
+ const dest = op.dest;
154
+ if (src.length === 0 || dest.length === 0) return;
155
+ assertSafePath(src, "copyPaths");
156
+ assertSafePath(dest, "copyPaths");
157
+ if (src === dest) return;
158
+ (0, import_utils.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
159
+ if (!(0, import_utils.hasOwn)(parent, leaf)) return;
160
+ (0, import_utils.set)(parent, dest, (0, import_utils.cloneDeep)(parent[leaf]));
161
+ });
162
+ });
163
+ return obj;
164
+ };
165
+ var movePaths = (docObject, operations, options = { mutable: true }) => {
166
+ const mutable = options.mutable !== false;
167
+ const obj = mutable ? docObject : (0, import_utils.cloneDeep)(docObject);
168
+ (0, import_utils.forEach)(Array.isArray(operations) ? operations : [], (op) => {
169
+ if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
170
+ const src = op.src;
171
+ const dest = op.dest;
172
+ if (src.length === 0 || dest.length === 0) return;
173
+ assertSafePath(src, "movePaths");
174
+ assertSafePath(dest, "movePaths");
175
+ if (src === dest) return;
176
+ (0, import_utils.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
177
+ if (!(0, import_utils.hasOwn)(parent, leaf)) return;
178
+ const value = (0, import_utils.cloneDeep)(parent[leaf]);
179
+ (0, import_utils.set)(parent, dest, value);
180
+ if (dest === leaf || dest.startsWith(`${leaf}.`)) return;
181
+ delete parent[leaf];
182
+ });
183
+ });
184
+ return obj;
185
+ };
186
+ var sliceArrays = (docObject, operations, options = { mutable: true, maxSlice: DEFAULT_MAX_SLICE }) => {
187
+ const mutable = options.mutable !== false;
188
+ const maxSlice = options.maxSlice ?? DEFAULT_MAX_SLICE;
189
+ assertNonNegativeSafeInteger(maxSlice, "maxSlice", "sliceArrays");
190
+ const obj = mutable ? docObject : (0, import_utils.cloneDeep)(docObject);
191
+ (0, import_utils.forEach)(Array.isArray(operations) ? operations : [], (op) => {
192
+ if (!op || typeof op.src !== "string") return;
193
+ const src = op.src;
194
+ if (src.length === 0) return;
195
+ assertSafePath(src, "sliceArrays");
196
+ const rawDest = typeof op.dest === "undefined" ? void 0 : op.dest;
197
+ if (typeof rawDest !== "undefined") {
198
+ if (typeof rawDest !== "string" || rawDest.length === 0) return;
199
+ assertSafePath(rawDest, "sliceArrays");
200
+ }
201
+ const skip = op.skip ?? 0;
202
+ assertNonNegativeSafeInteger(skip, "skip", "sliceArrays");
203
+ let limit;
204
+ if (typeof op.limit !== "undefined") {
205
+ assertNonNegativeSafeInteger(op.limit, "limit", "sliceArrays");
206
+ limit = Math.min(op.limit, maxSlice);
207
+ }
208
+ (0, import_utils.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
209
+ const leafValue = parent[leaf];
210
+ if (!(0, import_utils.isArray)(leafValue)) return;
211
+ const end = typeof limit === "undefined" ? void 0 : skip + limit;
212
+ const window = leafValue.slice(skip, end);
213
+ if (typeof rawDest === "undefined") {
214
+ (0, import_utils.set)(parent, leaf, window);
215
+ } else {
216
+ (0, import_utils.set)(parent, rawDest, window);
217
+ }
218
+ });
219
+ });
220
+ return obj;
221
+ };
222
+ var countPaths = (docObject, operations, options = { mutable: true }) => {
223
+ const mutable = options.mutable !== false;
224
+ const obj = mutable ? docObject : (0, import_utils.cloneDeep)(docObject);
225
+ (0, import_utils.forEach)(Array.isArray(operations) ? operations : [], (op) => {
226
+ if (!op || typeof op.src !== "string" || typeof op.dest !== "string") return;
227
+ const src = op.src;
228
+ const dest = op.dest;
229
+ if (src.length === 0 || dest.length === 0) return;
230
+ assertSafePath(src, "countPaths");
231
+ assertSafePath(dest, "countPaths");
232
+ (0, import_utils.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
233
+ if (!(0, import_utils.hasOwn)(parent, leaf)) return;
234
+ const leafValue = parent[leaf];
235
+ let count;
236
+ if ((0, import_utils.isArray)(leafValue)) {
237
+ count = leafValue.length;
238
+ } else if ((0, import_utils.isPlainObject)(leafValue)) {
239
+ count = Object.keys(leafValue).length;
240
+ } else if (leafValue === null || typeof leafValue === "undefined") {
241
+ count = 0;
242
+ } else {
243
+ count = 1;
244
+ }
245
+ (0, import_utils.set)(parent, dest, count);
246
+ });
247
+ });
248
+ return obj;
249
+ };
250
+ var maskPaths = (docObject, operations, options = { mutable: true }) => {
251
+ const mutable = options.mutable !== false;
252
+ const obj = mutable ? docObject : (0, import_utils.cloneDeep)(docObject);
253
+ (0, import_utils.forEach)(Array.isArray(operations) ? operations : [], (op) => {
254
+ if (!op || typeof op.src !== "string") return;
255
+ const src = op.src;
256
+ if (src.length === 0) return;
257
+ assertSafePath(src, "maskPaths");
258
+ const replacement = Object.prototype.hasOwnProperty.call(op, "replacement") ? op.replacement : DEFAULT_MASK_REPLACEMENT;
259
+ (0, import_utils.forEach)(resolveLeafParents(obj, src), ({ parent, leaf }) => {
260
+ if (!(0, import_utils.hasOwn)(parent, leaf)) return;
261
+ (0, import_utils.set)(parent, leaf, replacement);
262
+ });
263
+ });
264
+ return obj;
265
+ };
107
266
  // Annotate the CommonJS export names for ESM import in node:
108
267
  0 && (module.exports = {
109
- copyAndDepopulate
268
+ copyAndDepopulate,
269
+ copyPaths,
270
+ countPaths,
271
+ maskPaths,
272
+ movePaths,
273
+ sliceArrays
110
274
  });