lua-cli 2.4.1 → 2.5.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.
- package/CHANGELOG.md +29 -0
- package/USER_DATA_INSTANCE.md +621 -0
- package/dist/common/user.instance.d.ts +4 -1
- package/dist/common/user.instance.js +58 -2
- package/dist/web/app.css +85 -2
- package/dist/web/app.js +43 -43
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,35 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [2.5.1] - 2025-10-07
|
|
9
|
+
|
|
10
|
+
### 🐛 Bug Fixes
|
|
11
|
+
- **TypeScript Index Signature**: Added index signature to `UserDataInstance` class to fix TypeScript linting errors
|
|
12
|
+
- Resolves "Property does not exist on type" errors when accessing dynamic properties like `user.name`
|
|
13
|
+
- Now properly typed for dynamic property access via Proxy
|
|
14
|
+
|
|
15
|
+
## [2.5.0] - 2025-10-07
|
|
16
|
+
|
|
17
|
+
### ✨ New Features
|
|
18
|
+
- **Direct Property Access for UserDataInstance**: Now supports intuitive property access using Proxy pattern
|
|
19
|
+
- Access user data with `user.name` instead of `user.data.name`
|
|
20
|
+
- Set properties directly with `user.name = "John"`
|
|
21
|
+
- Full backward compatibility maintained - `user.data.name` still works
|
|
22
|
+
- Proper enumeration support for `Object.keys()` and `in` operator
|
|
23
|
+
|
|
24
|
+
### 🔧 Improvements
|
|
25
|
+
- **Reduced Sensitive Field Filtering**: Sanitization now only removes `agentId` and `userId`
|
|
26
|
+
- Allows access to more user data fields while maintaining essential privacy
|
|
27
|
+
- Custom keys and tokens are now accessible in tools
|
|
28
|
+
|
|
29
|
+
### 📖 Documentation
|
|
30
|
+
- **Comprehensive UserDataInstance Documentation**: Added full API reference with examples
|
|
31
|
+
|
|
32
|
+
### Technical Details
|
|
33
|
+
- Enhanced Proxy implementation with proper trap handlers for get, set, has, ownKeys, and getOwnPropertyDescriptor
|
|
34
|
+
- Reserved properties (`data`, `userAPI`, `update`, `clear`, `toJSON`) protected from proxy interception
|
|
35
|
+
- Maintains all existing methods and behavior while adding new access patterns
|
|
36
|
+
|
|
8
37
|
## [1.3.2-alpha.3] - 2025-09-25
|
|
9
38
|
|
|
10
39
|
### 🚀 Major Rewrite
|
|
@@ -0,0 +1,621 @@
|
|
|
1
|
+
# UserDataInstance API Reference
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`UserDataInstance` is a powerful, proxy-enhanced class that provides a fluent API for managing user-specific data in Lua AI tools. It automatically handles data sanitization, provides intuitive property access, and includes methods for updating and clearing user data.
|
|
6
|
+
|
|
7
|
+
## Table of Contents
|
|
8
|
+
|
|
9
|
+
- [Features](#features)
|
|
10
|
+
- [Installation](#installation)
|
|
11
|
+
- [Quick Start](#quick-start)
|
|
12
|
+
- [API Reference](#api-reference)
|
|
13
|
+
- [Property Access](#property-access)
|
|
14
|
+
- [Methods](#methods)
|
|
15
|
+
- [Examples](#examples)
|
|
16
|
+
- [Best Practices](#best-practices)
|
|
17
|
+
- [TypeScript Support](#typescript-support)
|
|
18
|
+
|
|
19
|
+
## Features
|
|
20
|
+
|
|
21
|
+
✨ **Direct Property Access** - Access data with `user.name` instead of `user.data.name`
|
|
22
|
+
🔒 **Automatic Sanitization** - Removes sensitive fields (agentId, userId) automatically
|
|
23
|
+
🔄 **Async Updates** - Built-in methods for updating and clearing user data
|
|
24
|
+
📝 **Console-Friendly** - Clean output when logging with `console.log()`
|
|
25
|
+
🎯 **Type-Safe** - Full TypeScript support with proper type definitions
|
|
26
|
+
⚡ **Proxy-Powered** - Seamless property access via JavaScript Proxy
|
|
27
|
+
🔙 **Backward Compatible** - Works with both new and old access patterns
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install lua-cli
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import { UserDataTool } from 'lua-cli';
|
|
39
|
+
|
|
40
|
+
// In your LuaSkill tool
|
|
41
|
+
class MyTool extends LuaTool {
|
|
42
|
+
async execute(input: any, context: ToolContext) {
|
|
43
|
+
// Access user data through context
|
|
44
|
+
const user = context.user;
|
|
45
|
+
|
|
46
|
+
// Direct property access (NEW! ✨)
|
|
47
|
+
console.log(user.name);
|
|
48
|
+
console.log(user.email);
|
|
49
|
+
console.log(user.preferences);
|
|
50
|
+
|
|
51
|
+
// Set properties directly
|
|
52
|
+
user.favoriteColor = "blue";
|
|
53
|
+
|
|
54
|
+
// Update on server
|
|
55
|
+
await user.update({ favoriteColor: "blue" });
|
|
56
|
+
|
|
57
|
+
return { success: true };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## API Reference
|
|
63
|
+
|
|
64
|
+
### Constructor
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
constructor(api: UserDataAPI, data: UserData)
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Parameters:**
|
|
71
|
+
- `api` - The UserDataAPI instance for making API calls
|
|
72
|
+
- `data` - The user data from the API (will be sanitized automatically)
|
|
73
|
+
|
|
74
|
+
**Returns:** A proxied instance that allows direct access to data properties
|
|
75
|
+
|
|
76
|
+
**Note:** You typically don't need to instantiate this directly. It's provided through the `context.user` object in your tools.
|
|
77
|
+
|
|
78
|
+
## Property Access
|
|
79
|
+
|
|
80
|
+
### Direct Access (Recommended ✨)
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
// Reading properties
|
|
84
|
+
const name = user.name;
|
|
85
|
+
const email = user.email;
|
|
86
|
+
const settings = user.settings;
|
|
87
|
+
|
|
88
|
+
// Setting properties (updates local instance only)
|
|
89
|
+
user.name = "John Doe";
|
|
90
|
+
user.email = "john@example.com";
|
|
91
|
+
user.settings = { theme: "dark" };
|
|
92
|
+
|
|
93
|
+
// Checking if property exists
|
|
94
|
+
if ('name' in user) {
|
|
95
|
+
console.log('User has a name');
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Traditional Access (Still Supported)
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Reading through .data property
|
|
103
|
+
const name = user.data.name;
|
|
104
|
+
const email = user.data.email;
|
|
105
|
+
|
|
106
|
+
// Setting through .data property
|
|
107
|
+
user.data.name = "John Doe";
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### Property Enumeration
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Get all user data keys
|
|
114
|
+
const keys = Object.keys(user); // Returns all data keys + methods
|
|
115
|
+
|
|
116
|
+
// Iterate over properties
|
|
117
|
+
for (const key in user) {
|
|
118
|
+
console.log(`${key}: ${user[key]}`);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Methods
|
|
123
|
+
|
|
124
|
+
### `update(data: Record<string, any>): Promise<any>`
|
|
125
|
+
|
|
126
|
+
Updates the user's data on the server and locally.
|
|
127
|
+
|
|
128
|
+
**Parameters:**
|
|
129
|
+
- `data` - Object containing the fields to update or add
|
|
130
|
+
|
|
131
|
+
**Returns:** Promise resolving to the updated sanitized user data
|
|
132
|
+
|
|
133
|
+
**Throws:** Error if the update fails
|
|
134
|
+
|
|
135
|
+
**Example:**
|
|
136
|
+
```typescript
|
|
137
|
+
// Update single field
|
|
138
|
+
await user.update({ name: "John Doe" });
|
|
139
|
+
|
|
140
|
+
// Update multiple fields
|
|
141
|
+
await user.update({
|
|
142
|
+
name: "John Doe",
|
|
143
|
+
email: "john@example.com",
|
|
144
|
+
preferences: {
|
|
145
|
+
theme: "dark",
|
|
146
|
+
notifications: true
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Access updated data
|
|
151
|
+
console.log(user.name); // "John Doe"
|
|
152
|
+
console.log(user.preferences.theme); // "dark"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### `clear(): Promise<boolean>`
|
|
156
|
+
|
|
157
|
+
Clears all user data for the current user on the server.
|
|
158
|
+
|
|
159
|
+
**Returns:** Promise resolving to `true` if clearing was successful
|
|
160
|
+
|
|
161
|
+
**Throws:** Error if the clear operation fails
|
|
162
|
+
|
|
163
|
+
**Example:**
|
|
164
|
+
```typescript
|
|
165
|
+
try {
|
|
166
|
+
const success = await user.clear();
|
|
167
|
+
if (success) {
|
|
168
|
+
console.log('User data cleared successfully');
|
|
169
|
+
}
|
|
170
|
+
} catch (error) {
|
|
171
|
+
console.error('Failed to clear user data');
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### `toJSON(): Record<string, any>`
|
|
176
|
+
|
|
177
|
+
Custom serialization method used when converting to JSON or logging.
|
|
178
|
+
|
|
179
|
+
**Returns:** The sanitized user data object
|
|
180
|
+
|
|
181
|
+
**Example:**
|
|
182
|
+
```typescript
|
|
183
|
+
// Automatic usage in JSON.stringify
|
|
184
|
+
const json = JSON.stringify(user);
|
|
185
|
+
|
|
186
|
+
// Explicit usage
|
|
187
|
+
const dataObject = user.toJSON();
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Data Sanitization
|
|
191
|
+
|
|
192
|
+
The `UserDataInstance` automatically removes sensitive fields before storing data locally:
|
|
193
|
+
|
|
194
|
+
**Removed Fields:**
|
|
195
|
+
- `agentId`
|
|
196
|
+
- `userId`
|
|
197
|
+
|
|
198
|
+
**Accessible Fields:** All other fields including:
|
|
199
|
+
- `name`, `email`, `phone`
|
|
200
|
+
- Custom data fields
|
|
201
|
+
- API keys and tokens (if stored in user data)
|
|
202
|
+
- Preferences and settings
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
// Server returns:
|
|
206
|
+
{
|
|
207
|
+
userId: "12345", // ❌ Removed
|
|
208
|
+
agentId: "agent-123", // ❌ Removed
|
|
209
|
+
name: "John Doe", // ✅ Accessible
|
|
210
|
+
email: "john@example.com", // ✅ Accessible
|
|
211
|
+
customKey: "value" // ✅ Accessible
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Local user instance has:
|
|
215
|
+
{
|
|
216
|
+
name: "John Doe",
|
|
217
|
+
email: "john@example.com",
|
|
218
|
+
customKey: "value"
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Examples
|
|
223
|
+
|
|
224
|
+
### Example 1: Storing User Preferences
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
class PreferencesTool extends LuaTool {
|
|
228
|
+
schema = {
|
|
229
|
+
input: z.object({
|
|
230
|
+
theme: z.enum(['light', 'dark']).optional(),
|
|
231
|
+
language: z.string().optional(),
|
|
232
|
+
notifications: z.boolean().optional()
|
|
233
|
+
}),
|
|
234
|
+
output: z.object({
|
|
235
|
+
message: z.string(),
|
|
236
|
+
preferences: z.any()
|
|
237
|
+
})
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
async execute(input: any, context: ToolContext) {
|
|
241
|
+
const user = context.user;
|
|
242
|
+
|
|
243
|
+
// Get current preferences or initialize
|
|
244
|
+
const currentPrefs = user.preferences || {};
|
|
245
|
+
|
|
246
|
+
// Update with new values
|
|
247
|
+
const newPrefs = {
|
|
248
|
+
...currentPrefs,
|
|
249
|
+
...input
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
// Save to server
|
|
253
|
+
await user.update({ preferences: newPrefs });
|
|
254
|
+
|
|
255
|
+
return {
|
|
256
|
+
message: 'Preferences updated successfully',
|
|
257
|
+
preferences: user.preferences
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Example 2: User Profile Management
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
class ProfileTool extends LuaTool {
|
|
267
|
+
schema = {
|
|
268
|
+
input: z.object({
|
|
269
|
+
action: z.enum(['get', 'update', 'clear']),
|
|
270
|
+
profile: z.object({
|
|
271
|
+
firstName: z.string().optional(),
|
|
272
|
+
lastName: z.string().optional(),
|
|
273
|
+
bio: z.string().optional(),
|
|
274
|
+
avatar: z.string().url().optional()
|
|
275
|
+
}).optional()
|
|
276
|
+
}),
|
|
277
|
+
output: z.object({
|
|
278
|
+
success: z.boolean(),
|
|
279
|
+
profile: z.any().optional(),
|
|
280
|
+
message: z.string()
|
|
281
|
+
})
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
async execute(input: any, context: ToolContext) {
|
|
285
|
+
const user = context.user;
|
|
286
|
+
|
|
287
|
+
switch (input.action) {
|
|
288
|
+
case 'get':
|
|
289
|
+
return {
|
|
290
|
+
success: true,
|
|
291
|
+
profile: {
|
|
292
|
+
firstName: user.firstName,
|
|
293
|
+
lastName: user.lastName,
|
|
294
|
+
bio: user.bio,
|
|
295
|
+
avatar: user.avatar
|
|
296
|
+
},
|
|
297
|
+
message: 'Profile retrieved'
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
case 'update':
|
|
301
|
+
await user.update(input.profile);
|
|
302
|
+
return {
|
|
303
|
+
success: true,
|
|
304
|
+
profile: input.profile,
|
|
305
|
+
message: 'Profile updated successfully'
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
case 'clear':
|
|
309
|
+
await user.clear();
|
|
310
|
+
return {
|
|
311
|
+
success: true,
|
|
312
|
+
message: 'Profile cleared successfully'
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
default:
|
|
316
|
+
throw new Error('Invalid action');
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Example 3: Shopping Cart Persistence
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
class CartTool extends LuaTool {
|
|
326
|
+
schema = {
|
|
327
|
+
input: z.object({
|
|
328
|
+
action: z.enum(['add', 'remove', 'get', 'clear']),
|
|
329
|
+
productId: z.string().optional(),
|
|
330
|
+
quantity: z.number().optional()
|
|
331
|
+
}),
|
|
332
|
+
output: z.object({
|
|
333
|
+
cart: z.array(z.any()),
|
|
334
|
+
total: z.number()
|
|
335
|
+
})
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
async execute(input: any, context: ToolContext) {
|
|
339
|
+
const user = context.user;
|
|
340
|
+
|
|
341
|
+
// Get current cart (direct access!)
|
|
342
|
+
let cart = user.cart || [];
|
|
343
|
+
|
|
344
|
+
switch (input.action) {
|
|
345
|
+
case 'add':
|
|
346
|
+
const existing = cart.find((item: any) =>
|
|
347
|
+
item.productId === input.productId
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
if (existing) {
|
|
351
|
+
existing.quantity += input.quantity;
|
|
352
|
+
} else {
|
|
353
|
+
cart.push({
|
|
354
|
+
productId: input.productId,
|
|
355
|
+
quantity: input.quantity
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
await user.update({ cart });
|
|
360
|
+
break;
|
|
361
|
+
|
|
362
|
+
case 'remove':
|
|
363
|
+
cart = cart.filter((item: any) =>
|
|
364
|
+
item.productId !== input.productId
|
|
365
|
+
);
|
|
366
|
+
await user.update({ cart });
|
|
367
|
+
break;
|
|
368
|
+
|
|
369
|
+
case 'clear':
|
|
370
|
+
await user.update({ cart: [] });
|
|
371
|
+
cart = [];
|
|
372
|
+
break;
|
|
373
|
+
|
|
374
|
+
case 'get':
|
|
375
|
+
// Just return current cart
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const total = cart.reduce((sum: number, item: any) =>
|
|
380
|
+
sum + (item.quantity || 0), 0
|
|
381
|
+
);
|
|
382
|
+
|
|
383
|
+
return { cart, total };
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
### Example 4: Session State Management
|
|
389
|
+
|
|
390
|
+
```typescript
|
|
391
|
+
class SessionTool extends LuaTool {
|
|
392
|
+
async execute(input: any, context: ToolContext) {
|
|
393
|
+
const user = context.user;
|
|
394
|
+
|
|
395
|
+
// Track page visits
|
|
396
|
+
const visits = user.pageVisits || 0;
|
|
397
|
+
await user.update({ pageVisits: visits + 1 });
|
|
398
|
+
|
|
399
|
+
// Track last seen timestamp
|
|
400
|
+
await user.update({
|
|
401
|
+
lastSeen: new Date().toISOString(),
|
|
402
|
+
sessionCount: (user.sessionCount || 0) + 1
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
// Check if returning user
|
|
406
|
+
const isReturning = visits > 0;
|
|
407
|
+
|
|
408
|
+
return {
|
|
409
|
+
isReturning,
|
|
410
|
+
visits: user.pageVisits,
|
|
411
|
+
sessionCount: user.sessionCount,
|
|
412
|
+
lastSeen: user.lastSeen
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Example 5: Multi-Step Form Data
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
class FormStateTool extends LuaTool {
|
|
422
|
+
async execute(input: any, context: ToolContext) {
|
|
423
|
+
const user = context.user;
|
|
424
|
+
|
|
425
|
+
// Get or initialize form state
|
|
426
|
+
const formData = user.multiStepForm || {
|
|
427
|
+
step: 1,
|
|
428
|
+
completed: {},
|
|
429
|
+
currentData: {}
|
|
430
|
+
};
|
|
431
|
+
|
|
432
|
+
// Update current step data
|
|
433
|
+
formData.completed[input.step] = true;
|
|
434
|
+
formData.currentData = {
|
|
435
|
+
...formData.currentData,
|
|
436
|
+
...input.data
|
|
437
|
+
};
|
|
438
|
+
|
|
439
|
+
// Move to next step
|
|
440
|
+
formData.step = input.step + 1;
|
|
441
|
+
|
|
442
|
+
// Save state
|
|
443
|
+
await user.update({ multiStepForm: formData });
|
|
444
|
+
|
|
445
|
+
return {
|
|
446
|
+
currentStep: formData.step,
|
|
447
|
+
completedSteps: Object.keys(formData.completed),
|
|
448
|
+
isComplete: formData.step > 5
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
## Best Practices
|
|
455
|
+
|
|
456
|
+
### ✅ DO
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
// Use direct property access for cleaner code
|
|
460
|
+
const name = user.name;
|
|
461
|
+
|
|
462
|
+
// Update multiple fields at once
|
|
463
|
+
await user.update({
|
|
464
|
+
field1: value1,
|
|
465
|
+
field2: value2
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
// Check for property existence before accessing
|
|
469
|
+
if ('preferences' in user) {
|
|
470
|
+
console.log(user.preferences);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// Initialize nested objects carefully
|
|
474
|
+
const prefs = user.preferences || {};
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
### ❌ DON'T
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
// Don't mutate and forget to call update()
|
|
481
|
+
user.name = "John";
|
|
482
|
+
// ❌ This only updates locally! Must call user.update()
|
|
483
|
+
|
|
484
|
+
// Don't assume sensitive fields are available
|
|
485
|
+
console.log(user.userId); // undefined (sanitized)
|
|
486
|
+
console.log(user.agentId); // undefined (sanitized)
|
|
487
|
+
|
|
488
|
+
// Don't make unnecessary update calls
|
|
489
|
+
await user.update({ field1: value1 });
|
|
490
|
+
await user.update({ field2: value2 }); // Combine these!
|
|
491
|
+
```
|
|
492
|
+
|
|
493
|
+
## TypeScript Support
|
|
494
|
+
|
|
495
|
+
### Type Definitions
|
|
496
|
+
|
|
497
|
+
```typescript
|
|
498
|
+
interface UserDataInstance {
|
|
499
|
+
data: Record<string, any>;
|
|
500
|
+
|
|
501
|
+
// Methods
|
|
502
|
+
update(data: Record<string, any>): Promise<any>;
|
|
503
|
+
clear(): Promise<boolean>;
|
|
504
|
+
toJSON(): Record<string, any>;
|
|
505
|
+
|
|
506
|
+
// Index signature for dynamic property access
|
|
507
|
+
[key: string]: any;
|
|
508
|
+
}
|
|
509
|
+
```
|
|
510
|
+
|
|
511
|
+
### Usage with Types
|
|
512
|
+
|
|
513
|
+
```typescript
|
|
514
|
+
// Define your user data shape
|
|
515
|
+
interface MyUserData {
|
|
516
|
+
name: string;
|
|
517
|
+
email: string;
|
|
518
|
+
preferences: {
|
|
519
|
+
theme: 'light' | 'dark';
|
|
520
|
+
notifications: boolean;
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Access with type checking
|
|
525
|
+
const user = context.user;
|
|
526
|
+
const name: string = user.name;
|
|
527
|
+
const prefs: MyUserData['preferences'] = user.preferences;
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
## Console Output
|
|
531
|
+
|
|
532
|
+
The `UserDataInstance` includes custom serialization for clean console output:
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
console.log(user);
|
|
536
|
+
// Output:
|
|
537
|
+
// {
|
|
538
|
+
// name: "John Doe",
|
|
539
|
+
// email: "john@example.com",
|
|
540
|
+
// preferences: { theme: "dark" }
|
|
541
|
+
// }
|
|
542
|
+
|
|
543
|
+
// Methods and internal properties are hidden
|
|
544
|
+
// Only the actual user data is displayed
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
## Reserved Properties
|
|
548
|
+
|
|
549
|
+
The following properties are reserved and cannot be overwritten via proxy:
|
|
550
|
+
|
|
551
|
+
- `data` - The internal data storage
|
|
552
|
+
- `userAPI` - The API client (non-enumerable)
|
|
553
|
+
- `update` - The update method
|
|
554
|
+
- `clear` - The clear method
|
|
555
|
+
- `toJSON` - The serialization method
|
|
556
|
+
|
|
557
|
+
Attempting to set these will maintain the original instance behavior.
|
|
558
|
+
|
|
559
|
+
## Performance Considerations
|
|
560
|
+
|
|
561
|
+
- **Proxy Overhead**: Minimal performance impact for property access
|
|
562
|
+
- **Update Batching**: Combine multiple field updates into a single `update()` call
|
|
563
|
+
- **Local Changes**: Setting properties directly only updates the local instance
|
|
564
|
+
- **Server Sync**: Always call `update()` to persist changes to the server
|
|
565
|
+
|
|
566
|
+
## Backward Compatibility
|
|
567
|
+
|
|
568
|
+
The new direct property access pattern is fully backward compatible:
|
|
569
|
+
|
|
570
|
+
```typescript
|
|
571
|
+
// Old pattern (still works)
|
|
572
|
+
user.data.name = "John";
|
|
573
|
+
await user.update({ name: user.data.name });
|
|
574
|
+
|
|
575
|
+
// New pattern (recommended)
|
|
576
|
+
user.name = "John";
|
|
577
|
+
await user.update({ name: user.name });
|
|
578
|
+
|
|
579
|
+
// Mixed usage (also works)
|
|
580
|
+
console.log(user.name); // Direct access
|
|
581
|
+
console.log(user.data.email); // Traditional access
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
## Error Handling
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
try {
|
|
588
|
+
await user.update({
|
|
589
|
+
name: "John Doe",
|
|
590
|
+
email: "john@example.com"
|
|
591
|
+
});
|
|
592
|
+
} catch (error) {
|
|
593
|
+
console.error('Failed to update user data:', error);
|
|
594
|
+
// Handle error appropriately
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
try {
|
|
598
|
+
await user.clear();
|
|
599
|
+
} catch (error) {
|
|
600
|
+
console.error('Failed to clear user data:', error);
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
## Related Resources
|
|
605
|
+
|
|
606
|
+
- [LuaTool Documentation](./TEMPLATE_GUIDE.md)
|
|
607
|
+
- [API Reference](./API_REFERENCE.md)
|
|
608
|
+
- [Getting Started](./GETTING_STARTED.md)
|
|
609
|
+
- [UserDataTool Example](./template/src/tools/UserDataTool.ts)
|
|
610
|
+
|
|
611
|
+
## Support
|
|
612
|
+
|
|
613
|
+
For issues, questions, or contributions:
|
|
614
|
+
- GitHub: [lua-ai-global/lua-cli](https://github.com/lua-ai-global/lua-cli)
|
|
615
|
+
- Email: stefan@heylua.ai
|
|
616
|
+
|
|
617
|
+
---
|
|
618
|
+
|
|
619
|
+
**Version:** 2.5.0
|
|
620
|
+
**Last Updated:** October 7, 2025
|
|
621
|
+
**License:** MIT
|
|
@@ -3,14 +3,17 @@ import { UserDataAPI } from "../types/index.js";
|
|
|
3
3
|
/**
|
|
4
4
|
* User data instance class providing a fluent API for managing user data
|
|
5
5
|
* Automatically sanitizes sensitive fields and provides methods for updating and clearing data
|
|
6
|
+
* Supports direct property access (e.g., user.name) instead of user.data.name
|
|
6
7
|
*/
|
|
7
8
|
export default class UserDataInstance {
|
|
8
9
|
data: Record<string, any>;
|
|
9
10
|
private userAPI;
|
|
11
|
+
[key: string]: any;
|
|
10
12
|
/**
|
|
11
|
-
* Creates a new UserDataInstance
|
|
13
|
+
* Creates a new UserDataInstance with proxy support for direct property access
|
|
12
14
|
* @param api - The UserDataAPI instance for making API calls
|
|
13
15
|
* @param data - The user data from the API (will be sanitized automatically)
|
|
16
|
+
* @returns Proxied instance that allows direct access to data properties
|
|
14
17
|
*/
|
|
15
18
|
constructor(api: UserDataAPI, data: UserData);
|
|
16
19
|
/**
|