@minipim/sdk 0.4.0 → 0.5.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 CHANGED
@@ -130,6 +130,43 @@ const all = await collectAll<Product>(pim, '/v1/products');
130
130
 
131
131
  Endpoints that return a **plain array** instead of the envelope (`/v1/categories` without `?limit=`, `/v1/attributes`, `/v1/products/{id}/variants`) are handled too (v0.3.0+): the array is treated as the one-and-only page, so `collectAll` works uniformly across every list endpoint.
132
132
 
133
+ ## Listing by category, and by price (v0.5.0+)
134
+
135
+ Two things storefronts almost always need, and previously had to do in memory.
136
+
137
+ **Category subtrees.** `categoryId` on its own is **self-only** — it matches products filed directly against that category. Catalogs commonly assign products to leaf categories, so a parent category can legitimately return an empty page. Add `includeDescendants` for the whole subtree:
138
+
139
+ ```ts
140
+ // Everything anywhere under "Backdrops", in ONE paginated query.
141
+ const { data } = await pim.GET('/v1/products', {
142
+ query: { categoryId, includeDescendants: true, limit: 50 },
143
+ });
144
+ ```
145
+
146
+ Don't fetch `/v1/categories`, resolve descendants yourself and issue one request per id — this is a single round trip and it paginates as one result set.
147
+
148
+ **Sorting and filtering by price.** There is no `sortBy: 'price'`, because a catalog's price attribute code is its own data (`price`, `msrp`, `list_price`, …). Name it:
149
+
150
+ ```ts
151
+ // Cheapest first
152
+ await pim.GET('/v1/products', {
153
+ query: { sortBy: 'attribute', sortAttribute: 'price', sortDir: 'asc' },
154
+ });
155
+
156
+ // $10.00–$50.00 — money bounds are INTEGER CENTS
157
+ await pim.GET('/v1/products', {
158
+ query: { filterAttribute: 'price', filterMin: 1000, filterMax: 5000 },
159
+ });
160
+ ```
161
+
162
+ - Works on `money`, `number` and `decimal` attributes. `measurement` is refused — its values carry a unit, so ordering raw amounts would rank 5 g above 2 kg.
163
+ - For `money`, bounds and ordering use `amount_cents`. **`filterMin: 1000` is $10.00.** No currency conversion — mixed-currency catalogs compare by number.
164
+ - The value read is the one at the **default scope** (no locale, no channel). Products with no value sort **last in both directions** and are excluded by either bound: no price, not a price of zero.
165
+ - An unknown code, or one of a non-orderable type, is a **422 naming the problem**. It never degrades to name ordering, so you can't get a plausible page that isn't sorted the way you asked.
166
+ - `sortAttribute` and `filterAttribute` are independent; set both for "cheapest first, within a budget".
167
+
168
+ Needs API **0.9.0+**. Check `GET /healthz`.
169
+
133
170
  ## Attribute helpers
134
171
 
135
172
  Attribute values are `unknown` and keyed by `(locale, channel)`. List responses return the raw `{ code: [{ locale, channel, value }] }` shape (only product *detail* with `?locale=&channel=` returns a flat `resolvedAttributes`). The SDK ships the flatten + coercion helpers so you don't reimplement them:
package/dist/index.cjs CHANGED
@@ -54,11 +54,15 @@ module.exports = __toCommonJS(src_exports);
54
54
  var import_openapi_fetch = __toESM(require("openapi-fetch"), 1);
55
55
  function createMinipimClient(opts) {
56
56
  const headers = {
57
- "x-organization-id": opts.organizationId,
57
+ // Conditional rather than relying on openapi-fetch to drop the undefined.
58
+ // It does (mergeHeaders skips undefined values), so this is belt-and-braces
59
+ // for the type, not a behaviour change — but it keeps the intent local
60
+ // instead of depending on a transitive dependency's merge semantics.
61
+ ...opts.organizationId ? { "x-organization-id": opts.organizationId } : {},
58
62
  ...opts.headers ?? {}
59
63
  };
60
64
  if (opts.apiKey) headers["Authorization"] = `Bearer ${opts.apiKey}`;
61
- if (opts.userId) headers["x-pim-user-id"] = opts.userId;
65
+ if (opts.userId) headers["x-user-id"] = opts.userId;
62
66
  return (0, import_openapi_fetch.default)({
63
67
  baseUrl: opts.baseUrl.replace(/\/$/, ""),
64
68
  fetch: opts.fetch,
package/dist/index.d.cts CHANGED
@@ -18,11 +18,16 @@ interface CreateMinipimClientOptions {
18
18
  */
19
19
  baseUrl: string;
20
20
  /**
21
- * Tenant the client speaks for. Required every authenticated endpoint
22
- * needs `x-organization-id`. API keys identify the principal, not the
23
- * tenant.
21
+ * Tenant the client speaks for, sent as `x-organization-id`.
22
+ *
23
+ * Optional when `apiKey` is set: an API key carries its own tenant, and the
24
+ * server resolves the org from the key row and ignores this header entirely
25
+ * (see the api-key auth provider — it returns the key's `organizationId`,
26
+ * and the auth plugin short-circuits before it ever reads the header).
27
+ *
28
+ * Required for session/`header`-mode auth, where the caller asserts the org.
24
29
  */
25
- organizationId: string;
30
+ organizationId?: string;
26
31
  /**
27
32
  * Bearer API key (e.g. `pim_abc123…`). Issue one in the admin under
28
33
  * /api-keys. If you're running in dev with `PIM_AUTH=header`, omit this
@@ -30,8 +35,10 @@ interface CreateMinipimClientOptions {
30
35
  */
31
36
  apiKey?: string;
32
37
  /**
33
- * Dev-only header (`x-pim-user-id`). Only set when the deployment is in
34
- * `header` auth mode. Ignored in `jwt` / `clerk` deployments.
38
+ * Dev-only header (`x-user-id`). Only set when the deployment is in `header`
39
+ * auth mode, where it must be paired with `organizationId`. Ignored in
40
+ * `jwt` / `clerk` deployments. The instance falls back to its own
41
+ * `defaultUserSubject` when this is absent.
35
42
  */
36
43
  userId?: string;
37
44
  /**
package/dist/index.d.ts CHANGED
@@ -18,11 +18,16 @@ interface CreateMinipimClientOptions {
18
18
  */
19
19
  baseUrl: string;
20
20
  /**
21
- * Tenant the client speaks for. Required every authenticated endpoint
22
- * needs `x-organization-id`. API keys identify the principal, not the
23
- * tenant.
21
+ * Tenant the client speaks for, sent as `x-organization-id`.
22
+ *
23
+ * Optional when `apiKey` is set: an API key carries its own tenant, and the
24
+ * server resolves the org from the key row and ignores this header entirely
25
+ * (see the api-key auth provider — it returns the key's `organizationId`,
26
+ * and the auth plugin short-circuits before it ever reads the header).
27
+ *
28
+ * Required for session/`header`-mode auth, where the caller asserts the org.
24
29
  */
25
- organizationId: string;
30
+ organizationId?: string;
26
31
  /**
27
32
  * Bearer API key (e.g. `pim_abc123…`). Issue one in the admin under
28
33
  * /api-keys. If you're running in dev with `PIM_AUTH=header`, omit this
@@ -30,8 +35,10 @@ interface CreateMinipimClientOptions {
30
35
  */
31
36
  apiKey?: string;
32
37
  /**
33
- * Dev-only header (`x-pim-user-id`). Only set when the deployment is in
34
- * `header` auth mode. Ignored in `jwt` / `clerk` deployments.
38
+ * Dev-only header (`x-user-id`). Only set when the deployment is in `header`
39
+ * auth mode, where it must be paired with `organizationId`. Ignored in
40
+ * `jwt` / `clerk` deployments. The instance falls back to its own
41
+ * `defaultUserSubject` when this is absent.
35
42
  */
36
43
  userId?: string;
37
44
  /**
package/dist/index.js CHANGED
@@ -2,11 +2,15 @@
2
2
  import createClient from "openapi-fetch";
3
3
  function createMinipimClient(opts) {
4
4
  const headers = {
5
- "x-organization-id": opts.organizationId,
5
+ // Conditional rather than relying on openapi-fetch to drop the undefined.
6
+ // It does (mergeHeaders skips undefined values), so this is belt-and-braces
7
+ // for the type, not a behaviour change — but it keeps the intent local
8
+ // instead of depending on a transitive dependency's merge semantics.
9
+ ...opts.organizationId ? { "x-organization-id": opts.organizationId } : {},
6
10
  ...opts.headers ?? {}
7
11
  };
8
12
  if (opts.apiKey) headers["Authorization"] = `Bearer ${opts.apiKey}`;
9
- if (opts.userId) headers["x-pim-user-id"] = opts.userId;
13
+ if (opts.userId) headers["x-user-id"] = opts.userId;
10
14
  return createClient({
11
15
  baseUrl: opts.baseUrl.replace(/\/$/, ""),
12
16
  fetch: opts.fetch,