@instruments/materialgraph 0.32.0 → 0.35.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/association-types.d.ts +11 -11
- package/associations-client.d.ts +7 -7
- package/associations-client.js +5 -5
- package/brand-types.d.ts +37 -41
- package/brand-types.js +3 -5
- package/brands-client.d.ts +22 -9
- package/brands-client.js +20 -6
- package/by-spec-types.d.ts +2 -3
- package/catalog-discovery-types.d.ts +3 -3
- package/catalogue-client.d.ts +2 -50
- package/catalogue-client.js +2 -10
- package/catalogue-types.d.ts +1 -1
- package/categories-client.d.ts +3 -5
- package/categories-client.js +1 -1
- package/category-types.d.ts +17 -30
- package/citation-types.d.ts +2 -2
- package/client-vocabulary.d.ts +2 -2
- package/client-vocabulary.js +4 -1
- package/client.d.ts +75 -102
- package/client.js +83 -100
- package/color-profiles-types.d.ts +70 -0
- package/colors-client.d.ts +76 -0
- package/colors-client.js +52 -0
- package/find-companions-types.d.ts +8 -8
- package/{find-variants-by-colour-types.d.ts → find-variants-by-color-types.d.ts} +70 -64
- package/find-variants-by-vector-types.d.ts +4 -1
- package/get-variant-types.d.ts +14 -2
- package/http-transport.d.ts +1 -1
- package/http-transport.js +1 -2
- package/identifiers.d.ts +2 -2
- package/identifiers.js +1 -1
- package/index.d.ts +5 -13
- package/index.js +1 -3
- package/list-variant-facets-types.d.ts +4 -4
- package/list-variants-types.d.ts +108 -19
- package/material-asset-types.d.ts +3 -3
- package/material-block-types.d.ts +9 -9
- package/material-types.d.ts +10 -8
- package/operations.d.ts +2 -2
- package/operations.js +14 -24
- package/package.json +2 -2
- package/{resolve-colour-types.d.ts → resolve-color-types.d.ts} +18 -18
- package/response-schemas.generated.d.ts +2 -2
- package/response-schemas.generated.js +40 -68
- package/response-schemas.js +1 -1
- package/samplize-catalogue-types.d.ts +4 -4
- package/samplize-colorvec.js +6 -6
- package/{corpus-scope-types.d.ts → search-catalogue-subset-types.d.ts} +3 -3
- package/search-variants-types.d.ts +91 -0
- package/types.d.ts +31 -283
- package/webmcp.generated.js +4 -9
- package/attribution-types.d.ts +0 -51
- package/attribution-types.js +0 -13
- package/by-colour-types.d.ts +0 -320
- package/colour-region-types.d.ts +0 -18
- package/colours-client.d.ts +0 -70
- package/colours-client.js +0 -38
- package/material-query-plan-types.d.ts +0 -70
- package/palette-search-types.d.ts +0 -171
- package/palette-search-types.js +0 -1
- package/query-understanding-types.d.ts +0 -254
- package/query-understanding-types.js +0 -1
- package/resolve-colour-types.js +0 -1
- package/search-card-types.d.ts +0 -86
- package/search-card-types.js +0 -1
- package/search-index-plan-types.d.ts +0 -74
- package/search-index-plan-types.js +0 -1
- package/search-refinement-types.d.ts +0 -103
- package/search-refinement-types.js +0 -2
- package/search-request-types.d.ts +0 -170
- package/search-request-types.js +0 -1
- package/search-response-receipt-types.d.ts +0 -24
- package/search-response-receipt-types.js +0 -1
- package/search-response-types.d.ts +0 -11
- package/search-response-types.js +0 -1
- package/search-result-validation.d.ts +0 -9
- package/search-result-validation.js +0 -83
- package/spec-types.d.ts +0 -109
- package/spec-types.js +0 -54
- package/specifications.d.ts +0 -34
- package/specifications.js +0 -56
- package/validation.d.ts +0 -43
- package/validation.js +0 -117
- /package/{by-colour-types.js → color-profiles-types.js} +0 -0
- /package/{colour-region-types.js → find-variants-by-color-types.js} +0 -0
- /package/{corpus-scope-types.js → resolve-color-types.js} +0 -0
- /package/{find-variants-by-colour-types.js → search-catalogue-subset-types.js} +0 -0
- /package/{material-query-plan-types.js → search-variants-types.js} +0 -0
package/search-card-types.d.ts
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module sdk/search-card-types
|
|
3
|
-
* @description The `view:"card"` search projection — the lean grid item and the
|
|
4
|
-
* envelope that carries it.
|
|
5
|
-
*
|
|
6
|
-
* Split out of `types.ts` — which is over the repository's source-size limit —
|
|
7
|
-
* along the same seam `match-types.ts` took: one view of one surface, and
|
|
8
|
-
* nothing else. The full-view item and the shared envelope stay in `types.ts`,
|
|
9
|
-
* which this module reads `SearchResponse` from.
|
|
10
|
-
*/
|
|
11
|
-
import type { ProductId, VariantId } from "./identifiers.js";
|
|
12
|
-
import type { OpenEnum, SearchData, SearchResponse } from "./types.js";
|
|
13
|
-
/**
|
|
14
|
-
* The lean `view:"card"` result item — identity + name + code + thumbnail +
|
|
15
|
-
* score.
|
|
16
|
-
*
|
|
17
|
-
* A PROJECTION, not a smaller answer: a field the card omits (palette, verdict,
|
|
18
|
-
* signals, availability) is unread here, never known-absent. Re-read the same
|
|
19
|
-
* `variantId` at `view: "full"` or via `getVariant()` before claiming anything the
|
|
20
|
-
* card does not carry.
|
|
21
|
-
*/
|
|
22
|
-
export type SearchCardItem = {
|
|
23
|
-
/** Accepted sampleability fact. Absent means unknown, never false. */
|
|
24
|
-
sampleable?: boolean;
|
|
25
|
-
/** Opaque — round-trip verbatim. */
|
|
26
|
-
variantId: VariantId;
|
|
27
|
-
productId: ProductId | null;
|
|
28
|
-
brandSlug: string | null;
|
|
29
|
-
brand: string;
|
|
30
|
-
/** Product name (the full item's `product`). */
|
|
31
|
-
name: string;
|
|
32
|
-
variantName: string | null;
|
|
33
|
-
imageUrl: string | null;
|
|
34
|
-
/**
|
|
35
|
-
* SHA-256 of the displayed image `imageUrl` points at, as a hex digest. Key a
|
|
36
|
-
* cache or dedupe on it rather than the URL. ABSENT means no hash is on
|
|
37
|
-
* record, never that there is no image.
|
|
38
|
-
*/
|
|
39
|
-
imageSha256?: string;
|
|
40
|
-
/**
|
|
41
|
-
* How many eligible variants the row's product has, the same set
|
|
42
|
-
* `listVariants` pages, so a follow-up list returns this many: the card's
|
|
43
|
-
* "12 colourways". ABSENT means the product is unknown or has no eligible
|
|
44
|
-
* variant.
|
|
45
|
-
*/
|
|
46
|
-
variantCount?: number;
|
|
47
|
-
/**
|
|
48
|
-
* The manufacturer's own item code, the same key the full item carries.
|
|
49
|
-
* Reach for it when two cards read alike: `name` is the PRODUCT name, and a
|
|
50
|
-
* paint product is a whole collection, so brand, product name and variant
|
|
51
|
-
* name together are shared by most of the catalogue. ABSENT means no code is
|
|
52
|
-
* recorded, never that the variant cannot be ordered.
|
|
53
|
-
*/
|
|
54
|
-
manufacturerCode?: string;
|
|
55
|
-
projectSaveCapability?: "allowed";
|
|
56
|
-
sourceStatus?: "provisional";
|
|
57
|
-
score: number;
|
|
58
|
-
/**
|
|
59
|
-
* Resolved values for the attribute ids sent in `returnAttributes`, keyed by
|
|
60
|
-
* attribute id. ABSENT entirely when `returnAttributes` was not sent.
|
|
61
|
-
*
|
|
62
|
-
* Three states, all different, and reading them as two is the mistake this
|
|
63
|
-
* shape exists to prevent: no `attributes` key means you did not ask; a key
|
|
64
|
-
* whose value is `null` means this material carries no accepted value for that
|
|
65
|
-
* attribute; anything else is what the graph holds. Values resolve with the
|
|
66
|
-
* normal variant-over-product precedence, and an array is a multi-valued
|
|
67
|
-
* attribute rather than a list of candidates.
|
|
68
|
-
*/
|
|
69
|
-
attributes?: Record<string, string | string[] | null>;
|
|
70
|
-
};
|
|
71
|
-
/** The `view:"card"` response form — the lean grid projection of {@link SearchResponse}. */
|
|
72
|
-
export type SearchCardResponse = {
|
|
73
|
-
data: Omit<SearchData, "view" | "results"> & {
|
|
74
|
-
view: OpenEnum<"card">;
|
|
75
|
-
results: SearchCardItem[];
|
|
76
|
-
};
|
|
77
|
-
} & Omit<SearchResponse, "data">;
|
|
78
|
-
/**
|
|
79
|
-
* The request forms the server answers with {@link SearchCardResponse}:
|
|
80
|
-
* `view: "card"`, or `intent: "typeahead"`, which forces the card view.
|
|
81
|
-
*/
|
|
82
|
-
export type CardProjectionRequest = {
|
|
83
|
-
view: "card";
|
|
84
|
-
} | {
|
|
85
|
-
intent: "typeahead";
|
|
86
|
-
};
|
package/search-card-types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module search-index-plan-types
|
|
3
|
-
* @description The plan the one search ran on, as `/search` reports it under
|
|
4
|
-
* `explain.plan` (ADR 0031). Mirrors `searchIndexPlanReceiptSchema` in
|
|
5
|
-
* `@materialgraph/products/search-index/plan-schema`; `key-parity.test.ts`
|
|
6
|
-
* holds the two to the same fields.
|
|
7
|
-
*/
|
|
8
|
-
import type { ColourRegionRange } from "./colour-region-types.js";
|
|
9
|
-
import type { CorpusScope } from "./corpus-scope-types.js";
|
|
10
|
-
/** One attribute the plan wants: its terms, or any value when `presence`. */
|
|
11
|
-
type SearchIndexPlanWant = {
|
|
12
|
-
attributeId: string;
|
|
13
|
-
/** Share of the category's materials stating the attribute, 0 to 1. */
|
|
14
|
-
fill: number;
|
|
15
|
-
/** The words it was read from, or `"(filter)"` for a caller's filter. */
|
|
16
|
-
phrase: string;
|
|
17
|
-
/** Any stated value satisfies it; `terms` is then empty. */
|
|
18
|
-
presence?: true;
|
|
19
|
-
/** A hard want removes rows; a soft one only counts toward the order. */
|
|
20
|
-
strength: "hard" | "soft";
|
|
21
|
-
terms: string[];
|
|
22
|
-
};
|
|
23
|
-
/**
|
|
24
|
-
* The query's plan: the category, brands, wanted and excluded attribute terms,
|
|
25
|
-
* the colour, the words left for text, and the caller's corpus scope.
|
|
26
|
-
*/
|
|
27
|
-
export type SearchIndexPlan = {
|
|
28
|
-
brands: {
|
|
29
|
-
ids: string[];
|
|
30
|
-
names: string[];
|
|
31
|
-
phrase: string;
|
|
32
|
-
} | null;
|
|
33
|
-
category: {
|
|
34
|
-
label: string;
|
|
35
|
-
phrase: string;
|
|
36
|
-
productTypeIds: string[];
|
|
37
|
-
} | null;
|
|
38
|
-
colour: {
|
|
39
|
-
family12: string[];
|
|
40
|
-
familyId: string;
|
|
41
|
-
hex: string;
|
|
42
|
-
oklab: [number, number, number];
|
|
43
|
-
phrase: string;
|
|
44
|
-
/** The colour's region, when the plan holds one rather than a point. */
|
|
45
|
-
range?: ColourRegionRange;
|
|
46
|
-
} | null;
|
|
47
|
-
/** Present only when the caller sent a scope narrower than the whole graph. */
|
|
48
|
-
corpusScope?: Exclude<CorpusScope, "all">;
|
|
49
|
-
excludeBrands: string[];
|
|
50
|
-
excludes: {
|
|
51
|
-
attributeId: string;
|
|
52
|
-
phrase: string;
|
|
53
|
-
term: string;
|
|
54
|
-
}[];
|
|
55
|
-
/** Words the reading did not place, left for the text match. */
|
|
56
|
-
leftover: string[];
|
|
57
|
-
normalised: string;
|
|
58
|
-
softAlone: boolean;
|
|
59
|
-
vendorColourNames: string[];
|
|
60
|
-
wants: SearchIndexPlanWant[];
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* Which reading a search ran on (`version`, `source`), whether it came from
|
|
64
|
-
* the cache, the plan itself, and its signature, so two runs of one request
|
|
65
|
-
* can be compared plan for plan.
|
|
66
|
-
*/
|
|
67
|
-
export type SearchPlanReceipt = {
|
|
68
|
-
cached: boolean;
|
|
69
|
-
plan: SearchIndexPlan;
|
|
70
|
-
signature: string;
|
|
71
|
-
source: "dictionary" | "jev";
|
|
72
|
-
version: string;
|
|
73
|
-
};
|
|
74
|
-
export {};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/** The spec-filter vocabulary MaterialGraph's filter and preference inputs share. */
|
|
2
|
-
/**
|
|
3
|
-
* A hard canonical-attribute filter accepted by the search engine — a term match
|
|
4
|
-
* (`{ attributeId, term }`), a presence check (`{ attributeId, hasValue: true }`)
|
|
5
|
-
* or a NUMERIC RANGE (`{ attributeId, min, max }`, bounds inclusive, at least one
|
|
6
|
-
* stated). Applied as a restriction BEFORE ranking.
|
|
7
|
-
*
|
|
8
|
-
* The `attributeId` may be a real registry id (`"fire_classifications"`,
|
|
9
|
-
* `"environmental_product_declaration"`) OR the virtual
|
|
10
|
-
* `"certification_programme"` (see `CERTIFICATION_PROGRAMME_ATTRIBUTE_ID`),
|
|
11
|
-
* which matches a programme — GREENGUARD, FloorScore, LEED, an EPD — wherever it
|
|
12
|
-
* landed across the four certification buckets. Presence (`hasValue`) is NOT
|
|
13
|
-
* supported for the virtual programme attribute. The typed `spec-types` mirror
|
|
14
|
-
* and the `specifications` builders produce values assignable to this type.
|
|
15
|
-
*/
|
|
16
|
-
/**
|
|
17
|
-
* Optional per-filter TRUST bar the matching value must clear.
|
|
18
|
-
*
|
|
19
|
-
* Raising the bar shrinks the result set for a reason worth stating: the
|
|
20
|
-
* materials it removes are not materials that fail the spec, they are materials
|
|
21
|
-
* whose claim is weaker than you demanded. That distinction is the whole point
|
|
22
|
-
* of this object — without it, "no GREENGUARD results" reads as "nobody is
|
|
23
|
-
* certified" when it means "nobody is certified TO THIS EVIDENTIAL STANDARD".
|
|
24
|
-
*/
|
|
25
|
-
export type SearchFilterTrust = {
|
|
26
|
-
/** Confidence floor, 0–1. A value below it is excluded, not marked failed. */
|
|
27
|
-
minConfidence?: number;
|
|
28
|
-
/**
|
|
29
|
-
* Demand an EXACT source basis (closed enum — an unknown basis is rejected).
|
|
30
|
-
* Strongest to weakest as evidence:
|
|
31
|
-
* - `declared` — the manufacturer stated it. Attributable, not independent.
|
|
32
|
-
* - `catalog_feed` — it arrived in a catalog feed. Trusted at ingest, unreviewed.
|
|
33
|
-
* - `derived` — MaterialGraph inferred it. Ours, not the manufacturer's — never
|
|
34
|
-
* present a `derived` value as something the brand claims.
|
|
35
|
-
*
|
|
36
|
-
* There is NO `tested` basis. This docblock advertised one as "the strongest
|
|
37
|
-
* claim available" through 0.12.0, and the union listed `declared` twice in
|
|
38
|
-
* its place — so a caller reading the documentation would write a value the
|
|
39
|
-
* server rejects with a 400, and the type would not stop them because the
|
|
40
|
-
* fourth arm was a duplicate rather than the value described.
|
|
41
|
-
* `packages/schema/src/source-basis.test.ts` asserts `tested` parses false;
|
|
42
|
-
* its absence is deliberate, not an omission.
|
|
43
|
-
*/
|
|
44
|
-
sourceBasis?: "catalog_feed" | "declared" | "derived";
|
|
45
|
-
/** Fold the attribute's own registry confidence requirement in, server-side. */
|
|
46
|
-
meetsRequirement?: true;
|
|
47
|
-
};
|
|
48
|
-
/**
|
|
49
|
-
* One HARD criterion. Applied as a restriction BEFORE ranking, so a filtered
|
|
50
|
-
* search's empty result means "nothing on record satisfies this", never "nothing
|
|
51
|
-
* exists" — the corpus's silence and the world's are different things. Use
|
|
52
|
-
* `preferences` instead when a miss should demote rather than remove.
|
|
53
|
-
*/
|
|
54
|
-
export type SearchFilter = {
|
|
55
|
-
attributeId: string;
|
|
56
|
-
term: string;
|
|
57
|
-
trust?: SearchFilterTrust;
|
|
58
|
-
} | {
|
|
59
|
-
attributeId: string;
|
|
60
|
-
excludesTerm: string;
|
|
61
|
-
trust?: SearchFilterTrust;
|
|
62
|
-
/**
|
|
63
|
-
* What an UNKNOWN effective value does. Absent or `"drop"` (default)
|
|
64
|
-
* requires a known effective value — a variant that never states the
|
|
65
|
-
* attribute does not pass. `"pass"` admits it — a variant only fails by
|
|
66
|
-
* STATING the excluded term.
|
|
67
|
-
*/
|
|
68
|
-
whenUnstated?: "drop" | "pass";
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* TERM SET — the attribute must equal any ONE of these. OR within the entry;
|
|
72
|
-
* AND still holds between entries, so a filter rail composes the same way
|
|
73
|
-
* whatever it is filtering on.
|
|
74
|
-
*
|
|
75
|
-
* The server has accepted this since the term-set arm shipped, and a consumer
|
|
76
|
-
* has been sending it successfully on the wire — but it was missing from this
|
|
77
|
-
* union through 0.12.0, so multi-value filtering type-checked nowhere while
|
|
78
|
-
* working everywhere. Brand multi-select is the common case:
|
|
79
|
-
* `{attributeId: "brand", terms: ["kvadrat", "interface"]}`.
|
|
80
|
-
*/
|
|
81
|
-
| {
|
|
82
|
-
attributeId: string;
|
|
83
|
-
terms: string[];
|
|
84
|
-
trust?: SearchFilterTrust;
|
|
85
|
-
} | {
|
|
86
|
-
attributeId: string;
|
|
87
|
-
hasValue: true;
|
|
88
|
-
trust?: SearchFilterTrust;
|
|
89
|
-
}
|
|
90
|
-
/** Date-sensitive presence is currently executable only for EPD expiration. */
|
|
91
|
-
| {
|
|
92
|
-
attributeId: "epd_expiration_date";
|
|
93
|
-
hasValue: true;
|
|
94
|
-
asOf: string;
|
|
95
|
-
trust?: SearchFilterTrust;
|
|
96
|
-
} | {
|
|
97
|
-
attributeId: string;
|
|
98
|
-
/** Inclusive lower bound. Omit for a ceiling-only range. */
|
|
99
|
-
min?: number;
|
|
100
|
-
/** Inclusive upper bound. Omit for a floor-only range. */
|
|
101
|
-
max?: number;
|
|
102
|
-
trust?: SearchFilterTrust;
|
|
103
|
-
};
|
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module search-request-types
|
|
3
|
-
* The two request shapes for the `/search` surface: the POST JSON body and the
|
|
4
|
-
* GET query-string form. Split out of `types.ts` so neither file outgrows the
|
|
5
|
-
* repo's source-size gate; every name here is re-exported from `types.ts`, so
|
|
6
|
-
* no import path changes.
|
|
7
|
-
*/
|
|
8
|
-
import type { ColourRegionRange } from "./colour-region-types.js";
|
|
9
|
-
import type { CorpusScope } from "./corpus-scope-types.js";
|
|
10
|
-
import type { Cursor, VariantId } from "./identifiers.js";
|
|
11
|
-
import type { OpenEnum, SearchImageInput } from "./types.js";
|
|
12
|
-
/**
|
|
13
|
-
* One attribute filter the one search runs as a hard token set on its index
|
|
14
|
-
* (ADR 0031, MG-2158). Entries on one attribute are OR'd; entries on different
|
|
15
|
-
* attributes AND together.
|
|
16
|
-
*
|
|
17
|
-
* - `term`: the value must carry this canonical term.
|
|
18
|
-
* - `terms`: any one of these terms satisfies it.
|
|
19
|
-
* - `excludesTerm`: the value must not carry this term. A material that states
|
|
20
|
-
* nothing for the attribute passes.
|
|
21
|
-
* - `hasValue: true`: any stated value.
|
|
22
|
-
* - `min` and/or `max`: an inclusive numeric range, on a numeric attribute
|
|
23
|
-
* (`"nrc_rating"`), a dimensions lens in millimetres
|
|
24
|
-
* (`"dimensions.thickness_mm"`, `"dimensions.short_side_mm"`) or a
|
|
25
|
-
* composition share on 0 to 1 (`"composition_weighted.wool"`). It is read as
|
|
26
|
-
* the number bands wholly inside it, so a bound between two band edges is
|
|
27
|
-
* read inward; a range no band fits inside is a 400.
|
|
28
|
-
*
|
|
29
|
-
* `attributeId` is an attribute the index carries tokens for, or
|
|
30
|
-
* `"certification_programme"` for any certification programme. Terms are
|
|
31
|
-
* canonical dictionary terms, not display names. Numeric attributes take
|
|
32
|
-
* `hasValue` or a range. An attribute or term the index does not carry is a
|
|
33
|
-
* 400 naming it.
|
|
34
|
-
*/
|
|
35
|
-
export type SearchIndexFilter = {
|
|
36
|
-
attributeId: string;
|
|
37
|
-
term: string;
|
|
38
|
-
} | {
|
|
39
|
-
attributeId: string;
|
|
40
|
-
terms: string[];
|
|
41
|
-
} | {
|
|
42
|
-
attributeId: string;
|
|
43
|
-
excludesTerm: string;
|
|
44
|
-
} | {
|
|
45
|
-
attributeId: string;
|
|
46
|
-
hasValue: true;
|
|
47
|
-
} | {
|
|
48
|
-
attributeId: string;
|
|
49
|
-
min?: number;
|
|
50
|
-
max?: number;
|
|
51
|
-
};
|
|
52
|
-
/**
|
|
53
|
-
* The POST `/search` JSON body (ADR 0031): the words, or a look-alike seed, and
|
|
54
|
-
* the page wanted. `category`, `filters`, `corpusScope`, `colour` and `brands`
|
|
55
|
-
* become fields of the query's plan and run in the same index query. The order
|
|
56
|
-
* is the index's own, so nothing here sets weights, a blend or a sort. Send
|
|
57
|
-
* `text`, `similarTo`, `image`, `colour`, `category`, `filters` or `brands`; a
|
|
58
|
-
* corpus scope alone does not say what to find. Any other field is a 400
|
|
59
|
-
* naming it.
|
|
60
|
-
*/
|
|
61
|
-
export type SearchInput = {
|
|
62
|
-
/**
|
|
63
|
-
* Free text ("oak flooring", "sage green velvet", "Kvadrat wool"). Brands,
|
|
64
|
-
* categories, colours and attributes are read from the words unless
|
|
65
|
-
* `brands`, `category` or `colour` says so instead; a corrected word is
|
|
66
|
-
* reported under `correctedQuery`.
|
|
67
|
-
*/
|
|
68
|
-
text?: string;
|
|
69
|
-
/** A query image (URL or base64); `text`, when also given, narrows it. */
|
|
70
|
-
image?: SearchImageInput;
|
|
71
|
-
/**
|
|
72
|
-
* Materials that LOOK like a seed: an existing variant, a Material Bank SKU
|
|
73
|
-
* (an unknown SKU is a 400 naming it), or a pre-computed image vector.
|
|
74
|
-
*/
|
|
75
|
-
similarTo?: {
|
|
76
|
-
variantId: VariantId;
|
|
77
|
-
} | {
|
|
78
|
-
materialBankId: string;
|
|
79
|
-
} | {
|
|
80
|
-
vector: string;
|
|
81
|
-
};
|
|
82
|
-
/**
|
|
83
|
-
* A hard category gate: public category-group or product-type names, or
|
|
84
|
-
* taxonomy keys. It replaces the category the words were read as. An
|
|
85
|
-
* unknown value is a 400 listing every valid name.
|
|
86
|
-
*/
|
|
87
|
-
category?: {
|
|
88
|
-
filter: string[];
|
|
89
|
-
};
|
|
90
|
-
/** Hard attribute filters, 1 to 24. See {@link SearchIndexFilter}. */
|
|
91
|
-
filters?: SearchIndexFilter[];
|
|
92
|
-
/**
|
|
93
|
-
* Order the page by a colour: a `hex`, or `variantId` for the colour of an
|
|
94
|
-
* existing Variant. The page is the colour's family, nearest first by OKLab
|
|
95
|
-
* distance; it replaces any colour read from `text`. A Variant with no colour
|
|
96
|
-
* on the index is a 400.
|
|
97
|
-
*
|
|
98
|
-
* `oklab` and `range` are additive: send them when you already resolved the
|
|
99
|
-
* exact OKLab point or the named colour's region (see `resolveColour()`), and
|
|
100
|
-
* the plan carries that colour without re-deriving it from `hex`. `hex` stays
|
|
101
|
-
* required.
|
|
102
|
-
*/
|
|
103
|
-
colour?: {
|
|
104
|
-
hex: string;
|
|
105
|
-
oklab?: [number, number, number];
|
|
106
|
-
range?: ColourRegionRange;
|
|
107
|
-
} | {
|
|
108
|
-
variantId: VariantId;
|
|
109
|
-
};
|
|
110
|
-
/**
|
|
111
|
-
* Brands the page must come from: canonical brand ids, exact slugs
|
|
112
|
-
* ("sherwin-williams") or exact display names ("Sherwin-Williams"), up to
|
|
113
|
-
* 50. They replace the brands `text` was read as. An empty list is the
|
|
114
|
-
* same request as omitting it — every brand. An unrecognised value is a
|
|
115
|
-
* 400 naming it; a display name shared by more than one brand is a 400
|
|
116
|
-
* naming the exact slugs to choose between; a brand MaterialGraph holds no
|
|
117
|
-
* materials under is a 400 naming a populated sibling when one exists.
|
|
118
|
-
*/
|
|
119
|
-
brands?: string[];
|
|
120
|
-
/**
|
|
121
|
-
* Attribute ids (at most 8) whose resolved value should ride each result
|
|
122
|
-
* under `attributes`, keyed by attribute id. Not a plan field: it changes
|
|
123
|
-
* no ordering and plays no part in the cursor. An attribute you did not
|
|
124
|
-
* ask for is not a key on `attributes` at all; one you asked for that a
|
|
125
|
-
* result carries no accepted value for is `null`; anything else is the
|
|
126
|
-
* value. An unknown attribute id is a 400 naming it.
|
|
127
|
-
*/
|
|
128
|
-
returnAttributes?: string[];
|
|
129
|
-
/**
|
|
130
|
-
* Which corpus to answer from, applied inside the index query. ABSENT (or
|
|
131
|
-
* `"all"`) means the whole graph. `"materialBankOrderable"` is a provenance
|
|
132
|
-
* signal (a Material Bank identity), not a storefront; the channel-backed
|
|
133
|
-
* scopes (`"materialBankNaOrderable"`, `"materialBankEuOrderable"`,
|
|
134
|
-
* `"designShopOrderable"`) answer where a material can be ordered,
|
|
135
|
-
* `"materialBankNaAndCatalogue"` is a governed discovery corpus (the union
|
|
136
|
-
* of Material Bank North America and Catalogue editorial membership, not a
|
|
137
|
-
* buyability claim), and `"samplizeCatalogue"` admits published US Samplize
|
|
138
|
-
* paint colours, regardless of stock.
|
|
139
|
-
*/
|
|
140
|
-
corpusScope?: CorpusScope;
|
|
141
|
-
/** Products per page (max 60). Absent, the search's own page size. */
|
|
142
|
-
limit?: number;
|
|
143
|
-
/**
|
|
144
|
-
* The previous response's `cursor`, passed back verbatim. A cursor from
|
|
145
|
-
* another query (other words, filters or scope) is a 409.
|
|
146
|
-
*/
|
|
147
|
-
cursor?: Cursor;
|
|
148
|
-
/** Add `timings` and `explain.plan`, the plan the search ran on. */
|
|
149
|
-
explain?: boolean;
|
|
150
|
-
/** Result projection: `"full"` (default) or the lean `"card"` grid form. */
|
|
151
|
-
view?: OpenEnum<"full" | "card">;
|
|
152
|
-
};
|
|
153
|
-
/**
|
|
154
|
-
* Query-string params for the GET `/search` form. `category` and `filters`
|
|
155
|
-
* have no query-string form; send them through `search()`.
|
|
156
|
-
*/
|
|
157
|
-
export type SearchGetParams = {
|
|
158
|
-
q?: string;
|
|
159
|
-
/** Seed the search from this variant's image. Opaque; pass it through verbatim. */
|
|
160
|
-
variantId?: VariantId;
|
|
161
|
-
/** Corpus applied inside the index query; absent means the whole graph. */
|
|
162
|
-
corpusScope?: CorpusScope;
|
|
163
|
-
limit?: number;
|
|
164
|
-
/** The previous response's `cursor`, passed back verbatim. */
|
|
165
|
-
cursor?: Cursor;
|
|
166
|
-
/** Result projection: `"full"` (default) or the lean `"card"` grid form. */
|
|
167
|
-
view?: OpenEnum<"full" | "card">;
|
|
168
|
-
/** `"1"` adds `timings` and `explain.plan`. */
|
|
169
|
-
explain?: OpenEnum<"0" | "1">;
|
|
170
|
-
};
|
package/search-request-types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @module sdk/search-response-receipt-types
|
|
3
|
-
* @description The ranking source and the explain-only plan receipt a Search
|
|
4
|
-
* response carries.
|
|
5
|
-
*/
|
|
6
|
-
import type { SearchPlanReceipt } from "./search-index-plan-types.js";
|
|
7
|
-
import type { OpenEnum } from "./types.js";
|
|
8
|
-
/**
|
|
9
|
-
* What ordered the page: `image` (a look-alike), `colour` (colour distance
|
|
10
|
-
* inside the named colour's family), `lexical` (the query's words, by text
|
|
11
|
-
* rank on the index), `attribute` (the category and filters alone define the
|
|
12
|
-
* page) or `agent` (the search agent chose the variants and their order).
|
|
13
|
-
*/
|
|
14
|
-
export type SearchMode = OpenEnum<"image" | "colour" | "lexical" | "agent" | "attribute">;
|
|
15
|
-
/** The explain-only receipt shared by full and card search responses. */
|
|
16
|
-
export type SearchResponseExplainReceipts = {
|
|
17
|
-
/**
|
|
18
|
-
* Present under `explain` only: the receipt of the plan the one search ran
|
|
19
|
-
* on, with the plan itself (ADR 0031). Diagnostic, like `timings`.
|
|
20
|
-
*/
|
|
21
|
-
explain?: {
|
|
22
|
-
plan: SearchPlanReceipt;
|
|
23
|
-
};
|
|
24
|
-
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import type { SearchData } from "./types.js";
|
|
2
|
-
/** Canonical Search wire response. Raw SDK methods preserve this envelope. */
|
|
3
|
-
export type SearchResponse = {
|
|
4
|
-
data: SearchData;
|
|
5
|
-
meta: {
|
|
6
|
-
operationId: "search_materials_post" | "search_materials_get";
|
|
7
|
-
requestId: string;
|
|
8
|
-
};
|
|
9
|
-
links: Record<string, never>;
|
|
10
|
-
warnings: never[];
|
|
11
|
-
};
|
package/search-response-types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import type { ValidationFailure } from "./validation.js";
|
|
2
|
-
/**
|
|
3
|
-
* Prove one wire value is a {@link SearchResultItem}, raising through `fail` at
|
|
4
|
-
* the first field that does not match `searchResultItemSchema`. Checks the shape
|
|
5
|
-
* only — unknown keys ride through untouched, per the SDK's pass-through
|
|
6
|
-
* contract (nothing on the emit path parses, so stripping would invent a
|
|
7
|
-
* normalisation the wire never had).
|
|
8
|
-
*/
|
|
9
|
-
export declare const checkSearchResultItem: (value: unknown, what: string, fail: ValidationFailure) => void;
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The one runtime validator for a {@link SearchResultItem} on the wire — the
|
|
3
|
-
* hand-rolled, zero-runtime-dependency mirror of `searchResultItemSchema`
|
|
4
|
-
* (`packages/products/src/search/search-result-schema.ts`) and its full
|
|
5
|
-
* transitive closure, including `searchResultSignalsSchema`.
|
|
6
|
-
*
|
|
7
|
-
* WHY THIS EXISTS SEPARATELY FROM THE TYPE MIRROR. `contract-parity.test.ts`
|
|
8
|
-
* proves the SDK's STATIC types match the canonical schemas. That is a
|
|
9
|
-
* compile-time proof about shapes; it validates nothing at run time. A malformed
|
|
10
|
-
* result item arriving over a stream was caught by neither, so the Match reader
|
|
11
|
-
* now checks every item through here.
|
|
12
|
-
*
|
|
13
|
-
* ONE HOME. This module is the single implementation — no copy lives inside
|
|
14
|
-
* `match-stream.ts`. It is package-internal (not re-exported from `index.ts`)
|
|
15
|
-
* because a published symbol is a permanent contract, and nothing outside the
|
|
16
|
-
* SDK needs to call it yet; promoting it later is additive.
|
|
17
|
-
*
|
|
18
|
-
* ENUMS ARE CLOSED, matching the canonical `z.enum(…)` members.
|
|
19
|
-
*/
|
|
20
|
-
import { requireArray, requireBoolean, requireEnum, requireNullableString, requireNumber, requireOptionalBoolean, requireOptionalString, requireRecord, requireString, } from "./validation.js";
|
|
21
|
-
// ── Leaf checks ──────────────────────────────────────────────────────────────
|
|
22
|
-
/** `palette[]` — `{ hex, proportion, name: string.nullable().optional() }`. */
|
|
23
|
-
const checkPaletteSwatch = (value, what, fail) => {
|
|
24
|
-
const record = requireRecord(value, what, fail);
|
|
25
|
-
requireString(record.hex, `${what}.hex`, fail);
|
|
26
|
-
requireNumber(record.proportion, `${what}.proportion`, fail);
|
|
27
|
-
// `.nullable().optional()` — absent OR null OR a string.
|
|
28
|
-
if (record.name !== undefined) {
|
|
29
|
-
requireNullableString(record.name, `${what}.name`, fail);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
/** `colourMeta` — three independently optional strings. */
|
|
33
|
-
const checkColourMeta = (value, what, fail) => {
|
|
34
|
-
const record = requireRecord(value, what, fail);
|
|
35
|
-
requireOptionalString(record.mood, `${what}.mood`, fail);
|
|
36
|
-
requireOptionalString(record.dominantFamily, `${what}.dominantFamily`, fail);
|
|
37
|
-
requireOptionalString(record.dominantHex, `${what}.dominantHex`, fail);
|
|
38
|
-
};
|
|
39
|
-
// ── The item ─────────────────────────────────────────────────────────────────
|
|
40
|
-
/**
|
|
41
|
-
* Prove one wire value is a {@link SearchResultItem}, raising through `fail` at
|
|
42
|
-
* the first field that does not match `searchResultItemSchema`. Checks the shape
|
|
43
|
-
* only — unknown keys ride through untouched, per the SDK's pass-through
|
|
44
|
-
* contract (nothing on the emit path parses, so stripping would invent a
|
|
45
|
-
* normalisation the wire never had).
|
|
46
|
-
*/
|
|
47
|
-
export const checkSearchResultItem = (value, what, fail) => {
|
|
48
|
-
const record = requireRecord(value, what, fail);
|
|
49
|
-
requireString(record.variantId, `${what}.variantId`, fail);
|
|
50
|
-
requireString(record.brand, `${what}.brand`, fail);
|
|
51
|
-
requireString(record.product, `${what}.product`, fail);
|
|
52
|
-
requireNullableString(record.brandSlug, `${what}.brandSlug`, fail);
|
|
53
|
-
requireNullableString(record.productId, `${what}.productId`, fail);
|
|
54
|
-
// `z.string().optional()` — absent, or a string. NOT nullable: the emit path
|
|
55
|
-
// omits the key rather than sending null when no MB identity is known.
|
|
56
|
-
requireOptionalString(record.materialBankId, `${what}.materialBankId`, fail);
|
|
57
|
-
requireOptionalString(record.manufacturerCode, `${what}.manufacturerCode`, fail);
|
|
58
|
-
if (record.projectSaveCapability !== undefined) {
|
|
59
|
-
requireEnum(record.projectSaveCapability, ["allowed"], `${what}.projectSaveCapability`, fail);
|
|
60
|
-
}
|
|
61
|
-
if (record.sourceStatus !== undefined) {
|
|
62
|
-
requireEnum(record.sourceStatus, ["provisional"], `${what}.sourceStatus`, fail);
|
|
63
|
-
}
|
|
64
|
-
// Same shape as `materialBankId`: absent, or a string. The emit path omits
|
|
65
|
-
// the key when no primary product type is on record rather than sending null,
|
|
66
|
-
// so "we don't know what this is" and "this is a null kind of thing" cannot
|
|
67
|
-
// be confused.
|
|
68
|
-
requireOptionalString(record.productType, `${what}.productType`, fail);
|
|
69
|
-
requireNullableString(record.color, `${what}.color`, fail);
|
|
70
|
-
requireNullableString(record.variantName, `${what}.variantName`, fail);
|
|
71
|
-
requireNullableString(record.imageUrl, `${what}.imageUrl`, fail);
|
|
72
|
-
requireBoolean(record.hasImage, `${what}.hasImage`, fail);
|
|
73
|
-
requireNumber(record.score, `${what}.score`, fail);
|
|
74
|
-
const palette = requireArray(record.palette, `${what}.palette`, fail);
|
|
75
|
-
palette.forEach((swatch, index) => {
|
|
76
|
-
checkPaletteSwatch(swatch, `${what}.palette[${index}]`, fail);
|
|
77
|
-
});
|
|
78
|
-
if (record.colourMeta !== undefined) {
|
|
79
|
-
checkColourMeta(record.colourMeta, `${what}.colourMeta`, fail);
|
|
80
|
-
}
|
|
81
|
-
requireOptionalBoolean(record.retired, `${what}.retired`, fail);
|
|
82
|
-
requireOptionalBoolean(record.sampleable, `${what}.sampleable`, fail);
|
|
83
|
-
};
|