@attlaz/client 1.106.1 → 1.108.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/dist/Client.d.ts +8 -1
- package/dist/Client.js +9 -0
- package/dist/Http/Transport/OAuthClient.d.ts +20 -0
- package/dist/Http/Transport/OAuthClient.js +35 -3
- package/dist/MarketPulse/Model/CrawlJob.d.ts +5 -0
- package/dist/MarketPulse/Model/CrawlJob.js +6 -0
- package/dist/MarketPulse/Service/CrawlJobEndpoint.d.ts +8 -2
- package/dist/MarketPulse/Service/CrawlJobEndpoint.js +9 -3
- package/dist/index.d.ts +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/Client.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OAuthClientToken } from './Http/OAuthClientToken.js';
|
|
2
2
|
import { ITransport, ParseErrorHandler } from './Http/Transport/ITransport.js';
|
|
3
|
-
import { OAuthClient } from './Http/Transport/OAuthClient.js';
|
|
3
|
+
import { OAuthClient, TokenChangeHandler } from './Http/Transport/OAuthClient.js';
|
|
4
4
|
import { AccessTokenEndpoint } from './Service/AccessTokenEndpoint.js';
|
|
5
5
|
import { AdapterConnectionEndpoint } from './Service/AdapterConnectionEndpoint.js';
|
|
6
6
|
import { AdapterEndpoint } from './Service/AdapterEndpoint.js';
|
|
@@ -91,6 +91,13 @@ export declare class Client {
|
|
|
91
91
|
getToken(): OAuthClientToken | null;
|
|
92
92
|
setToken(token: OAuthClientToken): void;
|
|
93
93
|
unsetAccessToken(): void;
|
|
94
|
+
/**
|
|
95
|
+
* Observe tokens the client acquires or discards on its own — see {@link TokenChangeHandler}.
|
|
96
|
+
* A consumer that persists the token must use this rather than reading getToken() after a call:
|
|
97
|
+
* refresh tokens rotate, so a refreshed token that is not written back is replayed later and
|
|
98
|
+
* rejected. Pass null to stop observing. OAuth-only; a custom transport has no token.
|
|
99
|
+
*/
|
|
100
|
+
setTokenChangeHandler(handler: TokenChangeHandler | null): void;
|
|
94
101
|
getApiInformation(): Promise<{
|
|
95
102
|
version: string;
|
|
96
103
|
}>;
|
package/dist/Client.js
CHANGED
|
@@ -184,6 +184,15 @@ export class Client {
|
|
|
184
184
|
unsetAccessToken() {
|
|
185
185
|
this.httpClient.unsetToken();
|
|
186
186
|
}
|
|
187
|
+
/**
|
|
188
|
+
* Observe tokens the client acquires or discards on its own — see {@link TokenChangeHandler}.
|
|
189
|
+
* A consumer that persists the token must use this rather than reading getToken() after a call:
|
|
190
|
+
* refresh tokens rotate, so a refreshed token that is not written back is replayed later and
|
|
191
|
+
* rejected. Pass null to stop observing. OAuth-only; a custom transport has no token.
|
|
192
|
+
*/
|
|
193
|
+
setTokenChangeHandler(handler) {
|
|
194
|
+
this.httpClient.setTokenChangeHandler(handler);
|
|
195
|
+
}
|
|
187
196
|
async getApiInformation() {
|
|
188
197
|
const result = await this.httpClient.request('/system/health', null, 'GET', false);
|
|
189
198
|
if (result.version === undefined) {
|
|
@@ -3,10 +3,20 @@ import { Parameters } from '../Data/Parameters.js';
|
|
|
3
3
|
import { OAuthClientOptions } from '../OAuthClientOptions.js';
|
|
4
4
|
import { OAuthClientToken } from '../OAuthClientToken.js';
|
|
5
5
|
import { ITransport, ParseErrorHandler } from './ITransport.js';
|
|
6
|
+
/**
|
|
7
|
+
* Invoked whenever the client changes the token by itself: after a successful authenticate or
|
|
8
|
+
* refresh (the server rotates refresh tokens, so the previous one is revoked at that moment), and
|
|
9
|
+
* with null when a refresh is rejected and the session is gone. A consumer that persists the token
|
|
10
|
+
* must listen here — reading getToken() after a call is too late, because a rotated token that is
|
|
11
|
+
* never written back is replayed later and rejected. Not invoked for setToken()/unsetToken(), which
|
|
12
|
+
* are the caller's own writes.
|
|
13
|
+
*/
|
|
14
|
+
export type TokenChangeHandler = (token: OAuthClientToken | null) => void;
|
|
6
15
|
export declare class OAuthClient implements ITransport {
|
|
7
16
|
private readonly options;
|
|
8
17
|
private debug;
|
|
9
18
|
private oauthClientToken;
|
|
19
|
+
private tokenChangeHandler;
|
|
10
20
|
private tokenAcquisitionPromise;
|
|
11
21
|
private version;
|
|
12
22
|
private parseErrorHandler;
|
|
@@ -43,6 +53,16 @@ export declare class OAuthClient implements ITransport {
|
|
|
43
53
|
getToken(): OAuthClientToken | null;
|
|
44
54
|
setToken(token: OAuthClientToken): void;
|
|
45
55
|
unsetToken(): void;
|
|
56
|
+
/**
|
|
57
|
+
* Observe tokens the client acquires or discards on its own. See {@link TokenChangeHandler}.
|
|
58
|
+
* Pass null to stop observing.
|
|
59
|
+
*/
|
|
60
|
+
setTokenChangeHandler(handler: TokenChangeHandler | null): void;
|
|
61
|
+
/**
|
|
62
|
+
* The single place the client updates its own token, so no acquisition path can change it
|
|
63
|
+
* without the handler hearing about it.
|
|
64
|
+
*/
|
|
65
|
+
private applyToken;
|
|
46
66
|
enableDebug(): void;
|
|
47
67
|
disableDebug(): void;
|
|
48
68
|
isDebugEnabled(): boolean;
|
|
@@ -9,6 +9,7 @@ export class OAuthClient {
|
|
|
9
9
|
options;
|
|
10
10
|
debug = false;
|
|
11
11
|
oauthClientToken = null;
|
|
12
|
+
tokenChangeHandler = null;
|
|
12
13
|
// Single-flight guard for token acquisition (initial client-credentials auth or refresh), so
|
|
13
14
|
// concurrent requests share one in-flight acquisition instead of each hitting the token endpoint.
|
|
14
15
|
tokenAcquisitionPromise = null;
|
|
@@ -34,7 +35,7 @@ export class OAuthClient {
|
|
|
34
35
|
params.client_secret = this.options.clientSecret;
|
|
35
36
|
}
|
|
36
37
|
const rawAuthToken = await this.requestToken(params);
|
|
37
|
-
this.
|
|
38
|
+
this.applyToken(this.tokenToOauthClientToken(rawAuthToken));
|
|
38
39
|
return true;
|
|
39
40
|
}
|
|
40
41
|
if (this.options.clientId !== null && this.options.clientSecret !== null) {
|
|
@@ -45,7 +46,7 @@ export class OAuthClient {
|
|
|
45
46
|
client_secret: this.options.clientSecret,
|
|
46
47
|
scope: this.options.scopes.join(' '),
|
|
47
48
|
});
|
|
48
|
-
this.
|
|
49
|
+
this.applyToken(this.tokenToOauthClientToken(rawAuthToken));
|
|
49
50
|
}
|
|
50
51
|
return true;
|
|
51
52
|
}
|
|
@@ -77,7 +78,7 @@ export class OAuthClient {
|
|
|
77
78
|
params.client_secret = this.options.clientSecret;
|
|
78
79
|
}
|
|
79
80
|
const rawAuthToken = await this.requestToken(params);
|
|
80
|
-
this.
|
|
81
|
+
this.applyToken(this.tokenToOauthClientToken(rawAuthToken));
|
|
81
82
|
}
|
|
82
83
|
catch (e) {
|
|
83
84
|
if (e instanceof ClientError) {
|
|
@@ -88,6 +89,11 @@ export class OAuthClient {
|
|
|
88
89
|
// error. Transient failures (network / 5xx) are rethrown unchanged so a
|
|
89
90
|
// hiccup doesn't force a logout.
|
|
90
91
|
if (e.httpStatus !== null && e.httpStatus >= 400 && e.httpStatus < 500) {
|
|
92
|
+
// Drop the rejected token: keeping it means every later request retries the
|
|
93
|
+
// same dead refresh token, and the server's firewall blocks the IP after a
|
|
94
|
+
// handful of those. A confidential client re-mints via client_credentials on
|
|
95
|
+
// the next request; a public one now fails fast with 401 instead of looping.
|
|
96
|
+
this.applyToken(null);
|
|
91
97
|
throw new ClientError('Refresh token rejected, re-authentication required', HttpStatus.HTTP_UNAUTHORIZED);
|
|
92
98
|
}
|
|
93
99
|
throw e;
|
|
@@ -258,6 +264,32 @@ export class OAuthClient {
|
|
|
258
264
|
unsetToken() {
|
|
259
265
|
this.oauthClientToken = null;
|
|
260
266
|
}
|
|
267
|
+
/**
|
|
268
|
+
* Observe tokens the client acquires or discards on its own. See {@link TokenChangeHandler}.
|
|
269
|
+
* Pass null to stop observing.
|
|
270
|
+
*/
|
|
271
|
+
setTokenChangeHandler(handler) {
|
|
272
|
+
this.tokenChangeHandler = handler;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* The single place the client updates its own token, so no acquisition path can change it
|
|
276
|
+
* without the handler hearing about it.
|
|
277
|
+
*/
|
|
278
|
+
applyToken(token) {
|
|
279
|
+
this.oauthClientToken = token;
|
|
280
|
+
if (this.tokenChangeHandler === null) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
try {
|
|
284
|
+
this.tokenChangeHandler(token);
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
// A consumer's persistence failure must not fail the authentication that succeeded —
|
|
288
|
+
// the token in memory is valid either way. Report it: silently dropping it would hide
|
|
289
|
+
// exactly the stale-token bug this handler exists to prevent.
|
|
290
|
+
console.error('[OAuthClient] Token change handler threw', { error });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
261
293
|
enableDebug() {
|
|
262
294
|
this.debug = true;
|
|
263
295
|
}
|
|
@@ -12,6 +12,11 @@ export declare class CrawlJob {
|
|
|
12
12
|
requestedAt: Date | null;
|
|
13
13
|
startedAt: Date | null;
|
|
14
14
|
finishedAt: Date | null;
|
|
15
|
+
/**
|
|
16
|
+
* This crawl looked at only part of the catalogue, so it is excluded from the delisting window:
|
|
17
|
+
* it never looked for most products, so its silence about them proves nothing.
|
|
18
|
+
*/
|
|
19
|
+
isPartial: boolean;
|
|
15
20
|
/** Progress (populated by list endpoints): total pages and how many have been processed. */
|
|
16
21
|
total: number;
|
|
17
22
|
processed: number;
|
|
@@ -10,6 +10,11 @@ export class CrawlJob {
|
|
|
10
10
|
requestedAt;
|
|
11
11
|
startedAt;
|
|
12
12
|
finishedAt;
|
|
13
|
+
/**
|
|
14
|
+
* This crawl looked at only part of the catalogue, so it is excluded from the delisting window:
|
|
15
|
+
* it never looked for most products, so its silence about them proves nothing.
|
|
16
|
+
*/
|
|
17
|
+
isPartial = false;
|
|
13
18
|
/** Progress (populated by list endpoints): total pages and how many have been processed. */
|
|
14
19
|
total = 0;
|
|
15
20
|
processed = 0;
|
|
@@ -27,6 +32,7 @@ export class CrawlJob {
|
|
|
27
32
|
job.requestedAt = raw.requested_at ? new Date(raw.requested_at) : null;
|
|
28
33
|
job.startedAt = raw.started_at ? new Date(raw.started_at) : null;
|
|
29
34
|
job.finishedAt = raw.finished_at ? new Date(raw.finished_at) : null;
|
|
35
|
+
job.isPartial = raw.is_partial === true;
|
|
30
36
|
job.total = Number(raw.total ?? 0);
|
|
31
37
|
job.processed = Number(raw.processed ?? 0);
|
|
32
38
|
return job;
|
|
@@ -10,8 +10,14 @@ import { Endpoint } from '../../Service/Endpoint.js';
|
|
|
10
10
|
* post-crawl processing (price history, product matching, price-index snapshots).
|
|
11
11
|
*/
|
|
12
12
|
export declare class CrawlJobEndpoint extends Endpoint {
|
|
13
|
-
/**
|
|
14
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Create a new (pending) crawl job for a vendor.
|
|
15
|
+
*
|
|
16
|
+
* @param isPartial This crawl will deliberately look at only part of the catalogue. Such a crawl
|
|
17
|
+
* is excluded from the delisting window: it never looked for most products, so its
|
|
18
|
+
* silence about them is not evidence they are gone.
|
|
19
|
+
*/
|
|
20
|
+
create(vendorId: string, isPartial?: boolean): Promise<CrawlJob>;
|
|
15
21
|
/** Update a crawl job's status ('started', 'finished', 'cancelled'). */
|
|
16
22
|
updateStatus(crawlJobId: string, status: CrawlJobStatus): Promise<CrawlJob>;
|
|
17
23
|
/** A vendor's crawl jobs (newest first), each with progress (`total` / `processed`). */
|
|
@@ -9,9 +9,15 @@ import { Endpoint } from '../../Service/Endpoint.js';
|
|
|
9
9
|
* post-crawl processing (price history, product matching, price-index snapshots).
|
|
10
10
|
*/
|
|
11
11
|
export class CrawlJobEndpoint extends Endpoint {
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Create a new (pending) crawl job for a vendor.
|
|
14
|
+
*
|
|
15
|
+
* @param isPartial This crawl will deliberately look at only part of the catalogue. Such a crawl
|
|
16
|
+
* is excluded from the delisting window: it never looked for most products, so its
|
|
17
|
+
* silence about them is not evidence they are gone.
|
|
18
|
+
*/
|
|
19
|
+
async create(vendorId, isPartial = false) {
|
|
20
|
+
const result = await this.requestObject('/pulse/crawl-jobs', { vendor: vendorId, isPartial }, CrawlJob.parse, 'POST');
|
|
15
21
|
const job = result.getData();
|
|
16
22
|
if (job === null) {
|
|
17
23
|
throw new Error('Unable to create crawl job: empty response');
|
package/dist/index.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export { ITransport } from './Http/Transport/ITransport.js';
|
|
|
11
11
|
export { DirectTransport } from './Http/Transport/DirectTransport.js';
|
|
12
12
|
export type { DirectTransportRoute } from './Http/Transport/DirectTransport.js';
|
|
13
13
|
export { OAuthClient } from './Http/Transport/OAuthClient.js';
|
|
14
|
+
export type { TokenChangeHandler } from './Http/Transport/OAuthClient.js';
|
|
14
15
|
export { OAuthClientOptions } from './Http/OAuthClientOptions.js';
|
|
15
16
|
export { OAuthClientToken } from './Http/OAuthClientToken.js';
|
|
16
17
|
export { ClientError } from './Http/ClientError.js';
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.107.0";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "1.
|
|
1
|
+
export const VERSION = "1.107.0";
|