@attlaz/client 1.107.0 → 1.109.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 CHANGED
@@ -35,6 +35,7 @@ import { ProductMatchEndpoint } from './MarketPulse/Service/ProductMatchEndpoint
35
35
  import { PulseCatalogProductEndpoint } from './MarketPulse/Service/PulseCatalogProductEndpoint.js';
36
36
  import { PriceAlertEndpoint } from './MarketPulse/Service/PriceAlertEndpoint.js';
37
37
  import { PricingEndpoint } from './MarketPulse/Service/PricingEndpoint.js';
38
+ import { OpportunityEndpoint } from './MarketPulse/Service/OpportunityEndpoint.js';
38
39
  import { ProductGroupEndpoint } from './MarketPulse/Service/ProductGroupEndpoint.js';
39
40
  import { CatalogProductEndpoint } from './Catalog/Service/CatalogProductEndpoint.js';
40
41
  import { GenerateContentEndpoint } from './DataEnrichment/Service/GenerateContentEndpoint.js';
@@ -147,6 +148,7 @@ export declare class Client {
147
148
  getCatalogProductEndpoint(): CatalogProductEndpoint;
148
149
  getPriceAlertEndpoint(): PriceAlertEndpoint;
149
150
  getPricingEndpoint(): PricingEndpoint;
151
+ getOpportunityEndpoint(): OpportunityEndpoint;
150
152
  getProductGroupEndpoint(): ProductGroupEndpoint;
151
153
  getGenerateContentEndpoint(): GenerateContentEndpoint;
152
154
  getCompetitorEndpoint(): CompetitorEndpoint;
package/dist/Client.js CHANGED
@@ -35,6 +35,7 @@ import { ProductMatchEndpoint } from './MarketPulse/Service/ProductMatchEndpoint
35
35
  import { PulseCatalogProductEndpoint } from './MarketPulse/Service/PulseCatalogProductEndpoint.js';
36
36
  import { PriceAlertEndpoint } from './MarketPulse/Service/PriceAlertEndpoint.js';
37
37
  import { PricingEndpoint } from './MarketPulse/Service/PricingEndpoint.js';
38
+ import { OpportunityEndpoint } from './MarketPulse/Service/OpportunityEndpoint.js';
38
39
  import { ProductGroupEndpoint } from './MarketPulse/Service/ProductGroupEndpoint.js';
39
40
  import { CatalogProductEndpoint } from './Catalog/Service/CatalogProductEndpoint.js';
40
41
  import { GenerateContentEndpoint } from './DataEnrichment/Service/GenerateContentEndpoint.js';
@@ -105,6 +106,7 @@ export class Client {
105
106
  PulseCatalogProductEndpoint,
106
107
  PriceAlertEndpoint,
107
108
  PricingEndpoint,
109
+ OpportunityEndpoint,
108
110
  ProductGroupEndpoint,
109
111
  CatalogProductEndpoint,
110
112
  GenerateContentEndpoint,
@@ -339,6 +341,9 @@ export class Client {
339
341
  getPricingEndpoint() {
340
342
  return this.getEndpoint('pricing', this.Store.PricingEndpoint);
341
343
  }
344
+ getOpportunityEndpoint() {
345
+ return this.getEndpoint('opportunity', this.Store.OpportunityEndpoint);
346
+ }
342
347
  getProductGroupEndpoint() {
343
348
  return this.getEndpoint('product-group', this.Store.ProductGroupEndpoint);
344
349
  }
@@ -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;
@@ -0,0 +1,41 @@
1
+ import { ApiRecord } from '../../Model/ApiRecord.js';
2
+ /**
3
+ * What the detection engine found wrong with a product's price.
4
+ *
5
+ * Duplicated from apps-lib rather than imported, per the client's enum-duplication convention: the
6
+ * client must not depend on a server package.
7
+ */
8
+ export type OpportunityType = 'OVERPRICED_WITH_ROOM' | 'NEEDLESSLY_CHEAP' | 'UNCOMPETITIVE' | 'JUST_CHANGED';
9
+ /**
10
+ * One detected pricing opportunity on one catalog product — the `/pulse/…/opportunities` resource.
11
+ *
12
+ * A derived cache, not history: every recompute delete-and-replaces the whole catalog, so a product
13
+ * that stops qualifying simply stops appearing. A product can hold two of these, because
14
+ * `JUST_CHANGED` co-occurs with one of the price types — which is why `id` is the (product, type)
15
+ * pair rather than the product id.
16
+ *
17
+ * The reference price throughout is the **median** of the product's matched, in-stock competitors,
18
+ * never the average. `NEEDLESSLY_CHEAP` is the one type judged against the cheapest rival instead.
19
+ */
20
+ export declare class ProductOpportunity {
21
+ id: string;
22
+ catalogProductId: string;
23
+ type: OpportunityType;
24
+ /** Money at stake. Ranks the worklist within one type; not comparable across types. */
25
+ score: number;
26
+ priceVsMedianPct: number | null;
27
+ marginPct: number | null;
28
+ medianCompetitorPrice: number | null;
29
+ competitorCount: number;
30
+ computedAt: Date | null;
31
+ /** Which threshold set produced this row, so rows from an older configuration are identifiable. */
32
+ configVersion: number;
33
+ productName: string;
34
+ productImage: string | null;
35
+ productBrand: string | null;
36
+ ownPrice: number;
37
+ /** The product's own currency. Never assume one — see DECISIONS.md (2026-08-08). */
38
+ currency: string | null;
39
+ constructor(id: string, catalogProductId: string, type: OpportunityType);
40
+ static parse(raw: ApiRecord): ProductOpportunity;
41
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * One detected pricing opportunity on one catalog product — the `/pulse/…/opportunities` resource.
3
+ *
4
+ * A derived cache, not history: every recompute delete-and-replaces the whole catalog, so a product
5
+ * that stops qualifying simply stops appearing. A product can hold two of these, because
6
+ * `JUST_CHANGED` co-occurs with one of the price types — which is why `id` is the (product, type)
7
+ * pair rather than the product id.
8
+ *
9
+ * The reference price throughout is the **median** of the product's matched, in-stock competitors,
10
+ * never the average. `NEEDLESSLY_CHEAP` is the one type judged against the cheapest rival instead.
11
+ */
12
+ export class ProductOpportunity {
13
+ id;
14
+ catalogProductId;
15
+ type;
16
+ /** Money at stake. Ranks the worklist within one type; not comparable across types. */
17
+ score = 0;
18
+ priceVsMedianPct = null;
19
+ marginPct = null;
20
+ medianCompetitorPrice = null;
21
+ competitorCount = 0;
22
+ computedAt = null;
23
+ /** Which threshold set produced this row, so rows from an older configuration are identifiable. */
24
+ configVersion = 0;
25
+ // The own product, carried on the row so a worklist renders without a call per product.
26
+ productName = '';
27
+ productImage = null;
28
+ productBrand = null;
29
+ ownPrice = 0;
30
+ /** The product's own currency. Never assume one — see DECISIONS.md (2026-08-08). */
31
+ currency = null;
32
+ constructor(id, catalogProductId, type) {
33
+ this.id = id;
34
+ this.catalogProductId = catalogProductId;
35
+ this.type = type;
36
+ }
37
+ static parse(raw) {
38
+ const opportunity = new ProductOpportunity(raw.id, raw.catalog_product, raw.type);
39
+ opportunity.score = Number(raw.score ?? 0);
40
+ opportunity.priceVsMedianPct = raw.price_vs_median_pct === null || raw.price_vs_median_pct === undefined ? null : Number(raw.price_vs_median_pct);
41
+ opportunity.marginPct = raw.margin_pct === null || raw.margin_pct === undefined ? null : Number(raw.margin_pct);
42
+ opportunity.medianCompetitorPrice = raw.median_competitor_price === null || raw.median_competitor_price === undefined ? null : Number(raw.median_competitor_price);
43
+ opportunity.competitorCount = Number(raw.competitor_count ?? 0);
44
+ opportunity.computedAt = raw.computed_at ? new Date(raw.computed_at) : null;
45
+ opportunity.configVersion = Number(raw.config_version ?? 0);
46
+ opportunity.productName = raw.product_name ?? '';
47
+ opportunity.productImage = raw.product_image ?? null;
48
+ opportunity.productBrand = raw.product_brand ?? null;
49
+ opportunity.ownPrice = Number(raw.own_price ?? 0);
50
+ opportunity.currency = raw.currency ?? null;
51
+ return opportunity;
52
+ }
53
+ }
@@ -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
- /** Create a new (pending) crawl job for a vendor. */
14
- create(vendorId: string): Promise<CrawlJob>;
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
- /** Create a new (pending) crawl job for a vendor. */
13
- async create(vendorId) {
14
- const result = await this.requestObject('/pulse/crawl-jobs', { vendor: vendorId }, CrawlJob.parse, 'POST');
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');
@@ -0,0 +1,17 @@
1
+ import { CatalogId } from '../../Catalog/Model/CatalogId.js';
2
+ import { CursorPagination } from '../../Model/Pagination/CursorPagination.js';
3
+ import { CollectionResult } from '../../Model/Result/CollectionResult.js';
4
+ import { Endpoint } from '../../Service/Endpoint.js';
5
+ import { OpportunityType, ProductOpportunity } from '../Model/ProductOpportunity.js';
6
+ /**
7
+ * The pricing opportunity worklist — `/pulse/catalogs/:catalogId/opportunities`.
8
+ */
9
+ export declare class OpportunityEndpoint extends Endpoint {
10
+ /**
11
+ * Opportunities in a catalog, ranked by money at stake.
12
+ *
13
+ * Pass a `type`: the score means something different per type, so an unfiltered read interleaves
14
+ * four scales into one order. Unfiltered is an overview of what exists, not a ranked worklist.
15
+ */
16
+ getOpportunities(catalogId: CatalogId | string, pagination?: CursorPagination | null, type?: OpportunityType | null): Promise<CollectionResult<ProductOpportunity>>;
17
+ }
@@ -0,0 +1,23 @@
1
+ import { path } from '../../Http/Data/Path.js';
2
+ import { QueryString } from '../../Http/Data/QueryString.js';
3
+ import { Endpoint } from '../../Service/Endpoint.js';
4
+ import { ProductOpportunity } from '../Model/ProductOpportunity.js';
5
+ /**
6
+ * The pricing opportunity worklist — `/pulse/catalogs/:catalogId/opportunities`.
7
+ */
8
+ export class OpportunityEndpoint extends Endpoint {
9
+ /**
10
+ * Opportunities in a catalog, ranked by money at stake.
11
+ *
12
+ * Pass a `type`: the score means something different per type, so an unfiltered read interleaves
13
+ * four scales into one order. Unfiltered is an overview of what exists, not a ranked worklist.
14
+ */
15
+ async getOpportunities(catalogId, pagination = null, type = null) {
16
+ const queryString = new QueryString(path('/pulse/catalogs/:catalogId/opportunities', { catalogId: catalogId.toString() }));
17
+ if (type !== null) {
18
+ queryString.set('type', type);
19
+ }
20
+ queryString.addPagination(pagination);
21
+ return await this.requestCollection(queryString, ProductOpportunity.parse);
22
+ }
23
+ }
@@ -16,8 +16,8 @@ export declare class AdapterConnectionEndpoint extends Endpoint {
16
16
  * it is a partial update. A configuration the API does not let a client set (a provider token)
17
17
  * is untouched rather than cleared.
18
18
  *
19
- * Needs an API that serves the PATCH route; it is also still registered as POST for clients
20
- * released before the verb changed.
19
+ * Needs an API that serves the PATCH route. The POST alias this once relied on has since been
20
+ * removed from Core-Api, so a client older than 1.104.0 can no longer write this at all.
21
21
  */
22
22
  saveConnectionConfiguration(connectionId: string, configuration: AdapterConnectionConfigurationValue[]): Promise<boolean>;
23
23
  getConnectionEvents(connectionId: string, pagination: CursorPagination): Promise<CollectionResult<AdapterConnectionEvent>>;
@@ -84,8 +84,8 @@ export class AdapterConnectionEndpoint extends Endpoint {
84
84
  * it is a partial update. A configuration the API does not let a client set (a provider token)
85
85
  * is untouched rather than cleared.
86
86
  *
87
- * Needs an API that serves the PATCH route; it is also still registered as POST for clients
88
- * released before the verb changed.
87
+ * Needs an API that serves the PATCH route. The POST alias this once relied on has since been
88
+ * removed from Core-Api, so a client older than 1.104.0 can no longer write this at all.
89
89
  */
90
90
  async saveConnectionConfiguration(connectionId, configuration) {
91
91
  try {
package/dist/index.d.ts CHANGED
@@ -122,6 +122,8 @@ export { PricingRule } from './MarketPulse/Model/PricingRule.js';
122
122
  export { PriceSuggestion, PriceSuggestionStatus } from './MarketPulse/Model/PriceSuggestion.js';
123
123
  export { PricingRuleData } from './MarketPulse/Model/PricingRuleData.js';
124
124
  export { PricingEndpoint } from './MarketPulse/Service/PricingEndpoint.js';
125
+ export { ProductOpportunity, OpportunityType } from './MarketPulse/Model/ProductOpportunity.js';
126
+ export { OpportunityEndpoint } from './MarketPulse/Service/OpportunityEndpoint.js';
125
127
  export { ProductGroup, ProductGroupType } from './MarketPulse/Model/ProductGroup.js';
126
128
  export { ProductGroupData } from './MarketPulse/Model/ProductGroupData.js';
127
129
  export { ProductGroupEndpoint } from './MarketPulse/Service/ProductGroupEndpoint.js';
package/dist/index.js CHANGED
@@ -99,6 +99,8 @@ export { PriceAlertEndpoint } from './MarketPulse/Service/PriceAlertEndpoint.js'
99
99
  export { PricingRule } from './MarketPulse/Model/PricingRule.js';
100
100
  export { PriceSuggestion } from './MarketPulse/Model/PriceSuggestion.js';
101
101
  export { PricingEndpoint } from './MarketPulse/Service/PricingEndpoint.js';
102
+ export { ProductOpportunity } from './MarketPulse/Model/ProductOpportunity.js';
103
+ export { OpportunityEndpoint } from './MarketPulse/Service/OpportunityEndpoint.js';
102
104
  export { ProductGroup } from './MarketPulse/Model/ProductGroup.js';
103
105
  export { ProductGroupEndpoint } from './MarketPulse/Service/ProductGroupEndpoint.js';
104
106
  export { CatalogProduct } from './Catalog/Model/CatalogProduct.js';
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const VERSION = "1.107.0";
1
+ export declare const VERSION = "1.108.0";
package/dist/version.js CHANGED
@@ -1 +1 @@
1
- export const VERSION = "1.107.0";
1
+ export const VERSION = "1.108.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@attlaz/client",
3
- "version": "1.107.0",
3
+ "version": "1.109.0",
4
4
  "description": "Javascript Client to access Attlaz API",
5
5
  "types": "./dist/index.d.ts",
6
6
  "main": "./dist/index.js",