@ecency/sdk 1.5.6 → 1.5.8
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 +201 -1
- package/dist/browser/index.js +9 -8
- package/dist/browser/index.js.map +1 -1
- package/dist/node/index.cjs +22 -8
- package/dist/node/index.cjs.map +1 -1
- package/dist/node/index.mjs +22 -8
- package/dist/node/index.mjs.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -2,12 +2,212 @@
|
|
|
2
2
|
|
|
3
3
|
Framework-agnostic data layer for Hive apps with first-class React Query support.
|
|
4
4
|
|
|
5
|
-
## What
|
|
5
|
+
## What's Inside
|
|
6
6
|
|
|
7
7
|
- Query and mutation option builders powered by [@tanstack/react-query](https://tanstack.com/query)
|
|
8
8
|
- Modular APIs: accounts, posts, communities, market, wallet, notifications, analytics, integrations, core, auth, bridge, games, hive-engine, operations, points, private-api, promotions, proposals, resource-credits, search, spk, witnesses
|
|
9
9
|
- Central configuration via `CONFIG` / `ConfigManager` (RPC client, QueryClient)
|
|
10
10
|
|
|
11
|
+
## Why React Query?
|
|
12
|
+
|
|
13
|
+
The Ecency SDK is built on **React Query** (TanStack Query) to provide a production-ready data synchronization layer out of the box. React Query transforms how Hive applications handle server state, eliminating common pitfalls and dramatically improving user experience.
|
|
14
|
+
|
|
15
|
+
### Key Benefits
|
|
16
|
+
|
|
17
|
+
#### 1. **Automatic Caching & Deduplication**
|
|
18
|
+
Multiple components can request the same data without redundant network calls. React Query automatically:
|
|
19
|
+
- Caches responses by query key
|
|
20
|
+
- Deduplicates concurrent requests
|
|
21
|
+
- Shares cached data across components instantly
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// Both components use the same query - only 1 API call is made
|
|
25
|
+
// Component A
|
|
26
|
+
useQuery(getAccountFullQueryOptions("ecency"));
|
|
27
|
+
|
|
28
|
+
// Component B (rendered simultaneously)
|
|
29
|
+
useQuery(getAccountFullQueryOptions("ecency")); // ← Uses cached data
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
#### 2. **Background Synchronization**
|
|
33
|
+
Data automatically stays fresh without manual refetching. React Query:
|
|
34
|
+
- Refetches stale data on window focus
|
|
35
|
+
- Updates data on network reconnection
|
|
36
|
+
- Supports configurable background polling
|
|
37
|
+
- Prevents showing outdated information
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
// Data refetches automatically when user returns to tab
|
|
41
|
+
const { data } = useQuery({
|
|
42
|
+
...getPostsRankedQueryOptions("trending", "", "", 20),
|
|
43
|
+
staleTime: 60000, // Consider fresh for 60s
|
|
44
|
+
refetchInterval: 120000 // Poll every 2 minutes
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
#### 3. **Optimistic Updates**
|
|
49
|
+
Instant UI feedback before blockchain confirmation:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const { mutateAsync } = useAccountUpdate(username, auth);
|
|
53
|
+
|
|
54
|
+
await mutateAsync(
|
|
55
|
+
{ metadata: newProfile },
|
|
56
|
+
{
|
|
57
|
+
// Update UI immediately
|
|
58
|
+
onMutate: (variables) => {
|
|
59
|
+
queryClient.setQueryData(
|
|
60
|
+
getAccountFullQueryOptions(username).queryKey,
|
|
61
|
+
(old) => ({ ...old, ...variables.metadata })
|
|
62
|
+
);
|
|
63
|
+
},
|
|
64
|
+
// Rollback on error
|
|
65
|
+
onError: (err, variables, context) => {
|
|
66
|
+
queryClient.setQueryData(
|
|
67
|
+
getAccountFullQueryOptions(username).queryKey,
|
|
68
|
+
context.previousData
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
#### 4. **SSR & Prefetching**
|
|
76
|
+
First-class server-side rendering support:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
// Next.js App Router example
|
|
80
|
+
export async function generateMetadata({ params }) {
|
|
81
|
+
const queryClient = new QueryClient();
|
|
82
|
+
|
|
83
|
+
// Prefetch on server
|
|
84
|
+
await queryClient.prefetchQuery(
|
|
85
|
+
getAccountFullQueryOptions(params.username)
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
// Data is hydrated on client instantly
|
|
89
|
+
return { title: /* ... */ };
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### 5. **Loading & Error States**
|
|
94
|
+
Built-in state management eliminates boilerplate:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
const { data, isLoading, error, isRefetching } = useQuery(
|
|
98
|
+
getAccountFullQueryOptions("ecency")
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
if (isLoading) return <Spinner />;
|
|
102
|
+
if (error) return <ErrorMessage error={error} />;
|
|
103
|
+
|
|
104
|
+
return <Profile data={data} isRefreshing={isRefetching} />;
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### 6. **Dependent Queries**
|
|
108
|
+
Chain queries with automatic dependency tracking:
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
// Step 1: Fetch account
|
|
112
|
+
const { data: account } = useQuery(getAccountFullQueryOptions(username));
|
|
113
|
+
|
|
114
|
+
// Step 2: Fetch wallet only after account loads
|
|
115
|
+
const { data: wallet } = useQuery({
|
|
116
|
+
...getAccountWalletAssetInfoQueryOptions(username, "HIVE"),
|
|
117
|
+
enabled: !!account // Wait for account
|
|
118
|
+
});
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
#### 7. **Pagination & Infinite Scroll**
|
|
122
|
+
Built-in pagination utilities:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
const {
|
|
126
|
+
data,
|
|
127
|
+
fetchNextPage,
|
|
128
|
+
hasNextPage,
|
|
129
|
+
isFetchingNextPage
|
|
130
|
+
} = useInfiniteQuery(
|
|
131
|
+
getPostsRankedInfiniteQueryOptions("trending", "hive-engine")
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
// Automatically manages page state and cursor tracking
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Why This Matters for Hive Apps
|
|
138
|
+
|
|
139
|
+
Hive applications face unique challenges:
|
|
140
|
+
- **High API latency**: Blockchain RPC calls can be slow (100-500ms)
|
|
141
|
+
- **Rate limits**: Excessive requests can hit node rate limits
|
|
142
|
+
- **Stale data**: Blockchain data changes frequently (new posts, votes, transfers)
|
|
143
|
+
- **Complex state**: Managing loading states, errors, and cache invalidation manually is error-prone
|
|
144
|
+
|
|
145
|
+
The Ecency SDK with React Query solves all of these:
|
|
146
|
+
|
|
147
|
+
✅ **Reduced API calls** by 70-90% through intelligent caching
|
|
148
|
+
✅ **Instant UI updates** with optimistic mutations
|
|
149
|
+
✅ **Zero manual cache management** - React Query handles invalidation
|
|
150
|
+
✅ **Better UX** with background updates and retry logic
|
|
151
|
+
✅ **Faster perceived performance** with prefetching and SSR
|
|
152
|
+
✅ **Less code** - no custom loading/error/caching logic needed
|
|
153
|
+
|
|
154
|
+
### How Other Apps Can Benefit
|
|
155
|
+
|
|
156
|
+
Any Hive application can leverage this SDK to:
|
|
157
|
+
|
|
158
|
+
1. **Drop custom data fetching code** - Use pre-built query options for all common Hive operations
|
|
159
|
+
2. **Share cache across features** - One query for account data serves entire app
|
|
160
|
+
3. **Add real-time features** easily with `refetchInterval` and optimistic updates
|
|
161
|
+
4. **Improve SEO** with SSR-ready queries that prefetch on server
|
|
162
|
+
5. **Reduce bundle size** - Share the SDK's type-safe queries instead of custom fetch logic
|
|
163
|
+
|
|
164
|
+
**Example: Building a Hive blog reader**
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { useQuery, useInfiniteQuery } from "@tanstack/react-query";
|
|
168
|
+
import {
|
|
169
|
+
getAccountFullQueryOptions,
|
|
170
|
+
getPostQueryOptions,
|
|
171
|
+
getPostsRankedInfiniteQueryOptions
|
|
172
|
+
} from "@ecency/sdk";
|
|
173
|
+
|
|
174
|
+
// Profile page - automatic caching
|
|
175
|
+
function ProfilePage({ username }) {
|
|
176
|
+
const { data: account, isLoading } = useQuery(
|
|
177
|
+
getAccountFullQueryOptions(username)
|
|
178
|
+
);
|
|
179
|
+
// ✅ Cached automatically, shared across components
|
|
180
|
+
// ✅ Refetches on window focus
|
|
181
|
+
// ✅ Handles loading/error states
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Feed page - infinite scroll
|
|
185
|
+
function FeedPage() {
|
|
186
|
+
const { data, fetchNextPage, hasNextPage } = useInfiniteQuery(
|
|
187
|
+
getPostsRankedInfiniteQueryOptions("trending")
|
|
188
|
+
);
|
|
189
|
+
// ✅ Automatic pagination
|
|
190
|
+
// ✅ Background updates
|
|
191
|
+
// ✅ Deduplicates concurrent requests
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Post page - dependent queries
|
|
195
|
+
function PostPage({ author, permlink }) {
|
|
196
|
+
const { data: post } = useQuery(
|
|
197
|
+
getPostQueryOptions(author, permlink)
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
const { data: authorAccount } = useQuery({
|
|
201
|
+
...getAccountFullQueryOptions(post?.author),
|
|
202
|
+
enabled: !!post // Wait for post to load
|
|
203
|
+
});
|
|
204
|
+
// ✅ Efficient dependent loading
|
|
205
|
+
// ✅ Shares cache with ProfilePage above
|
|
206
|
+
}
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**Zero manual cache management. Zero custom fetch logic. Production-ready data layer.**
|
|
210
|
+
|
|
11
211
|
## Installation
|
|
12
212
|
|
|
13
213
|
```sh
|
package/dist/browser/index.js
CHANGED
|
@@ -158,35 +158,35 @@ var ConfigManager;
|
|
|
158
158
|
return { safe: true };
|
|
159
159
|
}
|
|
160
160
|
function safeCompileRegex(pattern, maxLength = 200) {
|
|
161
|
+
const isDevelopment = typeof process !== "undefined" && false;
|
|
161
162
|
try {
|
|
162
163
|
if (!pattern) {
|
|
163
|
-
|
|
164
|
+
if (isDevelopment) ;
|
|
164
165
|
return null;
|
|
165
166
|
}
|
|
166
167
|
if (pattern.length > maxLength) {
|
|
167
|
-
|
|
168
|
+
if (isDevelopment) ;
|
|
168
169
|
return null;
|
|
169
170
|
}
|
|
170
171
|
const staticAnalysis = analyzeRedosRisk(pattern);
|
|
171
172
|
if (!staticAnalysis.safe) {
|
|
172
|
-
|
|
173
|
+
if (isDevelopment) ;
|
|
173
174
|
return null;
|
|
174
175
|
}
|
|
175
176
|
let regex;
|
|
176
177
|
try {
|
|
177
178
|
regex = new RegExp(pattern);
|
|
178
179
|
} catch (compileErr) {
|
|
179
|
-
|
|
180
|
+
if (isDevelopment) ;
|
|
180
181
|
return null;
|
|
181
182
|
}
|
|
182
183
|
const runtimeTest = testRegexPerformance(regex);
|
|
183
184
|
if (!runtimeTest.safe) {
|
|
184
|
-
|
|
185
|
+
if (isDevelopment) ;
|
|
185
186
|
return null;
|
|
186
187
|
}
|
|
187
188
|
return regex;
|
|
188
189
|
} catch (err) {
|
|
189
|
-
console.warn(`[SDK] DMCA pattern rejected: unexpected error - pattern: ${pattern.substring(0, 50)}...`, err);
|
|
190
190
|
return null;
|
|
191
191
|
}
|
|
192
192
|
}
|
|
@@ -197,7 +197,8 @@ var ConfigManager;
|
|
|
197
197
|
CONFIG.dmcaTagRegexes = tags.map((pattern) => safeCompileRegex(pattern)).filter((r) => r !== null);
|
|
198
198
|
CONFIG.dmcaPatternRegexes = [];
|
|
199
199
|
const rejectedTagCount = tags.length - CONFIG.dmcaTagRegexes.length;
|
|
200
|
-
|
|
200
|
+
const isDevelopment = typeof process !== "undefined" && false;
|
|
201
|
+
if (!CONFIG._dmcaInitialized && isDevelopment) {
|
|
201
202
|
console.log(`[SDK] DMCA configuration loaded:`);
|
|
202
203
|
console.log(` - Accounts: ${accounts.length}`);
|
|
203
204
|
console.log(` - Tag patterns: ${CONFIG.dmcaTagRegexes.length}/${tags.length} compiled (${rejectedTagCount} rejected)`);
|
|
@@ -205,8 +206,8 @@ var ConfigManager;
|
|
|
205
206
|
if (rejectedTagCount > 0) {
|
|
206
207
|
console.warn(`[SDK] ${rejectedTagCount} DMCA tag patterns were rejected due to security validation. Check warnings above for details.`);
|
|
207
208
|
}
|
|
208
|
-
CONFIG._dmcaInitialized = true;
|
|
209
209
|
}
|
|
210
|
+
CONFIG._dmcaInitialized = true;
|
|
210
211
|
}
|
|
211
212
|
ConfigManager2.setDmcaLists = setDmcaLists;
|
|
212
213
|
})(ConfigManager || (ConfigManager = {}));
|