@singi-labs/sifa-sdk 0.5.0 → 0.7.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/dist/index-IDRpze8y.d.cts +253 -0
- package/dist/index-IDRpze8y.d.ts +253 -0
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +3 -253
- package/dist/index.d.ts +3 -253
- package/dist/index.js +1 -1
- package/dist/query/index.cjs +378 -0
- package/dist/query/index.cjs.map +1 -0
- package/dist/query/index.d.cts +378 -0
- package/dist/query/index.d.ts +378 -0
- package/dist/query/index.js +351 -0
- package/dist/query/index.js.map +1 -0
- package/package.json +35 -7
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
import { createContext, useContext } from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
import { useQuery, useQueryClient, useMutation } from '@tanstack/react-query';
|
|
4
|
+
|
|
5
|
+
// src/query/client.ts
|
|
6
|
+
var ApiError = class extends Error {
|
|
7
|
+
status;
|
|
8
|
+
body;
|
|
9
|
+
constructor(message, status, body) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "ApiError";
|
|
12
|
+
this.status = status;
|
|
13
|
+
this.body = body;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var DEFAULT_TIMEOUT_MS = 1e4;
|
|
17
|
+
var MAX_RATE_LIMIT_RETRIES = 3;
|
|
18
|
+
var RATE_LIMIT_RETRY_CAP_SECONDS = 3;
|
|
19
|
+
async function apiFetch(config, path, options = {}) {
|
|
20
|
+
const fetchFn = config.fetch ?? globalThis.fetch;
|
|
21
|
+
const url = `${config.baseUrl}${path}`;
|
|
22
|
+
const maxRetries = options.retryOn429 ? MAX_RATE_LIMIT_RETRIES : 0;
|
|
23
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
24
|
+
const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
25
|
+
const headers = { ...options.headers ?? {} };
|
|
26
|
+
let body;
|
|
27
|
+
if (options.body !== void 0) {
|
|
28
|
+
headers["Content-Type"] ??= "application/json";
|
|
29
|
+
body = JSON.stringify(options.body);
|
|
30
|
+
}
|
|
31
|
+
const init = {
|
|
32
|
+
method: options.method ?? "GET",
|
|
33
|
+
headers,
|
|
34
|
+
body,
|
|
35
|
+
signal,
|
|
36
|
+
credentials: options.credentials,
|
|
37
|
+
cache: options.cache,
|
|
38
|
+
...options.next ? { next: options.next } : {}
|
|
39
|
+
};
|
|
40
|
+
const res = await fetchFn(url, init);
|
|
41
|
+
if (res.status === 429 && attempt < maxRetries) {
|
|
42
|
+
const retryAfterRaw = res.headers.get("retry-after");
|
|
43
|
+
const retryAfter = retryAfterRaw ? Number.parseInt(retryAfterRaw, 10) : 2;
|
|
44
|
+
const waitSeconds = Math.min(
|
|
45
|
+
Number.isFinite(retryAfter) ? retryAfter : 2,
|
|
46
|
+
RATE_LIMIT_RETRY_CAP_SECONDS
|
|
47
|
+
);
|
|
48
|
+
await new Promise((r) => setTimeout(r, waitSeconds * 1e3));
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (!res.ok) {
|
|
52
|
+
let errBody;
|
|
53
|
+
try {
|
|
54
|
+
errBody = await res.json();
|
|
55
|
+
} catch {
|
|
56
|
+
try {
|
|
57
|
+
errBody = await res.text();
|
|
58
|
+
} catch {
|
|
59
|
+
errBody = void 0;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw new ApiError(`Sifa API ${res.status} on ${path}`, res.status, errBody);
|
|
63
|
+
}
|
|
64
|
+
return await res.json();
|
|
65
|
+
}
|
|
66
|
+
throw new ApiError(`Sifa API exhausted retries on ${path}`, 429);
|
|
67
|
+
}
|
|
68
|
+
async function apiFetchOrNull(config, path, options = {}) {
|
|
69
|
+
try {
|
|
70
|
+
return await apiFetch(config, path, options);
|
|
71
|
+
} catch (e) {
|
|
72
|
+
if (e instanceof ApiError && e.status === 404) return null;
|
|
73
|
+
throw e;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
var SifaConfigContext = createContext(null);
|
|
77
|
+
function SifaProvider({ config, children }) {
|
|
78
|
+
return /* @__PURE__ */ jsx(SifaConfigContext.Provider, { value: config, children });
|
|
79
|
+
}
|
|
80
|
+
function useSifaConfig() {
|
|
81
|
+
const ctx = useContext(SifaConfigContext);
|
|
82
|
+
if (!ctx) {
|
|
83
|
+
throw new Error(
|
|
84
|
+
"useSifaConfig must be used inside <SifaProvider>. Wrap your app once with <SifaProvider config={...}>."
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
return ctx;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/query/fetchers/profile.ts
|
|
91
|
+
function fetchProfile(config, handleOrDid, options = {}) {
|
|
92
|
+
const path = `/api/profile/${encodeURIComponent(handleOrDid)}`;
|
|
93
|
+
return apiFetchOrNull(config, path, {
|
|
94
|
+
retryOn429: true,
|
|
95
|
+
...options
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// src/query/fetchers/positions.ts
|
|
100
|
+
function createPosition(config, data, options = {}) {
|
|
101
|
+
return apiFetch(config, "/api/positions", {
|
|
102
|
+
method: "POST",
|
|
103
|
+
body: data,
|
|
104
|
+
credentials: "include",
|
|
105
|
+
...options
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/query/fetchers/search.ts
|
|
110
|
+
var EMPTY_SEARCH = { profiles: [], total: 0, limit: 20, offset: 0 };
|
|
111
|
+
var EMPTY_FILTERS = { countries: [], industries: [], apps: [] };
|
|
112
|
+
async function fetchSearchProfiles(config, filters, options = {}) {
|
|
113
|
+
const params = new URLSearchParams();
|
|
114
|
+
if (filters.q) params.set("q", filters.q);
|
|
115
|
+
if (filters.skill) params.set("skill", filters.skill);
|
|
116
|
+
if (filters.country) params.set("country", filters.country);
|
|
117
|
+
if (filters.industry) params.set("industry", filters.industry);
|
|
118
|
+
if (filters.domain) params.set("domain", filters.domain);
|
|
119
|
+
if (filters.workplace) params.set("workplace", filters.workplace);
|
|
120
|
+
if (filters.app) params.set("app", filters.app);
|
|
121
|
+
if (filters.limit !== void 0) params.set("limit", String(filters.limit));
|
|
122
|
+
if (params.size === 0) return EMPTY_SEARCH;
|
|
123
|
+
return apiFetch(config, `/api/search/profiles?${params.toString()}`, {
|
|
124
|
+
cache: "no-store",
|
|
125
|
+
...options
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async function fetchSkillSuggestions(config, query, options = {}) {
|
|
129
|
+
if (!query.trim()) return [];
|
|
130
|
+
const path = `/api/search/skills?q=${encodeURIComponent(query)}&limit=8`;
|
|
131
|
+
const data = await apiFetch(config, path, {
|
|
132
|
+
cache: "no-store",
|
|
133
|
+
...options
|
|
134
|
+
});
|
|
135
|
+
return data.skills ?? [];
|
|
136
|
+
}
|
|
137
|
+
async function fetchSearchFilters(config, options = {}) {
|
|
138
|
+
try {
|
|
139
|
+
return await apiFetch(config, "/api/search/filters", {
|
|
140
|
+
next: { revalidate: 300 },
|
|
141
|
+
...options
|
|
142
|
+
});
|
|
143
|
+
} catch {
|
|
144
|
+
return EMPTY_FILTERS;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// src/query/fetchers/discovery.ts
|
|
149
|
+
async function fetchSimilarProfiles(config, did, opts = {}) {
|
|
150
|
+
const limit = opts.limit ?? 5;
|
|
151
|
+
const path = `/api/discover/similar/${encodeURIComponent(did)}?limit=${limit}`;
|
|
152
|
+
try {
|
|
153
|
+
const data = await apiFetch(config, path, {
|
|
154
|
+
next: { revalidate: 300 },
|
|
155
|
+
timeoutMs: 5e3,
|
|
156
|
+
...opts
|
|
157
|
+
});
|
|
158
|
+
return data.profiles ?? [];
|
|
159
|
+
} catch {
|
|
160
|
+
return [];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
async function fetchSuggestions(config, opts = {}) {
|
|
164
|
+
const params = new URLSearchParams();
|
|
165
|
+
if (opts.source) params.set("source", opts.source);
|
|
166
|
+
if (opts.includeDismissed) params.set("include_dismissed", "true");
|
|
167
|
+
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
168
|
+
if (opts.limit) params.set("limit", String(opts.limit));
|
|
169
|
+
const qs = params.toString();
|
|
170
|
+
const headers = { ...opts.headers ?? {} };
|
|
171
|
+
if (opts.cookieHeader) headers.cookie = opts.cookieHeader;
|
|
172
|
+
try {
|
|
173
|
+
return await apiFetch(config, `/api/suggestions${qs ? `?${qs}` : ""}`, {
|
|
174
|
+
credentials: "include",
|
|
175
|
+
cache: "no-store",
|
|
176
|
+
timeoutMs: 8e3,
|
|
177
|
+
...opts,
|
|
178
|
+
headers
|
|
179
|
+
});
|
|
180
|
+
} catch {
|
|
181
|
+
return { onSifa: [], notOnSifa: [] };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function fetchSuggestionCount(config, since, options = {}) {
|
|
185
|
+
const params = since ? `?since=${encodeURIComponent(since)}` : "";
|
|
186
|
+
try {
|
|
187
|
+
const data = await apiFetch(config, `/api/suggestions/count${params}`, {
|
|
188
|
+
credentials: "include",
|
|
189
|
+
cache: "no-store",
|
|
190
|
+
...options
|
|
191
|
+
});
|
|
192
|
+
return data.count ?? 0;
|
|
193
|
+
} catch {
|
|
194
|
+
return 0;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
async function fetchFeaturedProfile(config, options = {}) {
|
|
198
|
+
try {
|
|
199
|
+
return await apiFetch(config, "/api/featured-profile", {
|
|
200
|
+
next: { revalidate: 900 },
|
|
201
|
+
...options
|
|
202
|
+
});
|
|
203
|
+
} catch {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/query/fetchers/follow.ts
|
|
209
|
+
async function fetchFollowing(config, opts = {}) {
|
|
210
|
+
const params = new URLSearchParams();
|
|
211
|
+
if (opts.source) params.set("source", opts.source);
|
|
212
|
+
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
213
|
+
if (opts.limit) params.set("limit", String(opts.limit));
|
|
214
|
+
const qs = params.toString();
|
|
215
|
+
try {
|
|
216
|
+
return await apiFetch(config, `/api/following${qs ? `?${qs}` : ""}`, {
|
|
217
|
+
credentials: "include",
|
|
218
|
+
cache: "no-store",
|
|
219
|
+
...opts
|
|
220
|
+
});
|
|
221
|
+
} catch {
|
|
222
|
+
return { follows: [] };
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// src/query/keys.ts
|
|
227
|
+
var sifaQueryKeys = {
|
|
228
|
+
all: () => ["sifa"],
|
|
229
|
+
profile: {
|
|
230
|
+
all: () => ["sifa", "profile"],
|
|
231
|
+
byHandle: (handleOrDid) => ["sifa", "profile", handleOrDid]
|
|
232
|
+
},
|
|
233
|
+
position: {
|
|
234
|
+
all: () => ["sifa", "position"],
|
|
235
|
+
byOwner: (did) => ["sifa", "position", "by-owner", did]
|
|
236
|
+
},
|
|
237
|
+
search: {
|
|
238
|
+
all: () => ["sifa", "search"],
|
|
239
|
+
profiles: (filters) => ["sifa", "search", "profiles", filters],
|
|
240
|
+
skills: (query) => ["sifa", "search", "skills", query],
|
|
241
|
+
filters: () => ["sifa", "search", "filters"]
|
|
242
|
+
},
|
|
243
|
+
discovery: {
|
|
244
|
+
all: () => ["sifa", "discovery"],
|
|
245
|
+
similar: (did, limit) => ["sifa", "discovery", "similar", did, limit],
|
|
246
|
+
suggestions: (opts) => ["sifa", "discovery", "suggestions", opts],
|
|
247
|
+
suggestionCount: (since) => ["sifa", "discovery", "suggestion-count", since ?? null],
|
|
248
|
+
featured: () => ["sifa", "discovery", "featured"]
|
|
249
|
+
},
|
|
250
|
+
follow: {
|
|
251
|
+
all: () => ["sifa", "follow"],
|
|
252
|
+
following: (opts) => ["sifa", "follow", "following", opts]
|
|
253
|
+
}
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
// src/query/hooks/use-profile.ts
|
|
257
|
+
function useProfile(handleOrDid, options) {
|
|
258
|
+
const config = useSifaConfig();
|
|
259
|
+
return useQuery({
|
|
260
|
+
queryKey: sifaQueryKeys.profile.byHandle(handleOrDid ?? ""),
|
|
261
|
+
queryFn: () => fetchProfile(config, handleOrDid ?? ""),
|
|
262
|
+
enabled: Boolean(handleOrDid) && (options?.enabled ?? true),
|
|
263
|
+
...options
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
function useCreatePosition(ownerDid, options) {
|
|
267
|
+
const config = useSifaConfig();
|
|
268
|
+
const queryClient = useQueryClient();
|
|
269
|
+
return useMutation({
|
|
270
|
+
mutationFn: (data) => createPosition(config, data),
|
|
271
|
+
onSuccess: async (result, variables, onMutateResult, context) => {
|
|
272
|
+
if (result.success) {
|
|
273
|
+
await queryClient.invalidateQueries({ queryKey: sifaQueryKeys.profile.byHandle(ownerDid) });
|
|
274
|
+
await queryClient.invalidateQueries({ queryKey: sifaQueryKeys.position.byOwner(ownerDid) });
|
|
275
|
+
}
|
|
276
|
+
await options?.onSuccess?.(result, variables, onMutateResult, context);
|
|
277
|
+
},
|
|
278
|
+
...options
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
function useSearchProfiles(filters, options) {
|
|
282
|
+
const config = useSifaConfig();
|
|
283
|
+
return useQuery({
|
|
284
|
+
queryKey: sifaQueryKeys.search.profiles(filters),
|
|
285
|
+
queryFn: () => fetchSearchProfiles(config, filters),
|
|
286
|
+
...options
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
function useSkillSuggestions(query, options) {
|
|
290
|
+
const config = useSifaConfig();
|
|
291
|
+
return useQuery({
|
|
292
|
+
queryKey: sifaQueryKeys.search.skills(query),
|
|
293
|
+
queryFn: () => fetchSkillSuggestions(config, query),
|
|
294
|
+
enabled: query.trim().length > 0 && (options?.enabled ?? true),
|
|
295
|
+
...options
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
function useSearchFilters(options) {
|
|
299
|
+
const config = useSifaConfig();
|
|
300
|
+
return useQuery({
|
|
301
|
+
queryKey: sifaQueryKeys.search.filters(),
|
|
302
|
+
queryFn: () => fetchSearchFilters(config),
|
|
303
|
+
...options
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
function useSimilarProfiles(did, opts = {}, options) {
|
|
307
|
+
const config = useSifaConfig();
|
|
308
|
+
const limit = opts.limit ?? 5;
|
|
309
|
+
return useQuery({
|
|
310
|
+
queryKey: sifaQueryKeys.discovery.similar(did ?? "", limit),
|
|
311
|
+
queryFn: () => fetchSimilarProfiles(config, did ?? "", { limit }),
|
|
312
|
+
enabled: Boolean(did) && (options?.enabled ?? true),
|
|
313
|
+
...options
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
function useSuggestions(opts = {}, options) {
|
|
317
|
+
const config = useSifaConfig();
|
|
318
|
+
return useQuery({
|
|
319
|
+
queryKey: sifaQueryKeys.discovery.suggestions(opts),
|
|
320
|
+
queryFn: () => fetchSuggestions(config, opts),
|
|
321
|
+
...options
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function useSuggestionCount(since, options) {
|
|
325
|
+
const config = useSifaConfig();
|
|
326
|
+
return useQuery({
|
|
327
|
+
queryKey: sifaQueryKeys.discovery.suggestionCount(since),
|
|
328
|
+
queryFn: () => fetchSuggestionCount(config, since),
|
|
329
|
+
...options
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
function useFeaturedProfile(options) {
|
|
333
|
+
const config = useSifaConfig();
|
|
334
|
+
return useQuery({
|
|
335
|
+
queryKey: sifaQueryKeys.discovery.featured(),
|
|
336
|
+
queryFn: () => fetchFeaturedProfile(config),
|
|
337
|
+
...options
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
function useFollowing(opts = {}, options) {
|
|
341
|
+
const config = useSifaConfig();
|
|
342
|
+
return useQuery({
|
|
343
|
+
queryKey: sifaQueryKeys.follow.following(opts),
|
|
344
|
+
queryFn: () => fetchFollowing(config, opts),
|
|
345
|
+
...options
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export { ApiError, SifaProvider, apiFetch, apiFetchOrNull, createPosition, fetchFeaturedProfile, fetchFollowing, fetchProfile, fetchSearchFilters, fetchSearchProfiles, fetchSimilarProfiles, fetchSkillSuggestions, fetchSuggestionCount, fetchSuggestions, sifaQueryKeys, useCreatePosition, useFeaturedProfile, useFollowing, useProfile, useSearchFilters, useSearchProfiles, useSifaConfig, useSimilarProfiles, useSkillSuggestions, useSuggestionCount, useSuggestions };
|
|
350
|
+
//# sourceMappingURL=index.js.map
|
|
351
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/query/client.ts","../../src/query/config.tsx","../../src/query/fetchers/profile.ts","../../src/query/fetchers/positions.ts","../../src/query/fetchers/search.ts","../../src/query/fetchers/discovery.ts","../../src/query/fetchers/follow.ts","../../src/query/keys.ts","../../src/query/hooks/use-profile.ts","../../src/query/hooks/use-create-position.ts","../../src/query/hooks/use-search.ts","../../src/query/hooks/use-discovery.ts","../../src/query/hooks/use-follow.ts"],"names":["useQuery"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Foundation HTTP client for talking to the Sifa AppView.\n *\n * Stateless. Consumers supply a {@link SifaApiConfig} per call (the React\n * hooks read it from context; non-React consumers pass it explicitly). No\n * singletons, no module-level state.\n */\n\n/** Configuration passed to every fetcher. */\nexport interface SifaApiConfig {\n /** Base URL of the sifa-api AppView, e.g. `https://api.sifa.id`. No trailing slash. */\n baseUrl: string;\n /**\n * Optional fetch implementation. Defaults to {@link globalThis.fetch}.\n * Lets Next.js consumers pass their cache-enhanced fetch; node/Expo\n * consumers can leave this unset.\n */\n fetch?: typeof fetch;\n}\n\n/** Options accepted by {@link apiFetch}. */\nexport interface ApiFetchOptions {\n method?: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n /** Request body. Serialized to JSON automatically. */\n body?: unknown;\n /** AbortSignal. Defaults to `AbortSignal.timeout(timeoutMs)` if `timeoutMs` is set. */\n signal?: AbortSignal;\n /** Per-call timeout in milliseconds. Default: 10_000. Ignored if `signal` is provided. */\n timeoutMs?: number;\n /** Retry on HTTP 429 up to 3 times with the server's `Retry-After` delay (capped at 3s). */\n retryOn429?: boolean;\n /** Additional headers. `Content-Type: application/json` is set automatically when `body` is present. */\n headers?: Record<string, string>;\n credentials?: RequestCredentials;\n cache?: RequestCache;\n /**\n * Next.js-specific cache hints. Ignored on non-Next runtimes. Passed\n * through transparently as part of {@link RequestInit}.\n */\n next?: { revalidate?: number | false; tags?: string[] };\n}\n\n/** Error thrown by {@link apiFetch} on non-2xx responses. */\nexport class ApiError extends Error {\n readonly status: number;\n readonly body: unknown;\n\n constructor(message: string, status: number, body?: unknown) {\n super(message);\n this.name = 'ApiError';\n this.status = status;\n this.body = body;\n }\n}\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\nconst MAX_RATE_LIMIT_RETRIES = 3;\nconst RATE_LIMIT_RETRY_CAP_SECONDS = 3;\n\n/**\n * Generic fetcher used by all SDK query and mutation functions.\n *\n * Returns parsed JSON typed as `T`. Throws {@link ApiError} on non-2xx\n * responses. Use {@link apiFetchOrNull} when 404 should resolve to `null`\n * instead.\n */\nexport async function apiFetch<T = unknown>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T> {\n const fetchFn = config.fetch ?? globalThis.fetch;\n const url = `${config.baseUrl}${path}`;\n const maxRetries = options.retryOn429 ? MAX_RATE_LIMIT_RETRIES : 0;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n const signal = options.signal ?? AbortSignal.timeout(options.timeoutMs ?? DEFAULT_TIMEOUT_MS);\n\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n let body: BodyInit | undefined;\n if (options.body !== undefined) {\n headers['Content-Type'] ??= 'application/json';\n body = JSON.stringify(options.body);\n }\n\n // `next` is a Next.js extension to RequestInit; cast through.\n const init = {\n method: options.method ?? 'GET',\n headers,\n body,\n signal,\n credentials: options.credentials,\n cache: options.cache,\n ...(options.next ? { next: options.next } : {}),\n } as RequestInit;\n\n const res = await fetchFn(url, init);\n\n if (res.status === 429 && attempt < maxRetries) {\n const retryAfterRaw = res.headers.get('retry-after');\n const retryAfter = retryAfterRaw ? Number.parseInt(retryAfterRaw, 10) : 2;\n const waitSeconds = Math.min(\n Number.isFinite(retryAfter) ? retryAfter : 2,\n RATE_LIMIT_RETRY_CAP_SECONDS,\n );\n await new Promise((r) => setTimeout(r, waitSeconds * 1000));\n continue;\n }\n\n if (!res.ok) {\n let errBody: unknown;\n try {\n errBody = (await res.json()) as unknown;\n } catch {\n try {\n errBody = await res.text();\n } catch {\n errBody = undefined;\n }\n }\n throw new ApiError(`Sifa API ${res.status} on ${path}`, res.status, errBody);\n }\n\n return (await res.json()) as T;\n }\n\n throw new ApiError(`Sifa API exhausted retries on ${path}`, 429);\n}\n\n/**\n * Variant of {@link apiFetch} that resolves to `null` on HTTP 404 instead\n * of throwing. Useful for \"fetch by handle\" reads where missing is\n * expected (e.g. unknown profile).\n */\nexport async function apiFetchOrNull<T>(\n config: SifaApiConfig,\n path: string,\n options: ApiFetchOptions = {},\n): Promise<T | null> {\n try {\n return await apiFetch<T>(config, path, options);\n } catch (e) {\n if (e instanceof ApiError && e.status === 404) return null;\n throw e;\n }\n}\n","'use client';\n\nimport { createContext, useContext, type ReactNode } from 'react';\n\nimport type { SifaApiConfig } from './client.js';\n\nconst SifaConfigContext = createContext<SifaApiConfig | null>(null);\n\nexport interface SifaProviderProps {\n config: SifaApiConfig;\n children: ReactNode;\n}\n\n/**\n * Provides the {@link SifaApiConfig} that hooks under it can read via\n * {@link useSifaConfig}. Wrap the React tree once; hooks consume it.\n *\n * @example\n * ```tsx\n * <QueryClientProvider client={queryClient}>\n * <SifaProvider config={{ baseUrl: process.env.NEXT_PUBLIC_API_URL! }}>\n * <App />\n * </SifaProvider>\n * </QueryClientProvider>\n * ```\n */\nexport function SifaProvider({ config, children }: SifaProviderProps) {\n return <SifaConfigContext.Provider value={config}>{children}</SifaConfigContext.Provider>;\n}\n\n/**\n * Read the SDK's {@link SifaApiConfig} from context. Throws if no\n * {@link SifaProvider} is mounted above.\n */\nexport function useSifaConfig(): SifaApiConfig {\n const ctx = useContext(SifaConfigContext);\n if (!ctx) {\n throw new Error(\n 'useSifaConfig must be used inside <SifaProvider>. Wrap your app once with <SifaProvider config={...}>.',\n );\n }\n return ctx;\n}\n","import type { Profile } from '../../types/index.js';\nimport { apiFetchOrNull, type ApiFetchOptions, type SifaApiConfig } from '../client.js';\n\n/**\n * Read the aggregated profile for a handle or DID.\n *\n * Returns `null` when the AppView has no profile for the given identifier\n * (HTTP 404). Throws {@link ApiError} on other non-2xx responses.\n *\n * Server-callable (Next.js RSC) and client-callable (Expo, browser).\n */\nexport function fetchProfile(\n config: SifaApiConfig,\n handleOrDid: string,\n options: ApiFetchOptions = {},\n): Promise<Profile | null> {\n const path = `/api/profile/${encodeURIComponent(handleOrDid)}`;\n return apiFetchOrNull<Profile>(config, path, {\n retryOn429: true,\n ...options,\n });\n}\n","import { apiFetch, type ApiFetchOptions, type SifaApiConfig } from '../client.js';\n\n/** Result returned by record-write mutations (create / update / delete). */\nexport interface WriteResult {\n success: boolean;\n error?: string;\n pdsHost?: string;\n}\n\n/** Result returned by create mutations. Includes the newly created `rkey`. */\nexport interface CreateResult extends WriteResult {\n rkey?: string;\n}\n\n/**\n * Create a new `id.sifa.profile.position` record on the authenticated\n * user's PDS. The AppView signs and writes via the user's OAuth session.\n *\n * `data` should be a lexicon-shaped position record (without `createdAt`\n * or `rkey`; the AppView fills both). Validate with\n * `ProfilePositionRecordSchema.omit({ createdAt: true })` before calling\n * if you want client-side guarantees.\n */\nexport function createPosition(\n config: SifaApiConfig,\n data: Record<string, unknown>,\n options: ApiFetchOptions = {},\n): Promise<CreateResult> {\n return apiFetch<CreateResult>(config, '/api/positions', {\n method: 'POST',\n body: data,\n credentials: 'include',\n ...options,\n });\n}\n","import { apiFetch, type ApiFetchOptions, type SifaApiConfig } from '../client.js';\n\n/** Profile entry returned by the search endpoint. */\nexport interface ProfileSearchResult {\n did?: string;\n handle: string;\n displayName?: string;\n headline?: string;\n avatar?: string;\n about?: string;\n currentRole?: string;\n currentCompany?: string;\n industry?: string;\n domain?: string;\n countryCode?: string;\n locationCountry?: string;\n preferredWorkplace?: string[];\n claimed?: boolean;\n blueskyVerified?: boolean;\n blueskyVerifiedAt?: string | null;\n}\n\nexport interface SearchFilters {\n q?: string;\n skill?: string;\n country?: string;\n industry?: string;\n domain?: string;\n workplace?: string;\n app?: string;\n limit?: number;\n}\n\nexport interface SearchResponse {\n profiles: ProfileSearchResult[];\n total: number;\n limit: number;\n offset: number;\n}\n\n/** Skill typeahead suggestion. */\nexport interface SkillSearchResult {\n name: string;\n slug: string;\n category: string;\n userCount: number;\n}\n\nexport interface FilterOptions {\n countries: { countryCode: string; country: string; count: number }[];\n industries: { industry: string; count: number }[];\n apps: { appId: string; count: number }[];\n}\n\nconst EMPTY_SEARCH: SearchResponse = { profiles: [], total: 0, limit: 20, offset: 0 };\nconst EMPTY_FILTERS: FilterOptions = { countries: [], industries: [], apps: [] };\n\n/**\n * Search profiles by free-text query and optional filters. Returns an\n * empty result set when no filters are provided (matching sifa-web's\n * \"no input, no fetch\" behavior).\n */\nexport async function fetchSearchProfiles(\n config: SifaApiConfig,\n filters: SearchFilters,\n options: ApiFetchOptions = {},\n): Promise<SearchResponse> {\n const params = new URLSearchParams();\n if (filters.q) params.set('q', filters.q);\n if (filters.skill) params.set('skill', filters.skill);\n if (filters.country) params.set('country', filters.country);\n if (filters.industry) params.set('industry', filters.industry);\n if (filters.domain) params.set('domain', filters.domain);\n if (filters.workplace) params.set('workplace', filters.workplace);\n if (filters.app) params.set('app', filters.app);\n if (filters.limit !== undefined) params.set('limit', String(filters.limit));\n\n if (params.size === 0) return EMPTY_SEARCH;\n\n return apiFetch<SearchResponse>(config, `/api/search/profiles?${params.toString()}`, {\n cache: 'no-store',\n ...options,\n });\n}\n\n/**\n * Skill typeahead. Returns up to 8 matches for the given prefix. Empty\n * input returns an empty array without hitting the server.\n */\nexport async function fetchSkillSuggestions(\n config: SifaApiConfig,\n query: string,\n options: ApiFetchOptions = {},\n): Promise<SkillSearchResult[]> {\n if (!query.trim()) return [];\n const path = `/api/search/skills?q=${encodeURIComponent(query)}&limit=8`;\n const data = await apiFetch<{ skills?: SkillSearchResult[] }>(config, path, {\n cache: 'no-store',\n ...options,\n });\n return data.skills ?? [];\n}\n\n/** Available filter facets (countries, industries, apps) for search UI. */\nexport async function fetchSearchFilters(\n config: SifaApiConfig,\n options: ApiFetchOptions = {},\n): Promise<FilterOptions> {\n try {\n return await apiFetch<FilterOptions>(config, '/api/search/filters', {\n next: { revalidate: 300 },\n ...options,\n });\n } catch {\n return EMPTY_FILTERS;\n }\n}\n","import { apiFetch, type ApiFetchOptions, type SifaApiConfig } from '../client.js';\n\n/** Lightweight profile representation used by discovery endpoints. */\nexport interface SimilarProfile {\n did: string;\n handle: string;\n displayName?: string | null;\n avatar?: string | null;\n headline?: string | null;\n currentRole?: string | null;\n currentCompany?: string | null;\n industry?: string | null;\n domain?: string | null;\n}\n\nexport interface SuggestionProfile {\n did: string;\n handle: string;\n displayName?: string;\n headline?: string;\n avatarUrl?: string;\n source: string;\n dismissed: boolean;\n blueskyVerified?: boolean;\n}\n\nexport interface SuggestionsResponse {\n onSifa: SuggestionProfile[];\n notOnSifa: SuggestionProfile[];\n cursor?: string;\n}\n\nexport interface FeaturedProfile {\n did: string;\n handle: string;\n displayName?: string;\n avatar?: string;\n pronouns?: string;\n headline?: string;\n about?: string;\n currentRole?: string;\n currentCompany?: string;\n locationCountry?: string;\n locationRegion?: string;\n /** Legacy alias for `locationLocality`; emitted by sifa-api during the additive response window. */\n locationCity?: string;\n /** community.lexicon.location.address field name -- prefer over `locationCity`. */\n locationLocality?: string;\n countryCode?: string;\n location?: string;\n website?: string;\n openTo?: string[];\n preferredWorkplace?: string[];\n availableFromUtc?: number;\n availableToUtc?: number;\n followersCount?: number;\n atprotoFollowersCount?: number;\n pdsProvider?: { name: string; host: string } | null;\n claimed: boolean;\n featuredDate: string;\n}\n\n/** Profiles similar to the given DID (matchmaking). Returns `[]` on error. */\nexport async function fetchSimilarProfiles(\n config: SifaApiConfig,\n did: string,\n opts: { limit?: number } & ApiFetchOptions = {},\n): Promise<SimilarProfile[]> {\n const limit = opts.limit ?? 5;\n const path = `/api/discover/similar/${encodeURIComponent(did)}?limit=${limit}`;\n try {\n const data = await apiFetch<{ profiles?: SimilarProfile[] }>(config, path, {\n next: { revalidate: 300 },\n timeoutMs: 5000,\n ...opts,\n });\n return data.profiles ?? [];\n } catch {\n return [];\n }\n}\n\nexport interface FetchSuggestionsOptions extends ApiFetchOptions {\n source?: string;\n includeDismissed?: boolean;\n cursor?: string;\n limit?: number;\n /**\n * Pass the caller's `Cookie` header on Next.js RSC server-side calls.\n * `credentials: 'include'` does NOT propagate browser cookies in RSC,\n * so authenticated server fetches must forward the header explicitly.\n */\n cookieHeader?: string;\n}\n\n/** Discovery suggestions feed. Resolves to empty arrays on error. */\nexport async function fetchSuggestions(\n config: SifaApiConfig,\n opts: FetchSuggestionsOptions = {},\n): Promise<SuggestionsResponse> {\n const params = new URLSearchParams();\n if (opts.source) params.set('source', opts.source);\n if (opts.includeDismissed) params.set('include_dismissed', 'true');\n if (opts.cursor) params.set('cursor', opts.cursor);\n if (opts.limit) params.set('limit', String(opts.limit));\n\n const qs = params.toString();\n const headers: Record<string, string> = { ...(opts.headers ?? {}) };\n if (opts.cookieHeader) headers.cookie = opts.cookieHeader;\n\n try {\n return await apiFetch<SuggestionsResponse>(config, `/api/suggestions${qs ? `?${qs}` : ''}`, {\n credentials: 'include',\n cache: 'no-store',\n timeoutMs: 8000,\n ...opts,\n headers,\n });\n } catch {\n return { onSifa: [], notOnSifa: [] };\n }\n}\n\n/** Count of pending suggestions since an optional timestamp. */\nexport async function fetchSuggestionCount(\n config: SifaApiConfig,\n since?: string,\n options: ApiFetchOptions = {},\n): Promise<number> {\n const params = since ? `?since=${encodeURIComponent(since)}` : '';\n try {\n const data = await apiFetch<{ count?: number }>(config, `/api/suggestions/count${params}`, {\n credentials: 'include',\n cache: 'no-store',\n ...options,\n });\n return data.count ?? 0;\n } catch {\n return 0;\n }\n}\n\n/** Featured profile (rotated by sifa-api). Returns `null` when none. */\nexport async function fetchFeaturedProfile(\n config: SifaApiConfig,\n options: ApiFetchOptions = {},\n): Promise<FeaturedProfile | null> {\n try {\n return await apiFetch<FeaturedProfile>(config, '/api/featured-profile', {\n next: { revalidate: 900 },\n ...options,\n });\n } catch {\n return null;\n }\n}\n","import { apiFetch, type ApiFetchOptions, type SifaApiConfig } from '../client.js';\n\nexport interface FollowProfile {\n did: string;\n handle: string;\n displayName?: string;\n headline?: string;\n avatarUrl?: string;\n source: string;\n claimed: boolean;\n followedAt: string;\n blueskyVerified?: boolean;\n blueskyVerifiedAt?: string | null;\n}\n\nexport interface FollowingResponse {\n follows: FollowProfile[];\n cursor?: string;\n}\n\n/** People the authenticated user follows. Empty on error. */\nexport async function fetchFollowing(\n config: SifaApiConfig,\n opts: { source?: string; cursor?: string; limit?: number } & ApiFetchOptions = {},\n): Promise<FollowingResponse> {\n const params = new URLSearchParams();\n if (opts.source) params.set('source', opts.source);\n if (opts.cursor) params.set('cursor', opts.cursor);\n if (opts.limit) params.set('limit', String(opts.limit));\n const qs = params.toString();\n\n try {\n return await apiFetch<FollowingResponse>(config, `/api/following${qs ? `?${qs}` : ''}`, {\n credentials: 'include',\n cache: 'no-store',\n ...opts,\n });\n } catch {\n return { follows: [] };\n }\n}\n","/**\n * Query key factory for TanStack Query.\n *\n * Keys are read-only tuples; the hierarchy matches the SDK's fetcher\n * grouping. Use these instead of inline arrays so consumers can target\n * `queryClient.invalidateQueries({ queryKey: keys.profile.all() })` and\n * similar patterns without typos.\n *\n * Convention: every leaf key starts with the namespace ('sifa') so\n * consumers can invalidate everything Sifa-related in one call.\n */\nexport const sifaQueryKeys = {\n all: () => ['sifa'] as const,\n\n profile: {\n all: () => ['sifa', 'profile'] as const,\n byHandle: (handleOrDid: string) => ['sifa', 'profile', handleOrDid] as const,\n },\n\n position: {\n all: () => ['sifa', 'position'] as const,\n byOwner: (did: string) => ['sifa', 'position', 'by-owner', did] as const,\n },\n\n search: {\n all: () => ['sifa', 'search'] as const,\n profiles: (filters: Record<string, unknown>) =>\n ['sifa', 'search', 'profiles', filters] as const,\n skills: (query: string) => ['sifa', 'search', 'skills', query] as const,\n filters: () => ['sifa', 'search', 'filters'] as const,\n },\n\n discovery: {\n all: () => ['sifa', 'discovery'] as const,\n similar: (did: string, limit: number) => ['sifa', 'discovery', 'similar', did, limit] as const,\n suggestions: (opts: Record<string, unknown>) =>\n ['sifa', 'discovery', 'suggestions', opts] as const,\n suggestionCount: (since: string | undefined) =>\n ['sifa', 'discovery', 'suggestion-count', since ?? null] as const,\n featured: () => ['sifa', 'discovery', 'featured'] as const,\n },\n\n follow: {\n all: () => ['sifa', 'follow'] as const,\n following: (opts: Record<string, unknown>) => ['sifa', 'follow', 'following', opts] as const,\n },\n} as const;\n\nexport type SifaQueryKey =\n | ReturnType<typeof sifaQueryKeys.all>\n | ReturnType<typeof sifaQueryKeys.profile.all>\n | ReturnType<typeof sifaQueryKeys.profile.byHandle>\n | ReturnType<typeof sifaQueryKeys.position.all>\n | ReturnType<typeof sifaQueryKeys.position.byOwner>\n | ReturnType<typeof sifaQueryKeys.search.all>\n | ReturnType<typeof sifaQueryKeys.search.profiles>\n | ReturnType<typeof sifaQueryKeys.search.skills>\n | ReturnType<typeof sifaQueryKeys.search.filters>\n | ReturnType<typeof sifaQueryKeys.discovery.all>\n | ReturnType<typeof sifaQueryKeys.discovery.similar>\n | ReturnType<typeof sifaQueryKeys.discovery.suggestions>\n | ReturnType<typeof sifaQueryKeys.discovery.suggestionCount>\n | ReturnType<typeof sifaQueryKeys.discovery.featured>\n | ReturnType<typeof sifaQueryKeys.follow.all>\n | ReturnType<typeof sifaQueryKeys.follow.following>;\n","'use client';\n\nimport { useQuery, type UseQueryOptions } from '@tanstack/react-query';\n\nimport type { Profile } from '../../types/index.js';\nimport { useSifaConfig } from '../config.js';\nimport { fetchProfile } from '../fetchers/profile.js';\nimport { sifaQueryKeys } from '../keys.js';\n\n/**\n * React hook that reads an aggregated profile by handle or DID via\n * TanStack Query. Returns `null` data when the profile does not exist.\n *\n * Pass `{ enabled: false }` (or an empty `handleOrDid`) to defer the\n * fetch.\n */\nexport function useProfile(\n handleOrDid: string | undefined | null,\n options?: Omit<\n UseQueryOptions<\n Profile | null,\n Error,\n Profile | null,\n ReturnType<typeof sifaQueryKeys.profile.byHandle>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.profile.byHandle(handleOrDid ?? ''),\n queryFn: () => fetchProfile(config, handleOrDid ?? ''),\n enabled: Boolean(handleOrDid) && (options?.enabled ?? true),\n ...options,\n });\n}\n","'use client';\n\nimport { useMutation, useQueryClient, type UseMutationOptions } from '@tanstack/react-query';\n\nimport { useSifaConfig } from '../config.js';\nimport { createPosition, type CreateResult } from '../fetchers/positions.js';\nimport { sifaQueryKeys } from '../keys.js';\n\n/**\n * React hook for creating a new position record. On success, invalidates\n * the owner's profile cache so the new position is reflected on the next\n * read.\n *\n * The owner DID is required so the mutation can target the correct\n * profile cache entry for invalidation.\n */\nexport function useCreatePosition(\n ownerDid: string,\n options?: Omit<UseMutationOptions<CreateResult, Error, Record<string, unknown>>, 'mutationFn'>,\n) {\n const config = useSifaConfig();\n const queryClient = useQueryClient();\n\n return useMutation({\n mutationFn: (data: Record<string, unknown>) => createPosition(config, data),\n onSuccess: async (result, variables, onMutateResult, context) => {\n if (result.success) {\n await queryClient.invalidateQueries({ queryKey: sifaQueryKeys.profile.byHandle(ownerDid) });\n await queryClient.invalidateQueries({ queryKey: sifaQueryKeys.position.byOwner(ownerDid) });\n }\n await options?.onSuccess?.(result, variables, onMutateResult, context);\n },\n ...options,\n });\n}\n","'use client';\n\nimport { useQuery, type UseQueryOptions } from '@tanstack/react-query';\n\nimport { useSifaConfig } from '../config.js';\nimport {\n fetchSearchFilters,\n fetchSearchProfiles,\n fetchSkillSuggestions,\n type FilterOptions,\n type SearchFilters,\n type SearchResponse,\n type SkillSearchResult,\n} from '../fetchers/search.js';\nimport { sifaQueryKeys } from '../keys.js';\n\nexport function useSearchProfiles(\n filters: SearchFilters,\n options?: Omit<\n UseQueryOptions<\n SearchResponse,\n Error,\n SearchResponse,\n ReturnType<typeof sifaQueryKeys.search.profiles>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.search.profiles(filters as Record<string, unknown>),\n queryFn: () => fetchSearchProfiles(config, filters),\n ...options,\n });\n}\n\nexport function useSkillSuggestions(\n query: string,\n options?: Omit<\n UseQueryOptions<\n SkillSearchResult[],\n Error,\n SkillSearchResult[],\n ReturnType<typeof sifaQueryKeys.search.skills>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.search.skills(query),\n queryFn: () => fetchSkillSuggestions(config, query),\n enabled: query.trim().length > 0 && (options?.enabled ?? true),\n ...options,\n });\n}\n\nexport function useSearchFilters(\n options?: Omit<\n UseQueryOptions<\n FilterOptions,\n Error,\n FilterOptions,\n ReturnType<typeof sifaQueryKeys.search.filters>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.search.filters(),\n queryFn: () => fetchSearchFilters(config),\n ...options,\n });\n}\n","'use client';\n\nimport { useQuery, type UseQueryOptions } from '@tanstack/react-query';\n\nimport { useSifaConfig } from '../config.js';\nimport {\n fetchFeaturedProfile,\n fetchSimilarProfiles,\n fetchSuggestionCount,\n fetchSuggestions,\n type FeaturedProfile,\n type FetchSuggestionsOptions,\n type SimilarProfile,\n type SuggestionsResponse,\n} from '../fetchers/discovery.js';\nimport { sifaQueryKeys } from '../keys.js';\n\nexport function useSimilarProfiles(\n did: string | undefined | null,\n opts: { limit?: number } = {},\n options?: Omit<\n UseQueryOptions<\n SimilarProfile[],\n Error,\n SimilarProfile[],\n ReturnType<typeof sifaQueryKeys.discovery.similar>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n const limit = opts.limit ?? 5;\n return useQuery({\n queryKey: sifaQueryKeys.discovery.similar(did ?? '', limit),\n queryFn: () => fetchSimilarProfiles(config, did ?? '', { limit }),\n enabled: Boolean(did) && (options?.enabled ?? true),\n ...options,\n });\n}\n\nexport function useSuggestions(\n opts: Omit<\n FetchSuggestionsOptions,\n keyof Omit<FetchSuggestionsOptions, 'source' | 'includeDismissed' | 'cursor' | 'limit'>\n > = {},\n options?: Omit<\n UseQueryOptions<\n SuggestionsResponse,\n Error,\n SuggestionsResponse,\n ReturnType<typeof sifaQueryKeys.discovery.suggestions>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.discovery.suggestions(opts as Record<string, unknown>),\n queryFn: () => fetchSuggestions(config, opts),\n ...options,\n });\n}\n\nexport function useSuggestionCount(\n since?: string,\n options?: Omit<\n UseQueryOptions<\n number,\n Error,\n number,\n ReturnType<typeof sifaQueryKeys.discovery.suggestionCount>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.discovery.suggestionCount(since),\n queryFn: () => fetchSuggestionCount(config, since),\n ...options,\n });\n}\n\nexport function useFeaturedProfile(\n options?: Omit<\n UseQueryOptions<\n FeaturedProfile | null,\n Error,\n FeaturedProfile | null,\n ReturnType<typeof sifaQueryKeys.discovery.featured>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.discovery.featured(),\n queryFn: () => fetchFeaturedProfile(config),\n ...options,\n });\n}\n","'use client';\n\nimport { useQuery, type UseQueryOptions } from '@tanstack/react-query';\n\nimport { useSifaConfig } from '../config.js';\nimport { fetchFollowing, type FollowingResponse } from '../fetchers/follow.js';\nimport { sifaQueryKeys } from '../keys.js';\n\nexport function useFollowing(\n opts: { source?: string; cursor?: string; limit?: number } = {},\n options?: Omit<\n UseQueryOptions<\n FollowingResponse,\n Error,\n FollowingResponse,\n ReturnType<typeof sifaQueryKeys.follow.following>\n >,\n 'queryKey' | 'queryFn'\n >,\n) {\n const config = useSifaConfig();\n return useQuery({\n queryKey: sifaQueryKeys.follow.following(opts as Record<string, unknown>),\n queryFn: () => fetchFollowing(config, opts),\n ...options,\n });\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@singi-labs/sifa-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Sifa SDK — public client library for the Sifa AppView on AT Protocol. Shared by sifa-web and sifa-app.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -51,31 +51,59 @@
|
|
|
51
51
|
"default": "./dist/schemas/index.cjs"
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
|
+
"./query": {
|
|
55
|
+
"import": {
|
|
56
|
+
"types": "./dist/query/index.d.ts",
|
|
57
|
+
"default": "./dist/query/index.js"
|
|
58
|
+
},
|
|
59
|
+
"require": {
|
|
60
|
+
"types": "./dist/query/index.d.cts",
|
|
61
|
+
"default": "./dist/query/index.cjs"
|
|
62
|
+
}
|
|
63
|
+
},
|
|
54
64
|
"./package.json": "./package.json"
|
|
55
65
|
},
|
|
56
66
|
"main": "./dist/index.cjs",
|
|
57
67
|
"module": "./dist/index.js",
|
|
58
68
|
"types": "./dist/index.d.ts",
|
|
59
69
|
"sideEffects": false,
|
|
70
|
+
"engines": {
|
|
71
|
+
"node": ">=22"
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"zod": "4.4.3"
|
|
75
|
+
},
|
|
76
|
+
"peerDependencies": {
|
|
77
|
+
"@tanstack/react-query": "^5.100.0",
|
|
78
|
+
"react": ">=19"
|
|
79
|
+
},
|
|
80
|
+
"peerDependenciesMeta": {
|
|
81
|
+
"@tanstack/react-query": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"react": {
|
|
85
|
+
"optional": true
|
|
86
|
+
}
|
|
87
|
+
},
|
|
60
88
|
"devDependencies": {
|
|
61
89
|
"@changesets/cli": "2.31.0",
|
|
62
90
|
"@commitlint/cli": "21.0.1",
|
|
63
91
|
"@commitlint/config-conventional": "21.0.1",
|
|
92
|
+
"@tanstack/react-query": "5.100.10",
|
|
93
|
+
"@testing-library/react": "16.3.2",
|
|
94
|
+
"@types/react": "19.2.14",
|
|
64
95
|
"@vitest/coverage-v8": "3.2.1",
|
|
65
96
|
"eslint": "10.3.0",
|
|
66
97
|
"husky": "9.1.7",
|
|
98
|
+
"jsdom": "27.0.0",
|
|
67
99
|
"prettier": "3.8.3",
|
|
100
|
+
"react": "19.2.4",
|
|
101
|
+
"react-dom": "19.2.4",
|
|
68
102
|
"tsup": "8.5.1",
|
|
69
103
|
"typescript": "5.9.3",
|
|
70
104
|
"typescript-eslint": "8.59.3",
|
|
71
105
|
"vitest": "3.2.1"
|
|
72
106
|
},
|
|
73
|
-
"engines": {
|
|
74
|
-
"node": ">=22"
|
|
75
|
-
},
|
|
76
|
-
"dependencies": {
|
|
77
|
-
"zod": "4.4.3"
|
|
78
|
-
},
|
|
79
107
|
"scripts": {
|
|
80
108
|
"build": "tsup",
|
|
81
109
|
"dev": "tsup --watch",
|