Skip to content

Commit 85697e0

Browse files
committed
Add proxy-based lazy decoder
1 parent c6401b0 commit 85697e0

12 files changed

Lines changed: 2182 additions & 5 deletions

README.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Other platforms will fall back to source compilation (requires a C compiler and
2626

2727
## Usage
2828

29+
### Basic Encode/Decode
30+
2931
```javascript
3032
import { encode, decode, version } from '@jaydeebee/lite3-native-addon';
3133

@@ -39,6 +41,90 @@ console.log(obj); // { hello: 'world', count: 42 }
3941
console.log(version()); // Addon version
4042
```
4143

44+
### Lazy Proxy Access (Lite3Buffer)
45+
46+
For better performance with large objects where you only need a few fields, use `Lite3Buffer.from()` to create a lazy proxy that decodes values on-demand:
47+
48+
```typescript
49+
import { Lite3Buffer } from '@jaydeebee/lite3-native-addon';
50+
51+
// Create from an object (type is inferred)
52+
const proxy = Lite3Buffer.from({
53+
users: [
54+
{ name: 'Alice', age: 30 },
55+
{ name: 'Bob', age: 25 }
56+
],
57+
metadata: { count: 2 }
58+
});
59+
60+
// Access properties naturally - decoded lazily
61+
console.log(proxy.users[0].name); // 'Alice' - only this field is decoded
62+
console.log(proxy.metadata.count); // 2
63+
64+
// Array methods work as expected
65+
const names = proxy.users.map(u => u.name); // ['Alice', 'Bob']
66+
const adults = proxy.users.filter(u => u.age >= 18);
67+
68+
// Works with JSON.stringify, spreading, for...of, etc.
69+
console.log(JSON.stringify(proxy));
70+
const copy = { ...proxy };
71+
for (const user of proxy.users) {
72+
console.log(user.name);
73+
}
74+
```
75+
76+
#### Type Safety
77+
78+
When creating a proxy from a buffer, the return type defaults to `unknown` for safety (like `JSON.parse`). Provide a type parameter when you trust the data source:
79+
80+
```typescript
81+
interface User {
82+
name: string;
83+
age: number;
84+
}
85+
86+
// From buffer - returns unknown by default (safe)
87+
const data = Lite3Buffer.from(buffer);
88+
data.name; // TS error: 'unknown' has no property 'name'
89+
90+
// With type parameter - returns User (trusted)
91+
const user = Lite3Buffer.from<User>(buffer);
92+
user.name; // OK - full autocomplete and type checking
93+
94+
// For untrusted sources, consider runtime validation:
95+
import { z } from 'zod';
96+
const UserSchema = z.object({ name: z.string(), age: z.number() });
97+
const validated = UserSchema.parse(Lite3Buffer.from(buffer));
98+
```
99+
100+
#### Utility Functions
101+
102+
```typescript
103+
import { Lite3Buffer, $buffer, $decode } from '@jaydeebee/lite3-native-addon';
104+
105+
// Check if a value is a Lite3Buffer proxy
106+
Lite3Buffer.isLite3Buffer(proxy); // true
107+
Lite3Buffer.isLite3Buffer({}); // false
108+
109+
// Get the underlying buffer
110+
const buffer = Lite3Buffer.getBuffer(proxy);
111+
112+
// Force full decode (escape hatch)
113+
const pojo = proxy[$decode]();
114+
115+
// Access raw buffer via symbol (alternative)
116+
const rawBuffer = proxy[$buffer];
117+
```
118+
119+
#### Why Use Lite3Buffer?
120+
121+
| Scenario | `decode()` | `Lite3Buffer.from()` |
122+
|----------|-----------|---------------------|
123+
| Access all fields | Good | Similar |
124+
| Access few fields from large object | Wasteful | Efficient |
125+
| Repeated access to same field | Faster | Slightly slower (cached after first access) |
126+
| Pass-through / routing | Decode + re-encode | Keep as buffer |
127+
42128
## Supported Types
43129

44130
- Strings

binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
"src/addon.c",
1010
"src/addon_encode.c",
1111
"src/addon_decode.c",
12+
"src/addon_proxy.c",
1213
"deps/lite3/src/lite3.c",
1314
"deps/lite3/src/json_enc.c",
1415
"deps/lite3/src/ctx_api.c",

include/lite3-napi.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,16 @@
3636
extern napi_value encode(napi_env, napi_callback_info);
3737
extern napi_value decode(napi_env, napi_callback_info);
3838

39+
// Proxy support functions (addon_proxy.c):
40+
extern napi_value proxy_get_type(napi_env, napi_callback_info);
41+
extern napi_value proxy_get_array_type(napi_env, napi_callback_info);
42+
extern napi_value proxy_get_value(napi_env, napi_callback_info);
43+
extern napi_value proxy_get_array_element(napi_env, napi_callback_info);
44+
extern napi_value proxy_get_child_offset(napi_env, napi_callback_info);
45+
extern napi_value proxy_get_array_child_offset(napi_env, napi_callback_info);
46+
extern napi_value proxy_get_keys(napi_env, napi_callback_info);
47+
extern napi_value proxy_get_length(napi_env, napi_callback_info);
48+
extern napi_value proxy_has_key(napi_env, napi_callback_info);
49+
extern napi_value proxy_get_root_type(napi_env, napi_callback_info);
50+
3951
#endif // LITE3_NAPI_H

package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@
3333
"install": "prebuild-install || node-gyp rebuild",
3434
"prebuild": "prebuild --strip --runtime napi --target 9",
3535
"clean": "node-gyp clean && rm -rf dist prebuilds",
36-
"test": "node --test",
36+
"test": "vitest run",
37+
"test:watch": "vitest",
3738
"release:dry-run": "semantic-release --dry-run"
3839
},
3940
"keywords": [
@@ -62,7 +63,8 @@
6263
"prebuild": "^13.0.1",
6364
"semantic-release": "^25.0.2",
6465
"tsup": "^8.0.0",
65-
"typescript": "^5.0.0"
66+
"typescript": "^5.0.0",
67+
"vitest": "^4.0.16"
6668
},
6769
"engines": {
6870
"node": ">=20.0.0"

0 commit comments

Comments
 (0)