laplace-api 5.3.0 → 5.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "laplace-api",
3
- "version": "5.3.0",
3
+ "version": "5.5.0",
4
4
  "description": "Client library for Laplace API for the US stock market and BIST (Istanbul stock market) fundamental financial data.",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -25,7 +25,16 @@ export enum NewsOrderBy {
25
25
  QUALITY_SCORE = "quality_score",
26
26
  }
27
27
 
28
- export interface GetNewsV2Params {
28
+ export enum NewsLane {
29
+ GLOBAL_MACRO = "global_macro",
30
+ TR_EKONOMI = "tr_ekonomi",
31
+ BIST = "bist",
32
+ FAST_MOVERS = "fast_movers",
33
+ }
34
+
35
+ export interface GetNewsParams {
36
+ lane?: NewsLane;
37
+ apiSource?: string;
29
38
  newsType?: NewsType;
30
39
  orderBy?: NewsOrderBy;
31
40
  orderByDirection?: SortDirection;
@@ -41,6 +50,9 @@ export interface GetNewsV2Params {
41
50
  size?: number;
42
51
  }
43
52
 
53
+ /** @deprecated Use {@link GetNewsParams}; v1 and v2 now accept the same filters. */
54
+ export type GetNewsV2Params = GetNewsParams;
55
+
44
56
  export interface News {
45
57
  url: string;
46
58
  imageUrl: string;
@@ -102,6 +114,16 @@ export interface NewsCategory {
102
114
  name: string;
103
115
  }
104
116
 
117
+ export interface NewsLaneInfo {
118
+ id: NewsLane;
119
+ label: string;
120
+ }
121
+
122
+ export interface NewsApiSource {
123
+ id: string;
124
+ name: string;
125
+ }
126
+
105
127
  export class NewsClient extends Client {
106
128
  async getHighlights(
107
129
  region: Region,
@@ -128,42 +150,30 @@ export class NewsClient extends Client {
128
150
  });
129
151
  }
130
152
 
131
- async getNews(
132
- region: Region,
133
- locale: Locale,
134
- newsType?: NewsType,
135
- page?: number,
136
- size?: number,
137
- orderBy?: NewsOrderBy,
138
- orderByDirection?: SortDirection,
139
- extraFilters?: string
140
- ): Promise<PaginatedResponse<News>> {
141
- const params = {
142
- region,
143
- locale,
144
- ...(newsType != null && { newsType }),
145
- ...(page != null && { page }),
146
- ...(size != null && { size }),
147
- ...(orderBy != null && { orderBy }),
148
- ...(orderByDirection != null && { orderByDirection }),
149
- ...(extraFilters != null && { extraFilters }),
150
- };
153
+ async getNewsLanes(): Promise<NewsLaneInfo[]> {
154
+ return this.sendRequest<NewsLaneInfo[]>({
155
+ method: "GET",
156
+ url: "/api/v1/news/lanes",
157
+ });
158
+ }
151
159
 
152
- return this.sendRequest<PaginatedResponse<News>>({
160
+ async getApiSourceNames(): Promise<NewsApiSource[]> {
161
+ return this.sendRequest<NewsApiSource[]>({
153
162
  method: "GET",
154
- url: "/api/v1/news",
155
- params,
163
+ url: "/api/v1/news/api-source-names",
156
164
  });
157
165
  }
158
166
 
159
- async getNewsV2(
167
+ private buildNewsFilterParams(
160
168
  region: Region,
161
169
  locale: Locale,
162
- options?: GetNewsV2Params
163
- ): Promise<PaginatedResponse<NewsV2>> {
164
- const params = {
170
+ options?: GetNewsParams
171
+ ): Record<string, unknown> {
172
+ return {
165
173
  region,
166
174
  locale,
175
+ ...(options?.lane != null && { lane: options.lane }),
176
+ ...(options?.apiSource != null && { apiSource: options.apiSource }),
167
177
  ...(options?.newsType != null && { newsType: options.newsType }),
168
178
  ...(options?.orderBy != null && { orderBy: options.orderBy }),
169
179
  ...(options?.orderByDirection != null && {
@@ -186,11 +196,29 @@ export class NewsClient extends Client {
186
196
  ...(options?.page != null && { page: options.page }),
187
197
  ...(options?.size != null && { size: options.size }),
188
198
  };
199
+ }
189
200
 
201
+ async getNews(
202
+ region: Region,
203
+ locale: Locale,
204
+ options?: GetNewsParams
205
+ ): Promise<PaginatedResponse<News>> {
206
+ return this.sendRequest<PaginatedResponse<News>>({
207
+ method: "GET",
208
+ url: "/api/v1/news",
209
+ params: this.buildNewsFilterParams(region, locale, options),
210
+ });
211
+ }
212
+
213
+ async getNewsV2(
214
+ region: Region,
215
+ locale: Locale,
216
+ options?: GetNewsParams
217
+ ): Promise<PaginatedResponse<NewsV2>> {
190
218
  return this.sendRequest<PaginatedResponse<NewsV2>>({
191
219
  method: "GET",
192
220
  url: "/api/v2/news",
193
- params,
221
+ params: this.buildNewsFilterParams(region, locale, options),
194
222
  });
195
223
  }
196
224
 
@@ -200,9 +228,13 @@ export class NewsClient extends Client {
200
228
  sectors?: string[],
201
229
  tickers?: string[],
202
230
  categories?: string[],
203
- industries?: string[]
231
+ industries?: string[],
232
+ lane?: NewsLane,
233
+ apiSource?: string[]
204
234
  ): { events: AsyncIterable<NewsV2[]>, cancel: () => void } {
205
235
  let url = `${this["baseUrl"]}/api/v1/news/stream?locale=${locale}&region=${region}`;
236
+ if (lane != null) url += `&lane=${encodeURIComponent(lane)}`;
237
+ if (apiSource?.length) url += `&apiSource=${encodeURIComponent(apiSource.join(","))}`;
206
238
  if (sectors?.length) url += `&sectors=${encodeURIComponent(sectors.join(","))}`;
207
239
  if (tickers?.length) url += `&tickers=${encodeURIComponent(tickers.join(","))}`;
208
240
  if (categories?.length) url += `&categories=${encodeURIComponent(categories.join(","))}`;
@@ -5,6 +5,7 @@ import {
5
5
  NewsClient,
6
6
  NewsType,
7
7
  NewsOrderBy,
8
+ NewsLane,
8
9
  } from "../client/news";
9
10
  import "./client_test_suite";
10
11
  import { Region, Locale } from "../client/collections";
@@ -41,6 +42,13 @@ const mockNewsCategoriesResponse = [
41
42
  { id: "13705", name: "Stock Spesific News" }
42
43
  ];
43
44
 
45
+ const mockNewsLanesResponse = [
46
+ { id: "global_macro", label: "Global Macro" },
47
+ { id: "tr_ekonomi", label: "TR Ekonomi" },
48
+ { id: "bist", label: "BIST" },
49
+ { id: "fast_movers", label: "Fast Movers" }
50
+ ];
51
+
44
52
  const mockNewsResponse = {
45
53
  items: [
46
54
  {
@@ -125,16 +133,36 @@ describe("NewsClient", () => {
125
133
  expect(typeof c.name).toBe("string");
126
134
  });
127
135
 
136
+ test("getNewsLanes returns valid data", async () => {
137
+ const resp = await client.getNewsLanes();
138
+
139
+ expect(Array.isArray(resp)).toBe(true);
140
+ if (resp.length > 0) {
141
+ const l = resp[0];
142
+ expect(typeof l.id).toBe("string");
143
+ expect(typeof l.label).toBe("string");
144
+ }
145
+ });
146
+
147
+ test("getApiSourceNames returns valid data", async () => {
148
+ const resp = await client.getApiSourceNames();
149
+
150
+ expect(Array.isArray(resp)).toBe(true);
151
+ if (resp.length > 0) {
152
+ const s = resp[0];
153
+ expect(typeof s.id).toBe("string");
154
+ expect(typeof s.name).toBe("string");
155
+ }
156
+ });
157
+
128
158
  test("getNews returns valid paginated data", async () => {
129
- const resp = await client.getNews(
130
- Region.Us,
131
- Locale.Tr,
132
- NewsType.BRIEFS,
133
- 0,
134
- 10,
135
- NewsOrderBy.TIMESTAMP,
136
- SortDirection.Desc
137
- );
159
+ const resp = await client.getNews(Region.Us, Locale.Tr, {
160
+ newsType: NewsType.BRIEFS,
161
+ page: 0,
162
+ size: 10,
163
+ orderBy: NewsOrderBy.TIMESTAMP,
164
+ orderByDirection: SortDirection.Desc,
165
+ });
138
166
 
139
167
  expect(resp).toBeDefined();
140
168
  expect(typeof resp.recordCount).toBe("number");
@@ -352,20 +380,87 @@ describe("NewsClient", () => {
352
380
  });
353
381
  });
354
382
 
383
+ describe("getNewsLanes", () => {
384
+ test("calls correct endpoint and matches raw response", async () => {
385
+ cli.request.mockResolvedValueOnce({ data: mockNewsLanesResponse });
386
+
387
+ const resp = await client.getNewsLanes();
388
+
389
+ expect(cli.request).toHaveBeenCalledTimes(1);
390
+ const call = cli.request.mock.calls[0][0];
391
+
392
+ expect(call.method).toBe("GET");
393
+ expect(call.url).toBe("/api/v1/news/lanes");
394
+
395
+ expect(resp).toEqual(mockNewsLanesResponse);
396
+ });
397
+
398
+ test("bubbles up request error", async () => {
399
+ cli.request.mockRejectedValueOnce(new Error("Failed to fetch lanes"));
400
+
401
+ await expect(client.getNewsLanes()).rejects.toThrow(
402
+ "Failed to fetch lanes"
403
+ );
404
+
405
+ expect(cli.request).toHaveBeenCalledTimes(1);
406
+ });
407
+ });
408
+
409
+ describe("getApiSourceNames", () => {
410
+ const mockApiSourceNames = [
411
+ { id: "BBCBusiness", name: "BBC Business" },
412
+ { id: "MarketWatch", name: "MarketWatch" },
413
+ { id: "GazeteOksijen", name: "Gazete Oksijen" }
414
+ ];
415
+
416
+ test("calls correct endpoint and matches raw response", async () => {
417
+ cli.request.mockResolvedValueOnce({ data: mockApiSourceNames });
418
+
419
+ const resp = await client.getApiSourceNames();
420
+
421
+ expect(cli.request).toHaveBeenCalledTimes(1);
422
+ const call = cli.request.mock.calls[0][0];
423
+
424
+ expect(call.method).toBe("GET");
425
+ expect(call.url).toBe("/api/v1/news/api-source-names");
426
+
427
+ expect(resp).toEqual(mockApiSourceNames);
428
+ });
429
+
430
+ test("bubbles up request error", async () => {
431
+ cli.request.mockRejectedValueOnce(
432
+ new Error("Failed to fetch api source names")
433
+ );
434
+
435
+ await expect(client.getApiSourceNames()).rejects.toThrow(
436
+ "Failed to fetch api source names"
437
+ );
438
+
439
+ expect(cli.request).toHaveBeenCalledTimes(1);
440
+ });
441
+ });
442
+
355
443
  describe("getNews", () => {
356
444
  test("calls correct endpoint/params and matches raw response", async () => {
357
445
  cli.request.mockResolvedValueOnce({ data: mockNewsResponse });
358
446
 
359
- const resp = await client.getNews(
360
- Region.Tr,
361
- Locale.Tr,
362
- NewsType.BRIEFS,
363
- 1,
364
- 10,
365
- NewsOrderBy.TIMESTAMP,
366
- SortDirection.Desc,
367
- undefined
368
- );
447
+ const resp = await client.getNews(Region.Tr, Locale.Tr, {
448
+ lane: NewsLane.BIST,
449
+ apiSource: "BBCBusiness,MarketWatch",
450
+ newsType: NewsType.BRIEFS,
451
+ page: 1,
452
+ size: 10,
453
+ orderBy: NewsOrderBy.TIMESTAMP,
454
+ orderByDirection: SortDirection.Desc,
455
+ symbols: "AAPL,MSFT",
456
+ categories: "Sector News",
457
+ sectors: "Technology",
458
+ industries: "Software",
459
+ qualityScoreMin: 7,
460
+ qualityScoreMax: 10,
461
+ timestampFrom: "2026-05-01",
462
+ timestampTo: "2026-06-01",
463
+ });
369
464
 
370
465
  expect(cli.request).toHaveBeenCalledTimes(1);
371
466
  const call = cli.request.mock.calls[0][0];
@@ -375,11 +470,21 @@ describe("NewsClient", () => {
375
470
  expect(call.params).toEqual({
376
471
  region: Region.Tr,
377
472
  locale: Locale.Tr,
473
+ lane: NewsLane.BIST,
474
+ apiSource: "BBCBusiness,MarketWatch",
378
475
  newsType: NewsType.BRIEFS,
379
476
  page: 1,
380
477
  size: 10,
381
478
  orderBy: NewsOrderBy.TIMESTAMP,
382
- orderByDirection: SortDirection.Desc
479
+ orderByDirection: SortDirection.Desc,
480
+ symbols: "AAPL,MSFT",
481
+ categories: "Sector News",
482
+ sectors: "Technology",
483
+ industries: "Software",
484
+ qualityScoreMin: 7,
485
+ qualityScoreMax: 10,
486
+ timestampFrom: "2026-05-01",
487
+ timestampTo: "2026-06-01",
383
488
  });
384
489
 
385
490
  expect(resp.recordCount).toBe(352);
@@ -439,15 +544,13 @@ describe("NewsClient", () => {
439
544
  cli.request.mockRejectedValueOnce(new Error("Failed to fetch news"));
440
545
 
441
546
  await expect(
442
- client.getNews(
443
- Region.Tr,
444
- Locale.Tr,
445
- NewsType.REUTERS,
446
- 0,
447
- 10,
448
- NewsOrderBy.TIMESTAMP,
449
- SortDirection.Desc
450
- )
547
+ client.getNews(Region.Tr, Locale.Tr, {
548
+ newsType: NewsType.REUTERS,
549
+ page: 0,
550
+ size: 10,
551
+ orderBy: NewsOrderBy.TIMESTAMP,
552
+ orderByDirection: SortDirection.Desc,
553
+ })
451
554
  ).rejects.toThrow("Failed to fetch news");
452
555
 
453
556
  expect(cli.request).toHaveBeenCalledTimes(1);
@@ -459,6 +562,8 @@ describe("NewsClient", () => {
459
562
  cli.request.mockResolvedValueOnce({ data: mockNewsV2Response });
460
563
 
461
564
  const resp = await client.getNewsV2(Region.Tr, Locale.Tr, {
565
+ lane: NewsLane.GLOBAL_MACRO,
566
+ apiSource: "BBCBusiness,MarketWatch",
462
567
  newsType: NewsType.BRIEFS,
463
568
  page: 1,
464
569
  size: 10,
@@ -482,6 +587,8 @@ describe("NewsClient", () => {
482
587
  expect(call.params).toEqual({
483
588
  region: Region.Tr,
484
589
  locale: Locale.Tr,
590
+ lane: NewsLane.GLOBAL_MACRO,
591
+ apiSource: "BBCBusiness,MarketWatch",
485
592
  newsType: NewsType.BRIEFS,
486
593
  page: 1,
487
594
  size: 10,
@@ -587,7 +694,7 @@ describe("NewsClient", () => {
587
694
  data: mockAsyncIterator
588
695
  });
589
696
 
590
- const { events, cancel } = client.streamNews(Region.Us, Locale.En, ["tech"], ["AAPL"], ["category"], ["software"]);
697
+ const { events, cancel } = client.streamNews(Region.Us, Locale.En, ["tech"], ["AAPL"], ["category"], ["software"], NewsLane.GLOBAL_MACRO, ["BBCBusiness"]);
591
698
 
592
699
  for await (const _ of events) {
593
700
  break;
@@ -595,7 +702,7 @@ describe("NewsClient", () => {
595
702
 
596
703
  expect(axiosGetSpy).toHaveBeenCalledTimes(1);
597
704
  const callArgs = axiosGetSpy.mock.calls[0];
598
- expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=en&region=us&sectors=tech&tickers=AAPL&categories=category&industries=software`);
705
+ expect(callArgs[0]).toBe(`${client["baseUrl"]}/api/v1/news/stream?locale=en&region=us&lane=global_macro&apiSource=BBCBusiness&sectors=tech&tickers=AAPL&categories=category&industries=software`);
599
706
 
600
707
  cancel();
601
708
  axiosGetSpy.mockRestore();