@minipim/sdk 0.3.0 → 0.4.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 +28 -0
- package/dist/index.cjs +87 -2
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +79 -1
- package/dist/modifiers.cjs +120 -0
- package/dist/modifiers.d.cts +88 -0
- package/dist/modifiers.d.ts +88 -0
- package/dist/modifiers.js +87 -0
- package/dist/openapi.d.cts +162 -0
- package/dist/openapi.d.ts +162 -0
- package/package.json +14 -2
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
|
|
|
@@ -148,16 +155,94 @@ function formatMoney(money, locale = "en-US") {
|
|
|
148
155
|
currency: money.currency
|
|
149
156
|
}).format(money.amount_cents / 100);
|
|
150
157
|
}
|
|
158
|
+
|
|
159
|
+
// src/modifiers.ts
|
|
160
|
+
function asPriceAdjuster(value) {
|
|
161
|
+
if (!value || typeof value !== "object") return null;
|
|
162
|
+
const a = value;
|
|
163
|
+
if (a.adjuster === "fixed" && typeof a.amount_cents === "number" && typeof a.currency === "string") {
|
|
164
|
+
return { adjuster: "fixed", amount_cents: a.amount_cents, currency: a.currency };
|
|
165
|
+
}
|
|
166
|
+
if (a.adjuster === "percentage" && typeof a.basis_points === "number") {
|
|
167
|
+
return { adjuster: "percentage", basis_points: a.basis_points };
|
|
168
|
+
}
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
function asModifierConfig(config) {
|
|
172
|
+
if (!config || typeof config !== "object") return null;
|
|
173
|
+
const rawAdjusters = config.priceAdjusters;
|
|
174
|
+
if (!rawAdjusters || typeof rawAdjusters !== "object") return null;
|
|
175
|
+
const priceAdjusters = {};
|
|
176
|
+
for (const [key, raw] of Object.entries(rawAdjusters)) {
|
|
177
|
+
const pa = asPriceAdjuster(raw);
|
|
178
|
+
if (pa) priceAdjusters[key] = pa;
|
|
179
|
+
}
|
|
180
|
+
if (Object.keys(priceAdjusters).length === 0) return null;
|
|
181
|
+
const schemaVersion = config.schemaVersion;
|
|
182
|
+
return {
|
|
183
|
+
...typeof schemaVersion === "number" ? { schemaVersion } : {},
|
|
184
|
+
priceAdjusters
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
function getModifierPriceAdjuster(modifier, choiceValue) {
|
|
188
|
+
return asModifierConfig(modifier.config)?.priceAdjusters[choiceValue] ?? null;
|
|
189
|
+
}
|
|
190
|
+
function getModifierChoices(modifier) {
|
|
191
|
+
const { validation } = modifier;
|
|
192
|
+
const options = validation && typeof validation === "object" ? validation.options : void 0;
|
|
193
|
+
if (!Array.isArray(options)) return [];
|
|
194
|
+
const adjusters = asModifierConfig(modifier.config)?.priceAdjusters ?? {};
|
|
195
|
+
const out = [];
|
|
196
|
+
for (const opt of options) {
|
|
197
|
+
if (!opt || typeof opt !== "object") continue;
|
|
198
|
+
const value = opt.value;
|
|
199
|
+
if (typeof value !== "string") continue;
|
|
200
|
+
const label = opt.label;
|
|
201
|
+
out.push({
|
|
202
|
+
value,
|
|
203
|
+
label: typeof label === "string" ? label : value,
|
|
204
|
+
priceAdjuster: adjusters[value] ?? null
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
return out;
|
|
208
|
+
}
|
|
209
|
+
function priceAdjusterCents(base, adjuster) {
|
|
210
|
+
return adjuster.adjuster === "fixed" ? adjuster.amount_cents : Math.round(base.amount_cents * adjuster.basis_points / 1e4);
|
|
211
|
+
}
|
|
212
|
+
function applyPriceAdjuster(base, adjuster) {
|
|
213
|
+
return {
|
|
214
|
+
amount_cents: base.amount_cents + priceAdjusterCents(base, adjuster),
|
|
215
|
+
currency: base.currency
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function formatPriceAdjuster(adjuster, locale = "en-US") {
|
|
219
|
+
if (adjuster.adjuster === "fixed") {
|
|
220
|
+
const money = formatMoney(
|
|
221
|
+
{ amount_cents: adjuster.amount_cents, currency: adjuster.currency },
|
|
222
|
+
locale
|
|
223
|
+
);
|
|
224
|
+
return adjuster.amount_cents > 0 ? `+${money}` : money;
|
|
225
|
+
}
|
|
226
|
+
const percent = adjuster.basis_points / 100;
|
|
227
|
+
return percent > 0 ? `+${percent}%` : `${percent}%`;
|
|
228
|
+
}
|
|
151
229
|
// Annotate the CommonJS export names for ESM import in node:
|
|
152
230
|
0 && (module.exports = {
|
|
231
|
+
applyPriceAdjuster,
|
|
153
232
|
asMeasurement,
|
|
233
|
+
asModifierConfig,
|
|
154
234
|
asMoney,
|
|
235
|
+
asPriceAdjuster,
|
|
155
236
|
collectAll,
|
|
156
237
|
createMinipimClient,
|
|
157
238
|
flattenAttributes,
|
|
158
239
|
formatMoney,
|
|
240
|
+
formatPriceAdjuster,
|
|
159
241
|
getAttribute,
|
|
160
242
|
getErrorMessage,
|
|
243
|
+
getModifierChoices,
|
|
244
|
+
getModifierPriceAdjuster,
|
|
161
245
|
isMinipimError,
|
|
162
|
-
paginate
|
|
246
|
+
paginate,
|
|
247
|
+
priceAdjusterCents
|
|
163
248
|
});
|
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
|
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
|
package/dist/index.js
CHANGED
|
@@ -103,15 +103,93 @@ function formatMoney(money, locale = "en-US") {
|
|
|
103
103
|
currency: money.currency
|
|
104
104
|
}).format(money.amount_cents / 100);
|
|
105
105
|
}
|
|
106
|
+
|
|
107
|
+
// src/modifiers.ts
|
|
108
|
+
function asPriceAdjuster(value) {
|
|
109
|
+
if (!value || typeof value !== "object") return null;
|
|
110
|
+
const a = value;
|
|
111
|
+
if (a.adjuster === "fixed" && typeof a.amount_cents === "number" && typeof a.currency === "string") {
|
|
112
|
+
return { adjuster: "fixed", amount_cents: a.amount_cents, currency: a.currency };
|
|
113
|
+
}
|
|
114
|
+
if (a.adjuster === "percentage" && typeof a.basis_points === "number") {
|
|
115
|
+
return { adjuster: "percentage", basis_points: a.basis_points };
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
function asModifierConfig(config) {
|
|
120
|
+
if (!config || typeof config !== "object") return null;
|
|
121
|
+
const rawAdjusters = config.priceAdjusters;
|
|
122
|
+
if (!rawAdjusters || typeof rawAdjusters !== "object") return null;
|
|
123
|
+
const priceAdjusters = {};
|
|
124
|
+
for (const [key, raw] of Object.entries(rawAdjusters)) {
|
|
125
|
+
const pa = asPriceAdjuster(raw);
|
|
126
|
+
if (pa) priceAdjusters[key] = pa;
|
|
127
|
+
}
|
|
128
|
+
if (Object.keys(priceAdjusters).length === 0) return null;
|
|
129
|
+
const schemaVersion = config.schemaVersion;
|
|
130
|
+
return {
|
|
131
|
+
...typeof schemaVersion === "number" ? { schemaVersion } : {},
|
|
132
|
+
priceAdjusters
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
function getModifierPriceAdjuster(modifier, choiceValue) {
|
|
136
|
+
return asModifierConfig(modifier.config)?.priceAdjusters[choiceValue] ?? null;
|
|
137
|
+
}
|
|
138
|
+
function getModifierChoices(modifier) {
|
|
139
|
+
const { validation } = modifier;
|
|
140
|
+
const options = validation && typeof validation === "object" ? validation.options : void 0;
|
|
141
|
+
if (!Array.isArray(options)) return [];
|
|
142
|
+
const adjusters = asModifierConfig(modifier.config)?.priceAdjusters ?? {};
|
|
143
|
+
const out = [];
|
|
144
|
+
for (const opt of options) {
|
|
145
|
+
if (!opt || typeof opt !== "object") continue;
|
|
146
|
+
const value = opt.value;
|
|
147
|
+
if (typeof value !== "string") continue;
|
|
148
|
+
const label = opt.label;
|
|
149
|
+
out.push({
|
|
150
|
+
value,
|
|
151
|
+
label: typeof label === "string" ? label : value,
|
|
152
|
+
priceAdjuster: adjusters[value] ?? null
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
function priceAdjusterCents(base, adjuster) {
|
|
158
|
+
return adjuster.adjuster === "fixed" ? adjuster.amount_cents : Math.round(base.amount_cents * adjuster.basis_points / 1e4);
|
|
159
|
+
}
|
|
160
|
+
function applyPriceAdjuster(base, adjuster) {
|
|
161
|
+
return {
|
|
162
|
+
amount_cents: base.amount_cents + priceAdjusterCents(base, adjuster),
|
|
163
|
+
currency: base.currency
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function formatPriceAdjuster(adjuster, locale = "en-US") {
|
|
167
|
+
if (adjuster.adjuster === "fixed") {
|
|
168
|
+
const money = formatMoney(
|
|
169
|
+
{ amount_cents: adjuster.amount_cents, currency: adjuster.currency },
|
|
170
|
+
locale
|
|
171
|
+
);
|
|
172
|
+
return adjuster.amount_cents > 0 ? `+${money}` : money;
|
|
173
|
+
}
|
|
174
|
+
const percent = adjuster.basis_points / 100;
|
|
175
|
+
return percent > 0 ? `+${percent}%` : `${percent}%`;
|
|
176
|
+
}
|
|
106
177
|
export {
|
|
178
|
+
applyPriceAdjuster,
|
|
107
179
|
asMeasurement,
|
|
180
|
+
asModifierConfig,
|
|
108
181
|
asMoney,
|
|
182
|
+
asPriceAdjuster,
|
|
109
183
|
collectAll,
|
|
110
184
|
createMinipimClient,
|
|
111
185
|
flattenAttributes,
|
|
112
186
|
formatMoney,
|
|
187
|
+
formatPriceAdjuster,
|
|
113
188
|
getAttribute,
|
|
114
189
|
getErrorMessage,
|
|
190
|
+
getModifierChoices,
|
|
191
|
+
getModifierPriceAdjuster,
|
|
115
192
|
isMinipimError,
|
|
116
|
-
paginate
|
|
193
|
+
paginate,
|
|
194
|
+
priceAdjusterCents
|
|
117
195
|
};
|
|
@@ -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 };
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// src/attributes.ts
|
|
2
|
+
function formatMoney(money, locale = "en-US") {
|
|
3
|
+
return new Intl.NumberFormat(locale, {
|
|
4
|
+
style: "currency",
|
|
5
|
+
currency: money.currency
|
|
6
|
+
}).format(money.amount_cents / 100);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// src/modifiers.ts
|
|
10
|
+
function asPriceAdjuster(value) {
|
|
11
|
+
if (!value || typeof value !== "object") return null;
|
|
12
|
+
const a = value;
|
|
13
|
+
if (a.adjuster === "fixed" && typeof a.amount_cents === "number" && typeof a.currency === "string") {
|
|
14
|
+
return { adjuster: "fixed", amount_cents: a.amount_cents, currency: a.currency };
|
|
15
|
+
}
|
|
16
|
+
if (a.adjuster === "percentage" && typeof a.basis_points === "number") {
|
|
17
|
+
return { adjuster: "percentage", basis_points: a.basis_points };
|
|
18
|
+
}
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
function asModifierConfig(config) {
|
|
22
|
+
if (!config || typeof config !== "object") return null;
|
|
23
|
+
const rawAdjusters = config.priceAdjusters;
|
|
24
|
+
if (!rawAdjusters || typeof rawAdjusters !== "object") return null;
|
|
25
|
+
const priceAdjusters = {};
|
|
26
|
+
for (const [key, raw] of Object.entries(rawAdjusters)) {
|
|
27
|
+
const pa = asPriceAdjuster(raw);
|
|
28
|
+
if (pa) priceAdjusters[key] = pa;
|
|
29
|
+
}
|
|
30
|
+
if (Object.keys(priceAdjusters).length === 0) return null;
|
|
31
|
+
const schemaVersion = config.schemaVersion;
|
|
32
|
+
return {
|
|
33
|
+
...typeof schemaVersion === "number" ? { schemaVersion } : {},
|
|
34
|
+
priceAdjusters
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function getModifierPriceAdjuster(modifier, choiceValue) {
|
|
38
|
+
return asModifierConfig(modifier.config)?.priceAdjusters[choiceValue] ?? null;
|
|
39
|
+
}
|
|
40
|
+
function getModifierChoices(modifier) {
|
|
41
|
+
const { validation } = modifier;
|
|
42
|
+
const options = validation && typeof validation === "object" ? validation.options : void 0;
|
|
43
|
+
if (!Array.isArray(options)) return [];
|
|
44
|
+
const adjusters = asModifierConfig(modifier.config)?.priceAdjusters ?? {};
|
|
45
|
+
const out = [];
|
|
46
|
+
for (const opt of options) {
|
|
47
|
+
if (!opt || typeof opt !== "object") continue;
|
|
48
|
+
const value = opt.value;
|
|
49
|
+
if (typeof value !== "string") continue;
|
|
50
|
+
const label = opt.label;
|
|
51
|
+
out.push({
|
|
52
|
+
value,
|
|
53
|
+
label: typeof label === "string" ? label : value,
|
|
54
|
+
priceAdjuster: adjusters[value] ?? null
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
return out;
|
|
58
|
+
}
|
|
59
|
+
function priceAdjusterCents(base, adjuster) {
|
|
60
|
+
return adjuster.adjuster === "fixed" ? adjuster.amount_cents : Math.round(base.amount_cents * adjuster.basis_points / 1e4);
|
|
61
|
+
}
|
|
62
|
+
function applyPriceAdjuster(base, adjuster) {
|
|
63
|
+
return {
|
|
64
|
+
amount_cents: base.amount_cents + priceAdjusterCents(base, adjuster),
|
|
65
|
+
currency: base.currency
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function formatPriceAdjuster(adjuster, locale = "en-US") {
|
|
69
|
+
if (adjuster.adjuster === "fixed") {
|
|
70
|
+
const money = formatMoney(
|
|
71
|
+
{ amount_cents: adjuster.amount_cents, currency: adjuster.currency },
|
|
72
|
+
locale
|
|
73
|
+
);
|
|
74
|
+
return adjuster.amount_cents > 0 ? `+${money}` : money;
|
|
75
|
+
}
|
|
76
|
+
const percent = adjuster.basis_points / 100;
|
|
77
|
+
return percent > 0 ? `+${percent}%` : `${percent}%`;
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
applyPriceAdjuster,
|
|
81
|
+
asModifierConfig,
|
|
82
|
+
asPriceAdjuster,
|
|
83
|
+
formatPriceAdjuster,
|
|
84
|
+
getModifierChoices,
|
|
85
|
+
getModifierPriceAdjuster,
|
|
86
|
+
priceAdjusterCents
|
|
87
|
+
};
|
package/dist/openapi.d.cts
CHANGED
|
@@ -29,6 +29,7 @@ interface paths {
|
|
|
29
29
|
"application/json": {
|
|
30
30
|
status: string;
|
|
31
31
|
service: string;
|
|
32
|
+
version: string;
|
|
32
33
|
};
|
|
33
34
|
};
|
|
34
35
|
};
|
|
@@ -80,6 +81,57 @@ interface paths {
|
|
|
80
81
|
patch?: never;
|
|
81
82
|
trace?: never;
|
|
82
83
|
};
|
|
84
|
+
"/v1/changelog": {
|
|
85
|
+
parameters: {
|
|
86
|
+
query?: never;
|
|
87
|
+
header?: never;
|
|
88
|
+
path?: never;
|
|
89
|
+
cookie?: never;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Release changelog
|
|
93
|
+
* @description Structured release notes, newest first. The `version` of the first entry is the deployed version (also reported by /healthz).
|
|
94
|
+
*/
|
|
95
|
+
get: {
|
|
96
|
+
parameters: {
|
|
97
|
+
query?: never;
|
|
98
|
+
header?: never;
|
|
99
|
+
path?: never;
|
|
100
|
+
cookie?: never;
|
|
101
|
+
};
|
|
102
|
+
requestBody?: never;
|
|
103
|
+
responses: {
|
|
104
|
+
/** @description Default Response */
|
|
105
|
+
200: {
|
|
106
|
+
headers: {
|
|
107
|
+
[name: string]: unknown;
|
|
108
|
+
};
|
|
109
|
+
content: {
|
|
110
|
+
"application/json": {
|
|
111
|
+
version: string;
|
|
112
|
+
entries: {
|
|
113
|
+
version: string;
|
|
114
|
+
date: string;
|
|
115
|
+
title: string;
|
|
116
|
+
highlights: {
|
|
117
|
+
/** @enum {string} */
|
|
118
|
+
type: "feature" | "fix" | "perf" | "docs";
|
|
119
|
+
text: string;
|
|
120
|
+
}[];
|
|
121
|
+
}[];
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
put?: never;
|
|
128
|
+
post?: never;
|
|
129
|
+
delete?: never;
|
|
130
|
+
options?: never;
|
|
131
|
+
head?: never;
|
|
132
|
+
patch?: never;
|
|
133
|
+
trace?: never;
|
|
134
|
+
};
|
|
83
135
|
"/v1/organizations": {
|
|
84
136
|
parameters: {
|
|
85
137
|
query?: never;
|
|
@@ -1174,6 +1226,7 @@ interface paths {
|
|
|
1174
1226
|
familyId?: string;
|
|
1175
1227
|
categoryId?: string;
|
|
1176
1228
|
brand?: string;
|
|
1229
|
+
tag?: string | string[];
|
|
1177
1230
|
connectorId?: string;
|
|
1178
1231
|
updatedSince?: string;
|
|
1179
1232
|
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
|
|
@@ -1215,6 +1268,7 @@ interface paths {
|
|
|
1215
1268
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1216
1269
|
[key: string]: unknown;
|
|
1217
1270
|
};
|
|
1271
|
+
tags: string[];
|
|
1218
1272
|
/** Format: uuid */
|
|
1219
1273
|
createdBy: string | null;
|
|
1220
1274
|
/** Format: uuid */
|
|
@@ -1263,6 +1317,8 @@ interface paths {
|
|
|
1263
1317
|
value?: unknown;
|
|
1264
1318
|
}[];
|
|
1265
1319
|
};
|
|
1320
|
+
/** @default [] */
|
|
1321
|
+
tags?: string[];
|
|
1266
1322
|
};
|
|
1267
1323
|
};
|
|
1268
1324
|
};
|
|
@@ -1291,6 +1347,7 @@ interface paths {
|
|
|
1291
1347
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1292
1348
|
[key: string]: unknown;
|
|
1293
1349
|
};
|
|
1350
|
+
tags: string[];
|
|
1294
1351
|
/** Format: uuid */
|
|
1295
1352
|
createdBy: string | null;
|
|
1296
1353
|
/** Format: uuid */
|
|
@@ -1340,6 +1397,7 @@ interface paths {
|
|
|
1340
1397
|
familyId?: string;
|
|
1341
1398
|
categoryId?: string;
|
|
1342
1399
|
brand?: string;
|
|
1400
|
+
tag?: string | string[];
|
|
1343
1401
|
connectorId?: string;
|
|
1344
1402
|
updatedSince?: string;
|
|
1345
1403
|
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
|
|
@@ -1383,6 +1441,10 @@ interface paths {
|
|
|
1383
1441
|
value: string;
|
|
1384
1442
|
count: number;
|
|
1385
1443
|
}[];
|
|
1444
|
+
tags: {
|
|
1445
|
+
value: string;
|
|
1446
|
+
count: number;
|
|
1447
|
+
}[];
|
|
1386
1448
|
connectors: {
|
|
1387
1449
|
/** Format: uuid */
|
|
1388
1450
|
connectorId: string;
|
|
@@ -1403,6 +1465,48 @@ interface paths {
|
|
|
1403
1465
|
patch?: never;
|
|
1404
1466
|
trace?: never;
|
|
1405
1467
|
};
|
|
1468
|
+
"/v1/products/tags": {
|
|
1469
|
+
parameters: {
|
|
1470
|
+
query?: never;
|
|
1471
|
+
header?: never;
|
|
1472
|
+
path?: never;
|
|
1473
|
+
cookie?: never;
|
|
1474
|
+
};
|
|
1475
|
+
/**
|
|
1476
|
+
* List all product tags in use, with counts
|
|
1477
|
+
* @description Every distinct tag currently on at least one product, ordered by usage. Tags are free-form, normalized to lowercase slugs on write (`Featured` → `featured`). Filter products by tag with `GET /v1/products?tag=<tag>` (repeatable; multiple tags AND together).
|
|
1478
|
+
*/
|
|
1479
|
+
get: {
|
|
1480
|
+
parameters: {
|
|
1481
|
+
query?: never;
|
|
1482
|
+
header?: never;
|
|
1483
|
+
path?: never;
|
|
1484
|
+
cookie?: never;
|
|
1485
|
+
};
|
|
1486
|
+
requestBody?: never;
|
|
1487
|
+
responses: {
|
|
1488
|
+
/** @description Default Response */
|
|
1489
|
+
200: {
|
|
1490
|
+
headers: {
|
|
1491
|
+
[name: string]: unknown;
|
|
1492
|
+
};
|
|
1493
|
+
content: {
|
|
1494
|
+
"application/json": {
|
|
1495
|
+
value: string;
|
|
1496
|
+
count: number;
|
|
1497
|
+
}[];
|
|
1498
|
+
};
|
|
1499
|
+
};
|
|
1500
|
+
};
|
|
1501
|
+
};
|
|
1502
|
+
put?: never;
|
|
1503
|
+
post?: never;
|
|
1504
|
+
delete?: never;
|
|
1505
|
+
options?: never;
|
|
1506
|
+
head?: never;
|
|
1507
|
+
patch?: never;
|
|
1508
|
+
trace?: never;
|
|
1509
|
+
};
|
|
1406
1510
|
"/v1/products/{id}": {
|
|
1407
1511
|
parameters: {
|
|
1408
1512
|
query?: never;
|
|
@@ -1499,6 +1603,7 @@ interface paths {
|
|
|
1499
1603
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1500
1604
|
[key: string]: unknown;
|
|
1501
1605
|
};
|
|
1606
|
+
tags: string[];
|
|
1502
1607
|
/** Format: uuid */
|
|
1503
1608
|
createdBy: string | null;
|
|
1504
1609
|
/** Format: uuid */
|
|
@@ -1619,6 +1724,7 @@ interface paths {
|
|
|
1619
1724
|
value?: unknown;
|
|
1620
1725
|
}[];
|
|
1621
1726
|
};
|
|
1727
|
+
tags?: string[];
|
|
1622
1728
|
};
|
|
1623
1729
|
};
|
|
1624
1730
|
};
|
|
@@ -1647,6 +1753,7 @@ interface paths {
|
|
|
1647
1753
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1648
1754
|
[key: string]: unknown;
|
|
1649
1755
|
};
|
|
1756
|
+
tags: string[];
|
|
1650
1757
|
/** Format: uuid */
|
|
1651
1758
|
createdBy: string | null;
|
|
1652
1759
|
/** Format: uuid */
|
|
@@ -2092,6 +2199,61 @@ interface paths {
|
|
|
2092
2199
|
patch?: never;
|
|
2093
2200
|
trace?: never;
|
|
2094
2201
|
};
|
|
2202
|
+
"/v1/products/bulk-set-tags": {
|
|
2203
|
+
parameters: {
|
|
2204
|
+
query?: never;
|
|
2205
|
+
header?: never;
|
|
2206
|
+
path?: never;
|
|
2207
|
+
cookie?: never;
|
|
2208
|
+
};
|
|
2209
|
+
get?: never;
|
|
2210
|
+
put?: never;
|
|
2211
|
+
/**
|
|
2212
|
+
* Bulk-modify tags across products
|
|
2213
|
+
* @description `mode=add` unions the listed tags into each product, `mode=remove` strips them, `mode=replace` overwrites the full list. Tags are normalized (slugified + deduped) exactly like single-product writes.
|
|
2214
|
+
*/
|
|
2215
|
+
post: {
|
|
2216
|
+
parameters: {
|
|
2217
|
+
query?: never;
|
|
2218
|
+
header?: never;
|
|
2219
|
+
path?: never;
|
|
2220
|
+
cookie?: never;
|
|
2221
|
+
};
|
|
2222
|
+
requestBody: {
|
|
2223
|
+
content: {
|
|
2224
|
+
"application/json": {
|
|
2225
|
+
productIds: string[];
|
|
2226
|
+
/** @enum {string} */
|
|
2227
|
+
mode: "add" | "remove" | "replace";
|
|
2228
|
+
tags: string[];
|
|
2229
|
+
};
|
|
2230
|
+
};
|
|
2231
|
+
};
|
|
2232
|
+
responses: {
|
|
2233
|
+
/** @description Default Response */
|
|
2234
|
+
200: {
|
|
2235
|
+
headers: {
|
|
2236
|
+
[name: string]: unknown;
|
|
2237
|
+
};
|
|
2238
|
+
content: {
|
|
2239
|
+
"application/json": {
|
|
2240
|
+
results: {
|
|
2241
|
+
/** Format: uuid */
|
|
2242
|
+
productId: string;
|
|
2243
|
+
ok: boolean;
|
|
2244
|
+
error?: string;
|
|
2245
|
+
}[];
|
|
2246
|
+
};
|
|
2247
|
+
};
|
|
2248
|
+
};
|
|
2249
|
+
};
|
|
2250
|
+
};
|
|
2251
|
+
delete?: never;
|
|
2252
|
+
options?: never;
|
|
2253
|
+
head?: never;
|
|
2254
|
+
patch?: never;
|
|
2255
|
+
trace?: never;
|
|
2256
|
+
};
|
|
2095
2257
|
"/v1/products/{id}/variants": {
|
|
2096
2258
|
parameters: {
|
|
2097
2259
|
query?: never;
|
package/dist/openapi.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ interface paths {
|
|
|
29
29
|
"application/json": {
|
|
30
30
|
status: string;
|
|
31
31
|
service: string;
|
|
32
|
+
version: string;
|
|
32
33
|
};
|
|
33
34
|
};
|
|
34
35
|
};
|
|
@@ -80,6 +81,57 @@ interface paths {
|
|
|
80
81
|
patch?: never;
|
|
81
82
|
trace?: never;
|
|
82
83
|
};
|
|
84
|
+
"/v1/changelog": {
|
|
85
|
+
parameters: {
|
|
86
|
+
query?: never;
|
|
87
|
+
header?: never;
|
|
88
|
+
path?: never;
|
|
89
|
+
cookie?: never;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Release changelog
|
|
93
|
+
* @description Structured release notes, newest first. The `version` of the first entry is the deployed version (also reported by /healthz).
|
|
94
|
+
*/
|
|
95
|
+
get: {
|
|
96
|
+
parameters: {
|
|
97
|
+
query?: never;
|
|
98
|
+
header?: never;
|
|
99
|
+
path?: never;
|
|
100
|
+
cookie?: never;
|
|
101
|
+
};
|
|
102
|
+
requestBody?: never;
|
|
103
|
+
responses: {
|
|
104
|
+
/** @description Default Response */
|
|
105
|
+
200: {
|
|
106
|
+
headers: {
|
|
107
|
+
[name: string]: unknown;
|
|
108
|
+
};
|
|
109
|
+
content: {
|
|
110
|
+
"application/json": {
|
|
111
|
+
version: string;
|
|
112
|
+
entries: {
|
|
113
|
+
version: string;
|
|
114
|
+
date: string;
|
|
115
|
+
title: string;
|
|
116
|
+
highlights: {
|
|
117
|
+
/** @enum {string} */
|
|
118
|
+
type: "feature" | "fix" | "perf" | "docs";
|
|
119
|
+
text: string;
|
|
120
|
+
}[];
|
|
121
|
+
}[];
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
put?: never;
|
|
128
|
+
post?: never;
|
|
129
|
+
delete?: never;
|
|
130
|
+
options?: never;
|
|
131
|
+
head?: never;
|
|
132
|
+
patch?: never;
|
|
133
|
+
trace?: never;
|
|
134
|
+
};
|
|
83
135
|
"/v1/organizations": {
|
|
84
136
|
parameters: {
|
|
85
137
|
query?: never;
|
|
@@ -1174,6 +1226,7 @@ interface paths {
|
|
|
1174
1226
|
familyId?: string;
|
|
1175
1227
|
categoryId?: string;
|
|
1176
1228
|
brand?: string;
|
|
1229
|
+
tag?: string | string[];
|
|
1177
1230
|
connectorId?: string;
|
|
1178
1231
|
updatedSince?: string;
|
|
1179
1232
|
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
|
|
@@ -1215,6 +1268,7 @@ interface paths {
|
|
|
1215
1268
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1216
1269
|
[key: string]: unknown;
|
|
1217
1270
|
};
|
|
1271
|
+
tags: string[];
|
|
1218
1272
|
/** Format: uuid */
|
|
1219
1273
|
createdBy: string | null;
|
|
1220
1274
|
/** Format: uuid */
|
|
@@ -1263,6 +1317,8 @@ interface paths {
|
|
|
1263
1317
|
value?: unknown;
|
|
1264
1318
|
}[];
|
|
1265
1319
|
};
|
|
1320
|
+
/** @default [] */
|
|
1321
|
+
tags?: string[];
|
|
1266
1322
|
};
|
|
1267
1323
|
};
|
|
1268
1324
|
};
|
|
@@ -1291,6 +1347,7 @@ interface paths {
|
|
|
1291
1347
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1292
1348
|
[key: string]: unknown;
|
|
1293
1349
|
};
|
|
1350
|
+
tags: string[];
|
|
1294
1351
|
/** Format: uuid */
|
|
1295
1352
|
createdBy: string | null;
|
|
1296
1353
|
/** Format: uuid */
|
|
@@ -1340,6 +1397,7 @@ interface paths {
|
|
|
1340
1397
|
familyId?: string;
|
|
1341
1398
|
categoryId?: string;
|
|
1342
1399
|
brand?: string;
|
|
1400
|
+
tag?: string | string[];
|
|
1343
1401
|
connectorId?: string;
|
|
1344
1402
|
updatedSince?: string;
|
|
1345
1403
|
sortBy?: "name" | "slug" | "status" | "updatedAt" | "createdAt" | "relevance";
|
|
@@ -1383,6 +1441,10 @@ interface paths {
|
|
|
1383
1441
|
value: string;
|
|
1384
1442
|
count: number;
|
|
1385
1443
|
}[];
|
|
1444
|
+
tags: {
|
|
1445
|
+
value: string;
|
|
1446
|
+
count: number;
|
|
1447
|
+
}[];
|
|
1386
1448
|
connectors: {
|
|
1387
1449
|
/** Format: uuid */
|
|
1388
1450
|
connectorId: string;
|
|
@@ -1403,6 +1465,48 @@ interface paths {
|
|
|
1403
1465
|
patch?: never;
|
|
1404
1466
|
trace?: never;
|
|
1405
1467
|
};
|
|
1468
|
+
"/v1/products/tags": {
|
|
1469
|
+
parameters: {
|
|
1470
|
+
query?: never;
|
|
1471
|
+
header?: never;
|
|
1472
|
+
path?: never;
|
|
1473
|
+
cookie?: never;
|
|
1474
|
+
};
|
|
1475
|
+
/**
|
|
1476
|
+
* List all product tags in use, with counts
|
|
1477
|
+
* @description Every distinct tag currently on at least one product, ordered by usage. Tags are free-form, normalized to lowercase slugs on write (`Featured` → `featured`). Filter products by tag with `GET /v1/products?tag=<tag>` (repeatable; multiple tags AND together).
|
|
1478
|
+
*/
|
|
1479
|
+
get: {
|
|
1480
|
+
parameters: {
|
|
1481
|
+
query?: never;
|
|
1482
|
+
header?: never;
|
|
1483
|
+
path?: never;
|
|
1484
|
+
cookie?: never;
|
|
1485
|
+
};
|
|
1486
|
+
requestBody?: never;
|
|
1487
|
+
responses: {
|
|
1488
|
+
/** @description Default Response */
|
|
1489
|
+
200: {
|
|
1490
|
+
headers: {
|
|
1491
|
+
[name: string]: unknown;
|
|
1492
|
+
};
|
|
1493
|
+
content: {
|
|
1494
|
+
"application/json": {
|
|
1495
|
+
value: string;
|
|
1496
|
+
count: number;
|
|
1497
|
+
}[];
|
|
1498
|
+
};
|
|
1499
|
+
};
|
|
1500
|
+
};
|
|
1501
|
+
};
|
|
1502
|
+
put?: never;
|
|
1503
|
+
post?: never;
|
|
1504
|
+
delete?: never;
|
|
1505
|
+
options?: never;
|
|
1506
|
+
head?: never;
|
|
1507
|
+
patch?: never;
|
|
1508
|
+
trace?: never;
|
|
1509
|
+
};
|
|
1406
1510
|
"/v1/products/{id}": {
|
|
1407
1511
|
parameters: {
|
|
1408
1512
|
query?: never;
|
|
@@ -1499,6 +1603,7 @@ interface paths {
|
|
|
1499
1603
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1500
1604
|
[key: string]: unknown;
|
|
1501
1605
|
};
|
|
1606
|
+
tags: string[];
|
|
1502
1607
|
/** Format: uuid */
|
|
1503
1608
|
createdBy: string | null;
|
|
1504
1609
|
/** Format: uuid */
|
|
@@ -1619,6 +1724,7 @@ interface paths {
|
|
|
1619
1724
|
value?: unknown;
|
|
1620
1725
|
}[];
|
|
1621
1726
|
};
|
|
1727
|
+
tags?: string[];
|
|
1622
1728
|
};
|
|
1623
1729
|
};
|
|
1624
1730
|
};
|
|
@@ -1647,6 +1753,7 @@ interface paths {
|
|
|
1647
1753
|
attributes: (string | number | boolean | ("null" | null)) | unknown[] | {
|
|
1648
1754
|
[key: string]: unknown;
|
|
1649
1755
|
};
|
|
1756
|
+
tags: string[];
|
|
1650
1757
|
/** Format: uuid */
|
|
1651
1758
|
createdBy: string | null;
|
|
1652
1759
|
/** Format: uuid */
|
|
@@ -2092,6 +2199,61 @@ interface paths {
|
|
|
2092
2199
|
patch?: never;
|
|
2093
2200
|
trace?: never;
|
|
2094
2201
|
};
|
|
2202
|
+
"/v1/products/bulk-set-tags": {
|
|
2203
|
+
parameters: {
|
|
2204
|
+
query?: never;
|
|
2205
|
+
header?: never;
|
|
2206
|
+
path?: never;
|
|
2207
|
+
cookie?: never;
|
|
2208
|
+
};
|
|
2209
|
+
get?: never;
|
|
2210
|
+
put?: never;
|
|
2211
|
+
/**
|
|
2212
|
+
* Bulk-modify tags across products
|
|
2213
|
+
* @description `mode=add` unions the listed tags into each product, `mode=remove` strips them, `mode=replace` overwrites the full list. Tags are normalized (slugified + deduped) exactly like single-product writes.
|
|
2214
|
+
*/
|
|
2215
|
+
post: {
|
|
2216
|
+
parameters: {
|
|
2217
|
+
query?: never;
|
|
2218
|
+
header?: never;
|
|
2219
|
+
path?: never;
|
|
2220
|
+
cookie?: never;
|
|
2221
|
+
};
|
|
2222
|
+
requestBody: {
|
|
2223
|
+
content: {
|
|
2224
|
+
"application/json": {
|
|
2225
|
+
productIds: string[];
|
|
2226
|
+
/** @enum {string} */
|
|
2227
|
+
mode: "add" | "remove" | "replace";
|
|
2228
|
+
tags: string[];
|
|
2229
|
+
};
|
|
2230
|
+
};
|
|
2231
|
+
};
|
|
2232
|
+
responses: {
|
|
2233
|
+
/** @description Default Response */
|
|
2234
|
+
200: {
|
|
2235
|
+
headers: {
|
|
2236
|
+
[name: string]: unknown;
|
|
2237
|
+
};
|
|
2238
|
+
content: {
|
|
2239
|
+
"application/json": {
|
|
2240
|
+
results: {
|
|
2241
|
+
/** Format: uuid */
|
|
2242
|
+
productId: string;
|
|
2243
|
+
ok: boolean;
|
|
2244
|
+
error?: string;
|
|
2245
|
+
}[];
|
|
2246
|
+
};
|
|
2247
|
+
};
|
|
2248
|
+
};
|
|
2249
|
+
};
|
|
2250
|
+
};
|
|
2251
|
+
delete?: never;
|
|
2252
|
+
options?: never;
|
|
2253
|
+
head?: never;
|
|
2254
|
+
patch?: never;
|
|
2255
|
+
trace?: never;
|
|
2256
|
+
};
|
|
2095
2257
|
"/v1/products/{id}/variants": {
|
|
2096
2258
|
parameters: {
|
|
2097
2259
|
query?: never;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@minipim/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Typed TypeScript client for the MiniPim API.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://github.com/Epic-Design-Labs/minipim",
|
|
@@ -54,6 +54,16 @@
|
|
|
54
54
|
"default": "./dist/attributes.cjs"
|
|
55
55
|
}
|
|
56
56
|
},
|
|
57
|
+
"./modifiers": {
|
|
58
|
+
"import": {
|
|
59
|
+
"types": "./dist/modifiers.d.ts",
|
|
60
|
+
"default": "./dist/modifiers.js"
|
|
61
|
+
},
|
|
62
|
+
"require": {
|
|
63
|
+
"types": "./dist/modifiers.d.cts",
|
|
64
|
+
"default": "./dist/modifiers.cjs"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
57
67
|
"./openapi": {
|
|
58
68
|
"import": {
|
|
59
69
|
"types": "./dist/openapi.d.ts",
|
|
@@ -84,7 +94,8 @@
|
|
|
84
94
|
"devDependencies": {
|
|
85
95
|
"openapi-typescript": "^7.5.0",
|
|
86
96
|
"tsup": "^8.5.1",
|
|
87
|
-
"typescript": "^5.7.0"
|
|
97
|
+
"typescript": "^5.7.0",
|
|
98
|
+
"vitest": "^2.1.0"
|
|
88
99
|
},
|
|
89
100
|
"publishConfig": {
|
|
90
101
|
"access": "public"
|
|
@@ -93,6 +104,7 @@
|
|
|
93
104
|
"scripts": {
|
|
94
105
|
"build": "tsup",
|
|
95
106
|
"typecheck": "tsc --noEmit",
|
|
107
|
+
"test": "vitest run",
|
|
96
108
|
"gen": "openapi-typescript https://api.minipim.com/docs/json -o src/openapi.ts"
|
|
97
109
|
}
|
|
98
110
|
}
|