@singi-labs/sifa-sdk 0.11.9 → 0.11.11

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 CHANGED
@@ -1013,6 +1013,9 @@ var APP_CATEGORY_MAP = {
1013
1013
  // io.kich.recipe.recipe — authored recipes
1014
1014
  margin: "Research",
1015
1015
  // at.margin.{note,annotation} — web-annotation notes with a body
1016
+ // Onboard from moll.dev profile audit.
1017
+ recipe: "Recipes",
1018
+ // exchange.recipe.recipe — authored recipes on recipe.exchange
1016
1019
  // Web-only (rendered in pills/cards via sifa-web atproto-apps.ts;
1017
1020
  // no backend scan collection yet)
1018
1021
  linkat: "Links",
@@ -1963,6 +1966,13 @@ var APP_URL_PATTERNS = Object.freeze({
1963
1966
  urlPattern: "https://kich.io/recipes/{rkey}",
1964
1967
  profileUrlPattern: "https://kich.io"
1965
1968
  },
1969
+ recipe: {
1970
+ // recipe.exchange viewer. Verified: GET /recipes/{rkey} returns 200 (rkey
1971
+ // alone; no did needed) and /profiles/{handle} returns 200 for the author
1972
+ // page. /recipe/{rkey} 404s.
1973
+ urlPattern: "https://recipe.exchange/recipes/{rkey}",
1974
+ profileUrlPattern: "https://recipe.exchange/profiles/{handle}"
1975
+ },
1966
1976
  aetherdocs: {
1967
1977
  // Aether OS is a browser OS — presentations open inside it and there's no
1968
1978
  // public per-record viewer. The card embeds the deck from the record; the
@@ -2033,7 +2043,9 @@ var COLLECTION_TO_APP = [
2033
2043
  ["wiki.lichen.", "lichen"],
2034
2044
  // Onboard from aramzs.xyz profile audit.
2035
2045
  ["os.aether.", "aetherdocs"],
2036
- ["io.kich.recipe.recipe", "kich"]
2046
+ ["io.kich.recipe.recipe", "kich"],
2047
+ // Onboard from moll.dev profile audit.
2048
+ ["exchange.recipe.recipe", "recipe"]
2037
2049
  ];
2038
2050
 
2039
2051
  // src/cards/resolve-card-url.ts
@@ -2045,6 +2057,12 @@ function getAppIdForCollection(collection) {
2045
2057
  if (parts.length >= 2) return `${parts[0]}.${parts[1]}`;
2046
2058
  return collection;
2047
2059
  }
2060
+ function urlHealth(url) {
2061
+ return url ? { url, strategy: "url" } : { url: null, strategy: "none" };
2062
+ }
2063
+ function recordHealth(url) {
2064
+ return url ? { url, strategy: "record" } : { url: null, strategy: "none" };
2065
+ }
2048
2066
  function interpolate(pattern, vars) {
2049
2067
  let result = pattern;
2050
2068
  for (const [key, value] of Object.entries(vars)) {
@@ -2061,18 +2079,18 @@ function shouldUseItemPattern(appId, collection) {
2061
2079
  }
2062
2080
  return true;
2063
2081
  }
2064
- function patternUrl(appId, vars, collection) {
2082
+ function patternHealth(appId, vars, collection) {
2065
2083
  const patterns = APP_URL_PATTERNS[appId];
2066
- if (!patterns) return null;
2084
+ if (!patterns) return { url: null, strategy: "none" };
2067
2085
  if (patterns.urlPattern && shouldUseItemPattern(appId, collection)) {
2068
2086
  const url = interpolate(patterns.urlPattern, vars);
2069
- if (url) return url;
2087
+ if (url) return recordHealth(url);
2070
2088
  }
2071
2089
  if (patterns.profileUrlPattern) {
2072
2090
  const url = interpolate(patterns.profileUrlPattern, vars);
2073
- if (url) return url;
2091
+ if (url) return urlHealth(url);
2074
2092
  }
2075
- return null;
2093
+ return { url: null, strategy: "none" };
2076
2094
  }
2077
2095
  var TANGLED_REPO_SLUG = /^[a-zA-Z0-9._-]+$/;
2078
2096
  function stringOrNull(value) {
@@ -2085,33 +2103,31 @@ function parseAtUri(atUri) {
2085
2103
  if (!match || !match[1] || !match[2]) return null;
2086
2104
  return { did: match[1], rkey: match[2] };
2087
2105
  }
2088
- function resolveCardUrl(item) {
2106
+ function resolveCardHealth(item) {
2089
2107
  const { collection, record, uri, rkey, authorDid, authorHandle } = item;
2090
2108
  const appId = getAppIdForCollection(collection);
2091
2109
  if (collection.startsWith("sh.tangled.")) {
2092
2110
  const repoName = stringOrNull(record.name) ?? (collection === "sh.tangled.repo" ? rkey : null);
2093
2111
  if (repoName && authorHandle && TANGLED_REPO_SLUG.test(repoName)) {
2094
- return `https://tangled.sh/${authorHandle}/${repoName}`;
2112
+ return recordHealth(`https://tangled.sh/${authorHandle}/${repoName}`);
2095
2113
  }
2096
- return patternUrl("tangled", { handle: authorHandle, did: authorDid, rkey }, collection);
2114
+ return patternHealth("tangled", { handle: authorHandle, did: authorDid, rkey }, collection);
2097
2115
  }
2098
2116
  if (collection.startsWith("com.kipclip.") || collection.startsWith("community.lexicon.bookmarks.")) {
2099
2117
  const subject = stringOrNull(record.subject);
2100
- if (subject) return subject;
2101
- return patternUrl("kipclip", { handle: authorHandle, did: authorDid, rkey });
2118
+ if (subject) return urlHealth(subject);
2119
+ return patternHealth("kipclip", { handle: authorHandle, did: authorDid, rkey });
2102
2120
  }
2103
2121
  if (collection === "at.margin.bookmark") {
2104
- const source = stringOrNull(record.source);
2105
- if (source) return source;
2106
- return null;
2122
+ return urlHealth(stringOrNull(record.source));
2107
2123
  }
2108
2124
  if (collection === "at.margin.annotation" || collection === "at.margin.note") {
2109
2125
  const target = record.target;
2110
2126
  if (target != null && typeof target === "object") {
2111
2127
  const source = stringOrNull(target.source);
2112
- if (source) return source;
2128
+ if (source) return urlHealth(source);
2113
2129
  }
2114
- return APP_URL_PATTERNS.margin?.profileUrlPattern ?? null;
2130
+ return urlHealth(APP_URL_PATTERNS.margin?.profileUrlPattern ?? null);
2115
2131
  }
2116
2132
  if (collection === "community.lexicon.calendar.rsvp") {
2117
2133
  const subject = record.subject;
@@ -2120,62 +2136,68 @@ function resolveCardUrl(item) {
2120
2136
  if (subjectUri) {
2121
2137
  const parsed2 = parseAtUri(subjectUri);
2122
2138
  if (parsed2) {
2123
- return `https://smokesignal.events/${parsed2.did}/${parsed2.rkey}`;
2139
+ return urlHealth(`https://smokesignal.events/${parsed2.did}/${parsed2.rkey}`);
2124
2140
  }
2125
2141
  }
2126
2142
  }
2127
- return null;
2143
+ return { url: null, strategy: "none" };
2128
2144
  }
2129
2145
  if (collection === "community.lexicon.calendar.event") {
2130
2146
  const parsed2 = parseAtUri(uri);
2131
2147
  if (parsed2) {
2132
- return `https://smokesignal.events/${parsed2.did}/${parsed2.rkey}`;
2148
+ return recordHealth(`https://smokesignal.events/${parsed2.did}/${parsed2.rkey}`);
2133
2149
  }
2134
- return null;
2150
+ return { url: null, strategy: "none" };
2135
2151
  }
2136
2152
  if (collection === "quest.atmo.event") {
2137
2153
  const parsed2 = parseAtUri(uri);
2138
2154
  if (parsed2) {
2139
- return `https://atmo.rsvp/p/${parsed2.did}/e/${parsed2.rkey}`;
2155
+ return recordHealth(`https://atmo.rsvp/p/${parsed2.did}/e/${parsed2.rkey}`);
2140
2156
  }
2141
- return null;
2157
+ return { url: null, strategy: "none" };
2142
2158
  }
2143
2159
  if (collection === "quest.atmo.checkin") {
2144
2160
  const eventUri = stringOrNull(record.event);
2145
2161
  if (eventUri) {
2146
2162
  const parsed2 = parseAtUri(eventUri);
2147
2163
  if (parsed2) {
2148
- return `https://atmo.rsvp/p/${parsed2.did}/e/${parsed2.rkey}`;
2164
+ return urlHealth(`https://atmo.rsvp/p/${parsed2.did}/e/${parsed2.rkey}`);
2149
2165
  }
2150
2166
  }
2151
- return null;
2167
+ return { url: null, strategy: "none" };
2152
2168
  }
2153
2169
  if (collection === "fyi.atstore.listing.review") {
2154
2170
  const listingMeta = record.listingMeta;
2155
2171
  if (listingMeta != null && typeof listingMeta === "object") {
2156
2172
  const slug = stringOrNull(listingMeta.slug);
2157
- if (slug) return `https://atstore.fyi/products/${encodeURIComponent(slug)}`;
2173
+ if (slug) return urlHealth(`https://atstore.fyi/products/${encodeURIComponent(slug)}`);
2158
2174
  }
2159
- return APP_URL_PATTERNS.atstore?.profileUrlPattern ?? null;
2175
+ return urlHealth(APP_URL_PATTERNS.atstore?.profileUrlPattern ?? null);
2160
2176
  }
2161
2177
  if (collection === "social.crate.content") {
2162
- return stringOrNull(record.canonicalUrl);
2178
+ return urlHealth(stringOrNull(record.canonicalUrl));
2163
2179
  }
2164
2180
  if (collection === "social.crate.note") {
2165
- return null;
2181
+ return { url: null, strategy: "none" };
2166
2182
  }
2167
2183
  if (collection.startsWith("site.standard.")) {
2168
2184
  const siteUrl = stringOrNull(record.siteUrl);
2169
2185
  const path = stringOrNull(record.path);
2170
- if (siteUrl && path) return `${siteUrl}${path}`;
2171
- if (siteUrl) return siteUrl;
2186
+ if (siteUrl && path) return urlHealth(`${siteUrl}${path}`);
2187
+ if (siteUrl) return urlHealth(siteUrl);
2172
2188
  }
2173
2189
  if (collection === "io.kich.recipe.recipe") {
2174
- return patternUrl("kich", { handle: authorHandle, did: authorDid, rkey }, collection);
2190
+ return patternHealth("kich", { handle: authorHandle, did: authorDid, rkey }, collection);
2191
+ }
2192
+ if (collection === "exchange.recipe.recipe") {
2193
+ return patternHealth("recipe", { handle: authorHandle, did: authorDid, rkey }, collection);
2175
2194
  }
2176
2195
  const recordUrl = stringOrNull(record.url);
2177
- if (recordUrl) return recordUrl;
2178
- return patternUrl(appId, { handle: authorHandle, did: authorDid, rkey }, collection);
2196
+ if (recordUrl) return urlHealth(recordUrl);
2197
+ return patternHealth(appId, { handle: authorHandle, did: authorDid, rkey }, collection);
2198
+ }
2199
+ function resolveCardUrl(item) {
2200
+ return resolveCardHealth(item).url;
2179
2201
  }
2180
2202
 
2181
2203
  // src/cards/visibility.ts
@@ -2671,7 +2693,7 @@ var ProfileVolunteeringRecordSchema = zod.z.object({
2671
2693
  });
2672
2694
 
2673
2695
  // src/index.ts
2674
- var SIFA_SDK_VERSION = "0.11.9";
2696
+ var SIFA_SDK_VERSION = "0.11.11";
2675
2697
 
2676
2698
  exports.ACTIVITY_TIERS = ACTIVITY_TIERS;
2677
2699
  exports.ACTIVITY_VISIBILITY_RULES = ACTIVITY_VISIBILITY_RULES;
@@ -2817,6 +2839,7 @@ exports.presentationCsvRowToRecord = presentationCsvRowToRecord;
2817
2839
  exports.presentationDeliveryCsvRowToRecord = presentationDeliveryCsvRowToRecord;
2818
2840
  exports.profileToDimensionInputs = profileToDimensionInputs;
2819
2841
  exports.relativeLuminance = relativeLuminance;
2842
+ exports.resolveCardHealth = resolveCardHealth;
2820
2843
  exports.resolveCardUrl = resolveCardUrl;
2821
2844
  exports.rgbToString = rgbToString;
2822
2845
  exports.sanitizeDisplayText = sanitizeDisplayText;