@singi-labs/sifa-sdk 0.6.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.cjs +1 -1
- package/dist/index.js +1 -1
- package/dist/query/index.cjs +222 -5
- package/dist/query/index.cjs.map +1 -1
- package/dist/query/index.d.cts +220 -10
- package/dist/query/index.d.ts +220 -10
- package/dist/query/index.js +208 -7
- package/dist/query/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1088,7 +1088,7 @@ var ProfileVolunteeringRecordSchema = zod.z.object({
|
|
|
1088
1088
|
});
|
|
1089
1089
|
|
|
1090
1090
|
// src/index.ts
|
|
1091
|
-
var SIFA_SDK_VERSION = "0.
|
|
1091
|
+
var SIFA_SDK_VERSION = "0.7.0";
|
|
1092
1092
|
|
|
1093
1093
|
exports.CATEGORY_LABELS = CATEGORY_LABELS;
|
|
1094
1094
|
exports.CATEGORY_ORDER = CATEGORY_ORDER;
|
package/dist/index.js
CHANGED
|
@@ -1086,7 +1086,7 @@ var ProfileVolunteeringRecordSchema = z.object({
|
|
|
1086
1086
|
});
|
|
1087
1087
|
|
|
1088
1088
|
// src/index.ts
|
|
1089
|
-
var SIFA_SDK_VERSION = "0.
|
|
1089
|
+
var SIFA_SDK_VERSION = "0.7.0";
|
|
1090
1090
|
|
|
1091
1091
|
export { CATEGORY_LABELS, CATEGORY_ORDER, CONTINENTS, COUNTRIES, EndorsementConfirmationRecordSchema, EndorsementRecordSchema, GraphFollowRecordSchema, INDUSTRY_OPTIONS, PLATFORM_LABELS, PLATFORM_OPTIONS, ProfileCertificationRecordSchema, ProfileCourseRecordSchema, ProfileEducationRecordSchema, ProfileExternalAccountRecordSchema, ProfileHonorRecordSchema, ProfileLanguageRecordSchema, ProfilePositionRecordSchema, ProfileProjectRecordSchema, ProfilePublicationRecordSchema, ProfileSelfRecordSchema, ProfileSkillRecordSchema, ProfileVolunteeringRecordSchema, PublicationAuthorSchema, SIFA_SDK_VERSION, SKILL_CATEGORIES, atUriSchema, certDateExtractor, cidSchema, contrastRatio, countryCodeToFlag, dateRangeExtractor, datetimeSchema, dedupeSkills, detectPdsProvider, didSchema, findIndustry, formatDistanceToNow, formatLocation, formatRelativeTime, getContinent, getDisplayLabel, getFaviconUrl, getHandleStem, getIndustryLabelKey, getPdsDisplayName, getPlatformLabel, groupSkillsByCategory, isKnownPlatform, isValidRgbColor, languageTagSchema, lexiconDateExtractor, maxGraphemes, meetsContrastAA, parseLocationString, pdsProviderFromApi, relativeLuminance, rgbToString, sanitizeHandleInput, selfLabelsSchema, singleDateExtractor, sortByDateDesc, strongRefSchema, truncateGraphemes, uriSchema };
|
|
1092
1092
|
//# sourceMappingURL=index.js.map
|
package/dist/query/index.cjs
CHANGED
|
@@ -108,6 +108,123 @@ function createPosition(config, data, options = {}) {
|
|
|
108
108
|
});
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
// src/query/fetchers/search.ts
|
|
112
|
+
var EMPTY_SEARCH = { profiles: [], total: 0, limit: 20, offset: 0 };
|
|
113
|
+
var EMPTY_FILTERS = { countries: [], industries: [], apps: [] };
|
|
114
|
+
async function fetchSearchProfiles(config, filters, options = {}) {
|
|
115
|
+
const params = new URLSearchParams();
|
|
116
|
+
if (filters.q) params.set("q", filters.q);
|
|
117
|
+
if (filters.skill) params.set("skill", filters.skill);
|
|
118
|
+
if (filters.country) params.set("country", filters.country);
|
|
119
|
+
if (filters.industry) params.set("industry", filters.industry);
|
|
120
|
+
if (filters.domain) params.set("domain", filters.domain);
|
|
121
|
+
if (filters.workplace) params.set("workplace", filters.workplace);
|
|
122
|
+
if (filters.app) params.set("app", filters.app);
|
|
123
|
+
if (filters.limit !== void 0) params.set("limit", String(filters.limit));
|
|
124
|
+
if (params.size === 0) return EMPTY_SEARCH;
|
|
125
|
+
return apiFetch(config, `/api/search/profiles?${params.toString()}`, {
|
|
126
|
+
cache: "no-store",
|
|
127
|
+
...options
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
async function fetchSkillSuggestions(config, query, options = {}) {
|
|
131
|
+
if (!query.trim()) return [];
|
|
132
|
+
const path = `/api/search/skills?q=${encodeURIComponent(query)}&limit=8`;
|
|
133
|
+
const data = await apiFetch(config, path, {
|
|
134
|
+
cache: "no-store",
|
|
135
|
+
...options
|
|
136
|
+
});
|
|
137
|
+
return data.skills ?? [];
|
|
138
|
+
}
|
|
139
|
+
async function fetchSearchFilters(config, options = {}) {
|
|
140
|
+
try {
|
|
141
|
+
return await apiFetch(config, "/api/search/filters", {
|
|
142
|
+
next: { revalidate: 300 },
|
|
143
|
+
...options
|
|
144
|
+
});
|
|
145
|
+
} catch {
|
|
146
|
+
return EMPTY_FILTERS;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// src/query/fetchers/discovery.ts
|
|
151
|
+
async function fetchSimilarProfiles(config, did, opts = {}) {
|
|
152
|
+
const limit = opts.limit ?? 5;
|
|
153
|
+
const path = `/api/discover/similar/${encodeURIComponent(did)}?limit=${limit}`;
|
|
154
|
+
try {
|
|
155
|
+
const data = await apiFetch(config, path, {
|
|
156
|
+
next: { revalidate: 300 },
|
|
157
|
+
timeoutMs: 5e3,
|
|
158
|
+
...opts
|
|
159
|
+
});
|
|
160
|
+
return data.profiles ?? [];
|
|
161
|
+
} catch {
|
|
162
|
+
return [];
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
async function fetchSuggestions(config, opts = {}) {
|
|
166
|
+
const params = new URLSearchParams();
|
|
167
|
+
if (opts.source) params.set("source", opts.source);
|
|
168
|
+
if (opts.includeDismissed) params.set("include_dismissed", "true");
|
|
169
|
+
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
170
|
+
if (opts.limit) params.set("limit", String(opts.limit));
|
|
171
|
+
const qs = params.toString();
|
|
172
|
+
const headers = { ...opts.headers ?? {} };
|
|
173
|
+
if (opts.cookieHeader) headers.cookie = opts.cookieHeader;
|
|
174
|
+
try {
|
|
175
|
+
return await apiFetch(config, `/api/suggestions${qs ? `?${qs}` : ""}`, {
|
|
176
|
+
credentials: "include",
|
|
177
|
+
cache: "no-store",
|
|
178
|
+
timeoutMs: 8e3,
|
|
179
|
+
...opts,
|
|
180
|
+
headers
|
|
181
|
+
});
|
|
182
|
+
} catch {
|
|
183
|
+
return { onSifa: [], notOnSifa: [] };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function fetchSuggestionCount(config, since, options = {}) {
|
|
187
|
+
const params = since ? `?since=${encodeURIComponent(since)}` : "";
|
|
188
|
+
try {
|
|
189
|
+
const data = await apiFetch(config, `/api/suggestions/count${params}`, {
|
|
190
|
+
credentials: "include",
|
|
191
|
+
cache: "no-store",
|
|
192
|
+
...options
|
|
193
|
+
});
|
|
194
|
+
return data.count ?? 0;
|
|
195
|
+
} catch {
|
|
196
|
+
return 0;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
async function fetchFeaturedProfile(config, options = {}) {
|
|
200
|
+
try {
|
|
201
|
+
return await apiFetch(config, "/api/featured-profile", {
|
|
202
|
+
next: { revalidate: 900 },
|
|
203
|
+
...options
|
|
204
|
+
});
|
|
205
|
+
} catch {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/query/fetchers/follow.ts
|
|
211
|
+
async function fetchFollowing(config, opts = {}) {
|
|
212
|
+
const params = new URLSearchParams();
|
|
213
|
+
if (opts.source) params.set("source", opts.source);
|
|
214
|
+
if (opts.cursor) params.set("cursor", opts.cursor);
|
|
215
|
+
if (opts.limit) params.set("limit", String(opts.limit));
|
|
216
|
+
const qs = params.toString();
|
|
217
|
+
try {
|
|
218
|
+
return await apiFetch(config, `/api/following${qs ? `?${qs}` : ""}`, {
|
|
219
|
+
credentials: "include",
|
|
220
|
+
cache: "no-store",
|
|
221
|
+
...opts
|
|
222
|
+
});
|
|
223
|
+
} catch {
|
|
224
|
+
return { follows: [] };
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
111
228
|
// src/query/keys.ts
|
|
112
229
|
var sifaQueryKeys = {
|
|
113
230
|
all: () => ["sifa"],
|
|
@@ -118,10 +235,36 @@ var sifaQueryKeys = {
|
|
|
118
235
|
position: {
|
|
119
236
|
all: () => ["sifa", "position"],
|
|
120
237
|
byOwner: (did) => ["sifa", "position", "by-owner", did]
|
|
238
|
+
},
|
|
239
|
+
search: {
|
|
240
|
+
all: () => ["sifa", "search"],
|
|
241
|
+
profiles: (filters) => ["sifa", "search", "profiles", filters],
|
|
242
|
+
skills: (query) => ["sifa", "search", "skills", query],
|
|
243
|
+
filters: () => ["sifa", "search", "filters"]
|
|
244
|
+
},
|
|
245
|
+
discovery: {
|
|
246
|
+
all: () => ["sifa", "discovery"],
|
|
247
|
+
similar: (did, limit) => ["sifa", "discovery", "similar", did, limit],
|
|
248
|
+
suggestions: (opts) => ["sifa", "discovery", "suggestions", opts],
|
|
249
|
+
suggestionCount: (since) => ["sifa", "discovery", "suggestion-count", since ?? null],
|
|
250
|
+
featured: () => ["sifa", "discovery", "featured"]
|
|
251
|
+
},
|
|
252
|
+
follow: {
|
|
253
|
+
all: () => ["sifa", "follow"],
|
|
254
|
+
following: (opts) => ["sifa", "follow", "following", opts]
|
|
121
255
|
}
|
|
122
256
|
};
|
|
123
257
|
|
|
124
|
-
// src/query/hooks/use-
|
|
258
|
+
// src/query/hooks/use-profile.ts
|
|
259
|
+
function useProfile(handleOrDid, options) {
|
|
260
|
+
const config = useSifaConfig();
|
|
261
|
+
return reactQuery.useQuery({
|
|
262
|
+
queryKey: sifaQueryKeys.profile.byHandle(handleOrDid ?? ""),
|
|
263
|
+
queryFn: () => fetchProfile(config, handleOrDid ?? ""),
|
|
264
|
+
enabled: Boolean(handleOrDid) && (options?.enabled ?? true),
|
|
265
|
+
...options
|
|
266
|
+
});
|
|
267
|
+
}
|
|
125
268
|
function useCreatePosition(ownerDid, options) {
|
|
126
269
|
const config = useSifaConfig();
|
|
127
270
|
const queryClient = reactQuery.useQueryClient();
|
|
@@ -137,12 +280,70 @@ function useCreatePosition(ownerDid, options) {
|
|
|
137
280
|
...options
|
|
138
281
|
});
|
|
139
282
|
}
|
|
140
|
-
function
|
|
283
|
+
function useSearchProfiles(filters, options) {
|
|
141
284
|
const config = useSifaConfig();
|
|
142
285
|
return reactQuery.useQuery({
|
|
143
|
-
queryKey: sifaQueryKeys.
|
|
144
|
-
queryFn: () =>
|
|
145
|
-
|
|
286
|
+
queryKey: sifaQueryKeys.search.profiles(filters),
|
|
287
|
+
queryFn: () => fetchSearchProfiles(config, filters),
|
|
288
|
+
...options
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
function useSkillSuggestions(query, options) {
|
|
292
|
+
const config = useSifaConfig();
|
|
293
|
+
return reactQuery.useQuery({
|
|
294
|
+
queryKey: sifaQueryKeys.search.skills(query),
|
|
295
|
+
queryFn: () => fetchSkillSuggestions(config, query),
|
|
296
|
+
enabled: query.trim().length > 0 && (options?.enabled ?? true),
|
|
297
|
+
...options
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
function useSearchFilters(options) {
|
|
301
|
+
const config = useSifaConfig();
|
|
302
|
+
return reactQuery.useQuery({
|
|
303
|
+
queryKey: sifaQueryKeys.search.filters(),
|
|
304
|
+
queryFn: () => fetchSearchFilters(config),
|
|
305
|
+
...options
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function useSimilarProfiles(did, opts = {}, options) {
|
|
309
|
+
const config = useSifaConfig();
|
|
310
|
+
const limit = opts.limit ?? 5;
|
|
311
|
+
return reactQuery.useQuery({
|
|
312
|
+
queryKey: sifaQueryKeys.discovery.similar(did ?? "", limit),
|
|
313
|
+
queryFn: () => fetchSimilarProfiles(config, did ?? "", { limit }),
|
|
314
|
+
enabled: Boolean(did) && (options?.enabled ?? true),
|
|
315
|
+
...options
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
function useSuggestions(opts = {}, options) {
|
|
319
|
+
const config = useSifaConfig();
|
|
320
|
+
return reactQuery.useQuery({
|
|
321
|
+
queryKey: sifaQueryKeys.discovery.suggestions(opts),
|
|
322
|
+
queryFn: () => fetchSuggestions(config, opts),
|
|
323
|
+
...options
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
function useSuggestionCount(since, options) {
|
|
327
|
+
const config = useSifaConfig();
|
|
328
|
+
return reactQuery.useQuery({
|
|
329
|
+
queryKey: sifaQueryKeys.discovery.suggestionCount(since),
|
|
330
|
+
queryFn: () => fetchSuggestionCount(config, since),
|
|
331
|
+
...options
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
function useFeaturedProfile(options) {
|
|
335
|
+
const config = useSifaConfig();
|
|
336
|
+
return reactQuery.useQuery({
|
|
337
|
+
queryKey: sifaQueryKeys.discovery.featured(),
|
|
338
|
+
queryFn: () => fetchFeaturedProfile(config),
|
|
339
|
+
...options
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
function useFollowing(opts = {}, options) {
|
|
343
|
+
const config = useSifaConfig();
|
|
344
|
+
return reactQuery.useQuery({
|
|
345
|
+
queryKey: sifaQueryKeys.follow.following(opts),
|
|
346
|
+
queryFn: () => fetchFollowing(config, opts),
|
|
146
347
|
...options
|
|
147
348
|
});
|
|
148
349
|
}
|
|
@@ -152,10 +353,26 @@ exports.SifaProvider = SifaProvider;
|
|
|
152
353
|
exports.apiFetch = apiFetch;
|
|
153
354
|
exports.apiFetchOrNull = apiFetchOrNull;
|
|
154
355
|
exports.createPosition = createPosition;
|
|
356
|
+
exports.fetchFeaturedProfile = fetchFeaturedProfile;
|
|
357
|
+
exports.fetchFollowing = fetchFollowing;
|
|
155
358
|
exports.fetchProfile = fetchProfile;
|
|
359
|
+
exports.fetchSearchFilters = fetchSearchFilters;
|
|
360
|
+
exports.fetchSearchProfiles = fetchSearchProfiles;
|
|
361
|
+
exports.fetchSimilarProfiles = fetchSimilarProfiles;
|
|
362
|
+
exports.fetchSkillSuggestions = fetchSkillSuggestions;
|
|
363
|
+
exports.fetchSuggestionCount = fetchSuggestionCount;
|
|
364
|
+
exports.fetchSuggestions = fetchSuggestions;
|
|
156
365
|
exports.sifaQueryKeys = sifaQueryKeys;
|
|
157
366
|
exports.useCreatePosition = useCreatePosition;
|
|
367
|
+
exports.useFeaturedProfile = useFeaturedProfile;
|
|
368
|
+
exports.useFollowing = useFollowing;
|
|
158
369
|
exports.useProfile = useProfile;
|
|
370
|
+
exports.useSearchFilters = useSearchFilters;
|
|
371
|
+
exports.useSearchProfiles = useSearchProfiles;
|
|
159
372
|
exports.useSifaConfig = useSifaConfig;
|
|
373
|
+
exports.useSimilarProfiles = useSimilarProfiles;
|
|
374
|
+
exports.useSkillSuggestions = useSkillSuggestions;
|
|
375
|
+
exports.useSuggestionCount = useSuggestionCount;
|
|
376
|
+
exports.useSuggestions = useSuggestions;
|
|
160
377
|
//# sourceMappingURL=index.cjs.map
|
|
161
378
|
//# sourceMappingURL=index.cjs.map
|
package/dist/query/index.cjs.map
CHANGED
|
@@ -1 +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/keys.ts","../../src/query/hooks/use-create-position.ts","../../src/query/hooks/use-profile.ts"],"names":["createContext","useContext","useQueryClient","useMutation","useQuery"],"mappings":";;;;;;;AA2CO,IAAM,QAAA,GAAN,cAAuB,KAAA,CAAM;AAAA,EACzB,MAAA;AAAA,EACA,IAAA;AAAA,EAET,WAAA,CAAY,OAAA,EAAiB,MAAA,EAAgB,IAAA,EAAgB;AAC3D,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,UAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AAAA,EACd;AACF;AAEA,IAAM,kBAAA,GAAqB,GAAA;AAC3B,IAAM,sBAAA,GAAyB,CAAA;AAC/B,IAAM,4BAAA,GAA+B,CAAA;AASrC,eAAsB,QAAA,CACpB,MAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EAChB;AACZ,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,KAAA,IAAS,UAAA,CAAW,KAAA;AAC3C,EAAA,MAAM,GAAA,GAAM,CAAA,EAAG,MAAA,CAAO,OAAO,GAAG,IAAI,CAAA,CAAA;AACpC,EAAA,MAAM,UAAA,GAAa,OAAA,CAAQ,UAAA,GAAa,sBAAA,GAAyB,CAAA;AAEjE,EAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,UAAA,EAAY,OAAA,EAAA,EAAW;AACtD,IAAA,MAAM,SAAS,OAAA,CAAQ,MAAA,IAAU,YAAY,OAAA,CAAQ,OAAA,CAAQ,aAAa,kBAAkB,CAAA;AAE5F,IAAA,MAAM,UAAkC,EAAE,GAAI,OAAA,CAAQ,OAAA,IAAW,EAAC,EAAG;AACrE,IAAA,IAAI,IAAA;AACJ,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW;AAC9B,MAAA,OAAA,CAAQ,cAAc,CAAA,KAAM,kBAAA;AAC5B,MAAA,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA;AAAA,IACpC;AAGA,IAAA,MAAM,IAAA,GAAO;AAAA,MACX,MAAA,EAAQ,QAAQ,MAAA,IAAU,KAAA;AAAA,MAC1B,OAAA;AAAA,MACA,IAAA;AAAA,MACA,MAAA;AAAA,MACA,aAAa,OAAA,CAAQ,WAAA;AAAA,MACrB,OAAO,OAAA,CAAQ,KAAA;AAAA,MACf,GAAI,QAAQ,IAAA,GAAO,EAAE,MAAM,OAAA,CAAQ,IAAA,KAAS;AAAC,KAC/C;AAEA,IAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAQ,GAAA,EAAK,IAAI,CAAA;AAEnC,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,IAAO,OAAA,GAAU,UAAA,EAAY;AAC9C,MAAA,MAAM,aAAA,GAAgB,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAA;AACnD,MAAA,MAAM,aAAa,aAAA,GAAgB,MAAA,CAAO,QAAA,CAAS,aAAA,EAAe,EAAE,CAAA,GAAI,CAAA;AACxE,MAAA,MAAM,cAAc,IAAA,CAAK,GAAA;AAAA,QACvB,MAAA,CAAO,QAAA,CAAS,UAAU,CAAA,GAAI,UAAA,GAAa,CAAA;AAAA,QAC3C;AAAA,OACF;AACA,MAAA,MAAM,IAAI,QAAQ,CAAC,CAAA,KAAM,WAAW,CAAA,EAAG,WAAA,GAAc,GAAI,CAAC,CAAA;AAC1D,MAAA;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,IAAI,OAAA;AACJ,MAAA,IAAI;AACF,QAAA,OAAA,GAAW,MAAM,IAAI,IAAA,EAAK;AAAA,MAC5B,CAAA,CAAA,MAAQ;AACN,QAAA,IAAI;AACF,UAAA,OAAA,GAAU,MAAM,IAAI,IAAA,EAAK;AAAA,QAC3B,CAAA,CAAA,MAAQ;AACN,UAAA,OAAA,GAAU,MAAA;AAAA,QACZ;AAAA,MACF;AACA,MAAA,MAAM,IAAI,QAAA,CAAS,CAAA,SAAA,EAAY,GAAA,CAAI,MAAM,OAAO,IAAI,CAAA,CAAA,EAAI,GAAA,CAAI,MAAA,EAAQ,OAAO,CAAA;AAAA,IAC7E;AAEA,IAAA,OAAQ,MAAM,IAAI,IAAA,EAAK;AAAA,EACzB;AAEA,EAAA,MAAM,IAAI,QAAA,CAAS,CAAA,8BAAA,EAAiC,IAAI,IAAI,GAAG,CAAA;AACjE;AAOA,eAAsB,cAAA,CACpB,MAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EACT;AACnB,EAAA,IAAI;AACF,IAAA,OAAO,MAAM,QAAA,CAAY,MAAA,EAAQ,IAAA,EAAM,OAAO,CAAA;AAAA,EAChD,SAAS,CAAA,EAAG;AACV,IAAA,IAAI,CAAA,YAAa,QAAA,IAAY,CAAA,CAAE,MAAA,KAAW,KAAK,OAAO,IAAA;AACtD,IAAA,MAAM,CAAA;AAAA,EACR;AACF;AC3IA,IAAM,iBAAA,GAAoBA,oBAAoC,IAAI,CAAA;AAoB3D,SAAS,YAAA,CAAa,EAAE,MAAA,EAAQ,QAAA,EAAS,EAAsB;AACpE,EAAA,sCAAQ,iBAAA,CAAkB,QAAA,EAAlB,EAA2B,KAAA,EAAO,QAAS,QAAA,EAAS,CAAA;AAC9D;AAMO,SAAS,aAAA,GAA+B;AAC7C,EAAA,MAAM,GAAA,GAAMC,iBAAW,iBAAiB,CAAA;AACxC,EAAA,IAAI,CAAC,GAAA,EAAK;AACR,IAAA,MAAM,IAAI,KAAA;AAAA,MACR;AAAA,KACF;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;;;AC/BO,SAAS,YAAA,CACd,MAAA,EACA,WAAA,EACA,OAAA,GAA2B,EAAC,EACH;AACzB,EAAA,MAAM,IAAA,GAAO,CAAA,aAAA,EAAgB,kBAAA,CAAmB,WAAW,CAAC,CAAA,CAAA;AAC5D,EAAA,OAAO,cAAA,CAAwB,QAAQ,IAAA,EAAM;AAAA,IAC3C,UAAA,EAAY,IAAA;AAAA,IACZ,GAAG;AAAA,GACJ,CAAA;AACH;;;ACEO,SAAS,cAAA,CACd,MAAA,EACA,IAAA,EACA,OAAA,GAA2B,EAAC,EACL;AACvB,EAAA,OAAO,QAAA,CAAuB,QAAQ,gBAAA,EAAkB;AAAA,IACtD,MAAA,EAAQ,MAAA;AAAA,IACR,IAAA,EAAM,IAAA;AAAA,IACN,WAAA,EAAa,SAAA;AAAA,IACb,GAAG;AAAA,GACJ,CAAA;AACH;;;ACvBO,IAAM,aAAA,GAAgB;AAAA,EAC3B,GAAA,EAAK,MAAM,CAAC,MAAM,CAAA;AAAA,EAElB,OAAA,EAAS;AAAA,IACP,GAAA,EAAK,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA;AAAA,IAC7B,UAAU,CAAC,WAAA,KAAwB,CAAC,MAAA,EAAQ,WAAW,WAAW;AAAA,GACpE;AAAA,EAEA,QAAA,EAAU;AAAA,IACR,GAAA,EAAK,MAAM,CAAC,MAAA,EAAQ,UAAU,CAAA;AAAA,IAC9B,SAAS,CAAC,GAAA,KAAgB,CAAC,MAAA,EAAQ,UAAA,EAAY,YAAY,GAAG;AAAA;AAElE;;;ACPO,SAAS,iBAAA,CACd,UACA,OAAA,EACA;AACA,EAAA,MAAM,SAAS,aAAA,EAAc;AAC7B,EAAA,MAAM,cAAcC,yBAAA,EAAe;AAEnC,EAAA,OAAOC,sBAAA,CAAY;AAAA,IACjB,UAAA,EAAY,CAAC,IAAA,KAAkC,cAAA,CAAe,QAAQ,IAAI,CAAA;AAAA,IAC1E,SAAA,EAAW,OAAO,MAAA,EAAQ,SAAA,EAAW,gBAAgB,OAAA,KAAY;AAC/D,MAAA,IAAI,OAAO,OAAA,EAAS;AAClB,QAAA,MAAM,WAAA,CAAY,kBAAkB,EAAE,QAAA,EAAU,cAAc,OAAA,CAAQ,QAAA,CAAS,QAAQ,CAAA,EAAG,CAAA;AAC1F,QAAA,MAAM,WAAA,CAAY,kBAAkB,EAAE,QAAA,EAAU,cAAc,QAAA,CAAS,OAAA,CAAQ,QAAQ,CAAA,EAAG,CAAA;AAAA,MAC5F;AACA,MAAA,MAAM,OAAA,EAAS,SAAA,GAAY,MAAA,EAAQ,SAAA,EAAW,gBAAgB,OAAO,CAAA;AAAA,IACvE,CAAA;AAAA,IACA,GAAG;AAAA,GACJ,CAAA;AACH;AClBO,SAAS,UAAA,CACd,aACA,OAAA,EASA;AACA,EAAA,MAAM,SAAS,aAAA,EAAc;AAC7B,EAAA,OAAOC,mBAAA,CAAS;AAAA,IACd,QAAA,EAAU,aAAA,CAAc,OAAA,CAAQ,QAAA,CAAS,eAAe,EAAE,CAAA;AAAA,IAC1D,OAAA,EAAS,MAAM,YAAA,CAAa,MAAA,EAAQ,eAAe,EAAE,CAAA;AAAA,IACrD,OAAA,EAAS,OAAA,CAAQ,WAAW,CAAA,KAAM,SAAS,OAAA,IAAW,IAAA,CAAA;AAAA,IACtD,GAAG;AAAA,GACJ,CAAA;AACH","file":"index.cjs","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","/**\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} 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","'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 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"]}
|
|
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":["createContext","useContext","useQuery","useQueryClient","useMutation"],"mappings":"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","file":"index.cjs","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"]}
|