@singi-labs/sifa-sdk 0.12.8 → 0.12.10

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
@@ -2409,34 +2409,34 @@ function resolveCardHealth(item) {
2409
2409
  if (subject != null && typeof subject === "object") {
2410
2410
  const subjectUri = stringOrNull(subject.uri);
2411
2411
  if (subjectUri) {
2412
- const parsed2 = parseAtUri(subjectUri);
2413
- if (parsed2) {
2414
- return urlHealth(`https://smokesignal.events/${parsed2.did}/${parsed2.rkey}`);
2412
+ const parsed3 = parseAtUri(subjectUri);
2413
+ if (parsed3) {
2414
+ return urlHealth(`https://smokesignal.events/${parsed3.did}/${parsed3.rkey}`);
2415
2415
  }
2416
2416
  }
2417
2417
  }
2418
2418
  return { url: null, strategy: "none" };
2419
2419
  }
2420
2420
  if (collection === "community.lexicon.calendar.event") {
2421
- const parsed2 = parseAtUri(uri);
2422
- if (parsed2) {
2423
- return recordHealth(`https://smokesignal.events/${parsed2.did}/${parsed2.rkey}`);
2421
+ const parsed3 = parseAtUri(uri);
2422
+ if (parsed3) {
2423
+ return recordHealth(`https://smokesignal.events/${parsed3.did}/${parsed3.rkey}`);
2424
2424
  }
2425
2425
  return { url: null, strategy: "none" };
2426
2426
  }
2427
2427
  if (collection === "quest.atmo.event") {
2428
- const parsed2 = parseAtUri(uri);
2429
- if (parsed2) {
2430
- return recordHealth(`https://atmo.rsvp/p/${parsed2.did}/e/${parsed2.rkey}`);
2428
+ const parsed3 = parseAtUri(uri);
2429
+ if (parsed3) {
2430
+ return recordHealth(`https://atmo.rsvp/p/${parsed3.did}/e/${parsed3.rkey}`);
2431
2431
  }
2432
2432
  return { url: null, strategy: "none" };
2433
2433
  }
2434
2434
  if (collection === "quest.atmo.checkin") {
2435
2435
  const eventUri = stringOrNull(record.event);
2436
2436
  if (eventUri) {
2437
- const parsed2 = parseAtUri(eventUri);
2438
- if (parsed2) {
2439
- return urlHealth(`https://atmo.rsvp/p/${parsed2.did}/e/${parsed2.rkey}`);
2437
+ const parsed3 = parseAtUri(eventUri);
2438
+ if (parsed3) {
2439
+ return urlHealth(`https://atmo.rsvp/p/${parsed3.did}/e/${parsed3.rkey}`);
2440
2440
  }
2441
2441
  }
2442
2442
  return { url: null, strategy: "none" };
@@ -2581,9 +2581,9 @@ function getPublisherFromSiteUrl(siteUrl) {
2581
2581
  let hostname;
2582
2582
  let homeUrl;
2583
2583
  try {
2584
- const parsed2 = new URL(siteUrl);
2585
- hostname = parsed2.hostname;
2586
- homeUrl = `${parsed2.protocol}//${parsed2.hostname}`;
2584
+ const parsed3 = new URL(siteUrl);
2585
+ hostname = parsed3.hostname;
2586
+ homeUrl = `${parsed3.protocol}//${parsed3.hostname}`;
2587
2587
  } catch {
2588
2588
  return {
2589
2589
  id: STANDARD_PUBLISHER_ID,
@@ -2602,6 +2602,780 @@ function getPublisherFromSiteUrl(siteUrl) {
2602
2602
  };
2603
2603
  }
2604
2604
 
2605
+ // src/stream/verbs.json
2606
+ var verbs_default = {
2607
+ $schema: "https://sifa.id/schemas/activity-verbs/v1.json",
2608
+ version: "1.0.0",
2609
+ updated: "2026-07-17",
2610
+ defaultVerb: "created",
2611
+ verbs: {
2612
+ "app.bsky.feed.post": "posted",
2613
+ "blue.flashes.feed.post": "posted",
2614
+ "social.psky.feed.post": "posted",
2615
+ "social.popfeed.feed.post": "posted",
2616
+ "social.popfeed.feed.note": "posted",
2617
+ "fyi.unravel.frontpage.post": "posted",
2618
+ "forum.barazo.post": "posted",
2619
+ "app.bsky.feed.repost": "reposted",
2620
+ "com.whtwnd.blog.entry": "published",
2621
+ "site.standard.document": "published",
2622
+ "computer.aetheros.page": "published",
2623
+ "link.pastesphere.snippet": "published",
2624
+ "pub.leaflet.comment": "published",
2625
+ "id.sifa.profile.presentation": "presented",
2626
+ "id.sifa.profile.presentationDelivery": "presented",
2627
+ "id.sifa.endorsement": "endorsed",
2628
+ "sh.tangled.repo": "shipped",
2629
+ "sh.tangled.repo.issue": "shipped",
2630
+ "sh.tangled.repo.pull": "shipped",
2631
+ "app.sidetrail.trail": "shipped",
2632
+ "app.sidetrail.walk": "shipped",
2633
+ "social.colibri.membership": "joined",
2634
+ "id.sifa.project.membership": "joined",
2635
+ "id.sifa.project.member": "joined",
2636
+ "community.lexicon.calendar.rsvp": "joined",
2637
+ "at.youandme.connection": "joined",
2638
+ "id.sifa.graph.connection": "joined"
2639
+ }
2640
+ };
2641
+
2642
+ // src/stream/verbs.ts
2643
+ var STREAM_VERBS = [
2644
+ "posted",
2645
+ "reposted",
2646
+ "published",
2647
+ "presented",
2648
+ "endorsed",
2649
+ "joined",
2650
+ "shipped",
2651
+ "created"
2652
+ ];
2653
+ var streamVerbSchema = zod.z.enum(STREAM_VERBS);
2654
+ var verbMapSchema = zod.z.object({
2655
+ version: zod.z.string(),
2656
+ updated: zod.z.string(),
2657
+ defaultVerb: streamVerbSchema,
2658
+ verbs: zod.z.record(zod.z.string(), streamVerbSchema)
2659
+ });
2660
+ var parsed2 = verbMapSchema.parse(verbs_default);
2661
+ var ACTIVITY_VERBS = Object.freeze(parsed2);
2662
+ function verbForCollection(collection) {
2663
+ if (!collection) return ACTIVITY_VERBS.defaultVerb;
2664
+ return ACTIVITY_VERBS.verbs[collection] ?? ACTIVITY_VERBS.defaultVerb;
2665
+ }
2666
+ function getActivityVerbsVersion() {
2667
+ return { version: ACTIVITY_VERBS.version, updated: ACTIVITY_VERBS.updated };
2668
+ }
2669
+ var rgbChannelSchema = zod.z.number().min(0).max(255);
2670
+ var rgbColorSchema = zod.z.object({
2671
+ r: rgbChannelSchema,
2672
+ g: rgbChannelSchema,
2673
+ b: rgbChannelSchema
2674
+ });
2675
+ var aspectRatioSchema = zod.z.object({
2676
+ width: zod.z.number(),
2677
+ height: zod.z.number()
2678
+ });
2679
+ var streamSourceSchema = zod.z.object({
2680
+ appId: zod.z.string(),
2681
+ label: zod.z.string(),
2682
+ color: zod.z.string()
2683
+ });
2684
+ var streamThemeSchema = zod.z.object({
2685
+ background: rgbColorSchema,
2686
+ foreground: rgbColorSchema,
2687
+ accent: rgbColorSchema
2688
+ });
2689
+ var streamMediaResolvedSchema = zod.z.object({
2690
+ url: zod.z.string(),
2691
+ alt: zod.z.string(),
2692
+ aspectRatio: aspectRatioSchema.optional(),
2693
+ mimeType: zod.z.string().optional()
2694
+ });
2695
+ var streamMediaBlobSchema = zod.z.object({
2696
+ did: zod.z.string(),
2697
+ cid: zod.z.string(),
2698
+ alt: zod.z.string(),
2699
+ aspectRatio: aspectRatioSchema.optional(),
2700
+ mimeType: zod.z.string().optional()
2701
+ });
2702
+ var streamMediaSchema = zod.z.union([
2703
+ streamMediaResolvedSchema,
2704
+ streamMediaBlobSchema
2705
+ ]);
2706
+ var streamExternalLinkSchema = zod.z.object({
2707
+ url: zod.z.string(),
2708
+ title: zod.z.string().optional(),
2709
+ thumb: zod.z.string().optional()
2710
+ });
2711
+ var streamGeoSchema = zod.z.object({
2712
+ latitude: zod.z.number(),
2713
+ longitude: zod.z.number()
2714
+ });
2715
+ var streamAddressSchema = zod.z.object({
2716
+ name: zod.z.string().optional(),
2717
+ street: zod.z.string().optional(),
2718
+ locality: zod.z.string().optional(),
2719
+ region: zod.z.string().optional(),
2720
+ country: zod.z.string().optional(),
2721
+ postalCode: zod.z.string().optional()
2722
+ });
2723
+ var streamCardBodySchema = zod.z.discriminatedUnion("kind", [
2724
+ zod.z.object({ kind: zod.z.literal("text"), text: zod.z.string() }),
2725
+ zod.z.object({ kind: zod.z.literal("media"), text: zod.z.string().optional() }),
2726
+ zod.z.object({ kind: zod.z.literal("link"), text: zod.z.string().optional() }),
2727
+ zod.z.object({
2728
+ kind: zod.z.literal("track"),
2729
+ text: zod.z.string().optional(),
2730
+ trackTitle: zod.z.string().optional(),
2731
+ artist: zod.z.string().optional()
2732
+ }),
2733
+ zod.z.object({ kind: zod.z.literal("generic"), text: zod.z.string().optional() }),
2734
+ zod.z.object({
2735
+ kind: zod.z.literal("github-pr"),
2736
+ repoOwner: zod.z.string(),
2737
+ repoName: zod.z.string(),
2738
+ prNumber: zod.z.number(),
2739
+ title: zod.z.string(),
2740
+ url: zod.z.string().optional(),
2741
+ language: zod.z.string().optional(),
2742
+ additions: zod.z.number(),
2743
+ deletions: zod.z.number(),
2744
+ mergedAt: zod.z.string().optional()
2745
+ }),
2746
+ zod.z.object({
2747
+ kind: zod.z.literal("book"),
2748
+ title: zod.z.string(),
2749
+ authors: zod.z.array(zod.z.string()),
2750
+ stars: zod.z.number().optional(),
2751
+ status: zod.z.string().optional(),
2752
+ review: zod.z.string().optional()
2753
+ }),
2754
+ zod.z.object({
2755
+ kind: zod.z.literal("media-review"),
2756
+ reviewKind: zod.z.enum(["review", "post", "note", "other"]),
2757
+ title: zod.z.string().optional(),
2758
+ mediaType: zod.z.string().optional(),
2759
+ rating: zod.z.number().optional(),
2760
+ mainCredit: zod.z.string().optional(),
2761
+ reviewText: zod.z.string().optional(),
2762
+ isRevisit: zod.z.boolean()
2763
+ }),
2764
+ zod.z.object({
2765
+ kind: zod.z.literal("event-rsvp"),
2766
+ rsvpStatus: zod.z.enum(["going", "interested", "notgoing", "unknown"]),
2767
+ eventName: zod.z.string().optional(),
2768
+ startsAt: zod.z.string().optional(),
2769
+ endsAt: zod.z.string().optional(),
2770
+ mode: zod.z.enum(["inperson", "virtual", "hybrid"]).optional(),
2771
+ locationName: zod.z.string().optional(),
2772
+ locationLocality: zod.z.string().optional(),
2773
+ locationCountry: zod.z.string().optional()
2774
+ }),
2775
+ zod.z.object({
2776
+ kind: zod.z.literal("verification"),
2777
+ platform: zod.z.string(),
2778
+ verified: zod.z.boolean(),
2779
+ subjectLabel: zod.z.string().optional(),
2780
+ handle: zod.z.string().optional(),
2781
+ profileUrl: zod.z.string().optional()
2782
+ }),
2783
+ zod.z.object({
2784
+ kind: zod.z.literal("membership"),
2785
+ communityName: zod.z.string().optional(),
2786
+ description: zod.z.string().optional(),
2787
+ communityUri: zod.z.string().optional()
2788
+ }),
2789
+ zod.z.object({
2790
+ kind: zod.z.literal("location"),
2791
+ venueName: zod.z.string().optional(),
2792
+ shout: zod.z.string().optional(),
2793
+ address: streamAddressSchema.optional(),
2794
+ geo: streamGeoSchema.optional()
2795
+ }),
2796
+ zod.z.object({
2797
+ kind: zod.z.literal("travel"),
2798
+ origin: zod.z.string().optional(),
2799
+ destination: zod.z.string().optional(),
2800
+ transportation: zod.z.string().optional(),
2801
+ carrier: zod.z.string().optional(),
2802
+ carrierCode: zod.z.string().optional(),
2803
+ startDate: zod.z.string().optional(),
2804
+ endDate: zod.z.string().optional()
2805
+ }),
2806
+ zod.z.object({
2807
+ kind: zod.z.literal("standard-site"),
2808
+ title: zod.z.string().optional(),
2809
+ description: zod.z.string().optional(),
2810
+ siteUrl: zod.z.string().optional(),
2811
+ path: zod.z.string().optional(),
2812
+ publisherName: zod.z.string().optional(),
2813
+ icon: zod.z.string().optional(),
2814
+ coverImageUrl: zod.z.string().optional(),
2815
+ readingTime: zod.z.number().optional(),
2816
+ publishedAt: zod.z.string().optional()
2817
+ })
2818
+ ]);
2819
+ var streamCardSubjectSchema = zod.z.discriminatedUnion("kind", [
2820
+ zod.z.object({
2821
+ kind: zod.z.literal("post"),
2822
+ post: zod.z.lazy(() => streamCardVMSchema)
2823
+ }),
2824
+ zod.z.object({
2825
+ kind: zod.z.literal("person"),
2826
+ did: zod.z.string(),
2827
+ handle: zod.z.string().optional(),
2828
+ displayName: zod.z.string().optional(),
2829
+ avatar: zod.z.string().optional()
2830
+ }),
2831
+ zod.z.object({
2832
+ kind: zod.z.literal("record"),
2833
+ uri: zod.z.string(),
2834
+ title: zod.z.string().optional()
2835
+ })
2836
+ ]);
2837
+ var streamCardVMSchema = zod.z.lazy(
2838
+ () => zod.z.object({
2839
+ uri: zod.z.string(),
2840
+ cid: zod.z.string(),
2841
+ verb: streamVerbSchema,
2842
+ source: streamSourceSchema,
2843
+ tier: zod.z.enum(["creation", "action", "filtered"]),
2844
+ timestamp: zod.z.string(),
2845
+ title: zod.z.string(),
2846
+ body: streamCardBodySchema.optional(),
2847
+ media: zod.z.array(streamMediaSchema).optional(),
2848
+ externalLink: streamExternalLinkSchema.optional(),
2849
+ theme: streamThemeSchema.optional(),
2850
+ subject: zod.z.lazy(() => streamCardSubjectSchema).optional()
2851
+ })
2852
+ );
2853
+ function maxGraphemes(max) {
2854
+ return (value) => {
2855
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
2856
+ let count = 0;
2857
+ for (const _ of segmenter.segment(value)) {
2858
+ count++;
2859
+ if (count > max) return false;
2860
+ }
2861
+ return true;
2862
+ };
2863
+ }
2864
+ var didSchema = zod.z.string().regex(/^did:[a-z]+:[a-zA-Z0-9._:%-]+$/, "Invalid DID");
2865
+ var datetimeSchema = zod.z.string().datetime({ offset: true });
2866
+ var partialDateSchema = zod.z.string().regex(/^\d{4}(-\d{2}(-\d{2}(T.+)?)?)?$/, "Expected YYYY, YYYY-MM, YYYY-MM-DD, or a datetime");
2867
+ var atUriSchema = zod.z.string().regex(/^at:\/\/[^\s]+$/, "Invalid AT-URI");
2868
+ var cidSchema = zod.z.string().regex(/^(Qm[1-9A-HJ-NP-Za-km-z]{44}|b[A-Za-z0-9+/=]+)$/, "Invalid CID");
2869
+ var languageTagSchema = zod.z.string().regex(/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/, "Invalid BCP 47 language tag");
2870
+ var uriSchema = zod.z.string().url();
2871
+ var strongRefSchema = zod.z.object({
2872
+ uri: atUriSchema,
2873
+ cid: cidSchema
2874
+ });
2875
+ var externalRecordRefSchema = zod.z.object({
2876
+ uri: atUriSchema,
2877
+ cid: cidSchema.optional()
2878
+ });
2879
+ var selfLabelsSchema = zod.z.object({
2880
+ $type: zod.z.literal("com.atproto.label.defs#selfLabels").optional(),
2881
+ values: zod.z.array(zod.z.object({ val: zod.z.string() }))
2882
+ });
2883
+
2884
+ // src/publishing/registry.ts
2885
+ var STANDARD_SITE_PUBLISHERS = Object.freeze([
2886
+ { host: "leaflet.pub", name: "Leaflet", iconKey: "leaflet" },
2887
+ { host: "pckt.blog", name: "pckt", iconKey: "pckt" },
2888
+ { host: "offprint.app", name: "Offprint", iconKey: "offprint" }
2889
+ ]);
2890
+ function hostMatches(host, target) {
2891
+ return host === target || host.endsWith("." + target);
2892
+ }
2893
+ function hostFromUri(uri) {
2894
+ if (!uri) return null;
2895
+ try {
2896
+ return new URL(uri).host.toLowerCase();
2897
+ } catch {
2898
+ return null;
2899
+ }
2900
+ }
2901
+ function matchPublisherByHost(host) {
2902
+ if (!host) return null;
2903
+ const lower = host.toLowerCase();
2904
+ return STANDARD_SITE_PUBLISHERS.find((p) => hostMatches(lower, p.host)) ?? null;
2905
+ }
2906
+ function matchPublisherByUri(uri) {
2907
+ return matchPublisherByHost(hostFromUri(uri));
2908
+ }
2909
+
2910
+ // src/stream/to-stream-card-vm.ts
2911
+ var DEFAULT_SOURCE_COLOR = "slate";
2912
+ function asRecord(value) {
2913
+ return value !== null && typeof value === "object" ? value : null;
2914
+ }
2915
+ function asNonEmptyString(value) {
2916
+ return typeof value === "string" && value.length > 0 ? value : void 0;
2917
+ }
2918
+ function didFromUri(uri) {
2919
+ const match = uri.match(/^at:\/\/(did:[^/]+)\//);
2920
+ return match?.[1];
2921
+ }
2922
+ function blobCid(blob) {
2923
+ const rec = asRecord(blob);
2924
+ if (!rec) return void 0;
2925
+ const ref = rec.ref;
2926
+ if (typeof ref === "string") return ref;
2927
+ const link2 = asRecord(ref)?.["$link"];
2928
+ return typeof link2 === "string" ? link2 : void 0;
2929
+ }
2930
+ function aspectRatio(value) {
2931
+ const rec = asRecord(value);
2932
+ const width = rec?.width;
2933
+ const height = rec?.height;
2934
+ if (typeof width === "number" && typeof height === "number") return { width, height };
2935
+ return void 0;
2936
+ }
2937
+ function asFiniteNumber(value) {
2938
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2939
+ }
2940
+ function looseBlobCid(value) {
2941
+ const viaRef = blobCid(value);
2942
+ if (viaRef) return viaRef;
2943
+ const cid = asRecord(value)?.cid;
2944
+ return typeof cid === "string" && cid.length > 0 ? cid : void 0;
2945
+ }
2946
+ function blobMedia(value, did, alt) {
2947
+ const cid = looseBlobCid(value);
2948
+ if (!cid) return void 0;
2949
+ const media = { did, cid, alt };
2950
+ const ratio = aspectRatio(asRecord(value)?.aspectRatio);
2951
+ if (ratio) media.aspectRatio = ratio;
2952
+ const mimeType = asNonEmptyString(asRecord(value)?.mimeType);
2953
+ if (mimeType) media.mimeType = mimeType;
2954
+ return media;
2955
+ }
2956
+ function hashSuffix(value) {
2957
+ const i = value.lastIndexOf("#");
2958
+ return i >= 0 ? value.slice(i) : "";
2959
+ }
2960
+ var WORDS_PER_MINUTE = 200;
2961
+ function estimateReadingTime(text) {
2962
+ if (!text) return void 0;
2963
+ const words = text.trim().split(/\s+/).filter(Boolean).length;
2964
+ if (words === 0) return void 0;
2965
+ return Math.max(1, Math.round(words / WORDS_PER_MINUTE));
2966
+ }
2967
+ function beaconGeo(value) {
2968
+ const rec = asRecord(value);
2969
+ if (!rec) return void 0;
2970
+ const { latitude, longitude } = rec;
2971
+ const okType = (v) => typeof v === "string" || typeof v === "number";
2972
+ if (!okType(latitude) || !okType(longitude)) return void 0;
2973
+ const lat = Number(latitude);
2974
+ const lng = Number(longitude);
2975
+ if (!Number.isFinite(lat) || !Number.isFinite(lng)) return void 0;
2976
+ return { latitude: lat, longitude: lng };
2977
+ }
2978
+ function beaconAddress(value) {
2979
+ const rec = asRecord(value);
2980
+ if (!rec) return void 0;
2981
+ const address = {};
2982
+ const name = asNonEmptyString(rec.name);
2983
+ if (name) address.name = name;
2984
+ const street = asNonEmptyString(rec.street);
2985
+ if (street) address.street = street;
2986
+ const locality = asNonEmptyString(rec.locality);
2987
+ if (locality) address.locality = locality;
2988
+ const region = asNonEmptyString(rec.region);
2989
+ if (region) address.region = region;
2990
+ const country = asNonEmptyString(rec.country);
2991
+ if (country) address.country = country;
2992
+ const postalCode = asNonEmptyString(rec.postalCode);
2993
+ if (postalCode) address.postalCode = postalCode;
2994
+ return Object.keys(address).length > 0 ? address : void 0;
2995
+ }
2996
+ function parseAuthors(value) {
2997
+ if (typeof value !== "string") return [];
2998
+ return value.split(" ").map((a) => a.trim()).filter((a) => a.length > 0);
2999
+ }
3000
+ function buildSource(item, options) {
3001
+ return {
3002
+ appId: item.appId,
3003
+ label: item.appName,
3004
+ color: options.resolveSourceColor?.(item.appId) ?? DEFAULT_SOURCE_COLOR
3005
+ };
3006
+ }
3007
+ var TITLE_BY_VERB = {
3008
+ posted: (label) => `Posted on ${label}`,
3009
+ reposted: () => "Reposted a post",
3010
+ published: (label) => `Published on ${label}`,
3011
+ presented: () => "Gave a presentation",
3012
+ endorsed: () => "Wrote an endorsement",
3013
+ joined: (label) => `Joined ${label}`,
3014
+ shipped: (label) => `Shipped on ${label}`,
3015
+ created: (label) => `Created a record on ${label}`
3016
+ };
3017
+ function buildTitle(verb, label) {
3018
+ return TITLE_BY_VERB[verb](label);
3019
+ }
3020
+ function readTheme(record) {
3021
+ const theme = asRecord(record?.publicationTheme ?? record?.theme);
3022
+ if (!theme) return void 0;
3023
+ const { background, foreground, accent } = theme;
3024
+ if (isValidRgbColor(background) && isValidRgbColor(foreground) && isValidRgbColor(accent)) {
3025
+ return { background, foreground, accent };
3026
+ }
3027
+ return void 0;
3028
+ }
3029
+ function bskyImages(embed, did) {
3030
+ const images = embed.images;
3031
+ if (!Array.isArray(images)) return [];
3032
+ const out = [];
3033
+ for (const raw of images) {
3034
+ const img = asRecord(raw);
3035
+ if (!img) continue;
3036
+ const cid = blobCid(img.image);
3037
+ if (!cid) continue;
3038
+ const media = { did, cid, alt: asNonEmptyString(img.alt) ?? "" };
3039
+ const ratio = aspectRatio(img.aspectRatio);
3040
+ if (ratio) media.aspectRatio = ratio;
3041
+ const mimeType = asNonEmptyString(asRecord(img.image)?.mimeType);
3042
+ if (mimeType) media.mimeType = mimeType;
3043
+ out.push(media);
3044
+ }
3045
+ return out;
3046
+ }
3047
+ function bskyExternal(embed) {
3048
+ const external = asRecord(embed.external);
3049
+ if (!external) return void 0;
3050
+ const url = asNonEmptyString(external.uri);
3051
+ if (!url) return void 0;
3052
+ const link2 = { url };
3053
+ const title = asNonEmptyString(external.title);
3054
+ if (title) link2.title = title;
3055
+ return link2;
3056
+ }
3057
+ function bskyEmbedContent(record, did) {
3058
+ const embed = asRecord(record.embed);
3059
+ if (!embed) return {};
3060
+ const type = asNonEmptyString(embed["$type"]);
3061
+ const target = type === "app.bsky.embed.recordWithMedia" ? asRecord(embed.media) : embed;
3062
+ if (!target) return {};
3063
+ if (Array.isArray(target.images)) {
3064
+ const media = bskyImages(target, did);
3065
+ return media.length > 0 ? { media } : {};
3066
+ }
3067
+ if (target.external != null) {
3068
+ const externalLink = bskyExternal(target);
3069
+ return externalLink ? { externalLink } : {};
3070
+ }
3071
+ return {};
3072
+ }
3073
+ function applyBskyPost(vm, item, record) {
3074
+ if (!record) {
3075
+ vm.body = { kind: "generic" };
3076
+ return vm;
3077
+ }
3078
+ const did = didFromUri(item.uri);
3079
+ const text = asNonEmptyString(record.text);
3080
+ const { media, externalLink } = did ? bskyEmbedContent(record, did) : {};
3081
+ if (media) vm.media = media;
3082
+ if (externalLink) vm.externalLink = externalLink;
3083
+ let body;
3084
+ if (text) body = { kind: "text", text };
3085
+ else if (vm.media) body = { kind: "media" };
3086
+ else if (vm.externalLink) body = { kind: "link" };
3087
+ else body = { kind: "generic" };
3088
+ vm.body = body;
3089
+ return vm;
3090
+ }
3091
+ function withGeneric(vm) {
3092
+ vm.body = { kind: "generic" };
3093
+ return vm;
3094
+ }
3095
+ function applyGithubPr(vm, record) {
3096
+ const body = {
3097
+ kind: "github-pr",
3098
+ repoOwner: asNonEmptyString(record.repoOwner) ?? "",
3099
+ repoName: asNonEmptyString(record.repoName) ?? "",
3100
+ prNumber: asFiniteNumber(record.prNumber) ?? 0,
3101
+ title: asNonEmptyString(record.title) ?? "",
3102
+ additions: asFiniteNumber(record.additions) ?? 0,
3103
+ deletions: asFiniteNumber(record.deletions) ?? 0
3104
+ };
3105
+ const url = asNonEmptyString(record.url);
3106
+ if (url) body.url = url;
3107
+ const language = asNonEmptyString(record.language);
3108
+ if (language) body.language = language;
3109
+ const mergedAt = asNonEmptyString(record.mergedAt);
3110
+ if (mergedAt) body.mergedAt = mergedAt;
3111
+ vm.body = body;
3112
+ return vm;
3113
+ }
3114
+ function applyBook(vm, item, record) {
3115
+ const title = asNonEmptyString(record.title) ?? "Untitled book";
3116
+ const body = {
3117
+ kind: "book",
3118
+ title,
3119
+ authors: parseAuthors(record.authors)
3120
+ };
3121
+ const stars = asFiniteNumber(record.stars);
3122
+ if (stars !== void 0 && stars > 0) body.stars = stars;
3123
+ const status = asNonEmptyString(record.status);
3124
+ if (status) body.status = status;
3125
+ const review = asNonEmptyString(
3126
+ typeof record.review === "string" ? record.review.trim() : void 0
3127
+ );
3128
+ if (review) body.review = review;
3129
+ vm.body = body;
3130
+ const did = didFromUri(item.uri);
3131
+ if (did) {
3132
+ const cover = blobMedia(record.cover, did, `The cover of ${title}`);
3133
+ if (cover) vm.media = [cover];
3134
+ }
3135
+ return vm;
3136
+ }
3137
+ function popfeedReviewKind(collection) {
3138
+ if (collection.endsWith(".review")) return "review";
3139
+ if (collection.endsWith(".post")) return "post";
3140
+ if (collection.endsWith(".note")) return "note";
3141
+ return "other";
3142
+ }
3143
+ function applyMediaReview(vm, item, record) {
3144
+ const title = asNonEmptyString(record.title) ?? asNonEmptyString(record.name);
3145
+ const body = {
3146
+ kind: "media-review",
3147
+ reviewKind: popfeedReviewKind(item.collection),
3148
+ isRevisit: record.isRevisit === true
3149
+ };
3150
+ if (title) body.title = title;
3151
+ const mediaType = asNonEmptyString(record.creativeWorkType);
3152
+ if (mediaType) body.mediaType = mediaType;
3153
+ const rating = asFiniteNumber(record.rating);
3154
+ if (rating !== void 0) body.rating = rating;
3155
+ const mainCredit = asNonEmptyString(record.mainCredit);
3156
+ if (mainCredit) body.mainCredit = mainCredit;
3157
+ const reviewText = asNonEmptyString(record.text);
3158
+ if (reviewText) body.reviewText = reviewText;
3159
+ vm.body = body;
3160
+ const posterUrl = asNonEmptyString(record.posterUrl);
3161
+ if (posterUrl) vm.media = [{ url: posterUrl, alt: title ? `${title} poster` : "" }];
3162
+ return vm;
3163
+ }
3164
+ var RSVP_STATUS = {
3165
+ "#going": "going",
3166
+ "#interested": "interested",
3167
+ "#notgoing": "notgoing"
3168
+ };
3169
+ var RSVP_MODE = {
3170
+ "#inperson": "inperson",
3171
+ "#virtual": "virtual",
3172
+ "#hybrid": "hybrid"
3173
+ };
3174
+ function applyEventRsvp(vm, record) {
3175
+ const status = asNonEmptyString(record.status);
3176
+ const body = {
3177
+ kind: "event-rsvp",
3178
+ rsvpStatus: (status ? RSVP_STATUS[hashSuffix(status)] : void 0) ?? "unknown"
3179
+ };
3180
+ const meta = asRecord(record.eventMeta);
3181
+ if (meta) {
3182
+ const eventName = asNonEmptyString(meta.name);
3183
+ if (eventName) body.eventName = eventName;
3184
+ const startsAt = asNonEmptyString(meta.startsAt);
3185
+ if (startsAt) body.startsAt = startsAt;
3186
+ const endsAt = asNonEmptyString(meta.endsAt);
3187
+ if (endsAt) body.endsAt = endsAt;
3188
+ const mode = asNonEmptyString(meta.mode);
3189
+ const modeEnum = mode ? RSVP_MODE[hashSuffix(mode)] : void 0;
3190
+ if (modeEnum) body.mode = modeEnum;
3191
+ const locationName = asNonEmptyString(meta.locationName);
3192
+ if (locationName) body.locationName = locationName;
3193
+ const locationLocality = asNonEmptyString(meta.locationLocality);
3194
+ if (locationLocality) body.locationLocality = locationLocality;
3195
+ const locationCountry = asNonEmptyString(meta.locationCountry);
3196
+ if (locationCountry) body.locationCountry = locationCountry;
3197
+ }
3198
+ vm.body = body;
3199
+ return vm;
3200
+ }
3201
+ function applyKeytraceVerification(vm, record) {
3202
+ const identity = asRecord(record.identity) ?? {};
3203
+ const subject = asNonEmptyString(identity.subject);
3204
+ const displayName = asNonEmptyString(identity.displayName);
3205
+ const body = {
3206
+ kind: "verification",
3207
+ platform: asNonEmptyString(record.type) ?? "unknown",
3208
+ verified: record.status === "verified"
3209
+ };
3210
+ const subjectLabel = displayName ?? subject;
3211
+ if (subjectLabel) body.subjectLabel = subjectLabel;
3212
+ const profileUrl = asNonEmptyString(identity.profileUrl);
3213
+ if (profileUrl) body.profileUrl = profileUrl;
3214
+ vm.body = body;
3215
+ return vm;
3216
+ }
3217
+ function applyBskyVerification(vm, record) {
3218
+ const handle = asNonEmptyString(record.handle);
3219
+ const displayName = asNonEmptyString(record.displayName);
3220
+ const body = {
3221
+ kind: "verification",
3222
+ platform: "bluesky",
3223
+ verified: true
3224
+ };
3225
+ const subjectLabel = displayName ?? handle;
3226
+ if (subjectLabel) body.subjectLabel = subjectLabel;
3227
+ if (handle) body.handle = handle;
3228
+ vm.body = body;
3229
+ return vm;
3230
+ }
3231
+ function applyMembership(vm, record) {
3232
+ const body = { kind: "membership" };
3233
+ const communityUri = asNonEmptyString(record.community);
3234
+ if (communityUri) body.communityUri = communityUri;
3235
+ const meta = asRecord(record.communityMeta);
3236
+ const communityName = meta ? asNonEmptyString(meta.name) : void 0;
3237
+ if (communityName) body.communityName = communityName;
3238
+ const description = meta ? asNonEmptyString(meta.description) : void 0;
3239
+ if (description) body.description = description;
3240
+ vm.body = body;
3241
+ if (meta) {
3242
+ const ownerDid = asNonEmptyString(meta.ownerDid) ?? (communityUri ? didFromUri(communityUri) : void 0);
3243
+ if (ownerDid) {
3244
+ const picture = blobMedia(meta.picture, ownerDid, communityName ?? "");
3245
+ if (picture) vm.media = [picture];
3246
+ }
3247
+ }
3248
+ return vm;
3249
+ }
3250
+ function applyLocation(vm, record) {
3251
+ const body = { kind: "location" };
3252
+ const venueName = asNonEmptyString(record.venueName);
3253
+ if (venueName) body.venueName = venueName;
3254
+ const shout = asNonEmptyString(record.shout);
3255
+ if (shout) body.shout = shout;
3256
+ const address = beaconAddress(record.addressDetails);
3257
+ if (address) body.address = address;
3258
+ const geo = beaconGeo(record.location);
3259
+ if (geo) body.geo = geo;
3260
+ vm.body = body;
3261
+ return vm;
3262
+ }
3263
+ function applyTravel(vm, record) {
3264
+ const body = { kind: "travel" };
3265
+ const origin = asNonEmptyString(record.originTransportCode);
3266
+ if (origin) body.origin = origin;
3267
+ const destination = asNonEmptyString(record.destinationTransportCode);
3268
+ if (destination) body.destination = destination;
3269
+ const transportation = asNonEmptyString(record.transportation);
3270
+ if (transportation) body.transportation = transportation;
3271
+ const carrier = asNonEmptyString(record.carrier);
3272
+ if (carrier) body.carrier = carrier;
3273
+ const carrierCode = asNonEmptyString(record.carrierCode);
3274
+ if (carrierCode) body.carrierCode = carrierCode;
3275
+ const startDate = asNonEmptyString(record.startDate);
3276
+ if (startDate) body.startDate = startDate;
3277
+ const endDate = asNonEmptyString(record.endDate);
3278
+ if (endDate) body.endDate = endDate;
3279
+ vm.body = body;
3280
+ return vm;
3281
+ }
3282
+ function applyStandardSite(vm, record) {
3283
+ const body = { kind: "standard-site" };
3284
+ const title = asNonEmptyString(record.title);
3285
+ if (title) body.title = title;
3286
+ const description = asNonEmptyString(record.description);
3287
+ if (description) body.description = description;
3288
+ const siteUrl = asNonEmptyString(record.siteUrl);
3289
+ if (siteUrl) {
3290
+ body.siteUrl = siteUrl;
3291
+ const publisherName = matchPublisherByUri(siteUrl)?.name;
3292
+ if (publisherName) body.publisherName = publisherName;
3293
+ }
3294
+ const path = asNonEmptyString(record.path);
3295
+ if (path) body.path = path;
3296
+ const icon = asNonEmptyString(record.publicationIcon);
3297
+ if (icon) body.icon = icon;
3298
+ const coverImageUrl = asNonEmptyString(record.coverImageUrl);
3299
+ if (coverImageUrl) body.coverImageUrl = coverImageUrl;
3300
+ const readingTime = estimateReadingTime(asNonEmptyString(record.textContent));
3301
+ if (readingTime !== void 0) body.readingTime = readingTime;
3302
+ const publishedAt = asNonEmptyString(record.publishedAt);
3303
+ if (publishedAt) body.publishedAt = publishedAt;
3304
+ vm.body = body;
3305
+ return vm;
3306
+ }
3307
+ function applyYouAndMe(vm, record) {
3308
+ const did = asNonEmptyString(record.subject);
3309
+ if (did) vm.subject = { kind: "person", did };
3310
+ vm.body = { kind: "generic" };
3311
+ return vm;
3312
+ }
3313
+ function applyAsqAnswer(vm, record) {
3314
+ const uri = asNonEmptyString(asRecord(record.subject)?.uri);
3315
+ if (uri) vm.subject = { kind: "record", uri };
3316
+ vm.body = { kind: "generic" };
3317
+ return vm;
3318
+ }
3319
+ function toStreamCardVM(item, options = {}) {
3320
+ const verb = verbForCollection(item.collection);
3321
+ const source = buildSource(item, options);
3322
+ const record = asRecord(item.record);
3323
+ const timestamp = (record ? asNonEmptyString(record.createdAt) : void 0) ?? item.indexedAt;
3324
+ const vm = {
3325
+ uri: item.uri,
3326
+ cid: item.cid,
3327
+ verb,
3328
+ source,
3329
+ tier: getActivityTier(item.collection),
3330
+ timestamp,
3331
+ title: buildTitle(verb, source.label)
3332
+ };
3333
+ const theme = readTheme(record);
3334
+ if (theme) vm.theme = theme;
3335
+ if (item.subject) {
3336
+ vm.subject = { kind: "post", post: toStreamCardVM(item.subject, options) };
3337
+ }
3338
+ switch (item.collection) {
3339
+ case "app.bsky.feed.post":
3340
+ return applyBskyPost(vm, item, record);
3341
+ case "github.pull_request":
3342
+ return record ? applyGithubPr(vm, record) : withGeneric(vm);
3343
+ case "buzz.bookhive.book":
3344
+ return record ? applyBook(vm, item, record) : withGeneric(vm);
3345
+ case "community.lexicon.calendar.rsvp":
3346
+ return record ? applyEventRsvp(vm, record) : withGeneric(vm);
3347
+ case "dev.keytrace.claim":
3348
+ return record ? applyKeytraceVerification(vm, record) : withGeneric(vm);
3349
+ case "app.bsky.graph.verification":
3350
+ return record ? applyBskyVerification(vm, record) : withGeneric(vm);
3351
+ case "social.colibri.membership":
3352
+ return record ? applyMembership(vm, record) : withGeneric(vm);
3353
+ case "app.beaconbits.beacon":
3354
+ return record ? applyLocation(vm, record) : withGeneric(vm);
3355
+ case "social.passports.travel.leg":
3356
+ return record ? applyTravel(vm, record) : withGeneric(vm);
3357
+ case "site.standard.document":
3358
+ return record ? applyStandardSite(vm, record) : withGeneric(vm);
3359
+ case "at.youandme.connection":
3360
+ return record ? applyYouAndMe(vm, record) : withGeneric(vm);
3361
+ case "fyi.asq.answer":
3362
+ return record ? applyAsqAnswer(vm, record) : withGeneric(vm);
3363
+ }
3364
+ if (record && item.collection.startsWith("social.popfeed.feed.")) {
3365
+ return applyMediaReview(vm, item, record);
3366
+ }
3367
+ return withGeneric(vm);
3368
+ }
3369
+ function toStreamCardVMs(items, options = {}) {
3370
+ const out = [];
3371
+ for (const item of items) {
3372
+ if (isVisibleActivityItem(item.collection, item.record)) {
3373
+ out.push(toStreamCardVM(item, options));
3374
+ }
3375
+ }
3376
+ return out;
3377
+ }
3378
+
2605
3379
  // src/logic/profile-completeness.ts
2606
3380
  var COMPLETENESS_MAX_SCORE = 6;
2607
3381
  function completenessScore(c) {
@@ -3006,38 +3780,6 @@ function groupInvolvementByHeading(items) {
3006
3780
  }
3007
3781
  return groups;
3008
3782
  }
3009
- function maxGraphemes(max) {
3010
- return (value) => {
3011
- const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
3012
- let count = 0;
3013
- for (const _ of segmenter.segment(value)) {
3014
- count++;
3015
- if (count > max) return false;
3016
- }
3017
- return true;
3018
- };
3019
- }
3020
- var didSchema = zod.z.string().regex(/^did:[a-z]+:[a-zA-Z0-9._:%-]+$/, "Invalid DID");
3021
- var datetimeSchema = zod.z.string().datetime({ offset: true });
3022
- var partialDateSchema = zod.z.string().regex(/^\d{4}(-\d{2}(-\d{2}(T.+)?)?)?$/, "Expected YYYY, YYYY-MM, YYYY-MM-DD, or a datetime");
3023
- var atUriSchema = zod.z.string().regex(/^at:\/\/[^\s]+$/, "Invalid AT-URI");
3024
- var cidSchema = zod.z.string().regex(/^(Qm[1-9A-HJ-NP-Za-km-z]{44}|b[A-Za-z0-9+/=]+)$/, "Invalid CID");
3025
- var languageTagSchema = zod.z.string().regex(/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/, "Invalid BCP 47 language tag");
3026
- var uriSchema = zod.z.string().url();
3027
- var strongRefSchema = zod.z.object({
3028
- uri: atUriSchema,
3029
- cid: cidSchema
3030
- });
3031
- var externalRecordRefSchema = zod.z.object({
3032
- uri: atUriSchema,
3033
- cid: cidSchema.optional()
3034
- });
3035
- var selfLabelsSchema = zod.z.object({
3036
- $type: zod.z.literal("com.atproto.label.defs#selfLabels").optional(),
3037
- values: zod.z.array(zod.z.object({ val: zod.z.string() }))
3038
- });
3039
-
3040
- // src/schemas/endorsement-confirmation.ts
3041
3783
  var EndorsementConfirmationRecordSchema = zod.z.object({
3042
3784
  endorsement: strongRefSchema,
3043
3785
  createdAt: datetimeSchema
@@ -3217,8 +3959,8 @@ function encodeFeedCursor(cursor) {
3217
3959
  }
3218
3960
  function decodeFeedCursor(encoded) {
3219
3961
  const json = fromBase64Url(encoded);
3220
- const parsed2 = JSON.parse(json);
3221
- return FeedCursorSchema.parse(parsed2);
3962
+ const parsed3 = JSON.parse(json);
3963
+ return FeedCursorSchema.parse(parsed3);
3222
3964
  }
3223
3965
  var ProfileCertificationRecordSchema = zod.z.object({
3224
3966
  name: zod.z.string().min(1).refine(maxGraphemes(100)).max(1e3),
@@ -3432,9 +4174,10 @@ var ProfileVolunteeringRecordSchema = zod.z.object({
3432
4174
  });
3433
4175
 
3434
4176
  // src/index.ts
3435
- var SIFA_SDK_VERSION = "0.12.8";
4177
+ var SIFA_SDK_VERSION = "0.12.10";
3436
4178
 
3437
4179
  exports.ACTIVITY_TIERS = ACTIVITY_TIERS;
4180
+ exports.ACTIVITY_VERBS = ACTIVITY_VERBS;
3438
4181
  exports.ACTIVITY_VISIBILITY_RULES = ACTIVITY_VISIBILITY_RULES;
3439
4182
  exports.ADULT_CONTENT_LABELS = ADULT_CONTENT_LABELS;
3440
4183
  exports.ALL_SECTIONS = ALL_SECTIONS;
@@ -3521,6 +4264,7 @@ exports.SECTION_LABELS = SECTION_LABELS;
3521
4264
  exports.SIFA_SDK_VERSION = SIFA_SDK_VERSION;
3522
4265
  exports.SKILL_CATEGORIES = SKILL_CATEGORIES;
3523
4266
  exports.STANDARD_PUBLISHER_ID = STANDARD_PUBLISHER_ID;
4267
+ exports.STREAM_VERBS = STREAM_VERBS;
3524
4268
  exports.SifaFeedItemSchema = SifaFeedItemSchema;
3525
4269
  exports.VERIFICATION_PROVIDERS = VERIFICATION_PROVIDERS;
3526
4270
  exports.WORKPLACE_TYPE_LABELS = WORKPLACE_TYPE_LABELS;
@@ -3558,6 +4302,7 @@ exports.formatRelativeTime = formatRelativeTime;
3558
4302
  exports.formatTimelineDate = formatTimelineDate;
3559
4303
  exports.getActivityTaxonomyVersion = getActivityTaxonomyVersion;
3560
4304
  exports.getActivityTier = getActivityTier;
4305
+ exports.getActivityVerbsVersion = getActivityVerbsVersion;
3561
4306
  exports.getAppCategoryIcon = getAppCategoryIcon;
3562
4307
  exports.getAppIdForCollection = getAppIdForCollection;
3563
4308
  exports.getArtifactLinkKindLabel = getArtifactLinkKindLabel;
@@ -3648,11 +4393,24 @@ exports.sortLanguagesByProficiency = sortLanguagesByProficiency;
3648
4393
  exports.sortPositions = sortPositions;
3649
4394
  exports.sortProjects = sortProjects;
3650
4395
  exports.sortPublications = sortPublications;
4396
+ exports.streamAddressSchema = streamAddressSchema;
4397
+ exports.streamCardBodySchema = streamCardBodySchema;
4398
+ exports.streamCardSubjectSchema = streamCardSubjectSchema;
4399
+ exports.streamCardVMSchema = streamCardVMSchema;
4400
+ exports.streamExternalLinkSchema = streamExternalLinkSchema;
4401
+ exports.streamGeoSchema = streamGeoSchema;
4402
+ exports.streamMediaSchema = streamMediaSchema;
4403
+ exports.streamSourceSchema = streamSourceSchema;
4404
+ exports.streamThemeSchema = streamThemeSchema;
4405
+ exports.streamVerbSchema = streamVerbSchema;
3651
4406
  exports.stripHtmlToText = stripHtmlToText;
3652
4407
  exports.strongRefSchema = strongRefSchema;
3653
4408
  exports.summarizePresentationDeliveries = summarizePresentationDeliveries;
4409
+ exports.toStreamCardVM = toStreamCardVM;
4410
+ exports.toStreamCardVMs = toStreamCardVMs;
3654
4411
  exports.truncateGraphemes = truncateGraphemes;
3655
4412
  exports.uriSchema = uriSchema;
4413
+ exports.verbForCollection = verbForCollection;
3656
4414
  exports.visibleItems = visibleItems;
3657
4415
  //# sourceMappingURL=index.cjs.map
3658
4416
  //# sourceMappingURL=index.cjs.map