@a2ui-sdk/utils 0.2.0 → 0.2.2

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
@@ -43,12 +43,17 @@ import {
43
43
  import {
44
44
  // Data binding utilities
45
45
  resolveValue,
46
+ resolveActionContext,
46
47
  resolveStringValue,
47
48
  resolveNumberValue,
48
49
  resolveBooleanValue,
49
50
  isPathBinding,
50
51
 
51
52
  // Path utilities
53
+ normalizePath,
54
+ isAbsolutePath,
55
+ joinPaths,
56
+ resolvePath,
52
57
  parsePath,
53
58
  joinPath,
54
59
  getValueAtPath,
@@ -67,6 +72,36 @@ const { interpolate, hasInterpolation } = v0_9
67
72
 
68
73
  ## API
69
74
 
75
+ ### Scope and Path Resolution (v0.8+)
76
+
77
+ Work with scoped data paths:
78
+
79
+ ```tsx
80
+ import {
81
+ normalizePath,
82
+ isAbsolutePath,
83
+ joinPaths,
84
+ resolvePath,
85
+ } from '@a2ui-sdk/utils/0.8'
86
+
87
+ // Normalize path format
88
+ normalizePath('user/name') // '/user/name'
89
+ normalizePath('/items/') // '/items'
90
+
91
+ // Check if path is absolute
92
+ isAbsolutePath('/user/name') // true
93
+ isAbsolutePath('name') // false
94
+
95
+ // Join base path with relative path
96
+ joinPaths('/items/0', 'name') // '/items/0/name'
97
+ joinPaths('/items', '../users') // '/users'
98
+
99
+ // Resolve path with scope
100
+ resolvePath('/user/name', '/items/0') // '/user/name' (absolute, ignores basePath)
101
+ resolvePath('name', '/items/0') // '/items/0/name' (relative, uses basePath)
102
+ resolvePath('name', null) // '/name' (treats relative as absolute when no basePath)
103
+ ```
104
+
70
105
  ### String Interpolation (v0.9)
71
106
 
72
107
  Process strings with embedded expressions:
@@ -102,6 +137,18 @@ resolveValue({ path: '/user/name' }, dataModel) // 'Alice'
102
137
  resolveValue('static value', dataModel) // 'static value'
103
138
  ```
104
139
 
140
+ `resolveValue` also supports scoped path resolution:
141
+
142
+ ```tsx
143
+ import { resolveValue } from '@a2ui-sdk/utils/0.9'
144
+
145
+ const dataModel = { items: [{ name: 'Item 1' }, { name: 'Item 2' }] }
146
+
147
+ // Resolve with basePath for scoped data access
148
+ resolveValue({ path: 'name' }, dataModel, '/items/0') // 'Item 1'
149
+ resolveValue({ path: '/items/1/name' }, dataModel, '/items/0') // 'Item 2' (absolute path ignores basePath)
150
+ ```
151
+
105
152
  ### Path Utilities
106
153
 
107
154
  Work with JSON Pointer paths (RFC 6901):
@@ -8,20 +8,24 @@ import type { ValueSource, DataModel, DataEntry, DataModelValue } from '@a2ui-sd
8
8
  *
9
9
  * @param source - The value source (literal or path reference)
10
10
  * @param dataModel - The data model for path lookups
11
+ * @param basePath - Optional base path for resolving relative paths (null for root scope)
11
12
  * @param defaultValue - Default value if source is undefined or path not found
12
13
  * @returns The resolved value
13
14
  *
14
15
  * @example
15
16
  * // Literal values
16
- * resolveValue({ literalString: "Hello" }, {}); // "Hello"
17
- * resolveValue({ literalNumber: 42 }, {}); // 42
17
+ * resolveValue({ literalString: "Hello" }, {}, null); // "Hello"
18
+ * resolveValue({ literalNumber: 42 }, {}, null); // 42
18
19
  *
19
- * // Path references
20
+ * // Path references (absolute paths)
20
21
  * const model = { user: { name: "John" } };
21
- * resolveValue({ path: "/user/name" }, model); // "John"
22
- * resolveValue({ path: "/user/age" }, model, 0); // 0 (default)
22
+ * resolveValue({ path: "/user/name" }, model, null); // "John"
23
+ * resolveValue({ path: "/user/age" }, model, null, 0); // 0 (default)
24
+ *
25
+ * // Path references (relative paths with scope)
26
+ * resolveValue({ path: "name" }, model, "/user"); // "John" (resolves to "/user/name")
23
27
  */
24
- export declare function resolveValue<T = unknown>(source: ValueSource | undefined, dataModel: DataModel, defaultValue?: T): T;
28
+ export declare function resolveValue<T = unknown>(source: ValueSource | undefined, dataModel: DataModel, basePath?: string | null, defaultValue?: T): T;
25
29
  /**
26
30
  * Converts a DataEntry array to a plain object.
27
31
  * This is used for processing dataModelUpdate message contents.
@@ -47,9 +51,10 @@ export declare function contentsToObject(contents: DataEntry[]): Record<string,
47
51
  *
48
52
  * @param context - Array of action context items
49
53
  * @param dataModel - The data model for path lookups
54
+ * @param basePath - Optional base path for resolving relative paths (null for root scope)
50
55
  * @returns A plain object with resolved context values
51
56
  */
52
57
  export declare function resolveActionContext(context: Array<{
53
58
  key: string;
54
59
  value: ValueSource;
55
- }> | undefined, dataModel: DataModel): Record<string, unknown>;
60
+ }> | undefined, dataModel: DataModel, basePath?: string | null): Record<string, unknown>;
@@ -2,26 +2,30 @@
2
2
  * Data binding utility functions for A2UI.
3
3
  * Handles resolving value sources and converting data entries.
4
4
  */
5
- import { getValueByPath } from './pathUtils.js';
5
+ import { getValueByPath, resolvePath } from './pathUtils.js';
6
6
  /**
7
7
  * Resolves a ValueSource to its actual value.
8
8
  *
9
9
  * @param source - The value source (literal or path reference)
10
10
  * @param dataModel - The data model for path lookups
11
+ * @param basePath - Optional base path for resolving relative paths (null for root scope)
11
12
  * @param defaultValue - Default value if source is undefined or path not found
12
13
  * @returns The resolved value
13
14
  *
14
15
  * @example
15
16
  * // Literal values
16
- * resolveValue({ literalString: "Hello" }, {}); // "Hello"
17
- * resolveValue({ literalNumber: 42 }, {}); // 42
17
+ * resolveValue({ literalString: "Hello" }, {}, null); // "Hello"
18
+ * resolveValue({ literalNumber: 42 }, {}, null); // 42
18
19
  *
19
- * // Path references
20
+ * // Path references (absolute paths)
20
21
  * const model = { user: { name: "John" } };
21
- * resolveValue({ path: "/user/name" }, model); // "John"
22
- * resolveValue({ path: "/user/age" }, model, 0); // 0 (default)
22
+ * resolveValue({ path: "/user/name" }, model, null); // "John"
23
+ * resolveValue({ path: "/user/age" }, model, null, 0); // 0 (default)
24
+ *
25
+ * // Path references (relative paths with scope)
26
+ * resolveValue({ path: "name" }, model, "/user"); // "John" (resolves to "/user/name")
23
27
  */
24
- export function resolveValue(source, dataModel, defaultValue) {
28
+ export function resolveValue(source, dataModel, basePath = null, defaultValue) {
25
29
  if (source === undefined || source === null) {
26
30
  return defaultValue;
27
31
  }
@@ -38,7 +42,9 @@ export function resolveValue(source, dataModel, defaultValue) {
38
42
  return source.literalArray;
39
43
  }
40
44
  if ('path' in source) {
41
- const value = getValueByPath(dataModel, source.path);
45
+ // Resolve path against base path (scope)
46
+ const resolvedPath = resolvePath(source.path, basePath);
47
+ const value = getValueByPath(dataModel, resolvedPath);
42
48
  if (value === undefined) {
43
49
  return defaultValue;
44
50
  }
@@ -104,15 +110,16 @@ function normalizeKey(key) {
104
110
  *
105
111
  * @param context - Array of action context items
106
112
  * @param dataModel - The data model for path lookups
113
+ * @param basePath - Optional base path for resolving relative paths (null for root scope)
107
114
  * @returns A plain object with resolved context values
108
115
  */
109
- export function resolveActionContext(context, dataModel) {
116
+ export function resolveActionContext(context, dataModel, basePath = null) {
110
117
  if (!context) {
111
118
  return {};
112
119
  }
113
120
  const result = {};
114
121
  for (const item of context) {
115
- result[item.key] = resolveValue(item.value, dataModel);
122
+ result[item.key] = resolveValue(item.value, dataModel, basePath);
116
123
  }
117
124
  return result;
118
125
  }
@@ -40,3 +40,63 @@ export declare function setValueByPath(dataModel: DataModel, path: string, value
40
40
  * @returns A new data model with the data merged
41
41
  */
42
42
  export declare function mergeAtPath(dataModel: DataModel, path: string, data: Record<string, unknown>): DataModel;
43
+ /**
44
+ * Normalizes a path to ensure it starts with "/" and has no trailing "/".
45
+ *
46
+ * @param path - The path to normalize
47
+ * @returns The normalized path
48
+ *
49
+ * @example
50
+ * normalizePath("user/name"); // "/user/name"
51
+ * normalizePath("/items/"); // "/items"
52
+ * normalizePath("/data"); // "/data"
53
+ * normalizePath(""); // "/"
54
+ */
55
+ export declare function normalizePath(path: string): string;
56
+ /**
57
+ * Checks if a path is absolute (starts with "/").
58
+ *
59
+ * @param path - The path to check
60
+ * @returns True if the path is absolute, false otherwise
61
+ *
62
+ * @example
63
+ * isAbsolutePath("/user/name"); // true
64
+ * isAbsolutePath("name"); // false
65
+ * isAbsolutePath(""); // false
66
+ */
67
+ export declare function isAbsolutePath(path: string): boolean;
68
+ /**
69
+ * Joins two paths together.
70
+ *
71
+ * @param basePath - The base path (should be absolute)
72
+ * @param relativePath - The relative path to append
73
+ * @returns The combined path
74
+ *
75
+ * @example
76
+ * joinPaths("/items/0", "name"); // "/items/0/name"
77
+ * joinPaths("/user", "profile/age"); // "/user/profile/age"
78
+ * joinPaths("/data", ""); // "/data"
79
+ */
80
+ export declare function joinPaths(basePath: string, relativePath: string): string;
81
+ /**
82
+ * Resolves a path against a base path (scope).
83
+ * - Absolute paths (starting with "/") are returned normalized, ignoring the base path
84
+ * - Relative paths are resolved against the base path if provided
85
+ * - If base path is null, relative paths are treated as absolute
86
+ *
87
+ * @param path - The path to resolve (absolute or relative)
88
+ * @param basePath - The base path for resolving relative paths (null for root scope)
89
+ * @returns The resolved absolute path
90
+ *
91
+ * @example
92
+ * // Absolute paths ignore base path
93
+ * resolvePath("/user/name", "/items/0"); // "/user/name"
94
+ *
95
+ * // Relative paths resolve against base path
96
+ * resolvePath("name", "/items/0"); // "/items/0/name"
97
+ * resolvePath("profile/age", "/user"); // "/user/profile/age"
98
+ *
99
+ * // Relative paths with null base path (root scope)
100
+ * resolvePath("name", null); // "/name"
101
+ */
102
+ export declare function resolvePath(path: string, basePath: string | null): string;
@@ -109,3 +109,98 @@ export function mergeAtPath(dataModel, path, data) {
109
109
  const merged = { ...currentObj, ...data };
110
110
  return setValueByPath(dataModel, path, merged);
111
111
  }
112
+ /**
113
+ * Normalizes a path to ensure it starts with "/" and has no trailing "/".
114
+ *
115
+ * @param path - The path to normalize
116
+ * @returns The normalized path
117
+ *
118
+ * @example
119
+ * normalizePath("user/name"); // "/user/name"
120
+ * normalizePath("/items/"); // "/items"
121
+ * normalizePath("/data"); // "/data"
122
+ * normalizePath(""); // "/"
123
+ */
124
+ export function normalizePath(path) {
125
+ if (!path) {
126
+ return '/';
127
+ }
128
+ // Ensure leading slash
129
+ let normalized = path.startsWith('/') ? path : `/${path}`;
130
+ // Remove trailing slash (unless it's the root path)
131
+ if (normalized.length > 1 && normalized.endsWith('/')) {
132
+ normalized = normalized.slice(0, -1);
133
+ }
134
+ return normalized;
135
+ }
136
+ /**
137
+ * Checks if a path is absolute (starts with "/").
138
+ *
139
+ * @param path - The path to check
140
+ * @returns True if the path is absolute, false otherwise
141
+ *
142
+ * @example
143
+ * isAbsolutePath("/user/name"); // true
144
+ * isAbsolutePath("name"); // false
145
+ * isAbsolutePath(""); // false
146
+ */
147
+ export function isAbsolutePath(path) {
148
+ return path.startsWith('/');
149
+ }
150
+ /**
151
+ * Joins two paths together.
152
+ *
153
+ * @param basePath - The base path (should be absolute)
154
+ * @param relativePath - The relative path to append
155
+ * @returns The combined path
156
+ *
157
+ * @example
158
+ * joinPaths("/items/0", "name"); // "/items/0/name"
159
+ * joinPaths("/user", "profile/age"); // "/user/profile/age"
160
+ * joinPaths("/data", ""); // "/data"
161
+ */
162
+ export function joinPaths(basePath, relativePath) {
163
+ if (!relativePath) {
164
+ return normalizePath(basePath);
165
+ }
166
+ // Remove leading slash from relative path if present
167
+ const cleanRelative = relativePath.startsWith('/')
168
+ ? relativePath.slice(1)
169
+ : relativePath;
170
+ // Combine paths
171
+ const combined = `${normalizePath(basePath)}/${cleanRelative}`;
172
+ return normalizePath(combined);
173
+ }
174
+ /**
175
+ * Resolves a path against a base path (scope).
176
+ * - Absolute paths (starting with "/") are returned normalized, ignoring the base path
177
+ * - Relative paths are resolved against the base path if provided
178
+ * - If base path is null, relative paths are treated as absolute
179
+ *
180
+ * @param path - The path to resolve (absolute or relative)
181
+ * @param basePath - The base path for resolving relative paths (null for root scope)
182
+ * @returns The resolved absolute path
183
+ *
184
+ * @example
185
+ * // Absolute paths ignore base path
186
+ * resolvePath("/user/name", "/items/0"); // "/user/name"
187
+ *
188
+ * // Relative paths resolve against base path
189
+ * resolvePath("name", "/items/0"); // "/items/0/name"
190
+ * resolvePath("profile/age", "/user"); // "/user/profile/age"
191
+ *
192
+ * // Relative paths with null base path (root scope)
193
+ * resolvePath("name", null); // "/name"
194
+ */
195
+ export function resolvePath(path, basePath) {
196
+ // Absolute paths are used as-is (ignore base path)
197
+ if (isAbsolutePath(path)) {
198
+ return normalizePath(path);
199
+ }
200
+ // Root scope (null or "/" base path) - treat relative path as absolute
201
+ if (basePath === null || basePath === '/') {
202
+ return normalizePath(path);
203
+ }
204
+ // Relative path with scope - join with base path
205
+ return joinPaths(basePath, path);
206
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2ui-sdk/utils",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "A2UI utilities",
5
5
  "homepage": "https://a2ui-sdk.js.org/",
6
6
  "repository": {
@@ -43,6 +43,6 @@
43
43
  "vitest": "^4.0.16"
44
44
  },
45
45
  "dependencies": {
46
- "@a2ui-sdk/types": "0.2.0"
46
+ "@a2ui-sdk/types": "0.2.2"
47
47
  }
48
48
  }