@oxy.so/core 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/cjs/.tsbuildinfo +1 -1
  2. package/dist/cjs/OxyServices.errors.js +28 -1
  3. package/dist/cjs/i18n/locales/en-US.json +14 -1
  4. package/dist/cjs/i18n/locales/es-ES.json +14 -1
  5. package/dist/cjs/i18n/locales/locales/en-US.json +14 -1
  6. package/dist/cjs/i18n/locales/locales/es-ES.json +14 -1
  7. package/dist/cjs/mixins/OxyServices.agency.js +113 -0
  8. package/dist/cjs/mixins/OxyServices.assets.js +90 -0
  9. package/dist/cjs/mixins/OxyServices.connectedApps.js +20 -3
  10. package/dist/cjs/mixins/OxyServices.utility.js +1 -21
  11. package/dist/cjs/mixins/index.js +3 -4
  12. package/dist/cjs/server/index.js +6 -1
  13. package/dist/cjs/server/traffic.js +107 -0
  14. package/dist/cjs/server/trafficNodeHttp.js +86 -0
  15. package/dist/cjs/utils/textNormalization.js +1 -1
  16. package/dist/esm/.tsbuildinfo +1 -1
  17. package/dist/esm/OxyServices.errors.js +26 -0
  18. package/dist/esm/i18n/locales/en-US.json +14 -1
  19. package/dist/esm/i18n/locales/es-ES.json +14 -1
  20. package/dist/esm/i18n/locales/locales/en-US.json +14 -1
  21. package/dist/esm/i18n/locales/locales/es-ES.json +14 -1
  22. package/dist/esm/mixins/OxyServices.agency.js +110 -0
  23. package/dist/esm/mixins/OxyServices.assets.js +91 -1
  24. package/dist/esm/mixins/OxyServices.connectedApps.js +20 -3
  25. package/dist/esm/mixins/OxyServices.utility.js +1 -21
  26. package/dist/esm/mixins/index.js +3 -4
  27. package/dist/esm/server/index.js +2 -0
  28. package/dist/esm/server/traffic.js +104 -0
  29. package/dist/esm/server/trafficNodeHttp.js +79 -0
  30. package/dist/esm/utils/textNormalization.js +1 -1
  31. package/dist/types/.tsbuildinfo +1 -1
  32. package/dist/types/OxyServices.errors.d.ts +24 -0
  33. package/dist/types/index.d.ts +2 -1
  34. package/dist/types/mixins/OxyServices.agency.d.ts +135 -0
  35. package/dist/types/mixins/OxyServices.assets.d.ts +43 -1
  36. package/dist/types/mixins/OxyServices.connectedApps.d.ts +15 -0
  37. package/dist/types/mixins/OxyServices.links.d.ts +2 -2
  38. package/dist/types/mixins/OxyServices.utility.d.ts +0 -9
  39. package/dist/types/mixins/index.d.ts +2 -2
  40. package/dist/types/models/interfaces.d.ts +26 -0
  41. package/dist/types/server/index.d.ts +3 -0
  42. package/dist/types/server/traffic.d.ts +27 -0
  43. package/dist/types/server/trafficNodeHttp.d.ts +6 -0
  44. package/dist/types/utils/textNormalization.d.ts +1 -1
  45. package/package.json +4 -4
  46. package/src/OxyServices.errors.ts +36 -0
  47. package/src/i18n/locales/en-US.json +14 -1
  48. package/src/i18n/locales/es-ES.json +14 -1
  49. package/src/index.ts +15 -0
  50. package/src/mixins/OxyServices.agency.ts +277 -0
  51. package/src/mixins/OxyServices.assets.ts +105 -2
  52. package/src/mixins/OxyServices.connectedApps.ts +42 -4
  53. package/src/mixins/OxyServices.utility.ts +1 -26
  54. package/src/mixins/__tests__/OxyServices.serviceLinkedDownloadUrl.test.ts +139 -0
  55. package/src/mixins/__tests__/agency.test.ts +120 -0
  56. package/src/mixins/__tests__/connectedApps.test.ts +23 -0
  57. package/src/mixins/index.ts +4 -5
  58. package/src/models/interfaces.ts +27 -0
  59. package/src/server/__tests__/traffic.test.ts +49 -0
  60. package/src/server/__tests__/trafficNodeHttp.test.ts +38 -0
  61. package/src/server/index.ts +5 -0
  62. package/src/server/traffic.ts +116 -0
  63. package/src/server/trafficNodeHttp.ts +69 -0
  64. package/src/utils/textNormalization.ts +1 -1
  65. package/src/mixins/OxyServices.links.ts +0 -107
  66. package/src/mixins/__tests__/OxyServices.links.test.ts +0 -154
@@ -1,154 +0,0 @@
1
- /**
2
- * Link-preview mixin tests.
3
- *
4
- * Stubs `makeRequest` so the tests run with no network, then asserts:
5
- * - `getLinkPreview` builds `GET /links/preview?url=<percent-encoded>&wait=0|1`,
6
- * percent-encoding the target URL so its `?`/`&`/`=` cannot break out of the
7
- * query string, defaults `wait` to `0`, sends `wait=1` when `{ wait: true }`,
8
- * reads uncached (`cache:false`), returns the preview, and surfaces errors.
9
- * - `getLinkPreviews` de-duplicates input, no-ops to `{}` on empty input,
10
- * chunks at 50 URLs/request (`POST /links/previews`), merges every chunk's
11
- * `data` map keyed by the requested url, and surfaces a chunk failure.
12
- */
13
-
14
- import type { LinkPreview } from '@oxy.so/contracts';
15
- import { OxyServices } from '../../OxyServices';
16
-
17
- const sampleResolved: LinkPreview = {
18
- url: 'https://news.example.com/a',
19
- status: 'resolved',
20
- title: 'Headline',
21
- description: 'Lede',
22
- image: 'https://cloud.oxy.so/img123',
23
- siteName: 'Example News',
24
- favicon: 'https://cloud.oxy.so/fav123',
25
- resolvedAt: '2026-06-28T00:00:00.000Z',
26
- };
27
-
28
- describe('OxyServices.links', () => {
29
- let oxy: OxyServices;
30
- let makeRequestSpy: jest.SpyInstance;
31
-
32
- beforeEach(() => {
33
- oxy = new OxyServices({ baseURL: 'http://test.invalid' });
34
- makeRequestSpy = jest.spyOn(oxy, 'makeRequest');
35
- });
36
-
37
- afterEach(() => {
38
- jest.restoreAllMocks();
39
- });
40
-
41
- describe('getLinkPreview', () => {
42
- it('percent-encodes the URL and defaults wait=0', async () => {
43
- makeRequestSpy.mockResolvedValueOnce(sampleResolved);
44
-
45
- const result = await oxy.getLinkPreview('https://news.example.com/a?b=c&d=e');
46
-
47
- expect(result).toEqual(sampleResolved);
48
- expect(makeRequestSpy).toHaveBeenCalledTimes(1);
49
- expect(makeRequestSpy).toHaveBeenCalledWith(
50
- 'GET',
51
- '/links/preview?url=https%3A%2F%2Fnews.example.com%2Fa%3Fb%3Dc%26d%3De&wait=0',
52
- undefined,
53
- { cache: false },
54
- );
55
- });
56
-
57
- it('sends wait=1 when opts.wait is true', async () => {
58
- makeRequestSpy.mockResolvedValueOnce({ url: 'https://x.test/', status: 'pending' });
59
-
60
- await oxy.getLinkPreview('https://x.test/', { wait: true });
61
-
62
- expect(makeRequestSpy).toHaveBeenCalledWith(
63
- 'GET',
64
- '/links/preview?url=https%3A%2F%2Fx.test%2F&wait=1',
65
- undefined,
66
- { cache: false },
67
- );
68
- });
69
-
70
- it('surfaces errors via handleError', async () => {
71
- makeRequestSpy.mockRejectedValueOnce(new Error('boom'));
72
-
73
- await expect(oxy.getLinkPreview('https://x.test/')).rejects.toThrow('boom');
74
- });
75
- });
76
-
77
- describe('getLinkPreviews', () => {
78
- it('returns {} and performs no network call for empty / whitespace input', async () => {
79
- await expect(oxy.getLinkPreviews([])).resolves.toEqual({});
80
- await expect(oxy.getLinkPreviews(['', ' '])).resolves.toEqual({});
81
- expect(makeRequestSpy).not.toHaveBeenCalled();
82
- });
83
-
84
- it('de-duplicates and sends a single chunk for <= 50 unique URLs', async () => {
85
- // makeRequest unwraps the API's `{ data }` envelope, so the resolved value
86
- // is the bare record (NOT `{ data: {...} }`) — mirror that real shape here.
87
- const record: Record<string, LinkPreview> = { 'https://a.test/': sampleResolved };
88
- makeRequestSpy.mockResolvedValueOnce(record);
89
-
90
- const result = await oxy.getLinkPreviews([
91
- 'https://a.test/',
92
- 'https://a.test/', // duplicate
93
- ' ', // dropped
94
- ]);
95
-
96
- expect(result).toEqual(record);
97
- expect(makeRequestSpy).toHaveBeenCalledTimes(1);
98
- expect(makeRequestSpy).toHaveBeenCalledWith(
99
- 'POST',
100
- '/links/previews',
101
- { urls: ['https://a.test/'] },
102
- { cache: false },
103
- );
104
- });
105
-
106
- it('chunks at 50 URLs per request and merges each chunk data map', async () => {
107
- const urls = Array.from({ length: 120 }, (_, i) => `https://site.test/${i}`);
108
-
109
- makeRequestSpy.mockImplementation(
110
- async (
111
- _method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
112
- _url: string,
113
- data?: { urls: string[] },
114
- ): Promise<Record<string, LinkPreview>> => {
115
- const chunkUrls = data?.urls ?? [];
116
- const dataMap: Record<string, LinkPreview> = {};
117
- for (const u of chunkUrls) {
118
- dataMap[u] = { url: u, status: 'resolved', title: `t-${u}` };
119
- }
120
- return dataMap;
121
- },
122
- );
123
-
124
- const result = await oxy.getLinkPreviews(urls);
125
-
126
- // 120 unique URLs => 50 + 50 + 20 across three POSTs.
127
- expect(makeRequestSpy).toHaveBeenCalledTimes(3);
128
- const chunkSizes = makeRequestSpy.mock.calls.map((call) => (call[2] as { urls: string[] }).urls.length);
129
- expect(chunkSizes).toEqual([50, 50, 20]);
130
-
131
- // Every requested URL is present in the merged, request-keyed map.
132
- expect(Object.keys(result)).toHaveLength(120);
133
- expect(result['https://site.test/0']).toEqual({
134
- url: 'https://site.test/0',
135
- status: 'resolved',
136
- title: 't-https://site.test/0',
137
- });
138
- expect(result['https://site.test/119']).toEqual({
139
- url: 'https://site.test/119',
140
- status: 'resolved',
141
- title: 't-https://site.test/119',
142
- });
143
- });
144
-
145
- it('surfaces a chunk failure via handleError', async () => {
146
- const urls = Array.from({ length: 60 }, (_, i) => `https://site.test/${i}`);
147
- makeRequestSpy
148
- .mockResolvedValueOnce({ data: {} })
149
- .mockRejectedValueOnce(new Error('chunk failed'));
150
-
151
- await expect(oxy.getLinkPreviews(urls)).rejects.toThrow('chunk failed');
152
- });
153
- });
154
- });