@thorprovider/types 5.7.8 → 5.7.9
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/.turbo/turbo-build.log +18 -0
- package/.turbo/turbo-type-check.log +0 -0
- package/AGENTS.md +1 -1
- package/README.md +8 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/index.d.mts +373 -124
- package/dist/index.d.ts +373 -124
- package/dist/index.js +0 -138
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +0 -125
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
- package/publish.log +60 -0
- package/src/admin/DropshippingAdmin.ts +0 -35
- package/src/cart.ts +40 -0
- package/src/catalog.ts +210 -0
- package/src/collection.ts +10 -0
- package/src/commerce-provider.ts +36 -0
- package/src/commerce.ts +88 -0
- package/src/index.ts +30 -16
- package/src/provider.ts +14 -178
- package/src/storefront.ts +5 -32
package/src/catalog.ts
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @thorprovider/types — Canonical catalog query, truthful results, and facet support.
|
|
3
|
+
*
|
|
4
|
+
* Type-only module: no runtime code, constants, or schemas. These types are shared by
|
|
5
|
+
* the URL boundary, the runtime commerce bridge, and the adapters so every product
|
|
6
|
+
* listing asks the backend one well-formed question and reports truthfully what was
|
|
7
|
+
* applied. Validation (Zod) lives in `@thorprovider/runtime/contracts`; translation to
|
|
8
|
+
* backend parameters lives in the adapters.
|
|
9
|
+
*
|
|
10
|
+
* E2 `storefront-catalog-search-filters`. The canonical URL parameter contract is:
|
|
11
|
+
* `q` (text), the route category scope plus `categories`/`collections`, `sort`
|
|
12
|
+
* (`featured` | `name` | `newest`), `page`/`pageSize`, and the optional requested
|
|
13
|
+
* facets `priceMin`/`priceMax`/`inStock`/`attr.<name>`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { Product } from './product';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Canonical, backend-verified sort keys.
|
|
20
|
+
*
|
|
21
|
+
* Only these three are verified against Medusa v2: `featured` (server default),
|
|
22
|
+
* `name` (translated to `title`), and `newest` (translated to `-created_at`). Global
|
|
23
|
+
* price ordering and rating are not verified and are rejected by the boundary as a
|
|
24
|
+
* typed unsupported-query error. An adapter advertises the subset it can actually
|
|
25
|
+
* apply; the listing never offers a sort the backend cannot perform.
|
|
26
|
+
*/
|
|
27
|
+
export type CatalogSortKey = 'featured' | 'name' | 'newest';
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Canonical catalog query crossing the URL boundary, the runtime bridge, and the
|
|
31
|
+
* adapters.
|
|
32
|
+
*
|
|
33
|
+
* Fields mirror the fixed, lowercase URL namespace. They are optional because
|
|
34
|
+
* documented defaults (`sort` = `featured`, `page` = `1`, `pageSize` = the chosen
|
|
35
|
+
* target default) are resolved by the URL boundary and apply only to absent
|
|
36
|
+
* parameters, never to invalid present values.
|
|
37
|
+
*/
|
|
38
|
+
export interface CatalogQuery {
|
|
39
|
+
/** Free-text term (`q`). Trimmed, bounded, and empty treated as absent. */
|
|
40
|
+
text?: string;
|
|
41
|
+
/**
|
|
42
|
+
* Category scope supplied by the route path (`/categorias/[[...slug]]`). The route
|
|
43
|
+
* scope is always retained, even when both optional features are removed.
|
|
44
|
+
*/
|
|
45
|
+
categoryScope?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Category handles requested by the `category-filters` feature. The effective
|
|
48
|
+
* scope is `categoryScope ∩ categories`; an empty intersection is a valid
|
|
49
|
+
* zero-result query, never an invalid-query error.
|
|
50
|
+
*/
|
|
51
|
+
categories?: string[];
|
|
52
|
+
/** Collection handles requested by the `category-filters` feature. */
|
|
53
|
+
collections?: string[];
|
|
54
|
+
/** Canonical sort key; defaults are resolved at the URL boundary, not here. */
|
|
55
|
+
sort?: CatalogSortKey;
|
|
56
|
+
/** 1-based page; bounds are enforced at the URL boundary. */
|
|
57
|
+
page?: number;
|
|
58
|
+
/** Page size; bounds are enforced at the URL boundary. */
|
|
59
|
+
pageSize?: number;
|
|
60
|
+
/** Minimum price in major units; honored only when the price facet is demonstrated. */
|
|
61
|
+
priceMin?: number;
|
|
62
|
+
/** Maximum price in major units; honored only when the price facet is demonstrated. */
|
|
63
|
+
priceMax?: number;
|
|
64
|
+
/** In-stock only; honored only when demonstrated, otherwise a typed unsupported error. */
|
|
65
|
+
inStock?: boolean;
|
|
66
|
+
/** Requested attribute filters keyed by attribute name (`attr.<name>`). */
|
|
67
|
+
attributes?: Record<string, string[]>;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Filters the backend actually applied to a successful result.
|
|
72
|
+
*
|
|
73
|
+
* A result is reported as success only when every requested filter appears here.
|
|
74
|
+
*/
|
|
75
|
+
export interface CatalogAppliedFilters {
|
|
76
|
+
/** Text term applied within the current scope. */
|
|
77
|
+
text?: string;
|
|
78
|
+
/** Category handles applied (the effective intersected scope). */
|
|
79
|
+
categories?: string[];
|
|
80
|
+
/** Collection handles applied. */
|
|
81
|
+
collections?: string[];
|
|
82
|
+
/** Minimum price applied. */
|
|
83
|
+
priceMin?: number;
|
|
84
|
+
/** Maximum price applied. */
|
|
85
|
+
priceMax?: number;
|
|
86
|
+
/** In-stock constraint applied. */
|
|
87
|
+
inStock?: boolean;
|
|
88
|
+
/** Attribute filters applied, keyed by attribute name. */
|
|
89
|
+
attributes?: Record<string, string[]>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Canonical filter identifiers used by the support descriptor.
|
|
94
|
+
*
|
|
95
|
+
* `attr.<name>` covers backend-advertised filterable attributes.
|
|
96
|
+
*/
|
|
97
|
+
export type CatalogFacetId =
|
|
98
|
+
| 'categories'
|
|
99
|
+
| 'collections'
|
|
100
|
+
| 'price'
|
|
101
|
+
| 'inStock'
|
|
102
|
+
| `attr.${string}`;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Real-endpoint demonstration source for an offered facet.
|
|
106
|
+
*
|
|
107
|
+
* Records the actual backend endpoint demonstrated with the applied query for the
|
|
108
|
+
* current scope, not a capability flag or returned facet metadata.
|
|
109
|
+
*/
|
|
110
|
+
export interface CatalogFacetDemonstrationSource {
|
|
111
|
+
/** Real backend endpoint demonstrated with the applied query (e.g. `/store/products`). */
|
|
112
|
+
endpoint: string;
|
|
113
|
+
/** The query and scope the endpoint was demonstrated with. */
|
|
114
|
+
appliedQuery?: CatalogQuery;
|
|
115
|
+
/** Store/scope the demonstration applies to. */
|
|
116
|
+
scope?: string;
|
|
117
|
+
/** ISO-8601 timestamp of when support was demonstrated. */
|
|
118
|
+
demonstratedAt?: string;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Truthful facet support descriptor: which filters the backend has demonstrated
|
|
123
|
+
* (offered) versus which are undemonstrated and held, plus the demonstration source
|
|
124
|
+
* for each offered filter.
|
|
125
|
+
*
|
|
126
|
+
* Contains no counts or ranges. Facet option counts and numeric ranges come only from
|
|
127
|
+
* backend data returned for the current query and are never synthesized here.
|
|
128
|
+
*/
|
|
129
|
+
export interface FacetSupport {
|
|
130
|
+
/** Filters demonstrated against the real endpoint for the current scope. */
|
|
131
|
+
offered: CatalogFacetId[];
|
|
132
|
+
/** Filters not demonstrated; held and diagnosed, never silently offered. */
|
|
133
|
+
held: CatalogFacetId[];
|
|
134
|
+
/** Real-endpoint demonstration source per offered filter, keyed by filter id. */
|
|
135
|
+
demonstrations?: Record<string, CatalogFacetDemonstrationSource>;
|
|
136
|
+
/** Diagnostic reason per held filter, keyed by filter id. */
|
|
137
|
+
heldReasons?: Record<string, string>;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Truthful catalog result for a fully applied query.
|
|
142
|
+
*
|
|
143
|
+
* When any requested filter cannot be applied, the query MUST return a typed
|
|
144
|
+
* unsupported-query error (defined in `@thorprovider/runtime/contracts`) instead of
|
|
145
|
+
* this shape; a partial result is never labelled success. `total` is present only
|
|
146
|
+
* when the backend supplies it and is never manufactured.
|
|
147
|
+
*/
|
|
148
|
+
export interface CatalogResults {
|
|
149
|
+
/** Matching products for the requested scope, sort, and page. */
|
|
150
|
+
products: Product[];
|
|
151
|
+
/** Total matches supplied by the backend; absent when the backend returns none. */
|
|
152
|
+
total?: number;
|
|
153
|
+
/** Filters the backend actually applied. */
|
|
154
|
+
appliedFilters: CatalogAppliedFilters;
|
|
155
|
+
/** Facet support descriptor for the query and scope. */
|
|
156
|
+
facetSupport?: FacetSupport;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Adapter-seam typed failure code for a canonical catalog query.
|
|
161
|
+
*
|
|
162
|
+
* `unsupported-query` is the code an adapter returns when a requested filter
|
|
163
|
+
* cannot be applied to the backend. `invalid-query` and `unmounted-capability`
|
|
164
|
+
* are produced by the URL boundary (`@thorprovider/runtime/contracts`); the
|
|
165
|
+
* adapter seam mirrors them so the runtime bridge can surface either shape
|
|
166
|
+
* without a translation layer.
|
|
167
|
+
*/
|
|
168
|
+
export type CatalogQueryFailureCode =
|
|
169
|
+
| 'invalid-query'
|
|
170
|
+
| 'unsupported-query'
|
|
171
|
+
| 'unmounted-capability';
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Compact, structured repair metadata: the raw parameter names a host must drop
|
|
175
|
+
* to reach a valid query. Structurally identical to the URL boundary's repair.
|
|
176
|
+
*/
|
|
177
|
+
export interface CatalogQueryFailureRepair {
|
|
178
|
+
/** Raw URL parameter names to remove (exactly the offending fields). */
|
|
179
|
+
remove: string[];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Typed catalog-query failure returned across the adapter seam.
|
|
184
|
+
*
|
|
185
|
+
* Structurally identical to `CatalogQueryError` in
|
|
186
|
+
* `@thorprovider/runtime/contracts`, so the two are mutually assignable and the
|
|
187
|
+
* runtime bridge can pass one through as the other. `fields` names the
|
|
188
|
+
* offending raw parameters; `feature` is present only for
|
|
189
|
+
* `unmounted-capability`.
|
|
190
|
+
*/
|
|
191
|
+
export interface CatalogQueryFailure {
|
|
192
|
+
code: CatalogQueryFailureCode;
|
|
193
|
+
fields: string[];
|
|
194
|
+
message: string;
|
|
195
|
+
repair: CatalogQueryFailureRepair;
|
|
196
|
+
feature?: 'catalog-search' | 'category-filters';
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Discriminated outcome of a canonical catalog query against a provider.
|
|
201
|
+
*
|
|
202
|
+
* A provider returns `{ ok: true, results }` **only** when every requested
|
|
203
|
+
* filter was applied. When any requested filter cannot be applied it returns
|
|
204
|
+
* `{ ok: false, error }` with `code: 'unsupported-query'` naming the
|
|
205
|
+
* unappliable fields; a partial result is never returned as success and a
|
|
206
|
+
* requested filter is never silently dropped.
|
|
207
|
+
*/
|
|
208
|
+
export type CatalogQueryOutcome =
|
|
209
|
+
| { ok: true; results: CatalogResults }
|
|
210
|
+
| { ok: false; error: CatalogQueryFailure };
|
package/src/collection.ts
CHANGED
|
@@ -26,6 +26,16 @@ export interface GetCollectionsOptions {
|
|
|
26
26
|
limit?: number;
|
|
27
27
|
/** Search query to filter collections by title */
|
|
28
28
|
query?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Scope results to a specific sales channel / storefront channel.
|
|
31
|
+
* When provided, only collections that contain at least one product available
|
|
32
|
+
* in the given channel will be returned.
|
|
33
|
+
*
|
|
34
|
+
* Maps to: Medusa `sales_channel_id`, Shopify Sales Channel, etc.
|
|
35
|
+
* Adapters that support native server-side filtering should prefer it;
|
|
36
|
+
* others may fall back to a client-side post-filter pass.
|
|
37
|
+
*/
|
|
38
|
+
salesChannelId?: string;
|
|
29
39
|
}
|
|
30
40
|
|
|
31
41
|
/**
|
package/src/commerce-provider.ts
CHANGED
|
@@ -40,6 +40,7 @@ import type { StorefrontContext } from './storefront';
|
|
|
40
40
|
import type { StockLocation, InventoryLevel, StockValidation, FulfillmentOption } from './stock-location';
|
|
41
41
|
import type { StorefrontConfig } from './storefront-config';
|
|
42
42
|
import type { DesignerConfig, DesignerHistoryEntry } from './designer-config';
|
|
43
|
+
import type { CatalogQuery, CatalogQueryOutcome, CatalogSortKey } from './catalog';
|
|
43
44
|
|
|
44
45
|
/**
|
|
45
46
|
* Backend capabilities interface
|
|
@@ -213,6 +214,22 @@ export interface CommerceProvider {
|
|
|
213
214
|
*/
|
|
214
215
|
readonly capabilities: BackendCapabilities;
|
|
215
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Canonical catalog sort keys this provider can actually apply at the query
|
|
219
|
+
* level (E2 `storefront-catalog-search-filters`, design Decision 6).
|
|
220
|
+
*
|
|
221
|
+
* Advertises only backend-verified orderings — for Medusa, `featured`
|
|
222
|
+
* (server default), `name` (translated to `title`), and `newest` (translated
|
|
223
|
+
* to `-created_at`). A global price or rating sort is never advertised. The
|
|
224
|
+
* runtime query boundary consumes this set as `supportedSortKeys` and turns a
|
|
225
|
+
* requested sort outside it into a typed unsupported-query error, so the
|
|
226
|
+
* listing never offers an ordering the backend cannot perform.
|
|
227
|
+
*
|
|
228
|
+
* Optional and additive: when absent, no ordering beyond the canonical default
|
|
229
|
+
* is assumed from the advertisement.
|
|
230
|
+
*/
|
|
231
|
+
readonly supportedCatalogSortKeys?: readonly CatalogSortKey[];
|
|
232
|
+
|
|
216
233
|
/**
|
|
217
234
|
* Whether this provider is backed by fixture/mock data instead of a live backend.
|
|
218
235
|
*
|
|
@@ -247,6 +264,25 @@ export interface CommerceProvider {
|
|
|
247
264
|
* for layout/design workflows until a real backend is configured.
|
|
248
265
|
*/
|
|
249
266
|
getProducts(options?: GetProductsOptions): Promise<Product[]>;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Run a canonical, validated catalog query against the real listing endpoint
|
|
270
|
+
* (OPTIONAL — E2 `storefront-catalog-search-filters`, task 2.2).
|
|
271
|
+
*
|
|
272
|
+
* The adapter translates the canonical `CatalogQuery` (text, the route
|
|
273
|
+
* category scope plus `categories`/`collections`, sort, pagination, and
|
|
274
|
+
* requested filters) into its backend's parameters and returns a truthful
|
|
275
|
+
* `CatalogQueryOutcome`: `{ ok: true, results }` only when **every** requested
|
|
276
|
+
* filter was applied, or `{ ok: false, error }` with `code:
|
|
277
|
+
* 'unsupported-query'` naming the fields it cannot apply. It never returns a
|
|
278
|
+
* partial result labelled success and never silently drops a requested filter.
|
|
279
|
+
*
|
|
280
|
+
* Optional and additive: providers that cannot forward a canonical query omit
|
|
281
|
+
* it and callers fall back to `getProducts`/`getCategoryProducts`. The legacy
|
|
282
|
+
* zero-argument and options-based methods keep their `Product[]` return and
|
|
283
|
+
* are unchanged. This is the typed seam the runtime catalog bridge consumes.
|
|
284
|
+
*/
|
|
285
|
+
getCatalogProducts?(query: CatalogQuery): Promise<CatalogQueryOutcome>;
|
|
250
286
|
|
|
251
287
|
/**
|
|
252
288
|
* Get product recommendations for a given product
|
package/src/commerce.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @thorprovider/types — Commerce contracts (type-only)
|
|
3
|
+
*
|
|
4
|
+
* Single-tenant, guest-only commerce shapes owned once by this module.
|
|
5
|
+
* Validation lives in `@thorprovider/runtime/contracts` (pure Zod); this
|
|
6
|
+
* module exports types only and emits no runtime values.
|
|
7
|
+
*
|
|
8
|
+
* Canonical product/variant/Money/cart/order shapes are NOT redefined here —
|
|
9
|
+
* they stay owned by `./common`, `./product`, `./cart` and `./order`.
|
|
10
|
+
*
|
|
11
|
+
* @module commerce
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { Money } from './common';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The validated single-store context — the multitenancy seam.
|
|
18
|
+
*
|
|
19
|
+
* Declares store identity, sales channel, region, and currency once and is
|
|
20
|
+
* validated at the boundary before any commerce call. Multitenancy is
|
|
21
|
+
* post-MVP; a future tenancy layer varies these values without redefining
|
|
22
|
+
* the commerce shapes.
|
|
23
|
+
*/
|
|
24
|
+
export interface StoreContext {
|
|
25
|
+
/** Stable store identity. */
|
|
26
|
+
storeId: string;
|
|
27
|
+
|
|
28
|
+
/** Sales channel the store is scoped to (Medusa `sales_channel_id`). */
|
|
29
|
+
salesChannelId: string;
|
|
30
|
+
|
|
31
|
+
/** Backend region that supplies currency, countries and shipping. */
|
|
32
|
+
regionId: string;
|
|
33
|
+
|
|
34
|
+
/** ISO 4217 currency code from the validated context — never hardcoded. */
|
|
35
|
+
currencyCode: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Issued guest shopper identity, scoped to a store.
|
|
40
|
+
*
|
|
41
|
+
* Backed by a cookie the Next host owns; it maps to a persisted backend guest
|
|
42
|
+
* cart so a reload reattaches to the same cart and two shoppers stay isolated.
|
|
43
|
+
*/
|
|
44
|
+
export interface ShopperIdentity {
|
|
45
|
+
/** Opaque shopper identity issued by the host cookie. */
|
|
46
|
+
shopperId: string;
|
|
47
|
+
|
|
48
|
+
/** Store the identity is scoped to. */
|
|
49
|
+
storeId: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* A persisted backend guest cart session bound to a shopper identity.
|
|
54
|
+
*/
|
|
55
|
+
export interface CartSession {
|
|
56
|
+
/** Backend cart identifier. */
|
|
57
|
+
cartId: string;
|
|
58
|
+
|
|
59
|
+
/** Owning shopper identity. */
|
|
60
|
+
shopperId: string;
|
|
61
|
+
|
|
62
|
+
/** Store the session is scoped to. */
|
|
63
|
+
storeId: string;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Safe, buyer/store-authorized order receipt projection.
|
|
68
|
+
*
|
|
69
|
+
* A minimal recoverable subset of the canonical `Order` (`./order`), used by
|
|
70
|
+
* the guest receipt protocol. Access is authorized for the buyer or the
|
|
71
|
+
* store; it is never granted by possession of a bare public order id.
|
|
72
|
+
*/
|
|
73
|
+
export interface OrderReceipt {
|
|
74
|
+
/** Backend order identifier. */
|
|
75
|
+
orderId: string;
|
|
76
|
+
|
|
77
|
+
/** Human-readable order number. */
|
|
78
|
+
orderNumber: string;
|
|
79
|
+
|
|
80
|
+
/** Buyer email the order was placed with. */
|
|
81
|
+
email: string;
|
|
82
|
+
|
|
83
|
+
/** Order total in major units. */
|
|
84
|
+
total: Money;
|
|
85
|
+
|
|
86
|
+
/** ISO 8601 creation timestamp. */
|
|
87
|
+
createdAt: string;
|
|
88
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,16 @@ export type {
|
|
|
20
20
|
Country,
|
|
21
21
|
} from './common';
|
|
22
22
|
|
|
23
|
+
// ============================================
|
|
24
|
+
// Commerce Contracts (single-store context + guest shapes)
|
|
25
|
+
// ============================================
|
|
26
|
+
export type {
|
|
27
|
+
StoreContext,
|
|
28
|
+
ShopperIdentity,
|
|
29
|
+
CartSession,
|
|
30
|
+
OrderReceipt,
|
|
31
|
+
} from './commerce';
|
|
32
|
+
|
|
23
33
|
export type {
|
|
24
34
|
AuthorConfig,
|
|
25
35
|
BrandConfig,
|
|
@@ -43,6 +53,23 @@ export type {
|
|
|
43
53
|
QueryResponse,
|
|
44
54
|
} from './query';
|
|
45
55
|
|
|
56
|
+
// ============================================
|
|
57
|
+
// Catalog Query & Facet Support Types
|
|
58
|
+
// ============================================
|
|
59
|
+
export type {
|
|
60
|
+
CatalogSortKey,
|
|
61
|
+
CatalogQuery,
|
|
62
|
+
CatalogAppliedFilters,
|
|
63
|
+
CatalogFacetId,
|
|
64
|
+
CatalogFacetDemonstrationSource,
|
|
65
|
+
FacetSupport,
|
|
66
|
+
CatalogResults,
|
|
67
|
+
CatalogQueryFailureCode,
|
|
68
|
+
CatalogQueryFailureRepair,
|
|
69
|
+
CatalogQueryFailure,
|
|
70
|
+
CatalogQueryOutcome,
|
|
71
|
+
} from './catalog';
|
|
72
|
+
|
|
46
73
|
// ============================================
|
|
47
74
|
// Product Types
|
|
48
75
|
// ============================================
|
|
@@ -142,15 +169,10 @@ export type {
|
|
|
142
169
|
} from './order';
|
|
143
170
|
|
|
144
171
|
// ============================================
|
|
145
|
-
// Provider Types (L1 -
|
|
172
|
+
// Provider Types (L1 - type-only)
|
|
173
|
+
// Runtime provider registry lives in @thorprovider/adapters
|
|
146
174
|
// ============================================
|
|
147
|
-
export {
|
|
148
|
-
|
|
149
|
-
export {
|
|
150
|
-
isSupportedProviderType,
|
|
151
|
-
getProviderMetadata,
|
|
152
|
-
PROVIDER_METADATA,
|
|
153
|
-
} from './provider';
|
|
175
|
+
export type { ProviderTypeString } from './provider';
|
|
154
176
|
|
|
155
177
|
// ============================================
|
|
156
178
|
// Storefront Types
|
|
@@ -160,10 +182,6 @@ export type {
|
|
|
160
182
|
StorefrontPlatformType,
|
|
161
183
|
} from './storefront';
|
|
162
184
|
|
|
163
|
-
export {
|
|
164
|
-
StorefrontConfigError,
|
|
165
|
-
} from './storefront';
|
|
166
|
-
|
|
167
185
|
// ============================================
|
|
168
186
|
// Storefront Configuration Types
|
|
169
187
|
// ============================================
|
|
@@ -421,10 +439,6 @@ export type {
|
|
|
421
439
|
ConvertDropshipperDraftOrderBody,
|
|
422
440
|
ConvertDropshipperDraftOrderResponse,
|
|
423
441
|
DeleteDropshipperDraftOrderResponse,
|
|
424
|
-
// Order totals preview
|
|
425
|
-
PreviewDropshipperOrderItem,
|
|
426
|
-
PreviewDropshipperOrderTotalsBody,
|
|
427
|
-
PreviewDropshipperOrderTotalsResponse,
|
|
428
442
|
// Dropshipper addresses (channel-scoped)
|
|
429
443
|
DropshipperAddressBody,
|
|
430
444
|
DropshipperAddressResponse,
|
package/src/provider.ts
CHANGED
|
@@ -1,47 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @thorprovider/types — Supported Providers
|
|
2
|
+
* @thorprovider/types — Supported Providers (type-only)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* This is the single source of truth for provider types across the monorepo.
|
|
4
|
+
* Defines the provider-type union contract shared across the monorepo.
|
|
6
5
|
*
|
|
7
6
|
* @remarks
|
|
8
|
-
* - L1 (Foundation):
|
|
9
|
-
* - L2 (@thorprovider/adapters)
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* All supported commerce platform providers.
|
|
22
|
-
* `mock` is the backend-optional runtime provider used for layout/design mode.
|
|
23
|
-
* `medusa` is fully implemented for real backend usage; others are placeholders for future expansion.
|
|
24
|
-
*
|
|
25
|
-
* @remarks Implementation status:
|
|
26
|
-
* - ✅ mock: Fixture-backed provider for backend-optional storefronts
|
|
27
|
-
* - ✅ medusa: Fully implemented
|
|
28
|
-
* - 🟡 stelorder: In progress (ERP adapter v1)
|
|
29
|
-
* - 🟡 shopify: Planned (Phase 2)
|
|
30
|
-
* - 🟡 bigcommerce: Planned (Phase 2)
|
|
31
|
-
* - 🟡 woocommerce: Planned (Phase 3)
|
|
32
|
-
* - 🟡 spree: Planned (Phase 3)
|
|
33
|
-
* - 🟡 magento: Planned (Phase 3)
|
|
7
|
+
* - L1 (Foundation): owns the `ProviderTypeString` union type only.
|
|
8
|
+
* - L2 (@thorprovider/adapters) owns the runtime provider registry
|
|
9
|
+
* (`SupportedProviderType`, `PROVIDER_METADATA`, `isSupportedProviderType`,
|
|
10
|
+
* `getProviderMetadata`) in `@thorprovider/adapters`.
|
|
11
|
+
*
|
|
12
|
+
* When adding a new provider:
|
|
13
|
+
* 1. Add the string to `ProviderTypeString` below.
|
|
14
|
+
* 2. Add it to `SupportedProviderType` + `PROVIDER_METADATA` in
|
|
15
|
+
* packages/adapters/src/factory/provider-types.ts.
|
|
16
|
+
* 3. Create packages/adapters/src/providers/[platform]/ and implement the
|
|
17
|
+
* `CommerceProvider` interface.
|
|
18
|
+
* 4. Update `StorefrontPlatformType` in storefront.ts.
|
|
34
19
|
*/
|
|
35
|
-
export enum SupportedProviderType {
|
|
36
|
-
Mock = 'mock',
|
|
37
|
-
Medusa = 'medusa',
|
|
38
|
-
StelOrder = 'stelorder',
|
|
39
|
-
Shopify = 'shopify',
|
|
40
|
-
BigCommerce = 'bigcommerce',
|
|
41
|
-
WooCommerce = 'woocommerce',
|
|
42
|
-
Spree = 'spree',
|
|
43
|
-
Magento = 'magento',
|
|
44
|
-
}
|
|
45
20
|
|
|
46
21
|
/**
|
|
47
22
|
* Union type of all supported provider string values.
|
|
@@ -55,142 +30,3 @@ export enum SupportedProviderType {
|
|
|
55
30
|
* ```
|
|
56
31
|
*/
|
|
57
32
|
export type ProviderTypeString = 'mock' | 'medusa' | 'stelorder' | 'shopify' | 'bigcommerce' | 'woocommerce' | 'spree' | 'magento';
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* Metadata about each provider.
|
|
61
|
-
* Useful for UI, error messages, validation, and documentation.
|
|
62
|
-
*
|
|
63
|
-
* @internal
|
|
64
|
-
*/
|
|
65
|
-
export const PROVIDER_METADATA: Record<
|
|
66
|
-
ProviderTypeString,
|
|
67
|
-
{
|
|
68
|
-
name: string;
|
|
69
|
-
description: string;
|
|
70
|
-
requiresStorefront: boolean;
|
|
71
|
-
requiredEnvVars: string[];
|
|
72
|
-
maxRetries: number;
|
|
73
|
-
}
|
|
74
|
-
> = {
|
|
75
|
-
mock: {
|
|
76
|
-
name: 'Mock',
|
|
77
|
-
description: 'Fixture-backed runtime provider for layout/design mode without a real backend',
|
|
78
|
-
requiresStorefront: false,
|
|
79
|
-
requiredEnvVars: [],
|
|
80
|
-
maxRetries: 0,
|
|
81
|
-
},
|
|
82
|
-
medusa: {
|
|
83
|
-
name: 'Medusa JS',
|
|
84
|
-
description: 'Medusa v2 commerce engine',
|
|
85
|
-
requiresStorefront: true,
|
|
86
|
-
requiredEnvVars: [
|
|
87
|
-
'NEXT_PUBLIC_COMMERCE_API_URL',
|
|
88
|
-
'NEXT_PUBLIC_COMMERCE_API_KEY',
|
|
89
|
-
'NEXT_PUBLIC_SALES_CHANNEL_ID',
|
|
90
|
-
],
|
|
91
|
-
maxRetries: 3,
|
|
92
|
-
},
|
|
93
|
-
stelorder: {
|
|
94
|
-
name: 'Stel Order',
|
|
95
|
-
description: 'Stel Order ERP and sales document API',
|
|
96
|
-
requiresStorefront: false,
|
|
97
|
-
requiredEnvVars: [
|
|
98
|
-
'NEXT_PUBLIC_COMMERCE_API_URL',
|
|
99
|
-
'NEXT_PUBLIC_COMMERCE_API_KEY',
|
|
100
|
-
],
|
|
101
|
-
maxRetries: 2,
|
|
102
|
-
},
|
|
103
|
-
shopify: {
|
|
104
|
-
name: 'Shopify',
|
|
105
|
-
description: 'Shopify Storefront API (GraphQL)',
|
|
106
|
-
requiresStorefront: false,
|
|
107
|
-
requiredEnvVars: [
|
|
108
|
-
'NEXT_PUBLIC_SHOPIFY_STORE_DOMAIN',
|
|
109
|
-
'NEXT_PUBLIC_SHOPIFY_STOREFRONT_TOKEN',
|
|
110
|
-
],
|
|
111
|
-
maxRetries: 3,
|
|
112
|
-
},
|
|
113
|
-
bigcommerce: {
|
|
114
|
-
name: 'BigCommerce',
|
|
115
|
-
description: 'BigCommerce REST Storefront API',
|
|
116
|
-
requiresStorefront: false,
|
|
117
|
-
requiredEnvVars: [
|
|
118
|
-
'BIGCOMMERCE_STORE_HASH',
|
|
119
|
-
'BIGCOMMERCE_STOREFRONT_API_TOKEN',
|
|
120
|
-
'BIGCOMMERCE_CHANNEL_ID',
|
|
121
|
-
],
|
|
122
|
-
maxRetries: 3,
|
|
123
|
-
},
|
|
124
|
-
woocommerce: {
|
|
125
|
-
name: 'WooCommerce',
|
|
126
|
-
description: 'WooCommerce Store API + REST API v3',
|
|
127
|
-
requiresStorefront: false,
|
|
128
|
-
requiredEnvVars: [
|
|
129
|
-
'WOOCOMMERCE_URL',
|
|
130
|
-
'WOOCOMMERCE_CONSUMER_KEY',
|
|
131
|
-
'WOOCOMMERCE_CONSUMER_SECRET',
|
|
132
|
-
],
|
|
133
|
-
maxRetries: 3,
|
|
134
|
-
},
|
|
135
|
-
spree: {
|
|
136
|
-
name: 'Spree Commerce',
|
|
137
|
-
description: 'Spree API v2 Storefront (JSON:API)',
|
|
138
|
-
requiresStorefront: false,
|
|
139
|
-
requiredEnvVars: [
|
|
140
|
-
'SPREE_API_URL',
|
|
141
|
-
],
|
|
142
|
-
maxRetries: 3,
|
|
143
|
-
},
|
|
144
|
-
magento: {
|
|
145
|
-
name: 'Adobe Commerce (Magento)',
|
|
146
|
-
description: 'Adobe Commerce GraphQL + REST API',
|
|
147
|
-
requiresStorefront: true,
|
|
148
|
-
requiredEnvVars: [
|
|
149
|
-
'MAGENTO_URL',
|
|
150
|
-
'MAGENTO_STORE_CODE',
|
|
151
|
-
],
|
|
152
|
-
maxRetries: 3,
|
|
153
|
-
},
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Check if a string is a valid provider type.
|
|
158
|
-
*
|
|
159
|
-
* @example
|
|
160
|
-
* ```typescript
|
|
161
|
-
* if (isSupportedProviderType(process.env.COMMERCE_PROVIDER)) {
|
|
162
|
-
* // type is narrowed to ProviderTypeString
|
|
163
|
-
* }
|
|
164
|
-
* ```
|
|
165
|
-
*/
|
|
166
|
-
export function isSupportedProviderType(
|
|
167
|
-
value: unknown
|
|
168
|
-
): value is ProviderTypeString {
|
|
169
|
-
return (
|
|
170
|
-
typeof value === 'string' &&
|
|
171
|
-
Object.values(SupportedProviderType).includes(value as SupportedProviderType)
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Get metadata for a provider type.
|
|
177
|
-
* Useful for validation, error messages, and logging.
|
|
178
|
-
*
|
|
179
|
-
* @example
|
|
180
|
-
* ```typescript
|
|
181
|
-
* const meta = getProviderMetadata('medusa');
|
|
182
|
-
* console.log(`Using ${meta.name} with max ${meta.maxRetries} retries`);
|
|
183
|
-
* ```
|
|
184
|
-
*/
|
|
185
|
-
export function getProviderMetadata(
|
|
186
|
-
type: ProviderTypeString
|
|
187
|
-
): (typeof PROVIDER_METADATA)[ProviderTypeString] {
|
|
188
|
-
if (!isSupportedProviderType(type)) {
|
|
189
|
-
throw new Error(
|
|
190
|
-
`Unsupported provider: ${type}. Supported: ${Object.values(
|
|
191
|
-
SupportedProviderType
|
|
192
|
-
).join(', ')}`
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
return PROVIDER_METADATA[type];
|
|
196
|
-
}
|