@singi-labs/sifa-sdk 0.12.85 → 0.12.86

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
@@ -5827,7 +5827,7 @@ function describeSifaRecord(collection, value) {
5827
5827
  }
5828
5828
 
5829
5829
  // src/index.ts
5830
- var SIFA_SDK_VERSION = "0.12.85";
5830
+ var SIFA_SDK_VERSION = "0.12.86";
5831
5831
 
5832
5832
  exports.ACTIVITY_TIERS = ACTIVITY_TIERS;
5833
5833
  exports.ACTIVITY_VERBS = ACTIVITY_VERBS;
package/dist/index.js CHANGED
@@ -5825,7 +5825,7 @@ function describeSifaRecord(collection, value) {
5825
5825
  }
5826
5826
 
5827
5827
  // src/index.ts
5828
- var SIFA_SDK_VERSION = "0.12.85";
5828
+ var SIFA_SDK_VERSION = "0.12.86";
5829
5829
 
5830
5830
  export { ACTIVITY_TIERS, ACTIVITY_VERBS, ACTIVITY_VISIBILITY_RULES, ADULT_CONTENT_LABELS, ALL_SECTIONS, APP_CATEGORIES, APP_CATEGORY_IDS, APP_CATEGORY_MAP, APP_URL_PATTERNS, ARTIFACT_LINK_KIND_LABELS, ARTIFACT_LINK_KIND_OPTIONS, ArtifactLinkSchema, AtmosphereFeedItemSchema, BlobRefSchema, CALENDAR_EVENT_MODE_LABELS, CALENDAR_EVENT_STATUS_LABELS, CATEGORY_LABELS, CATEGORY_ORDER, COLLECTION_TO_APP, COMPANY_OPTIONAL_EMPLOYMENT_TYPES, COMPANY_PAGE_MIN_FIRMOGRAPHIC_FIELDS, COMPLETENESS_MAX_SCORE, CONTINENTS, COUNTRIES, DIMENSIONS_MAX_SCORE, EMPLOYMENT_TYPE_GROUPS, EMPLOYMENT_TYPE_LABELS, ENTITY_REF_ANCHORS, EndorsementConfirmationRecordSchema, EndorsementRecordSchema, EntityImportSearchResponseSchema, EntityMintDomainResponseSchema, EntityResolveDomainRequestSchema, EntityResolveDomainResponseSchema, EntitySearchResponseSchema, EntitySearchResultSchema, EntitySelectRequestSchema, EntitySelectResponseSchema, FEATURE_FLAGS, FeatureAllowlistEntrySchema, FeedActorSchema, FollowFeedItemSchema, FollowFeedPageSchema, FollowProfilePageSchema, FollowProfileSchema, GraphFollowRecordSchema, INDUSTRY_OPTIONS, INVESTMENT_ROLE_LABELS, INVESTMENT_ROLE_OPTIONS, INVESTMENT_STAGE_LABELS, INVESTMENT_STAGE_OPTIONS, INVESTMENT_STATUS_LABELS, INVESTMENT_STATUS_OPTIONS, INVOLVEMENT_HEADING_ORDER, INVOLVEMENT_KIND_HEADINGS, INVOLVEMENT_KIND_LABELS, INVOLVEMENT_KIND_OPTIONS, InvestmentAmountSchema, MIN_SKILLS, ON_BEHALF_OF_EMPLOYMENT_TYPES, OPEN_TO_OPTIONS, OPEN_TO_TOKENS, OPEN_TO_TOKEN_TO_VALUE, OPEN_TO_VALUE_TO_TOKEN, OrgEmploymentAttestationRecordSchema, OrgProfileRecordSchema, PLATFORM_LABELS, PLATFORM_OPTIONS, PRESENTATION_LINK_TYPE_LABELS, PRESENTATION_LINK_TYPE_OPTIONS, PRESENTATION_ROLE_LABELS, PRESENTATION_ROLE_OPTIONS, PROFILE_INVESTMENT_NSID, PROFILE_INVOLVEMENT_NSID, PROJECT_ROLES, PUBLISHERS, PresentationDurationSchema, PresentationLinkSchema, ProfileCertificationRecordSchema, ProfileCourseRecordSchema, ProfileEducationRecordSchema, ProfileExternalAccountRecordSchema, ProfileHonorRecordSchema, ProfileInvestmentRecordSchema, ProfileInvolvementRecordSchema, ProfileLanguageRecordSchema, ProfilePositionRecordSchema, ProfilePresentationDeliveryRecordSchema, ProfilePresentationRecordSchema, ProfileProjectRecordSchema, ProfilePublicationRecordSchema, ProfileSelfRecordSchema, ProfileSkillRecordSchema, ProfileVolunteeringRecordSchema, PublicationAuthorSchema, ROADMAP_ISSUES_REPO, ROADMAP_ITEM_META, SECTION_GROUPS, SECTION_LABELS, SIFA_REPO_GROUPS, SIFA_SDK_VERSION, SKILL_CATEGORIES, STANDARD_PUBLISHER_ID, STREAM_VERBS, SifaFeedItemSchema, VERIFICATION_PROVIDERS, WORKPLACE_TYPE_LABELS, WORKPLACE_TYPE_OPTIONS, atUriSchema, buildMetaDescription, buildTalkSlug, categoryForApp, certDateExtractor, cidSchema, classifyEntityRef, completenessPercent, completenessScore, contrastRatio, countFilledDimensions, countryCodeToFlag, dateRangeExtractor, datetimeSchema, decodeFeedCursor, dedupeSkills, describeSifaRecord, detectPdsProvider, didSchema, dimensionsFromInputs, durationFromMinutes, encodeFeedCursor, entityDisambiguationLabel, entityResultKey, externalRecordRefSchema, filterHidden, findIndustry, formatCompanyName, formatDateRange, formatDistanceToNow, formatLocation, formatPresentationDuration, formatRelativeTime, formatTimelineDate, getActivityTaxonomyVersion, getActivityTier, getActivityVerbsVersion, getAppCategoryIcon, getAppIdForCollection, getArtifactLinkKindLabel, getCalendarEventModeLabel, getCalendarEventStatusLabel, getContinent, getDisplayLabel, getEmploymentTypeLabel, getFaviconUrl, getFilledDimensionsMap, getHandleStem, getIndustryLabelKey, getInvestmentRoleLabel, getInvestmentStageLabel, getInvestmentStatusLabel, getInvolvementKindHeading, getInvolvementKindLabel, getLexiconEntry, getOpenToLabelKey, getPdsDisplayName, getPlatformLabel, getPresentationLinkTypeLabel, getPresentationRoleLabel, getPublisherByHost, getPublisherById, getPublisherFromSiteUrl, getTierMeta, getVerificationProvider, getVisibleSectionIds, getWorkplaceTypeLabel, groupInvolvementByHeading, groupSkillsByCategory, groupSkillsBySubCategory, groupSkillsForDisplay, hasAdultContent, hasPersonalProfileContent, hoistPrimary, isAppCategory, isCompanyPageIndexable, isCompanyRequired, isKnownAppId, isKnownPlatform, isKnownVerificationProvider, isLinked, isOnBehalfOfApplicable, isPseudoEmployer, isRegistrableDomainHandle, isSectionPopulated, isValidRgbColor, isVisibleActivityItem, languageTagSchema, lexiconDateExtractor, limitCombiningMarks, locationSegments, looksLikeDomain, makeGraphFollowRecordSchema, maxGraphemes, meetsContrastAA, normalizeCompanyKey, normalizeLegalForm, normalizeOpenTo, normalizePlatformId, normalizePresentationMode, normalizePresentationRole, normalizeSkillCategory, normalizeWorkplaceTypes, openToTokenToValue, openToValueToToken, parseIntendedAudiences, parseLocationString, parsePresentationDuration, parseTalkRkey, partialDateSchema, pdsProviderFromApi, pickPrimaryPosition, presentationCsvRowToRecord, presentationDeliveryCsvRowToRecord, primaryVerification, profileToDimensionInputs, qualifiesAsOrg, relativeLuminance, rendersCompanyProfile, rendersPersonalProfile, repoGroupForCollection, resolveAccountFacetMode, resolveCardHealth, resolveCardUrl, resolveEmbed, resolveVerifierProvider, rgbToString, roadmapIssueUrl, sanitizeDisplayText, sanitizeHandleInput, searchResultDisambiguation, selfLabelsSchema, singleDateExtractor, skillRefSchema, slugifyTitle, sortByActiveDateRange, sortByDateDesc, sortCertifications, sortEducation, sortHonors, sortLanguages, sortLanguagesByProficiency, sortPositions, sortProjects, sortPublications, streamAddressSchema, streamAuthorSchema, streamCardBodySchema, streamCardSubjectSchema, streamCardVMSchema, streamExternalLinkSchema, streamGeoSchema, streamMediaSchema, streamRichSegmentSchema, streamSourceSchema, streamThemeSchema, streamVerbSchema, stripHtmlToText, strongRefSchema, summarizePresentationDeliveries, summarizeProfileView, toStreamCardVM, toStreamCardVMs, truncateGraphemes, uriSchema, verbForCollection, visibleItems, youtubeThumbnailUrl, youtubeVideoId };
5831
5831
  //# sourceMappingURL=index.js.map
@@ -120,6 +120,43 @@ function matchPublisherByUri(uri) {
120
120
  return matchPublisherByHost(hostFromUri(uri));
121
121
  }
122
122
 
123
+ // src/publishing/linkblog.ts
124
+ var SKYREADER_LINKBLOG_MARKER_URL = "https://skyreader.app/linkblog";
125
+ function isRecord(value) {
126
+ return typeof value === "object" && value !== null;
127
+ }
128
+ function hasLinkblogMarker(record) {
129
+ const marker = record.skyreaderLinkblog;
130
+ return typeof marker === "string" && marker.length > 0;
131
+ }
132
+ function hostOf(uri) {
133
+ try {
134
+ return new URL(uri).host.toLowerCase();
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+ function isLinkblogPublication(record) {
140
+ return isRecord(record) && hasLinkblogMarker(record);
141
+ }
142
+ function isLinkblogShareDocument(record, opts = {}) {
143
+ if (!isRecord(record)) return false;
144
+ if (hasLinkblogMarker(record)) return true;
145
+ if (opts.publicationIsLinkblog === true) return true;
146
+ const links = record.links;
147
+ if (!Array.isArray(links)) return false;
148
+ const pubHost = opts.publicationHost?.toLowerCase();
149
+ for (const link of links) {
150
+ if (!isRecord(link) || typeof link.rel !== "string") continue;
151
+ if (link.rel === "repost") return true;
152
+ if (link.rel === "related" && pubHost && typeof link.uri === "string") {
153
+ const linkHost = hostOf(link.uri);
154
+ if (linkHost && linkHost !== pubHost) return true;
155
+ }
156
+ }
157
+ return false;
158
+ }
159
+
123
160
  // src/publishing/types.ts
124
161
  var STANDARD_SITE_PUBLICATION_NSID = "site.standard.publication";
125
162
  var STANDARD_SITE_DOCUMENT_NSID = "site.standard.document";
@@ -144,6 +181,7 @@ function hasStandardSiteAssociatedRef(refs) {
144
181
  }
145
182
 
146
183
  exports.BasicThemeSchema = BasicThemeSchema;
184
+ exports.SKYREADER_LINKBLOG_MARKER_URL = SKYREADER_LINKBLOG_MARKER_URL;
147
185
  exports.STANDARD_SITE_AUTH_SOCIAL_NSID = STANDARD_SITE_AUTH_SOCIAL_NSID;
148
186
  exports.STANDARD_SITE_DOCUMENT_NSID = STANDARD_SITE_DOCUMENT_NSID;
149
187
  exports.STANDARD_SITE_PUBLICATION_NSID = STANDARD_SITE_PUBLICATION_NSID;
@@ -156,6 +194,8 @@ exports.StandardSiteRecommendRecordSchema = StandardSiteRecommendRecordSchema;
156
194
  exports.StandardSiteSubscriptionRecordSchema = StandardSiteSubscriptionRecordSchema;
157
195
  exports.hasStandardSiteAssociatedRef = hasStandardSiteAssociatedRef;
158
196
  exports.hostMatches = hostMatches;
197
+ exports.isLinkblogPublication = isLinkblogPublication;
198
+ exports.isLinkblogShareDocument = isLinkblogShareDocument;
159
199
  exports.isStandardSiteAtUri = isStandardSiteAtUri;
160
200
  exports.matchPublisherByHost = matchPublisherByHost;
161
201
  exports.matchPublisherByUri = matchPublisherByUri;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/schemas/shared.ts","../../src/publishing/schemas.ts","../../src/publishing/registry.ts","../../src/publishing/types.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * Grapheme-aware refinement matching the AT Protocol lexicon `maxGraphemes`\n * constraint. JS strings are sequences of UTF-16 code units, but lexicon\n * `maxGraphemes` counts user-perceived characters (grapheme clusters), so\n * emoji sequences, regional indicators, ZWJ joins, and combining marks all\n * count as one unit each. We use `Intl.Segmenter` to enforce this correctly.\n */\nexport function maxGraphemes(max: number) {\n return (value: string): boolean => {\n const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });\n let count = 0;\n for (const _ of segmenter.segment(value)) {\n count++;\n if (count > max) return false;\n }\n return true;\n };\n}\n\n/** Decentralized identifier, AT Protocol `format: did`. */\nexport const didSchema = z.string().regex(/^did:[a-z]+:[a-zA-Z0-9._:%-]+$/, 'Invalid DID');\n\n/** RFC 3339 datetime with timezone offset, AT Protocol `format: datetime`. */\nexport const datetimeSchema = z.string().datetime({ offset: true });\n\n/**\n * Freeform calendar date for user-facing profile dates (start/end, issued,\n * completed, awarded, published, etc.). Accepts a bare year, year-month,\n * year-month-day, or a full datetime (so a legacy record's RFC-3339 date maps\n * through on backfill without loss). NOT strict `datetime` -- users typically\n * remember only month/year, and LinkedIn-importer writes carry partial dates,\n * so the lexicons document these as freeform `YYYY-MM` / `YYYY-MM-DD`. Record\n * metadata like `createdAt` stays on `datetimeSchema`.\n */\nexport const partialDateSchema = z\n .string()\n .regex(/^\\d{4}(-\\d{2}(-\\d{2}(T.+)?)?)?$/, 'Expected YYYY, YYYY-MM, YYYY-MM-DD, or a datetime');\n\n/** Generic AT-URI, AT Protocol `format: at-uri`. */\nexport const atUriSchema = z.string().regex(/^at:\\/\\/[^\\s]+$/, 'Invalid AT-URI');\n\n/** Content identifier, AT Protocol `format: cid`. Loose validation -- accepts CIDv0 and CIDv1. */\nexport const cidSchema = z\n .string()\n .regex(/^(Qm[1-9A-HJ-NP-Za-km-z]{44}|b[A-Za-z0-9+/=]+)$/, 'Invalid CID');\n\n/** BCP 47 language tag, AT Protocol `format: language`. */\nexport const languageTagSchema = z\n .string()\n .regex(/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/, 'Invalid BCP 47 language tag');\n\n/** Generic URI, AT Protocol `format: uri`. */\nexport const uriSchema = z.string().url();\n\n/**\n * StrongRef shape from `com.atproto.repo.strongRef` -- pins a record by both\n * AT-URI (identity) and CID (version).\n */\nexport const strongRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema,\n});\n\n/**\n * Reference to an `id.sifa.profile.skill` record by AT-URI. Mirrors\n * `id.sifa.defs#skillRef`, which carries no CID at all: skills are mutable\n * records in the same user's repo, so the reference resolves live and tracks\n * edits to the target.\n */\nexport const skillRefSchema = z.object({\n uri: atUriSchema,\n});\n\n/**\n * Reference to a record by AT-URI, with an optional CID. Mirrors\n * `id.sifa.defs#externalRecordRef`. Unlike a strongRef the CID is optional:\n * consumers resolve the AT-URI live so the reference tracks edits to the\n * target, and the CID is a best-effort integrity hint only.\n */\nexport const externalRecordRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema.optional(),\n});\n\n/**\n * Self-labels shape from `com.atproto.label.defs#selfLabels`. Modelled\n * permissively because clients rarely construct this directly; the AppView\n * handles label validation.\n */\nexport const selfLabelsSchema = z.object({\n $type: z.literal('com.atproto.label.defs#selfLabels').optional(),\n values: z.array(z.object({ val: z.string() })),\n});\n","import { z } from 'zod';\n\nimport {\n atUriSchema,\n datetimeSchema,\n didSchema,\n maxGraphemes,\n selfLabelsSchema,\n strongRefSchema,\n uriSchema,\n} from '../schemas/shared.js';\n\n/**\n * Zod schemas mirroring the canonical Standard.site lexicons that Sifa\n * consumes when rendering publication embeds.\n *\n * Canonical lexicons live at:\n * DID: did:plc:re3ebnp5v7ffagz6rb6xfei4\n * PDS: https://auriporia.us-west.host.bsky.network\n * Collection: com.atproto.lexicon.schema\n *\n * Sifa does NOT own these lexicons; we vendor the validation shapes so\n * clients can parse augmented embeds and (in Phase 4) write subscription\n * records to viewers' PDSes. Re-check against the canonical PDS on each\n * SDK release to detect schema drift.\n */\n\nconst rgbColorSchema = z.object({\n r: z.number().int().min(0).max(255),\n g: z.number().int().min(0).max(255),\n b: z.number().int().min(0).max(255),\n});\n\nexport type RgbColor = z.infer<typeof rgbColorSchema>;\n\n/** site.standard.theme.basic */\nexport const BasicThemeSchema = z.object({\n background: rgbColorSchema,\n foreground: rgbColorSchema,\n accent: rgbColorSchema,\n accentForeground: rgbColorSchema,\n});\n\nexport type BasicTheme = z.infer<typeof BasicThemeSchema>;\n\nconst blobRefSchema = z\n .object({\n $type: z.string().optional(),\n ref: z.unknown(),\n mimeType: z.string().optional(),\n size: z.number().optional(),\n })\n .passthrough();\n\n/** site.standard.publication */\nexport const StandardSitePublicationRecordSchema = z\n .object({\n url: uriSchema,\n name: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n icon: blobRefSchema.optional(),\n basicTheme: BasicThemeSchema.optional(),\n labels: selfLabelsSchema.optional(),\n preferences: z\n .object({\n showInDiscover: z.boolean().optional(),\n })\n .partial()\n .optional(),\n })\n .passthrough();\n\nexport type StandardSitePublicationRecord = z.infer<typeof StandardSitePublicationRecordSchema>;\n\nconst contributorSchema = z\n .object({\n did: didSchema,\n role: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n displayName: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n })\n .passthrough();\n\n/** site.standard.document */\nexport const StandardSiteDocumentRecordSchema = z\n .object({\n site: z.string().min(1),\n title: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n path: z.string().optional(),\n publishedAt: datetimeSchema,\n updatedAt: datetimeSchema.optional(),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n textContent: z.string().optional(),\n tags: z.array(z.string().refine(maxGraphemes(128)).max(1280)).optional(),\n coverImage: blobRefSchema.optional(),\n contributors: z.array(contributorSchema).optional(),\n bskyPostRef: strongRefSchema.optional(),\n labels: selfLabelsSchema.optional(),\n })\n .passthrough();\n\nexport type StandardSiteDocumentRecord = z.infer<typeof StandardSiteDocumentRecordSchema>;\n\n/**\n * site.standard.graph.subscription\n *\n * Authored by a viewer to declare a subscription to a publication. Used\n * by Phase 4 inline-subscribe path. Record key is a TID; the AT-URI\n * shape is `at://<viewer-did>/site.standard.graph.subscription/<rkey>`.\n */\nexport const StandardSiteSubscriptionRecordSchema = z.object({\n publication: atUriSchema,\n createdAt: datetimeSchema.optional(),\n});\n\nexport type StandardSiteSubscriptionRecord = z.infer<typeof StandardSiteSubscriptionRecordSchema>;\n\n/**\n * site.standard.graph.recommend\n *\n * Document-level \"like\" record. Same scope as subscription via the\n * site.standard.authSocial OAuth permission-set, so Phase 4 unlocks\n * inline likes alongside inline subscribes.\n */\nexport const StandardSiteRecommendRecordSchema = z.object({\n document: atUriSchema,\n createdAt: datetimeSchema,\n});\n\nexport type StandardSiteRecommendRecord = z.infer<typeof StandardSiteRecommendRecordSchema>;\n","/**\n * Publisher allowlist for the Standard.site rich embed.\n *\n * Mirrors `bluesky-social/social-app`\n * `src/components/Post/Embed/StandardSiteEmbed/publishers.ts`. The only\n * gate this provides is which publishers get the highlighted\n * \"Subscribe on {Publisher}\" CTA with a publisher icon vs the plain\n * \"View publication\" fallback — any Standard.site embed renders\n * regardless of allowlist membership.\n *\n * Sync policy: review additions against the upstream bsky list weekly;\n * Singi Labs reviews before merge.\n */\n\nexport interface Publisher {\n /** Lowercase host. Subdomains are matched via `hostMatches`. */\n host: string;\n /** Human-facing publisher name shown in CTA copy. */\n name: string;\n /**\n * Stable identifier used by clients to look up brand icons. The SDK\n * does not ship icon assets — sifa-web maintains them keyed by this\n * id so React Native vs web rendering can diverge.\n */\n iconKey: 'leaflet' | 'pckt' | 'offprint';\n}\n\nexport const STANDARD_SITE_PUBLISHERS: readonly Publisher[] = Object.freeze([\n { host: 'leaflet.pub', name: 'Leaflet', iconKey: 'leaflet' },\n { host: 'pckt.blog', name: 'pckt', iconKey: 'pckt' },\n { host: 'offprint.app', name: 'Offprint', iconKey: 'offprint' },\n]);\n\n/**\n * Returns true when `host` exactly matches `target` or is a subdomain of\n * it. Hosts are expected to be lowercase already.\n */\nexport function hostMatches(host: string, target: string): boolean {\n return host === target || host.endsWith('.' + target);\n}\n\nfunction hostFromUri(uri: string | undefined | null): string | null {\n if (!uri) return null;\n try {\n return new URL(uri).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * Match a publisher by host. Returns the publisher when `host` is on the\n * allowlist (exact or subdomain match), null otherwise. Host should be\n * lowercase.\n */\nexport function matchPublisherByHost(host: string | null | undefined): Publisher | null {\n if (!host) return null;\n const lower = host.toLowerCase();\n return STANDARD_SITE_PUBLISHERS.find((p) => hostMatches(lower, p.host)) ?? null;\n}\n\n/**\n * Match a publisher by URI. Convenience wrapper around\n * `matchPublisherByHost` for callers that have a publication URL handy.\n */\nexport function matchPublisherByUri(uri: string | undefined | null): Publisher | null {\n return matchPublisherByHost(hostFromUri(uri));\n}\n","import type { BasicTheme } from './schemas.js';\n\n/**\n * Publication metadata embedded in the augmented activity view by\n * sifa-api when an external link card matches an indexed publication.\n *\n * `uri` is the publication's AT-URI (e.g.\n * `at://did:plc:abc/site.standard.publication/xyz`). `theme` carries\n * the publication's own colors when supplied — clients should apply a\n * WCAG min-contrast fallback before honoring them.\n *\n * `icon` is left as `unknown` here because its representation depends\n * on the surface: blob ref on the wire, CDN URL after resolution.\n */\nexport interface PublicationSource {\n uri: string;\n title: string;\n icon?: unknown;\n theme?: BasicTheme;\n}\n\n/**\n * Shape attached to activity items whose external link card URL matches\n * a Standard.site record. Mirrors the relevant fields of bsky's\n * `StandardSiteEmbed` view so a sifa-web renderer can be near-1:1.\n *\n * `associatedRefs` carries AT-URIs the client may use in Phase 4 to\n * query subscription state for the viewer.\n */\nexport interface StandardSiteEmbedView {\n uri: string;\n source: PublicationSource;\n associatedRefs: { uri: string }[];\n createdAt?: string;\n readingTime?: number;\n}\n\nexport const STANDARD_SITE_PUBLICATION_NSID = 'site.standard.publication' as const;\nexport const STANDARD_SITE_DOCUMENT_NSID = 'site.standard.document' as const;\nexport const STANDARD_SITE_SUBSCRIPTION_NSID = 'site.standard.graph.subscription' as const;\nexport const STANDARD_SITE_RECOMMEND_NSID = 'site.standard.graph.recommend' as const;\n\n/**\n * Pre-defined OAuth permission-set the consumer apps request to write\n * subscription + recommend records on behalf of the viewer. The actual\n * permission-set is published under\n * `at://did:plc:re3ebnp5v7ffagz6rb6xfei4/com.atproto.lexicon.schema/site.standard.authSocial`.\n */\nexport const STANDARD_SITE_AUTH_SOCIAL_NSID = 'site.standard.authSocial' as const;\n\nconst COLLECTION_SET = new Set<string>([\n STANDARD_SITE_PUBLICATION_NSID,\n STANDARD_SITE_DOCUMENT_NSID,\n STANDARD_SITE_SUBSCRIPTION_NSID,\n STANDARD_SITE_RECOMMEND_NSID,\n]);\n\n/**\n * True when the AT-URI's collection segment is a Standard.site\n * collection. Useful for detecting Standard.site embed augmentation on\n * arbitrary activity items.\n */\nexport function isStandardSiteAtUri(uri: string): boolean {\n if (!uri.startsWith('at://')) return false;\n const segments = uri.slice('at://'.length).split('/');\n const collection = segments[1];\n return collection !== undefined && COLLECTION_SET.has(collection);\n}\n\n/**\n * True when any `associatedRefs[].uri` is a Standard.site collection\n * AT-URI. The bsky client uses the same check to gate the\n * `StandardSiteEmbed` renderer.\n */\nexport function hasStandardSiteAssociatedRef(refs: { uri: string }[] | undefined): boolean {\n if (!refs) return false;\n return refs.some((r) => isStandardSiteAtUri(r.uri));\n}\n"]}
1
+ {"version":3,"sources":["../../src/schemas/shared.ts","../../src/publishing/schemas.ts","../../src/publishing/registry.ts","../../src/publishing/linkblog.ts","../../src/publishing/types.ts"],"names":["z"],"mappings":"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","file":"index.cjs","sourcesContent":["import { z } from 'zod';\n\n/**\n * Grapheme-aware refinement matching the AT Protocol lexicon `maxGraphemes`\n * constraint. JS strings are sequences of UTF-16 code units, but lexicon\n * `maxGraphemes` counts user-perceived characters (grapheme clusters), so\n * emoji sequences, regional indicators, ZWJ joins, and combining marks all\n * count as one unit each. We use `Intl.Segmenter` to enforce this correctly.\n */\nexport function maxGraphemes(max: number) {\n return (value: string): boolean => {\n const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });\n let count = 0;\n for (const _ of segmenter.segment(value)) {\n count++;\n if (count > max) return false;\n }\n return true;\n };\n}\n\n/** Decentralized identifier, AT Protocol `format: did`. */\nexport const didSchema = z.string().regex(/^did:[a-z]+:[a-zA-Z0-9._:%-]+$/, 'Invalid DID');\n\n/** RFC 3339 datetime with timezone offset, AT Protocol `format: datetime`. */\nexport const datetimeSchema = z.string().datetime({ offset: true });\n\n/**\n * Freeform calendar date for user-facing profile dates (start/end, issued,\n * completed, awarded, published, etc.). Accepts a bare year, year-month,\n * year-month-day, or a full datetime (so a legacy record's RFC-3339 date maps\n * through on backfill without loss). NOT strict `datetime` -- users typically\n * remember only month/year, and LinkedIn-importer writes carry partial dates,\n * so the lexicons document these as freeform `YYYY-MM` / `YYYY-MM-DD`. Record\n * metadata like `createdAt` stays on `datetimeSchema`.\n */\nexport const partialDateSchema = z\n .string()\n .regex(/^\\d{4}(-\\d{2}(-\\d{2}(T.+)?)?)?$/, 'Expected YYYY, YYYY-MM, YYYY-MM-DD, or a datetime');\n\n/** Generic AT-URI, AT Protocol `format: at-uri`. */\nexport const atUriSchema = z.string().regex(/^at:\\/\\/[^\\s]+$/, 'Invalid AT-URI');\n\n/** Content identifier, AT Protocol `format: cid`. Loose validation -- accepts CIDv0 and CIDv1. */\nexport const cidSchema = z\n .string()\n .regex(/^(Qm[1-9A-HJ-NP-Za-km-z]{44}|b[A-Za-z0-9+/=]+)$/, 'Invalid CID');\n\n/** BCP 47 language tag, AT Protocol `format: language`. */\nexport const languageTagSchema = z\n .string()\n .regex(/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/, 'Invalid BCP 47 language tag');\n\n/** Generic URI, AT Protocol `format: uri`. */\nexport const uriSchema = z.string().url();\n\n/**\n * StrongRef shape from `com.atproto.repo.strongRef` -- pins a record by both\n * AT-URI (identity) and CID (version).\n */\nexport const strongRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema,\n});\n\n/**\n * Reference to an `id.sifa.profile.skill` record by AT-URI. Mirrors\n * `id.sifa.defs#skillRef`, which carries no CID at all: skills are mutable\n * records in the same user's repo, so the reference resolves live and tracks\n * edits to the target.\n */\nexport const skillRefSchema = z.object({\n uri: atUriSchema,\n});\n\n/**\n * Reference to a record by AT-URI, with an optional CID. Mirrors\n * `id.sifa.defs#externalRecordRef`. Unlike a strongRef the CID is optional:\n * consumers resolve the AT-URI live so the reference tracks edits to the\n * target, and the CID is a best-effort integrity hint only.\n */\nexport const externalRecordRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema.optional(),\n});\n\n/**\n * Self-labels shape from `com.atproto.label.defs#selfLabels`. Modelled\n * permissively because clients rarely construct this directly; the AppView\n * handles label validation.\n */\nexport const selfLabelsSchema = z.object({\n $type: z.literal('com.atproto.label.defs#selfLabels').optional(),\n values: z.array(z.object({ val: z.string() })),\n});\n","import { z } from 'zod';\n\nimport {\n atUriSchema,\n datetimeSchema,\n didSchema,\n maxGraphemes,\n selfLabelsSchema,\n strongRefSchema,\n uriSchema,\n} from '../schemas/shared.js';\n\n/**\n * Zod schemas mirroring the canonical Standard.site lexicons that Sifa\n * consumes when rendering publication embeds.\n *\n * Canonical lexicons live at:\n * DID: did:plc:re3ebnp5v7ffagz6rb6xfei4\n * PDS: https://auriporia.us-west.host.bsky.network\n * Collection: com.atproto.lexicon.schema\n *\n * Sifa does NOT own these lexicons; we vendor the validation shapes so\n * clients can parse augmented embeds and (in Phase 4) write subscription\n * records to viewers' PDSes. Re-check against the canonical PDS on each\n * SDK release to detect schema drift.\n */\n\nconst rgbColorSchema = z.object({\n r: z.number().int().min(0).max(255),\n g: z.number().int().min(0).max(255),\n b: z.number().int().min(0).max(255),\n});\n\nexport type RgbColor = z.infer<typeof rgbColorSchema>;\n\n/** site.standard.theme.basic */\nexport const BasicThemeSchema = z.object({\n background: rgbColorSchema,\n foreground: rgbColorSchema,\n accent: rgbColorSchema,\n accentForeground: rgbColorSchema,\n});\n\nexport type BasicTheme = z.infer<typeof BasicThemeSchema>;\n\nconst blobRefSchema = z\n .object({\n $type: z.string().optional(),\n ref: z.unknown(),\n mimeType: z.string().optional(),\n size: z.number().optional(),\n })\n .passthrough();\n\n/** site.standard.publication */\nexport const StandardSitePublicationRecordSchema = z\n .object({\n url: uriSchema,\n name: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n icon: blobRefSchema.optional(),\n basicTheme: BasicThemeSchema.optional(),\n labels: selfLabelsSchema.optional(),\n preferences: z\n .object({\n showInDiscover: z.boolean().optional(),\n })\n .partial()\n .optional(),\n })\n .passthrough();\n\nexport type StandardSitePublicationRecord = z.infer<typeof StandardSitePublicationRecordSchema>;\n\nconst contributorSchema = z\n .object({\n did: didSchema,\n role: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n displayName: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n })\n .passthrough();\n\n/** site.standard.document */\nexport const StandardSiteDocumentRecordSchema = z\n .object({\n site: z.string().min(1),\n title: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n path: z.string().optional(),\n publishedAt: datetimeSchema,\n updatedAt: datetimeSchema.optional(),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n textContent: z.string().optional(),\n tags: z.array(z.string().refine(maxGraphemes(128)).max(1280)).optional(),\n coverImage: blobRefSchema.optional(),\n contributors: z.array(contributorSchema).optional(),\n bskyPostRef: strongRefSchema.optional(),\n labels: selfLabelsSchema.optional(),\n })\n .passthrough();\n\nexport type StandardSiteDocumentRecord = z.infer<typeof StandardSiteDocumentRecordSchema>;\n\n/**\n * site.standard.graph.subscription\n *\n * Authored by a viewer to declare a subscription to a publication. Used\n * by Phase 4 inline-subscribe path. Record key is a TID; the AT-URI\n * shape is `at://<viewer-did>/site.standard.graph.subscription/<rkey>`.\n */\nexport const StandardSiteSubscriptionRecordSchema = z.object({\n publication: atUriSchema,\n createdAt: datetimeSchema.optional(),\n});\n\nexport type StandardSiteSubscriptionRecord = z.infer<typeof StandardSiteSubscriptionRecordSchema>;\n\n/**\n * site.standard.graph.recommend\n *\n * Document-level \"like\" record. Same scope as subscription via the\n * site.standard.authSocial OAuth permission-set, so Phase 4 unlocks\n * inline likes alongside inline subscribes.\n */\nexport const StandardSiteRecommendRecordSchema = z.object({\n document: atUriSchema,\n createdAt: datetimeSchema,\n});\n\nexport type StandardSiteRecommendRecord = z.infer<typeof StandardSiteRecommendRecordSchema>;\n","/**\n * Publisher allowlist for the Standard.site rich embed.\n *\n * Mirrors `bluesky-social/social-app`\n * `src/components/Post/Embed/StandardSiteEmbed/publishers.ts`. The only\n * gate this provides is which publishers get the highlighted\n * \"Subscribe on {Publisher}\" CTA with a publisher icon vs the plain\n * \"View publication\" fallback — any Standard.site embed renders\n * regardless of allowlist membership.\n *\n * Sync policy: review additions against the upstream bsky list weekly;\n * Singi Labs reviews before merge.\n */\n\nexport interface Publisher {\n /** Lowercase host. Subdomains are matched via `hostMatches`. */\n host: string;\n /** Human-facing publisher name shown in CTA copy. */\n name: string;\n /**\n * Stable identifier used by clients to look up brand icons. The SDK\n * does not ship icon assets — sifa-web maintains them keyed by this\n * id so React Native vs web rendering can diverge.\n */\n iconKey: 'leaflet' | 'pckt' | 'offprint';\n}\n\nexport const STANDARD_SITE_PUBLISHERS: readonly Publisher[] = Object.freeze([\n { host: 'leaflet.pub', name: 'Leaflet', iconKey: 'leaflet' },\n { host: 'pckt.blog', name: 'pckt', iconKey: 'pckt' },\n { host: 'offprint.app', name: 'Offprint', iconKey: 'offprint' },\n]);\n\n/**\n * Returns true when `host` exactly matches `target` or is a subdomain of\n * it. Hosts are expected to be lowercase already.\n */\nexport function hostMatches(host: string, target: string): boolean {\n return host === target || host.endsWith('.' + target);\n}\n\nfunction hostFromUri(uri: string | undefined | null): string | null {\n if (!uri) return null;\n try {\n return new URL(uri).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * Match a publisher by host. Returns the publisher when `host` is on the\n * allowlist (exact or subdomain match), null otherwise. Host should be\n * lowercase.\n */\nexport function matchPublisherByHost(host: string | null | undefined): Publisher | null {\n if (!host) return null;\n const lower = host.toLowerCase();\n return STANDARD_SITE_PUBLISHERS.find((p) => hostMatches(lower, p.host)) ?? null;\n}\n\n/**\n * Match a publisher by URI. Convenience wrapper around\n * `matchPublisherByHost` for callers that have a publication URL handy.\n */\nexport function matchPublisherByUri(uri: string | undefined | null): Publisher | null {\n return matchPublisherByHost(hostFromUri(uri));\n}\n","/**\n * Linkblog-share detection for the Standard.site document family.\n *\n * A \"linkblog\" (e.g. Skyreader) writes a `site.standard.document` to the\n * user's PDS for every *external* article they share. Those documents are\n * valid Standard.site posts, so they surface in Sifa's activity stream and\n * profile portfolio as if the user authored them — but the user only shared\n * someone else's writing (sifa-workspace#351).\n *\n * These predicates identify such shares so callers can exclude them, while\n * leaving genuinely authored Standard.site posts (Leaflet, pckt, Offprint\n * essays) untouched.\n *\n * Signals, most to least reliable:\n * 1. The Skyreader provenance marker on the document (`skyreaderLinkblog`).\n * 2. The document's parent publication is itself a linkblog (the marker\n * lives on the publication even when older documents lack their own).\n * Callers resolve the publication and pass `publicationIsLinkblog`.\n * 3. A `links` entry with `rel: \"repost\"` — a quote-reshare, inherently\n * not the user's own writing.\n * 4. Heuristic: a `links` entry with `rel: \"related\"` pointing to an\n * external host different from the document's own publication host —\n * the document's subject is an off-site article. Only applied when the\n * caller supplies the resolved `publicationHost`, so an authored post\n * that merely cites an external resource is never hidden on a guess.\n */\n\n/**\n * Discovery marker Skyreader stamps on every linkblog publication and on\n * link-share documents. Value is a fixed URL, not a per-user value.\n */\nexport const SKYREADER_LINKBLOG_MARKER_URL = 'https://skyreader.app/linkblog';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** A non-empty `skyreaderLinkblog` marker field. */\nfunction hasLinkblogMarker(record: Record<string, unknown>): boolean {\n const marker = record.skyreaderLinkblog;\n return typeof marker === 'string' && marker.length > 0;\n}\n\nfunction hostOf(uri: string): string | null {\n try {\n return new URL(uri).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * True when a `site.standard.publication` record is a linkblog rather than an\n * authored publication — i.e. it carries the Skyreader provenance marker.\n */\nexport function isLinkblogPublication(record: unknown): boolean {\n return isRecord(record) && hasLinkblogMarker(record);\n}\n\nexport interface LinkblogShareOptions {\n /**\n * True when the document's parent publication has been resolved and is\n * itself a linkblog (see {@link isLinkblogPublication}). Catches shares\n * whose document predates the per-document marker.\n */\n publicationIsLinkblog?: boolean;\n /**\n * Resolved host of the document's parent publication, enabling the\n * external-subject heuristic. Omit when the publication (hence its host)\n * could not be resolved — the heuristic then stays off.\n */\n publicationHost?: string;\n}\n\n/**\n * True when a `site.standard.document` is a shared/linkblogged external\n * article rather than the user's own writing. See the module comment for the\n * signal order.\n */\nexport function isLinkblogShareDocument(record: unknown, opts: LinkblogShareOptions = {}): boolean {\n if (!isRecord(record)) return false;\n\n // 1. Provenance marker on the document itself.\n if (hasLinkblogMarker(record)) return true;\n\n // 2. Parent publication is a linkblog (caller-resolved).\n if (opts.publicationIsLinkblog === true) return true;\n\n const links = record.links;\n if (!Array.isArray(links)) return false;\n\n const pubHost = opts.publicationHost?.toLowerCase();\n for (const link of links) {\n if (!isRecord(link) || typeof link.rel !== 'string') continue;\n\n // 3. Reshare of another record.\n if (link.rel === 'repost') return true;\n\n // 4. External-subject heuristic (only with a known publication host).\n if (link.rel === 'related' && pubHost && typeof link.uri === 'string') {\n const linkHost = hostOf(link.uri);\n if (linkHost && linkHost !== pubHost) return true;\n }\n }\n\n return false;\n}\n","import type { BasicTheme } from './schemas.js';\n\n/**\n * Publication metadata embedded in the augmented activity view by\n * sifa-api when an external link card matches an indexed publication.\n *\n * `uri` is the publication's AT-URI (e.g.\n * `at://did:plc:abc/site.standard.publication/xyz`). `theme` carries\n * the publication's own colors when supplied — clients should apply a\n * WCAG min-contrast fallback before honoring them.\n *\n * `icon` is left as `unknown` here because its representation depends\n * on the surface: blob ref on the wire, CDN URL after resolution.\n */\nexport interface PublicationSource {\n uri: string;\n title: string;\n icon?: unknown;\n theme?: BasicTheme;\n}\n\n/**\n * Shape attached to activity items whose external link card URL matches\n * a Standard.site record. Mirrors the relevant fields of bsky's\n * `StandardSiteEmbed` view so a sifa-web renderer can be near-1:1.\n *\n * `associatedRefs` carries AT-URIs the client may use in Phase 4 to\n * query subscription state for the viewer.\n */\nexport interface StandardSiteEmbedView {\n uri: string;\n source: PublicationSource;\n associatedRefs: { uri: string }[];\n createdAt?: string;\n readingTime?: number;\n}\n\nexport const STANDARD_SITE_PUBLICATION_NSID = 'site.standard.publication' as const;\nexport const STANDARD_SITE_DOCUMENT_NSID = 'site.standard.document' as const;\nexport const STANDARD_SITE_SUBSCRIPTION_NSID = 'site.standard.graph.subscription' as const;\nexport const STANDARD_SITE_RECOMMEND_NSID = 'site.standard.graph.recommend' as const;\n\n/**\n * Pre-defined OAuth permission-set the consumer apps request to write\n * subscription + recommend records on behalf of the viewer. The actual\n * permission-set is published under\n * `at://did:plc:re3ebnp5v7ffagz6rb6xfei4/com.atproto.lexicon.schema/site.standard.authSocial`.\n */\nexport const STANDARD_SITE_AUTH_SOCIAL_NSID = 'site.standard.authSocial' as const;\n\nconst COLLECTION_SET = new Set<string>([\n STANDARD_SITE_PUBLICATION_NSID,\n STANDARD_SITE_DOCUMENT_NSID,\n STANDARD_SITE_SUBSCRIPTION_NSID,\n STANDARD_SITE_RECOMMEND_NSID,\n]);\n\n/**\n * True when the AT-URI's collection segment is a Standard.site\n * collection. Useful for detecting Standard.site embed augmentation on\n * arbitrary activity items.\n */\nexport function isStandardSiteAtUri(uri: string): boolean {\n if (!uri.startsWith('at://')) return false;\n const segments = uri.slice('at://'.length).split('/');\n const collection = segments[1];\n return collection !== undefined && COLLECTION_SET.has(collection);\n}\n\n/**\n * True when any `associatedRefs[].uri` is a Standard.site collection\n * AT-URI. The bsky client uses the same check to gate the\n * `StandardSiteEmbed` renderer.\n */\nexport function hasStandardSiteAssociatedRef(refs: { uri: string }[] | undefined): boolean {\n if (!refs) return false;\n return refs.some((r) => isStandardSiteAtUri(r.uri));\n}\n"]}
@@ -189,6 +189,63 @@ declare function matchPublisherByHost(host: string | null | undefined): Publishe
189
189
  */
190
190
  declare function matchPublisherByUri(uri: string | undefined | null): Publisher | null;
191
191
 
192
+ /**
193
+ * Linkblog-share detection for the Standard.site document family.
194
+ *
195
+ * A "linkblog" (e.g. Skyreader) writes a `site.standard.document` to the
196
+ * user's PDS for every *external* article they share. Those documents are
197
+ * valid Standard.site posts, so they surface in Sifa's activity stream and
198
+ * profile portfolio as if the user authored them — but the user only shared
199
+ * someone else's writing (sifa-workspace#351).
200
+ *
201
+ * These predicates identify such shares so callers can exclude them, while
202
+ * leaving genuinely authored Standard.site posts (Leaflet, pckt, Offprint
203
+ * essays) untouched.
204
+ *
205
+ * Signals, most to least reliable:
206
+ * 1. The Skyreader provenance marker on the document (`skyreaderLinkblog`).
207
+ * 2. The document's parent publication is itself a linkblog (the marker
208
+ * lives on the publication even when older documents lack their own).
209
+ * Callers resolve the publication and pass `publicationIsLinkblog`.
210
+ * 3. A `links` entry with `rel: "repost"` — a quote-reshare, inherently
211
+ * not the user's own writing.
212
+ * 4. Heuristic: a `links` entry with `rel: "related"` pointing to an
213
+ * external host different from the document's own publication host —
214
+ * the document's subject is an off-site article. Only applied when the
215
+ * caller supplies the resolved `publicationHost`, so an authored post
216
+ * that merely cites an external resource is never hidden on a guess.
217
+ */
218
+ /**
219
+ * Discovery marker Skyreader stamps on every linkblog publication and on
220
+ * link-share documents. Value is a fixed URL, not a per-user value.
221
+ */
222
+ declare const SKYREADER_LINKBLOG_MARKER_URL = "https://skyreader.app/linkblog";
223
+ /**
224
+ * True when a `site.standard.publication` record is a linkblog rather than an
225
+ * authored publication — i.e. it carries the Skyreader provenance marker.
226
+ */
227
+ declare function isLinkblogPublication(record: unknown): boolean;
228
+ interface LinkblogShareOptions {
229
+ /**
230
+ * True when the document's parent publication has been resolved and is
231
+ * itself a linkblog (see {@link isLinkblogPublication}). Catches shares
232
+ * whose document predates the per-document marker.
233
+ */
234
+ publicationIsLinkblog?: boolean;
235
+ /**
236
+ * Resolved host of the document's parent publication, enabling the
237
+ * external-subject heuristic. Omit when the publication (hence its host)
238
+ * could not be resolved — the heuristic then stays off.
239
+ */
240
+ publicationHost?: string;
241
+ }
242
+ /**
243
+ * True when a `site.standard.document` is a shared/linkblogged external
244
+ * article rather than the user's own writing. See the module comment for the
245
+ * signal order.
246
+ */
247
+ declare function isLinkblogShareDocument(record: unknown, opts?: LinkblogShareOptions): boolean;
248
+
192
249
  /**
193
250
  * Publication metadata embedded in the augmented activity view by
194
251
  * sifa-api when an external link card matches an indexed publication.
@@ -250,4 +307,4 @@ declare function hasStandardSiteAssociatedRef(refs: {
250
307
  uri: string;
251
308
  }[] | undefined): boolean;
252
309
 
253
- export { type BasicTheme, BasicThemeSchema, type PublicationSource, type Publisher, type RgbColor, STANDARD_SITE_AUTH_SOCIAL_NSID, STANDARD_SITE_DOCUMENT_NSID, STANDARD_SITE_PUBLICATION_NSID, STANDARD_SITE_PUBLISHERS, STANDARD_SITE_RECOMMEND_NSID, STANDARD_SITE_SUBSCRIPTION_NSID, type StandardSiteDocumentRecord, StandardSiteDocumentRecordSchema, type StandardSiteEmbedView, type StandardSitePublicationRecord, StandardSitePublicationRecordSchema, type StandardSiteRecommendRecord, StandardSiteRecommendRecordSchema, type StandardSiteSubscriptionRecord, StandardSiteSubscriptionRecordSchema, hasStandardSiteAssociatedRef, hostMatches, isStandardSiteAtUri, matchPublisherByHost, matchPublisherByUri };
310
+ export { type BasicTheme, BasicThemeSchema, type LinkblogShareOptions, type PublicationSource, type Publisher, type RgbColor, SKYREADER_LINKBLOG_MARKER_URL, STANDARD_SITE_AUTH_SOCIAL_NSID, STANDARD_SITE_DOCUMENT_NSID, STANDARD_SITE_PUBLICATION_NSID, STANDARD_SITE_PUBLISHERS, STANDARD_SITE_RECOMMEND_NSID, STANDARD_SITE_SUBSCRIPTION_NSID, type StandardSiteDocumentRecord, StandardSiteDocumentRecordSchema, type StandardSiteEmbedView, type StandardSitePublicationRecord, StandardSitePublicationRecordSchema, type StandardSiteRecommendRecord, StandardSiteRecommendRecordSchema, type StandardSiteSubscriptionRecord, StandardSiteSubscriptionRecordSchema, hasStandardSiteAssociatedRef, hostMatches, isLinkblogPublication, isLinkblogShareDocument, isStandardSiteAtUri, matchPublisherByHost, matchPublisherByUri };
@@ -189,6 +189,63 @@ declare function matchPublisherByHost(host: string | null | undefined): Publishe
189
189
  */
190
190
  declare function matchPublisherByUri(uri: string | undefined | null): Publisher | null;
191
191
 
192
+ /**
193
+ * Linkblog-share detection for the Standard.site document family.
194
+ *
195
+ * A "linkblog" (e.g. Skyreader) writes a `site.standard.document` to the
196
+ * user's PDS for every *external* article they share. Those documents are
197
+ * valid Standard.site posts, so they surface in Sifa's activity stream and
198
+ * profile portfolio as if the user authored them — but the user only shared
199
+ * someone else's writing (sifa-workspace#351).
200
+ *
201
+ * These predicates identify such shares so callers can exclude them, while
202
+ * leaving genuinely authored Standard.site posts (Leaflet, pckt, Offprint
203
+ * essays) untouched.
204
+ *
205
+ * Signals, most to least reliable:
206
+ * 1. The Skyreader provenance marker on the document (`skyreaderLinkblog`).
207
+ * 2. The document's parent publication is itself a linkblog (the marker
208
+ * lives on the publication even when older documents lack their own).
209
+ * Callers resolve the publication and pass `publicationIsLinkblog`.
210
+ * 3. A `links` entry with `rel: "repost"` — a quote-reshare, inherently
211
+ * not the user's own writing.
212
+ * 4. Heuristic: a `links` entry with `rel: "related"` pointing to an
213
+ * external host different from the document's own publication host —
214
+ * the document's subject is an off-site article. Only applied when the
215
+ * caller supplies the resolved `publicationHost`, so an authored post
216
+ * that merely cites an external resource is never hidden on a guess.
217
+ */
218
+ /**
219
+ * Discovery marker Skyreader stamps on every linkblog publication and on
220
+ * link-share documents. Value is a fixed URL, not a per-user value.
221
+ */
222
+ declare const SKYREADER_LINKBLOG_MARKER_URL = "https://skyreader.app/linkblog";
223
+ /**
224
+ * True when a `site.standard.publication` record is a linkblog rather than an
225
+ * authored publication — i.e. it carries the Skyreader provenance marker.
226
+ */
227
+ declare function isLinkblogPublication(record: unknown): boolean;
228
+ interface LinkblogShareOptions {
229
+ /**
230
+ * True when the document's parent publication has been resolved and is
231
+ * itself a linkblog (see {@link isLinkblogPublication}). Catches shares
232
+ * whose document predates the per-document marker.
233
+ */
234
+ publicationIsLinkblog?: boolean;
235
+ /**
236
+ * Resolved host of the document's parent publication, enabling the
237
+ * external-subject heuristic. Omit when the publication (hence its host)
238
+ * could not be resolved — the heuristic then stays off.
239
+ */
240
+ publicationHost?: string;
241
+ }
242
+ /**
243
+ * True when a `site.standard.document` is a shared/linkblogged external
244
+ * article rather than the user's own writing. See the module comment for the
245
+ * signal order.
246
+ */
247
+ declare function isLinkblogShareDocument(record: unknown, opts?: LinkblogShareOptions): boolean;
248
+
192
249
  /**
193
250
  * Publication metadata embedded in the augmented activity view by
194
251
  * sifa-api when an external link card matches an indexed publication.
@@ -250,4 +307,4 @@ declare function hasStandardSiteAssociatedRef(refs: {
250
307
  uri: string;
251
308
  }[] | undefined): boolean;
252
309
 
253
- export { type BasicTheme, BasicThemeSchema, type PublicationSource, type Publisher, type RgbColor, STANDARD_SITE_AUTH_SOCIAL_NSID, STANDARD_SITE_DOCUMENT_NSID, STANDARD_SITE_PUBLICATION_NSID, STANDARD_SITE_PUBLISHERS, STANDARD_SITE_RECOMMEND_NSID, STANDARD_SITE_SUBSCRIPTION_NSID, type StandardSiteDocumentRecord, StandardSiteDocumentRecordSchema, type StandardSiteEmbedView, type StandardSitePublicationRecord, StandardSitePublicationRecordSchema, type StandardSiteRecommendRecord, StandardSiteRecommendRecordSchema, type StandardSiteSubscriptionRecord, StandardSiteSubscriptionRecordSchema, hasStandardSiteAssociatedRef, hostMatches, isStandardSiteAtUri, matchPublisherByHost, matchPublisherByUri };
310
+ export { type BasicTheme, BasicThemeSchema, type LinkblogShareOptions, type PublicationSource, type Publisher, type RgbColor, SKYREADER_LINKBLOG_MARKER_URL, STANDARD_SITE_AUTH_SOCIAL_NSID, STANDARD_SITE_DOCUMENT_NSID, STANDARD_SITE_PUBLICATION_NSID, STANDARD_SITE_PUBLISHERS, STANDARD_SITE_RECOMMEND_NSID, STANDARD_SITE_SUBSCRIPTION_NSID, type StandardSiteDocumentRecord, StandardSiteDocumentRecordSchema, type StandardSiteEmbedView, type StandardSitePublicationRecord, StandardSitePublicationRecordSchema, type StandardSiteRecommendRecord, StandardSiteRecommendRecordSchema, type StandardSiteSubscriptionRecord, StandardSiteSubscriptionRecordSchema, hasStandardSiteAssociatedRef, hostMatches, isLinkblogPublication, isLinkblogShareDocument, isStandardSiteAtUri, matchPublisherByHost, matchPublisherByUri };
@@ -118,6 +118,43 @@ function matchPublisherByUri(uri) {
118
118
  return matchPublisherByHost(hostFromUri(uri));
119
119
  }
120
120
 
121
+ // src/publishing/linkblog.ts
122
+ var SKYREADER_LINKBLOG_MARKER_URL = "https://skyreader.app/linkblog";
123
+ function isRecord(value) {
124
+ return typeof value === "object" && value !== null;
125
+ }
126
+ function hasLinkblogMarker(record) {
127
+ const marker = record.skyreaderLinkblog;
128
+ return typeof marker === "string" && marker.length > 0;
129
+ }
130
+ function hostOf(uri) {
131
+ try {
132
+ return new URL(uri).host.toLowerCase();
133
+ } catch {
134
+ return null;
135
+ }
136
+ }
137
+ function isLinkblogPublication(record) {
138
+ return isRecord(record) && hasLinkblogMarker(record);
139
+ }
140
+ function isLinkblogShareDocument(record, opts = {}) {
141
+ if (!isRecord(record)) return false;
142
+ if (hasLinkblogMarker(record)) return true;
143
+ if (opts.publicationIsLinkblog === true) return true;
144
+ const links = record.links;
145
+ if (!Array.isArray(links)) return false;
146
+ const pubHost = opts.publicationHost?.toLowerCase();
147
+ for (const link of links) {
148
+ if (!isRecord(link) || typeof link.rel !== "string") continue;
149
+ if (link.rel === "repost") return true;
150
+ if (link.rel === "related" && pubHost && typeof link.uri === "string") {
151
+ const linkHost = hostOf(link.uri);
152
+ if (linkHost && linkHost !== pubHost) return true;
153
+ }
154
+ }
155
+ return false;
156
+ }
157
+
121
158
  // src/publishing/types.ts
122
159
  var STANDARD_SITE_PUBLICATION_NSID = "site.standard.publication";
123
160
  var STANDARD_SITE_DOCUMENT_NSID = "site.standard.document";
@@ -141,6 +178,6 @@ function hasStandardSiteAssociatedRef(refs) {
141
178
  return refs.some((r) => isStandardSiteAtUri(r.uri));
142
179
  }
143
180
 
144
- export { BasicThemeSchema, STANDARD_SITE_AUTH_SOCIAL_NSID, STANDARD_SITE_DOCUMENT_NSID, STANDARD_SITE_PUBLICATION_NSID, STANDARD_SITE_PUBLISHERS, STANDARD_SITE_RECOMMEND_NSID, STANDARD_SITE_SUBSCRIPTION_NSID, StandardSiteDocumentRecordSchema, StandardSitePublicationRecordSchema, StandardSiteRecommendRecordSchema, StandardSiteSubscriptionRecordSchema, hasStandardSiteAssociatedRef, hostMatches, isStandardSiteAtUri, matchPublisherByHost, matchPublisherByUri };
181
+ export { BasicThemeSchema, SKYREADER_LINKBLOG_MARKER_URL, STANDARD_SITE_AUTH_SOCIAL_NSID, STANDARD_SITE_DOCUMENT_NSID, STANDARD_SITE_PUBLICATION_NSID, STANDARD_SITE_PUBLISHERS, STANDARD_SITE_RECOMMEND_NSID, STANDARD_SITE_SUBSCRIPTION_NSID, StandardSiteDocumentRecordSchema, StandardSitePublicationRecordSchema, StandardSiteRecommendRecordSchema, StandardSiteSubscriptionRecordSchema, hasStandardSiteAssociatedRef, hostMatches, isLinkblogPublication, isLinkblogShareDocument, isStandardSiteAtUri, matchPublisherByHost, matchPublisherByUri };
145
182
  //# sourceMappingURL=index.js.map
146
183
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/schemas/shared.ts","../../src/publishing/schemas.ts","../../src/publishing/registry.ts","../../src/publishing/types.ts"],"names":["z"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/**\n * Grapheme-aware refinement matching the AT Protocol lexicon `maxGraphemes`\n * constraint. JS strings are sequences of UTF-16 code units, but lexicon\n * `maxGraphemes` counts user-perceived characters (grapheme clusters), so\n * emoji sequences, regional indicators, ZWJ joins, and combining marks all\n * count as one unit each. We use `Intl.Segmenter` to enforce this correctly.\n */\nexport function maxGraphemes(max: number) {\n return (value: string): boolean => {\n const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });\n let count = 0;\n for (const _ of segmenter.segment(value)) {\n count++;\n if (count > max) return false;\n }\n return true;\n };\n}\n\n/** Decentralized identifier, AT Protocol `format: did`. */\nexport const didSchema = z.string().regex(/^did:[a-z]+:[a-zA-Z0-9._:%-]+$/, 'Invalid DID');\n\n/** RFC 3339 datetime with timezone offset, AT Protocol `format: datetime`. */\nexport const datetimeSchema = z.string().datetime({ offset: true });\n\n/**\n * Freeform calendar date for user-facing profile dates (start/end, issued,\n * completed, awarded, published, etc.). Accepts a bare year, year-month,\n * year-month-day, or a full datetime (so a legacy record's RFC-3339 date maps\n * through on backfill without loss). NOT strict `datetime` -- users typically\n * remember only month/year, and LinkedIn-importer writes carry partial dates,\n * so the lexicons document these as freeform `YYYY-MM` / `YYYY-MM-DD`. Record\n * metadata like `createdAt` stays on `datetimeSchema`.\n */\nexport const partialDateSchema = z\n .string()\n .regex(/^\\d{4}(-\\d{2}(-\\d{2}(T.+)?)?)?$/, 'Expected YYYY, YYYY-MM, YYYY-MM-DD, or a datetime');\n\n/** Generic AT-URI, AT Protocol `format: at-uri`. */\nexport const atUriSchema = z.string().regex(/^at:\\/\\/[^\\s]+$/, 'Invalid AT-URI');\n\n/** Content identifier, AT Protocol `format: cid`. Loose validation -- accepts CIDv0 and CIDv1. */\nexport const cidSchema = z\n .string()\n .regex(/^(Qm[1-9A-HJ-NP-Za-km-z]{44}|b[A-Za-z0-9+/=]+)$/, 'Invalid CID');\n\n/** BCP 47 language tag, AT Protocol `format: language`. */\nexport const languageTagSchema = z\n .string()\n .regex(/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/, 'Invalid BCP 47 language tag');\n\n/** Generic URI, AT Protocol `format: uri`. */\nexport const uriSchema = z.string().url();\n\n/**\n * StrongRef shape from `com.atproto.repo.strongRef` -- pins a record by both\n * AT-URI (identity) and CID (version).\n */\nexport const strongRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema,\n});\n\n/**\n * Reference to an `id.sifa.profile.skill` record by AT-URI. Mirrors\n * `id.sifa.defs#skillRef`, which carries no CID at all: skills are mutable\n * records in the same user's repo, so the reference resolves live and tracks\n * edits to the target.\n */\nexport const skillRefSchema = z.object({\n uri: atUriSchema,\n});\n\n/**\n * Reference to a record by AT-URI, with an optional CID. Mirrors\n * `id.sifa.defs#externalRecordRef`. Unlike a strongRef the CID is optional:\n * consumers resolve the AT-URI live so the reference tracks edits to the\n * target, and the CID is a best-effort integrity hint only.\n */\nexport const externalRecordRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema.optional(),\n});\n\n/**\n * Self-labels shape from `com.atproto.label.defs#selfLabels`. Modelled\n * permissively because clients rarely construct this directly; the AppView\n * handles label validation.\n */\nexport const selfLabelsSchema = z.object({\n $type: z.literal('com.atproto.label.defs#selfLabels').optional(),\n values: z.array(z.object({ val: z.string() })),\n});\n","import { z } from 'zod';\n\nimport {\n atUriSchema,\n datetimeSchema,\n didSchema,\n maxGraphemes,\n selfLabelsSchema,\n strongRefSchema,\n uriSchema,\n} from '../schemas/shared.js';\n\n/**\n * Zod schemas mirroring the canonical Standard.site lexicons that Sifa\n * consumes when rendering publication embeds.\n *\n * Canonical lexicons live at:\n * DID: did:plc:re3ebnp5v7ffagz6rb6xfei4\n * PDS: https://auriporia.us-west.host.bsky.network\n * Collection: com.atproto.lexicon.schema\n *\n * Sifa does NOT own these lexicons; we vendor the validation shapes so\n * clients can parse augmented embeds and (in Phase 4) write subscription\n * records to viewers' PDSes. Re-check against the canonical PDS on each\n * SDK release to detect schema drift.\n */\n\nconst rgbColorSchema = z.object({\n r: z.number().int().min(0).max(255),\n g: z.number().int().min(0).max(255),\n b: z.number().int().min(0).max(255),\n});\n\nexport type RgbColor = z.infer<typeof rgbColorSchema>;\n\n/** site.standard.theme.basic */\nexport const BasicThemeSchema = z.object({\n background: rgbColorSchema,\n foreground: rgbColorSchema,\n accent: rgbColorSchema,\n accentForeground: rgbColorSchema,\n});\n\nexport type BasicTheme = z.infer<typeof BasicThemeSchema>;\n\nconst blobRefSchema = z\n .object({\n $type: z.string().optional(),\n ref: z.unknown(),\n mimeType: z.string().optional(),\n size: z.number().optional(),\n })\n .passthrough();\n\n/** site.standard.publication */\nexport const StandardSitePublicationRecordSchema = z\n .object({\n url: uriSchema,\n name: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n icon: blobRefSchema.optional(),\n basicTheme: BasicThemeSchema.optional(),\n labels: selfLabelsSchema.optional(),\n preferences: z\n .object({\n showInDiscover: z.boolean().optional(),\n })\n .partial()\n .optional(),\n })\n .passthrough();\n\nexport type StandardSitePublicationRecord = z.infer<typeof StandardSitePublicationRecordSchema>;\n\nconst contributorSchema = z\n .object({\n did: didSchema,\n role: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n displayName: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n })\n .passthrough();\n\n/** site.standard.document */\nexport const StandardSiteDocumentRecordSchema = z\n .object({\n site: z.string().min(1),\n title: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n path: z.string().optional(),\n publishedAt: datetimeSchema,\n updatedAt: datetimeSchema.optional(),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n textContent: z.string().optional(),\n tags: z.array(z.string().refine(maxGraphemes(128)).max(1280)).optional(),\n coverImage: blobRefSchema.optional(),\n contributors: z.array(contributorSchema).optional(),\n bskyPostRef: strongRefSchema.optional(),\n labels: selfLabelsSchema.optional(),\n })\n .passthrough();\n\nexport type StandardSiteDocumentRecord = z.infer<typeof StandardSiteDocumentRecordSchema>;\n\n/**\n * site.standard.graph.subscription\n *\n * Authored by a viewer to declare a subscription to a publication. Used\n * by Phase 4 inline-subscribe path. Record key is a TID; the AT-URI\n * shape is `at://<viewer-did>/site.standard.graph.subscription/<rkey>`.\n */\nexport const StandardSiteSubscriptionRecordSchema = z.object({\n publication: atUriSchema,\n createdAt: datetimeSchema.optional(),\n});\n\nexport type StandardSiteSubscriptionRecord = z.infer<typeof StandardSiteSubscriptionRecordSchema>;\n\n/**\n * site.standard.graph.recommend\n *\n * Document-level \"like\" record. Same scope as subscription via the\n * site.standard.authSocial OAuth permission-set, so Phase 4 unlocks\n * inline likes alongside inline subscribes.\n */\nexport const StandardSiteRecommendRecordSchema = z.object({\n document: atUriSchema,\n createdAt: datetimeSchema,\n});\n\nexport type StandardSiteRecommendRecord = z.infer<typeof StandardSiteRecommendRecordSchema>;\n","/**\n * Publisher allowlist for the Standard.site rich embed.\n *\n * Mirrors `bluesky-social/social-app`\n * `src/components/Post/Embed/StandardSiteEmbed/publishers.ts`. The only\n * gate this provides is which publishers get the highlighted\n * \"Subscribe on {Publisher}\" CTA with a publisher icon vs the plain\n * \"View publication\" fallback — any Standard.site embed renders\n * regardless of allowlist membership.\n *\n * Sync policy: review additions against the upstream bsky list weekly;\n * Singi Labs reviews before merge.\n */\n\nexport interface Publisher {\n /** Lowercase host. Subdomains are matched via `hostMatches`. */\n host: string;\n /** Human-facing publisher name shown in CTA copy. */\n name: string;\n /**\n * Stable identifier used by clients to look up brand icons. The SDK\n * does not ship icon assets — sifa-web maintains them keyed by this\n * id so React Native vs web rendering can diverge.\n */\n iconKey: 'leaflet' | 'pckt' | 'offprint';\n}\n\nexport const STANDARD_SITE_PUBLISHERS: readonly Publisher[] = Object.freeze([\n { host: 'leaflet.pub', name: 'Leaflet', iconKey: 'leaflet' },\n { host: 'pckt.blog', name: 'pckt', iconKey: 'pckt' },\n { host: 'offprint.app', name: 'Offprint', iconKey: 'offprint' },\n]);\n\n/**\n * Returns true when `host` exactly matches `target` or is a subdomain of\n * it. Hosts are expected to be lowercase already.\n */\nexport function hostMatches(host: string, target: string): boolean {\n return host === target || host.endsWith('.' + target);\n}\n\nfunction hostFromUri(uri: string | undefined | null): string | null {\n if (!uri) return null;\n try {\n return new URL(uri).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * Match a publisher by host. Returns the publisher when `host` is on the\n * allowlist (exact or subdomain match), null otherwise. Host should be\n * lowercase.\n */\nexport function matchPublisherByHost(host: string | null | undefined): Publisher | null {\n if (!host) return null;\n const lower = host.toLowerCase();\n return STANDARD_SITE_PUBLISHERS.find((p) => hostMatches(lower, p.host)) ?? null;\n}\n\n/**\n * Match a publisher by URI. Convenience wrapper around\n * `matchPublisherByHost` for callers that have a publication URL handy.\n */\nexport function matchPublisherByUri(uri: string | undefined | null): Publisher | null {\n return matchPublisherByHost(hostFromUri(uri));\n}\n","import type { BasicTheme } from './schemas.js';\n\n/**\n * Publication metadata embedded in the augmented activity view by\n * sifa-api when an external link card matches an indexed publication.\n *\n * `uri` is the publication's AT-URI (e.g.\n * `at://did:plc:abc/site.standard.publication/xyz`). `theme` carries\n * the publication's own colors when supplied — clients should apply a\n * WCAG min-contrast fallback before honoring them.\n *\n * `icon` is left as `unknown` here because its representation depends\n * on the surface: blob ref on the wire, CDN URL after resolution.\n */\nexport interface PublicationSource {\n uri: string;\n title: string;\n icon?: unknown;\n theme?: BasicTheme;\n}\n\n/**\n * Shape attached to activity items whose external link card URL matches\n * a Standard.site record. Mirrors the relevant fields of bsky's\n * `StandardSiteEmbed` view so a sifa-web renderer can be near-1:1.\n *\n * `associatedRefs` carries AT-URIs the client may use in Phase 4 to\n * query subscription state for the viewer.\n */\nexport interface StandardSiteEmbedView {\n uri: string;\n source: PublicationSource;\n associatedRefs: { uri: string }[];\n createdAt?: string;\n readingTime?: number;\n}\n\nexport const STANDARD_SITE_PUBLICATION_NSID = 'site.standard.publication' as const;\nexport const STANDARD_SITE_DOCUMENT_NSID = 'site.standard.document' as const;\nexport const STANDARD_SITE_SUBSCRIPTION_NSID = 'site.standard.graph.subscription' as const;\nexport const STANDARD_SITE_RECOMMEND_NSID = 'site.standard.graph.recommend' as const;\n\n/**\n * Pre-defined OAuth permission-set the consumer apps request to write\n * subscription + recommend records on behalf of the viewer. The actual\n * permission-set is published under\n * `at://did:plc:re3ebnp5v7ffagz6rb6xfei4/com.atproto.lexicon.schema/site.standard.authSocial`.\n */\nexport const STANDARD_SITE_AUTH_SOCIAL_NSID = 'site.standard.authSocial' as const;\n\nconst COLLECTION_SET = new Set<string>([\n STANDARD_SITE_PUBLICATION_NSID,\n STANDARD_SITE_DOCUMENT_NSID,\n STANDARD_SITE_SUBSCRIPTION_NSID,\n STANDARD_SITE_RECOMMEND_NSID,\n]);\n\n/**\n * True when the AT-URI's collection segment is a Standard.site\n * collection. Useful for detecting Standard.site embed augmentation on\n * arbitrary activity items.\n */\nexport function isStandardSiteAtUri(uri: string): boolean {\n if (!uri.startsWith('at://')) return false;\n const segments = uri.slice('at://'.length).split('/');\n const collection = segments[1];\n return collection !== undefined && COLLECTION_SET.has(collection);\n}\n\n/**\n * True when any `associatedRefs[].uri` is a Standard.site collection\n * AT-URI. The bsky client uses the same check to gate the\n * `StandardSiteEmbed` renderer.\n */\nexport function hasStandardSiteAssociatedRef(refs: { uri: string }[] | undefined): boolean {\n if (!refs) return false;\n return refs.some((r) => isStandardSiteAtUri(r.uri));\n}\n"]}
1
+ {"version":3,"sources":["../../src/schemas/shared.ts","../../src/publishing/schemas.ts","../../src/publishing/registry.ts","../../src/publishing/linkblog.ts","../../src/publishing/types.ts"],"names":["z"],"mappings":"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","file":"index.js","sourcesContent":["import { z } from 'zod';\n\n/**\n * Grapheme-aware refinement matching the AT Protocol lexicon `maxGraphemes`\n * constraint. JS strings are sequences of UTF-16 code units, but lexicon\n * `maxGraphemes` counts user-perceived characters (grapheme clusters), so\n * emoji sequences, regional indicators, ZWJ joins, and combining marks all\n * count as one unit each. We use `Intl.Segmenter` to enforce this correctly.\n */\nexport function maxGraphemes(max: number) {\n return (value: string): boolean => {\n const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });\n let count = 0;\n for (const _ of segmenter.segment(value)) {\n count++;\n if (count > max) return false;\n }\n return true;\n };\n}\n\n/** Decentralized identifier, AT Protocol `format: did`. */\nexport const didSchema = z.string().regex(/^did:[a-z]+:[a-zA-Z0-9._:%-]+$/, 'Invalid DID');\n\n/** RFC 3339 datetime with timezone offset, AT Protocol `format: datetime`. */\nexport const datetimeSchema = z.string().datetime({ offset: true });\n\n/**\n * Freeform calendar date for user-facing profile dates (start/end, issued,\n * completed, awarded, published, etc.). Accepts a bare year, year-month,\n * year-month-day, or a full datetime (so a legacy record's RFC-3339 date maps\n * through on backfill without loss). NOT strict `datetime` -- users typically\n * remember only month/year, and LinkedIn-importer writes carry partial dates,\n * so the lexicons document these as freeform `YYYY-MM` / `YYYY-MM-DD`. Record\n * metadata like `createdAt` stays on `datetimeSchema`.\n */\nexport const partialDateSchema = z\n .string()\n .regex(/^\\d{4}(-\\d{2}(-\\d{2}(T.+)?)?)?$/, 'Expected YYYY, YYYY-MM, YYYY-MM-DD, or a datetime');\n\n/** Generic AT-URI, AT Protocol `format: at-uri`. */\nexport const atUriSchema = z.string().regex(/^at:\\/\\/[^\\s]+$/, 'Invalid AT-URI');\n\n/** Content identifier, AT Protocol `format: cid`. Loose validation -- accepts CIDv0 and CIDv1. */\nexport const cidSchema = z\n .string()\n .regex(/^(Qm[1-9A-HJ-NP-Za-km-z]{44}|b[A-Za-z0-9+/=]+)$/, 'Invalid CID');\n\n/** BCP 47 language tag, AT Protocol `format: language`. */\nexport const languageTagSchema = z\n .string()\n .regex(/^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/, 'Invalid BCP 47 language tag');\n\n/** Generic URI, AT Protocol `format: uri`. */\nexport const uriSchema = z.string().url();\n\n/**\n * StrongRef shape from `com.atproto.repo.strongRef` -- pins a record by both\n * AT-URI (identity) and CID (version).\n */\nexport const strongRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema,\n});\n\n/**\n * Reference to an `id.sifa.profile.skill` record by AT-URI. Mirrors\n * `id.sifa.defs#skillRef`, which carries no CID at all: skills are mutable\n * records in the same user's repo, so the reference resolves live and tracks\n * edits to the target.\n */\nexport const skillRefSchema = z.object({\n uri: atUriSchema,\n});\n\n/**\n * Reference to a record by AT-URI, with an optional CID. Mirrors\n * `id.sifa.defs#externalRecordRef`. Unlike a strongRef the CID is optional:\n * consumers resolve the AT-URI live so the reference tracks edits to the\n * target, and the CID is a best-effort integrity hint only.\n */\nexport const externalRecordRefSchema = z.object({\n uri: atUriSchema,\n cid: cidSchema.optional(),\n});\n\n/**\n * Self-labels shape from `com.atproto.label.defs#selfLabels`. Modelled\n * permissively because clients rarely construct this directly; the AppView\n * handles label validation.\n */\nexport const selfLabelsSchema = z.object({\n $type: z.literal('com.atproto.label.defs#selfLabels').optional(),\n values: z.array(z.object({ val: z.string() })),\n});\n","import { z } from 'zod';\n\nimport {\n atUriSchema,\n datetimeSchema,\n didSchema,\n maxGraphemes,\n selfLabelsSchema,\n strongRefSchema,\n uriSchema,\n} from '../schemas/shared.js';\n\n/**\n * Zod schemas mirroring the canonical Standard.site lexicons that Sifa\n * consumes when rendering publication embeds.\n *\n * Canonical lexicons live at:\n * DID: did:plc:re3ebnp5v7ffagz6rb6xfei4\n * PDS: https://auriporia.us-west.host.bsky.network\n * Collection: com.atproto.lexicon.schema\n *\n * Sifa does NOT own these lexicons; we vendor the validation shapes so\n * clients can parse augmented embeds and (in Phase 4) write subscription\n * records to viewers' PDSes. Re-check against the canonical PDS on each\n * SDK release to detect schema drift.\n */\n\nconst rgbColorSchema = z.object({\n r: z.number().int().min(0).max(255),\n g: z.number().int().min(0).max(255),\n b: z.number().int().min(0).max(255),\n});\n\nexport type RgbColor = z.infer<typeof rgbColorSchema>;\n\n/** site.standard.theme.basic */\nexport const BasicThemeSchema = z.object({\n background: rgbColorSchema,\n foreground: rgbColorSchema,\n accent: rgbColorSchema,\n accentForeground: rgbColorSchema,\n});\n\nexport type BasicTheme = z.infer<typeof BasicThemeSchema>;\n\nconst blobRefSchema = z\n .object({\n $type: z.string().optional(),\n ref: z.unknown(),\n mimeType: z.string().optional(),\n size: z.number().optional(),\n })\n .passthrough();\n\n/** site.standard.publication */\nexport const StandardSitePublicationRecordSchema = z\n .object({\n url: uriSchema,\n name: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n icon: blobRefSchema.optional(),\n basicTheme: BasicThemeSchema.optional(),\n labels: selfLabelsSchema.optional(),\n preferences: z\n .object({\n showInDiscover: z.boolean().optional(),\n })\n .partial()\n .optional(),\n })\n .passthrough();\n\nexport type StandardSitePublicationRecord = z.infer<typeof StandardSitePublicationRecordSchema>;\n\nconst contributorSchema = z\n .object({\n did: didSchema,\n role: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n displayName: z.string().refine(maxGraphemes(100)).max(1000).optional(),\n })\n .passthrough();\n\n/** site.standard.document */\nexport const StandardSiteDocumentRecordSchema = z\n .object({\n site: z.string().min(1),\n title: z.string().min(1).refine(maxGraphemes(500)).max(5000),\n path: z.string().optional(),\n publishedAt: datetimeSchema,\n updatedAt: datetimeSchema.optional(),\n description: z.string().refine(maxGraphemes(3000)).max(30000).optional(),\n textContent: z.string().optional(),\n tags: z.array(z.string().refine(maxGraphemes(128)).max(1280)).optional(),\n coverImage: blobRefSchema.optional(),\n contributors: z.array(contributorSchema).optional(),\n bskyPostRef: strongRefSchema.optional(),\n labels: selfLabelsSchema.optional(),\n })\n .passthrough();\n\nexport type StandardSiteDocumentRecord = z.infer<typeof StandardSiteDocumentRecordSchema>;\n\n/**\n * site.standard.graph.subscription\n *\n * Authored by a viewer to declare a subscription to a publication. Used\n * by Phase 4 inline-subscribe path. Record key is a TID; the AT-URI\n * shape is `at://<viewer-did>/site.standard.graph.subscription/<rkey>`.\n */\nexport const StandardSiteSubscriptionRecordSchema = z.object({\n publication: atUriSchema,\n createdAt: datetimeSchema.optional(),\n});\n\nexport type StandardSiteSubscriptionRecord = z.infer<typeof StandardSiteSubscriptionRecordSchema>;\n\n/**\n * site.standard.graph.recommend\n *\n * Document-level \"like\" record. Same scope as subscription via the\n * site.standard.authSocial OAuth permission-set, so Phase 4 unlocks\n * inline likes alongside inline subscribes.\n */\nexport const StandardSiteRecommendRecordSchema = z.object({\n document: atUriSchema,\n createdAt: datetimeSchema,\n});\n\nexport type StandardSiteRecommendRecord = z.infer<typeof StandardSiteRecommendRecordSchema>;\n","/**\n * Publisher allowlist for the Standard.site rich embed.\n *\n * Mirrors `bluesky-social/social-app`\n * `src/components/Post/Embed/StandardSiteEmbed/publishers.ts`. The only\n * gate this provides is which publishers get the highlighted\n * \"Subscribe on {Publisher}\" CTA with a publisher icon vs the plain\n * \"View publication\" fallback — any Standard.site embed renders\n * regardless of allowlist membership.\n *\n * Sync policy: review additions against the upstream bsky list weekly;\n * Singi Labs reviews before merge.\n */\n\nexport interface Publisher {\n /** Lowercase host. Subdomains are matched via `hostMatches`. */\n host: string;\n /** Human-facing publisher name shown in CTA copy. */\n name: string;\n /**\n * Stable identifier used by clients to look up brand icons. The SDK\n * does not ship icon assets — sifa-web maintains them keyed by this\n * id so React Native vs web rendering can diverge.\n */\n iconKey: 'leaflet' | 'pckt' | 'offprint';\n}\n\nexport const STANDARD_SITE_PUBLISHERS: readonly Publisher[] = Object.freeze([\n { host: 'leaflet.pub', name: 'Leaflet', iconKey: 'leaflet' },\n { host: 'pckt.blog', name: 'pckt', iconKey: 'pckt' },\n { host: 'offprint.app', name: 'Offprint', iconKey: 'offprint' },\n]);\n\n/**\n * Returns true when `host` exactly matches `target` or is a subdomain of\n * it. Hosts are expected to be lowercase already.\n */\nexport function hostMatches(host: string, target: string): boolean {\n return host === target || host.endsWith('.' + target);\n}\n\nfunction hostFromUri(uri: string | undefined | null): string | null {\n if (!uri) return null;\n try {\n return new URL(uri).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * Match a publisher by host. Returns the publisher when `host` is on the\n * allowlist (exact or subdomain match), null otherwise. Host should be\n * lowercase.\n */\nexport function matchPublisherByHost(host: string | null | undefined): Publisher | null {\n if (!host) return null;\n const lower = host.toLowerCase();\n return STANDARD_SITE_PUBLISHERS.find((p) => hostMatches(lower, p.host)) ?? null;\n}\n\n/**\n * Match a publisher by URI. Convenience wrapper around\n * `matchPublisherByHost` for callers that have a publication URL handy.\n */\nexport function matchPublisherByUri(uri: string | undefined | null): Publisher | null {\n return matchPublisherByHost(hostFromUri(uri));\n}\n","/**\n * Linkblog-share detection for the Standard.site document family.\n *\n * A \"linkblog\" (e.g. Skyreader) writes a `site.standard.document` to the\n * user's PDS for every *external* article they share. Those documents are\n * valid Standard.site posts, so they surface in Sifa's activity stream and\n * profile portfolio as if the user authored them — but the user only shared\n * someone else's writing (sifa-workspace#351).\n *\n * These predicates identify such shares so callers can exclude them, while\n * leaving genuinely authored Standard.site posts (Leaflet, pckt, Offprint\n * essays) untouched.\n *\n * Signals, most to least reliable:\n * 1. The Skyreader provenance marker on the document (`skyreaderLinkblog`).\n * 2. The document's parent publication is itself a linkblog (the marker\n * lives on the publication even when older documents lack their own).\n * Callers resolve the publication and pass `publicationIsLinkblog`.\n * 3. A `links` entry with `rel: \"repost\"` — a quote-reshare, inherently\n * not the user's own writing.\n * 4. Heuristic: a `links` entry with `rel: \"related\"` pointing to an\n * external host different from the document's own publication host —\n * the document's subject is an off-site article. Only applied when the\n * caller supplies the resolved `publicationHost`, so an authored post\n * that merely cites an external resource is never hidden on a guess.\n */\n\n/**\n * Discovery marker Skyreader stamps on every linkblog publication and on\n * link-share documents. Value is a fixed URL, not a per-user value.\n */\nexport const SKYREADER_LINKBLOG_MARKER_URL = 'https://skyreader.app/linkblog';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\n/** A non-empty `skyreaderLinkblog` marker field. */\nfunction hasLinkblogMarker(record: Record<string, unknown>): boolean {\n const marker = record.skyreaderLinkblog;\n return typeof marker === 'string' && marker.length > 0;\n}\n\nfunction hostOf(uri: string): string | null {\n try {\n return new URL(uri).host.toLowerCase();\n } catch {\n return null;\n }\n}\n\n/**\n * True when a `site.standard.publication` record is a linkblog rather than an\n * authored publication — i.e. it carries the Skyreader provenance marker.\n */\nexport function isLinkblogPublication(record: unknown): boolean {\n return isRecord(record) && hasLinkblogMarker(record);\n}\n\nexport interface LinkblogShareOptions {\n /**\n * True when the document's parent publication has been resolved and is\n * itself a linkblog (see {@link isLinkblogPublication}). Catches shares\n * whose document predates the per-document marker.\n */\n publicationIsLinkblog?: boolean;\n /**\n * Resolved host of the document's parent publication, enabling the\n * external-subject heuristic. Omit when the publication (hence its host)\n * could not be resolved — the heuristic then stays off.\n */\n publicationHost?: string;\n}\n\n/**\n * True when a `site.standard.document` is a shared/linkblogged external\n * article rather than the user's own writing. See the module comment for the\n * signal order.\n */\nexport function isLinkblogShareDocument(record: unknown, opts: LinkblogShareOptions = {}): boolean {\n if (!isRecord(record)) return false;\n\n // 1. Provenance marker on the document itself.\n if (hasLinkblogMarker(record)) return true;\n\n // 2. Parent publication is a linkblog (caller-resolved).\n if (opts.publicationIsLinkblog === true) return true;\n\n const links = record.links;\n if (!Array.isArray(links)) return false;\n\n const pubHost = opts.publicationHost?.toLowerCase();\n for (const link of links) {\n if (!isRecord(link) || typeof link.rel !== 'string') continue;\n\n // 3. Reshare of another record.\n if (link.rel === 'repost') return true;\n\n // 4. External-subject heuristic (only with a known publication host).\n if (link.rel === 'related' && pubHost && typeof link.uri === 'string') {\n const linkHost = hostOf(link.uri);\n if (linkHost && linkHost !== pubHost) return true;\n }\n }\n\n return false;\n}\n","import type { BasicTheme } from './schemas.js';\n\n/**\n * Publication metadata embedded in the augmented activity view by\n * sifa-api when an external link card matches an indexed publication.\n *\n * `uri` is the publication's AT-URI (e.g.\n * `at://did:plc:abc/site.standard.publication/xyz`). `theme` carries\n * the publication's own colors when supplied — clients should apply a\n * WCAG min-contrast fallback before honoring them.\n *\n * `icon` is left as `unknown` here because its representation depends\n * on the surface: blob ref on the wire, CDN URL after resolution.\n */\nexport interface PublicationSource {\n uri: string;\n title: string;\n icon?: unknown;\n theme?: BasicTheme;\n}\n\n/**\n * Shape attached to activity items whose external link card URL matches\n * a Standard.site record. Mirrors the relevant fields of bsky's\n * `StandardSiteEmbed` view so a sifa-web renderer can be near-1:1.\n *\n * `associatedRefs` carries AT-URIs the client may use in Phase 4 to\n * query subscription state for the viewer.\n */\nexport interface StandardSiteEmbedView {\n uri: string;\n source: PublicationSource;\n associatedRefs: { uri: string }[];\n createdAt?: string;\n readingTime?: number;\n}\n\nexport const STANDARD_SITE_PUBLICATION_NSID = 'site.standard.publication' as const;\nexport const STANDARD_SITE_DOCUMENT_NSID = 'site.standard.document' as const;\nexport const STANDARD_SITE_SUBSCRIPTION_NSID = 'site.standard.graph.subscription' as const;\nexport const STANDARD_SITE_RECOMMEND_NSID = 'site.standard.graph.recommend' as const;\n\n/**\n * Pre-defined OAuth permission-set the consumer apps request to write\n * subscription + recommend records on behalf of the viewer. The actual\n * permission-set is published under\n * `at://did:plc:re3ebnp5v7ffagz6rb6xfei4/com.atproto.lexicon.schema/site.standard.authSocial`.\n */\nexport const STANDARD_SITE_AUTH_SOCIAL_NSID = 'site.standard.authSocial' as const;\n\nconst COLLECTION_SET = new Set<string>([\n STANDARD_SITE_PUBLICATION_NSID,\n STANDARD_SITE_DOCUMENT_NSID,\n STANDARD_SITE_SUBSCRIPTION_NSID,\n STANDARD_SITE_RECOMMEND_NSID,\n]);\n\n/**\n * True when the AT-URI's collection segment is a Standard.site\n * collection. Useful for detecting Standard.site embed augmentation on\n * arbitrary activity items.\n */\nexport function isStandardSiteAtUri(uri: string): boolean {\n if (!uri.startsWith('at://')) return false;\n const segments = uri.slice('at://'.length).split('/');\n const collection = segments[1];\n return collection !== undefined && COLLECTION_SET.has(collection);\n}\n\n/**\n * True when any `associatedRefs[].uri` is a Standard.site collection\n * AT-URI. The bsky client uses the same check to gate the\n * `StandardSiteEmbed` renderer.\n */\nexport function hasStandardSiteAssociatedRef(refs: { uri: string }[] | undefined): boolean {\n if (!refs) return false;\n return refs.some((r) => isStandardSiteAtUri(r.uri));\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@singi-labs/sifa-sdk",
3
- "version": "0.12.85",
3
+ "version": "0.12.86",
4
4
  "description": "Sifa SDK — public client library for the Sifa AppView on AT Protocol. Shared by sifa-web and sifa-app.",
5
5
  "license": "MIT",
6
6
  "type": "module",