@byline/admin 3.2.0 → 3.2.1

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.
@@ -2,7 +2,7 @@
2
2
  import { jsx } from "react/jsx-runtime";
3
3
  import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
4
4
  import { normalizeHooks } from "@byline/core";
5
- import { get, set as external_lodash_es_set } from "lodash-es";
5
+ import { get, set as external_nested_path_js_set } from "./nested-path.js";
6
6
  const sameLocaleSet = (a, b)=>{
7
7
  if (a.length !== b.length) return false;
8
8
  const sa = [
@@ -84,7 +84,7 @@ const FormProvider = ({ children, initialData = {} })=>{
84
84
  const newFieldValues = {
85
85
  ...fieldValues.current
86
86
  };
87
- external_lodash_es_set(newFieldValues, name, value);
87
+ external_nested_path_js_set(newFieldValues, name, value);
88
88
  fieldValues.current = newFieldValues;
89
89
  dirtyFields.current.add(name);
90
90
  notifyFieldListeners(name, value);
@@ -0,0 +1,24 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ *
8
+ * Minimal nested `get`/`set` over string field paths, replacing lodash-es
9
+ * (which pulled a large shared chunk onto unrelated bundles). Supports the
10
+ * dot + bracket notation produced by the form field-path builders, e.g.
11
+ * `title`, `a.b.c`, `items[0].title`, `blocks[2].nested[1].field`.
12
+ *
13
+ * `set` mirrors lodash semantics: it creates intermediate **arrays** when the
14
+ * next path segment is a numeric index and plain **objects** otherwise, and it
15
+ * mutates `object` in place (callers shallow-copy the root first, as before).
16
+ *
17
+ * Deliberately NOT a general lodash replacement — it does not handle quoted
18
+ * keys (`a["b.c"]`), negative indices, or array-path inputs, none of which the
19
+ * form paths ever produce. See nested-path.test.node.ts for the covered cases.
20
+ */
21
+ /** Split a field path into segments: `items[0].title` -> ['items','0','title']. */
22
+ export declare function toPath(path: string): string[];
23
+ export declare function get<T = any>(object: unknown, path: string): T;
24
+ export declare function set<T extends object>(object: T, path: string, value: unknown): T;
@@ -0,0 +1,29 @@
1
+ const isIndexKey = (key)=>/^(?:0|[1-9]\d*)$/.test(key);
2
+ function toPath(path) {
3
+ return path.match(/[^.[\]]+/g) ?? [];
4
+ }
5
+ function get(object, path) {
6
+ if (null == object) return;
7
+ let current = object;
8
+ for (const key of toPath(path)){
9
+ if (null == current) return;
10
+ current = current[key];
11
+ }
12
+ return current;
13
+ }
14
+ function set(object, path, value) {
15
+ if (null == object) return object;
16
+ const keys = toPath(path);
17
+ if (0 === keys.length) return object;
18
+ let current = object;
19
+ for(let i = 0; i < keys.length - 1; i++){
20
+ const key = keys[i];
21
+ const nextKey = keys[i + 1];
22
+ const existing = current[key];
23
+ if (null == existing || 'object' != typeof existing) current[key] = isIndexKey(nextKey) ? [] : {};
24
+ current = current[key];
25
+ }
26
+ current[keys[keys.length - 1]] = value;
27
+ return object;
28
+ }
29
+ export { get, set, toPath };
@@ -0,0 +1,8 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+ export {};
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@byline/admin",
3
3
  "private": false,
4
4
  "license": "MPL-2.0",
5
- "version": "3.2.0",
5
+ "version": "3.2.1",
6
6
  "engines": {
7
7
  "node": ">=20.9.0"
8
8
  },
@@ -142,15 +142,14 @@
142
142
  "@tanstack/react-form-start": "^1.32.0",
143
143
  "classnames": "^2.5.1",
144
144
  "jose": "^6.2.3",
145
- "lodash-es": "^4.18.1",
146
145
  "react-diff-viewer-continued": "^4.2.2",
147
146
  "uuid": "^14.0.0",
148
147
  "zod": "^4.4.3",
149
148
  "zod-form-data": "^3.0.1",
150
- "@byline/auth": "3.2.0",
151
- "@byline/core": "3.2.0",
152
- "@byline/ui": "3.2.0",
153
- "@byline/i18n": "3.2.0"
149
+ "@byline/core": "3.2.1",
150
+ "@byline/i18n": "3.2.1",
151
+ "@byline/ui": "3.2.1",
152
+ "@byline/auth": "3.2.1"
154
153
  },
155
154
  "peerDependencies": {
156
155
  "react": "^19.0.0",
@@ -160,7 +159,6 @@
160
159
  "@biomejs/biome": "2.4.15",
161
160
  "@rsbuild/plugin-react": "^2.0.0",
162
161
  "@rslib/core": "^0.21.5",
163
- "@types/lodash-es": "^4.17.12",
164
162
  "@types/node": "^25.9.1",
165
163
  "@types/react": "19.2.15",
166
164
  "@types/react-dom": "19.2.3",
@@ -14,7 +14,12 @@ import { createContext, useCallback, useContext, useEffect, useRef, useState } f
14
14
  import type { Field, FieldBeforeChangeResult, FieldHookContext } from '@byline/core'
15
15
  import { normalizeHooks } from '@byline/core'
16
16
  import type { DocumentPatch, FieldSetPatch } from '@byline/core/patches'
17
- import { get as getNestedValue, set as setNestedValue } from 'lodash-es'
17
+
18
+ // Vendored nested get/set (see ./nested-path) — removes the lodash-es dep
19
+ // outright. A bare `from 'lodash-es'` import otherwise pools into a single
20
+ // ~85KB chunk that leaks onto the public frontend bundle (form-context is
21
+ // reachable from the layout graph).
22
+ import { get as getNestedValue, set as setNestedValue } from './nested-path'
18
23
 
19
24
  interface FormError {
20
25
  field: string
@@ -0,0 +1,85 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ */
8
+
9
+ import { describe, expect, it } from 'vitest'
10
+
11
+ import { get, set, toPath } from './nested-path'
12
+
13
+ describe('toPath', () => {
14
+ it('parses dot and bracket notation', () => {
15
+ expect(toPath('title')).toEqual(['title'])
16
+ expect(toPath('a.b.c')).toEqual(['a', 'b', 'c'])
17
+ expect(toPath('items[0].title')).toEqual(['items', '0', 'title'])
18
+ expect(toPath('blocks[2].nested[1].field')).toEqual(['blocks', '2', 'nested', '1', 'field'])
19
+ })
20
+ })
21
+
22
+ describe('get', () => {
23
+ const obj = { a: { b: 1, zero: 0, empty: '' }, items: [{ title: 'x' }, { title: 'y' }] }
24
+
25
+ it('reads nested, array, and mixed paths', () => {
26
+ expect(get(obj, 'a.b')).toBe(1)
27
+ expect(get(obj, 'items[0].title')).toBe('x')
28
+ expect(get(obj, 'items[1].title')).toBe('y')
29
+ })
30
+
31
+ it('preserves falsy values (does not conflate with missing)', () => {
32
+ expect(get(obj, 'a.zero')).toBe(0)
33
+ expect(get(obj, 'a.empty')).toBe('')
34
+ })
35
+
36
+ it('returns undefined for missing paths or nullish roots', () => {
37
+ expect(get(obj, 'a.x')).toBeUndefined()
38
+ expect(get(obj, 'missing.deep.path')).toBeUndefined()
39
+ expect(get(obj, 'items[5].title')).toBeUndefined()
40
+ expect(get(null, 'a.b')).toBeUndefined()
41
+ expect(get(undefined, 'a.b')).toBeUndefined()
42
+ })
43
+ })
44
+
45
+ describe('set', () => {
46
+ it('sets simple and nested values, creating intermediate objects', () => {
47
+ const o: any = {}
48
+ set(o, 'a.b.c', 1)
49
+ expect(o).toEqual({ a: { b: { c: 1 } } })
50
+ })
51
+
52
+ it('creates arrays for numeric index segments', () => {
53
+ const o: any = {}
54
+ set(o, 'items[0].title', 'x')
55
+ expect(Array.isArray(o.items)).toBe(true)
56
+ expect(o.items[0]).toEqual({ title: 'x' })
57
+ })
58
+
59
+ it('handles deep, mixed array/object paths', () => {
60
+ const o: any = {}
61
+ set(o, 'blocks[1].nested[0].field', 42)
62
+ expect(Array.isArray(o.blocks)).toBe(true)
63
+ expect(Array.isArray(o.blocks[1].nested)).toBe(true)
64
+ expect(o.blocks[1].nested[0].field).toBe(42)
65
+ })
66
+
67
+ it('overwrites existing values and preserves siblings', () => {
68
+ const o: any = { a: { b: 1, keep: 2 } }
69
+ set(o, 'a.b', 9)
70
+ expect(o).toEqual({ a: { b: 9, keep: 2 } })
71
+ })
72
+
73
+ it('writes into a pre-existing array element without clobbering the array', () => {
74
+ const o: any = { items: [{ title: 'x' }, { title: 'y' }] }
75
+ set(o, 'items[1].title', 'z')
76
+ expect(o.items[1].title).toBe('z')
77
+ expect(o.items[0].title).toBe('x')
78
+ expect(Array.isArray(o.items)).toBe(true)
79
+ })
80
+
81
+ it('returns the mutated root', () => {
82
+ const o: any = {}
83
+ expect(set(o, 'x', 1)).toBe(o)
84
+ })
85
+ })
@@ -0,0 +1,60 @@
1
+ /**
2
+ * This Source Code is subject to the terms of the Mozilla Public
3
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
4
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+ *
6
+ * Copyright (c) Infonomic Company Limited
7
+ *
8
+ * Minimal nested `get`/`set` over string field paths, replacing lodash-es
9
+ * (which pulled a large shared chunk onto unrelated bundles). Supports the
10
+ * dot + bracket notation produced by the form field-path builders, e.g.
11
+ * `title`, `a.b.c`, `items[0].title`, `blocks[2].nested[1].field`.
12
+ *
13
+ * `set` mirrors lodash semantics: it creates intermediate **arrays** when the
14
+ * next path segment is a numeric index and plain **objects** otherwise, and it
15
+ * mutates `object` in place (callers shallow-copy the root first, as before).
16
+ *
17
+ * Deliberately NOT a general lodash replacement — it does not handle quoted
18
+ * keys (`a["b.c"]`), negative indices, or array-path inputs, none of which the
19
+ * form paths ever produce. See nested-path.test.node.ts for the covered cases.
20
+ */
21
+
22
+ const isIndexKey = (key: string): boolean => /^(?:0|[1-9]\d*)$/.test(key)
23
+
24
+ /** Split a field path into segments: `items[0].title` -> ['items','0','title']. */
25
+ export function toPath(path: string): string[] {
26
+ return path.match(/[^.[\]]+/g) ?? []
27
+ }
28
+
29
+ // Returns `any` (not `T | undefined`) to match lodash's loose `get` contract,
30
+ // so existing call sites that treat the result as `any` keep type-checking.
31
+ export function get<T = any>(object: unknown, path: string): T {
32
+ if (object == null) return undefined as T
33
+ let current: any = object
34
+ for (const key of toPath(path)) {
35
+ if (current == null) return undefined as T
36
+ current = current[key]
37
+ }
38
+ return current as T
39
+ }
40
+
41
+ export function set<T extends object>(object: T, path: string, value: unknown): T {
42
+ if (object == null) return object
43
+ const keys = toPath(path)
44
+ if (keys.length === 0) return object
45
+
46
+ let current: any = object
47
+ for (let i = 0; i < keys.length - 1; i++) {
48
+ // Bounded by the loop condition, so these indexed reads are always defined.
49
+ const key = keys[i] as string
50
+ const nextKey = keys[i + 1] as string
51
+ const existing = current[key]
52
+ if (existing == null || typeof existing !== 'object') {
53
+ // Create the container the next segment needs: array for an index, else object.
54
+ current[key] = isIndexKey(nextKey) ? [] : {}
55
+ }
56
+ current = current[key]
57
+ }
58
+ current[keys[keys.length - 1] as string] = value
59
+ return object
60
+ }