@a2ui-sdk/utils 0.1.1 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@a2ui-sdk/utils",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "A2UI utilities",
5
5
  "homepage": "https://a2ui-sdk.js.org/",
6
6
  "repository": {
@@ -11,6 +11,7 @@
11
11
  "type": "module",
12
12
  "main": "./dist/index.js",
13
13
  "types": "./dist/index.d.ts",
14
+ "sideEffects": false,
14
15
  "exports": {
15
16
  ".": {
16
17
  "types": "./dist/index.d.ts",
@@ -29,8 +30,8 @@
29
30
  "dist"
30
31
  ],
31
32
  "scripts": {
32
- "dev": "tsc -b --watch",
33
- "build": "tsc -b",
33
+ "dev": "tsc -b tsconfig.build.json --watch",
34
+ "build": "tsc -b tsconfig.build.json",
34
35
  "test": "vitest",
35
36
  "test:run": "vitest run",
36
37
  "test:coverage": "vitest run --coverage"
@@ -42,6 +43,6 @@
42
43
  "vitest": "^4.0.16"
43
44
  },
44
45
  "dependencies": {
45
- "@a2ui-sdk/types": "0.1.1"
46
+ "@a2ui-sdk/types": "0.2.0"
46
47
  }
47
48
  }
@@ -1,6 +0,0 @@
1
- /**
2
- * dataBinding Tests
3
- *
4
- * Tests for data binding utility functions used in A2UI.
5
- */
6
- export {};
@@ -1,271 +0,0 @@
1
- /**
2
- * dataBinding Tests
3
- *
4
- * Tests for data binding utility functions used in A2UI.
5
- */
6
- import { describe, it, expect } from 'vitest';
7
- import { resolveValue, contentsToObject, resolveActionContext, } from './dataBinding.js';
8
- describe('dataBinding', () => {
9
- describe('resolveValue', () => {
10
- const testModel = {
11
- user: {
12
- name: 'John',
13
- age: 30,
14
- active: true,
15
- },
16
- items: ['a', 'b', 'c'],
17
- count: 42,
18
- };
19
- describe('with undefined/null source', () => {
20
- it('should return default value when source is undefined', () => {
21
- expect(resolveValue(undefined, testModel, 'default')).toBe('default');
22
- });
23
- it('should return default value when source is null', () => {
24
- expect(resolveValue(null, testModel, 'default')).toBe('default');
25
- });
26
- it('should return undefined when no default provided and source is undefined', () => {
27
- expect(resolveValue(undefined, testModel)).toBeUndefined();
28
- });
29
- });
30
- describe('with literal values', () => {
31
- it('should resolve literalString', () => {
32
- const source = { literalString: 'Hello' };
33
- expect(resolveValue(source, testModel)).toBe('Hello');
34
- });
35
- it('should resolve empty literalString', () => {
36
- const source = { literalString: '' };
37
- expect(resolveValue(source, testModel, 'default')).toBe('');
38
- });
39
- it('should resolve literalNumber', () => {
40
- const source = { literalNumber: 42 };
41
- expect(resolveValue(source, testModel)).toBe(42);
42
- });
43
- it('should resolve zero literalNumber', () => {
44
- const source = { literalNumber: 0 };
45
- expect(resolveValue(source, testModel, 99)).toBe(0);
46
- });
47
- it('should resolve negative literalNumber', () => {
48
- const source = { literalNumber: -10 };
49
- expect(resolveValue(source, testModel)).toBe(-10);
50
- });
51
- it('should resolve literalBoolean true', () => {
52
- const source = { literalBoolean: true };
53
- expect(resolveValue(source, testModel)).toBe(true);
54
- });
55
- it('should resolve literalBoolean false', () => {
56
- const source = { literalBoolean: false };
57
- expect(resolveValue(source, testModel, true)).toBe(false);
58
- });
59
- it('should resolve literalArray', () => {
60
- const source = { literalArray: ['x', 'y', 'z'] };
61
- expect(resolveValue(source, testModel)).toEqual([
62
- 'x',
63
- 'y',
64
- 'z',
65
- ]);
66
- });
67
- it('should resolve empty literalArray', () => {
68
- const source = { literalArray: [] };
69
- expect(resolveValue(source, testModel, ['default'])).toEqual([]);
70
- });
71
- });
72
- describe('with path references', () => {
73
- it('should resolve path to string value', () => {
74
- const source = { path: '/user/name' };
75
- expect(resolveValue(source, testModel)).toBe('John');
76
- });
77
- it('should resolve path to number value', () => {
78
- const source = { path: '/count' };
79
- expect(resolveValue(source, testModel)).toBe(42);
80
- });
81
- it('should resolve path to boolean value', () => {
82
- const source = { path: '/user/active' };
83
- expect(resolveValue(source, testModel)).toBe(true);
84
- });
85
- it('should resolve path to nested object', () => {
86
- const source = { path: '/user' };
87
- expect(resolveValue(source, testModel)).toEqual({
88
- name: 'John',
89
- age: 30,
90
- active: true,
91
- });
92
- });
93
- it('should resolve path to array', () => {
94
- const source = { path: '/items' };
95
- expect(resolveValue(source, testModel)).toEqual([
96
- 'a',
97
- 'b',
98
- 'c',
99
- ]);
100
- });
101
- it('should return default when path not found', () => {
102
- const source = { path: '/nonexistent' };
103
- expect(resolveValue(source, testModel, 'default')).toBe('default');
104
- });
105
- it('should return undefined when path not found and no default', () => {
106
- const source = { path: '/nonexistent' };
107
- expect(resolveValue(source, testModel)).toBeUndefined();
108
- });
109
- it('should handle empty data model', () => {
110
- const source = { path: '/user/name' };
111
- expect(resolveValue(source, {}, 'default')).toBe('default');
112
- });
113
- });
114
- describe('with unknown source structure', () => {
115
- it('should return default value for unknown source structure', () => {
116
- const source = { unknown: 'value' };
117
- expect(resolveValue(source, testModel, 'default')).toBe('default');
118
- });
119
- });
120
- });
121
- describe('contentsToObject', () => {
122
- it('should convert string entry', () => {
123
- const contents = [{ key: 'name', valueString: 'John' }];
124
- expect(contentsToObject(contents)).toEqual({ name: 'John' });
125
- });
126
- it('should convert number entry', () => {
127
- const contents = [{ key: 'age', valueNumber: 30 }];
128
- expect(contentsToObject(contents)).toEqual({ age: 30 });
129
- });
130
- it('should convert boolean entry', () => {
131
- const contents = [{ key: 'active', valueBoolean: true }];
132
- expect(contentsToObject(contents)).toEqual({ active: true });
133
- });
134
- it('should convert false boolean entry', () => {
135
- const contents = [{ key: 'active', valueBoolean: false }];
136
- expect(contentsToObject(contents)).toEqual({ active: false });
137
- });
138
- it('should convert zero number entry', () => {
139
- const contents = [{ key: 'count', valueNumber: 0 }];
140
- expect(contentsToObject(contents)).toEqual({ count: 0 });
141
- });
142
- it('should convert empty string entry', () => {
143
- const contents = [{ key: 'name', valueString: '' }];
144
- expect(contentsToObject(contents)).toEqual({ name: '' });
145
- });
146
- it('should convert nested map entry', () => {
147
- const contents = [
148
- {
149
- key: 'user',
150
- valueMap: [
151
- { key: 'name', valueString: 'John' },
152
- { key: 'age', valueNumber: 30 },
153
- ],
154
- },
155
- ];
156
- expect(contentsToObject(contents)).toEqual({
157
- user: { name: 'John', age: 30 },
158
- });
159
- });
160
- it('should convert deeply nested map entry', () => {
161
- const contents = [
162
- {
163
- key: 'user',
164
- valueMap: [
165
- {
166
- key: 'profile',
167
- valueMap: [{ key: 'email', valueString: 'john@example.com' }],
168
- },
169
- ],
170
- },
171
- ];
172
- expect(contentsToObject(contents)).toEqual({
173
- user: { profile: { email: 'john@example.com' } },
174
- });
175
- });
176
- it('should convert multiple entries', () => {
177
- const contents = [
178
- { key: 'name', valueString: 'John' },
179
- { key: 'age', valueNumber: 30 },
180
- { key: 'active', valueBoolean: true },
181
- ];
182
- expect(contentsToObject(contents)).toEqual({
183
- name: 'John',
184
- age: 30,
185
- active: true,
186
- });
187
- });
188
- it('should normalize path keys to last segment', () => {
189
- const contents = [
190
- { key: '/form/name', valueString: 'John' },
191
- { key: '/form/age', valueNumber: 30 },
192
- ];
193
- expect(contentsToObject(contents)).toEqual({
194
- name: 'John',
195
- age: 30,
196
- });
197
- });
198
- it('should handle empty contents array', () => {
199
- expect(contentsToObject([])).toEqual({});
200
- });
201
- it('should handle entry with no value type', () => {
202
- const contents = [{ key: 'empty' }];
203
- expect(contentsToObject(contents)).toEqual({});
204
- });
205
- });
206
- describe('resolveActionContext', () => {
207
- const testModel = {
208
- user: {
209
- name: 'John',
210
- age: 30,
211
- },
212
- selectedId: 'item-123',
213
- };
214
- it('should return empty object for undefined context', () => {
215
- expect(resolveActionContext(undefined, testModel)).toEqual({});
216
- });
217
- it('should return empty object for empty context array', () => {
218
- expect(resolveActionContext([], testModel)).toEqual({});
219
- });
220
- it('should resolve literalString values', () => {
221
- const context = [{ key: 'action', value: { literalString: 'submit' } }];
222
- expect(resolveActionContext(context, testModel)).toEqual({
223
- action: 'submit',
224
- });
225
- });
226
- it('should resolve literalNumber values', () => {
227
- const context = [{ key: 'count', value: { literalNumber: 5 } }];
228
- expect(resolveActionContext(context, testModel)).toEqual({ count: 5 });
229
- });
230
- it('should resolve literalBoolean values', () => {
231
- const context = [{ key: 'confirmed', value: { literalBoolean: true } }];
232
- expect(resolveActionContext(context, testModel)).toEqual({
233
- confirmed: true,
234
- });
235
- });
236
- it('should resolve path references', () => {
237
- const context = [{ key: 'userName', value: { path: '/user/name' } }];
238
- expect(resolveActionContext(context, testModel)).toEqual({
239
- userName: 'John',
240
- });
241
- });
242
- it('should resolve multiple context items', () => {
243
- const context = [
244
- { key: 'action', value: { literalString: 'update' } },
245
- { key: 'userId', value: { path: '/selectedId' } },
246
- { key: 'confirmed', value: { literalBoolean: true } },
247
- ];
248
- expect(resolveActionContext(context, testModel)).toEqual({
249
- action: 'update',
250
- userId: 'item-123',
251
- confirmed: true,
252
- });
253
- });
254
- it('should return undefined for non-existent path', () => {
255
- const context = [{ key: 'missing', value: { path: '/nonexistent' } }];
256
- expect(resolveActionContext(context, testModel)).toEqual({
257
- missing: undefined,
258
- });
259
- });
260
- it('should resolve nested path references', () => {
261
- const context = [
262
- { key: 'name', value: { path: '/user/name' } },
263
- { key: 'age', value: { path: '/user/age' } },
264
- ];
265
- expect(resolveActionContext(context, testModel)).toEqual({
266
- name: 'John',
267
- age: 30,
268
- });
269
- });
270
- });
271
- });
@@ -1,6 +0,0 @@
1
- /**
2
- * pathUtils Tests
3
- *
4
- * Tests for path utility functions used in A2UI data model operations.
5
- */
6
- export {};
@@ -1,211 +0,0 @@
1
- /**
2
- * pathUtils Tests
3
- *
4
- * Tests for path utility functions used in A2UI data model operations.
5
- */
6
- import { describe, it, expect } from 'vitest';
7
- import { getValueByPath, setValueByPath, mergeAtPath } from './pathUtils.js';
8
- describe('pathUtils', () => {
9
- describe('getValueByPath', () => {
10
- const testModel = {
11
- user: {
12
- name: 'John',
13
- age: 30,
14
- profile: {
15
- email: 'john@example.com',
16
- active: true,
17
- },
18
- },
19
- items: ['a', 'b', 'c'],
20
- count: 42,
21
- };
22
- it('should return entire data model for empty path', () => {
23
- expect(getValueByPath(testModel, '')).toEqual(testModel);
24
- });
25
- it('should return entire data model for root path', () => {
26
- expect(getValueByPath(testModel, '/')).toEqual(testModel);
27
- });
28
- it('should get top-level string value', () => {
29
- const model = { name: 'test' };
30
- expect(getValueByPath(model, '/name')).toBe('test');
31
- });
32
- it('should get top-level number value', () => {
33
- expect(getValueByPath(testModel, '/count')).toBe(42);
34
- });
35
- it('should get top-level array value', () => {
36
- expect(getValueByPath(testModel, '/items')).toEqual(['a', 'b', 'c']);
37
- });
38
- it('should get nested object value', () => {
39
- expect(getValueByPath(testModel, '/user')).toEqual({
40
- name: 'John',
41
- age: 30,
42
- profile: {
43
- email: 'john@example.com',
44
- active: true,
45
- },
46
- });
47
- });
48
- it('should get deeply nested string value', () => {
49
- expect(getValueByPath(testModel, '/user/name')).toBe('John');
50
- });
51
- it('should get deeply nested number value', () => {
52
- expect(getValueByPath(testModel, '/user/age')).toBe(30);
53
- });
54
- it('should get deeply nested object value', () => {
55
- expect(getValueByPath(testModel, '/user/profile')).toEqual({
56
- email: 'john@example.com',
57
- active: true,
58
- });
59
- });
60
- it('should get very deeply nested value', () => {
61
- expect(getValueByPath(testModel, '/user/profile/email')).toBe('john@example.com');
62
- expect(getValueByPath(testModel, '/user/profile/active')).toBe(true);
63
- });
64
- it('should return undefined for non-existent path', () => {
65
- expect(getValueByPath(testModel, '/nonexistent')).toBeUndefined();
66
- });
67
- it('should return undefined for non-existent nested path', () => {
68
- expect(getValueByPath(testModel, '/user/nonexistent')).toBeUndefined();
69
- });
70
- it('should return undefined for path through non-object', () => {
71
- expect(getValueByPath(testModel, '/count/nested')).toBeUndefined();
72
- });
73
- it('should return undefined when intermediate value is null', () => {
74
- const model = { user: null };
75
- expect(getValueByPath(model, '/user/name')).toBeUndefined();
76
- });
77
- it('should return undefined when intermediate value is undefined', () => {
78
- const model = { user: undefined };
79
- expect(getValueByPath(model, '/user/name')).toBeUndefined();
80
- });
81
- it('should handle paths without leading slash', () => {
82
- expect(getValueByPath(testModel, 'user/name')).toBe('John');
83
- });
84
- it('should handle empty data model', () => {
85
- expect(getValueByPath({}, '/user/name')).toBeUndefined();
86
- });
87
- });
88
- describe('setValueByPath', () => {
89
- it('should return merged object for empty path with object value', () => {
90
- const model = { a: 1 };
91
- const result = setValueByPath(model, '', { b: 2 });
92
- expect(result).toEqual({ a: 1, b: 2 });
93
- });
94
- it('should return merged object for root path with object value', () => {
95
- const model = { a: 1 };
96
- const result = setValueByPath(model, '/', { b: 2 });
97
- expect(result).toEqual({ a: 1, b: 2 });
98
- });
99
- it('should return original model for root path with non-object value', () => {
100
- const model = { a: 1 };
101
- const result = setValueByPath(model, '/', 'string');
102
- expect(result).toEqual({ a: 1 });
103
- });
104
- it('should return original model for root path with array value', () => {
105
- const model = { a: 1 };
106
- const result = setValueByPath(model, '/', [1, 2, 3]);
107
- expect(result).toEqual({ a: 1 });
108
- });
109
- it('should set top-level value', () => {
110
- const model = { a: 1 };
111
- const result = setValueByPath(model, '/b', 2);
112
- expect(result).toEqual({ a: 1, b: 2 });
113
- });
114
- it('should update existing top-level value', () => {
115
- const model = { a: 1 };
116
- const result = setValueByPath(model, '/a', 2);
117
- expect(result).toEqual({ a: 2 });
118
- });
119
- it('should set nested value in existing object', () => {
120
- const model = { user: { name: 'John' } };
121
- const result = setValueByPath(model, '/user/age', 30);
122
- expect(result).toEqual({ user: { name: 'John', age: 30 } });
123
- });
124
- it('should update existing nested value', () => {
125
- const model = { user: { name: 'John' } };
126
- const result = setValueByPath(model, '/user/name', 'Jane');
127
- expect(result).toEqual({ user: { name: 'Jane' } });
128
- });
129
- it('should create nested structure if not exists', () => {
130
- const model = {};
131
- const result = setValueByPath(model, '/user/profile/email', 'test@test.com');
132
- expect(result).toEqual({
133
- user: { profile: { email: 'test@test.com' } },
134
- });
135
- });
136
- it('should replace non-object with object when setting nested path', () => {
137
- const model = { user: 'string' };
138
- const result = setValueByPath(model, '/user/name', 'John');
139
- expect(result).toEqual({ user: { name: 'John' } });
140
- });
141
- it('should be immutable - not modify original model', () => {
142
- const model = { user: { name: 'John' } };
143
- const result = setValueByPath(model, '/user/name', 'Jane');
144
- expect(model).toEqual({ user: { name: 'John' } });
145
- expect(result).toEqual({ user: { name: 'Jane' } });
146
- });
147
- it('should handle setting null value', () => {
148
- const model = { user: { name: 'John' } };
149
- const result = setValueByPath(model, '/user/name', null);
150
- expect(result).toEqual({ user: { name: null } });
151
- });
152
- it('should handle setting array value', () => {
153
- const model = { items: [] };
154
- const result = setValueByPath(model, '/items', ['a', 'b']);
155
- expect(result).toEqual({ items: ['a', 'b'] });
156
- });
157
- it('should handle setting object value', () => {
158
- const model = { user: null };
159
- const result = setValueByPath(model, '/user', { name: 'John' });
160
- expect(result).toEqual({ user: { name: 'John' } });
161
- });
162
- });
163
- describe('mergeAtPath', () => {
164
- it('should merge at root for empty path', () => {
165
- const model = { a: 1 };
166
- const result = mergeAtPath(model, '', { b: 2 });
167
- expect(result).toEqual({ a: 1, b: 2 });
168
- });
169
- it('should merge at root for root path', () => {
170
- const model = { a: 1 };
171
- const result = mergeAtPath(model, '/', { b: 2 });
172
- expect(result).toEqual({ a: 1, b: 2 });
173
- });
174
- it('should overwrite existing keys at root', () => {
175
- const model = { a: 1, b: 2 };
176
- const result = mergeAtPath(model, '/', { b: 3, c: 4 });
177
- expect(result).toEqual({ a: 1, b: 3, c: 4 });
178
- });
179
- it('should merge at nested path', () => {
180
- const model = { user: { name: 'John' } };
181
- const result = mergeAtPath(model, '/user', { age: 30 });
182
- expect(result).toEqual({ user: { name: 'John', age: 30 } });
183
- });
184
- it('should overwrite existing keys at nested path', () => {
185
- const model = { user: { name: 'John', age: 25 } };
186
- const result = mergeAtPath(model, '/user', { age: 30 });
187
- expect(result).toEqual({ user: { name: 'John', age: 30 } });
188
- });
189
- it('should create path if not exists', () => {
190
- const model = {};
191
- const result = mergeAtPath(model, '/user', { name: 'John' });
192
- expect(result).toEqual({ user: { name: 'John' } });
193
- });
194
- it('should replace non-object at path with merged result', () => {
195
- const model = { user: 'string' };
196
- const result = mergeAtPath(model, '/user', { name: 'John' });
197
- expect(result).toEqual({ user: { name: 'John' } });
198
- });
199
- it('should handle array at path by treating as non-object', () => {
200
- const model = { items: [1, 2, 3] };
201
- const result = mergeAtPath(model, '/items', { a: 1 });
202
- expect(result).toEqual({ items: { a: 1 } });
203
- });
204
- it('should be immutable - not modify original model', () => {
205
- const model = { user: { name: 'John' } };
206
- const result = mergeAtPath(model, '/user', { age: 30 });
207
- expect(model).toEqual({ user: { name: 'John' } });
208
- expect(result).toEqual({ user: { name: 'John', age: 30 } });
209
- });
210
- });
211
- });
@@ -1,6 +0,0 @@
1
- /**
2
- * dataBinding Tests
3
- *
4
- * Tests for data binding utility functions used in A2UI 0.9.
5
- */
6
- export {};