@putiikkipalvelu/storefront-sdk 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Putiikkipalvelu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # @putiikkipalvelu/storefront-sdk
2
+
3
+ > **Alpha (0.x)**: API may change between versions
4
+
5
+ TypeScript SDK for building headless storefronts with Putiikkipalvelu.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npm install @putiikkipalvelu/storefront-sdk
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ ```typescript
16
+ import { StorefrontClient } from '@putiikkipalvelu/storefront-sdk';
17
+
18
+ const client = new StorefrontClient({
19
+ apiKey: process.env.STOREFRONT_API_KEY!,
20
+ baseUrl: 'https://putiikkipalvelu.fi',
21
+ });
22
+
23
+ // Get store configuration
24
+ const config = await client.store.getConfig();
25
+ console.log(config.store.name);
26
+ ```
27
+
28
+ ## Next.js Integration
29
+
30
+ ```typescript
31
+ // lib/storefront.ts
32
+ import { StorefrontClient } from '@putiikkipalvelu/storefront-sdk';
33
+
34
+ export const storefront = new StorefrontClient({
35
+ apiKey: process.env.STOREFRONT_API_KEY!,
36
+ baseUrl: process.env.NEXT_PUBLIC_STOREFRONT_API_URL!,
37
+ });
38
+ ```
39
+
40
+ ### With Caching
41
+
42
+ ```typescript
43
+ const config = await storefront.store.getConfig({
44
+ next: { revalidate: 300 } // Cache for 5 minutes
45
+ });
46
+ ```
47
+
48
+ ## Available Resources
49
+
50
+ - `store.getConfig()` - Store configuration, SEO, payments, campaigns
51
+
52
+ More resources coming soon.
53
+
54
+ ## License
55
+
56
+ MIT
package/dist/index.cjs ADDED
@@ -0,0 +1,196 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});// src/utils/errors.ts
2
+ var StorefrontError = class _StorefrontError extends Error {
3
+ constructor(message, status, code) {
4
+ super(message);
5
+ this.name = "StorefrontError";
6
+ this.status = status;
7
+ this.code = code;
8
+ if (Error.captureStackTrace) {
9
+ Error.captureStackTrace(this, _StorefrontError);
10
+ }
11
+ }
12
+ };
13
+ var AuthError = class extends StorefrontError {
14
+ constructor(message = "Invalid or missing API key") {
15
+ super(message, 401, "UNAUTHORIZED");
16
+ this.name = "AuthError";
17
+ }
18
+ };
19
+ var RateLimitError = class extends StorefrontError {
20
+ constructor(message = "Rate limit exceeded", retryAfter = null) {
21
+ super(message, 429, "RATE_LIMIT_EXCEEDED");
22
+ this.name = "RateLimitError";
23
+ this.retryAfter = retryAfter;
24
+ }
25
+ };
26
+ var NotFoundError = class extends StorefrontError {
27
+ constructor(message = "Resource not found") {
28
+ super(message, 404, "NOT_FOUND");
29
+ this.name = "NotFoundError";
30
+ }
31
+ };
32
+ var ValidationError = class extends StorefrontError {
33
+ constructor(message = "Validation failed") {
34
+ super(message, 400, "VALIDATION_ERROR");
35
+ this.name = "ValidationError";
36
+ }
37
+ };
38
+
39
+ // src/utils/fetch.ts
40
+ var SDK_VERSION = "0.1.0";
41
+ function createFetcher(config) {
42
+ const { apiKey, baseUrl, timeout = 3e4 } = config;
43
+ async function request(endpoint, options = {}) {
44
+ const { method = "GET", body, params, signal, headers = {}, cache, ...frameworkOptions } = options;
45
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
46
+ const normalizedEndpoint = endpoint.startsWith("/") ? endpoint.slice(1) : endpoint;
47
+ const url = new URL(normalizedEndpoint, normalizedBase);
48
+ if (params) {
49
+ for (const [key, value] of Object.entries(params)) {
50
+ if (value !== void 0) {
51
+ url.searchParams.set(key, String(value));
52
+ }
53
+ }
54
+ }
55
+ const controller = new AbortController();
56
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
57
+ const combinedSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
58
+ try {
59
+ const fetchOptions = {
60
+ method,
61
+ headers: {
62
+ "Content-Type": "application/json",
63
+ "x-api-key": apiKey,
64
+ "x-sdk-version": SDK_VERSION,
65
+ ...headers
66
+ },
67
+ body: body ? JSON.stringify(body) : void 0,
68
+ signal: combinedSignal,
69
+ cache,
70
+ // Spread framework-specific options (e.g., Next.js `next`, or any other)
71
+ ...frameworkOptions
72
+ };
73
+ const response = await fetch(url.toString(), fetchOptions);
74
+ clearTimeout(timeoutId);
75
+ if (!response.ok) {
76
+ await handleErrorResponse(response);
77
+ }
78
+ const data = await response.json();
79
+ return data;
80
+ } catch (error) {
81
+ clearTimeout(timeoutId);
82
+ if (error instanceof StorefrontError) {
83
+ throw error;
84
+ }
85
+ if (error instanceof Error && error.name === "AbortError") {
86
+ throw new StorefrontError("Request timed out", 408, "TIMEOUT");
87
+ }
88
+ throw new StorefrontError(
89
+ error instanceof Error ? error.message : "Network error",
90
+ 0,
91
+ "NETWORK_ERROR"
92
+ );
93
+ }
94
+ }
95
+ return { request };
96
+ }
97
+ async function handleErrorResponse(response) {
98
+ let errorMessage = null;
99
+ try {
100
+ const json = await response.json();
101
+ if (json && typeof json === "object" && "error" in json) {
102
+ errorMessage = json.error;
103
+ }
104
+ } catch (e) {
105
+ }
106
+ const message = errorMessage || response.statusText || "Request failed";
107
+ switch (response.status) {
108
+ case 401:
109
+ throw new AuthError(message);
110
+ case 404:
111
+ throw new NotFoundError(message);
112
+ case 429: {
113
+ const retryAfter = response.headers.get("Retry-After");
114
+ throw new RateLimitError(message, retryAfter ? parseInt(retryAfter, 10) : null);
115
+ }
116
+ case 400:
117
+ throw new ValidationError(message);
118
+ default:
119
+ throw new StorefrontError(message, response.status, "API_ERROR");
120
+ }
121
+ }
122
+
123
+ // src/resources/store.ts
124
+ function createStoreResource(fetcher) {
125
+ return {
126
+ /**
127
+ * Get the complete store configuration including settings, SEO, payments, campaigns, and features.
128
+ *
129
+ * @example Basic usage
130
+ * ```typescript
131
+ * const config = await client.store.getConfig();
132
+ * console.log(config.store.name);
133
+ * console.log(config.seo.seoTitle);
134
+ * console.log(config.campaigns);
135
+ * ```
136
+ *
137
+ * @example Next.js - with caching
138
+ * ```typescript
139
+ * const config = await client.store.getConfig({
140
+ * next: { revalidate: 300, tags: ['store-config'] }
141
+ * });
142
+ * ```
143
+ *
144
+ * @example Nuxt - wrap with useAsyncData
145
+ * ```typescript
146
+ * const { data: config } = await useAsyncData(
147
+ * 'store-config',
148
+ * () => client.store.getConfig()
149
+ * );
150
+ * ```
151
+ *
152
+ * @example Standard fetch caching
153
+ * ```typescript
154
+ * const config = await client.store.getConfig({
155
+ * cache: 'force-cache'
156
+ * });
157
+ * ```
158
+ */
159
+ async getConfig(options) {
160
+ return fetcher.request("/api/storefront/v1/store-config", {
161
+ ...options
162
+ });
163
+ }
164
+ };
165
+ }
166
+
167
+ // src/client.ts
168
+ function createStorefrontClient(config) {
169
+ if (!config.apiKey) {
170
+ throw new Error("apiKey is required");
171
+ }
172
+ if (!config.baseUrl) {
173
+ throw new Error("baseUrl is required");
174
+ }
175
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
176
+ const maskedApiKey = config.apiKey.length > 8 ? `${config.apiKey.slice(0, 8)}...` : config.apiKey;
177
+ const fetcher = createFetcher({
178
+ apiKey: config.apiKey,
179
+ baseUrl,
180
+ timeout: config.timeout
181
+ });
182
+ return {
183
+ apiKey: maskedApiKey,
184
+ baseUrl,
185
+ store: createStoreResource(fetcher)
186
+ };
187
+ }
188
+
189
+
190
+
191
+
192
+
193
+
194
+
195
+ exports.AuthError = AuthError; exports.NotFoundError = NotFoundError; exports.RateLimitError = RateLimitError; exports.StorefrontError = StorefrontError; exports.ValidationError = ValidationError; exports.createStorefrontClient = createStorefrontClient;
196
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["d:\\Projektit\\verkkokauppapalvelu\\packages\\sdk\\dist\\index.cjs"],"names":[],"mappings":"AAAA;AACA,IAAI,gBAAgB,EAAE,MAAM,iBAAiB,QAAQ,MAAM;AAC3D,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;AACrC,IAAI,KAAK,CAAC,OAAO,CAAC;AAClB,IAAI,IAAI,CAAC,KAAK,EAAE,iBAAiB;AACjC,IAAI,IAAI,CAAC,OAAO,EAAE,MAAM;AACxB,IAAI,IAAI,CAAC,KAAK,EAAE,IAAI;AACpB,IAAI,GAAG,CAAC,KAAK,CAAC,iBAAiB,EAAE;AACjC,MAAM,KAAK,CAAC,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,CAAC;AACrD,IAAI;AACJ,EAAE;AACF,CAAC;AACD,IAAI,UAAU,EAAE,MAAM,QAAQ,gBAAgB;AAC9C,EAAE,WAAW,CAAC,QAAQ,EAAE,4BAA4B,EAAE;AACtD,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,cAAc,CAAC;AACvC,IAAI,IAAI,CAAC,KAAK,EAAE,WAAW;AAC3B,EAAE;AACF,CAAC;AACD,IAAI,eAAe,EAAE,MAAM,QAAQ,gBAAgB;AACnD,EAAE,WAAW,CAAC,QAAQ,EAAE,qBAAqB,EAAE,WAAW,EAAE,IAAI,EAAE;AAClE,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,qBAAqB,CAAC;AAC9C,IAAI,IAAI,CAAC,KAAK,EAAE,gBAAgB;AAChC,IAAI,IAAI,CAAC,WAAW,EAAE,UAAU;AAChC,EAAE;AACF,CAAC;AACD,IAAI,cAAc,EAAE,MAAM,QAAQ,gBAAgB;AAClD,EAAE,WAAW,CAAC,QAAQ,EAAE,oBAAoB,EAAE;AAC9C,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC;AACpC,IAAI,IAAI,CAAC,KAAK,EAAE,eAAe;AAC/B,EAAE;AACF,CAAC;AACD,IAAI,gBAAgB,EAAE,MAAM,QAAQ,gBAAgB;AACpD,EAAE,WAAW,CAAC,QAAQ,EAAE,mBAAmB,EAAE;AAC7C,IAAI,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,kBAAkB,CAAC;AAC3C,IAAI,IAAI,CAAC,KAAK,EAAE,iBAAiB;AACjC,EAAE;AACF,CAAC;AACD;AACA;AACA,IAAI,YAAY,EAAE,OAAO;AACzB,SAAS,aAAa,CAAC,MAAM,EAAE;AAC/B,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,MAAM;AACnD,EAAE,MAAM,SAAS,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE;AACjD,IAAI,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,GAAG,iBAAiB,EAAE,EAAE,OAAO;AACtG,IAAI,MAAM,eAAe,EAAE,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,UAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,MAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA;AACA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,MAAA;AACA,IAAA;AACA,IAAA;AACA,MAAA;AACA,IAAA;AACA,MAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,IAAA;AACA,MAAA;AACA,QAAA;AACA,MAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA,EAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,EAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"D:\\Projektit\\verkkokauppapalvelu\\packages\\sdk\\dist\\index.cjs","sourcesContent":[null]}
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Store configuration types for the Storefront API
3
+ */
4
+ /**
5
+ * Store business information
6
+ */
7
+ interface StoreInfo {
8
+ id: string;
9
+ name: string;
10
+ email: string;
11
+ phone: string;
12
+ address: string;
13
+ city: string;
14
+ postalCode: string;
15
+ country: string;
16
+ currency: string;
17
+ currencySymbol: string;
18
+ defaultVatRate: number;
19
+ businessId: string;
20
+ logoUrl: string | null;
21
+ }
22
+ /**
23
+ * Store SEO and social media configuration
24
+ */
25
+ interface StoreSeo {
26
+ seoTitle: string | null;
27
+ seoDescription: string | null;
28
+ domain: string | null;
29
+ openGraphImageUrl: string | null;
30
+ twitterImageUrl: string | null;
31
+ instagramUrl: string | null;
32
+ facebookUrl: string | null;
33
+ priceRange: string | null;
34
+ businessType: string | null;
35
+ }
36
+ /**
37
+ * Payment configuration
38
+ */
39
+ interface PaymentConfig {
40
+ methods: string[];
41
+ defaultVatRate: number;
42
+ }
43
+ /**
44
+ * Feature flags for the store
45
+ */
46
+ interface FeatureFlags {
47
+ wishlistEnabled: boolean;
48
+ guestCheckoutEnabled: boolean;
49
+ newsletterEnabled: boolean;
50
+ reviewsEnabled: boolean;
51
+ }
52
+ /**
53
+ * Shipment method for free shipping campaigns
54
+ */
55
+ interface ShipmentMethod {
56
+ id: string;
57
+ storeId: string;
58
+ name: string;
59
+ description: string | null;
60
+ price: number;
61
+ active: boolean;
62
+ min_estimate_delivery_days: number | null;
63
+ max_estimate_delivery_days: number | null;
64
+ }
65
+ /**
66
+ * Free shipping campaign details
67
+ */
68
+ interface FreeShippingCampaign {
69
+ id: string;
70
+ campaignId: string;
71
+ minimumSpend: number;
72
+ shipmentMethods: ShipmentMethod[];
73
+ }
74
+ /**
75
+ * Category reference for Buy X Pay Y campaigns
76
+ */
77
+ interface CategoryReference {
78
+ id: string;
79
+ name?: string;
80
+ slug?: string;
81
+ }
82
+ /**
83
+ * Buy X Pay Y campaign details
84
+ */
85
+ interface BuyXPayYCampaign {
86
+ id: string;
87
+ campaignId: string;
88
+ buyQuantity: number;
89
+ payQuantity: number;
90
+ applicableCategories: CategoryReference[];
91
+ }
92
+ /**
93
+ * Campaign type enum
94
+ */
95
+ type CampaignType = "FREE_SHIPPING" | "BUY_X_PAY_Y";
96
+ /**
97
+ * Store campaign
98
+ * Note: Date fields can be Date objects (from Prisma) or strings (after JSON serialization)
99
+ */
100
+ interface Campaign {
101
+ id: string;
102
+ storeId: string;
103
+ name: string;
104
+ description: string | null;
105
+ type: CampaignType;
106
+ startDate: Date | string;
107
+ endDate: Date | string | null;
108
+ isActive: boolean;
109
+ createdAt: Date | string;
110
+ updatedAt: Date | string;
111
+ FreeShippingCampaign?: FreeShippingCampaign | null;
112
+ BuyXPayYCampaign?: BuyXPayYCampaign | null;
113
+ }
114
+ /**
115
+ * Complete store configuration returned by store.getConfig()
116
+ */
117
+ interface StoreConfig {
118
+ store: StoreInfo;
119
+ seo: StoreSeo;
120
+ payments: PaymentConfig;
121
+ campaigns: Campaign[];
122
+ features: FeatureFlags;
123
+ }
124
+
125
+ /**
126
+ * SDK Configuration options
127
+ */
128
+ interface StorefrontClientConfig {
129
+ /**
130
+ * Your store's API key (required)
131
+ */
132
+ apiKey: string;
133
+ /**
134
+ * Base URL for the Storefront API
135
+ */
136
+ baseUrl: string;
137
+ /**
138
+ * Request timeout in milliseconds
139
+ * @default 30000
140
+ */
141
+ timeout?: number;
142
+ }
143
+ /**
144
+ * Base options that can be passed to any API method.
145
+ * Framework-agnostic - works with Next.js, Nuxt, or plain fetch.
146
+ */
147
+ interface FetchOptions {
148
+ /**
149
+ * AbortSignal for cancelling requests
150
+ */
151
+ signal?: AbortSignal;
152
+ /**
153
+ * Additional headers to send with the request
154
+ */
155
+ headers?: Record<string, string>;
156
+ /**
157
+ * Standard fetch cache mode
158
+ */
159
+ cache?: RequestCache;
160
+ /**
161
+ * Framework-specific options passthrough.
162
+ * These are spread directly to the underlying fetch call.
163
+ *
164
+ * @example Next.js caching
165
+ * ```typescript
166
+ * await client.store.getConfig({
167
+ * next: { revalidate: 60, tags: ['store-config'] }
168
+ * });
169
+ * ```
170
+ *
171
+ * @example Standard fetch
172
+ * ```typescript
173
+ * await client.store.getConfig({
174
+ * cache: 'force-cache'
175
+ * });
176
+ * ```
177
+ */
178
+ [key: string]: unknown;
179
+ }
180
+
181
+ interface FetcherConfig {
182
+ apiKey: string;
183
+ baseUrl: string;
184
+ timeout?: number;
185
+ }
186
+ interface RequestOptions extends FetchOptions {
187
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
188
+ body?: unknown;
189
+ params?: Record<string, string | number | boolean | undefined>;
190
+ }
191
+ /**
192
+ * Fetcher instance type returned by createFetcher
193
+ */
194
+ type Fetcher = ReturnType<typeof createFetcher>;
195
+ /**
196
+ * Create a configured fetcher instance for making API requests
197
+ */
198
+ declare function createFetcher(config: FetcherConfig): {
199
+ request: <T>(endpoint: string, options?: RequestOptions) => Promise<T>;
200
+ };
201
+
202
+ /**
203
+ * Store resource for fetching store configuration
204
+ */
205
+ declare function createStoreResource(fetcher: Fetcher): {
206
+ /**
207
+ * Get the complete store configuration including settings, SEO, payments, campaigns, and features.
208
+ *
209
+ * @example Basic usage
210
+ * ```typescript
211
+ * const config = await client.store.getConfig();
212
+ * console.log(config.store.name);
213
+ * console.log(config.seo.seoTitle);
214
+ * console.log(config.campaigns);
215
+ * ```
216
+ *
217
+ * @example Next.js - with caching
218
+ * ```typescript
219
+ * const config = await client.store.getConfig({
220
+ * next: { revalidate: 300, tags: ['store-config'] }
221
+ * });
222
+ * ```
223
+ *
224
+ * @example Nuxt - wrap with useAsyncData
225
+ * ```typescript
226
+ * const { data: config } = await useAsyncData(
227
+ * 'store-config',
228
+ * () => client.store.getConfig()
229
+ * );
230
+ * ```
231
+ *
232
+ * @example Standard fetch caching
233
+ * ```typescript
234
+ * const config = await client.store.getConfig({
235
+ * cache: 'force-cache'
236
+ * });
237
+ * ```
238
+ */
239
+ getConfig(options?: FetchOptions): Promise<StoreConfig>;
240
+ };
241
+ /**
242
+ * Type for the store resource
243
+ */
244
+ type StoreResource = ReturnType<typeof createStoreResource>;
245
+
246
+ /**
247
+ * The Storefront API client
248
+ */
249
+ interface StorefrontClient {
250
+ /**
251
+ * The configured API key (masked for security)
252
+ */
253
+ readonly apiKey: string;
254
+ /**
255
+ * The base URL for API requests
256
+ */
257
+ readonly baseUrl: string;
258
+ /**
259
+ * Store configuration resource
260
+ */
261
+ readonly store: StoreResource;
262
+ }
263
+ /**
264
+ * Create a new Storefront API client
265
+ */
266
+ declare function createStorefrontClient(config: StorefrontClientConfig): StorefrontClient;
267
+
268
+ /**
269
+ * Base error class for all Storefront API errors
270
+ */
271
+ declare class StorefrontError extends Error {
272
+ readonly status: number;
273
+ readonly code: string;
274
+ constructor(message: string, status: number, code: string);
275
+ }
276
+ /**
277
+ * Error thrown when API returns 401 Unauthorized
278
+ */
279
+ declare class AuthError extends StorefrontError {
280
+ constructor(message?: string);
281
+ }
282
+ /**
283
+ * Error thrown when API returns 429 Too Many Requests
284
+ */
285
+ declare class RateLimitError extends StorefrontError {
286
+ readonly retryAfter: number | null;
287
+ constructor(message?: string, retryAfter?: number | null);
288
+ }
289
+ /**
290
+ * Error thrown when a requested resource is not found (404)
291
+ */
292
+ declare class NotFoundError extends StorefrontError {
293
+ constructor(message?: string);
294
+ }
295
+ /**
296
+ * Error thrown when request validation fails (400)
297
+ */
298
+ declare class ValidationError extends StorefrontError {
299
+ constructor(message?: string);
300
+ }
301
+
302
+ export { AuthError, type BuyXPayYCampaign, type Campaign, type CampaignType, type CategoryReference, type FeatureFlags, type FetchOptions, type FreeShippingCampaign, NotFoundError, type PaymentConfig, RateLimitError, type ShipmentMethod, type StoreConfig, type StoreInfo, type StoreSeo, type StorefrontClient, type StorefrontClientConfig, StorefrontError, ValidationError, createStorefrontClient };
@@ -0,0 +1,302 @@
1
+ /**
2
+ * Store configuration types for the Storefront API
3
+ */
4
+ /**
5
+ * Store business information
6
+ */
7
+ interface StoreInfo {
8
+ id: string;
9
+ name: string;
10
+ email: string;
11
+ phone: string;
12
+ address: string;
13
+ city: string;
14
+ postalCode: string;
15
+ country: string;
16
+ currency: string;
17
+ currencySymbol: string;
18
+ defaultVatRate: number;
19
+ businessId: string;
20
+ logoUrl: string | null;
21
+ }
22
+ /**
23
+ * Store SEO and social media configuration
24
+ */
25
+ interface StoreSeo {
26
+ seoTitle: string | null;
27
+ seoDescription: string | null;
28
+ domain: string | null;
29
+ openGraphImageUrl: string | null;
30
+ twitterImageUrl: string | null;
31
+ instagramUrl: string | null;
32
+ facebookUrl: string | null;
33
+ priceRange: string | null;
34
+ businessType: string | null;
35
+ }
36
+ /**
37
+ * Payment configuration
38
+ */
39
+ interface PaymentConfig {
40
+ methods: string[];
41
+ defaultVatRate: number;
42
+ }
43
+ /**
44
+ * Feature flags for the store
45
+ */
46
+ interface FeatureFlags {
47
+ wishlistEnabled: boolean;
48
+ guestCheckoutEnabled: boolean;
49
+ newsletterEnabled: boolean;
50
+ reviewsEnabled: boolean;
51
+ }
52
+ /**
53
+ * Shipment method for free shipping campaigns
54
+ */
55
+ interface ShipmentMethod {
56
+ id: string;
57
+ storeId: string;
58
+ name: string;
59
+ description: string | null;
60
+ price: number;
61
+ active: boolean;
62
+ min_estimate_delivery_days: number | null;
63
+ max_estimate_delivery_days: number | null;
64
+ }
65
+ /**
66
+ * Free shipping campaign details
67
+ */
68
+ interface FreeShippingCampaign {
69
+ id: string;
70
+ campaignId: string;
71
+ minimumSpend: number;
72
+ shipmentMethods: ShipmentMethod[];
73
+ }
74
+ /**
75
+ * Category reference for Buy X Pay Y campaigns
76
+ */
77
+ interface CategoryReference {
78
+ id: string;
79
+ name?: string;
80
+ slug?: string;
81
+ }
82
+ /**
83
+ * Buy X Pay Y campaign details
84
+ */
85
+ interface BuyXPayYCampaign {
86
+ id: string;
87
+ campaignId: string;
88
+ buyQuantity: number;
89
+ payQuantity: number;
90
+ applicableCategories: CategoryReference[];
91
+ }
92
+ /**
93
+ * Campaign type enum
94
+ */
95
+ type CampaignType = "FREE_SHIPPING" | "BUY_X_PAY_Y";
96
+ /**
97
+ * Store campaign
98
+ * Note: Date fields can be Date objects (from Prisma) or strings (after JSON serialization)
99
+ */
100
+ interface Campaign {
101
+ id: string;
102
+ storeId: string;
103
+ name: string;
104
+ description: string | null;
105
+ type: CampaignType;
106
+ startDate: Date | string;
107
+ endDate: Date | string | null;
108
+ isActive: boolean;
109
+ createdAt: Date | string;
110
+ updatedAt: Date | string;
111
+ FreeShippingCampaign?: FreeShippingCampaign | null;
112
+ BuyXPayYCampaign?: BuyXPayYCampaign | null;
113
+ }
114
+ /**
115
+ * Complete store configuration returned by store.getConfig()
116
+ */
117
+ interface StoreConfig {
118
+ store: StoreInfo;
119
+ seo: StoreSeo;
120
+ payments: PaymentConfig;
121
+ campaigns: Campaign[];
122
+ features: FeatureFlags;
123
+ }
124
+
125
+ /**
126
+ * SDK Configuration options
127
+ */
128
+ interface StorefrontClientConfig {
129
+ /**
130
+ * Your store's API key (required)
131
+ */
132
+ apiKey: string;
133
+ /**
134
+ * Base URL for the Storefront API
135
+ */
136
+ baseUrl: string;
137
+ /**
138
+ * Request timeout in milliseconds
139
+ * @default 30000
140
+ */
141
+ timeout?: number;
142
+ }
143
+ /**
144
+ * Base options that can be passed to any API method.
145
+ * Framework-agnostic - works with Next.js, Nuxt, or plain fetch.
146
+ */
147
+ interface FetchOptions {
148
+ /**
149
+ * AbortSignal for cancelling requests
150
+ */
151
+ signal?: AbortSignal;
152
+ /**
153
+ * Additional headers to send with the request
154
+ */
155
+ headers?: Record<string, string>;
156
+ /**
157
+ * Standard fetch cache mode
158
+ */
159
+ cache?: RequestCache;
160
+ /**
161
+ * Framework-specific options passthrough.
162
+ * These are spread directly to the underlying fetch call.
163
+ *
164
+ * @example Next.js caching
165
+ * ```typescript
166
+ * await client.store.getConfig({
167
+ * next: { revalidate: 60, tags: ['store-config'] }
168
+ * });
169
+ * ```
170
+ *
171
+ * @example Standard fetch
172
+ * ```typescript
173
+ * await client.store.getConfig({
174
+ * cache: 'force-cache'
175
+ * });
176
+ * ```
177
+ */
178
+ [key: string]: unknown;
179
+ }
180
+
181
+ interface FetcherConfig {
182
+ apiKey: string;
183
+ baseUrl: string;
184
+ timeout?: number;
185
+ }
186
+ interface RequestOptions extends FetchOptions {
187
+ method?: "GET" | "POST" | "PUT" | "DELETE" | "PATCH";
188
+ body?: unknown;
189
+ params?: Record<string, string | number | boolean | undefined>;
190
+ }
191
+ /**
192
+ * Fetcher instance type returned by createFetcher
193
+ */
194
+ type Fetcher = ReturnType<typeof createFetcher>;
195
+ /**
196
+ * Create a configured fetcher instance for making API requests
197
+ */
198
+ declare function createFetcher(config: FetcherConfig): {
199
+ request: <T>(endpoint: string, options?: RequestOptions) => Promise<T>;
200
+ };
201
+
202
+ /**
203
+ * Store resource for fetching store configuration
204
+ */
205
+ declare function createStoreResource(fetcher: Fetcher): {
206
+ /**
207
+ * Get the complete store configuration including settings, SEO, payments, campaigns, and features.
208
+ *
209
+ * @example Basic usage
210
+ * ```typescript
211
+ * const config = await client.store.getConfig();
212
+ * console.log(config.store.name);
213
+ * console.log(config.seo.seoTitle);
214
+ * console.log(config.campaigns);
215
+ * ```
216
+ *
217
+ * @example Next.js - with caching
218
+ * ```typescript
219
+ * const config = await client.store.getConfig({
220
+ * next: { revalidate: 300, tags: ['store-config'] }
221
+ * });
222
+ * ```
223
+ *
224
+ * @example Nuxt - wrap with useAsyncData
225
+ * ```typescript
226
+ * const { data: config } = await useAsyncData(
227
+ * 'store-config',
228
+ * () => client.store.getConfig()
229
+ * );
230
+ * ```
231
+ *
232
+ * @example Standard fetch caching
233
+ * ```typescript
234
+ * const config = await client.store.getConfig({
235
+ * cache: 'force-cache'
236
+ * });
237
+ * ```
238
+ */
239
+ getConfig(options?: FetchOptions): Promise<StoreConfig>;
240
+ };
241
+ /**
242
+ * Type for the store resource
243
+ */
244
+ type StoreResource = ReturnType<typeof createStoreResource>;
245
+
246
+ /**
247
+ * The Storefront API client
248
+ */
249
+ interface StorefrontClient {
250
+ /**
251
+ * The configured API key (masked for security)
252
+ */
253
+ readonly apiKey: string;
254
+ /**
255
+ * The base URL for API requests
256
+ */
257
+ readonly baseUrl: string;
258
+ /**
259
+ * Store configuration resource
260
+ */
261
+ readonly store: StoreResource;
262
+ }
263
+ /**
264
+ * Create a new Storefront API client
265
+ */
266
+ declare function createStorefrontClient(config: StorefrontClientConfig): StorefrontClient;
267
+
268
+ /**
269
+ * Base error class for all Storefront API errors
270
+ */
271
+ declare class StorefrontError extends Error {
272
+ readonly status: number;
273
+ readonly code: string;
274
+ constructor(message: string, status: number, code: string);
275
+ }
276
+ /**
277
+ * Error thrown when API returns 401 Unauthorized
278
+ */
279
+ declare class AuthError extends StorefrontError {
280
+ constructor(message?: string);
281
+ }
282
+ /**
283
+ * Error thrown when API returns 429 Too Many Requests
284
+ */
285
+ declare class RateLimitError extends StorefrontError {
286
+ readonly retryAfter: number | null;
287
+ constructor(message?: string, retryAfter?: number | null);
288
+ }
289
+ /**
290
+ * Error thrown when a requested resource is not found (404)
291
+ */
292
+ declare class NotFoundError extends StorefrontError {
293
+ constructor(message?: string);
294
+ }
295
+ /**
296
+ * Error thrown when request validation fails (400)
297
+ */
298
+ declare class ValidationError extends StorefrontError {
299
+ constructor(message?: string);
300
+ }
301
+
302
+ export { AuthError, type BuyXPayYCampaign, type Campaign, type CampaignType, type CategoryReference, type FeatureFlags, type FetchOptions, type FreeShippingCampaign, NotFoundError, type PaymentConfig, RateLimitError, type ShipmentMethod, type StoreConfig, type StoreInfo, type StoreSeo, type StorefrontClient, type StorefrontClientConfig, StorefrontError, ValidationError, createStorefrontClient };
package/dist/index.js ADDED
@@ -0,0 +1,196 @@
1
+ // src/utils/errors.ts
2
+ var StorefrontError = class _StorefrontError extends Error {
3
+ constructor(message, status, code) {
4
+ super(message);
5
+ this.name = "StorefrontError";
6
+ this.status = status;
7
+ this.code = code;
8
+ if (Error.captureStackTrace) {
9
+ Error.captureStackTrace(this, _StorefrontError);
10
+ }
11
+ }
12
+ };
13
+ var AuthError = class extends StorefrontError {
14
+ constructor(message = "Invalid or missing API key") {
15
+ super(message, 401, "UNAUTHORIZED");
16
+ this.name = "AuthError";
17
+ }
18
+ };
19
+ var RateLimitError = class extends StorefrontError {
20
+ constructor(message = "Rate limit exceeded", retryAfter = null) {
21
+ super(message, 429, "RATE_LIMIT_EXCEEDED");
22
+ this.name = "RateLimitError";
23
+ this.retryAfter = retryAfter;
24
+ }
25
+ };
26
+ var NotFoundError = class extends StorefrontError {
27
+ constructor(message = "Resource not found") {
28
+ super(message, 404, "NOT_FOUND");
29
+ this.name = "NotFoundError";
30
+ }
31
+ };
32
+ var ValidationError = class extends StorefrontError {
33
+ constructor(message = "Validation failed") {
34
+ super(message, 400, "VALIDATION_ERROR");
35
+ this.name = "ValidationError";
36
+ }
37
+ };
38
+
39
+ // src/utils/fetch.ts
40
+ var SDK_VERSION = "0.1.0";
41
+ function createFetcher(config) {
42
+ const { apiKey, baseUrl, timeout = 3e4 } = config;
43
+ async function request(endpoint, options = {}) {
44
+ const { method = "GET", body, params, signal, headers = {}, cache, ...frameworkOptions } = options;
45
+ const normalizedBase = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
46
+ const normalizedEndpoint = endpoint.startsWith("/") ? endpoint.slice(1) : endpoint;
47
+ const url = new URL(normalizedEndpoint, normalizedBase);
48
+ if (params) {
49
+ for (const [key, value] of Object.entries(params)) {
50
+ if (value !== void 0) {
51
+ url.searchParams.set(key, String(value));
52
+ }
53
+ }
54
+ }
55
+ const controller = new AbortController();
56
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
57
+ const combinedSignal = signal ? AbortSignal.any([signal, controller.signal]) : controller.signal;
58
+ try {
59
+ const fetchOptions = {
60
+ method,
61
+ headers: {
62
+ "Content-Type": "application/json",
63
+ "x-api-key": apiKey,
64
+ "x-sdk-version": SDK_VERSION,
65
+ ...headers
66
+ },
67
+ body: body ? JSON.stringify(body) : void 0,
68
+ signal: combinedSignal,
69
+ cache,
70
+ // Spread framework-specific options (e.g., Next.js `next`, or any other)
71
+ ...frameworkOptions
72
+ };
73
+ const response = await fetch(url.toString(), fetchOptions);
74
+ clearTimeout(timeoutId);
75
+ if (!response.ok) {
76
+ await handleErrorResponse(response);
77
+ }
78
+ const data = await response.json();
79
+ return data;
80
+ } catch (error) {
81
+ clearTimeout(timeoutId);
82
+ if (error instanceof StorefrontError) {
83
+ throw error;
84
+ }
85
+ if (error instanceof Error && error.name === "AbortError") {
86
+ throw new StorefrontError("Request timed out", 408, "TIMEOUT");
87
+ }
88
+ throw new StorefrontError(
89
+ error instanceof Error ? error.message : "Network error",
90
+ 0,
91
+ "NETWORK_ERROR"
92
+ );
93
+ }
94
+ }
95
+ return { request };
96
+ }
97
+ async function handleErrorResponse(response) {
98
+ let errorMessage = null;
99
+ try {
100
+ const json = await response.json();
101
+ if (json && typeof json === "object" && "error" in json) {
102
+ errorMessage = json.error;
103
+ }
104
+ } catch {
105
+ }
106
+ const message = errorMessage || response.statusText || "Request failed";
107
+ switch (response.status) {
108
+ case 401:
109
+ throw new AuthError(message);
110
+ case 404:
111
+ throw new NotFoundError(message);
112
+ case 429: {
113
+ const retryAfter = response.headers.get("Retry-After");
114
+ throw new RateLimitError(message, retryAfter ? parseInt(retryAfter, 10) : null);
115
+ }
116
+ case 400:
117
+ throw new ValidationError(message);
118
+ default:
119
+ throw new StorefrontError(message, response.status, "API_ERROR");
120
+ }
121
+ }
122
+
123
+ // src/resources/store.ts
124
+ function createStoreResource(fetcher) {
125
+ return {
126
+ /**
127
+ * Get the complete store configuration including settings, SEO, payments, campaigns, and features.
128
+ *
129
+ * @example Basic usage
130
+ * ```typescript
131
+ * const config = await client.store.getConfig();
132
+ * console.log(config.store.name);
133
+ * console.log(config.seo.seoTitle);
134
+ * console.log(config.campaigns);
135
+ * ```
136
+ *
137
+ * @example Next.js - with caching
138
+ * ```typescript
139
+ * const config = await client.store.getConfig({
140
+ * next: { revalidate: 300, tags: ['store-config'] }
141
+ * });
142
+ * ```
143
+ *
144
+ * @example Nuxt - wrap with useAsyncData
145
+ * ```typescript
146
+ * const { data: config } = await useAsyncData(
147
+ * 'store-config',
148
+ * () => client.store.getConfig()
149
+ * );
150
+ * ```
151
+ *
152
+ * @example Standard fetch caching
153
+ * ```typescript
154
+ * const config = await client.store.getConfig({
155
+ * cache: 'force-cache'
156
+ * });
157
+ * ```
158
+ */
159
+ async getConfig(options) {
160
+ return fetcher.request("/api/storefront/v1/store-config", {
161
+ ...options
162
+ });
163
+ }
164
+ };
165
+ }
166
+
167
+ // src/client.ts
168
+ function createStorefrontClient(config) {
169
+ if (!config.apiKey) {
170
+ throw new Error("apiKey is required");
171
+ }
172
+ if (!config.baseUrl) {
173
+ throw new Error("baseUrl is required");
174
+ }
175
+ const baseUrl = config.baseUrl.replace(/\/$/, "");
176
+ const maskedApiKey = config.apiKey.length > 8 ? `${config.apiKey.slice(0, 8)}...` : config.apiKey;
177
+ const fetcher = createFetcher({
178
+ apiKey: config.apiKey,
179
+ baseUrl,
180
+ timeout: config.timeout
181
+ });
182
+ return {
183
+ apiKey: maskedApiKey,
184
+ baseUrl,
185
+ store: createStoreResource(fetcher)
186
+ };
187
+ }
188
+ export {
189
+ AuthError,
190
+ NotFoundError,
191
+ RateLimitError,
192
+ StorefrontError,
193
+ ValidationError,
194
+ createStorefrontClient
195
+ };
196
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/errors.ts","../src/utils/fetch.ts","../src/resources/store.ts","../src/client.ts"],"sourcesContent":["/**\r\n * Base error class for all Storefront API errors\r\n */\r\nexport class StorefrontError extends Error {\r\n public readonly status: number;\r\n public readonly code: string;\r\n\r\n constructor(message: string, status: number, code: string) {\r\n super(message);\r\n this.name = \"StorefrontError\";\r\n this.status = status;\r\n this.code = code;\r\n\r\n // Maintains proper stack trace in V8 environments\r\n if (Error.captureStackTrace) {\r\n Error.captureStackTrace(this, StorefrontError);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when API returns 401 Unauthorized\r\n */\r\nexport class AuthError extends StorefrontError {\r\n constructor(message: string = \"Invalid or missing API key\") {\r\n super(message, 401, \"UNAUTHORIZED\");\r\n this.name = \"AuthError\";\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when API returns 429 Too Many Requests\r\n */\r\nexport class RateLimitError extends StorefrontError {\r\n public readonly retryAfter: number | null;\r\n\r\n constructor(message: string = \"Rate limit exceeded\", retryAfter: number | null = null) {\r\n super(message, 429, \"RATE_LIMIT_EXCEEDED\");\r\n this.name = \"RateLimitError\";\r\n this.retryAfter = retryAfter;\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when a requested resource is not found (404)\r\n */\r\nexport class NotFoundError extends StorefrontError {\r\n constructor(message: string = \"Resource not found\") {\r\n super(message, 404, \"NOT_FOUND\");\r\n this.name = \"NotFoundError\";\r\n }\r\n}\r\n\r\n/**\r\n * Error thrown when request validation fails (400)\r\n */\r\nexport class ValidationError extends StorefrontError {\r\n constructor(message: string = \"Validation failed\") {\r\n super(message, 400, \"VALIDATION_ERROR\");\r\n this.name = \"ValidationError\";\r\n }\r\n}\r\n","import type { FetchOptions } from \"../types/index.js\";\r\nimport { StorefrontError, AuthError, RateLimitError, NotFoundError, ValidationError } from \"./errors.js\";\r\n\r\n// SDK version - will be replaced during build or read from package.json\r\nconst SDK_VERSION = \"0.1.0\";\r\n\r\nexport interface FetcherConfig {\r\n apiKey: string;\r\n baseUrl: string;\r\n timeout?: number;\r\n}\r\n\r\nexport interface RequestOptions extends FetchOptions {\r\n method?: \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"PATCH\";\r\n body?: unknown;\r\n params?: Record<string, string | number | boolean | undefined>;\r\n}\r\n\r\n/**\r\n * Fetcher instance type returned by createFetcher\r\n */\r\nexport type Fetcher = ReturnType<typeof createFetcher>;\r\n\r\n/**\r\n * Create a configured fetcher instance for making API requests\r\n */\r\nexport function createFetcher(config: FetcherConfig) {\r\n const { apiKey, baseUrl, timeout = 30000 } = config;\r\n\r\n /**\r\n * Make an authenticated request to the Storefront API\r\n */\r\n async function request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {\r\n const { method = \"GET\", body, params, signal, headers = {}, cache, ...frameworkOptions } = options;\r\n\r\n // Build URL with query parameters\r\n // Ensure baseUrl ends with / for proper URL resolution\r\n const normalizedBase = baseUrl.endsWith(\"/\") ? baseUrl : `${baseUrl}/`;\r\n // Remove leading slash from endpoint to avoid URL resolution issues\r\n const normalizedEndpoint = endpoint.startsWith(\"/\") ? endpoint.slice(1) : endpoint;\r\n const url = new URL(normalizedEndpoint, normalizedBase);\r\n\r\n if (params) {\r\n for (const [key, value] of Object.entries(params)) {\r\n if (value !== undefined) {\r\n url.searchParams.set(key, String(value));\r\n }\r\n }\r\n }\r\n\r\n // Create abort controller for timeout\r\n const controller = new AbortController();\r\n const timeoutId = setTimeout(() => controller.abort(), timeout);\r\n\r\n // Combine signals if provided\r\n const combinedSignal = signal\r\n ? AbortSignal.any([signal, controller.signal])\r\n : controller.signal;\r\n\r\n try {\r\n const fetchOptions: RequestInit & Record<string, unknown> = {\r\n method,\r\n headers: {\r\n \"Content-Type\": \"application/json\",\r\n \"x-api-key\": apiKey,\r\n \"x-sdk-version\": SDK_VERSION,\r\n ...headers,\r\n },\r\n body: body ? JSON.stringify(body) : undefined,\r\n signal: combinedSignal,\r\n cache,\r\n // Spread framework-specific options (e.g., Next.js `next`, or any other)\r\n ...frameworkOptions,\r\n };\r\n\r\n const response = await fetch(url.toString(), fetchOptions);\r\n\r\n clearTimeout(timeoutId);\r\n\r\n // Handle error responses\r\n if (!response.ok) {\r\n await handleErrorResponse(response);\r\n }\r\n\r\n // Parse response\r\n const data = await response.json();\r\n return data as T;\r\n } catch (error) {\r\n clearTimeout(timeoutId);\r\n\r\n // Re-throw SDK errors\r\n if (error instanceof StorefrontError) {\r\n throw error;\r\n }\r\n\r\n // Handle abort/timeout\r\n if (error instanceof Error && error.name === \"AbortError\") {\r\n throw new StorefrontError(\"Request timed out\", 408, \"TIMEOUT\");\r\n }\r\n\r\n // Handle network errors\r\n throw new StorefrontError(\r\n error instanceof Error ? error.message : \"Network error\",\r\n 0,\r\n \"NETWORK_ERROR\"\r\n );\r\n }\r\n }\r\n\r\n return { request };\r\n}\r\n\r\n/**\r\n * Handle non-2xx responses and throw appropriate errors\r\n */\r\nasync function handleErrorResponse(response: Response): Promise<never> {\r\n let errorMessage: string | null = null;\r\n\r\n try {\r\n const json: unknown = await response.json();\r\n // Extract error message if present\r\n if (json && typeof json === \"object\" && \"error\" in json) {\r\n errorMessage = (json as { error: string }).error;\r\n }\r\n } catch {\r\n // Response body is not JSON\r\n }\r\n\r\n const message = errorMessage || response.statusText || \"Request failed\";\r\n\r\n switch (response.status) {\r\n case 401:\r\n throw new AuthError(message);\r\n\r\n case 404:\r\n throw new NotFoundError(message);\r\n\r\n case 429: {\r\n const retryAfter = response.headers.get(\"Retry-After\");\r\n throw new RateLimitError(message, retryAfter ? parseInt(retryAfter, 10) : null);\r\n }\r\n\r\n case 400:\r\n throw new ValidationError(message);\r\n\r\n default:\r\n throw new StorefrontError(message, response.status, \"API_ERROR\");\r\n }\r\n}\r\n","import type { FetchOptions } from \"../types/index.js\";\r\nimport type { StoreConfig } from \"../types/store.js\";\r\nimport type { Fetcher } from \"../utils/fetch.js\";\r\n\r\n/**\r\n * Store resource for fetching store configuration\r\n */\r\nexport function createStoreResource(fetcher: Fetcher) {\r\n return {\r\n /**\r\n * Get the complete store configuration including settings, SEO, payments, campaigns, and features.\r\n *\r\n * @example Basic usage\r\n * ```typescript\r\n * const config = await client.store.getConfig();\r\n * console.log(config.store.name);\r\n * console.log(config.seo.seoTitle);\r\n * console.log(config.campaigns);\r\n * ```\r\n *\r\n * @example Next.js - with caching\r\n * ```typescript\r\n * const config = await client.store.getConfig({\r\n * next: { revalidate: 300, tags: ['store-config'] }\r\n * });\r\n * ```\r\n *\r\n * @example Nuxt - wrap with useAsyncData\r\n * ```typescript\r\n * const { data: config } = await useAsyncData(\r\n * 'store-config',\r\n * () => client.store.getConfig()\r\n * );\r\n * ```\r\n *\r\n * @example Standard fetch caching\r\n * ```typescript\r\n * const config = await client.store.getConfig({\r\n * cache: 'force-cache'\r\n * });\r\n * ```\r\n */\r\n async getConfig(options?: FetchOptions): Promise<StoreConfig> {\r\n return fetcher.request<StoreConfig>(\"/api/storefront/v1/store-config\", {\r\n ...options,\r\n });\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Type for the store resource\r\n */\r\nexport type StoreResource = ReturnType<typeof createStoreResource>;\r\n","import type { StorefrontClientConfig } from \"./types/index.js\";\r\nimport { createFetcher } from \"./utils/fetch.js\";\r\nimport { createStoreResource, type StoreResource } from \"./resources/store.js\";\r\n\r\n/**\r\n * The Storefront API client\r\n */\r\nexport interface StorefrontClient {\r\n /**\r\n * The configured API key (masked for security)\r\n */\r\n readonly apiKey: string;\r\n\r\n /**\r\n * The base URL for API requests\r\n */\r\n readonly baseUrl: string;\r\n\r\n /**\r\n * Store configuration resource\r\n */\r\n readonly store: StoreResource;\r\n}\r\n\r\n/**\r\n * Create a new Storefront API client\r\n */\r\nexport function createStorefrontClient(config: StorefrontClientConfig): StorefrontClient {\r\n if (!config.apiKey) {\r\n throw new Error(\"apiKey is required\");\r\n }\r\n if (!config.baseUrl) {\r\n throw new Error(\"baseUrl is required\");\r\n }\r\n\r\n // Ensure baseUrl doesn't have trailing slash\r\n const baseUrl = config.baseUrl.replace(/\\/$/, \"\");\r\n\r\n // Mask API key for security (show first 8 chars only)\r\n const maskedApiKey =\r\n config.apiKey.length > 8\r\n ? `${config.apiKey.slice(0, 8)}...`\r\n : config.apiKey;\r\n\r\n // Create the fetcher for making authenticated requests\r\n const fetcher = createFetcher({\r\n apiKey: config.apiKey,\r\n baseUrl,\r\n timeout: config.timeout,\r\n });\r\n\r\n return {\r\n apiKey: maskedApiKey,\r\n baseUrl,\r\n store: createStoreResource(fetcher),\r\n };\r\n}\r\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@putiikkipalvelu/storefront-sdk",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript SDK for Putiikkipalvelu Storefront API",
5
+ "author": "Putiikkipalvelu",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "main": "./dist/index.cjs",
9
+ "module": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./dist/index.d.cts",
19
+ "default": "./dist/index.cjs"
20
+ }
21
+ }
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md"
26
+ ],
27
+ "sideEffects": false,
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsup --watch",
31
+ "test": "vitest run",
32
+ "test:watch": "vitest",
33
+ "typecheck": "tsc --noEmit",
34
+ "prepublishOnly": "npm run build"
35
+ },
36
+ "devDependencies": {
37
+ "tsup": "^8.3.5",
38
+ "typescript": "^5.7.2",
39
+ "vitest": "^2.1.8"
40
+ },
41
+ "engines": {
42
+ "node": ">=18.0.0"
43
+ },
44
+ "keywords": [
45
+ "putiikkipalvelu",
46
+ "storefront",
47
+ "ecommerce",
48
+ "sdk",
49
+ "api-client"
50
+ ],
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "https://github.com/Nikojuu/verkkokauppapalvelu",
54
+ "directory": "packages/sdk"
55
+ },
56
+ "homepage": "https://github.com/Nikojuu/verkkokauppapalvelu/tree/SDK-package/packages/sdk",
57
+ "bugs": {
58
+ "url": "https://github.com/Nikojuu/verkkokauppapalvelu/issues"
59
+ }
60
+ }