{"_id":"@bernierllc/social-media-threads","_rev":"4-4a35677abe002d992d8c7f372846a89e","name":"@bernierllc/social-media-threads","dist-tags":{"latest":"1.2.1"},"versions":{"0.0.0":{"name":"@bernierllc/social-media-threads","version":"0.0.0","keywords":["oidc","trusted-publishing","setup"],"_id":"@bernierllc/social-media-threads@0.0.0","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"dist":{"shasum":"f7ec314cd22b96a5339f2298ae8fcf88eb9ad6e2","tarball":"https://registry.npmjs.org/@bernierllc/social-media-threads/-/social-media-threads-0.0.0.tgz","fileCount":2,"integrity":"sha512-7qeV/FEem8Es8ln5NH6RuYG0NEgbSqsTPL2VEsIvs5EyGfqaq8fBG0ggzYpmh+huk3QNNcAPYA/rcPp4w9S9gQ==","signatures":[{"sig":"MEQCIFOtZJqzQnbfEippP0uCFWCEx2cyHAuFXW0PJjfGGJQxAiBtwLpXhsQuPn4mXxUkccT34Px0xNwa9b2kWHq74dtT9A==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":2091},"_npmUser":{"name":"mkbernier","email":"mkbernier@gmail.com"},"_npmVersion":"11.16.0","description":"OIDC trusted publishing setup package for @bernierllc/social-media-threads","directories":{},"_nodeVersion":"26.3.1","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/social-media-threads_0.0.0_1785868575441_0.5776413467289987","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@bernierllc/social-media-threads","version":"1.1.0","keywords":["threads","social-media","api","oauth2","graph-api","posting","bernierllc"],"author":{"name":"Bernier LLC"},"license":"SEE LICENSE IN LICENSE","_id":"@bernierllc/social-media-threads@1.1.0","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"homepage":"https://github.com/bernierllc/tools#readme","bugs":{"url":"https://github.com/bernierllc/tools/issues"},"dist":{"shasum":"36fe693eb73224a27ccb7c8a325c2dc3e5d22e24","tarball":"https://registry.npmjs.org/@bernierllc/social-media-threads/-/social-media-threads-1.1.0.tgz","fileCount":19,"integrity":"sha512-XxO56v7K+KWn9xeEHRRZOtuJT0JkP9z63CYrZJptBgV8SewgU0MPUknNHEZO8zSqeykIGPHZGJVFVDXl2GX2wg==","signatures":[{"sig":"MEUCIQDfNy3TFqnoLbSgRwtQLMtDVAqZ5rua3/oAPssCQZZi+QIgUlWqL8gAh3CDvb/xHqkeTjttqHoL76L75GCFtXEskDc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":55169},"main":"dist/index.js","type":"module","_from":"file:bernierllc-social-media-threads-1.1.0.tgz","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"lint":"eslint src --ext .ts","test":"NODE_OPTIONS='--experimental-vm-modules' jest --watch","build":"tsc","clean":"rimraf dist","lint:fix":"eslint src --ext .ts --fix","prebuild":"npm run clean","test:run":"NODE_OPTIONS='--experimental-vm-modules' jest --passWithNoTests","test:coverage":"NODE_OPTIONS='--experimental-vm-modules' jest --coverage --passWithNoTests"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c1c98b78-18bd-49ac-85a0-a1f78a044bbc"}},"_resolved":"/home/runner/work/tools/tools/packages/service/social-media-threads/bernierllc-social-media-threads-1.1.0.tgz","_integrity":"sha512-XxO56v7K+KWn9xeEHRRZOtuJT0JkP9z63CYrZJptBgV8SewgU0MPUknNHEZO8zSqeykIGPHZGJVFVDXl2GX2wg==","bernierllc":{"integration":{"logger":"integrated","neverhub":"required","docsIntegration":"ready"}},"repository":{"url":"git+https://github.com/bernierllc/tools.git","type":"git","directory":"packages/service/social-media-threads"},"_npmVersion":"12.0.2","description":"Threads API (Meta) integration service with OAuth 2.0, two-step container-based publishing, and token lifecycle management","directories":{},"_nodeVersion":"24.18.0","publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.57.0","rimraf":"^5.0.7","ts-jest":"^29.1.4","typescript":"^5.4.5","@types/jest":"^29.5.12","@types/node":"^20.14.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/social-media-threads_1.1.0_1785868723795_0.9046374843760205","host":"s3://npm-registry-packages-npm-production"}},"1.2.0":{"name":"@bernierllc/social-media-threads","version":"1.2.0","keywords":["threads","social-media","api","oauth2","graph-api","posting","bernierllc"],"author":{"name":"Bernier LLC"},"license":"SEE LICENSE IN LICENSE","_id":"@bernierllc/social-media-threads@1.2.0","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"homepage":"https://github.com/bernierllc/tools#readme","bugs":{"url":"https://github.com/bernierllc/tools/issues"},"dist":{"shasum":"48bc1987970a4890c23b2ec49fdbb86228c5c0cc","tarball":"https://registry.npmjs.org/@bernierllc/social-media-threads/-/social-media-threads-1.2.0.tgz","fileCount":19,"integrity":"sha512-Z0sWulniFIy6g3K7ocoScdaDphsTCjCo6P4eZrUXFbpGQCDcCb+beA/K3gwSKoCJJMIFgb0vEYbWMlZx0DotKA==","signatures":[{"sig":"MEYCIQCQEUKoB5u3/kYHcywkglALHzm24dBnDggHD/hQoSMivAIhALPBU5TZB/gNjGb93hl8xCU7LJwEizPlG489HnPo58gE","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":58068},"main":"dist/index.js","type":"module","_from":"file:bernierllc-social-media-threads-1.2.0.tgz","types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"lint":"eslint src --ext .ts","test":"NODE_OPTIONS='--experimental-vm-modules' jest --watch","build":"tsc","clean":"rimraf dist","lint:fix":"eslint src --ext .ts --fix","prebuild":"npm run clean","test:run":"NODE_OPTIONS='--experimental-vm-modules' jest --passWithNoTests","test:coverage":"NODE_OPTIONS='--experimental-vm-modules' jest --coverage --passWithNoTests"},"_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:c1c98b78-18bd-49ac-85a0-a1f78a044bbc"}},"_resolved":"/home/runner/work/tools/tools/packages/service/social-media-threads/bernierllc-social-media-threads-1.2.0.tgz","_integrity":"sha512-Z0sWulniFIy6g3K7ocoScdaDphsTCjCo6P4eZrUXFbpGQCDcCb+beA/K3gwSKoCJJMIFgb0vEYbWMlZx0DotKA==","bernierllc":{"integration":{"logger":"integrated","neverhub":"required","docsIntegration":"ready"}},"repository":{"url":"git+https://github.com/bernierllc/tools.git","type":"git","directory":"packages/service/social-media-threads"},"_npmVersion":"12.0.2","description":"Threads API (Meta) integration service with OAuth 2.0, two-step container-based publishing, and token lifecycle management","directories":{},"_nodeVersion":"24.18.0","dependencies":{"@bernierllc/neverhub-adapter":"0.4.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.57.0","rimraf":"^5.0.7","ts-jest":"^29.1.4","typescript":"^5.4.5","@types/jest":"^29.5.12","@types/node":"^20.14.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"tmp":"tmp/social-media-threads_1.2.0_1785876749057_0.9119426440685989","host":"s3://npm-registry-packages-npm-production"}},"1.2.1":{"_id":"@bernierllc/social-media-threads@1.2.1","bugs":{"url":"https://github.com/bernierllc/tools/issues"},"dist":{"shasum":"36acb909e01f962e7623e95cb8d2c4b9f9698b58","tarball":"https://registry.npmjs.org/@bernierllc/social-media-threads/-/social-media-threads-1.2.1.tgz","fileCount":19,"integrity":"sha512-t2/ErTg/70noymXNQgxp0ZcnO3l3GdqV5CDBs6XK4OsGSDC471o7Yu7rPAUq5z1nW7xb2rjre3lAJWRS39ieWg==","signatures":[{"sig":"MEUCIQDBM+4TTJeP3UGnyaXU3ydRfd9LjsjYH729mksKO4CUrQIgVeXjT0XQYCx5iMvTDJ3WOc0moeEvdkVcIfA7xyDjwpE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHqn2Kop34/LULgyBXS6u3ruVWNp528e5L4G1TiNQ3UpAiEA+t6M+7wBnb53lW4oCDmT4MheVgutakIkw8LYtznp2Rg="}],"unpackedSize":58069},"main":"dist/index.js","name":"@bernierllc/social-media-threads","type":"module","_from":"file:bernierllc-social-media-threads-1.2.1.tgz","types":"dist/index.d.ts","author":{"name":"Bernier LLC"},"engines":{"node":">=18.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"license":"SEE LICENSE IN LICENSE","scripts":{"lint":"eslint src --ext .ts","test":"NODE_OPTIONS='--experimental-vm-modules' jest --watch","build":"tsc","clean":"rimraf dist","lint:fix":"eslint src --ext .ts --fix","prebuild":"npm run clean","test:run":"NODE_OPTIONS='--experimental-vm-modules' jest --passWithNoTests","test:coverage":"NODE_OPTIONS='--experimental-vm-modules' jest --coverage --passWithNoTests"},"version":"1.2.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"c1c98b78-18bd-49ac-85a0-a1f78a044bbc"}},"homepage":"https://github.com/bernierllc/tools#readme","keywords":["threads","social-media","api","oauth2","graph-api","posting","bernierllc"],"_resolved":"/home/runner/work/tools/tools/packages/service/social-media-threads/bernierllc-social-media-threads-1.2.1.tgz","_integrity":"sha512-t2/ErTg/70noymXNQgxp0ZcnO3l3GdqV5CDBs6XK4OsGSDC471o7Yu7rPAUq5z1nW7xb2rjre3lAJWRS39ieWg==","bernierllc":{"integration":{"logger":"integrated","neverhub":"required","docsIntegration":"ready"}},"repository":{"url":"git+https://github.com/bernierllc/tools.git","type":"git","directory":"packages/service/social-media-threads"},"_npmVersion":"12.2.0","description":"Threads API (Meta) integration service with OAuth 2.0, two-step container-based publishing, and token lifecycle management","directories":{},"maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"_nodeVersion":"24.21.0","dependencies":{"@bernierllc/neverhub-adapter":"^0.4.1"},"publishConfig":{"access":"public","registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^29.7.0","eslint":"^8.57.0","rimraf":"^5.0.7","ts-jest":"^29.1.4","typescript":"^5.4.5","@types/jest":"^29.5.12","@types/node":"^20.14.0","@typescript-eslint/parser":"^7.0.0","@typescript-eslint/eslint-plugin":"^7.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/social-media-threads_1.2.1_1790884559219_0.6423595151055239"}}},"time":{"created":"2026-08-04T18:36:15.260Z","modified":"2026-10-01T19:55:59.510Z","0.0.0":"2026-08-04T18:36:15.604Z","1.1.0":"2026-08-04T18:38:43.934Z","1.2.0":"2026-08-04T20:52:29.221Z","1.2.1":"2026-10-01T19:55:59.321Z"},"bugs":{"url":"https://github.com/bernierllc/tools/issues"},"author":{"name":"Bernier LLC"},"license":"SEE LICENSE IN LICENSE","homepage":"https://github.com/bernierllc/tools#readme","keywords":["threads","social-media","api","oauth2","graph-api","posting","bernierllc"],"repository":{"url":"git+https://github.com/bernierllc/tools.git","type":"git","directory":"packages/service/social-media-threads"},"description":"Threads API (Meta) integration service with OAuth 2.0, two-step container-based publishing, and token lifecycle management","maintainers":[{"name":"alikhan410","email":"mczeyo@gmail.com"},{"name":"mkbernier","email":"mkbernier@gmail.com"}],"readme":"# @bernierllc/social-media-threads\n\nThreads API (Meta) integration service with OAuth 2.0, two-step container-based publishing, and access token lifecycle management.\n\n## Features\n\n- OAuth 2.0 authorization code flow (`threads_basic`, `threads_content_publish` scopes)\n- Short-lived → long-lived token exchange and long-lived token refresh\n- Single-account-per-instance credential storage\n- Two-step container-based publishing (text, image, video posts)\n- Typed error classes with token-expiry handling\n- Optional NeverHub service-discovery registration (graceful degradation)\n\n## Installation\n\n```bash\nnpm install @bernierllc/social-media-threads\n```\n\n## Usage\n\n### Basic Setup\n\n```typescript\nimport { ThreadsService } from '@bernierllc/social-media-threads';\n\nconst threads = new ThreadsService({\n  clientId: 'your-threads-app-id',\n  clientSecret: 'your-threads-app-secret',\n  callbackUrl: 'https://your-app.com/callback',\n  apiVersion: 'v1.0', // Optional, defaults to v1.0\n});\n```\n\n### Authentication (OAuth flow)\n\n```typescript\n// 1. Redirect the user to the authorization URL\nconst authUrl = threads.getAuthorizationUrl('optional-csrf-state');\n\n// 2. On callback, exchange the returned `code` for a short-lived token\nconst shortLived = await threads.exchangeCodeForToken(code);\n\n// 3. Exchange the short-lived token for a long-lived token (~60 days)\nconst longLived = await threads.exchangeForLongLivedToken();\n\n// 4. Before it expires, refresh the long-lived token\nconst refreshed = await threads.refreshLongLivedToken();\n```\n\n### Authenticate with existing credentials\n\n```typescript\nconst authResult = await threads.authenticate({\n  accessToken: 'existing-long-lived-token',\n  tokenType: 'long_lived',\n  userId: '1234567',\n  expiresAt: Date.now() + 3600000,\n});\n\nif (authResult.success) {\n  console.log('Authenticated as:', authResult.user?.username);\n}\n```\n\n### Posting Content\n\nPublishing is a two-step process (create a media container, then publish it). `postThread` (and its alias `publishThread`) wraps both steps:\n\n```typescript\nconst result = await threads.postThread({\n  mediaType: 'TEXT',\n  text: 'Hello Threads!',\n});\n\nif (result.success) {\n  console.log('Post ID:', result.postId);\n} else {\n  console.error('Error:', result.error);\n}\n```\n\nImage and video posts use a media URL instead of text-only content:\n\n```typescript\nawait threads.postThread({\n  mediaType: 'IMAGE',\n  imageUrl: 'https://example.com/image.jpg',\n  text: 'Optional caption',\n});\n\nawait threads.postThread({\n  mediaType: 'VIDEO',\n  videoUrl: 'https://example.com/video.mp4',\n  replyControl: 'accounts_you_follow', // optional\n});\n```\n\nThe two steps are also available individually:\n\n```typescript\nconst containerId = await threads.createMediaContainer({ mediaType: 'TEXT', text: 'Hello!' });\nconst publishResult = await threads.publishMediaContainer(containerId);\n```\n\n### NeverHub Service Discovery\n\n`initialize()` registers the service with NeverHub. It is entirely optional —\nevery method below works without calling it — and it is safe to call when\nNeverHub is not running: registration silently no-ops in degraded mode.\n\n```typescript\nawait threads.initialize();\n```\n\n## API Reference\n\n### ThreadsService\n\n#### Constructor\n\n```typescript\nnew ThreadsService(config: ThreadsServiceConfig)\n```\n\n**ThreadsServiceConfig:**\n\n- `clientId` (string, required): Threads App ID\n- `clientSecret` (string, required): Threads App Secret\n- `callbackUrl` (string, required): OAuth callback URL\n- `scopes` (string[], optional): OAuth scopes (default: `['threads_basic', 'threads_content_publish']`)\n- `apiVersion` (string, optional): API version (default: `'v1.0'`)\n\n#### Methods\n\n**getAuthorizationUrl(state?: string, callbackUrl?: string): string**\n\nBuild the URL to redirect the user to for authorization.\n\n**authenticate(credentials: ThreadsCredentials): Promise<ThreadsAuthResult>**\n\nStore credentials on the instance and verify them by fetching the profile.\n\n**exchangeCodeForToken(code: string, callbackUrl?: string): Promise<ThreadsAuthResult>**\n\nExchange an OAuth authorization code for a short-lived access token.\n\n**exchangeForLongLivedToken(shortLivedToken?: string): Promise<ThreadsAuthResult>**\n\nExchange a short-lived token for a long-lived token (defaults to the stored token).\n\n**refreshLongLivedToken(accessToken?: string): Promise<ThreadsAuthResult>**\n\nRefresh a long-lived token before it expires (defaults to the stored token).\n\n**createMediaContainer(content: ThreadsContent): Promise<string>**\n\nCreate a media container (step 1 of publishing). Throws `SocialMediaThreadsError` on failure.\n\n**publishMediaContainer(containerId: string): Promise<ThreadsPostResult>**\n\nPublish a previously created container (step 2 of publishing).\n\n**postThread(content: ThreadsContent): Promise<ThreadsPostResult>** / **publishThread(content: ThreadsContent): Promise<ThreadsPostResult>**\n\nCreate and publish a container in one call. `publishThread` is an alias of `postThread`.\n\n**isAuthenticated(): boolean**\n\nCheck if the service holds valid, non-expired credentials.\n\n**getUser(): ThreadsUser | null**\n\nGet the authenticated user's profile, if fetched.\n\n**getCredentials(): ThreadsCredentials | null**\n\nGet the currently stored credentials.\n\n## Requirements\n\n- Node.js >= 18.0.0\n- A Threads App configured via the Meta Developer Portal with `threads_basic` and `threads_content_publish` permissions\n\n## Error Handling\n\nOAuth and token methods return a result object with `success`/`error` fields and never throw. Publishing methods differ by step:\n\n- `createMediaContainer` throws a `SocialMediaThreadsError` (with `code: 'INVALID_INPUT' | 'OPERATION_FAILED' | 'TOKEN_EXPIRED'`) on failure.\n- `publishMediaContainer`, `postThread`, and `publishThread` return a `{ success: false, error }` result instead of throwing.\n\n```typescript\ntry {\n  const containerId = await threads.createMediaContainer({ mediaType: 'TEXT', text: 'Hi' });\n} catch (error) {\n  if (error instanceof SocialMediaThreadsError && error.code === 'TOKEN_EXPIRED') {\n    // refresh the token and retry\n  }\n}\n```\n\n## Testing\n\n```bash\nnpm test                # Run tests in watch mode\nnpm run test:run       # Run tests once\nnpm run test:coverage  # Run tests with coverage\n```\n\n## License\n\nCopyright (c) 2025 Bernier LLC\n\nThis file is licensed to the client under a limited-use license.\nThe client may use and modify this code *only within the scope of the project it was delivered for*.\nRedistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.\n\n## Support\n\nFor issues and questions, please open an issue on the GitHub repository.\n","readmeFilename":"README.md"}