@hardlydifficult/social 1.0.60 → 1.0.61

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/README.md CHANGED
@@ -4,10 +4,10 @@ Opinionated social read client for X.
4
4
 
5
5
  This package is intentionally narrow:
6
6
 
7
- - One provider: X
8
7
  - One factory: `createSocial()`
9
- - Terse methods: `post()`, `timeline()`, `likes()`
10
- - Like watching that starts immediately
8
+ - One authenticated account: `me`
9
+ - Small resource tree: `posts.get()`, `me.timeline()`, `me.likes()`
10
+ - Like watching as an async stream
11
11
 
12
12
  ## Installation
13
13
 
@@ -21,127 +21,110 @@ npm install @hardlydifficult/social
21
21
  import { createSocial } from "@hardlydifficult/social";
22
22
 
23
23
  const social = createSocial({
24
- bearerToken: process.env.X_BEARER_TOKEN,
24
+ token: process.env.X_BEARER_TOKEN,
25
25
  });
26
26
 
27
- const posts = await social.timeline(10);
28
- const liked = await social.likes(10);
29
- const post = await social.post("1234567890");
27
+ const timeline = await social.me.timeline({ limit: 10 });
28
+ const liked = await social.me.likes();
29
+ const post = await social.posts.get("1234567890");
30
30
  ```
31
31
 
32
- If you omit `bearerToken`, the client reads `X_BEARER_TOKEN` from the environment.
32
+ If you omit `token`, the client reads `X_BEARER_TOKEN` from the environment.
33
+ `bearerToken` is accepted as a compatibility alias.
33
34
 
34
35
  ## API
35
36
 
36
37
  ### `createSocial(options?)`
37
38
 
38
- Creates the default client. X is the only supported provider today, so the options are X-specific without extra nesting.
39
+ Creates the social client.
39
40
 
40
41
  ```ts
41
42
  const social = createSocial({
42
- bearerToken: process.env.X_BEARER_TOKEN,
43
- limit: 25,
43
+ token: process.env.X_BEARER_TOKEN,
44
+ defaultLimit: 25,
44
45
  });
45
46
  ```
46
47
 
47
48
  Options:
48
49
 
50
+ - `token?: string`
49
51
  - `bearerToken?: string`
52
+ - `defaultLimit?: number`
50
53
  - `limit?: number`
54
+ - `maxResults?: number`
55
+ - `type?: "x"`
51
56
 
52
- `maxResults` is still accepted as a compatibility alias for `limit`.
57
+ `bearerToken`, `limit`, and `maxResults` are accepted as compatibility aliases.
58
+ `type` is optional because X is the only supported provider.
53
59
 
54
- ### `social.post(id)`
60
+ ### `social.posts.get(id)`
55
61
 
56
62
  Fetch a single post.
57
63
 
58
64
  ```ts
59
- const post = await social.post("123");
65
+ const post = await social.posts.get("123");
60
66
  ```
61
67
 
62
- ### `social.timeline(limitOrOptions?)`
68
+ ### `social.me.timeline(options?)`
63
69
 
64
70
  Fetch the authenticated user timeline.
65
71
 
66
72
  ```ts
67
- const recent = await social.timeline();
68
- const firstTen = await social.timeline(10);
69
- const custom = await social.timeline({ limit: 5 });
73
+ const recent = await social.me.timeline();
74
+ const firstTen = await social.me.timeline({ limit: 10 });
70
75
  ```
71
76
 
72
- ### `social.likes(limitOrOptions?)`
77
+ ### `social.me.likes(options?)`
73
78
 
74
79
  Fetch posts liked by the authenticated user.
75
80
 
76
81
  ```ts
77
- const liked = await social.likes();
78
- const latestLiked = await social.likes(20);
82
+ const liked = await social.me.likes();
83
+ const latestLiked = await social.me.likes({ limit: 20 });
79
84
  ```
80
85
 
81
- ### `social.watchLikes(onLike, options?)`
86
+ ### `social.me.watchLikes(options?)`
82
87
 
83
- Starts polling immediately and returns a watcher you can stop later.
88
+ Returns an async stream of newly liked posts. The first poll seeds the current
89
+ likes and emits nothing, so iteration only yields likes discovered after the
90
+ stream starts.
84
91
 
85
92
  ```ts
86
- const watcher = social.watchLikes(
87
- ({ post, seenAt }) => {
88
- console.log(`[${seenAt}] ${post.url}`);
89
- },
90
- { everyMs: 30_000 }
91
- );
92
-
93
- // later
94
- watcher.stop();
95
- ```
96
-
97
- Options:
98
-
99
- - `everyMs?: number`
100
- - `onError?: (error: Error) => void`
101
-
102
- You can also use the object form if you prefer:
93
+ const controller = new AbortController();
103
94
 
104
- ```ts
105
- const watcher = social.watchLikes({
95
+ for await (const like of social.me.watchLikes({
106
96
  everyMs: 30_000,
107
- onLike: ({ post }) => {
108
- console.log(post.text);
109
- },
110
- });
97
+ signal: controller.signal,
98
+ })) {
99
+ console.log(`[${like.seenAt}] ${like.post.url}`);
100
+ }
111
101
  ```
112
102
 
113
- ## Watcher
114
-
115
- `watchLikes()` returns a `SocialLikeWatcher`.
116
-
117
- Methods:
103
+ Options:
118
104
 
119
- - `start()` starts polling and returns the watcher
120
- - `stop()` stops polling
121
- - `poll()` runs one polling cycle manually
105
+ - `everyMs?: number`
106
+ - `pollIntervalMs?: number`
107
+ - `signal?: AbortSignal`
122
108
 
123
- The first poll seeds known likes and does not emit notifications for existing liked posts.
109
+ `pollIntervalMs` is accepted as a compatibility alias for `everyMs`.
124
110
 
125
111
  ## Types
126
112
 
127
113
  Core types exported by the package:
128
114
 
115
+ - `CreateSocialOptions`
116
+ - `Provider`
117
+ - `XConfig`
129
118
  - `SocialOptions`
119
+ - `SocialConfig`
120
+ - `Social`
130
121
  - `SocialListOptions`
131
122
  - `SocialPost`
132
123
  - `SocialAuthor`
133
124
  - `SocialPostMetrics`
134
125
  - `LikeNotification`
135
126
  - `WatchLikesOptions`
136
-
137
- ## Compatibility Aliases
138
-
139
- These still exist, but they are no longer the preferred style:
140
-
141
- - `createSocialClient()` delegates to `createSocial()`
142
- - `client.getPost()` delegates to `client.post()`
143
- - `client.likedPosts()` delegates to `client.likes()`
144
- - `pollIntervalMs` is accepted as an alias for `everyMs`
127
+ - `LikeWatcherOptions`
145
128
 
146
129
  ## Scope
147
130
 
package/dist/index.d.ts CHANGED
@@ -1,16 +1,8 @@
1
- export type { Provider, XConfig, SocialOptions, SocialConfig, SocialListOptions, SocialPost, SocialAuthor, SocialPostMetrics, WatchLikesOptions, LikeWatcherOptions, LikeNotification, } from "./types.js";
2
- export { SocialClient } from "./SocialClient.js";
3
- export { SocialLikeWatcher, type SocialLikeWatcherOptions, } from "./SocialLikeWatcher.js";
4
- import { SocialClient } from "./SocialClient.js";
5
- import type { SocialOptions } from "./types.js";
1
+ export type { CreateSocialOptions, LikeNotification, LikeWatcherOptions, Provider, Social, SocialAuthor, SocialConfig, SocialListOptions, SocialOptions, SocialPost, SocialPostMetrics, WatchLikesOptions, XConfig, } from "./types.js";
2
+ import type { CreateSocialOptions, Social } from "./types.js";
6
3
  /**
7
- * Create the opinionated social client. X is the only provider today,
8
- * so the factory accepts X settings directly and will also read
9
- * X_BEARER_TOKEN from the environment.
4
+ * Create the opinionated social client. X is the only supported provider and
5
+ * token lookup falls back to X_BEARER_TOKEN automatically.
10
6
  */
11
- export declare function createSocial(config?: SocialOptions): SocialClient;
12
- /**
13
- * Backwards-compatible alias for older call sites.
14
- */
15
- export declare function createSocialClient(config?: SocialOptions): SocialClient;
7
+ export declare function createSocial(config?: CreateSocialOptions): Social;
16
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,QAAQ,EACR,OAAO,EACP,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,GAC9B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,aAAkB,GAAG,YAAY,CAErE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,GAAE,aAAkB,GAAG,YAAY,CAE3E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,QAAQ,EACR,MAAM,EACN,YAAY,EACZ,YAAY,EACZ,iBAAiB,EACjB,aAAa,EACb,UAAU,EACV,iBAAiB,EACjB,iBAAiB,EACjB,OAAO,GACR,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAG9D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,mBAAwB,GAAG,MAAM,CAErE"}
package/dist/index.js CHANGED
@@ -1,26 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SocialLikeWatcher = exports.SocialClient = void 0;
4
3
  exports.createSocial = createSocial;
5
- exports.createSocialClient = createSocialClient;
6
- var SocialClient_js_1 = require("./SocialClient.js");
7
- Object.defineProperty(exports, "SocialClient", { enumerable: true, get: function () { return SocialClient_js_1.SocialClient; } });
8
- var SocialLikeWatcher_js_1 = require("./SocialLikeWatcher.js");
9
- Object.defineProperty(exports, "SocialLikeWatcher", { enumerable: true, get: function () { return SocialLikeWatcher_js_1.SocialLikeWatcher; } });
10
- const SocialClient_js_2 = require("./SocialClient.js");
11
- const x_1 = require("./x");
4
+ const createXSocial_js_1 = require("./x/createXSocial.js");
12
5
  /**
13
- * Create the opinionated social client. X is the only provider today,
14
- * so the factory accepts X settings directly and will also read
15
- * X_BEARER_TOKEN from the environment.
6
+ * Create the opinionated social client. X is the only supported provider and
7
+ * token lookup falls back to X_BEARER_TOKEN automatically.
16
8
  */
17
9
  function createSocial(config = {}) {
18
- return new SocialClient_js_2.SocialClient(new x_1.XSocialClient(config));
19
- }
20
- /**
21
- * Backwards-compatible alias for older call sites.
22
- */
23
- function createSocialClient(config = {}) {
24
- return createSocial(config);
10
+ return (0, createXSocial_js_1.createXSocial)(config);
25
11
  }
26
12
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AA6BA,oCAEC;AAKD,gDAEC;AAxBD,qDAAiD;AAAxC,+GAAA,YAAY,OAAA;AACrB,+DAGgC;AAF9B,yHAAA,iBAAiB,OAAA;AAInB,uDAAiD;AAEjD,2BAAoC;AAEpC;;;;GAIG;AACH,SAAgB,YAAY,CAAC,SAAwB,EAAE;IACrD,OAAO,IAAI,8BAAY,CAAC,IAAI,iBAAa,CAAC,MAAM,CAAC,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,SAAgB,kBAAkB,CAAC,SAAwB,EAAE;IAC3D,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAC9B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAuBA,oCAEC;AARD,2DAAqD;AAErD;;;GAGG;AACH,SAAgB,YAAY,CAAC,SAA8B,EAAE;IAC3D,OAAO,IAAA,gCAAa,EAAC,MAAM,CAAC,CAAC;AAC/B,CAAC"}
package/dist/types.d.ts CHANGED
@@ -1,12 +1,15 @@
1
- export type Provider = "x";
2
- export interface SocialOptions {
1
+ export interface CreateSocialOptions {
3
2
  readonly type?: "x";
3
+ readonly token?: string;
4
4
  readonly bearerToken?: string;
5
+ readonly defaultLimit?: number;
5
6
  readonly limit?: number;
6
7
  readonly maxResults?: number;
7
8
  }
8
- export type XConfig = SocialOptions;
9
- export type SocialConfig = SocialOptions;
9
+ export type Provider = "x";
10
+ export type SocialOptions = CreateSocialOptions;
11
+ export type XConfig = CreateSocialOptions;
12
+ export type SocialConfig = CreateSocialOptions;
10
13
  export interface SocialListOptions {
11
14
  readonly limit?: number;
12
15
  }
@@ -35,8 +38,17 @@ export interface LikeNotification {
35
38
  export interface WatchLikesOptions {
36
39
  readonly everyMs?: number;
37
40
  readonly pollIntervalMs?: number;
38
- readonly onLike: (notification: LikeNotification) => void;
39
- readonly onError?: (error: Error) => void;
41
+ readonly signal?: AbortSignal;
42
+ }
43
+ export interface Social {
44
+ readonly posts: {
45
+ get(postId: string): Promise<SocialPost | null>;
46
+ };
47
+ readonly me: {
48
+ timeline(options?: SocialListOptions): Promise<readonly SocialPost[]>;
49
+ likes(options?: SocialListOptions): Promise<readonly SocialPost[]>;
50
+ watchLikes(options?: WatchLikesOptions): AsyncIterable<LikeNotification>;
51
+ };
40
52
  }
41
53
  export type LikeWatcherOptions = WatchLikesOptions;
42
54
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC;AAE3B,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC;IACpB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,OAAO,GAAG,aAAa,CAAC;AAEpC,MAAM,MAAM,YAAY,GAAG,aAAa,CAAC;AAEzC,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,CAAC,YAAY,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC1D,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAC3C;AAED,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC;IACpB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,MAAM,QAAQ,GAAG,GAAG,CAAC;AAC3B,MAAM,MAAM,aAAa,GAAG,mBAAmB,CAAC;AAChD,MAAM,MAAM,OAAO,GAAG,mBAAmB,CAAC;AAC1C,MAAM,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAE/C,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;CAC9B;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,MAAM,EAAE,YAAY,CAAC;IAC9B,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;CACrC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC;CAC/B;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,KAAK,EAAE;QACd,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;KACjD,CAAC;IACF,QAAQ,CAAC,EAAE,EAAE;QACX,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;QACtE,KAAK,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;QACnE,UAAU,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAAC,gBAAgB,CAAC,CAAC;KAC1E,CAAC;CACH;AAED,MAAM,MAAM,kBAAkB,GAAG,iBAAiB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { CreateSocialOptions, Social } from "../types.js";
2
+ /** Creates the X-backed social read client with token and limit fallbacks. */
3
+ export declare function createXSocial(options?: CreateSocialOptions): Social;
4
+ //# sourceMappingURL=createXSocial.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createXSocial.d.ts","sourceRoot":"","sources":["../../src/x/createXSocial.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EAEnB,MAAM,EAIP,MAAM,aAAa,CAAC;AA8BrB,8EAA8E;AAC9E,wBAAgB,aAAa,CAAC,OAAO,GAAE,mBAAwB,GAAG,MAAM,CA8EvE"}
@@ -0,0 +1,183 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createXSocial = createXSocial;
4
+ const date_time_1 = require("@hardlydifficult/date-time");
5
+ const DEFAULT_LIMIT = 25;
6
+ const DEFAULT_WATCH_INTERVAL_MS = (0, date_time_1.duration)({ minutes: 1 });
7
+ /** Creates the X-backed social read client with token and limit fallbacks. */
8
+ function createXSocial(options = {}) {
9
+ const token = options.token ?? options.bearerToken ?? process.env.X_BEARER_TOKEN ?? "";
10
+ const defaultLimit = options.defaultLimit ??
11
+ options.limit ??
12
+ options.maxResults ??
13
+ DEFAULT_LIMIT;
14
+ if (token.length === 0) {
15
+ throw new Error("X bearer token is required. Set X_BEARER_TOKEN.");
16
+ }
17
+ async function request(path) {
18
+ const response = await fetch(`https://api.x.com/2${path}`, {
19
+ headers: {
20
+ Authorization: `Bearer ${token}`,
21
+ "Content-Type": "application/json",
22
+ },
23
+ });
24
+ if (!response.ok) {
25
+ const status = String(response.status);
26
+ throw new Error(`X API request failed: ${status} ${response.statusText}`);
27
+ }
28
+ return (await response.json());
29
+ }
30
+ async function getPost(postId) {
31
+ const response = await request(`/tweets/${postId}${buildPostQuery()}`);
32
+ const tweet = readSingleTweet(response.data);
33
+ if (!tweet) {
34
+ return null;
35
+ }
36
+ return normalizeTweet(tweet, response.includes?.users ?? []);
37
+ }
38
+ async function listTimeline(options) {
39
+ const response = await request(`/users/me/timelines/reverse_chronological${buildListQuery(resolveLimit(options, defaultLimit))}`);
40
+ return normalizeTweets(response);
41
+ }
42
+ async function listLikes(options) {
43
+ const response = await request(`/users/me/liked_tweets${buildListQuery(resolveLimit(options, defaultLimit))}`);
44
+ return normalizeTweets(response);
45
+ }
46
+ return {
47
+ posts: {
48
+ get: getPost,
49
+ },
50
+ me: {
51
+ timeline: listTimeline,
52
+ likes: listLikes,
53
+ watchLikes(watchOptions = {}) {
54
+ return watchLikesStream({
55
+ everyMs: watchOptions.everyMs ??
56
+ watchOptions.pollIntervalMs ??
57
+ DEFAULT_WATCH_INTERVAL_MS,
58
+ signal: watchOptions.signal,
59
+ listLikes,
60
+ });
61
+ },
62
+ },
63
+ };
64
+ }
65
+ function resolveLimit(options, defaultLimit) {
66
+ return options?.limit ?? defaultLimit;
67
+ }
68
+ function buildPostQuery() {
69
+ const params = new URLSearchParams({
70
+ expansions: "author_id",
71
+ "tweet.fields": "created_at,public_metrics",
72
+ "user.fields": "name,username",
73
+ });
74
+ return `?${params.toString()}`;
75
+ }
76
+ function buildListQuery(limit) {
77
+ const params = new URLSearchParams({
78
+ expansions: "author_id",
79
+ "tweet.fields": "created_at,public_metrics",
80
+ "user.fields": "name,username",
81
+ max_results: String(limit),
82
+ });
83
+ return `?${params.toString()}`;
84
+ }
85
+ function normalizeTweets(response) {
86
+ return readTweetList(response.data).map((tweet) => normalizeTweet(tweet, response.includes?.users ?? []));
87
+ }
88
+ function readSingleTweet(data) {
89
+ if (!isTweet(data)) {
90
+ return null;
91
+ }
92
+ return data;
93
+ }
94
+ function readTweetList(data) {
95
+ if (!Array.isArray(data)) {
96
+ return [];
97
+ }
98
+ return data.filter((item) => isTweet(item));
99
+ }
100
+ function isTweet(value) {
101
+ if (typeof value !== "object" || value === null) {
102
+ return false;
103
+ }
104
+ const candidate = value;
105
+ return typeof candidate.id === "string" && typeof candidate.text === "string";
106
+ }
107
+ function normalizeTweet(tweet, users) {
108
+ const author = users.find((user) => user.id === tweet.author_id);
109
+ return {
110
+ id: tweet.id,
111
+ text: tweet.text,
112
+ createdAt: tweet.created_at ?? new Date(0).toISOString(),
113
+ url: `https://x.com/${author?.username ?? "i"}/status/${tweet.id}`,
114
+ author: {
115
+ id: author?.id ?? tweet.author_id ?? "unknown",
116
+ username: author?.username ?? "unknown",
117
+ displayName: author?.name ?? author?.username ?? "Unknown",
118
+ },
119
+ metrics: {
120
+ likes: tweet.public_metrics?.like_count ?? 0,
121
+ replies: tweet.public_metrics?.reply_count ?? 0,
122
+ reposts: tweet.public_metrics?.retweet_count ?? 0,
123
+ },
124
+ };
125
+ }
126
+ async function* watchLikesStream({ everyMs, signal, listLikes, }) {
127
+ const knownLikeIds = new Set();
128
+ let seeded = false;
129
+ while (signal?.aborted !== true) {
130
+ const liked = await listLikes();
131
+ const seenAt = new Date().toISOString();
132
+ if (!seeded) {
133
+ for (const post of liked) {
134
+ knownLikeIds.add(post.id);
135
+ }
136
+ seeded = true;
137
+ }
138
+ else {
139
+ const notifications = [];
140
+ for (const post of liked) {
141
+ if (!knownLikeIds.has(post.id)) {
142
+ knownLikeIds.add(post.id);
143
+ notifications.push({ post, seenAt });
144
+ }
145
+ }
146
+ for (const notification of notifications.reverse()) {
147
+ yield notification;
148
+ }
149
+ }
150
+ if (!(await sleep(everyMs, signal))) {
151
+ return;
152
+ }
153
+ }
154
+ }
155
+ function sleep(ms, signal) {
156
+ if (signal?.aborted === true) {
157
+ return Promise.resolve(false);
158
+ }
159
+ if (signal === undefined) {
160
+ return new Promise((resolve) => {
161
+ const timer = setTimeout(() => {
162
+ resolve(true);
163
+ }, ms);
164
+ timer.unref();
165
+ });
166
+ }
167
+ const abortSignal = signal;
168
+ return new Promise((resolve) => {
169
+ const timer = setTimeout(onTimeout, ms);
170
+ timer.unref();
171
+ abortSignal.addEventListener("abort", onAbort, { once: true });
172
+ function onTimeout() {
173
+ abortSignal.removeEventListener("abort", onAbort);
174
+ resolve(true);
175
+ }
176
+ function onAbort() {
177
+ clearTimeout(timer);
178
+ abortSignal.removeEventListener("abort", onAbort);
179
+ resolve(false);
180
+ }
181
+ });
182
+ }
183
+ //# sourceMappingURL=createXSocial.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createXSocial.js","sourceRoot":"","sources":["../../src/x/createXSocial.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hardlydifficult/social",
3
- "version": "1.0.60",
3
+ "version": "1.0.61",
4
4
  "main": "./dist/index.js",
5
5
  "types": "./dist/index.d.ts",
6
6
  "files": [
@@ -33,6 +33,6 @@
33
33
  }
34
34
  },
35
35
  "dependencies": {
36
- "@hardlydifficult/date-time": "1.0.24"
36
+ "@hardlydifficult/date-time": "1.0.25"
37
37
  }
38
38
  }
@@ -1,22 +0,0 @@
1
- import { SocialLikeWatcher, type SocialLikeWatcherOptions } from "./SocialLikeWatcher.js";
2
- import type { SocialProviderClient } from "./SocialProviderClient.js";
3
- import type { LikeNotification, SocialListOptions, SocialPost } from "./types.js";
4
- type SocialListInput = number | SocialListOptions | {
5
- readonly maxResults?: number;
6
- };
7
- /**
8
- *
9
- */
10
- export declare class SocialClient {
11
- private readonly provider;
12
- constructor(provider: SocialProviderClient);
13
- post(postId: string): Promise<SocialPost | null>;
14
- getPost(postId: string): Promise<SocialPost | null>;
15
- timeline(options?: SocialListInput): Promise<readonly SocialPost[]>;
16
- likes(options?: SocialListInput): Promise<readonly SocialPost[]>;
17
- likedPosts(options?: SocialListInput): Promise<readonly SocialPost[]>;
18
- watchLikes(options: SocialLikeWatcherOptions): SocialLikeWatcher;
19
- watchLikes(onLike: (notification: LikeNotification) => void, options?: Omit<SocialLikeWatcherOptions, "onLike">): SocialLikeWatcher;
20
- }
21
- export {};
22
- //# sourceMappingURL=SocialClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialClient.d.ts","sourceRoot":"","sources":["../src/SocialClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,UAAU,EACX,MAAM,YAAY,CAAC;AAEpB,KAAK,eAAe,GAChB,MAAM,GACN,iBAAiB,GACjB;IACE,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC9B,CAAC;AAwBN;;GAEG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBAAR,QAAQ,EAAE,oBAAoB;IAE3D,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAIhD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAInD,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAInE,KAAK,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAIhE,UAAU,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAIrE,UAAU,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB;IAChE,UAAU,CACR,MAAM,EAAE,CAAC,YAAY,EAAE,gBAAgB,KAAK,IAAI,EAChD,OAAO,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,QAAQ,CAAC,GACjD,iBAAiB;CAerB"}
@@ -1,51 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SocialClient = void 0;
4
- const SocialLikeWatcher_js_1 = require("./SocialLikeWatcher.js");
5
- function normalizeListOptions(options) {
6
- if (typeof options === "number") {
7
- return { limit: options };
8
- }
9
- if (!options) {
10
- return undefined;
11
- }
12
- return {
13
- limit: ("limit" in options ? options.limit : undefined) ??
14
- ("maxResults" in options ? options.maxResults : undefined),
15
- };
16
- }
17
- /**
18
- *
19
- */
20
- class SocialClient {
21
- provider;
22
- constructor(provider) {
23
- this.provider = provider;
24
- }
25
- post(postId) {
26
- return this.provider.post(postId);
27
- }
28
- getPost(postId) {
29
- return this.post(postId);
30
- }
31
- timeline(options) {
32
- return this.provider.timeline(normalizeListOptions(options));
33
- }
34
- likes(options) {
35
- return this.provider.likes(normalizeListOptions(options));
36
- }
37
- likedPosts(options) {
38
- return this.likes(options);
39
- }
40
- watchLikes(optionsOrHandler, options) {
41
- const watcherOptions = typeof optionsOrHandler === "function"
42
- ? {
43
- ...options,
44
- onLike: optionsOrHandler,
45
- }
46
- : optionsOrHandler;
47
- return SocialLikeWatcher_js_1.SocialLikeWatcher.create(this.provider, watcherOptions).start();
48
- }
49
- }
50
- exports.SocialClient = SocialClient;
51
- //# sourceMappingURL=SocialClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialClient.js","sourceRoot":"","sources":["../src/SocialClient.ts"],"names":[],"mappings":";;;AAAA,iEAGgC;AAmBhC,SAAS,oBAAoB,CAC3B,OAAyB;IAEzB,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;IAC5B,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,OAAO;QACL,KAAK,EACH,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;YAChD,CAAC,YAAY,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC;KAC7D,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAa,YAAY;IACM;IAA7B,YAA6B,QAA8B;QAA9B,aAAQ,GAAR,QAAQ,CAAsB;IAAG,CAAC;IAE/D,IAAI,CAAC,MAAc;QACjB,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED,QAAQ,CAAC,OAAyB;QAChC,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,OAAyB;QAC7B,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,UAAU,CAAC,OAAyB;QAClC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAOD,UAAU,CACR,gBAAiC,EACjC,OAAkD;QAElD,MAAM,cAAc,GAClB,OAAO,gBAAgB,KAAK,UAAU;YACpC,CAAC,CAAC;gBACE,GAAG,OAAO;gBACV,MAAM,EAAE,gBAAgB;aACzB;YACH,CAAC,CAAC,gBAAgB,CAAC;QAEvB,OAAO,wCAAiB,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC,KAAK,EAAE,CAAC;IACzE,CAAC;CACF;AA1CD,oCA0CC"}
@@ -1,20 +0,0 @@
1
- import type { SocialProviderClient } from "./SocialProviderClient.js";
2
- import type { WatchLikesOptions } from "./types.js";
3
- export type SocialLikeWatcherOptions = WatchLikesOptions;
4
- /**
5
- *
6
- */
7
- export declare class SocialLikeWatcher {
8
- private readonly provider;
9
- private readonly options;
10
- private timer;
11
- private polling;
12
- private seeded;
13
- private readonly knownLikeIds;
14
- constructor(provider: SocialProviderClient, options: Required<Pick<WatchLikesOptions, "everyMs">> & Pick<WatchLikesOptions, "onLike" | "onError">);
15
- static create(provider: SocialProviderClient, options: WatchLikesOptions): SocialLikeWatcher;
16
- start(): this;
17
- stop(): void;
18
- poll(): Promise<void>;
19
- }
20
- //# sourceMappingURL=SocialLikeWatcher.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialLikeWatcher.d.ts","sourceRoot":"","sources":["../src/SocialLikeWatcher.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEtE,MAAM,MAAM,wBAAwB,GAAG,iBAAiB,CAAC;AAEzD;;GAEG;AACH,qBAAa,iBAAiB;IAO1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAP1B,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAqB;gBAG/B,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,QAAQ,CAAC,IAAI,CAAC,iBAAiB,EAAE,SAAS,CAAC,CAAC,GACpE,IAAI,CAAC,iBAAiB,EAAE,QAAQ,GAAG,SAAS,CAAC;IAGjD,MAAM,CAAC,MAAM,CACX,QAAQ,EAAE,oBAAoB,EAC9B,OAAO,EAAE,iBAAiB,GACzB,iBAAiB;IAQpB,KAAK,IAAI,IAAI;IAWb,IAAI,IAAI,IAAI;IAON,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CA6C5B"}
@@ -1,81 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SocialLikeWatcher = void 0;
4
- const date_time_1 = require("@hardlydifficult/date-time");
5
- /**
6
- *
7
- */
8
- class SocialLikeWatcher {
9
- provider;
10
- options;
11
- timer = null;
12
- polling = false;
13
- seeded = false;
14
- knownLikeIds = new Set();
15
- constructor(provider, options) {
16
- this.provider = provider;
17
- this.options = options;
18
- }
19
- static create(provider, options) {
20
- return new SocialLikeWatcher(provider, {
21
- ...options,
22
- everyMs: options.everyMs ?? options.pollIntervalMs ?? date_time_1.MILLISECONDS_PER_MINUTE,
23
- });
24
- }
25
- start() {
26
- if (this.timer !== null) {
27
- return this;
28
- }
29
- void this.poll();
30
- this.timer = setInterval(() => void this.poll(), this.options.everyMs);
31
- this.timer.unref();
32
- return this;
33
- }
34
- stop() {
35
- if (this.timer !== null) {
36
- clearInterval(this.timer);
37
- this.timer = null;
38
- }
39
- }
40
- async poll() {
41
- if (this.polling) {
42
- return;
43
- }
44
- this.polling = true;
45
- try {
46
- const liked = await this.provider.likes();
47
- const now = new Date().toISOString();
48
- if (!this.seeded) {
49
- for (const post of liked) {
50
- this.knownLikeIds.add(post.id);
51
- }
52
- this.seeded = true;
53
- return;
54
- }
55
- const notifications = [];
56
- for (const post of liked) {
57
- if (!this.knownLikeIds.has(post.id)) {
58
- this.knownLikeIds.add(post.id);
59
- notifications.push({ post, seenAt: now });
60
- }
61
- }
62
- for (const notification of notifications.reverse()) {
63
- this.options.onLike(notification);
64
- }
65
- }
66
- catch (error) {
67
- const normalizedError = error instanceof Error ? error : new Error(String(error));
68
- if (this.options.onError) {
69
- this.options.onError(normalizedError);
70
- }
71
- else {
72
- console.error("SocialLikeWatcher: poll failed:", normalizedError.message);
73
- }
74
- }
75
- finally {
76
- this.polling = false;
77
- }
78
- }
79
- }
80
- exports.SocialLikeWatcher = SocialLikeWatcher;
81
- //# sourceMappingURL=SocialLikeWatcher.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialLikeWatcher.js","sourceRoot":"","sources":["../src/SocialLikeWatcher.ts"],"names":[],"mappings":"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"}
@@ -1,7 +0,0 @@
1
- import type { SocialListOptions, SocialPost } from "./types.js";
2
- export interface SocialProviderClient {
3
- post(postId: string): Promise<SocialPost | null>;
4
- timeline(options?: SocialListOptions): Promise<readonly SocialPost[]>;
5
- likes(options?: SocialListOptions): Promise<readonly SocialPost[]>;
6
- }
7
- //# sourceMappingURL=SocialProviderClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialProviderClient.d.ts","sourceRoot":"","sources":["../src/SocialProviderClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAEhE,MAAM,WAAW,oBAAoB;IACnC,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACjD,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;IACtE,KAAK,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC,CAAC;CACpE"}
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=SocialProviderClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"SocialProviderClient.js","sourceRoot":"","sources":["../src/SocialProviderClient.ts"],"names":[],"mappings":""}
@@ -1,15 +0,0 @@
1
- import type { SocialProviderClient } from "../SocialProviderClient.js";
2
- import type { SocialListOptions, SocialPost } from "../types.js";
3
- /**
4
- *
5
- */
6
- export declare class MastodonSocialClient implements SocialProviderClient {
7
- private readonly message;
8
- post(_postId: string): Promise<SocialPost | null>;
9
- getPost(postId: string): Promise<SocialPost | null>;
10
- timeline(_options?: SocialListOptions): Promise<readonly SocialPost[]>;
11
- getTimeline(options?: SocialListOptions): Promise<readonly SocialPost[]>;
12
- likes(_options?: SocialListOptions): Promise<readonly SocialPost[]>;
13
- getLikedPosts(options?: SocialListOptions): Promise<readonly SocialPost[]>;
14
- }
15
- //# sourceMappingURL=MastodonSocialClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MastodonSocialClient.d.ts","sourceRoot":"","sources":["../../src/mastodon/MastodonSocialClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEjE;;GAEG;AACH,qBAAa,oBAAqB,YAAW,oBAAoB;IAC/D,OAAO,CAAC,QAAQ,CAAC,OAAO,CACiF;IAEzG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAIjD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAInD,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAItE,WAAW,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAIxE,KAAK,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAInE,aAAa,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;CAG3E"}
@@ -1,29 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MastodonSocialClient = void 0;
4
- /**
5
- *
6
- */
7
- class MastodonSocialClient {
8
- message = 'MastodonSocialClient is not yet supported in @hardlydifficult/social. Use provider type "x" for now.';
9
- post(_postId) {
10
- throw new Error(this.message);
11
- }
12
- getPost(postId) {
13
- return this.post(postId);
14
- }
15
- timeline(_options) {
16
- throw new Error(this.message);
17
- }
18
- getTimeline(options) {
19
- return this.timeline(options);
20
- }
21
- likes(_options) {
22
- throw new Error(this.message);
23
- }
24
- getLikedPosts(options) {
25
- return this.likes(options);
26
- }
27
- }
28
- exports.MastodonSocialClient = MastodonSocialClient;
29
- //# sourceMappingURL=MastodonSocialClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"MastodonSocialClient.js","sourceRoot":"","sources":["../../src/mastodon/MastodonSocialClient.ts"],"names":[],"mappings":";;;AAGA;;GAEG;AACH,MAAa,oBAAoB;IACd,OAAO,GACtB,sGAAsG,CAAC;IAEzG,IAAI,CAAC,OAAe;QAClB,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,OAAO,CAAC,MAAc;QACpB,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IAED,QAAQ,CAAC,QAA4B;QACnC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,WAAW,CAAC,OAA2B;QACrC,OAAO,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,QAA4B;QAChC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAED,aAAa,CAAC,OAA2B;QACvC,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;CACF;AA3BD,oDA2BC"}
@@ -1,23 +0,0 @@
1
- import type { SocialProviderClient } from "../SocialProviderClient.js";
2
- import type { SocialListOptions, SocialPost, XConfig } from "../types.js";
3
- /**
4
- *
5
- */
6
- export declare class XSocialClient implements SocialProviderClient {
7
- private readonly bearerToken;
8
- private readonly limit;
9
- constructor(config: XConfig);
10
- post(postId: string): Promise<SocialPost | null>;
11
- getPost(postId: string): Promise<SocialPost | null>;
12
- timeline(options?: SocialListOptions): Promise<readonly SocialPost[]>;
13
- getTimeline(options?: SocialListOptions): Promise<readonly SocialPost[]>;
14
- likes(options?: SocialListOptions): Promise<readonly SocialPost[]>;
15
- getLikedPosts(options?: SocialListOptions): Promise<readonly SocialPost[]>;
16
- private buildQuery;
17
- private request;
18
- private readSingleTweet;
19
- private readTweetList;
20
- private isTweet;
21
- private normalizeTweet;
22
- }
23
- //# sourceMappingURL=XSocialClient.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"XSocialClient.d.ts","sourceRoot":"","sources":["../../src/x/XSocialClient.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AACvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AA2B1E;;GAEG;AACH,qBAAa,aAAc,YAAW,oBAAoB;IACxD,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,MAAM,EAAE,OAAO;IASrB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAYtD,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAI7C,QAAQ,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAU3E,WAAW,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAIlE,KAAK,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAUxE,aAAa,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,UAAU,EAAE,CAAC;IAI1E,OAAO,CAAC,UAAU;YAWJ,OAAO;IAgBrB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,OAAO;IAWf,OAAO,CAAC,cAAc;CAoBvB"}
@@ -1,104 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.XSocialClient = void 0;
4
- /**
5
- *
6
- */
7
- class XSocialClient {
8
- bearerToken;
9
- limit;
10
- constructor(config) {
11
- this.bearerToken = config.bearerToken ?? process.env.X_BEARER_TOKEN ?? "";
12
- this.limit = config.limit ?? config.maxResults ?? 25;
13
- if (this.bearerToken.length === 0) {
14
- throw new Error("X bearer token is required. Set X_BEARER_TOKEN.");
15
- }
16
- }
17
- async post(postId) {
18
- const response = await this.request(`/tweets/${postId}${this.buildQuery()}`);
19
- const tweet = this.readSingleTweet(response.data);
20
- if (!tweet) {
21
- return null;
22
- }
23
- return this.normalizeTweet(tweet, response.includes?.users ?? []);
24
- }
25
- getPost(postId) {
26
- return this.post(postId);
27
- }
28
- async timeline(options) {
29
- const response = await this.request(`/users/me/timelines/reverse_chronological${this.buildQuery(options?.limit)}`);
30
- return this.readTweetList(response.data).map((tweet) => this.normalizeTweet(tweet, response.includes?.users ?? []));
31
- }
32
- getTimeline(options) {
33
- return this.timeline(options);
34
- }
35
- async likes(options) {
36
- const response = await this.request(`/users/me/liked_tweets${this.buildQuery(options?.limit)}`);
37
- return this.readTweetList(response.data).map((tweet) => this.normalizeTweet(tweet, response.includes?.users ?? []));
38
- }
39
- getLikedPosts(options) {
40
- return this.likes(options);
41
- }
42
- buildQuery(limit) {
43
- const params = new URLSearchParams({
44
- expansions: "author_id",
45
- "tweet.fields": "created_at,public_metrics",
46
- "user.fields": "name,username",
47
- max_results: String(limit ?? this.limit),
48
- });
49
- return `?${params.toString()}`;
50
- }
51
- async request(path) {
52
- const response = await fetch(`https://api.x.com/2${path}`, {
53
- headers: {
54
- Authorization: `Bearer ${this.bearerToken}`,
55
- "Content-Type": "application/json",
56
- },
57
- });
58
- if (!response.ok) {
59
- const status = String(response.status);
60
- throw new Error(`X API request failed: ${status} ${response.statusText}`);
61
- }
62
- return (await response.json());
63
- }
64
- readSingleTweet(data) {
65
- if (!this.isTweet(data)) {
66
- return null;
67
- }
68
- return data;
69
- }
70
- readTweetList(data) {
71
- if (!Array.isArray(data)) {
72
- return [];
73
- }
74
- return data.filter((item) => this.isTweet(item));
75
- }
76
- isTweet(value) {
77
- if (typeof value !== "object" || value === null) {
78
- return false;
79
- }
80
- const candidate = value;
81
- return (typeof candidate.id === "string" && typeof candidate.text === "string");
82
- }
83
- normalizeTweet(tweet, users) {
84
- const author = users.find((user) => user.id === tweet.author_id);
85
- return {
86
- id: tweet.id,
87
- text: tweet.text,
88
- createdAt: tweet.created_at ?? new Date(0).toISOString(),
89
- url: `https://x.com/${author?.username ?? "i"}/status/${tweet.id}`,
90
- author: {
91
- id: author?.id ?? tweet.author_id ?? "unknown",
92
- username: author?.username ?? "unknown",
93
- displayName: author?.name ?? author?.username ?? "Unknown",
94
- },
95
- metrics: {
96
- likes: tweet.public_metrics?.like_count ?? 0,
97
- replies: tweet.public_metrics?.reply_count ?? 0,
98
- reposts: tweet.public_metrics?.retweet_count ?? 0,
99
- },
100
- };
101
- }
102
- }
103
- exports.XSocialClient = XSocialClient;
104
- //# sourceMappingURL=XSocialClient.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"XSocialClient.js","sourceRoot":"","sources":["../../src/x/XSocialClient.ts"],"names":[],"mappings":"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"}
package/dist/x/index.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export { XSocialClient } from "./XSocialClient.js";
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/x/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC"}
package/dist/x/index.js DELETED
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.XSocialClient = void 0;
4
- var XSocialClient_js_1 = require("./XSocialClient.js");
5
- Object.defineProperty(exports, "XSocialClient", { enumerable: true, get: function () { return XSocialClient_js_1.XSocialClient; } });
6
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/x/index.ts"],"names":[],"mappings":";;;AAAA,uDAAmD;AAA1C,iHAAA,aAAa,OAAA"}