@singi-labs/sifa-sdk 0.19.74 → 0.19.76

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.
Files changed (63) hide show
  1. package/dist/format/index.js +2 -0
  2. package/dist/format/index.js.map +1 -1
  3. package/dist/format/openalex.js +10 -0
  4. package/dist/format/openalex.js.map +1 -0
  5. package/dist/{index-BFdNI6jA.d.ts → index-DAiJZvPg.d.ts} +8 -0
  6. package/dist/{index-BzkaABDb.d.cts → index-k6tBrbEU.d.cts} +8 -0
  7. package/dist/index.cjs +71 -1
  8. package/dist/index.cjs.map +1 -1
  9. package/dist/index.d.cts +14 -6
  10. package/dist/index.d.ts +14 -6
  11. package/dist/index.js +27 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/jsonld/index.d.cts +1 -1
  14. package/dist/jsonld/index.d.ts +1 -1
  15. package/dist/logic/index.js +26 -0
  16. package/dist/logic/index.js.map +1 -1
  17. package/dist/logic/site-activity-preset.js +68 -0
  18. package/dist/logic/site-activity-preset.js.map +1 -0
  19. package/dist/{network-map-BRBzAHrT.d.cts → network-map-DInAc_DB.d.cts} +3 -3
  20. package/dist/{network-map-DIUQSB0r.d.ts → network-map-DvALufF9.d.ts} +3 -3
  21. package/dist/{profile-summary-X4MGExfS.d.cts → profile-summary-BcbJrph1.d.cts} +1 -1
  22. package/dist/{profile-summary-6GySSJvO.d.ts → profile-summary-PRDqxAQT.d.ts} +1 -1
  23. package/dist/query/fetchers/activity-preferences.js +43 -0
  24. package/dist/query/fetchers/activity-preferences.js.map +1 -0
  25. package/dist/query/fetchers/activity.js +3 -0
  26. package/dist/query/fetchers/activity.js.map +1 -1
  27. package/dist/query/fetchers/index.cjs +88 -1
  28. package/dist/query/fetchers/index.cjs.map +1 -1
  29. package/dist/query/fetchers/index.d.cts +6 -6
  30. package/dist/query/fetchers/index.d.ts +6 -6
  31. package/dist/query/fetchers/index.js +16 -0
  32. package/dist/query/fetchers/index.js.map +1 -1
  33. package/dist/query/fetchers/user-settings.js +48 -0
  34. package/dist/query/fetchers/user-settings.js.map +1 -0
  35. package/dist/query/hooks/index.cjs +141 -1
  36. package/dist/query/hooks/index.cjs.map +1 -1
  37. package/dist/query/hooks/index.d.cts +4 -4
  38. package/dist/query/hooks/index.d.ts +4 -4
  39. package/dist/query/hooks/index.js +11 -0
  40. package/dist/query/hooks/index.js.map +1 -1
  41. package/dist/query/hooks/use-activity-preferences.js +53 -0
  42. package/dist/query/hooks/use-activity-preferences.js.map +1 -0
  43. package/dist/query/hooks/use-activity.js.map +1 -1
  44. package/dist/query/hooks/use-user-settings.js +34 -0
  45. package/dist/query/hooks/use-user-settings.js.map +1 -0
  46. package/dist/query/index.cjs +143 -1
  47. package/dist/query/index.cjs.map +1 -1
  48. package/dist/query/index.d.cts +9 -9
  49. package/dist/query/index.d.ts +9 -9
  50. package/dist/query/index.js +27 -0
  51. package/dist/query/index.js.map +1 -1
  52. package/dist/query/keys.js +7 -1
  53. package/dist/query/keys.js.map +1 -1
  54. package/dist/resume/index.d.cts +1 -1
  55. package/dist/resume/index.d.ts +1 -1
  56. package/dist/types/index.js.map +1 -1
  57. package/dist/{types-DxMMqOJo.d.ts → types-CI24asgo.d.ts} +72 -2
  58. package/dist/{types-DXep6553.d.cts → types-qw55KvoV.d.cts} +72 -2
  59. package/dist/{use-org-notification-emails-CfDfHwBl.d.ts → use-user-settings-B1rJmjQR.d.ts} +32 -5
  60. package/dist/{use-org-notification-emails-5V3ZAlRd.d.cts → use-user-settings-CYDXJNmI.d.cts} +32 -5
  61. package/dist/{org-Spq8yU-B.d.ts → user-settings-Qwi6wSnK.d.ts} +89 -4
  62. package/dist/{org-A9Tz-Uv9.d.cts → user-settings-lenJHz19.d.cts} +89 -4
  63. package/package.json +1 -1
@@ -0,0 +1,43 @@
1
+ // src/query/fetchers/activity-preferences.ts
2
+ import {
3
+ apiFetch,
4
+ apiWrite,
5
+ encodeIdentifier
6
+ } from "../client.js";
7
+ async function fetchActivityPreferences(config, options = {}) {
8
+ const headers = { ...options.headers ?? {} };
9
+ if (options.cookieHeader) headers.cookie = options.cookieHeader;
10
+ try {
11
+ return await apiFetch(config, "/api/activity-preferences", {
12
+ credentials: "include",
13
+ cache: "no-store",
14
+ ...options,
15
+ headers
16
+ });
17
+ } catch {
18
+ return null;
19
+ }
20
+ }
21
+ function updateActivityPreferences(config, input, options = {}) {
22
+ return apiWrite(config, "/api/activity-preferences", "PUT", {
23
+ ...options,
24
+ body: input
25
+ });
26
+ }
27
+ async function fetchSiteActivityPreset(config, handleOrDid, options = {}) {
28
+ try {
29
+ return await apiFetch(
30
+ config,
31
+ `/api/activity-preferences/site-preset/${encodeIdentifier(handleOrDid)}`,
32
+ options
33
+ );
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+ export {
39
+ fetchActivityPreferences,
40
+ fetchSiteActivityPreset,
41
+ updateActivityPreferences
42
+ };
43
+ //# sourceMappingURL=activity-preferences.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/query/fetchers/activity-preferences.ts"],"sourcesContent":["import type { SiteActivityPreset } from '../../logic/site-activity-preset.js';\nimport {\n apiFetch,\n apiWrite,\n encodeIdentifier,\n type ApiFetchOptions,\n type SifaApiConfig,\n type WriteResult,\n} from '../client.js';\n\n/**\n * The authenticated user's activity preferences, resolved to an answer for\n * every known key. `sitePreset` is the personal-site \"show only\" filter; both\n * lists empty means no preset.\n */\nexport interface ActivityPreferences {\n streamCategories: Record<string, boolean>;\n digestItemTypes: Record<string, boolean>;\n sitePreset: SiteActivityPreset;\n}\n\n/**\n * Partial update. Only the keys present change. `sitePreset`, when present,\n * replaces the whole stored preset; send empty lists to clear it.\n */\nexport interface UpdateActivityPreferencesInput {\n streamCategories?: Record<string, boolean>;\n digestItemTypes?: Record<string, boolean>;\n sitePreset?: SiteActivityPreset;\n}\n\n/** A user's personal-site preset as anyone can read it. */\nexport interface SiteActivityPresetResponse extends SiteActivityPreset {\n did: string;\n /** False when both lists are empty, i.e. the site shows all activity. */\n active: boolean;\n}\n\nexport interface FetchActivityPreferencesOptions extends ApiFetchOptions {\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 */\n cookieHeader?: string;\n}\n\n/** The caller's own preferences. Returns `null` on any error. */\nexport async function fetchActivityPreferences(\n config: SifaApiConfig,\n options: FetchActivityPreferencesOptions = {},\n): Promise<ActivityPreferences | null> {\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n if (options.cookieHeader) headers.cookie = options.cookieHeader;\n\n try {\n return await apiFetch<ActivityPreferences>(config, '/api/activity-preferences', {\n credentials: 'include',\n cache: 'no-store',\n ...options,\n headers,\n });\n } catch {\n return null;\n }\n}\n\n/** Update the caller's own preferences. */\nexport function updateActivityPreferences(\n config: SifaApiConfig,\n input: UpdateActivityPreferencesInput,\n options: ApiFetchOptions = {},\n): Promise<WriteResult<Partial<ActivityPreferences>>> {\n return apiWrite<Partial<ActivityPreferences>>(config, '/api/activity-preferences', 'PUT', {\n ...options,\n body: input,\n });\n}\n\n/**\n * Public read of a user's personal-site preset, by handle or DID. Site\n * builders use it to show the same activity page.sifa.id shows. Returns\n * `null` on any error.\n */\nexport async function fetchSiteActivityPreset(\n config: SifaApiConfig,\n handleOrDid: string,\n options: ApiFetchOptions = {},\n): Promise<SiteActivityPresetResponse | null> {\n try {\n return await apiFetch<SiteActivityPresetResponse>(\n config,\n `/api/activity-preferences/site-preset/${encodeIdentifier(handleOrDid)}`,\n options,\n );\n } catch {\n return null;\n }\n}\n"],"mappings":";AACA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OAIK;AAuCP,eAAsB,yBACpB,QACA,UAA2C,CAAC,GACP;AACrC,QAAM,UAAkC,EAAE,GAAI,QAAQ,WAAW,CAAC,EAAG;AACrE,MAAI,QAAQ,aAAc,SAAQ,SAAS,QAAQ;AAEnD,MAAI;AACF,WAAO,MAAM,SAA8B,QAAQ,6BAA6B;AAAA,MAC9E,aAAa;AAAA,MACb,OAAO;AAAA,MACP,GAAG;AAAA,MACH;AAAA,IACF,CAAC;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,SAAS,0BACd,QACA,OACA,UAA2B,CAAC,GACwB;AACpD,SAAO,SAAuC,QAAQ,6BAA6B,OAAO;AAAA,IACxF,GAAG;AAAA,IACH,MAAM;AAAA,EACR,CAAC;AACH;AAOA,eAAsB,wBACpB,QACA,aACA,UAA2B,CAAC,GACgB;AAC5C,MAAI;AACF,WAAO,MAAM;AAAA,MACX;AAAA,MACA,yCAAyC,iBAAiB,WAAW,CAAC;AAAA,MACtE;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -33,6 +33,9 @@ async function fetchActivityTeaser(config, handleOrDid, options = {}) {
33
33
  async function fetchActivityFeed(config, handleOrDid, options = {}) {
34
34
  const params = new URLSearchParams();
35
35
  if (options.category) params.set("category", options.category);
36
+ if (options.categories?.length) params.set("categories", options.categories.join(","));
37
+ if (options.tags?.length) params.set("tags", options.tags.join(","));
38
+ if (options.preset) params.set("preset", options.preset);
36
39
  if (options.limit) params.set("limit", String(options.limit));
37
40
  if (options.cursor) params.set("cursor", options.cursor);
38
41
  const qs = params.toString();
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/query/fetchers/activity.ts"],"sourcesContent":["import type { ActivityLabel } from '../../cards/adult-content.js';\nimport { encodeIdentifier, apiFetch, type ApiFetchOptions, type SifaApiConfig } from '../client.js';\nimport type { QuotedPostResult } from './quoted-posts.js';\n\n/**\n * Liveness of an activity card's destination, as reported by sifa-api's\n * `/api/activity` enrichment (see sifa-api `url-health-checker`). How liveness\n * is measured depends on the card's health strategy (see `resolveCardHealth`):\n * first-party permalinks (`record`) are checked by record existence on the PDS;\n * foreign/derived targets (`url`) by HTTP reachability (HEAD, then GET).\n *\n * ok -- record exists, or the URL returned 2xx/3xx\n * broken -- confirmed dead (record deleted, or >=2 consecutive 4xx / >=5 consecutive 5xx)\n * unverifiable -- 403/429, PDS unreachable, or network error; NOT dead\n * unknown -- newly seen, not yet checked\n *\n * Consumers should only suppress UI on `'broken'`. All other values\n * (including missing) mean \"render normally\".\n */\nexport type ActivityItemLinkHealth = 'ok' | 'broken' | 'unverifiable' | 'unknown';\n\nexport interface HeatmapDay {\n date: string;\n total: number;\n apps: { appId: string; count: number }[];\n}\n\nexport interface HeatmapResponse {\n days: HeatmapDay[];\n appTotals: { appId: string; appName: string; total: number }[];\n thresholds: [number, number, number, number];\n}\n\nexport interface ActivityItem {\n uri: string;\n cid: string;\n collection: string;\n rkey: string;\n record: Record<string, unknown>;\n appId: string;\n appName: string;\n category: string;\n indexedAt: string;\n /**\n * The record author's handle / display name / avatar, injected by the server\n * on MULTI-author feeds (the following feed) so a card can show whose activity\n * it is. Unset on the per-author activity feed (all one author). Mirrors the\n * stream `ActivityItem`'s author fields the view-model transform reads.\n */\n authorHandle?: string;\n authorDisplayName?: string;\n authorAvatar?: string;\n /**\n * Set by the server when an `app.bsky.embed.record` quote was already\n * resolved upstream (AppView path). Mutually exclusive with `quotedPostUri`.\n */\n quotedPost?: QuotedPostResult;\n /**\n * Set by the server when an `app.bsky.embed.record` quote needs client-side\n * resolution (PDS path). Pass batches to {@link resolveQuotedPosts}.\n * Mutually exclusive with `quotedPost`.\n */\n quotedPostUri?: string;\n /**\n * Reachability of the card's external destination as last checked by\n * sifa-api. Undefined for legacy responses; treat as 'unknown'.\n * See {@link ActivityItemLinkHealth}.\n */\n linkHealth?: ActivityItemLinkHealth;\n /**\n * Content labels from `app.bsky.feed.defs#postView.labels`. Set by\n * sifa-api for Bluesky posts; undefined for other sources and legacy\n * responses. Pass items to {@link hasAdultContent} to decide whether to\n * gate media. See {@link ActivityLabel}.\n */\n labels?: ActivityLabel[];\n /**\n * The owner has hidden this item from their profile. Only ever set on the\n * owner's own view of their feed -- every other surface omits hidden items\n * rather than flagging them -- so a card can offer an unhide in place.\n */\n hidden?: boolean;\n}\n\nexport interface ActivityTeaserResponse {\n items: ActivityItem[];\n blueskyGated?: boolean;\n backfillPending?: boolean;\n failedApps?: string[];\n}\n\nexport interface ActivityFeedResponse {\n items: ActivityItem[];\n cursor: string | null;\n hasMore: boolean;\n availableCategories?: string[];\n blueskyGated?: boolean;\n failedApps?: string[];\n}\n\n/**\n * Per-day activity counts for a profile across all ATproto apps. Returns\n * `null` on any error so callers can render a graceful empty state.\n */\nexport async function fetchHeatmapData(\n config: SifaApiConfig,\n handleOrDid: string,\n days: number,\n options: ApiFetchOptions = {},\n): Promise<HeatmapResponse | null> {\n const path = `/api/activity/${encodeIdentifier(handleOrDid)}/heatmap?days=${days}`;\n try {\n return await apiFetch<HeatmapResponse>(config, path, {\n next: { revalidate: 900, tags: [`heatmap-${handleOrDid}`] },\n ...options,\n });\n } catch {\n return null;\n }\n}\n\nexport interface FetchActivityTeaserOptions extends ApiFetchOptions {\n /**\n * Pass the caller's `Cookie` header on Next.js RSC server-side calls.\n * Required for authenticated server fetches because `credentials: 'include'`\n * does not propagate browser cookies in RSC.\n */\n cookieHeader?: string;\n}\n\n/**\n * Recent activity teaser for a profile (homepage-sized slice). Caps the\n * upstream wait so the SSR path cannot hang. Returns `null` on any error.\n */\nexport async function fetchActivityTeaser(\n config: SifaApiConfig,\n handleOrDid: string,\n options: FetchActivityTeaserOptions = {},\n): Promise<ActivityTeaserResponse | null> {\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n if (options.cookieHeader) headers.cookie = options.cookieHeader;\n\n try {\n return await apiFetch<ActivityTeaserResponse>(\n config,\n `/api/activity/${encodeIdentifier(handleOrDid)}/teaser`,\n {\n credentials: 'include',\n timeoutMs: 8000,\n next: { revalidate: 300, tags: [`activity-teaser-${handleOrDid}`] },\n ...options,\n headers,\n },\n );\n } catch {\n return null;\n }\n}\n\nexport interface FetchActivityFeedOptions extends ApiFetchOptions {\n category?: string;\n limit?: number;\n cursor?: string;\n cookieHeader?: string;\n}\n\n/**\n * Paginated activity feed for a profile. Always fresh (`cache: 'no-store'`).\n * Returns `null` on any error.\n */\nexport async function fetchActivityFeed(\n config: SifaApiConfig,\n handleOrDid: string,\n options: FetchActivityFeedOptions = {},\n): Promise<ActivityFeedResponse | null> {\n const params = new URLSearchParams();\n if (options.category) params.set('category', options.category);\n if (options.limit) params.set('limit', String(options.limit));\n if (options.cursor) params.set('cursor', options.cursor);\n const qs = params.toString();\n\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n if (options.cookieHeader) headers.cookie = options.cookieHeader;\n\n try {\n return await apiFetch<ActivityFeedResponse>(\n config,\n `/api/activity/${encodeIdentifier(handleOrDid)}${qs ? `?${qs}` : ''}`,\n {\n credentials: 'include',\n cache: 'no-store',\n ...options,\n headers,\n },\n );\n } catch {\n return null;\n }\n}\n"],"mappings":";AACA,SAAS,kBAAkB,gBAA0D;AAuGrF,eAAsB,iBACpB,QACA,aACA,MACA,UAA2B,CAAC,GACK;AACjC,QAAM,OAAO,iBAAiB,iBAAiB,WAAW,CAAC,iBAAiB,IAAI;AAChF,MAAI;AACF,WAAO,MAAM,SAA0B,QAAQ,MAAM;AAAA,MACnD,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC,WAAW,WAAW,EAAE,EAAE;AAAA,MAC1D,GAAG;AAAA,IACL,CAAC;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeA,eAAsB,oBACpB,QACA,aACA,UAAsC,CAAC,GACC;AACxC,QAAM,UAAkC,EAAE,GAAI,QAAQ,WAAW,CAAC,EAAG;AACrE,MAAI,QAAQ,aAAc,SAAQ,SAAS,QAAQ;AAEnD,MAAI;AACF,WAAO,MAAM;AAAA,MACX;AAAA,MACA,iBAAiB,iBAAiB,WAAW,CAAC;AAAA,MAC9C;AAAA,QACE,aAAa;AAAA,QACb,WAAW;AAAA,QACX,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC,mBAAmB,WAAW,EAAE,EAAE;AAAA,QAClE,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAaA,eAAsB,kBACpB,QACA,aACA,UAAoC,CAAC,GACC;AACtC,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,QAAQ,SAAU,QAAO,IAAI,YAAY,QAAQ,QAAQ;AAC7D,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AAC5D,MAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AACvD,QAAM,KAAK,OAAO,SAAS;AAE3B,QAAM,UAAkC,EAAE,GAAI,QAAQ,WAAW,CAAC,EAAG;AACrE,MAAI,QAAQ,aAAc,SAAQ,SAAS,QAAQ;AAEnD,MAAI;AACF,WAAO,MAAM;AAAA,MACX;AAAA,MACA,iBAAiB,iBAAiB,WAAW,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACnE;AAAA,QACE,aAAa;AAAA,QACb,OAAO;AAAA,QACP,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../src/query/fetchers/activity.ts"],"sourcesContent":["import type { ActivityLabel } from '../../cards/adult-content.js';\nimport { encodeIdentifier, apiFetch, type ApiFetchOptions, type SifaApiConfig } from '../client.js';\nimport type { QuotedPostResult } from './quoted-posts.js';\n\n/**\n * Liveness of an activity card's destination, as reported by sifa-api's\n * `/api/activity` enrichment (see sifa-api `url-health-checker`). How liveness\n * is measured depends on the card's health strategy (see `resolveCardHealth`):\n * first-party permalinks (`record`) are checked by record existence on the PDS;\n * foreign/derived targets (`url`) by HTTP reachability (HEAD, then GET).\n *\n * ok -- record exists, or the URL returned 2xx/3xx\n * broken -- confirmed dead (record deleted, or >=2 consecutive 4xx / >=5 consecutive 5xx)\n * unverifiable -- 403/429, PDS unreachable, or network error; NOT dead\n * unknown -- newly seen, not yet checked\n *\n * Consumers should only suppress UI on `'broken'`. All other values\n * (including missing) mean \"render normally\".\n */\nexport type ActivityItemLinkHealth = 'ok' | 'broken' | 'unverifiable' | 'unknown';\n\nexport interface HeatmapDay {\n date: string;\n total: number;\n apps: { appId: string; count: number }[];\n}\n\nexport interface HeatmapResponse {\n days: HeatmapDay[];\n appTotals: { appId: string; appName: string; total: number }[];\n thresholds: [number, number, number, number];\n}\n\nexport interface ActivityItem {\n uri: string;\n cid: string;\n collection: string;\n rkey: string;\n record: Record<string, unknown>;\n appId: string;\n appName: string;\n category: string;\n indexedAt: string;\n /**\n * The record author's handle / display name / avatar, injected by the server\n * on MULTI-author feeds (the following feed) so a card can show whose activity\n * it is. Unset on the per-author activity feed (all one author). Mirrors the\n * stream `ActivityItem`'s author fields the view-model transform reads.\n */\n authorHandle?: string;\n authorDisplayName?: string;\n authorAvatar?: string;\n /**\n * Set by the server when an `app.bsky.embed.record` quote was already\n * resolved upstream (AppView path). Mutually exclusive with `quotedPostUri`.\n */\n quotedPost?: QuotedPostResult;\n /**\n * Set by the server when an `app.bsky.embed.record` quote needs client-side\n * resolution (PDS path). Pass batches to {@link resolveQuotedPosts}.\n * Mutually exclusive with `quotedPost`.\n */\n quotedPostUri?: string;\n /**\n * Reachability of the card's external destination as last checked by\n * sifa-api. Undefined for legacy responses; treat as 'unknown'.\n * See {@link ActivityItemLinkHealth}.\n */\n linkHealth?: ActivityItemLinkHealth;\n /**\n * Content labels from `app.bsky.feed.defs#postView.labels`. Set by\n * sifa-api for Bluesky posts; undefined for other sources and legacy\n * responses. Pass items to {@link hasAdultContent} to decide whether to\n * gate media. See {@link ActivityLabel}.\n */\n labels?: ActivityLabel[];\n /**\n * The owner has hidden this item from their profile. Only ever set on the\n * owner's own view of their feed -- every other surface omits hidden items\n * rather than flagging them -- so a card can offer an unhide in place.\n */\n hidden?: boolean;\n}\n\nexport interface ActivityTeaserResponse {\n items: ActivityItem[];\n blueskyGated?: boolean;\n backfillPending?: boolean;\n failedApps?: string[];\n}\n\nexport interface ActivityFeedResponse {\n items: ActivityItem[];\n cursor: string | null;\n hasMore: boolean;\n availableCategories?: string[];\n blueskyGated?: boolean;\n failedApps?: string[];\n}\n\n/**\n * Per-day activity counts for a profile across all ATproto apps. Returns\n * `null` on any error so callers can render a graceful empty state.\n */\nexport async function fetchHeatmapData(\n config: SifaApiConfig,\n handleOrDid: string,\n days: number,\n options: ApiFetchOptions = {},\n): Promise<HeatmapResponse | null> {\n const path = `/api/activity/${encodeIdentifier(handleOrDid)}/heatmap?days=${days}`;\n try {\n return await apiFetch<HeatmapResponse>(config, path, {\n next: { revalidate: 900, tags: [`heatmap-${handleOrDid}`] },\n ...options,\n });\n } catch {\n return null;\n }\n}\n\nexport interface FetchActivityTeaserOptions extends ApiFetchOptions {\n /**\n * Pass the caller's `Cookie` header on Next.js RSC server-side calls.\n * Required for authenticated server fetches because `credentials: 'include'`\n * does not propagate browser cookies in RSC.\n */\n cookieHeader?: string;\n}\n\n/**\n * Recent activity teaser for a profile (homepage-sized slice). Caps the\n * upstream wait so the SSR path cannot hang. Returns `null` on any error.\n */\nexport async function fetchActivityTeaser(\n config: SifaApiConfig,\n handleOrDid: string,\n options: FetchActivityTeaserOptions = {},\n): Promise<ActivityTeaserResponse | null> {\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n if (options.cookieHeader) headers.cookie = options.cookieHeader;\n\n try {\n return await apiFetch<ActivityTeaserResponse>(\n config,\n `/api/activity/${encodeIdentifier(handleOrDid)}/teaser`,\n {\n credentials: 'include',\n timeoutMs: 8000,\n next: { revalidate: 300, tags: [`activity-teaser-${handleOrDid}`] },\n ...options,\n headers,\n },\n );\n } catch {\n return null;\n }\n}\n\nexport interface FetchActivityFeedOptions extends ApiFetchOptions {\n category?: string;\n /**\n * Show only these app categories. Applies when `category` is unset (the\n * \"all\" view).\n */\n categories?: string[];\n /**\n * Show only long-form posts carrying one of these tags. Other activity is\n * not filtered on tags. See `SiteActivityPreset`.\n */\n tags?: string[];\n /**\n * Apply the profile owner's saved preset for this surface. Explicit\n * `categories` / `tags` override the matching half of the preset.\n */\n preset?: 'site';\n limit?: number;\n cursor?: string;\n cookieHeader?: string;\n}\n\n/**\n * Paginated activity feed for a profile. Always fresh (`cache: 'no-store'`).\n * Returns `null` on any error.\n */\nexport async function fetchActivityFeed(\n config: SifaApiConfig,\n handleOrDid: string,\n options: FetchActivityFeedOptions = {},\n): Promise<ActivityFeedResponse | null> {\n const params = new URLSearchParams();\n if (options.category) params.set('category', options.category);\n if (options.categories?.length) params.set('categories', options.categories.join(','));\n if (options.tags?.length) params.set('tags', options.tags.join(','));\n if (options.preset) params.set('preset', options.preset);\n if (options.limit) params.set('limit', String(options.limit));\n if (options.cursor) params.set('cursor', options.cursor);\n const qs = params.toString();\n\n const headers: Record<string, string> = { ...(options.headers ?? {}) };\n if (options.cookieHeader) headers.cookie = options.cookieHeader;\n\n try {\n return await apiFetch<ActivityFeedResponse>(\n config,\n `/api/activity/${encodeIdentifier(handleOrDid)}${qs ? `?${qs}` : ''}`,\n {\n credentials: 'include',\n cache: 'no-store',\n ...options,\n headers,\n },\n );\n } catch {\n return null;\n }\n}\n"],"mappings":";AACA,SAAS,kBAAkB,gBAA0D;AAuGrF,eAAsB,iBACpB,QACA,aACA,MACA,UAA2B,CAAC,GACK;AACjC,QAAM,OAAO,iBAAiB,iBAAiB,WAAW,CAAC,iBAAiB,IAAI;AAChF,MAAI;AACF,WAAO,MAAM,SAA0B,QAAQ,MAAM;AAAA,MACnD,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC,WAAW,WAAW,EAAE,EAAE;AAAA,MAC1D,GAAG;AAAA,IACL,CAAC;AAAA,EACH,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAeA,eAAsB,oBACpB,QACA,aACA,UAAsC,CAAC,GACC;AACxC,QAAM,UAAkC,EAAE,GAAI,QAAQ,WAAW,CAAC,EAAG;AACrE,MAAI,QAAQ,aAAc,SAAQ,SAAS,QAAQ;AAEnD,MAAI;AACF,WAAO,MAAM;AAAA,MACX;AAAA,MACA,iBAAiB,iBAAiB,WAAW,CAAC;AAAA,MAC9C;AAAA,QACE,aAAa;AAAA,QACb,WAAW;AAAA,QACX,MAAM,EAAE,YAAY,KAAK,MAAM,CAAC,mBAAmB,WAAW,EAAE,EAAE;AAAA,QAClE,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AA4BA,eAAsB,kBACpB,QACA,aACA,UAAoC,CAAC,GACC;AACtC,QAAM,SAAS,IAAI,gBAAgB;AACnC,MAAI,QAAQ,SAAU,QAAO,IAAI,YAAY,QAAQ,QAAQ;AAC7D,MAAI,QAAQ,YAAY,OAAQ,QAAO,IAAI,cAAc,QAAQ,WAAW,KAAK,GAAG,CAAC;AACrF,MAAI,QAAQ,MAAM,OAAQ,QAAO,IAAI,QAAQ,QAAQ,KAAK,KAAK,GAAG,CAAC;AACnE,MAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AACvD,MAAI,QAAQ,MAAO,QAAO,IAAI,SAAS,OAAO,QAAQ,KAAK,CAAC;AAC5D,MAAI,QAAQ,OAAQ,QAAO,IAAI,UAAU,QAAQ,MAAM;AACvD,QAAM,KAAK,OAAO,SAAS;AAE3B,QAAM,UAAkC,EAAE,GAAI,QAAQ,WAAW,CAAC,EAAG;AACrE,MAAI,QAAQ,aAAc,SAAQ,SAAS,QAAQ;AAEnD,MAAI;AACF,WAAO,MAAM;AAAA,MACX;AAAA,MACA,iBAAiB,iBAAiB,WAAW,CAAC,GAAG,KAAK,IAAI,EAAE,KAAK,EAAE;AAAA,MACnE;AAAA,QACE,aAAa;AAAA,QACb,OAAO;AAAA,QACP,GAAG;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACF;","names":[]}
@@ -801,6 +801,39 @@ async function fetchHiddenActivityItems(config, options = {}) {
801
801
  }
802
802
  }
803
803
 
804
+ // src/query/fetchers/activity-preferences.ts
805
+ async function fetchActivityPreferences(config, options = {}) {
806
+ const headers = { ...options.headers ?? {} };
807
+ if (options.cookieHeader) headers.cookie = options.cookieHeader;
808
+ try {
809
+ return await apiFetch(config, "/api/activity-preferences", {
810
+ credentials: "include",
811
+ cache: "no-store",
812
+ ...options,
813
+ headers
814
+ });
815
+ } catch {
816
+ return null;
817
+ }
818
+ }
819
+ function updateActivityPreferences(config, input, options = {}) {
820
+ return apiWrite(config, "/api/activity-preferences", "PUT", {
821
+ ...options,
822
+ body: input
823
+ });
824
+ }
825
+ async function fetchSiteActivityPreset(config, handleOrDid, options = {}) {
826
+ try {
827
+ return await apiFetch(
828
+ config,
829
+ `/api/activity-preferences/site-preset/${encodeIdentifier(handleOrDid)}`,
830
+ options
831
+ );
832
+ } catch {
833
+ return null;
834
+ }
835
+ }
836
+
804
837
  // src/query/fetchers/stats.ts
805
838
  async function fetchStats(config, options = {}) {
806
839
  try {
@@ -1282,6 +1315,9 @@ async function fetchActivityTeaser(config, handleOrDid, options = {}) {
1282
1315
  async function fetchActivityFeed(config, handleOrDid, options = {}) {
1283
1316
  const params = new URLSearchParams();
1284
1317
  if (options.category) params.set("category", options.category);
1318
+ if (options.categories?.length) params.set("categories", options.categories.join(","));
1319
+ if (options.tags?.length) params.set("tags", options.tags.join(","));
1320
+ if (options.preset) params.set("preset", options.preset);
1285
1321
  if (options.limit) params.set("limit", String(options.limit));
1286
1322
  if (options.cursor) params.set("cursor", options.cursor);
1287
1323
  const qs = params.toString();
@@ -1843,6 +1879,45 @@ async function fetchProfileSummary(config, actor, options = {}) {
1843
1879
  const view = await fetchGetProfileView(config, actor, fetchOptions);
1844
1880
  return view ? summarizeProfileView(view, { maxSkills }) : null;
1845
1881
  }
1882
+ var UserSettingsSchema = zod.z.object({
1883
+ respectBskyBlocks: zod.z.boolean(),
1884
+ autoLinkCompanies: zod.z.boolean(),
1885
+ /**
1886
+ * Show per-publication citation counts from OpenAlex on the profile.
1887
+ * Off by default; an API that predates the setting reads as off.
1888
+ */
1889
+ showCitationCounts: zod.z.boolean().default(false)
1890
+ });
1891
+ async function fetchUserSettings(config, options = {}) {
1892
+ const { cookieHeader, ...rest } = options;
1893
+ const headers = { ...rest.headers ?? {} };
1894
+ if (cookieHeader) headers.cookie = cookieHeader;
1895
+ try {
1896
+ const data = await apiFetch(config, "/api/settings", {
1897
+ cache: "no-store",
1898
+ credentials: "include",
1899
+ ...rest,
1900
+ headers
1901
+ });
1902
+ const parsed = UserSettingsSchema.safeParse(data);
1903
+ return parsed.success ? parsed.data : null;
1904
+ } catch {
1905
+ return null;
1906
+ }
1907
+ }
1908
+ async function updateUserSettings(config, patch) {
1909
+ try {
1910
+ const data = await apiFetch(config, "/api/settings", {
1911
+ method: "PATCH",
1912
+ credentials: "include",
1913
+ body: patch
1914
+ });
1915
+ const parsed = UserSettingsSchema.safeParse(data);
1916
+ return parsed.success ? parsed.data : null;
1917
+ } catch {
1918
+ return null;
1919
+ }
1920
+ }
1846
1921
 
1847
1922
  // src/query/keys.ts
1848
1923
  var sifaQueryKeys = {
@@ -1926,7 +2001,10 @@ var sifaQueryKeys = {
1926
2001
  feed: (handleOrDid, opts) => ["sifa", "activity", "feed", handleOrDid, opts],
1927
2002
  // Session-scoped: the AppView reads the caller's own hides from their
1928
2003
  // cookie, so there is only ever one list per client.
1929
- hiddenItems: () => ["sifa", "activity", "hidden-items"]
2004
+ hiddenItems: () => ["sifa", "activity", "hidden-items"],
2005
+ // Session-scoped, like hiddenItems: the caller's own preferences.
2006
+ preferences: () => ["sifa", "activity", "preferences"],
2007
+ sitePreset: (handleOrDid) => ["sifa", "activity", "site-preset", handleOrDid]
1930
2008
  },
1931
2009
  github: {
1932
2010
  all: () => ["sifa", "github"],
@@ -1982,6 +2060,9 @@ var sifaQueryKeys = {
1982
2060
  all: () => ["sifa", "bsky-preferences"],
1983
2061
  contentLabels: () => ["sifa", "bsky-preferences", "content-labels"]
1984
2062
  },
2063
+ userSettings: {
2064
+ all: () => ["sifa", "user-settings"]
2065
+ },
1985
2066
  repoInventory: {
1986
2067
  all: () => ["sifa", "repo-inventory"],
1987
2068
  list: () => ["sifa", "repo-inventory", "list"]
@@ -2143,6 +2224,7 @@ exports.PROFILE_COMPLETENESS_SIGNALS = PROFILE_COMPLETENESS_SIGNALS;
2143
2224
  exports.QUOTED_POSTS_BATCH_MAX = QUOTED_POSTS_BATCH_MAX;
2144
2225
  exports.RpgItemStatusSchema = RpgItemStatusSchema;
2145
2226
  exports.RpgStatusResponseSchema = RpgStatusResponseSchema;
2227
+ exports.UserSettingsSchema = UserSettingsSchema;
2146
2228
  exports.addFeatureAllowlist = addFeatureAllowlist;
2147
2229
  exports.addOrgNotificationEmail = addOrgNotificationEmail;
2148
2230
  exports.apiFetch = apiFetch;
@@ -2184,6 +2266,7 @@ exports.dismissEndorsement = dismissEndorsement;
2184
2266
  exports.dismissUnlinkedPosition = dismissUnlinkedPosition;
2185
2267
  exports.fetchAccounts = fetchAccounts;
2186
2268
  exports.fetchActivityFeed = fetchActivityFeed;
2269
+ exports.fetchActivityPreferences = fetchActivityPreferences;
2187
2270
  exports.fetchActivityTeaser = fetchActivityTeaser;
2188
2271
  exports.fetchAppsRegistry = fetchAppsRegistry;
2189
2272
  exports.fetchAtFundLink = fetchAtFundLink;
@@ -2219,6 +2302,7 @@ exports.fetchSearchCompanies = fetchSearchCompanies;
2219
2302
  exports.fetchSearchFilters = fetchSearchFilters;
2220
2303
  exports.fetchSearchProfiles = fetchSearchProfiles;
2221
2304
  exports.fetchSimilarProfiles = fetchSimilarProfiles;
2305
+ exports.fetchSiteActivityPreset = fetchSiteActivityPreset;
2222
2306
  exports.fetchSkillSuggestions = fetchSkillSuggestions;
2223
2307
  exports.fetchSpeakers = fetchSpeakers;
2224
2308
  exports.fetchStats = fetchStats;
@@ -2226,6 +2310,7 @@ exports.fetchSuggestionCount = fetchSuggestionCount;
2226
2310
  exports.fetchSuggestions = fetchSuggestions;
2227
2311
  exports.fetchTalks = fetchTalks;
2228
2312
  exports.fetchUnlinkedPositions = fetchUnlinkedPositions;
2313
+ exports.fetchUserSettings = fetchUserSettings;
2229
2314
  exports.fetchViewerPositionConfirmations = fetchViewerPositionConfirmations;
2230
2315
  exports.fetchWipePreview = fetchWipePreview;
2231
2316
  exports.followUser = followUser;
@@ -2277,6 +2362,7 @@ exports.unrevealMarqueDomain = unrevealMarqueDomain;
2277
2362
  exports.unsetExternalAccountPrimary = unsetExternalAccountPrimary;
2278
2363
  exports.unsetPositionPrimary = unsetPositionPrimary;
2279
2364
  exports.unsetSectionPrimary = unsetSectionPrimary;
2365
+ exports.updateActivityPreferences = updateActivityPreferences;
2280
2366
  exports.updateEducation = updateEducation;
2281
2367
  exports.updateExternalAccount = updateExternalAccount;
2282
2368
  exports.updateInvestment = updateInvestment;
@@ -2287,6 +2373,7 @@ exports.updateProfileOverride = updateProfileOverride;
2287
2373
  exports.updateProfileSelf = updateProfileSelf;
2288
2374
  exports.updateRecord = updateRecord;
2289
2375
  exports.updateSkill = updateSkill;
2376
+ exports.updateUserSettings = updateUserSettings;
2290
2377
  exports.uploadAvatar = uploadAvatar;
2291
2378
  exports.uploadNamePronunciationAudio = uploadNamePronunciationAudio;
2292
2379
  exports.verifyExternalAccount = verifyExternalAccount;