@minipim/sdk 0.3.1 → 0.4.1

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
@@ -153,6 +153,34 @@ const weight = asMeasurement(getAttribute(product.attributes, 'weight')); // { a
153
153
 
154
154
  Also available at the `@minipim/sdk/attributes` subpath.
155
155
 
156
+ ## Modifier helpers
157
+
158
+ Modifiers are order-line options that don't create a SKU (engraving, add-ons, print/hardware choices). They live at `GET /v1/products/{id}/modifiers` and `GET /v1/modifiers/{id}` — product *detail* only flags their presence via `modifierCount` / `hasRequiredModifiers`, so a PDP with required options must fetch the list. Price deltas are in `config.priceAdjusters`, keyed by choice value slug; **money deltas are integer cents + ISO currency** (not dollars) and percentage deltas are integer basis points. These helpers resolve and price them:
159
+
160
+ ```ts
161
+ import {
162
+ getModifierChoices, getModifierPriceAdjuster, applyPriceAdjuster, formatPriceAdjuster,
163
+ getAttribute, asMoney, formatMoney,
164
+ } from '@minipim/sdk';
165
+
166
+ const [mod] = await pim.GET('/v1/products/{id}/modifiers', { params: { path: { id } } }).then((r) => r.data);
167
+
168
+ // choices joined with their structured delta
169
+ for (const c of getModifierChoices(mod)) {
170
+ // c.priceAdjuster is null for free choices (and product_list modifiers, whose
171
+ // price lives on a referenced product — see DEVELOPERS.md)
172
+ const suffix = c.priceAdjuster ? ` (${formatPriceAdjuster(c.priceAdjuster)})` : '';
173
+ console.log(`${c.label}${suffix}`); // "Pair of LED Lights (+$170.00)"
174
+ }
175
+
176
+ // price a selected choice against the base price
177
+ const base = asMoney(getAttribute(product.attributes, 'price')); // { amount_cents, currency }
178
+ const chosen = getModifierPriceAdjuster(mod, 'pair_of_led_lights');
179
+ if (base && chosen) formatMoney(applyPriceAdjuster(base, chosen)); // "$869.00"
180
+ ```
181
+
182
+ `asPriceAdjuster` / `asModifierConfig` narrow the opaque `config`; they **reject the pre-2026-07 legacy shape** (bare `value` in dollars) so the SDK never misreads dollars as cents. Also available at the `@minipim/sdk/modifiers` subpath.
183
+
156
184
  ## Error handling
157
185
 
158
186
  `res.error` is a per-path union that's awkward to narrow. Use the shared guard:
package/dist/index.cjs CHANGED
@@ -30,16 +30,23 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
+ applyPriceAdjuster: () => applyPriceAdjuster,
33
34
  asMeasurement: () => asMeasurement,
35
+ asModifierConfig: () => asModifierConfig,
34
36
  asMoney: () => asMoney,
37
+ asPriceAdjuster: () => asPriceAdjuster,
35
38
  collectAll: () => collectAll,
36
39
  createMinipimClient: () => createMinipimClient,
37
40
  flattenAttributes: () => flattenAttributes,
38
41
  formatMoney: () => formatMoney,
42
+ formatPriceAdjuster: () => formatPriceAdjuster,
39
43
  getAttribute: () => getAttribute,
40
44
  getErrorMessage: () => getErrorMessage,
45
+ getModifierChoices: () => getModifierChoices,
46
+ getModifierPriceAdjuster: () => getModifierPriceAdjuster,
41
47
  isMinipimError: () => isMinipimError,
42
- paginate: () => paginate
48
+ paginate: () => paginate,
49
+ priceAdjusterCents: () => priceAdjusterCents
43
50
  });
44
51
  module.exports = __toCommonJS(src_exports);
45
52
 
@@ -47,11 +54,15 @@ module.exports = __toCommonJS(src_exports);
47
54
  var import_openapi_fetch = __toESM(require("openapi-fetch"), 1);
48
55
  function createMinipimClient(opts) {
49
56
  const headers = {
50
- "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 } : {},
51
62
  ...opts.headers ?? {}
52
63
  };
53
64
  if (opts.apiKey) headers["Authorization"] = `Bearer ${opts.apiKey}`;
54
- if (opts.userId) headers["x-pim-user-id"] = opts.userId;
65
+ if (opts.userId) headers["x-user-id"] = opts.userId;
55
66
  return (0, import_openapi_fetch.default)({
56
67
  baseUrl: opts.baseUrl.replace(/\/$/, ""),
57
68
  fetch: opts.fetch,
@@ -148,16 +159,94 @@ function formatMoney(money, locale = "en-US") {
148
159
  currency: money.currency
149
160
  }).format(money.amount_cents / 100);
150
161
  }
162
+
163
+ // src/modifiers.ts
164
+ function asPriceAdjuster(value) {
165
+ if (!value || typeof value !== "object") return null;
166
+ const a = value;
167
+ if (a.adjuster === "fixed" && typeof a.amount_cents === "number" && typeof a.currency === "string") {
168
+ return { adjuster: "fixed", amount_cents: a.amount_cents, currency: a.currency };
169
+ }
170
+ if (a.adjuster === "percentage" && typeof a.basis_points === "number") {
171
+ return { adjuster: "percentage", basis_points: a.basis_points };
172
+ }
173
+ return null;
174
+ }
175
+ function asModifierConfig(config) {
176
+ if (!config || typeof config !== "object") return null;
177
+ const rawAdjusters = config.priceAdjusters;
178
+ if (!rawAdjusters || typeof rawAdjusters !== "object") return null;
179
+ const priceAdjusters = {};
180
+ for (const [key, raw] of Object.entries(rawAdjusters)) {
181
+ const pa = asPriceAdjuster(raw);
182
+ if (pa) priceAdjusters[key] = pa;
183
+ }
184
+ if (Object.keys(priceAdjusters).length === 0) return null;
185
+ const schemaVersion = config.schemaVersion;
186
+ return {
187
+ ...typeof schemaVersion === "number" ? { schemaVersion } : {},
188
+ priceAdjusters
189
+ };
190
+ }
191
+ function getModifierPriceAdjuster(modifier, choiceValue) {
192
+ return asModifierConfig(modifier.config)?.priceAdjusters[choiceValue] ?? null;
193
+ }
194
+ function getModifierChoices(modifier) {
195
+ const { validation } = modifier;
196
+ const options = validation && typeof validation === "object" ? validation.options : void 0;
197
+ if (!Array.isArray(options)) return [];
198
+ const adjusters = asModifierConfig(modifier.config)?.priceAdjusters ?? {};
199
+ const out = [];
200
+ for (const opt of options) {
201
+ if (!opt || typeof opt !== "object") continue;
202
+ const value = opt.value;
203
+ if (typeof value !== "string") continue;
204
+ const label = opt.label;
205
+ out.push({
206
+ value,
207
+ label: typeof label === "string" ? label : value,
208
+ priceAdjuster: adjusters[value] ?? null
209
+ });
210
+ }
211
+ return out;
212
+ }
213
+ function priceAdjusterCents(base, adjuster) {
214
+ return adjuster.adjuster === "fixed" ? adjuster.amount_cents : Math.round(base.amount_cents * adjuster.basis_points / 1e4);
215
+ }
216
+ function applyPriceAdjuster(base, adjuster) {
217
+ return {
218
+ amount_cents: base.amount_cents + priceAdjusterCents(base, adjuster),
219
+ currency: base.currency
220
+ };
221
+ }
222
+ function formatPriceAdjuster(adjuster, locale = "en-US") {
223
+ if (adjuster.adjuster === "fixed") {
224
+ const money = formatMoney(
225
+ { amount_cents: adjuster.amount_cents, currency: adjuster.currency },
226
+ locale
227
+ );
228
+ return adjuster.amount_cents > 0 ? `+${money}` : money;
229
+ }
230
+ const percent = adjuster.basis_points / 100;
231
+ return percent > 0 ? `+${percent}%` : `${percent}%`;
232
+ }
151
233
  // Annotate the CommonJS export names for ESM import in node:
152
234
  0 && (module.exports = {
235
+ applyPriceAdjuster,
153
236
  asMeasurement,
237
+ asModifierConfig,
154
238
  asMoney,
239
+ asPriceAdjuster,
155
240
  collectAll,
156
241
  createMinipimClient,
157
242
  flattenAttributes,
158
243
  formatMoney,
244
+ formatPriceAdjuster,
159
245
  getAttribute,
160
246
  getErrorMessage,
247
+ getModifierChoices,
248
+ getModifierPriceAdjuster,
161
249
  isMinipimError,
162
- paginate
250
+ paginate,
251
+ priceAdjusterCents
163
252
  });
package/dist/index.d.cts CHANGED
@@ -2,6 +2,7 @@ import { Client } from 'openapi-fetch';
2
2
  import { paths } from './openapi.cjs';
3
3
  export { components, operations } from './openapi.cjs';
4
4
  export { AttributeValueRecord, AttributesPayload, Measurement, Money, ResolveOptions, asMeasurement, asMoney, flattenAttributes, formatMoney, getAttribute } from './attributes.cjs';
5
+ export { ModifierChoice, ModifierConfig, ModifierLike, PriceAdjuster, applyPriceAdjuster, asModifierConfig, asPriceAdjuster, formatPriceAdjuster, getModifierChoices, getModifierPriceAdjuster, priceAdjusterCents } from './modifiers.cjs';
5
6
 
6
7
  /**
7
8
  * Thin wrapper around `openapi-fetch` that pre-binds the tenancy header and
@@ -17,11 +18,16 @@ interface CreateMinipimClientOptions {
17
18
  */
18
19
  baseUrl: string;
19
20
  /**
20
- * Tenant the client speaks for. Required every authenticated endpoint
21
- * needs `x-organization-id`. API keys identify the principal, not the
22
- * 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.
23
29
  */
24
- organizationId: string;
30
+ organizationId?: string;
25
31
  /**
26
32
  * Bearer API key (e.g. `pim_abc123…`). Issue one in the admin under
27
33
  * /api-keys. If you're running in dev with `PIM_AUTH=header`, omit this
@@ -29,8 +35,10 @@ interface CreateMinipimClientOptions {
29
35
  */
30
36
  apiKey?: string;
31
37
  /**
32
- * Dev-only header (`x-pim-user-id`). Only set when the deployment is in
33
- * `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.
34
42
  */
35
43
  userId?: string;
36
44
  /**
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ import { Client } from 'openapi-fetch';
2
2
  import { paths } from './openapi.js';
3
3
  export { components, operations } from './openapi.js';
4
4
  export { AttributeValueRecord, AttributesPayload, Measurement, Money, ResolveOptions, asMeasurement, asMoney, flattenAttributes, formatMoney, getAttribute } from './attributes.js';
5
+ export { ModifierChoice, ModifierConfig, ModifierLike, PriceAdjuster, applyPriceAdjuster, asModifierConfig, asPriceAdjuster, formatPriceAdjuster, getModifierChoices, getModifierPriceAdjuster, priceAdjusterCents } from './modifiers.js';
5
6
 
6
7
  /**
7
8
  * Thin wrapper around `openapi-fetch` that pre-binds the tenancy header and
@@ -17,11 +18,16 @@ interface CreateMinipimClientOptions {
17
18
  */
18
19
  baseUrl: string;
19
20
  /**
20
- * Tenant the client speaks for. Required every authenticated endpoint
21
- * needs `x-organization-id`. API keys identify the principal, not the
22
- * 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.
23
29
  */
24
- organizationId: string;
30
+ organizationId?: string;
25
31
  /**
26
32
  * Bearer API key (e.g. `pim_abc123…`). Issue one in the admin under
27
33
  * /api-keys. If you're running in dev with `PIM_AUTH=header`, omit this
@@ -29,8 +35,10 @@ interface CreateMinipimClientOptions {
29
35
  */
30
36
  apiKey?: string;
31
37
  /**
32
- * Dev-only header (`x-pim-user-id`). Only set when the deployment is in
33
- * `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.
34
42
  */
35
43
  userId?: string;
36
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,
@@ -103,15 +107,93 @@ function formatMoney(money, locale = "en-US") {
103
107
  currency: money.currency
104
108
  }).format(money.amount_cents / 100);
105
109
  }
110
+
111
+ // src/modifiers.ts
112
+ function asPriceAdjuster(value) {
113
+ if (!value || typeof value !== "object") return null;
114
+ const a = value;
115
+ if (a.adjuster === "fixed" && typeof a.amount_cents === "number" && typeof a.currency === "string") {
116
+ return { adjuster: "fixed", amount_cents: a.amount_cents, currency: a.currency };
117
+ }
118
+ if (a.adjuster === "percentage" && typeof a.basis_points === "number") {
119
+ return { adjuster: "percentage", basis_points: a.basis_points };
120
+ }
121
+ return null;
122
+ }
123
+ function asModifierConfig(config) {
124
+ if (!config || typeof config !== "object") return null;
125
+ const rawAdjusters = config.priceAdjusters;
126
+ if (!rawAdjusters || typeof rawAdjusters !== "object") return null;
127
+ const priceAdjusters = {};
128
+ for (const [key, raw] of Object.entries(rawAdjusters)) {
129
+ const pa = asPriceAdjuster(raw);
130
+ if (pa) priceAdjusters[key] = pa;
131
+ }
132
+ if (Object.keys(priceAdjusters).length === 0) return null;
133
+ const schemaVersion = config.schemaVersion;
134
+ return {
135
+ ...typeof schemaVersion === "number" ? { schemaVersion } : {},
136
+ priceAdjusters
137
+ };
138
+ }
139
+ function getModifierPriceAdjuster(modifier, choiceValue) {
140
+ return asModifierConfig(modifier.config)?.priceAdjusters[choiceValue] ?? null;
141
+ }
142
+ function getModifierChoices(modifier) {
143
+ const { validation } = modifier;
144
+ const options = validation && typeof validation === "object" ? validation.options : void 0;
145
+ if (!Array.isArray(options)) return [];
146
+ const adjusters = asModifierConfig(modifier.config)?.priceAdjusters ?? {};
147
+ const out = [];
148
+ for (const opt of options) {
149
+ if (!opt || typeof opt !== "object") continue;
150
+ const value = opt.value;
151
+ if (typeof value !== "string") continue;
152
+ const label = opt.label;
153
+ out.push({
154
+ value,
155
+ label: typeof label === "string" ? label : value,
156
+ priceAdjuster: adjusters[value] ?? null
157
+ });
158
+ }
159
+ return out;
160
+ }
161
+ function priceAdjusterCents(base, adjuster) {
162
+ return adjuster.adjuster === "fixed" ? adjuster.amount_cents : Math.round(base.amount_cents * adjuster.basis_points / 1e4);
163
+ }
164
+ function applyPriceAdjuster(base, adjuster) {
165
+ return {
166
+ amount_cents: base.amount_cents + priceAdjusterCents(base, adjuster),
167
+ currency: base.currency
168
+ };
169
+ }
170
+ function formatPriceAdjuster(adjuster, locale = "en-US") {
171
+ if (adjuster.adjuster === "fixed") {
172
+ const money = formatMoney(
173
+ { amount_cents: adjuster.amount_cents, currency: adjuster.currency },
174
+ locale
175
+ );
176
+ return adjuster.amount_cents > 0 ? `+${money}` : money;
177
+ }
178
+ const percent = adjuster.basis_points / 100;
179
+ return percent > 0 ? `+${percent}%` : `${percent}%`;
180
+ }
106
181
  export {
182
+ applyPriceAdjuster,
107
183
  asMeasurement,
184
+ asModifierConfig,
108
185
  asMoney,
186
+ asPriceAdjuster,
109
187
  collectAll,
110
188
  createMinipimClient,
111
189
  flattenAttributes,
112
190
  formatMoney,
191
+ formatPriceAdjuster,
113
192
  getAttribute,
114
193
  getErrorMessage,
194
+ getModifierChoices,
195
+ getModifierPriceAdjuster,
115
196
  isMinipimError,
116
- paginate
197
+ paginate,
198
+ priceAdjusterCents
117
199
  };
@@ -0,0 +1,120 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/modifiers.ts
21
+ var modifiers_exports = {};
22
+ __export(modifiers_exports, {
23
+ applyPriceAdjuster: () => applyPriceAdjuster,
24
+ asModifierConfig: () => asModifierConfig,
25
+ asPriceAdjuster: () => asPriceAdjuster,
26
+ formatPriceAdjuster: () => formatPriceAdjuster,
27
+ getModifierChoices: () => getModifierChoices,
28
+ getModifierPriceAdjuster: () => getModifierPriceAdjuster,
29
+ priceAdjusterCents: () => priceAdjusterCents
30
+ });
31
+ module.exports = __toCommonJS(modifiers_exports);
32
+
33
+ // src/attributes.ts
34
+ function formatMoney(money, locale = "en-US") {
35
+ return new Intl.NumberFormat(locale, {
36
+ style: "currency",
37
+ currency: money.currency
38
+ }).format(money.amount_cents / 100);
39
+ }
40
+
41
+ // src/modifiers.ts
42
+ function asPriceAdjuster(value) {
43
+ if (!value || typeof value !== "object") return null;
44
+ const a = value;
45
+ if (a.adjuster === "fixed" && typeof a.amount_cents === "number" && typeof a.currency === "string") {
46
+ return { adjuster: "fixed", amount_cents: a.amount_cents, currency: a.currency };
47
+ }
48
+ if (a.adjuster === "percentage" && typeof a.basis_points === "number") {
49
+ return { adjuster: "percentage", basis_points: a.basis_points };
50
+ }
51
+ return null;
52
+ }
53
+ function asModifierConfig(config) {
54
+ if (!config || typeof config !== "object") return null;
55
+ const rawAdjusters = config.priceAdjusters;
56
+ if (!rawAdjusters || typeof rawAdjusters !== "object") return null;
57
+ const priceAdjusters = {};
58
+ for (const [key, raw] of Object.entries(rawAdjusters)) {
59
+ const pa = asPriceAdjuster(raw);
60
+ if (pa) priceAdjusters[key] = pa;
61
+ }
62
+ if (Object.keys(priceAdjusters).length === 0) return null;
63
+ const schemaVersion = config.schemaVersion;
64
+ return {
65
+ ...typeof schemaVersion === "number" ? { schemaVersion } : {},
66
+ priceAdjusters
67
+ };
68
+ }
69
+ function getModifierPriceAdjuster(modifier, choiceValue) {
70
+ return asModifierConfig(modifier.config)?.priceAdjusters[choiceValue] ?? null;
71
+ }
72
+ function getModifierChoices(modifier) {
73
+ const { validation } = modifier;
74
+ const options = validation && typeof validation === "object" ? validation.options : void 0;
75
+ if (!Array.isArray(options)) return [];
76
+ const adjusters = asModifierConfig(modifier.config)?.priceAdjusters ?? {};
77
+ const out = [];
78
+ for (const opt of options) {
79
+ if (!opt || typeof opt !== "object") continue;
80
+ const value = opt.value;
81
+ if (typeof value !== "string") continue;
82
+ const label = opt.label;
83
+ out.push({
84
+ value,
85
+ label: typeof label === "string" ? label : value,
86
+ priceAdjuster: adjusters[value] ?? null
87
+ });
88
+ }
89
+ return out;
90
+ }
91
+ function priceAdjusterCents(base, adjuster) {
92
+ return adjuster.adjuster === "fixed" ? adjuster.amount_cents : Math.round(base.amount_cents * adjuster.basis_points / 1e4);
93
+ }
94
+ function applyPriceAdjuster(base, adjuster) {
95
+ return {
96
+ amount_cents: base.amount_cents + priceAdjusterCents(base, adjuster),
97
+ currency: base.currency
98
+ };
99
+ }
100
+ function formatPriceAdjuster(adjuster, locale = "en-US") {
101
+ if (adjuster.adjuster === "fixed") {
102
+ const money = formatMoney(
103
+ { amount_cents: adjuster.amount_cents, currency: adjuster.currency },
104
+ locale
105
+ );
106
+ return adjuster.amount_cents > 0 ? `+${money}` : money;
107
+ }
108
+ const percent = adjuster.basis_points / 100;
109
+ return percent > 0 ? `+${percent}%` : `${percent}%`;
110
+ }
111
+ // Annotate the CommonJS export names for ESM import in node:
112
+ 0 && (module.exports = {
113
+ applyPriceAdjuster,
114
+ asModifierConfig,
115
+ asPriceAdjuster,
116
+ formatPriceAdjuster,
117
+ getModifierChoices,
118
+ getModifierPriceAdjuster,
119
+ priceAdjusterCents
120
+ });
@@ -0,0 +1,88 @@
1
+ import { Money } from './attributes.cjs';
2
+
3
+ /**
4
+ * Modifier helpers. Modifiers are order-line options that do NOT produce a new
5
+ * SKU — engraving, gift wrap, print/hardware choices, add-ons. Their price
6
+ * deltas live in `config.priceAdjusters`, keyed by choice value slug: money
7
+ * deltas are integer cents + ISO currency (the same Money convention as
8
+ * everywhere else), percentage deltas are integer basis points.
9
+ *
10
+ * The API exposes modifiers at `GET /v1/products/{id}/modifiers` and
11
+ * `GET /v1/modifiers/{id}`. Product *detail* only flags their presence
12
+ * (`modifierCount`, `hasRequiredModifiers`) — fetch the list to price a PDP.
13
+ *
14
+ * The generated OpenAPI types model `config`/`validation` as opaque JSON, so
15
+ * these helpers define the structured shapes and narrow into them — the same
16
+ * pattern as `asMoney` in ./attributes. Legacy pre-cents adjusters (a bare
17
+ * `value` in dollars, no `schemaVersion`) are intentionally ignored by
18
+ * `asPriceAdjuster`, so the SDK never misreads dollars as cents.
19
+ */
20
+
21
+ /** A structured modifier price delta for one choice. */
22
+ type PriceAdjuster = {
23
+ adjuster: 'fixed';
24
+ amount_cents: number;
25
+ currency: string;
26
+ } | {
27
+ adjuster: 'percentage';
28
+ basis_points: number;
29
+ };
30
+ /** The `config` object on a modifier definition (schemaVersion 2+). */
31
+ interface ModifierConfig {
32
+ schemaVersion?: number;
33
+ priceAdjusters: Record<string, PriceAdjuster>;
34
+ }
35
+ /** A modifier choice, joined with its structured price delta (if any). */
36
+ interface ModifierChoice {
37
+ /** Choice value slug — matches `config.priceAdjusters` keys. */
38
+ value: string;
39
+ /** Human label (option labels are stored un-localized). */
40
+ label: string;
41
+ /** Structured delta, or null for free choices / product_list modifiers. */
42
+ priceAdjuster: PriceAdjuster | null;
43
+ }
44
+ /**
45
+ * Minimal structural shape of a modifier payload — just the fields these
46
+ * helpers read. Accepts a full modifier from the API directly (its extra
47
+ * fields are ignored).
48
+ */
49
+ interface ModifierLike {
50
+ validation?: unknown;
51
+ config?: unknown;
52
+ }
53
+ /** Narrow a raw value to a PriceAdjuster, or null if it isn't one. */
54
+ declare function asPriceAdjuster(value: unknown): PriceAdjuster | null;
55
+ /**
56
+ * Narrow a raw modifier `config` to a typed ModifierConfig, or null when it
57
+ * carries no structured price (free modifiers, or product_list modifiers whose
58
+ * price lives on a referenced product). `priceAdjusters` only contains entries
59
+ * that parse — legacy/unknown shapes are dropped.
60
+ */
61
+ declare function asModifierConfig(config: unknown): ModifierConfig | null;
62
+ /** The structured price adjuster for a given choice value, or null. */
63
+ declare function getModifierPriceAdjuster(modifier: ModifierLike, choiceValue: string): PriceAdjuster | null;
64
+ /**
65
+ * Resolve a modifier's choices, each joined with its structured price delta.
66
+ * Returns `[]` for modifiers with no choice list (free-text, number, file).
67
+ */
68
+ declare function getModifierChoices(modifier: ModifierLike): ModifierChoice[];
69
+ /**
70
+ * The signed cent delta a price adjuster applies to a base price. `fixed`
71
+ * ignores the base (returns its `amount_cents`); `percentage` is `basis_points`
72
+ * of `base.amount_cents`, rounded to the nearest cent.
73
+ */
74
+ declare function priceAdjusterCents(base: Money, adjuster: PriceAdjuster): number;
75
+ /**
76
+ * Apply a price adjuster to a base Money, returning the adjusted total in the
77
+ * base currency. A `fixed` adjuster's currency is assumed to match the base
78
+ * (modifiers are same-currency in practice); only its cent delta is used.
79
+ */
80
+ declare function applyPriceAdjuster(base: Money, adjuster: PriceAdjuster): Money;
81
+ /**
82
+ * Human-readable, always-signed delta string: `+$170.00`, `-$2.70`, `+10%`.
83
+ * `fixed` uses `formatMoney` (locale-correct negatives); `percentage` renders
84
+ * basis points as a percent.
85
+ */
86
+ declare function formatPriceAdjuster(adjuster: PriceAdjuster, locale?: string): string;
87
+
88
+ export { type ModifierChoice, type ModifierConfig, type ModifierLike, type PriceAdjuster, applyPriceAdjuster, asModifierConfig, asPriceAdjuster, formatPriceAdjuster, getModifierChoices, getModifierPriceAdjuster, priceAdjusterCents };
@@ -0,0 +1,88 @@
1
+ import { Money } from './attributes.js';
2
+
3
+ /**
4
+ * Modifier helpers. Modifiers are order-line options that do NOT produce a new
5
+ * SKU — engraving, gift wrap, print/hardware choices, add-ons. Their price
6
+ * deltas live in `config.priceAdjusters`, keyed by choice value slug: money
7
+ * deltas are integer cents + ISO currency (the same Money convention as
8
+ * everywhere else), percentage deltas are integer basis points.
9
+ *
10
+ * The API exposes modifiers at `GET /v1/products/{id}/modifiers` and
11
+ * `GET /v1/modifiers/{id}`. Product *detail* only flags their presence
12
+ * (`modifierCount`, `hasRequiredModifiers`) — fetch the list to price a PDP.
13
+ *
14
+ * The generated OpenAPI types model `config`/`validation` as opaque JSON, so
15
+ * these helpers define the structured shapes and narrow into them — the same
16
+ * pattern as `asMoney` in ./attributes. Legacy pre-cents adjusters (a bare
17
+ * `value` in dollars, no `schemaVersion`) are intentionally ignored by
18
+ * `asPriceAdjuster`, so the SDK never misreads dollars as cents.
19
+ */
20
+
21
+ /** A structured modifier price delta for one choice. */
22
+ type PriceAdjuster = {
23
+ adjuster: 'fixed';
24
+ amount_cents: number;
25
+ currency: string;
26
+ } | {
27
+ adjuster: 'percentage';
28
+ basis_points: number;
29
+ };
30
+ /** The `config` object on a modifier definition (schemaVersion 2+). */
31
+ interface ModifierConfig {
32
+ schemaVersion?: number;
33
+ priceAdjusters: Record<string, PriceAdjuster>;
34
+ }
35
+ /** A modifier choice, joined with its structured price delta (if any). */
36
+ interface ModifierChoice {
37
+ /** Choice value slug — matches `config.priceAdjusters` keys. */
38
+ value: string;
39
+ /** Human label (option labels are stored un-localized). */
40
+ label: string;
41
+ /** Structured delta, or null for free choices / product_list modifiers. */
42
+ priceAdjuster: PriceAdjuster | null;
43
+ }
44
+ /**
45
+ * Minimal structural shape of a modifier payload — just the fields these
46
+ * helpers read. Accepts a full modifier from the API directly (its extra
47
+ * fields are ignored).
48
+ */
49
+ interface ModifierLike {
50
+ validation?: unknown;
51
+ config?: unknown;
52
+ }
53
+ /** Narrow a raw value to a PriceAdjuster, or null if it isn't one. */
54
+ declare function asPriceAdjuster(value: unknown): PriceAdjuster | null;
55
+ /**
56
+ * Narrow a raw modifier `config` to a typed ModifierConfig, or null when it
57
+ * carries no structured price (free modifiers, or product_list modifiers whose
58
+ * price lives on a referenced product). `priceAdjusters` only contains entries
59
+ * that parse — legacy/unknown shapes are dropped.
60
+ */
61
+ declare function asModifierConfig(config: unknown): ModifierConfig | null;
62
+ /** The structured price adjuster for a given choice value, or null. */
63
+ declare function getModifierPriceAdjuster(modifier: ModifierLike, choiceValue: string): PriceAdjuster | null;
64
+ /**
65
+ * Resolve a modifier's choices, each joined with its structured price delta.
66
+ * Returns `[]` for modifiers with no choice list (free-text, number, file).
67
+ */
68
+ declare function getModifierChoices(modifier: ModifierLike): ModifierChoice[];
69
+ /**
70
+ * The signed cent delta a price adjuster applies to a base price. `fixed`
71
+ * ignores the base (returns its `amount_cents`); `percentage` is `basis_points`
72
+ * of `base.amount_cents`, rounded to the nearest cent.
73
+ */
74
+ declare function priceAdjusterCents(base: Money, adjuster: PriceAdjuster): number;
75
+ /**
76
+ * Apply a price adjuster to a base Money, returning the adjusted total in the
77
+ * base currency. A `fixed` adjuster's currency is assumed to match the base
78
+ * (modifiers are same-currency in practice); only its cent delta is used.
79
+ */
80
+ declare function applyPriceAdjuster(base: Money, adjuster: PriceAdjuster): Money;
81
+ /**
82
+ * Human-readable, always-signed delta string: `+$170.00`, `-$2.70`, `+10%`.
83
+ * `fixed` uses `formatMoney` (locale-correct negatives); `percentage` renders
84
+ * basis points as a percent.
85
+ */
86
+ declare function formatPriceAdjuster(adjuster: PriceAdjuster, locale?: string): string;
87
+
88
+ export { type ModifierChoice, type ModifierConfig, type ModifierLike, type PriceAdjuster, applyPriceAdjuster, asModifierConfig, asPriceAdjuster, formatPriceAdjuster, getModifierChoices, getModifierPriceAdjuster, priceAdjusterCents };