@happyvertical/smrt-social 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/AGENTS.md +18 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +94 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/collections/index.d.ts +5 -0
  8. package/dist/collections/index.d.ts.map +1 -0
  9. package/dist/collections/oauth-state-collection.d.ts +9 -0
  10. package/dist/collections/oauth-state-collection.d.ts.map +1 -0
  11. package/dist/collections/social-account-collection.d.ts +9 -0
  12. package/dist/collections/social-account-collection.d.ts.map +1 -0
  13. package/dist/collections/social-post-analytics-snapshot-collection.d.ts +18 -0
  14. package/dist/collections/social-post-analytics-snapshot-collection.d.ts.map +1 -0
  15. package/dist/collections/social-post-collection.d.ts +32 -0
  16. package/dist/collections/social-post-collection.d.ts.map +1 -0
  17. package/dist/index.d.ts +6 -0
  18. package/dist/index.d.ts.map +1 -0
  19. package/dist/index.js +824 -0
  20. package/dist/index.js.map +1 -0
  21. package/dist/manifest.json +3363 -0
  22. package/dist/oauth-state.d.ts +127 -0
  23. package/dist/oauth-state.d.ts.map +1 -0
  24. package/dist/server.d.ts +18 -0
  25. package/dist/server.d.ts.map +1 -0
  26. package/dist/server.js +106 -0
  27. package/dist/server.js.map +1 -0
  28. package/dist/smrt-knowledge.json +1398 -0
  29. package/dist/social-account.d.ts +270 -0
  30. package/dist/social-account.d.ts.map +1 -0
  31. package/dist/social-post-analytics-snapshot.d.ts +40 -0
  32. package/dist/social-post-analytics-snapshot.d.ts.map +1 -0
  33. package/dist/social-post.d.ts +258 -0
  34. package/dist/social-post.d.ts.map +1 -0
  35. package/dist/svelte/components/SocialAccountSettings.svelte +243 -0
  36. package/dist/svelte/components/SocialAccountSettings.svelte.d.ts +15 -0
  37. package/dist/svelte/components/SocialAccountSettings.svelte.d.ts.map +1 -0
  38. package/dist/svelte/i18n.d.ts +9 -0
  39. package/dist/svelte/i18n.d.ts.map +1 -0
  40. package/dist/svelte/i18n.js +15 -0
  41. package/dist/svelte/index.d.ts +6 -0
  42. package/dist/svelte/index.d.ts.map +1 -0
  43. package/dist/svelte/index.js +2 -0
  44. package/dist/svelte/types.d.ts +16 -0
  45. package/dist/svelte/types.d.ts.map +1 -0
  46. package/dist/svelte/types.js +1 -0
  47. package/package.json +88 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/__smrt-register__.ts","../src/oauth-state.ts","../src/collections/oauth-state-collection.ts","../src/social-account.ts","../src/collections/social-account-collection.ts","../src/social-post.ts","../src/social-post-analytics-snapshot.ts","../src/collections/social-post-analytics-snapshot-collection.ts","../src/collections/social-post-collection.ts"],"sourcesContent":["/**\n * Self-registers this package's build-time manifest before any @smrt() decorator\n * in the package fires. Fixes issue #1132: in consumer runtimes (tsx, SvelteKit\n * SSR, plain `vite dev`) the decorator's synchronous manifest lookup previously\n * missed because no step populated the global manifest cache — classes got\n * registered with zero fields and `save()` / `toJSON()` silently dropped every\n * declared property.\n *\n * Import this module as the first statement in `src/index.ts` so its top-level\n * side effect runs ahead of any class module's @smrt() decorator.\n *\n * Silent no-op in dev/test, where the vitest plugin already populates manifests\n * via a different path. Only needs to succeed in the published dist output.\n *\n * @see https://github.com/happyvertical/smrt/issues/1132\n */\nimport { ObjectRegistry } from '@happyvertical/smrt-core';\n\n// `new URL('./manifest.json', import.meta.url)` resolves at runtime to the\n// manifest sitting next to this module's compiled output. Vite warns at build\n// time that it cannot pre-resolve the URL; that is the intended behavior —\n// the URL must resolve to dist/manifest.json at runtime, not be inlined.\nObjectRegistry.registerPackageManifest(\n new URL('./manifest.json', import.meta.url),\n);\n","/**\n * OAuth State Model\n *\n * Manages temporary OAuth state for connecting social accounts.\n * Used to verify callback requests and prevent CSRF attacks.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { SocialPlatformType } from './social-account.js';\n\n/**\n * OAuth state creation options\n */\nexport interface OAuthStateOptions extends SmrtObjectOptions {\n /**\n * Platform being connected\n */\n platform?: SocialPlatformType;\n\n /**\n * CSRF state token\n */\n state?: string;\n\n /**\n * PKCE code verifier (for platforms that require it)\n */\n codeVerifier?: string | null;\n\n /**\n * Redirect URI used in the OAuth request\n */\n redirectUri?: string;\n\n /**\n * Requested OAuth scopes\n */\n scopes?: string[];\n\n /**\n * When this state expires\n */\n expiresAt?: Date;\n\n /**\n * Tenant ID for multi-tenant isolation\n */\n tenantId?: string | null;\n}\n\n/**\n * Temporary OAuth state for social account connection\n *\n * OAuthState stores temporary data during the OAuth flow to:\n * - Verify callback requests match initiated requests (CSRF protection)\n * - Store PKCE code verifier for code exchange\n * - Track redirect URI and scopes for verification\n *\n * These records should be cleaned up after successful connection\n * or after expiration.\n *\n * @example\n * ```typescript\n * import { OAuthState } from '@happyvertical/smrt-social';\n *\n * // Create state when initiating OAuth\n * const state = new OAuthState({\n * platform: 'youtube',\n * state: crypto.randomUUID(),\n * codeVerifier: generatePKCEVerifier(),\n * redirectUri: 'https://app.example.com/oauth/callback',\n * scopes: ['youtube.upload', 'youtube.readonly'],\n * expiresAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes\n * });\n * await state.save();\n *\n * // After successful callback, delete the state\n * await state.delete();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: ['list', 'get', 'create', 'delete'],\n },\n mcp: false,\n cli: true,\n})\nexport class OAuthState extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Platform being connected\n */\n platform: SocialPlatformType = 'youtube';\n\n /**\n * CSRF state token\n * This is sent to the OAuth provider and verified on callback\n */\n state: string = '';\n\n /**\n * PKCE code verifier\n * Required for platforms using PKCE (YouTube, etc.)\n */\n codeVerifier: string | null = null;\n\n /**\n * Redirect URI used in the OAuth request\n * Must match on callback for verification\n */\n redirectUri: string = '';\n\n /**\n * Requested OAuth scopes\n */\n scopes: string[] = [];\n\n /**\n * When this state expires\n * States should be short-lived (10 minutes typical)\n */\n expiresAt: Date = new Date(Date.now() + 10 * 60 * 1000);\n\n constructor(options: OAuthStateOptions = {}) {\n super(options);\n\n if (options.platform !== undefined) this.platform = options.platform;\n if (options.state !== undefined) this.state = options.state;\n if (options.codeVerifier !== undefined)\n this.codeVerifier = options.codeVerifier;\n if (options.redirectUri !== undefined)\n this.redirectUri = options.redirectUri;\n if (options.scopes !== undefined) this.scopes = options.scopes;\n if (options.expiresAt !== undefined) this.expiresAt = options.expiresAt;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n\n /**\n * Check if the state has expired\n */\n get isExpired(): boolean {\n return Date.now() >= this.expiresAt.getTime();\n }\n\n /**\n * Check if the state is still valid\n */\n get isValid(): boolean {\n return !this.isExpired && this.state.length > 0;\n }\n\n /**\n * Verify a callback state matches this record\n */\n verifyState(callbackState: string): boolean {\n return this.isValid && this.state === callbackState;\n }\n\n /**\n * Generate a new random state token\n */\n static generateState(): string {\n return crypto.randomUUID();\n }\n\n /**\n * Generate a PKCE code verifier\n * Returns a 43-128 character random string\n */\n static generateCodeVerifier(): string {\n const array = new Uint8Array(32);\n crypto.getRandomValues(array);\n return Array.from(array, (byte) => byte.toString(16).padStart(2, '0')).join(\n '',\n );\n }\n\n /**\n * Generate PKCE code challenge from verifier (S256 method)\n * Note: This requires async crypto operations\n */\n static async generateCodeChallenge(verifier: string): Promise<string> {\n const encoder = new TextEncoder();\n const data = encoder.encode(verifier);\n const digest = await crypto.subtle.digest('SHA-256', data);\n const base64 = btoa(String.fromCharCode(...new Uint8Array(digest)));\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=/g, '');\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { OAuthState } from '../oauth-state.js';\n\nexport class OAuthStateCollection extends SmrtCollection<OAuthState> {\n static readonly _itemClass = OAuthState;\n\n async findByState(state: string): Promise<OAuthState | null> {\n return this.get({ state });\n }\n\n async findExpired(now: Date = new Date()): Promise<OAuthState[]> {\n return this.list({\n where: { 'expiresAt <=': now },\n orderBy: 'expiresAt ASC',\n });\n }\n\n async deleteExpired(now: Date = new Date()): Promise<number> {\n const expired = await this.findExpired(now);\n await Promise.all(expired.map((state) => state.delete()));\n return expired.length;\n }\n}\n","/**\n * Social Account Model\n *\n * Manages connected social media platform accounts for publishing.\n * Stores OAuth credentials and account settings.\n */\n\nimport { randomUUID } from 'node:crypto';\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { SecretService } from '@happyvertical/smrt-secrets';\nimport {\n getCurrentTenant,\n TenantScoped,\n tenantId,\n withTenant,\n} from '@happyvertical/smrt-tenancy';\nimport { getSocialPlatformSetup } from './server.js';\n\n/**\n * Supported social platforms\n */\nexport type SocialPlatformType =\n | 'youtube'\n | 'threads'\n | 'x'\n | 'bluesky'\n | 'facebook';\n\n/**\n * Account connection status\n */\nexport type AccountStatus =\n | 'connected'\n | 'disconnected'\n | 'expired'\n | 'missing_permissions'\n | 'error';\n\n/**\n * Link behavior for posts with links\n */\nexport type LinkBehavior =\n | 'description'\n | 'inline'\n | 'attachment'\n | 'reply'\n | 'none';\n\n/**\n * Controls how far publish operations are allowed to go.\n */\nexport type PublishMode =\n | 'dry_run'\n | 'stage_remote'\n | 'private_or_scheduled'\n | 'public';\n\n/**\n * Social account creation options\n */\nexport interface SocialAccountOptions extends SmrtObjectOptions {\n /**\n * Human-readable name for the account\n */\n name?: string;\n\n /**\n * Social platform type\n */\n platform?: SocialPlatformType;\n\n /**\n * Platform-specific user ID\n */\n platformUserId?: string | null;\n\n /**\n * Platform username/handle\n */\n platformUsername?: string | null;\n\n /**\n * Profile URL on the platform\n */\n platformUrl?: string | null;\n\n /**\n * Deprecated raw OAuth access token\n */\n accessToken?: string | null;\n\n /**\n * Deprecated raw OAuth refresh token\n */\n refreshToken?: string | null;\n\n /**\n * Secret name containing the full platform credential payload\n */\n credentialSecretId?: string | null;\n\n /**\n * Secret name containing the OAuth access token\n */\n accessTokenSecretName?: string | null;\n\n /**\n * Secret name containing the OAuth refresh token\n */\n refreshTokenSecretName?: string | null;\n\n /**\n * Token expiration time\n */\n tokenExpiresAt?: Date | null;\n\n /**\n * Whether the account is active\n * @default true\n */\n isActive?: boolean;\n\n /**\n * Default hashtags to add to posts\n */\n defaultHashtags?: string[];\n\n /**\n * Granted OAuth scopes or platform permissions\n */\n scopes?: string[];\n\n /**\n * Required permissions that still need approval/granting\n */\n missingPermissions?: string[];\n\n /**\n * How to handle links in posts\n * @default 'description'\n */\n linkBehavior?: LinkBehavior;\n\n /**\n * Safety mode for publish operations.\n * @default 'dry_run'\n */\n publishMode?: PublishMode;\n\n /**\n * Separate latch required before public publishing can happen.\n * @default false\n */\n publicPublishingAllowed?: boolean;\n\n /**\n * Account connection status\n * @default 'connected'\n */\n status?: AccountStatus;\n\n /**\n * Error message if status is 'error'\n */\n errorMessage?: string | null;\n\n /**\n * Tenant ID for multi-tenant isolation\n */\n tenantId?: string | null;\n}\n\n/**\n * Connected social media account for publishing\n *\n * SocialAccount represents a connected social platform account\n * with OAuth credentials and publishing settings. Accounts can\n * be used to publish content to multiple platforms.\n *\n * @example\n * ```typescript\n * import { SocialAccount } from '@happyvertical/smrt-social';\n *\n * const account = new SocialAccount({\n * name: 'Bentley News YouTube',\n * platform: 'youtube',\n * platformUserId: 'UC...',\n * platformUsername: 'Bentley News',\n * accessToken: '...encrypted...',\n * refreshToken: '...encrypted...',\n * tokenExpiresAt: new Date('2026-02-25'),\n * defaultHashtags: ['news', 'local', 'bentley'],\n * linkBehavior: 'description',\n * });\n * await account.save();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: ['list', 'get', 'create', 'update', 'delete'],\n },\n mcp: {\n include: ['list', 'get'],\n },\n cli: true,\n})\nexport class SocialAccount extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Human-readable name for the account\n */\n name: string = '';\n\n /**\n * Social platform type\n */\n platform: SocialPlatformType = 'youtube';\n\n /**\n * Platform-specific user ID\n */\n platformUserId: string | null = null;\n\n /**\n * Platform username/handle\n */\n platformUsername: string | null = null;\n\n /**\n * Profile URL on the platform\n */\n platformUrl: string | null = null;\n\n /**\n * Deprecated raw OAuth access token.\n * Prefer credentialSecretId/accessTokenSecretName.\n */\n accessToken: string | null = null;\n\n /**\n * Deprecated raw OAuth refresh token.\n * Prefer credentialSecretId/refreshTokenSecretName.\n */\n refreshToken: string | null = null;\n\n /**\n * Secret name containing the complete platform credential payload.\n */\n credentialSecretId: string | null = null;\n\n /**\n * Secret name containing only the access token.\n */\n accessTokenSecretName: string | null = null;\n\n /**\n * Secret name containing only the refresh token.\n */\n refreshTokenSecretName: string | null = null;\n\n /**\n * Token expiration time\n */\n tokenExpiresAt: Date | null = null;\n\n /**\n * Whether the account is active\n */\n isActive: boolean = true;\n\n /**\n * Default hashtags to add to posts\n */\n defaultHashtags: string[] = [];\n\n /**\n * Granted OAuth scopes or platform permissions.\n */\n scopes: string[] = [];\n\n /**\n * Required permissions that still need app review or user grant.\n */\n missingPermissions: string[] = [];\n\n /**\n * How to handle links in posts\n * - description: Include link in post body/description\n * - reply: Post link as a reply (better for X algorithm)\n * - none: Don't include link\n */\n linkBehavior: LinkBehavior = 'description';\n\n /**\n * Safety mode for publish operations.\n */\n publishMode: PublishMode = 'dry_run';\n\n /**\n * Separate latch required before public publishing is allowed.\n */\n publicPublishingAllowed: boolean = false;\n\n /**\n * Account connection status\n */\n status: AccountStatus = 'connected';\n\n /**\n * Error message if status is 'error'\n */\n errorMessage: string | null = null;\n\n constructor(options: SocialAccountOptions = {}) {\n super(options);\n\n if (options.name !== undefined) this.name = options.name;\n if (options.platform !== undefined) this.platform = options.platform;\n if (options.platformUserId !== undefined)\n this.platformUserId = options.platformUserId;\n if (options.platformUsername !== undefined)\n this.platformUsername = options.platformUsername;\n if (options.platformUrl !== undefined)\n this.platformUrl = options.platformUrl;\n if (options.accessToken !== undefined)\n this.accessToken = options.accessToken;\n if (options.refreshToken !== undefined)\n this.refreshToken = options.refreshToken;\n if (options.credentialSecretId !== undefined)\n this.credentialSecretId = options.credentialSecretId;\n if (options.accessTokenSecretName !== undefined)\n this.accessTokenSecretName = options.accessTokenSecretName;\n if (options.refreshTokenSecretName !== undefined)\n this.refreshTokenSecretName = options.refreshTokenSecretName;\n if (options.tokenExpiresAt !== undefined)\n this.tokenExpiresAt = options.tokenExpiresAt;\n if (options.isActive !== undefined) this.isActive = options.isActive;\n if (options.defaultHashtags !== undefined)\n this.defaultHashtags = options.defaultHashtags;\n if (options.scopes !== undefined) this.scopes = options.scopes;\n if (options.missingPermissions !== undefined)\n this.missingPermissions = options.missingPermissions;\n if (options.linkBehavior !== undefined)\n this.linkBehavior = options.linkBehavior;\n if (options.publishMode !== undefined)\n this.publishMode = options.publishMode;\n if (options.publicPublishingAllowed !== undefined)\n this.publicPublishingAllowed = options.publicPublishingAllowed;\n if (options.status !== undefined) this.status = options.status;\n if (options.errorMessage !== undefined)\n this.errorMessage = options.errorMessage;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n\n if (options.platform !== undefined) {\n const setup = getSocialPlatformSetup(this.platform);\n if (options.linkBehavior === undefined) {\n this.linkBehavior = setup.defaultLinkBehavior;\n }\n if (options.publishMode === undefined) {\n this.publishMode = setup.defaultPublishMode;\n }\n }\n }\n\n /**\n * Social accounts need a slug identity that is scoped by tenant and platform.\n * A newsroom may connect `@localnews` on X, YouTube, Threads, and Facebook;\n * the generic name-derived slug would make those accounts overwrite each\n * other through SMRT's slug/context upsert identity.\n */\n async getSlug(): Promise<string | null | undefined> {\n if (!this.slug) {\n const identity =\n this.platformUserId || this.platformUsername || this.name || this.id;\n if (!identity) {\n return this.slug;\n }\n\n const source = [\n this.tenantId ? `tenant-${this.tenantId}` : 'no-tenant',\n this.platform,\n identity,\n ]\n .filter(Boolean)\n .join('-');\n\n this.slug = source\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/(^-|-$)/g, '');\n }\n\n return this.slug;\n }\n\n /**\n * Check if the token is expired or will expire soon\n */\n get isTokenExpired(): boolean {\n if (!this.tokenExpiresAt) return false;\n // Expire 5 minutes early to allow for refresh\n const expiryBuffer = 5 * 60 * 1000;\n return Date.now() >= this.tokenExpiresAt.getTime() - expiryBuffer;\n }\n\n /**\n * Check if the account needs attention (expired or error)\n */\n get needsAttention(): boolean {\n return (\n this.status !== 'connected' ||\n this.isTokenExpired ||\n this.missingPermissions.length > 0 ||\n (this.publishMode === 'public' && !this.publicPublishingAllowed)\n );\n }\n\n /**\n * Check if the account is ready for publishing\n */\n get isReady(): boolean {\n return (\n this.isActive &&\n this.status === 'connected' &&\n this.hasCredentials &&\n this.missingPermissions.length === 0 &&\n !this.isTokenExpired &&\n (this.publishMode !== 'public' || this.publicPublishingAllowed)\n );\n }\n\n /**\n * Effective publish mode after applying the public-publishing latch.\n */\n get effectivePublishMode(): PublishMode {\n if (this.publishMode === 'public' && !this.publicPublishingAllowed) {\n return 'dry_run';\n }\n return this.publishMode;\n }\n\n /**\n * Check whether any usable credential reference exists.\n */\n get hasCredentials(): boolean {\n return Boolean(\n this.credentialSecretId || this.accessTokenSecretName || this.accessToken,\n );\n }\n\n /**\n * Store all platform credentials in smrt-secrets as a single JSON payload.\n */\n async setCredentials(\n credentials: Record<string, unknown>,\n options: {\n description?: string;\n category?: string;\n } = {},\n ): Promise<void> {\n if (!this.id) {\n this.id = randomUUID();\n }\n\n const secretName = this.credentialSecretId ?? `social-account-${this.id}`;\n const tenantId = this.requireCredentialTenantId(\n 'store social account credentials',\n );\n const secretService = await SecretService.create({ db: this.db });\n\n await secretService.storeForTenant(\n tenantId,\n secretName,\n JSON.stringify(credentials),\n {\n description: options.description ?? `Credentials for ${this.name}`,\n category: options.category ?? 'social',\n },\n );\n\n this.credentialSecretId = secretName;\n\n await this.withCredentialTenantContext(async () => {\n await this.save();\n });\n }\n\n /**\n * Retrieve platform credentials from smrt-secrets, falling back to deprecated fields.\n */\n async getCredentials(): Promise<Record<string, unknown> | null> {\n if (!this.credentialSecretId) {\n if (this.accessTokenSecretName || this.refreshTokenSecretName) {\n const tenantId = this.requireCredentialTenantId(\n 'retrieve social account credentials',\n );\n const secretService = await SecretService.create({ db: this.db });\n const credentials: Record<string, unknown> = {};\n\n if (this.accessTokenSecretName) {\n const secret = await secretService.retrieveForTenant(\n tenantId,\n this.accessTokenSecretName,\n );\n credentials.accessToken = secret.value;\n }\n\n if (this.refreshTokenSecretName) {\n const secret = await secretService.retrieveForTenant(\n tenantId,\n this.refreshTokenSecretName,\n );\n credentials.refreshToken = secret.value;\n }\n\n return credentials;\n }\n\n if (!this.accessToken && !this.refreshToken) return null;\n return {\n accessToken: this.accessToken,\n refreshToken: this.refreshToken,\n };\n }\n\n const credentialSecretId = this.credentialSecretId;\n const tenantId = this.requireCredentialTenantId(\n 'retrieve social account credentials',\n );\n const secretService = await SecretService.create({ db: this.db });\n const secret = await secretService.retrieveForTenant(\n tenantId,\n credentialSecretId,\n );\n return JSON.parse(secret.value);\n }\n\n private async withCredentialTenantContext<T>(\n fn: () => Promise<T>,\n ): Promise<T> {\n const tenantId = this.getCredentialTenantId();\n const currentTenantId = getCurrentTenant()?.tenantId;\n\n if (!tenantId || currentTenantId === tenantId) {\n return fn();\n }\n\n return withTenant({ tenantId }, fn);\n }\n\n private getCredentialTenantId(): string | null {\n if (this.tenantId) {\n return this.tenantId;\n }\n\n return getCurrentTenant()?.tenantId ?? null;\n }\n\n private requireCredentialTenantId(action: string): string {\n const tenantId = this.getCredentialTenantId();\n if (!tenantId) {\n throw new Error(`Tenant context required to ${action}.`);\n }\n return tenantId;\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport { SocialAccount, type SocialPlatformType } from '../social-account.js';\n\nexport class SocialAccountCollection extends SmrtCollection<SocialAccount> {\n static readonly _itemClass = SocialAccount;\n\n async findActive(platform?: SocialPlatformType): Promise<SocialAccount[]> {\n const accounts = await this.list({\n where: {\n isActive: true,\n ...(platform ? { platform } : {}),\n },\n orderBy: 'name ASC',\n });\n\n return accounts;\n }\n\n async findReady(platform?: SocialPlatformType): Promise<SocialAccount[]> {\n const accounts = await this.findActive(platform);\n return accounts.filter((account) => account.isReady);\n }\n\n async findNeedsAttention(): Promise<SocialAccount[]> {\n const accounts = await this.list({\n where: { isActive: true },\n orderBy: 'name ASC',\n });\n return accounts.filter((account) => account.needsAttention);\n }\n}\n","/**\n * Social Post Model\n *\n * Manages posts published or scheduled to social media platforms.\n * Tracks publishing status and engagement analytics.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport {\n crossPackageRef,\n foreignKey,\n SmrtObject,\n smrt,\n} from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport { VideoContent } from '@happyvertical/smrt-video';\nimport { SocialAccount } from './social-account.js';\n\n/**\n * Post status\n */\nexport type PostStatus =\n | 'draft'\n | 'pending_approval'\n | 'approved'\n | 'scheduled'\n | 'publishing'\n | 'dry_run'\n | 'staged'\n | 'published'\n | 'failed'\n | 'cancelled';\n\n/**\n * Social post type\n */\nexport type SocialPostType = 'text' | 'link' | 'image' | 'video';\n\n/**\n * Post analytics\n */\nexport interface PostAnalytics {\n /**\n * View/impression count\n */\n views?: number;\n\n /**\n * Impression count when the platform distinguishes it from views\n */\n impressions?: number;\n\n /**\n * Like/favorite count\n */\n likes?: number;\n\n /**\n * Comment count\n */\n comments?: number;\n\n /**\n * Share/repost count\n */\n shares?: number;\n\n /**\n * Link click count\n */\n clicks?: number;\n\n /**\n * Raw platform analytics payload\n */\n raw?: unknown;\n\n /**\n * When analytics were last updated\n */\n lastUpdated?: Date;\n}\n\n/**\n * Social post creation options\n */\nexport interface SocialPostOptions extends SmrtObjectOptions {\n /**\n * Social account to publish to\n */\n socialAccountId?: string | null;\n\n /**\n * Video content to publish\n */\n videoContentId?: string | null;\n\n /**\n * Generic content ID (for non-video content)\n */\n contentId?: string | null;\n\n /**\n * High-level post type\n */\n postType?: SocialPostType;\n\n /**\n * Public media URL for platforms that cannot upload buffers\n */\n mediaUrl?: string | null;\n\n /**\n * Post title (for platforms that support it)\n */\n title?: string | null;\n\n /**\n * Post description/caption\n */\n description?: string;\n\n /**\n * Hashtags to include\n */\n hashtags?: string[];\n\n /**\n * Link URL to include\n */\n linkUrl?: string | null;\n\n /**\n * Platform-specific post ID (after publishing)\n */\n platformPostId?: string | null;\n\n /**\n * Public post URL (after publishing)\n */\n platformUrl?: string | null;\n\n /**\n * Scheduled publish time\n */\n scheduledAt?: Date | null;\n\n /**\n * Actual publish time\n */\n publishedAt?: Date | null;\n\n /**\n * Post status\n * @default 'draft'\n */\n status?: PostStatus;\n\n /**\n * Error message if status is 'failed'\n */\n errorMessage?: string | null;\n\n /**\n * Engagement analytics\n */\n analytics?: PostAnalytics;\n\n /**\n * When analytics were last synced\n */\n analyticsLastSyncedAt?: Date | null;\n\n /**\n * Tenant ID for multi-tenant isolation\n */\n tenantId?: string | null;\n}\n\n/**\n * Social media post for multi-platform publishing\n *\n * SocialPost represents a post published or scheduled to a social\n * platform. It tracks publishing status and engagement analytics,\n * and can reference VideoContent for video posts.\n *\n * @example\n * ```typescript\n * import { SocialPost } from '@happyvertical/smrt-social';\n *\n * const post = new SocialPost({\n * socialAccountId: 'account-123',\n * videoContentId: 'video-456',\n * title: 'Breaking News from Bentley',\n * description: 'Latest updates from the town council meeting.',\n * hashtags: ['news', 'local', 'bentley'],\n * linkUrl: 'https://example.com/article',\n * scheduledAt: new Date('2026-01-26T18:00:00Z'),\n * });\n * await post.save();\n * ```\n */\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: ['list', 'get', 'create', 'update', 'delete'],\n },\n mcp: {\n include: ['list', 'get'],\n },\n cli: true,\n})\nexport class SocialPost extends SmrtObject {\n /**\n * Tenant ID for multi-tenant isolation\n */\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n /**\n * Social account to publish to\n */\n @foreignKey(() => SocialAccount)\n socialAccountId: string | null = null;\n\n /**\n * Video content to publish\n */\n @foreignKey(() => VideoContent)\n videoContentId: string | null = null;\n\n /**\n * Generic content ID (for non-video content)\n */\n @crossPackageRef('@happyvertical/smrt-content:Content')\n contentId: string | null = null;\n\n /**\n * High-level post type\n */\n postType: SocialPostType = 'text';\n\n /**\n * Public media URL for platforms that require URL media publishing\n */\n mediaUrl: string | null = null;\n\n /**\n * Post title (for platforms that support it)\n */\n title: string | null = null;\n\n /**\n * Post description/caption\n */\n description: string = '';\n\n /**\n * Hashtags to include\n */\n hashtags: string[] = [];\n\n /**\n * Link URL to include in the post\n */\n linkUrl: string | null = null;\n\n /**\n * Platform-specific post ID (set after publishing)\n */\n platformPostId: string | null = null;\n\n /**\n * Public post URL (set after publishing)\n */\n platformUrl: string | null = null;\n\n /**\n * Scheduled publish time\n * If set, post will be scheduled instead of published immediately\n */\n scheduledAt: Date | null = null;\n\n /**\n * Actual publish time\n */\n publishedAt: Date | null = null;\n\n /**\n * Post status\n * - draft: Not yet submitted for publishing\n * - pending_approval: Awaiting editorial approval\n * - approved: Approved and ready to publish\n * - scheduled: Queued for future publishing\n * - publishing: Currently being published\n * - dry_run: Payload was validated without remote write\n * - staged: Non-public platform object/container was created\n * - published: Successfully published\n * - failed: Publishing failed\n * - cancelled: Publishing was cancelled\n */\n status: PostStatus = 'draft';\n\n /**\n * Error message if status is 'failed'\n */\n errorMessage: string | null = null;\n\n /**\n * Engagement analytics\n */\n analytics: PostAnalytics = {};\n\n /**\n * When analytics were last synced\n */\n analyticsLastSyncedAt: Date | null = null;\n\n constructor(options: SocialPostOptions = {}) {\n super(options);\n\n if (options.socialAccountId !== undefined)\n this.socialAccountId = options.socialAccountId;\n if (options.videoContentId !== undefined)\n this.videoContentId = options.videoContentId;\n if (options.contentId !== undefined) this.contentId = options.contentId;\n if (options.postType !== undefined) this.postType = options.postType;\n if (options.mediaUrl !== undefined) this.mediaUrl = options.mediaUrl;\n if (options.title !== undefined) this.title = options.title;\n if (options.description !== undefined)\n this.description = options.description;\n if (options.hashtags !== undefined) this.hashtags = options.hashtags;\n if (options.linkUrl !== undefined) this.linkUrl = options.linkUrl;\n if (options.platformPostId !== undefined)\n this.platformPostId = options.platformPostId;\n if (options.platformUrl !== undefined)\n this.platformUrl = options.platformUrl;\n if (options.scheduledAt !== undefined)\n this.scheduledAt = options.scheduledAt;\n if (options.publishedAt !== undefined)\n this.publishedAt = options.publishedAt;\n if (options.status !== undefined) this.status = options.status;\n if (options.errorMessage !== undefined)\n this.errorMessage = options.errorMessage;\n if (options.analytics !== undefined) this.analytics = options.analytics;\n if (options.analyticsLastSyncedAt !== undefined)\n this.analyticsLastSyncedAt = options.analyticsLastSyncedAt;\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n }\n\n /**\n * Check if the post is scheduled for future publishing\n */\n get isScheduled(): boolean {\n return this.status === 'scheduled' && this.scheduledAt !== null;\n }\n\n /**\n * Check if the post has been published\n */\n get isPublished(): boolean {\n return this.status === 'published';\n }\n\n /**\n * Check if the post can be edited (draft or failed)\n */\n get isEditable(): boolean {\n return (\n this.status === 'draft' ||\n this.status === 'pending_approval' ||\n this.status === 'failed'\n );\n }\n\n /**\n * Check if the post is due for publishing now.\n */\n get isDueForPublish(): boolean {\n if (this.status !== 'approved' && this.status !== 'scheduled') {\n return false;\n }\n return !this.scheduledAt || this.scheduledAt.getTime() <= Date.now();\n }\n\n /**\n * Get formatted hashtag string\n */\n get hashtagString(): string {\n return this.hashtags\n .map((tag) => (tag.startsWith('#') ? tag : `#${tag}`))\n .join(' ');\n }\n\n /**\n * Get the full post text with hashtags\n */\n get fullText(): string {\n const parts = [this.description];\n if (this.hashtags.length > 0) {\n parts.push(this.hashtagString);\n }\n return parts.join('\\n\\n');\n }\n}\n","/**\n * Social Post Analytics Snapshot Model\n *\n * Stores timestamped normalized and raw platform analytics payloads.\n */\n\nimport type { SmrtObjectOptions } from '@happyvertical/smrt-core';\nimport { field, foreignKey, SmrtObject, smrt } from '@happyvertical/smrt-core';\nimport { TenantScoped, tenantId } from '@happyvertical/smrt-tenancy';\nimport type { SocialPlatformType } from './social-account.js';\nimport { type PostAnalytics, SocialPost } from './social-post.js';\n\nexport type RawAnalyticsPayload = Record<string, unknown> | unknown[] | null;\n\nexport interface SocialPostAnalyticsSnapshotOptions extends SmrtObjectOptions {\n /**\n * Tenant ID for multi-tenant isolation\n */\n tenantId?: string | null;\n\n /**\n * Social post being measured\n */\n socialPostId?: string | null;\n\n /**\n * Platform that produced this snapshot\n */\n platform?: SocialPlatformType | null;\n\n /**\n * Normalized platform analytics.\n */\n analytics?: PostAnalytics;\n\n /**\n * Raw platform payload\n */\n raw?: RawAnalyticsPayload;\n\n /**\n * When the platform data was captured\n */\n capturedAt?: Date;\n}\n\n@TenantScoped({ mode: 'optional' })\n@smrt({\n tableStrategy: 'sti',\n api: {\n include: ['list', 'get', 'create', 'delete'],\n },\n mcp: {\n include: ['list', 'get'],\n },\n cli: true,\n})\nexport class SocialPostAnalyticsSnapshot extends SmrtObject {\n @tenantId({ nullable: true })\n tenantId: string | null = null;\n\n @foreignKey(() => SocialPost)\n socialPostId: string | null = null;\n\n @field({ required: true })\n platform: SocialPlatformType | null = null;\n\n analytics: PostAnalytics = {};\n\n @field({ type: 'json', nullable: true })\n raw: RawAnalyticsPayload = null;\n\n capturedAt: Date = new Date();\n\n constructor(options: SocialPostAnalyticsSnapshotOptions = {}) {\n super(options);\n\n if (options.tenantId !== undefined) this.tenantId = options.tenantId;\n if (options.socialPostId !== undefined)\n this.socialPostId = options.socialPostId;\n if (options.platform !== undefined) this.platform = options.platform;\n if (options.analytics !== undefined) this.analytics = options.analytics;\n if (options.raw !== undefined) this.raw = options.raw;\n if (options.capturedAt !== undefined) this.capturedAt = options.capturedAt;\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport type { SocialPlatformType } from '../social-account.js';\nimport type { PostAnalytics } from '../social-post.js';\nimport {\n type RawAnalyticsPayload,\n SocialPostAnalyticsSnapshot,\n} from '../social-post-analytics-snapshot.js';\n\nfunction isRawAnalyticsPayload(value: unknown): value is RawAnalyticsPayload {\n return (\n value === null ||\n Array.isArray(value) ||\n (typeof value === 'object' && value !== null)\n );\n}\n\nexport class SocialPostAnalyticsSnapshotCollection extends SmrtCollection<SocialPostAnalyticsSnapshot> {\n static readonly _itemClass = SocialPostAnalyticsSnapshot;\n\n async recordSnapshot(options: {\n socialPostId: string;\n platform: SocialPlatformType;\n metrics: PostAnalytics;\n raw?: RawAnalyticsPayload;\n capturedAt?: Date;\n tenantId?: string | null;\n }): Promise<SocialPostAnalyticsSnapshot> {\n const snapshot = await this.create({\n socialPostId: options.socialPostId,\n platform: options.platform,\n analytics: options.metrics,\n raw:\n options.raw ??\n (isRawAnalyticsPayload(options.metrics.raw)\n ? options.metrics.raw\n : null),\n capturedAt: options.capturedAt ?? new Date(),\n tenantId: options.tenantId,\n });\n return snapshot;\n }\n\n async findForPost(\n socialPostId: string,\n ): Promise<SocialPostAnalyticsSnapshot[]> {\n return this.list({\n where: { socialPostId },\n orderBy: 'capturedAt DESC',\n });\n }\n\n async findLatestForPost(\n socialPostId: string,\n ): Promise<SocialPostAnalyticsSnapshot | null> {\n const snapshots = await this.findForPost(socialPostId);\n return snapshots[0] ?? null;\n }\n}\n","import { SmrtCollection } from '@happyvertical/smrt-core';\nimport {\n type PostAnalytics,\n type PostStatus,\n SocialPost,\n type SocialPostType,\n} from '../social-post.js';\n\nexport interface CreateSocialPostDraftOptions {\n socialAccountId: string;\n contentId?: string | null;\n videoContentId?: string | null;\n postType?: SocialPostType;\n title?: string | null;\n description?: string;\n hashtags?: string[];\n linkUrl?: string | null;\n mediaUrl?: string | null;\n scheduledAt?: Date | null;\n tenantId?: string | null;\n}\n\nexport interface PublishSuccessData {\n platformPostId: string;\n platformUrl: string;\n publishedAt?: Date;\n status?: PostStatus;\n}\n\nexport class SocialPostCollection extends SmrtCollection<SocialPost> {\n static readonly _itemClass = SocialPost;\n\n async createDraft(\n options: CreateSocialPostDraftOptions,\n ): Promise<SocialPost> {\n return this.create({\n ...options,\n postType:\n options.postType ??\n (options.videoContentId || options.mediaUrl\n ? 'video'\n : options.linkUrl\n ? 'link'\n : 'text'),\n status: options.scheduledAt ? 'scheduled' : 'draft',\n });\n }\n\n async findByStatus(status: PostStatus): Promise<SocialPost[]> {\n return this.list({ where: { status }, orderBy: 'scheduledAt ASC' });\n }\n\n async findForAccount(socialAccountId: string): Promise<SocialPost[]> {\n return this.list({\n where: { socialAccountId },\n orderBy: 'created_at DESC',\n });\n }\n\n async findDueForPublish(now: Date = new Date()): Promise<SocialPost[]> {\n const [approved, scheduled] = await Promise.all([\n this.list({\n where: { status: 'approved' },\n orderBy: 'scheduledAt ASC',\n }),\n this.list({\n where: { status: 'scheduled', 'scheduledAt <=': now },\n orderBy: 'scheduledAt ASC',\n }),\n ]);\n\n return [\n ...approved.filter((post) => post.isDueForPublish),\n ...scheduled,\n ].sort(\n (a, b) =>\n (a.scheduledAt?.getTime() ?? 0) - (b.scheduledAt?.getTime() ?? 0),\n );\n }\n\n async recordPublishSuccess(\n post: SocialPost,\n data: PublishSuccessData,\n ): Promise<SocialPost> {\n const status = data.status ?? 'published';\n post.platformPostId = data.platformPostId;\n post.platformUrl = data.platformUrl;\n if (status === 'published') {\n post.publishedAt = data.publishedAt ?? post.publishedAt ?? new Date();\n }\n post.status = status;\n post.errorMessage = null;\n await post.save();\n return post;\n }\n\n async recordPublishFailure(\n post: SocialPost,\n error: Error | string,\n ): Promise<SocialPost> {\n post.status = 'failed';\n post.errorMessage = formatPublishError(error);\n await post.save();\n return post;\n }\n\n async updateLatestAnalytics(\n post: SocialPost,\n analytics: PostAnalytics,\n syncedAt: Date = new Date(),\n ): Promise<SocialPost> {\n post.analytics = {\n ...analytics,\n lastUpdated: analytics.lastUpdated ?? syncedAt,\n };\n post.analyticsLastSyncedAt = syncedAt;\n await post.save();\n return post;\n }\n}\n\nfunction formatPublishError(error: Error | string): string {\n if (typeof error === 'string') {\n return error;\n }\n\n const details = [error.message];\n const code = (error as Error & { code?: unknown }).code;\n if (code !== undefined) {\n details.push(`code=${String(code)}`);\n }\n if (error.stack) {\n details.push(error.stack);\n }\n return details.join('\\n');\n}\n"],"names":["__decorateClass","tenantId","secretService","secret"],"mappings":"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"}