ngx-json-render 0.7.5 → 0.7.6

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.
@@ -1,9 +1,8 @@
1
1
  import { NgComponentOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { signal, Injectable, inject, computed, untracked, effect, isDevMode, DestroyRef, InjectionToken, input, output, viewChild, afterNextRender, ChangeDetectionStrategy, Component, Injector, forwardRef } from '@angular/core';
3
+ import { signal, Injectable, isDevMode, inject, computed, untracked, effect, DestroyRef, InjectionToken, input, output, viewChild, afterNextRender, ChangeDetectionStrategy, Component, Injector, forwardRef } from '@angular/core';
4
4
  import { createStateStore, getByPath, runValidation, resolveAction, nextActionDispatchId, notifyActionDispatch, notifyActionSettle, executeAction, isDevtoolsActive, subscribeDevtoolsActive, resolveRepeatStatePath, evaluateVisibility, resolveElementProps, resolveBindings, resolveActionParam, autoFixSpec, validateSpec, createDirectiveRegistry, resolveRepeatItemStatePath, removeByPath, addByPath, setByPath, SPEC_DATA_PART_TYPE, nestedToFlat, createMixedStreamParser, defineSchema } from '@json-render/core';
5
5
  export { autoFixSpec, createStateStore, formatSpecIssues, nestedToFlat, validateSpec } from '@json-render/core';
6
- import { flattenToPointers } from '@json-render/core/store-utils';
7
6
 
8
7
  const EMPTY = signal(undefined, ...(ngDevMode ? [{ debugName: "EMPTY" }] : /* istanbul ignore next */ []));
9
8
  /**
@@ -49,6 +48,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.22", ngImpo
49
48
  type: Injectable
50
49
  }] });
51
50
 
51
+ /** Nesting below this is kept as one leaf, the same cut-off core uses. */
52
+ const MAX_FLATTEN_DEPTH = 20;
53
+ /** Escape one key for a JSON Pointer: `~` → `~0`, `/` → `~1`. */
54
+ function escapePointerSegment(key) {
55
+ return key.replace(/~/g, '~0').replace(/\//g, '~1');
56
+ }
57
+ /**
58
+ * Flatten plain objects into a `{ pointer: leaf }` map; arrays and anything
59
+ * that is not a plain object are leaves.
60
+ *
61
+ * Core's `flattenToPointers` does the same but joins keys unescaped, so a key
62
+ * such as `a/b` comes back as `/a/b` and a store write puts it at `a` → `b`.
63
+ * Every path built here is escaped, so it reads back the key it came from.
64
+ */
65
+ function flattenToPointers(obj) {
66
+ const result = {};
67
+ const seen = new Set();
68
+ let warned = false;
69
+ const walk = (value, prefix, depth) => {
70
+ for (const [key, child] of Object.entries(value)) {
71
+ const pointer = `${prefix}/${escapePointerSegment(key)}`;
72
+ if (isPlainObject(child) && !seen.has(child)) {
73
+ if (depth < MAX_FLATTEN_DEPTH) {
74
+ seen.add(child);
75
+ walk(child, pointer, depth + 1);
76
+ continue;
77
+ }
78
+ if (isDevMode() && !warned) {
79
+ warned = true;
80
+ console.warn(`[ngx-json-render] state nested deeper than ${MAX_FLATTEN_DEPTH} levels is treated as one value.`);
81
+ }
82
+ }
83
+ result[pointer] = child;
84
+ }
85
+ };
86
+ walk(obj, '', 0);
87
+ return result;
88
+ }
89
+ function isPlainObject(value) {
90
+ return (value !== null &&
91
+ typeof value === 'object' &&
92
+ Object.getPrototypeOf(value) === Object.prototype);
93
+ }
94
+
52
95
  /**
53
96
  * The state store of a `<json-render>` subtree.
54
97
  *
@@ -222,7 +265,7 @@ function diffState(prev, next) {
222
265
  changes.push({ path, value: b });
223
266
  const keys = new Set([...childKeys(a), ...childKeys(b)]);
224
267
  for (const key of keys) {
225
- walk(child(a, key), child(b, key), `${path}/${escapePointer(key)}`);
268
+ walk(child(a, key), child(b, key), `${path}/${escapePointerSegment(key)}`);
226
269
  }
227
270
  };
228
271
  walk(prev, next, '');
@@ -236,9 +279,6 @@ function child(value, key) {
236
279
  ? value[key]
237
280
  : undefined;
238
281
  }
239
- function escapePointer(key) {
240
- return key.replace(/~/g, '~0').replace(/\//g, '~1');
241
- }
242
282
  /**
243
283
  * Inject the state store of the nearest `<json-render>` renderer.
244
284
  * Must be called from within the renderer's subtree (e.g. a catalog
@@ -410,17 +450,9 @@ class JsonRenderValidationService {
410
450
  /** Validate a single field and record the result. */
411
451
  validate(path, config) {
412
452
  const currentState = this.state.getSnapshot();
413
- const segments = path.split('/').filter(Boolean);
414
- let value = currentState;
415
- for (const seg of segments) {
416
- if (value != null && typeof value === 'object') {
417
- value = value[seg];
418
- }
419
- else {
420
- value = undefined;
421
- break;
422
- }
423
- }
453
+ // The store's own pointer walk, so `~1` and `~0` mean what they mean
454
+ // everywhere else a path is read.
455
+ const value = getByPath(currentState, path);
424
456
  const result = runValidation(config, {
425
457
  value,
426
458
  stateModel: currentState,