@asstio-dev/storefront 0.5.0 → 0.6.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/README.md +8 -1
- package/dist/filters.d.ts +12 -0
- package/dist/filters.js +39 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.js +6 -3
- package/dist/schema.d.ts +3 -0
- package/package.json +2 -2
- package/src/schema.d.ts +3 -0
package/README.md
CHANGED
|
@@ -190,7 +190,14 @@ goes blank because of a bad query string:
|
|
|
190
190
|
|
|
191
191
|
`q`, `page`, `pageSize`, `sort` and `filter` are all in the contract, so they are typed and autocompleted.
|
|
192
192
|
`filter` is a `deepObject`: `{ brand: "dior,chanel" }` goes out as `filter[brand]=dior,chanel`, which is the
|
|
193
|
-
form the API reads.
|
|
193
|
+
form the API reads. In a list, swatch or hierarchy filter `,` separates values, `\,` is a comma inside a value and
|
|
194
|
+
`\\` a backslash — value keys are raw values, and an option such as `14,2` is the key `14,2`. Never join or split a
|
|
195
|
+
filter parameter yourself: `encodeFilterValues(values)` builds one and `decodeFilterValues(param)` reads one (for
|
|
196
|
+
the selected state of a URL you parse), for every facet alike. `filterQuery()` uses `encodeFilterValues` for
|
|
197
|
+
multi-select arrays, passes a single string (a range `100-500`, a toggle `true`) as given and drops `undefined` ones.
|
|
198
|
+
A range is `min-max`: either bound may be left out and either may be negative — `-10--2`, `-10-2`, `-10-` (at least
|
|
199
|
+
−10), `--2` (at most −2) — and a leading dash with nothing before it keeps its meaning, `-500` is "at most 500". A
|
|
200
|
+
range whose lower bound is above its upper bound is ignored with a warning.
|
|
194
201
|
|
|
195
202
|
`/v1/sitemap` takes `type` (`product` | `collection`), `since` (ISO-8601, inclusive), `page` and `pageSize`
|
|
196
203
|
(max 1000, default 500).
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The list grammar of a `filter[<key>]` parameter (list, swatch and hierarchy facets): `,` separates values, `\,` is a
|
|
3
|
+
* comma inside a value and `\\` a backslash. A value key is the raw value — an option such as "14,2" is the key "14,2" —
|
|
4
|
+
* so a frontend never joins or splits a filter parameter by hand: it builds and toggles filter URLs with these two, for
|
|
5
|
+
* every facet alike. Range (`min-max`) and toggle (`true`) parameters are single strings and need neither.
|
|
6
|
+
*/
|
|
7
|
+
export declare function encodeFilterValues(values: readonly string[]): string;
|
|
8
|
+
/**
|
|
9
|
+
* The values a `filter[<key>]` parameter selects, read the way the API reads it: `\,` and `\\` unescaped, a backslash
|
|
10
|
+
* before anything else (or at the end) kept as it is, values trimmed, empty ones and repeats dropped.
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeFilterValues(param: string): string[];
|
package/dist/filters.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The list grammar of a `filter[<key>]` parameter (list, swatch and hierarchy facets): `,` separates values, `\,` is a
|
|
3
|
+
* comma inside a value and `\\` a backslash. A value key is the raw value — an option such as "14,2" is the key "14,2" —
|
|
4
|
+
* so a frontend never joins or splits a filter parameter by hand: it builds and toggles filter URLs with these two, for
|
|
5
|
+
* every facet alike. Range (`min-max`) and toggle (`true`) parameters are single strings and need neither.
|
|
6
|
+
*/
|
|
7
|
+
export function encodeFilterValues(values) {
|
|
8
|
+
return values.map((v) => v.replace(/\\/g, "\\\\").replace(/,/g, "\\,")).join(",");
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* The values a `filter[<key>]` parameter selects, read the way the API reads it: `\,` and `\\` unescaped, a backslash
|
|
12
|
+
* before anything else (or at the end) kept as it is, values trimmed, empty ones and repeats dropped.
|
|
13
|
+
*/
|
|
14
|
+
export function decodeFilterValues(param) {
|
|
15
|
+
const values = [];
|
|
16
|
+
let current = "";
|
|
17
|
+
const push = () => {
|
|
18
|
+
const value = current.trim();
|
|
19
|
+
current = "";
|
|
20
|
+
if (value.length > 0 && !values.includes(value))
|
|
21
|
+
values.push(value);
|
|
22
|
+
};
|
|
23
|
+
for (let i = 0; i < param.length; i++) {
|
|
24
|
+
const c = param[i];
|
|
25
|
+
const next = param[i + 1];
|
|
26
|
+
if (c === "\\" && (next === "," || next === "\\")) {
|
|
27
|
+
current += next;
|
|
28
|
+
i++;
|
|
29
|
+
}
|
|
30
|
+
else if (c === ",") {
|
|
31
|
+
push();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
current += c;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
push();
|
|
38
|
+
return values;
|
|
39
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -38,10 +38,12 @@ export declare function createStorefrontClient(opts: StorefrontClientOptions): C
|
|
|
38
38
|
/** The `filter` query parameter of a listing call: one facet key to one value, as the generated types spell it. */
|
|
39
39
|
export type FilterSelection = NonNullable<NonNullable<paths["/v1/products"]["get"]["parameters"]["query"]>["filter"]>;
|
|
40
40
|
/**
|
|
41
|
-
* Collapse a facet selection into the typed `filter` object: several values for one facet are
|
|
42
|
-
*
|
|
41
|
+
* Collapse a facet selection into the typed `filter` object: several values for one facet are encoded with
|
|
42
|
+
* `encodeFilterValues` (so a value containing a comma or a backslash survives), a single string is sent as given (a
|
|
43
|
+
* range "100-500", a toggle "true"), and `undefined` facets drop out — an unchecked checkbox needs no special case.
|
|
43
44
|
* `{ brand: ["dior", "chanel"], price: "100-500", size: undefined }` → `{ brand: "dior,chanel", price: "100-500" }`.
|
|
44
45
|
* The client sends it as `filter[brand]=dior,chanel` (deepObject), which is what the API reads.
|
|
45
46
|
*/
|
|
46
47
|
export declare function filterQuery(selection: Record<string, string | string[] | undefined>): FilterSelection;
|
|
48
|
+
export { decodeFilterValues, encodeFilterValues } from "./filters.js";
|
|
47
49
|
export { fileUrl, mediaHash, mediaUrl, MediaUrlError, pickWidth, pictureSources, posterUrl, srcSet, videoSources, type MediaConfig, type MediaDescriptor, type PictureSources, type VideoSource, } from "./media.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import createClient from "openapi-fetch";
|
|
2
|
+
import { encodeFilterValues } from "./filters.js";
|
|
2
3
|
/** Thin typed client: every call carries X-Site-Key / X-Market / Accept-Language. Works in browsers and on the server. */
|
|
3
4
|
export function createStorefrontClient(opts) {
|
|
4
5
|
const client = createClient({ baseUrl: opts.baseUrl.replace(/\/$/, ""), fetch: opts.fetch });
|
|
@@ -18,8 +19,9 @@ export function createStorefrontClient(opts) {
|
|
|
18
19
|
return client;
|
|
19
20
|
}
|
|
20
21
|
/**
|
|
21
|
-
* Collapse a facet selection into the typed `filter` object: several values for one facet are
|
|
22
|
-
*
|
|
22
|
+
* Collapse a facet selection into the typed `filter` object: several values for one facet are encoded with
|
|
23
|
+
* `encodeFilterValues` (so a value containing a comma or a backslash survives), a single string is sent as given (a
|
|
24
|
+
* range "100-500", a toggle "true"), and `undefined` facets drop out — an unchecked checkbox needs no special case.
|
|
23
25
|
* `{ brand: ["dior", "chanel"], price: "100-500", size: undefined }` → `{ brand: "dior,chanel", price: "100-500" }`.
|
|
24
26
|
* The client sends it as `filter[brand]=dior,chanel` (deepObject), which is what the API reads.
|
|
25
27
|
*/
|
|
@@ -28,8 +30,9 @@ export function filterQuery(selection) {
|
|
|
28
30
|
for (const [key, value] of Object.entries(selection)) {
|
|
29
31
|
if (value === undefined)
|
|
30
32
|
continue;
|
|
31
|
-
out[key] = Array.isArray(value) ? value
|
|
33
|
+
out[key] = Array.isArray(value) ? encodeFilterValues(value) : value;
|
|
32
34
|
}
|
|
33
35
|
return out;
|
|
34
36
|
}
|
|
37
|
+
export { decodeFilterValues, encodeFilterValues } from "./filters.js";
|
|
35
38
|
export { fileUrl, mediaHash, mediaUrl, MediaUrlError, pickWidth, pictureSources, posterUrl, srcSet, videoSources, } from "./media.js";
|
package/dist/schema.d.ts
CHANGED
|
@@ -370,6 +370,8 @@ export interface components {
|
|
|
370
370
|
text: string;
|
|
371
371
|
};
|
|
372
372
|
FacetDto: {
|
|
373
|
+
collapsed: boolean;
|
|
374
|
+
help: null | string;
|
|
373
375
|
key: string;
|
|
374
376
|
label: string;
|
|
375
377
|
match: string;
|
|
@@ -387,6 +389,7 @@ export interface components {
|
|
|
387
389
|
children: components["schemas"]["FacetValueDto"][] | null;
|
|
388
390
|
/** Format: int64 */
|
|
389
391
|
count: number | string;
|
|
392
|
+
help: null | string;
|
|
390
393
|
key: string;
|
|
391
394
|
label: string;
|
|
392
395
|
pinned: boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asstio-dev/storefront",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Typed client for the Asstio Storefront API (generated from OpenAPI).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"build": "npm run generate && tsc -p tsconfig.json && node -e \"require('node:fs').copyFileSync('src/schema.d.ts','dist/schema.d.ts')\" && npm run typecheck-examples && npm run smoke",
|
|
21
21
|
"test": "vitest run",
|
|
22
22
|
"check": "npm run generate && npm run typecheck-examples && npm test && git diff --exit-code -- src/schema.d.ts && test -z \"$(git status --porcelain -- src/schema.d.ts)\"",
|
|
23
|
-
"smoke": "node --input-type=module -e \"import(\\\"./dist/index.js\\\").then(m => { for (const f of [\\\"createStorefrontClient\\\", \\\"filterQuery\\\", \\\"pictureSources\\\", \\\"mediaUrl\\\"]) if (typeof m[f] !== \\\"function\\\") throw new Error(\\\"missing export \\\" + f) })\""
|
|
23
|
+
"smoke": "node --input-type=module -e \"import(\\\"./dist/index.js\\\").then(m => { for (const f of [\\\"createStorefrontClient\\\", \\\"filterQuery\\\", \\\"encodeFilterValues\\\", \\\"decodeFilterValues\\\", \\\"pictureSources\\\", \\\"mediaUrl\\\"]) if (typeof m[f] !== \\\"function\\\") throw new Error(\\\"missing export \\\" + f) })\""
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
26
|
"openapi-fetch": "0.17.0"
|
package/src/schema.d.ts
CHANGED
|
@@ -370,6 +370,8 @@ export interface components {
|
|
|
370
370
|
text: string;
|
|
371
371
|
};
|
|
372
372
|
FacetDto: {
|
|
373
|
+
collapsed: boolean;
|
|
374
|
+
help: null | string;
|
|
373
375
|
key: string;
|
|
374
376
|
label: string;
|
|
375
377
|
match: string;
|
|
@@ -387,6 +389,7 @@ export interface components {
|
|
|
387
389
|
children: components["schemas"]["FacetValueDto"][] | null;
|
|
388
390
|
/** Format: int64 */
|
|
389
391
|
count: number | string;
|
|
392
|
+
help: null | string;
|
|
390
393
|
key: string;
|
|
391
394
|
label: string;
|
|
392
395
|
pinned: boolean;
|