@xpoz/xpoz 0.1.0 → 0.2.1

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
@@ -1,11 +1,13 @@
1
1
  # Xpoz TypeScript SDK
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@xpoz/xpoz)](https://www.npmjs.com/package/@xpoz/xpoz)
4
+
3
5
  TypeScript SDK for the [Xpoz](https://xpoz.ai) social media intelligence platform. Query Twitter/X, Instagram, and Reddit data through a simple, typed interface.
4
6
 
5
7
  ## Installation
6
8
 
7
9
  ```bash
8
- npm install xpoz
10
+ npm install @xpoz/xpoz
9
11
  ```
10
12
 
11
13
  Requires Node.js 18+.
@@ -31,6 +33,7 @@ The SDK wraps Xpoz's [MCP](https://modelcontextprotocol.io) server, abstracting
31
33
  - **30 data methods** across Twitter, Instagram, and Reddit
32
34
  - **Fully async** — all methods return `Promise<T>`
33
35
  - **Automatic operation polling** — long-running queries are abstracted away
36
+ - **Response types** — choose between fast (immediate), paging (full pagination), or CSV export
34
37
  - **Server-side pagination** — `PaginatedResult<T>` with `nextPage()`, `getPage(n)`
35
38
  - **CSV export** — `exportCsv()` on any paginated result
36
39
  - **Field selection** — request only the fields you need
@@ -40,7 +43,7 @@ The SDK wraps Xpoz's [MCP](https://modelcontextprotocol.io) server, abstracting
40
43
  ## Quick Start
41
44
 
42
45
  ```typescript
43
- import { XpozClient } from "xpoz";
46
+ import { XpozClient, ResponseType } from "@xpoz/xpoz";
44
47
 
45
48
  const client = new XpozClient({ apiKey: "your-api-key" });
46
49
  await client.connect();
@@ -135,6 +138,73 @@ const user = await client.twitter.getUser("elonmusk", {
135
138
 
136
139
  Requesting fewer fields significantly improves response time.
137
140
 
141
+ ## Response Types
142
+
143
+ Search and query methods support a `responseType` option that controls how results are returned. Import the `ResponseType` enum:
144
+
145
+ ```typescript
146
+ import { XpozClient, ResponseType } from "@xpoz/xpoz";
147
+ ```
148
+
149
+ | Mode | Enum Value | Behavior | Best For |
150
+ | --- | --- | --- | --- |
151
+ | **Fast** | `ResponseType.Fast` | Returns up to 300 results immediately, no async polling (default) | Quick queries, UI previews |
152
+ | **Paging** | `ResponseType.Paging` | Async paginated query with full dataset access | Full analysis, large datasets |
153
+ | **CSV** | `ResponseType.Csv` | Async bulk export, use `exportCsv()` to get download URL | Data exports |
154
+
155
+ ### Fast mode (default)
156
+
157
+ The default behavior. Returns results immediately without polling. Use `limit` to constrain the number of results (max 300):
158
+
159
+ ```typescript
160
+ const results = await client.twitter.searchPosts("bitcoin", {
161
+ startDate: "2025-01-01",
162
+ responseType: ResponseType.Fast,
163
+ limit: 50,
164
+ });
165
+ console.log(results.data.length); // up to 50 results, returned immediately
166
+ ```
167
+
168
+ ### Paging mode
169
+
170
+ Returns paginated results with full `totalRows`, `totalPages`, and `tableName` for cursor-based navigation:
171
+
172
+ ```typescript
173
+ const results = await client.twitter.searchPosts("bitcoin", {
174
+ startDate: "2025-01-01",
175
+ responseType: ResponseType.Paging, // optional — this is the default
176
+ });
177
+ console.log(results.pagination.totalRows); // total matching rows
178
+ if (results.hasNextPage()) {
179
+ const page2 = await results.nextPage();
180
+ }
181
+ ```
182
+
183
+ ### CSV mode
184
+
185
+ Initiates an async export. Call `exportCsv()` on the result to poll the export operation and get a download URL:
186
+
187
+ ```typescript
188
+ const results = await client.twitter.searchPosts("bitcoin", {
189
+ startDate: "2025-01-01",
190
+ responseType: ResponseType.Csv,
191
+ });
192
+ const downloadUrl = await results.exportCsv();
193
+ console.log(downloadUrl); // URL to download the CSV file
194
+ ```
195
+
196
+ ### Methods supporting `responseType` and `limit`
197
+
198
+ The following methods accept both `responseType` and `limit`:
199
+
200
+ - `twitter.getPostsByAuthor()`, `twitter.searchPosts()`, `twitter.getUsersByKeywords()`
201
+ - `instagram.getPostsByUser()`, `instagram.searchPosts()`, `instagram.getUsersByKeywords()`
202
+ - `reddit.searchPosts()`
203
+
204
+ These methods accept `limit` only:
205
+
206
+ - `twitter.searchUsers()`, `instagram.searchUsers()`, `reddit.searchUsers()`, `reddit.searchSubreddits()`
207
+
138
208
  ## Query Syntax
139
209
 
140
210
  The `query` parameter on all `search*` and `get*ByKeywords` methods supports a Lucene-style full-text syntax across Twitter, Instagram, and Reddit.
@@ -189,7 +259,8 @@ import {
189
259
  OperationTimeoutError,
190
260
  OperationFailedError,
191
261
  OperationCancelledError,
192
- } from "xpoz";
262
+ ResponseType,
263
+ } from "@xpoz/xpoz";
193
264
 
194
265
  try {
195
266
  const user = await client.twitter.getUser("nonexistent_user_12345");
@@ -226,10 +297,11 @@ const user = await client.twitter.getUser("44196397", { identifierType: "id" });
226
297
 
227
298
  #### `searchUsers(name, options?) -> Promise<TwitterUser[]>`
228
299
 
229
- Search users by name or username. Returns up to 10 results.
300
+ Search users by name or username. Returns up to 10 results by default. Use `limit` to adjust.
230
301
 
231
302
  ```typescript
232
303
  const users = await client.twitter.searchUsers("elon");
304
+ const topFive = await client.twitter.searchUsers("elon", { limit: 5 });
233
305
  ```
234
306
 
235
307
  #### `getUserConnections(username, connectionType, options?) -> Promise<PaginatedResult<TwitterUser>>`
@@ -243,11 +315,13 @@ const following = await client.twitter.getUserConnections("elonmusk", "following
243
315
 
244
316
  #### `getUsersByKeywords(query, options?) -> Promise<PaginatedResult<TwitterUser>>`
245
317
 
246
- Find users who authored posts matching a keyword query.
318
+ Find users who authored posts matching a keyword query. Supports `responseType` and `limit`.
247
319
 
248
320
  ```typescript
249
321
  const users = await client.twitter.getUsersByKeywords('"machine learning"', {
250
322
  fields: ["username", "name", "followersCount"],
323
+ responseType: ResponseType.Fast,
324
+ limit: 20,
251
325
  });
252
326
  ```
253
327
 
@@ -261,17 +335,19 @@ const tweets = await client.twitter.getPostsByIds(["1234567890", "0987654321"]);
261
335
 
262
336
  #### `getPostsByAuthor(identifier, options?) -> Promise<PaginatedResult<TwitterPost>>`
263
337
 
264
- Get all posts by an author with optional date filtering.
338
+ Get all posts by an author with optional date filtering. Supports `responseType` and `limit`.
265
339
 
266
340
  ```typescript
267
341
  const results = await client.twitter.getPostsByAuthor("elonmusk", {
268
342
  startDate: "2025-01-01",
343
+ responseType: ResponseType.Fast,
344
+ limit: 100,
269
345
  });
270
346
  ```
271
347
 
272
348
  #### `searchPosts(query, options?) -> Promise<PaginatedResult<TwitterPost>>`
273
349
 
274
- Full-text search with filters. Supports exact phrases (`"machine learning"`), boolean operators (`AI AND python`), and parentheses.
350
+ Full-text search with filters. Supports exact phrases (`"machine learning"`), boolean operators (`AI AND python`), and parentheses. Supports `responseType` and `limit`.
275
351
 
276
352
  ```typescript
277
353
  const results = await client.twitter.searchPosts('"artificial intelligence" AND ethics', {
@@ -279,6 +355,8 @@ const results = await client.twitter.searchPosts('"artificial intelligence" AND
279
355
  endDate: "2025-06-01",
280
356
  language: "en",
281
357
  fields: ["id", "text", "likeCount", "authorUsername", "createdAtDate"],
358
+ responseType: ResponseType.Fast,
359
+ limit: 50,
282
360
  });
283
361
  ```
284
362
 
@@ -336,8 +414,11 @@ console.log(`${user.fullName} — ${user.followerCount?.toLocaleString()} follow
336
414
 
337
415
  #### `searchUsers(name, options?) -> Promise<InstagramUser[]>`
338
416
 
417
+ Search users by name. Use `limit` to adjust the number of results.
418
+
339
419
  ```typescript
340
420
  const users = await client.instagram.searchUsers("nasa");
421
+ const topThree = await client.instagram.searchUsers("nasa", { limit: 3 });
341
422
  ```
342
423
 
343
424
  #### `getUserConnections(username, connectionType, options?) -> Promise<PaginatedResult<InstagramUser>>`
@@ -348,8 +429,13 @@ const followers = await client.instagram.getUserConnections("instagram", "follow
348
429
 
349
430
  #### `getUsersByKeywords(query, options?) -> Promise<PaginatedResult<InstagramUser>>`
350
431
 
432
+ Find users who authored posts matching a keyword query. Supports `responseType` and `limit`.
433
+
351
434
  ```typescript
352
- const users = await client.instagram.getUsersByKeywords('"sustainable fashion"');
435
+ const users = await client.instagram.getUsersByKeywords('"sustainable fashion"', {
436
+ responseType: ResponseType.Fast,
437
+ limit: 20,
438
+ });
353
439
  ```
354
440
 
355
441
  #### `getPostsByIds(postIds, options?) -> Promise<InstagramPost[]>`
@@ -362,14 +448,24 @@ const posts = await client.instagram.getPostsByIds(["3606450040306139062_4836333
362
448
 
363
449
  #### `getPostsByUser(identifier, options?) -> Promise<PaginatedResult<InstagramPost>>`
364
450
 
451
+ Get all posts by a user. Supports `responseType` and `limit`.
452
+
365
453
  ```typescript
366
- const results = await client.instagram.getPostsByUser("nasa");
454
+ const results = await client.instagram.getPostsByUser("nasa", {
455
+ responseType: ResponseType.Fast,
456
+ limit: 50,
457
+ });
367
458
  ```
368
459
 
369
460
  #### `searchPosts(query, options?) -> Promise<PaginatedResult<InstagramPost>>`
370
461
 
462
+ Full-text search with filters. Supports `responseType` and `limit`.
463
+
371
464
  ```typescript
372
- const results = await client.instagram.searchPosts("travel photography");
465
+ const results = await client.instagram.searchPosts("travel photography", {
466
+ responseType: ResponseType.Fast,
467
+ limit: 30,
468
+ });
373
469
  ```
374
470
 
375
471
  #### `getComments(postId, options?) -> Promise<PaginatedResult<InstagramComment>>`
@@ -402,8 +498,11 @@ console.log(`${user.username} — ${user.totalKarma?.toLocaleString()} karma`);
402
498
 
403
499
  #### `searchUsers(name, options?) -> Promise<RedditUser[]>`
404
500
 
501
+ Search users by name. Use `limit` to adjust the number of results.
502
+
405
503
  ```typescript
406
504
  const users = await client.reddit.searchUsers("spez");
505
+ const topThree = await client.reddit.searchUsers("spez", { limit: 3 });
407
506
  ```
408
507
 
409
508
  #### `getUsersByKeywords(query, options?) -> Promise<PaginatedResult<RedditUser>>`
@@ -416,13 +515,15 @@ const users = await client.reddit.getUsersByKeywords('"machine learning"', {
416
515
 
417
516
  #### `searchPosts(query, options?) -> Promise<PaginatedResult<RedditPost>>`
418
517
 
419
- `sort`: `"relevance"`, `"hot"`, `"top"`, `"new"`, `"comments"`. `time`: `"hour"`, `"day"`, `"week"`, `"month"`, `"year"`, `"all"`.
518
+ `sort`: `"relevance"`, `"hot"`, `"top"`, `"new"`, `"comments"`. `time`: `"hour"`, `"day"`, `"week"`, `"month"`, `"year"`, `"all"`. Supports `responseType` and `limit`.
420
519
 
421
520
  ```typescript
422
521
  const results = await client.reddit.searchPosts("python tutorial", {
423
522
  subreddit: "learnpython",
424
523
  sort: "top",
425
524
  time: "month",
525
+ responseType: ResponseType.Fast,
526
+ limit: 25,
426
527
  });
427
528
  ```
428
529
 
@@ -448,8 +549,11 @@ const comments = await client.reddit.searchComments("helpful tip", {
448
549
 
449
550
  #### `searchSubreddits(query, options?) -> Promise<RedditSubreddit[]>`
450
551
 
552
+ Search subreddits by name. Use `limit` to adjust the number of results.
553
+
451
554
  ```typescript
452
555
  const subs = await client.reddit.searchSubreddits("machine learning");
556
+ const topFive = await client.reddit.searchSubreddits("machine learning", { limit: 5 });
453
557
  ```
454
558
 
455
559
  #### `getSubredditWithPosts(subredditName, options?) -> Promise<SubredditWithPosts>`
@@ -634,12 +738,14 @@ All fields are optional and typed as their respective TypeScript types. Unknown
634
738
  - `post: RedditPost`
635
739
  - `comments: RedditComment[]`
636
740
  - `commentsPagination: PaginationInfo | null`
741
+ - `commentsTableName: string | null`
637
742
 
638
743
  **`SubredditWithPosts`** — returned by `getSubredditWithPosts()`:
639
744
 
640
745
  - `subreddit: RedditSubreddit`
641
746
  - `posts: RedditPost[]`
642
747
  - `postsPagination: PaginationInfo | null`
748
+ - `postsTableName: string | null`
643
749
 
644
750
  ---
645
751
 
package/dist/index.cjs CHANGED
@@ -25,10 +25,12 @@ __export(index_exports, {
25
25
  OperationFailedError: () => OperationFailedError,
26
26
  OperationTimeoutError: () => OperationTimeoutError,
27
27
  PaginatedResult: () => PaginatedResult,
28
+ ResponseType: () => ResponseType,
28
29
  VERSION: () => VERSION,
29
30
  XpozClient: () => XpozClient,
30
31
  XpozConnectionError: () => XpozConnectionError,
31
- XpozError: () => XpozError
32
+ XpozError: () => XpozError,
33
+ checkForUpdates: () => checkForUpdates
32
34
  });
33
35
  module.exports = __toCommonJS(index_exports);
34
36
 
@@ -282,7 +284,7 @@ function coerce(value) {
282
284
  }
283
285
 
284
286
  // src/version.ts
285
- var VERSION = "0.1.0";
287
+ var VERSION = "0.2.1";
286
288
 
287
289
  // src/mcp/transport.ts
288
290
  var USER_AGENT = `xpoz-ts-sdk/${VERSION}`;
@@ -395,6 +397,12 @@ var OperationCancelledError = class extends XpozError {
395
397
  var DEFAULT_SERVER_URL = "https://mcp.xpoz.ai/mcp";
396
398
  var ENV_API_KEY = "XPOZ_API_KEY";
397
399
  var ENV_SERVER_URL = "XPOZ_SERVER_URL";
400
+ var ResponseType = /* @__PURE__ */ ((ResponseType2) => {
401
+ ResponseType2["Fast"] = "fast";
402
+ ResponseType2["Paging"] = "paging";
403
+ ResponseType2["Csv"] = "csv";
404
+ return ResponseType2;
405
+ })(ResponseType || {});
398
406
  var POLL_INTERVAL_MS = 5e3;
399
407
  var DEFAULT_TIMEOUT_MS = 3e5;
400
408
 
@@ -499,6 +507,9 @@ var BaseNamespace = class {
499
507
  }
500
508
  async callAndMaybePoll(toolName, args) {
501
509
  const result = await this.callTool(toolName, args);
510
+ if ("results" in result) {
511
+ return result;
512
+ }
502
513
  const operationId = result["operationId"];
503
514
  if (operationId) {
504
515
  return waitForResult(this.callTool, operationId, this.timeoutMs);
@@ -517,7 +528,11 @@ var BaseNamespace = class {
517
528
  return this.buildPaginatedResult(pageRaw, parseItem, toolName, baseArgs);
518
529
  };
519
530
  const fetchExport = async (opId) => {
520
- const pollResult = await waitForResult(this.callTool, opId, this.timeoutMs);
531
+ const pollResult = await waitForResult(
532
+ this.callTool,
533
+ opId,
534
+ this.timeoutMs
535
+ );
521
536
  return pollResult["downloadUrl"] ?? "";
522
537
  };
523
538
  return new PaginatedResult({
@@ -593,12 +608,13 @@ var TwitterNamespace = class extends BaseNamespace {
593
608
  }
594
609
  async getPostsByAuthor(identifier, options = {}) {
595
610
  const args = this.buildArgs({
596
- identifier,
597
- identifierType: options.identifierType ?? "username",
611
+ username: identifier,
598
612
  fields: options.fields,
599
613
  startDate: options.startDate,
600
614
  endDate: options.endDate,
601
- forceLatest: options.forceLatest
615
+ forceLatest: options.forceLatest,
616
+ responseType: options.responseType,
617
+ limit: options.limit
602
618
  });
603
619
  const result = await this.callAndMaybePoll(GET_TWITTER_POSTS_BY_AUTHOR, args);
604
620
  return this.buildPaginatedResult(result, parseTwitterPost, GET_TWITTER_POSTS_BY_AUTHOR, args);
@@ -613,9 +629,10 @@ var TwitterNamespace = class extends BaseNamespace {
613
629
  authorId: options.authorId,
614
630
  language: options.language,
615
631
  forceLatest: options.forceLatest,
616
- responseType: options.responseType
632
+ responseType: options.responseType,
633
+ limit: options.limit
617
634
  });
618
- if (options.responseType === "csv") {
635
+ if (options.responseType === "csv" /* Csv */) {
619
636
  const raw = await this.callTool(SEARCH_TWITTER_POSTS, args);
620
637
  const exportOpId = raw["operationId"] ?? raw["dataDumpExportOperationId"] ?? null;
621
638
  const csvRaw = { results: [], dataDumpExportOperationId: exportOpId };
@@ -726,7 +743,9 @@ var InstagramNamespace = class extends BaseNamespace {
726
743
  fields: options.fields,
727
744
  startDate: options.startDate,
728
745
  endDate: options.endDate,
729
- forceLatest: options.forceLatest
746
+ forceLatest: options.forceLatest,
747
+ responseType: options.responseType,
748
+ limit: options.limit
730
749
  });
731
750
  const result = await this.callAndMaybePoll(GET_INSTAGRAM_POSTS_BY_USER, args);
732
751
  return this.buildPaginatedResult(
@@ -737,7 +756,15 @@ var InstagramNamespace = class extends BaseNamespace {
737
756
  );
738
757
  }
739
758
  async searchPosts(query, options = {}) {
740
- const args = this.buildArgs({ query, ...options });
759
+ const args = this.buildArgs({
760
+ query,
761
+ fields: options.fields,
762
+ startDate: options.startDate,
763
+ endDate: options.endDate,
764
+ forceLatest: options.forceLatest,
765
+ responseType: options.responseType,
766
+ limit: options.limit
767
+ });
741
768
  const result = await this.callAndMaybePoll(SEARCH_INSTAGRAM_POSTS, args);
742
769
  return this.buildPaginatedResult(result, parsePost, SEARCH_INSTAGRAM_POSTS, args);
743
770
  }
@@ -904,12 +931,53 @@ var RedditNamespace = class extends BaseNamespace {
904
931
  }
905
932
  };
906
933
 
934
+ // src/versionCheck.ts
935
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@xpoz/xpoz/latest";
936
+ var CHECK_TIMEOUT_MS = 3e3;
937
+ var PACKAGE_NAME = "@xpoz/xpoz";
938
+ var hasWarned = false;
939
+ async function checkForUpdates() {
940
+ if (hasWarned) return;
941
+ try {
942
+ const controller = new AbortController();
943
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
944
+ const response = await fetch(NPM_REGISTRY_URL, {
945
+ signal: controller.signal,
946
+ headers: { Accept: "application/json" }
947
+ });
948
+ clearTimeout(timeout);
949
+ if (!response.ok) return;
950
+ const data = await response.json();
951
+ const latest = data.version;
952
+ if (!latest || latest === VERSION) return;
953
+ if (isNewerVersion(latest, VERSION)) {
954
+ hasWarned = true;
955
+ console.warn(
956
+ `[xpoz] A newer version of ${PACKAGE_NAME} is available: ${latest} (current: ${VERSION}). Run \`npm install ${PACKAGE_NAME}@latest\` to update.`
957
+ );
958
+ }
959
+ } catch {
960
+ }
961
+ }
962
+ function isNewerVersion(latestVersion, currentVersion) {
963
+ const latestVersionParts = latestVersion.split(".").map(Number);
964
+ const currentVersionParts = currentVersion.split(".").map(Number);
965
+ for (let i = 0; i < 3; i++) {
966
+ const latestVersionPart = latestVersionParts[i] ?? 0;
967
+ const currentVersionPart = currentVersionParts[i] ?? 0;
968
+ if (latestVersionPart > currentVersionPart) return true;
969
+ if (latestVersionPart < currentVersionPart) return false;
970
+ }
971
+ return false;
972
+ }
973
+
907
974
  // src/client.ts
908
975
  var XpozClient = class {
909
976
  twitter;
910
977
  instagram;
911
978
  reddit;
912
979
  transport;
980
+ versionCheck;
913
981
  constructor(options = {}) {
914
982
  const apiKey = options.apiKey ?? process.env[ENV_API_KEY];
915
983
  if (!apiKey) {
@@ -917,6 +985,7 @@ var XpozClient = class {
917
985
  `API key required. Get your token at http://xpoz.ai/get-token?utm_source=ts_sdk&utm_medium=sdk (login \u2192 copy token), then pass it as apiKey or set the ${ENV_API_KEY} environment variable.`
918
986
  );
919
987
  }
988
+ this.versionCheck = options.versionCheck ?? true;
920
989
  const serverUrl = options.serverUrl ?? process.env[ENV_SERVER_URL] ?? DEFAULT_SERVER_URL;
921
990
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
922
991
  this.transport = new McpTransport(serverUrl, apiKey);
@@ -927,6 +996,9 @@ var XpozClient = class {
927
996
  }
928
997
  async connect() {
929
998
  await this.transport.connect();
999
+ if (this.versionCheck) {
1000
+ checkForUpdates();
1001
+ }
930
1002
  }
931
1003
  async close() {
932
1004
  await this.transport.close();
@@ -942,8 +1014,10 @@ var XpozClient = class {
942
1014
  OperationFailedError,
943
1015
  OperationTimeoutError,
944
1016
  PaginatedResult,
1017
+ ResponseType,
945
1018
  VERSION,
946
1019
  XpozClient,
947
1020
  XpozConnectionError,
948
- XpozError
1021
+ XpozError,
1022
+ checkForUpdates
949
1023
  });
package/dist/index.d.cts CHANGED
@@ -144,17 +144,24 @@ interface TwitterUser {
144
144
  [key: string]: unknown;
145
145
  }
146
146
 
147
+ declare enum ResponseType {
148
+ Fast = "fast",
149
+ Paging = "paging",
150
+ Csv = "csv"
151
+ }
152
+
147
153
  declare class TwitterNamespace extends BaseNamespace {
148
154
  getPostsByIds(postIds: string[], options?: {
149
155
  fields?: string[];
150
156
  forceLatest?: boolean;
151
157
  }): Promise<TwitterPost[]>;
152
158
  getPostsByAuthor(identifier: string, options?: {
153
- identifierType?: string;
154
159
  fields?: string[];
155
160
  startDate?: string;
156
161
  endDate?: string;
157
162
  forceLatest?: boolean;
163
+ responseType?: ResponseType;
164
+ limit?: number;
158
165
  }): Promise<PaginatedResult<TwitterPost>>;
159
166
  searchPosts(query: string, options?: {
160
167
  fields?: string[];
@@ -164,7 +171,8 @@ declare class TwitterNamespace extends BaseNamespace {
164
171
  authorId?: string;
165
172
  language?: string;
166
173
  forceLatest?: boolean;
167
- responseType?: string;
174
+ responseType?: ResponseType;
175
+ limit?: number;
168
176
  }): Promise<PaginatedResult<TwitterPost>>;
169
177
  getRetweets(postId: string, options?: {
170
178
  fields?: string[];
@@ -206,6 +214,8 @@ declare class TwitterNamespace extends BaseNamespace {
206
214
  endDate?: string;
207
215
  language?: string;
208
216
  forceLatest?: boolean;
217
+ responseType?: ResponseType;
218
+ limit?: number;
209
219
  }): Promise<PaginatedResult<TwitterUser>>;
210
220
  }
211
221
 
@@ -300,12 +310,16 @@ declare class InstagramNamespace extends BaseNamespace {
300
310
  startDate?: string;
301
311
  endDate?: string;
302
312
  forceLatest?: boolean;
313
+ responseType?: ResponseType;
314
+ limit?: number;
303
315
  }): Promise<PaginatedResult<InstagramPost>>;
304
316
  searchPosts(query: string, options?: {
305
317
  fields?: string[];
306
318
  startDate?: string;
307
319
  endDate?: string;
308
320
  forceLatest?: boolean;
321
+ responseType?: ResponseType;
322
+ limit?: number;
309
323
  }): Promise<PaginatedResult<InstagramPost>>;
310
324
  getComments(postId: string, options?: {
311
325
  fields?: string[];
@@ -334,6 +348,8 @@ declare class InstagramNamespace extends BaseNamespace {
334
348
  startDate?: string;
335
349
  endDate?: string;
336
350
  forceLatest?: boolean;
351
+ responseType?: ResponseType;
352
+ limit?: number;
337
353
  }): Promise<PaginatedResult<InstagramUser>>;
338
354
  }
339
355
 
@@ -490,6 +506,8 @@ declare class RedditNamespace extends BaseNamespace {
490
506
  time?: string;
491
507
  subreddit?: string;
492
508
  forceLatest?: boolean;
509
+ responseType?: ResponseType;
510
+ limit?: number;
493
511
  }): Promise<PaginatedResult<RedditPost>>;
494
512
  getPostWithComments(postId: string, options?: {
495
513
  postFields?: string[];
@@ -540,10 +558,12 @@ declare class XpozClient {
540
558
  instagram: InstagramNamespace;
541
559
  reddit: RedditNamespace;
542
560
  private transport;
561
+ private versionCheck;
543
562
  constructor(options?: {
544
563
  apiKey?: string;
545
564
  serverUrl?: string;
546
565
  timeoutMs?: number;
566
+ versionCheck?: boolean;
547
567
  });
548
568
  connect(): Promise<void>;
549
569
  close(): Promise<void>;
@@ -574,6 +594,8 @@ declare class OperationCancelledError extends XpozError {
574
594
  constructor(operationId: string);
575
595
  }
576
596
 
577
- declare const VERSION = "0.1.0";
597
+ declare const VERSION = "0.2.1";
598
+
599
+ declare function checkForUpdates(): Promise<void>;
578
600
 
579
- export { AuthenticationError, type InstagramComment, type InstagramPost, type InstagramUser, OperationCancelledError, OperationFailedError, OperationTimeoutError, PaginatedResult, type PaginationInfo, type RedditComment, type RedditPost, type RedditPostWithComments, type RedditSubreddit, type RedditUser, type SubredditWithPosts, type TwitterPost, type TwitterUser, VERSION, XpozClient, XpozConnectionError, XpozError };
601
+ export { AuthenticationError, type InstagramComment, type InstagramPost, type InstagramUser, OperationCancelledError, OperationFailedError, OperationTimeoutError, PaginatedResult, type PaginationInfo, type RedditComment, type RedditPost, type RedditPostWithComments, type RedditSubreddit, type RedditUser, ResponseType, type SubredditWithPosts, type TwitterPost, type TwitterUser, VERSION, XpozClient, XpozConnectionError, XpozError, checkForUpdates };
package/dist/index.d.ts CHANGED
@@ -144,17 +144,24 @@ interface TwitterUser {
144
144
  [key: string]: unknown;
145
145
  }
146
146
 
147
+ declare enum ResponseType {
148
+ Fast = "fast",
149
+ Paging = "paging",
150
+ Csv = "csv"
151
+ }
152
+
147
153
  declare class TwitterNamespace extends BaseNamespace {
148
154
  getPostsByIds(postIds: string[], options?: {
149
155
  fields?: string[];
150
156
  forceLatest?: boolean;
151
157
  }): Promise<TwitterPost[]>;
152
158
  getPostsByAuthor(identifier: string, options?: {
153
- identifierType?: string;
154
159
  fields?: string[];
155
160
  startDate?: string;
156
161
  endDate?: string;
157
162
  forceLatest?: boolean;
163
+ responseType?: ResponseType;
164
+ limit?: number;
158
165
  }): Promise<PaginatedResult<TwitterPost>>;
159
166
  searchPosts(query: string, options?: {
160
167
  fields?: string[];
@@ -164,7 +171,8 @@ declare class TwitterNamespace extends BaseNamespace {
164
171
  authorId?: string;
165
172
  language?: string;
166
173
  forceLatest?: boolean;
167
- responseType?: string;
174
+ responseType?: ResponseType;
175
+ limit?: number;
168
176
  }): Promise<PaginatedResult<TwitterPost>>;
169
177
  getRetweets(postId: string, options?: {
170
178
  fields?: string[];
@@ -206,6 +214,8 @@ declare class TwitterNamespace extends BaseNamespace {
206
214
  endDate?: string;
207
215
  language?: string;
208
216
  forceLatest?: boolean;
217
+ responseType?: ResponseType;
218
+ limit?: number;
209
219
  }): Promise<PaginatedResult<TwitterUser>>;
210
220
  }
211
221
 
@@ -300,12 +310,16 @@ declare class InstagramNamespace extends BaseNamespace {
300
310
  startDate?: string;
301
311
  endDate?: string;
302
312
  forceLatest?: boolean;
313
+ responseType?: ResponseType;
314
+ limit?: number;
303
315
  }): Promise<PaginatedResult<InstagramPost>>;
304
316
  searchPosts(query: string, options?: {
305
317
  fields?: string[];
306
318
  startDate?: string;
307
319
  endDate?: string;
308
320
  forceLatest?: boolean;
321
+ responseType?: ResponseType;
322
+ limit?: number;
309
323
  }): Promise<PaginatedResult<InstagramPost>>;
310
324
  getComments(postId: string, options?: {
311
325
  fields?: string[];
@@ -334,6 +348,8 @@ declare class InstagramNamespace extends BaseNamespace {
334
348
  startDate?: string;
335
349
  endDate?: string;
336
350
  forceLatest?: boolean;
351
+ responseType?: ResponseType;
352
+ limit?: number;
337
353
  }): Promise<PaginatedResult<InstagramUser>>;
338
354
  }
339
355
 
@@ -490,6 +506,8 @@ declare class RedditNamespace extends BaseNamespace {
490
506
  time?: string;
491
507
  subreddit?: string;
492
508
  forceLatest?: boolean;
509
+ responseType?: ResponseType;
510
+ limit?: number;
493
511
  }): Promise<PaginatedResult<RedditPost>>;
494
512
  getPostWithComments(postId: string, options?: {
495
513
  postFields?: string[];
@@ -540,10 +558,12 @@ declare class XpozClient {
540
558
  instagram: InstagramNamespace;
541
559
  reddit: RedditNamespace;
542
560
  private transport;
561
+ private versionCheck;
543
562
  constructor(options?: {
544
563
  apiKey?: string;
545
564
  serverUrl?: string;
546
565
  timeoutMs?: number;
566
+ versionCheck?: boolean;
547
567
  });
548
568
  connect(): Promise<void>;
549
569
  close(): Promise<void>;
@@ -574,6 +594,8 @@ declare class OperationCancelledError extends XpozError {
574
594
  constructor(operationId: string);
575
595
  }
576
596
 
577
- declare const VERSION = "0.1.0";
597
+ declare const VERSION = "0.2.1";
598
+
599
+ declare function checkForUpdates(): Promise<void>;
578
600
 
579
- export { AuthenticationError, type InstagramComment, type InstagramPost, type InstagramUser, OperationCancelledError, OperationFailedError, OperationTimeoutError, PaginatedResult, type PaginationInfo, type RedditComment, type RedditPost, type RedditPostWithComments, type RedditSubreddit, type RedditUser, type SubredditWithPosts, type TwitterPost, type TwitterUser, VERSION, XpozClient, XpozConnectionError, XpozError };
601
+ export { AuthenticationError, type InstagramComment, type InstagramPost, type InstagramUser, OperationCancelledError, OperationFailedError, OperationTimeoutError, PaginatedResult, type PaginationInfo, type RedditComment, type RedditPost, type RedditPostWithComments, type RedditSubreddit, type RedditUser, ResponseType, type SubredditWithPosts, type TwitterPost, type TwitterUser, VERSION, XpozClient, XpozConnectionError, XpozError, checkForUpdates };
package/dist/index.js CHANGED
@@ -248,7 +248,7 @@ function coerce(value) {
248
248
  }
249
249
 
250
250
  // src/version.ts
251
- var VERSION = "0.1.0";
251
+ var VERSION = "0.2.1";
252
252
 
253
253
  // src/mcp/transport.ts
254
254
  var USER_AGENT = `xpoz-ts-sdk/${VERSION}`;
@@ -361,6 +361,12 @@ var OperationCancelledError = class extends XpozError {
361
361
  var DEFAULT_SERVER_URL = "https://mcp.xpoz.ai/mcp";
362
362
  var ENV_API_KEY = "XPOZ_API_KEY";
363
363
  var ENV_SERVER_URL = "XPOZ_SERVER_URL";
364
+ var ResponseType = /* @__PURE__ */ ((ResponseType2) => {
365
+ ResponseType2["Fast"] = "fast";
366
+ ResponseType2["Paging"] = "paging";
367
+ ResponseType2["Csv"] = "csv";
368
+ return ResponseType2;
369
+ })(ResponseType || {});
364
370
  var POLL_INTERVAL_MS = 5e3;
365
371
  var DEFAULT_TIMEOUT_MS = 3e5;
366
372
 
@@ -465,6 +471,9 @@ var BaseNamespace = class {
465
471
  }
466
472
  async callAndMaybePoll(toolName, args) {
467
473
  const result = await this.callTool(toolName, args);
474
+ if ("results" in result) {
475
+ return result;
476
+ }
468
477
  const operationId = result["operationId"];
469
478
  if (operationId) {
470
479
  return waitForResult(this.callTool, operationId, this.timeoutMs);
@@ -483,7 +492,11 @@ var BaseNamespace = class {
483
492
  return this.buildPaginatedResult(pageRaw, parseItem, toolName, baseArgs);
484
493
  };
485
494
  const fetchExport = async (opId) => {
486
- const pollResult = await waitForResult(this.callTool, opId, this.timeoutMs);
495
+ const pollResult = await waitForResult(
496
+ this.callTool,
497
+ opId,
498
+ this.timeoutMs
499
+ );
487
500
  return pollResult["downloadUrl"] ?? "";
488
501
  };
489
502
  return new PaginatedResult({
@@ -559,12 +572,13 @@ var TwitterNamespace = class extends BaseNamespace {
559
572
  }
560
573
  async getPostsByAuthor(identifier, options = {}) {
561
574
  const args = this.buildArgs({
562
- identifier,
563
- identifierType: options.identifierType ?? "username",
575
+ username: identifier,
564
576
  fields: options.fields,
565
577
  startDate: options.startDate,
566
578
  endDate: options.endDate,
567
- forceLatest: options.forceLatest
579
+ forceLatest: options.forceLatest,
580
+ responseType: options.responseType,
581
+ limit: options.limit
568
582
  });
569
583
  const result = await this.callAndMaybePoll(GET_TWITTER_POSTS_BY_AUTHOR, args);
570
584
  return this.buildPaginatedResult(result, parseTwitterPost, GET_TWITTER_POSTS_BY_AUTHOR, args);
@@ -579,9 +593,10 @@ var TwitterNamespace = class extends BaseNamespace {
579
593
  authorId: options.authorId,
580
594
  language: options.language,
581
595
  forceLatest: options.forceLatest,
582
- responseType: options.responseType
596
+ responseType: options.responseType,
597
+ limit: options.limit
583
598
  });
584
- if (options.responseType === "csv") {
599
+ if (options.responseType === "csv" /* Csv */) {
585
600
  const raw = await this.callTool(SEARCH_TWITTER_POSTS, args);
586
601
  const exportOpId = raw["operationId"] ?? raw["dataDumpExportOperationId"] ?? null;
587
602
  const csvRaw = { results: [], dataDumpExportOperationId: exportOpId };
@@ -692,7 +707,9 @@ var InstagramNamespace = class extends BaseNamespace {
692
707
  fields: options.fields,
693
708
  startDate: options.startDate,
694
709
  endDate: options.endDate,
695
- forceLatest: options.forceLatest
710
+ forceLatest: options.forceLatest,
711
+ responseType: options.responseType,
712
+ limit: options.limit
696
713
  });
697
714
  const result = await this.callAndMaybePoll(GET_INSTAGRAM_POSTS_BY_USER, args);
698
715
  return this.buildPaginatedResult(
@@ -703,7 +720,15 @@ var InstagramNamespace = class extends BaseNamespace {
703
720
  );
704
721
  }
705
722
  async searchPosts(query, options = {}) {
706
- const args = this.buildArgs({ query, ...options });
723
+ const args = this.buildArgs({
724
+ query,
725
+ fields: options.fields,
726
+ startDate: options.startDate,
727
+ endDate: options.endDate,
728
+ forceLatest: options.forceLatest,
729
+ responseType: options.responseType,
730
+ limit: options.limit
731
+ });
707
732
  const result = await this.callAndMaybePoll(SEARCH_INSTAGRAM_POSTS, args);
708
733
  return this.buildPaginatedResult(result, parsePost, SEARCH_INSTAGRAM_POSTS, args);
709
734
  }
@@ -870,12 +895,53 @@ var RedditNamespace = class extends BaseNamespace {
870
895
  }
871
896
  };
872
897
 
898
+ // src/versionCheck.ts
899
+ var NPM_REGISTRY_URL = "https://registry.npmjs.org/@xpoz/xpoz/latest";
900
+ var CHECK_TIMEOUT_MS = 3e3;
901
+ var PACKAGE_NAME = "@xpoz/xpoz";
902
+ var hasWarned = false;
903
+ async function checkForUpdates() {
904
+ if (hasWarned) return;
905
+ try {
906
+ const controller = new AbortController();
907
+ const timeout = setTimeout(() => controller.abort(), CHECK_TIMEOUT_MS);
908
+ const response = await fetch(NPM_REGISTRY_URL, {
909
+ signal: controller.signal,
910
+ headers: { Accept: "application/json" }
911
+ });
912
+ clearTimeout(timeout);
913
+ if (!response.ok) return;
914
+ const data = await response.json();
915
+ const latest = data.version;
916
+ if (!latest || latest === VERSION) return;
917
+ if (isNewerVersion(latest, VERSION)) {
918
+ hasWarned = true;
919
+ console.warn(
920
+ `[xpoz] A newer version of ${PACKAGE_NAME} is available: ${latest} (current: ${VERSION}). Run \`npm install ${PACKAGE_NAME}@latest\` to update.`
921
+ );
922
+ }
923
+ } catch {
924
+ }
925
+ }
926
+ function isNewerVersion(latestVersion, currentVersion) {
927
+ const latestVersionParts = latestVersion.split(".").map(Number);
928
+ const currentVersionParts = currentVersion.split(".").map(Number);
929
+ for (let i = 0; i < 3; i++) {
930
+ const latestVersionPart = latestVersionParts[i] ?? 0;
931
+ const currentVersionPart = currentVersionParts[i] ?? 0;
932
+ if (latestVersionPart > currentVersionPart) return true;
933
+ if (latestVersionPart < currentVersionPart) return false;
934
+ }
935
+ return false;
936
+ }
937
+
873
938
  // src/client.ts
874
939
  var XpozClient = class {
875
940
  twitter;
876
941
  instagram;
877
942
  reddit;
878
943
  transport;
944
+ versionCheck;
879
945
  constructor(options = {}) {
880
946
  const apiKey = options.apiKey ?? process.env[ENV_API_KEY];
881
947
  if (!apiKey) {
@@ -883,6 +949,7 @@ var XpozClient = class {
883
949
  `API key required. Get your token at http://xpoz.ai/get-token?utm_source=ts_sdk&utm_medium=sdk (login \u2192 copy token), then pass it as apiKey or set the ${ENV_API_KEY} environment variable.`
884
950
  );
885
951
  }
952
+ this.versionCheck = options.versionCheck ?? true;
886
953
  const serverUrl = options.serverUrl ?? process.env[ENV_SERVER_URL] ?? DEFAULT_SERVER_URL;
887
954
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
888
955
  this.transport = new McpTransport(serverUrl, apiKey);
@@ -893,6 +960,9 @@ var XpozClient = class {
893
960
  }
894
961
  async connect() {
895
962
  await this.transport.connect();
963
+ if (this.versionCheck) {
964
+ checkForUpdates();
965
+ }
896
966
  }
897
967
  async close() {
898
968
  await this.transport.close();
@@ -907,8 +977,10 @@ export {
907
977
  OperationFailedError,
908
978
  OperationTimeoutError,
909
979
  PaginatedResult,
980
+ ResponseType,
910
981
  VERSION,
911
982
  XpozClient,
912
983
  XpozConnectionError,
913
- XpozError
984
+ XpozError,
985
+ checkForUpdates
914
986
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xpoz/xpoz",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "TypeScript SDK for the Xpoz social media intelligence platform",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -43,5 +43,14 @@
43
43
  "url": "https://github.com/XPOZpublic/xpoz-ts-sdk/issues"
44
44
  },
45
45
  "license": "MIT",
46
- "keywords": ["xpoz", "social-media", "twitter", "instagram", "reddit", "sdk", "mcp", "api"]
46
+ "keywords": [
47
+ "xpoz",
48
+ "social-media",
49
+ "twitter",
50
+ "instagram",
51
+ "reddit",
52
+ "sdk",
53
+ "mcp",
54
+ "api"
55
+ ]
47
56
  }