@inneropen/marvin-sdk 1.0.2 → 1.3.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/dist/index.cjs CHANGED
@@ -53,56 +53,307 @@ var init_config = __esm({
53
53
  }
54
54
  });
55
55
 
56
- // src/client/http.ts
57
- var MarvinHttpClient;
58
- var init_http = __esm({
59
- "src/client/http.ts"() {
56
+ // src/core/errors/MarvinError.ts
57
+ var MarvinError, MarvinApiError, MarvinAuthError, MarvinConfigError, MarvinValidationError;
58
+ var init_MarvinError = __esm({
59
+ "src/core/errors/MarvinError.ts"() {
60
+ "use strict";
61
+ MarvinError = class _MarvinError extends Error {
62
+ constructor(message, code, statusCode) {
63
+ super(message);
64
+ this.code = code;
65
+ this.statusCode = statusCode;
66
+ this.name = "MarvinError";
67
+ Object.setPrototypeOf(this, _MarvinError.prototype);
68
+ }
69
+ code;
70
+ statusCode;
71
+ };
72
+ MarvinApiError = class _MarvinApiError extends MarvinError {
73
+ constructor(message, statusCode, endpoint, responseBody) {
74
+ super(message, "API_ERROR", statusCode);
75
+ this.statusCode = statusCode;
76
+ this.endpoint = endpoint;
77
+ this.responseBody = responseBody;
78
+ this.name = "MarvinApiError";
79
+ Object.setPrototypeOf(this, _MarvinApiError.prototype);
80
+ }
81
+ statusCode;
82
+ endpoint;
83
+ responseBody;
84
+ static fromResponse(status, statusText, endpoint, body) {
85
+ const message = `Marvin API error: ${status} ${statusText} at ${endpoint}${body ? `
86
+ ${body}` : ""}`;
87
+ return new _MarvinApiError(message, status, endpoint, body);
88
+ }
89
+ };
90
+ MarvinAuthError = class _MarvinAuthError extends MarvinError {
91
+ constructor(message, statusCode) {
92
+ super(message, "AUTH_ERROR", statusCode);
93
+ this.name = "MarvinAuthError";
94
+ Object.setPrototypeOf(this, _MarvinAuthError.prototype);
95
+ }
96
+ };
97
+ MarvinConfigError = class _MarvinConfigError extends MarvinError {
98
+ constructor(message) {
99
+ super(message, "CONFIG_ERROR");
100
+ this.name = "MarvinConfigError";
101
+ Object.setPrototypeOf(this, _MarvinConfigError.prototype);
102
+ }
103
+ };
104
+ MarvinValidationError = class _MarvinValidationError extends MarvinError {
105
+ constructor(message, field) {
106
+ super(message, "VALIDATION_ERROR");
107
+ this.field = field;
108
+ this.name = "MarvinValidationError";
109
+ Object.setPrototypeOf(this, _MarvinValidationError.prototype);
110
+ }
111
+ field;
112
+ };
113
+ }
114
+ });
115
+
116
+ // src/core/errors/index.ts
117
+ var init_errors = __esm({
118
+ "src/core/errors/index.ts"() {
60
119
  "use strict";
61
- MarvinHttpClient = class {
120
+ init_MarvinError();
121
+ }
122
+ });
123
+
124
+ // src/core/http/HttpClient.ts
125
+ var HttpClient;
126
+ var init_HttpClient = __esm({
127
+ "src/core/http/HttpClient.ts"() {
128
+ "use strict";
129
+ init_errors();
130
+ HttpClient = class {
131
+ baseUrl;
132
+ auth;
133
+ defaultHeaders;
134
+ credentials;
135
+ timeout;
62
136
  constructor(config) {
63
- this.config = config;
137
+ this.baseUrl = config.baseUrl;
138
+ this.auth = config.auth;
139
+ this.defaultHeaders = config.defaultHeaders || {};
140
+ this.credentials = config.credentials || "same-origin";
141
+ this.timeout = config.timeout || 3e4;
64
142
  }
65
- config;
66
- async fetch(endpoint) {
67
- const url = `${this.config.apiUrl}${endpoint}`;
143
+ /**
144
+ * Build full URL from endpoint
145
+ */
146
+ buildUrl(endpoint) {
147
+ const base = this.baseUrl.replace(/\/$/, "");
148
+ const path = endpoint.replace(/^\//, "");
149
+ return `${base}/${path}`;
150
+ }
151
+ /**
152
+ * Build headers for request
153
+ */
154
+ buildHeaders(additionalHeaders) {
155
+ const headers = new Headers({
156
+ "Content-Type": "application/json",
157
+ ...this.defaultHeaders,
158
+ ...additionalHeaders
159
+ });
160
+ this.auth.injectAuth(headers);
161
+ return headers;
162
+ }
163
+ /**
164
+ * Build query string from params
165
+ */
166
+ buildQueryString(params) {
167
+ const filtered = Object.entries(params).filter(([_, value]) => value !== void 0).map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
168
+ return filtered.length > 0 ? `?${filtered.join("&")}` : "";
169
+ }
170
+ /**
171
+ * Perform HTTP request
172
+ */
173
+ async request(method, endpoint, options) {
174
+ const url = this.buildUrl(endpoint) + (options?.params ? this.buildQueryString(options.params) : "");
175
+ const headers = this.buildHeaders(options?.headers);
176
+ const controller = new AbortController();
177
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
68
178
  try {
69
179
  const response = await fetch(url, {
70
- headers: {
71
- "Authorization": `Bearer ${this.config.siteClientToken}`,
72
- "Content-Type": "application/json"
73
- }
180
+ method,
181
+ headers,
182
+ body: options?.body ? JSON.stringify(options.body) : void 0,
183
+ credentials: this.credentials,
184
+ signal: controller.signal
74
185
  });
186
+ clearTimeout(timeoutId);
187
+ if (response.status === 401 && this.auth.handleUnauthorized) {
188
+ await this.auth.handleUnauthorized();
189
+ }
75
190
  if (!response.ok) {
76
- const errorText = await response.text();
77
- throw new Error(
78
- `Marvin API error: ${response.status} ${response.statusText} at ${endpoint}
79
- ${errorText}`
191
+ const errorText = await response.text().catch(() => "");
192
+ throw MarvinApiError.fromResponse(
193
+ response.status,
194
+ response.statusText,
195
+ endpoint,
196
+ errorText
80
197
  );
81
198
  }
199
+ const contentType = response.headers.get("content-type");
200
+ if (!contentType || !contentType.includes("application/json")) {
201
+ return void 0;
202
+ }
82
203
  return await response.json();
83
204
  } catch (error) {
205
+ clearTimeout(timeoutId);
206
+ if (error instanceof MarvinApiError) {
207
+ throw error;
208
+ }
84
209
  if (error instanceof Error) {
85
- const safeMessage = error.message.replace(
86
- this.config.siteClientToken,
87
- "[REDACTED]"
88
- );
89
- throw new Error(safeMessage);
210
+ if (error.name === "AbortError") {
211
+ throw new MarvinApiError(
212
+ `Request timeout after ${this.timeout}ms`,
213
+ 408,
214
+ endpoint
215
+ );
216
+ }
217
+ throw new MarvinApiError(error.message, 0, endpoint);
90
218
  }
91
219
  throw error;
92
220
  }
93
221
  }
94
- buildQueryString(params) {
95
- const filtered = Object.entries(params).filter(([_, value]) => value !== void 0).map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`);
96
- return filtered.length > 0 ? `?${filtered.join("&")}` : "";
222
+ /**
223
+ * GET request
224
+ */
225
+ async get(endpoint, params) {
226
+ return this.request("GET", endpoint, { params });
227
+ }
228
+ /**
229
+ * POST request
230
+ */
231
+ async post(endpoint, body) {
232
+ return this.request("POST", endpoint, { body });
233
+ }
234
+ /**
235
+ * PUT request
236
+ */
237
+ async put(endpoint, body) {
238
+ return this.request("PUT", endpoint, { body });
239
+ }
240
+ /**
241
+ * PATCH request
242
+ */
243
+ async patch(endpoint, body) {
244
+ return this.request("PATCH", endpoint, { body });
245
+ }
246
+ /**
247
+ * DELETE request
248
+ */
249
+ async delete(endpoint) {
250
+ return this.request("DELETE", endpoint);
251
+ }
252
+ };
253
+ }
254
+ });
255
+
256
+ // src/core/http/index.ts
257
+ var init_http = __esm({
258
+ "src/core/http/index.ts"() {
259
+ "use strict";
260
+ init_HttpClient();
261
+ }
262
+ });
263
+
264
+ // src/core/auth/AuthStrategy.ts
265
+ var BearerTokenAuth, SessionAuth, NoAuth;
266
+ var init_AuthStrategy = __esm({
267
+ "src/core/auth/AuthStrategy.ts"() {
268
+ "use strict";
269
+ BearerTokenAuth = class {
270
+ constructor(token) {
271
+ this.token = token;
272
+ }
273
+ token;
274
+ injectAuth(headers) {
275
+ headers.set("Authorization", `Bearer ${this.token}`);
276
+ }
277
+ getDescription() {
278
+ return "Bearer token authentication";
279
+ }
280
+ };
281
+ SessionAuth = class {
282
+ constructor(sessionToken, onUnauthorized) {
283
+ this.sessionToken = sessionToken;
284
+ this.onUnauthorized = onUnauthorized;
285
+ }
286
+ sessionToken;
287
+ onUnauthorized;
288
+ injectAuth(headers) {
289
+ if (this.sessionToken) {
290
+ headers.set("Authorization", `Bearer ${this.sessionToken}`);
291
+ }
292
+ }
293
+ async handleUnauthorized() {
294
+ if (this.onUnauthorized) {
295
+ await this.onUnauthorized();
296
+ }
297
+ }
298
+ getDescription() {
299
+ return this.sessionToken ? "Session authentication (explicit token)" : "Session authentication (cookies)";
300
+ }
301
+ };
302
+ NoAuth = class {
303
+ injectAuth(_headers) {
304
+ }
305
+ getDescription() {
306
+ return "No authentication";
97
307
  }
98
308
  };
99
309
  }
100
310
  });
101
311
 
102
- // src/utils/cache.ts
312
+ // src/core/auth/index.ts
313
+ var init_auth = __esm({
314
+ "src/core/auth/index.ts"() {
315
+ "use strict";
316
+ init_AuthStrategy();
317
+ }
318
+ });
319
+
320
+ // src/core/pagination/PaginatedResponse.ts
321
+ function createPaginatedResponse(data, meta) {
322
+ return { data, meta };
323
+ }
324
+ function extractData(response) {
325
+ if (Array.isArray(response)) {
326
+ return response;
327
+ }
328
+ return response.data;
329
+ }
330
+ function isPaginatedResponse(response) {
331
+ return !Array.isArray(response) && "data" in response;
332
+ }
333
+ function pageToOffset(page, limit) {
334
+ return (page - 1) * limit;
335
+ }
336
+ function offsetToPage(offset, limit) {
337
+ return Math.floor(offset / limit) + 1;
338
+ }
339
+ var init_PaginatedResponse = __esm({
340
+ "src/core/pagination/PaginatedResponse.ts"() {
341
+ "use strict";
342
+ }
343
+ });
344
+
345
+ // src/core/pagination/index.ts
346
+ var init_pagination = __esm({
347
+ "src/core/pagination/index.ts"() {
348
+ "use strict";
349
+ init_PaginatedResponse();
350
+ }
351
+ });
352
+
353
+ // src/core/cache.ts
103
354
  var MarvinCache;
104
355
  var init_cache = __esm({
105
- "src/utils/cache.ts"() {
356
+ "src/core/cache.ts"() {
106
357
  "use strict";
107
358
  MarvinCache = class {
108
359
  constructor(duration = 5 * 60 * 1e3) {
@@ -139,6 +390,42 @@ var init_cache = __esm({
139
390
  }
140
391
  });
141
392
 
393
+ // src/core/index.ts
394
+ var init_core = __esm({
395
+ "src/core/index.ts"() {
396
+ "use strict";
397
+ init_http();
398
+ init_auth();
399
+ init_errors();
400
+ init_pagination();
401
+ init_cache();
402
+ }
403
+ });
404
+
405
+ // src/client/http.ts
406
+ var MarvinHttpClient;
407
+ var init_http2 = __esm({
408
+ "src/client/http.ts"() {
409
+ "use strict";
410
+ init_core();
411
+ MarvinHttpClient = class extends HttpClient {
412
+ constructor(config) {
413
+ super({
414
+ baseUrl: config.apiUrl,
415
+ auth: new BearerTokenAuth(config.siteClientToken),
416
+ credentials: "same-origin"
417
+ });
418
+ }
419
+ /**
420
+ * Fetch endpoint (backward compatibility)
421
+ */
422
+ async fetch(endpoint) {
423
+ return this.get(endpoint);
424
+ }
425
+ };
426
+ }
427
+ });
428
+
142
429
  // src/entries/entry.ts
143
430
  var Entry;
144
431
  var init_entry = __esm({
@@ -242,11 +529,12 @@ var init_entries = __esm({
242
529
  entry_type: options.entryType,
243
530
  collection: options.collection,
244
531
  limit: options.limit,
245
- offset: options.offset,
246
- status: options.status || "published"
532
+ offset: options.offset
533
+ // Don't send status - backend always returns published
247
534
  });
248
535
  const endpoint = `/api/publish/${this.workspaceSlug}/entries${queryString}`;
249
- return this.http.fetch(endpoint);
536
+ const response = await this.http.fetch(endpoint);
537
+ return response.data || [];
250
538
  }
251
539
  /**
252
540
  * Get a single entry by slug
@@ -330,8 +618,7 @@ var init_collection = __esm({
330
618
  * Get all entries in this collection
331
619
  */
332
620
  async entries() {
333
- const endpoint = `/api/publish/${this.workspaceSlug}/collections/${this.data.slug}/entries`;
334
- return this.http.fetch(endpoint);
621
+ return this.data.entries || [];
335
622
  }
336
623
  /**
337
624
  * Get assets in this collection (future)
@@ -373,7 +660,8 @@ var init_collections = __esm({
373
660
  */
374
661
  async list() {
375
662
  const endpoint = `/api/publish/${this.workspaceSlug}/collections`;
376
- return this.http.fetch(endpoint);
663
+ const response = await this.http.fetch(endpoint);
664
+ return response.data || [];
377
665
  }
378
666
  /**
379
667
  * Get a single collection by slug
@@ -416,7 +704,8 @@ var init_assets = __esm({
416
704
  offset: options.offset
417
705
  });
418
706
  const endpoint = `/api/publish/${this.workspaceSlug}/assets${queryString}`;
419
- return this.http.fetch(endpoint);
707
+ const response = await this.http.fetch(endpoint);
708
+ return response.data || [];
420
709
  }
421
710
  /**
422
711
  * Convenience: Get all images
@@ -531,7 +820,8 @@ var init_resources = __esm({
531
820
  offset: options.offset
532
821
  });
533
822
  const endpoint = `/api/publish/${this.workspaceSlug}/resources${queryString}`;
534
- return this.http.fetch(endpoint);
823
+ const response = await this.http.fetch(endpoint);
824
+ return response.data || [];
535
825
  }
536
826
  /**
537
827
  * Get a single resource by slug
@@ -624,8 +914,8 @@ var init_marvin = __esm({
624
914
  "src/client/marvin.ts"() {
625
915
  "use strict";
626
916
  init_config();
627
- init_http();
628
- init_cache();
917
+ init_http2();
918
+ init_core();
629
919
  init_workspace();
630
920
  MarvinClient = class {
631
921
  constructor(config) {
@@ -826,17 +1116,33 @@ var init_marvin = __esm({
826
1116
  // src/index.ts
827
1117
  var index_exports = {};
828
1118
  __export(index_exports, {
1119
+ BearerTokenAuth: () => BearerTokenAuth,
829
1120
  Collection: () => Collection,
830
1121
  Entry: () => Entry,
1122
+ HttpClient: () => HttpClient,
1123
+ MarvinApiError: () => MarvinApiError,
1124
+ MarvinAuthError: () => MarvinAuthError,
1125
+ MarvinCache: () => MarvinCache,
831
1126
  MarvinClient: () => MarvinClient,
1127
+ MarvinConfigError: () => MarvinConfigError,
1128
+ MarvinError: () => MarvinError,
1129
+ MarvinValidationError: () => MarvinValidationError,
1130
+ NoAuth: () => NoAuth,
832
1131
  Resource: () => Resource,
1132
+ SessionAuth: () => SessionAuth,
833
1133
  Workspace: () => Workspace,
834
1134
  createConfigFromEnv: () => createConfigFromEnv,
835
- createMarvinClient: () => createMarvinClient
1135
+ createMarvinClient: () => createMarvinClient,
1136
+ createPaginatedResponse: () => createPaginatedResponse,
1137
+ extractData: () => extractData,
1138
+ isPaginatedResponse: () => isPaginatedResponse,
1139
+ offsetToPage: () => offsetToPage,
1140
+ pageToOffset: () => pageToOffset
836
1141
  });
837
1142
  module.exports = __toCommonJS(index_exports);
838
1143
  init_marvin();
839
1144
  init_config();
1145
+ init_core();
840
1146
  init_workspace();
841
1147
  init_entry();
842
1148
  init_collection();
@@ -849,11 +1155,26 @@ function createMarvinClient(config) {
849
1155
  }
850
1156
  // Annotate the CommonJS export names for ESM import in node:
851
1157
  0 && (module.exports = {
1158
+ BearerTokenAuth,
852
1159
  Collection,
853
1160
  Entry,
1161
+ HttpClient,
1162
+ MarvinApiError,
1163
+ MarvinAuthError,
1164
+ MarvinCache,
854
1165
  MarvinClient,
1166
+ MarvinConfigError,
1167
+ MarvinError,
1168
+ MarvinValidationError,
1169
+ NoAuth,
855
1170
  Resource,
1171
+ SessionAuth,
856
1172
  Workspace,
857
1173
  createConfigFromEnv,
858
- createMarvinClient
1174
+ createMarvinClient,
1175
+ createPaginatedResponse,
1176
+ extractData,
1177
+ isPaginatedResponse,
1178
+ offsetToPage,
1179
+ pageToOffset
859
1180
  });
package/dist/index.d.cts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { MarvinResource, MarvinEntry, MarvinAsset, MarvinCollection, MarvinEntryType, MarvinSite, GetResourcesOptions as GetResourcesOptions$1 } from './types/index.cjs';
2
2
  export { GetAssetsOptions, GetEntriesOptions, MarvinPublishResponse } from './types/index.cjs';
3
+ import { H as HttpClient } from './HttpClient-CLtgQGXH.cjs';
4
+ export { A as AuthStrategy, B as BearerTokenAuth, a as HttpClientConfig, N as NoAuth, S as SessionAuth } from './HttpClient-CLtgQGXH.cjs';
3
5
 
4
6
  /**
5
7
  * Marvin SDK Configuration
@@ -16,14 +18,109 @@ interface MarvinConfig {
16
18
  declare function createConfigFromEnv(overrides?: Partial<MarvinConfig>): MarvinConfig;
17
19
 
18
20
  /**
19
- * HTTP Client for Marvin API
21
+ * Base error class for all Marvin SDK errors
20
22
  */
23
+ declare class MarvinError extends Error {
24
+ readonly code?: string | undefined;
25
+ readonly statusCode?: number | undefined;
26
+ constructor(message: string, code?: string | undefined, statusCode?: number | undefined);
27
+ }
28
+ /**
29
+ * HTTP/API request errors
30
+ */
31
+ declare class MarvinApiError extends MarvinError {
32
+ readonly statusCode: number;
33
+ readonly endpoint: string;
34
+ readonly responseBody?: string | undefined;
35
+ constructor(message: string, statusCode: number, endpoint: string, responseBody?: string | undefined);
36
+ static fromResponse(status: number, statusText: string, endpoint: string, body?: string): MarvinApiError;
37
+ }
38
+ /**
39
+ * Authentication/authorization errors
40
+ */
41
+ declare class MarvinAuthError extends MarvinError {
42
+ constructor(message: string, statusCode?: number);
43
+ }
44
+ /**
45
+ * Configuration errors
46
+ */
47
+ declare class MarvinConfigError extends MarvinError {
48
+ constructor(message: string);
49
+ }
50
+ /**
51
+ * Validation errors
52
+ */
53
+ declare class MarvinValidationError extends MarvinError {
54
+ readonly field?: string | undefined;
55
+ constructor(message: string, field?: string | undefined);
56
+ }
21
57
 
22
- declare class MarvinHttpClient {
23
- private config;
58
+ /**
59
+ * Pagination response types and helpers
60
+ */
61
+ interface PaginationMeta {
62
+ total?: number;
63
+ page?: number;
64
+ limit?: number;
65
+ offset?: number;
66
+ hasMore?: boolean;
67
+ }
68
+ interface PaginatedResponse<T> {
69
+ data: T[];
70
+ meta?: PaginationMeta;
71
+ }
72
+ interface PaginationParams {
73
+ limit?: number;
74
+ offset?: number;
75
+ page?: number;
76
+ }
77
+ /**
78
+ * Create a paginated response
79
+ */
80
+ declare function createPaginatedResponse<T>(data: T[], meta?: PaginationMeta): PaginatedResponse<T>;
81
+ /**
82
+ * Extract data from paginated response
83
+ */
84
+ declare function extractData<T>(response: PaginatedResponse<T> | T[]): T[];
85
+ /**
86
+ * Check if response is paginated
87
+ */
88
+ declare function isPaginatedResponse<T>(response: PaginatedResponse<T> | T[]): response is PaginatedResponse<T>;
89
+ /**
90
+ * Calculate pagination offset from page number
91
+ */
92
+ declare function pageToOffset(page: number, limit: number): number;
93
+ /**
94
+ * Calculate page number from offset
95
+ */
96
+ declare function offsetToPage(offset: number, limit: number): number;
97
+
98
+ /**
99
+ * Simple in-memory cache for SDK data
100
+ */
101
+ declare class MarvinCache {
102
+ private duration;
103
+ private cache;
104
+ constructor(duration?: number);
105
+ get<T>(key: string): T | undefined;
106
+ set<T>(key: string, data: T): void;
107
+ has(key: string): boolean;
108
+ clear(): void;
109
+ delete(key: string): void;
110
+ }
111
+
112
+ /**
113
+ * HTTP Client for Marvin Publishing API
114
+ *
115
+ * Wrapper around core HttpClient for Publishing API specifics
116
+ */
117
+
118
+ declare class MarvinHttpClient extends HttpClient {
24
119
  constructor(config: MarvinConfig);
120
+ /**
121
+ * Fetch endpoint (backward compatibility)
122
+ */
25
123
  fetch<T>(endpoint: string): Promise<T>;
26
- buildQueryString(params: Record<string, string | number | undefined>): string;
27
124
  }
28
125
 
29
126
  /**
@@ -427,4 +524,4 @@ declare class MarvinClient {
427
524
  */
428
525
  declare function createMarvinClient(config?: Partial<MarvinConfig>): MarvinClient;
429
526
 
430
- export { Collection, Entry, GetResourcesOptions$1 as GetResourcesOptions, MarvinAsset, MarvinClient, MarvinCollection, type MarvinConfig, MarvinEntry, MarvinEntryType, MarvinResource, MarvinSite, Resource, Workspace, createConfigFromEnv, createMarvinClient };
527
+ export { Collection, Entry, GetResourcesOptions$1 as GetResourcesOptions, HttpClient, MarvinApiError, MarvinAsset, MarvinAuthError, MarvinCache, MarvinClient, MarvinCollection, type MarvinConfig, MarvinConfigError, MarvinEntry, MarvinEntryType, MarvinError, MarvinResource, MarvinSite, MarvinValidationError, type PaginatedResponse, type PaginationMeta, type PaginationParams, Resource, Workspace, createConfigFromEnv, createMarvinClient, createPaginatedResponse, extractData, isPaginatedResponse, offsetToPage, pageToOffset };