@use-stall/core 0.0.1 → 0.0.2

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.mjs CHANGED
@@ -1,11 +1,1286 @@
1
- // src/lib/utils.ts
2
- var capitalize = (str) => {
3
- return str.charAt(0).toUpperCase() + str.slice(1);
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/utils/logger.ts
9
+ var Logger = class {
10
+ level;
11
+ context;
12
+ constructor(context = "Stall", level = "info") {
13
+ this.context = context;
14
+ this.level = level;
15
+ }
16
+ shouldLog(level) {
17
+ const levels = {
18
+ debug: 0,
19
+ info: 1,
20
+ warn: 2,
21
+ error: 3
22
+ };
23
+ return levels[level] >= levels[this.level];
24
+ }
25
+ format(level, message, data) {
26
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
27
+ const prefix = `[${timestamp}] [${this.context}] [${level.toUpperCase()}]`;
28
+ if (data) {
29
+ return `${prefix} ${message} ${JSON.stringify(data)}`;
30
+ }
31
+ return `${prefix} ${message}`;
32
+ }
33
+ debug(message, data) {
34
+ if (this.shouldLog("debug")) {
35
+ console.debug(this.format("debug", message, data));
36
+ }
37
+ }
38
+ info(message, data) {
39
+ if (this.shouldLog("info")) {
40
+ console.info(this.format("info", message, data));
41
+ }
42
+ }
43
+ warn(message, data) {
44
+ if (this.shouldLog("warn")) {
45
+ console.warn(this.format("warn", message, data));
46
+ }
47
+ }
48
+ error(message, error) {
49
+ if (this.shouldLog("error")) {
50
+ const data = error instanceof Error ? { message: error.message, stack: error.stack } : error;
51
+ console.error(this.format("error", message, data));
52
+ }
53
+ }
54
+ setLevel(level) {
55
+ this.level = level;
56
+ }
4
57
  };
5
- var toPureString = (str) => {
6
- return str.replace(/\s+/gi, "").toLowerCase();
58
+ function createLogger(context = "Stall", level = "info") {
59
+ return new Logger(context, level);
60
+ }
61
+
62
+ // src/utils/error-handler.ts
63
+ var StallConnectorError = class extends Error {
64
+ code;
65
+ details;
66
+ statusCode;
67
+ constructor(message, code, statusCode, details) {
68
+ super(message);
69
+ this.name = "StallConnectorError";
70
+ this.code = code;
71
+ this.statusCode = statusCode;
72
+ this.details = details;
73
+ }
74
+ };
75
+ var ErrorCodes = {
76
+ // Initialization errors
77
+ INVALID_CONFIGURATION: "INVALID_CONFIGURATION",
78
+ MISSING_CREDENTIALS: "MISSING_CREDENTIALS",
79
+ INVALID_CREDENTIALS: "INVALID_CREDENTIALS",
80
+ PLUGIN_LOAD_FAILED: "PLUGIN_LOAD_FAILED",
81
+ PLUGIN_NOT_FOUND: "PLUGIN_NOT_FOUND",
82
+ // Operation errors
83
+ OPERATION_FAILED: "OPERATION_FAILED",
84
+ OPERATION_TIMEOUT: "OPERATION_TIMEOUT",
85
+ OPERATION_NOT_SUPPORTED: "OPERATION_NOT_SUPPORTED",
86
+ // Network errors
87
+ NETWORK_ERROR: "NETWORK_ERROR",
88
+ REQUEST_FAILED: "REQUEST_FAILED",
89
+ RATE_LIMITED: "RATE_LIMITED",
90
+ // Validation errors
91
+ VALIDATION_FAILED: "VALIDATION_FAILED",
92
+ INVALID_DATA: "INVALID_DATA",
93
+ MISSING_REQUIRED_FIELD: "MISSING_REQUIRED_FIELD",
94
+ // Authentication errors
95
+ UNAUTHORIZED: "UNAUTHORIZED",
96
+ FORBIDDEN: "FORBIDDEN",
97
+ TOKEN_EXPIRED: "TOKEN_EXPIRED",
98
+ // Server errors
99
+ INTERNAL_ERROR: "INTERNAL_ERROR",
100
+ SERVICE_UNAVAILABLE: "SERVICE_UNAVAILABLE"
101
+ };
102
+ function handleConnectorError(error, context) {
103
+ if (error instanceof StallConnectorError) {
104
+ return error;
105
+ }
106
+ if (error?.response) {
107
+ const statusCode = error.response.status;
108
+ const data = error.response.data || error.response.body;
109
+ let code = ErrorCodes.REQUEST_FAILED;
110
+ if (statusCode === 401) code = ErrorCodes.UNAUTHORIZED;
111
+ else if (statusCode === 403) code = ErrorCodes.FORBIDDEN;
112
+ else if (statusCode === 429) code = ErrorCodes.RATE_LIMITED;
113
+ else if (statusCode >= 500) code = ErrorCodes.INTERNAL_ERROR;
114
+ return new StallConnectorError(
115
+ `${context}: ${data?.message || error.message}`,
116
+ code,
117
+ statusCode,
118
+ data
119
+ );
120
+ }
121
+ if (error?.code === "ECONNABORTED" || error?.message?.includes("timeout")) {
122
+ return new StallConnectorError(
123
+ `${context}: Operation timed out`,
124
+ ErrorCodes.OPERATION_TIMEOUT
125
+ );
126
+ }
127
+ if (error?.code === "ECONNREFUSED" || error?.code === "ENOTFOUND" || error?.message?.includes("network")) {
128
+ return new StallConnectorError(
129
+ `${context}: Network error - ${error.message}`,
130
+ ErrorCodes.NETWORK_ERROR
131
+ );
132
+ }
133
+ if (error?.name === "ValidationError") {
134
+ return new StallConnectorError(
135
+ `${context}: Validation failed - ${error.message}`,
136
+ ErrorCodes.VALIDATION_FAILED,
137
+ 400,
138
+ error.details
139
+ );
140
+ }
141
+ return new StallConnectorError(
142
+ `${context}: ${error.message || "Unknown error"}`,
143
+ ErrorCodes.OPERATION_FAILED,
144
+ void 0,
145
+ error
146
+ );
147
+ }
148
+ function createErrorResponse(error, operation, connectorId) {
149
+ const isStallError = error instanceof StallConnectorError;
150
+ return {
151
+ success: false,
152
+ error: {
153
+ code: isStallError ? error.code : ErrorCodes.OPERATION_FAILED,
154
+ message: error.message,
155
+ details: isStallError ? error.details : void 0
156
+ },
157
+ metadata: {
158
+ connector_id: connectorId,
159
+ operation,
160
+ timestamp: Date.now(),
161
+ request_id: generateRequestId()
162
+ }
163
+ };
164
+ }
165
+ function generateRequestId() {
166
+ return `req_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
167
+ }
168
+
169
+ // src/core/connector-loader.ts
170
+ var ConnectorLoader = class {
171
+ logger;
172
+ connectorUrl;
173
+ cache = /* @__PURE__ */ new Map();
174
+ cacheTtl;
175
+ constructor(options) {
176
+ this.logger = new Logger("ConnectorLoader");
177
+ this.connectorUrl = options.connectorUrl;
178
+ this.cacheTtl = options.cacheTtl || 60 * 60 * 1e3;
179
+ }
180
+ /**
181
+ * Load a connector plugin from public URL
182
+ * URL pattern: {connectorUrl}/{integrationId}/index.js
183
+ */
184
+ async loadConnector(integrationId) {
185
+ const cacheKey = integrationId;
186
+ if (this.cache.has(cacheKey)) {
187
+ this.logger.debug(`Loaded ${integrationId} from cache`);
188
+ return this.cache.get(cacheKey);
189
+ }
190
+ try {
191
+ this.logger.info(`Loading connector plugin: ${integrationId}`);
192
+ const url = this.buildConnectorUrl(integrationId);
193
+ this.logger.debug(`Fetching from: ${url}`);
194
+ const response = await fetch(url);
195
+ if (!response.ok) {
196
+ throw new Error(
197
+ `Failed to fetch connector: ${response.statusText} (${response.status})`
198
+ );
199
+ }
200
+ const code = await response.text();
201
+ const plugin = await this.instantiatePlugin(code, integrationId);
202
+ this.logger.info(`Successfully loaded ${integrationId}`);
203
+ this.cache.set(cacheKey, plugin);
204
+ return plugin;
205
+ } catch (error) {
206
+ this.logger.error(
207
+ `Failed to load connector ${integrationId}`,
208
+ error
209
+ );
210
+ throw new StallConnectorError(
211
+ `Failed to load connector plugin: ${integrationId}`,
212
+ ErrorCodes.PLUGIN_LOAD_FAILED,
213
+ void 0,
214
+ { originalError: error.message }
215
+ );
216
+ }
217
+ }
218
+ /**
219
+ * Instantiate a plugin from code
220
+ */
221
+ async instantiatePlugin(code, integrationId) {
222
+ try {
223
+ const moduleExports = {};
224
+ const module = { exports: moduleExports };
225
+ const fn = new Function(
226
+ "module",
227
+ "exports",
228
+ "require",
229
+ code + "\nreturn module.exports;"
230
+ );
231
+ const pluginModule = fn(
232
+ module,
233
+ moduleExports,
234
+ this.createRequireProxy()
235
+ );
236
+ const PluginClass = pluginModule.default || pluginModule.Plugin;
237
+ if (!PluginClass) {
238
+ throw new Error("Plugin module does not export a default or Plugin");
239
+ }
240
+ const instance = new PluginClass();
241
+ if (!this.validatePlugin(instance)) {
242
+ throw new Error("Plugin does not implement IConnectorPlugin interface");
243
+ }
244
+ return instance;
245
+ } catch (error) {
246
+ throw new StallConnectorError(
247
+ `Failed to instantiate plugin: ${integrationId}`,
248
+ ErrorCodes.PLUGIN_LOAD_FAILED,
249
+ void 0,
250
+ { originalError: error.message }
251
+ );
252
+ }
253
+ }
254
+ /**
255
+ * Validate that an object implements IConnectorPlugin
256
+ */
257
+ validatePlugin(plugin) {
258
+ const requiredMethods = ["initialize", "verify", "getSupportedModules"];
259
+ const requiredModules = ["orders", "products", "customers", "inventory"];
260
+ for (const method of requiredMethods) {
261
+ if (typeof plugin[method] !== "function") {
262
+ this.logger.warn(`Plugin missing method: ${method}`);
263
+ }
264
+ }
265
+ for (const module of requiredModules) {
266
+ if (typeof plugin[module] !== "object") {
267
+ this.logger.warn(`Plugin missing module: ${module}`);
268
+ }
269
+ }
270
+ return true;
271
+ }
272
+ /**
273
+ * Build the connector plugin URL
274
+ * Pattern: {connectorUrl}/{integrationId}/index.js
275
+ */
276
+ buildConnectorUrl(integrationId) {
277
+ const baseUrl = this.connectorUrl.endsWith("/") ? this.connectorUrl.slice(0, -1) : this.connectorUrl;
278
+ return `${baseUrl}/${integrationId}/index.js`;
279
+ }
280
+ /**
281
+ * Create a proxy for require() in loaded plugins
282
+ */
283
+ createRequireProxy() {
284
+ return (id) => {
285
+ if (id === "@use-stall/types") {
286
+ return __require("@use-stall/types");
287
+ }
288
+ if (id === "node:crypto" || id === "crypto") {
289
+ return __require("crypto");
290
+ }
291
+ this.logger.warn(`Attempted to require unsupported module: ${id}`);
292
+ return {};
293
+ };
294
+ }
295
+ /**
296
+ * Clear the cache
297
+ */
298
+ clearCache() {
299
+ this.cache.clear();
300
+ this.logger.info("Plugin cache cleared");
301
+ }
302
+ /**
303
+ * Remove a specific plugin from cache
304
+ */
305
+ removeCacheEntry(integrationId) {
306
+ this.cache.delete(integrationId);
307
+ this.logger.info(`Removed ${integrationId} from cache`);
308
+ }
309
+ };
310
+
311
+ // src/core/plugin-manager.ts
312
+ var PluginManager = class {
313
+ plugin = null;
314
+ logger;
315
+ connectorLoader = null;
316
+ initialized = false;
317
+ constructor(logLevel = "info") {
318
+ this.logger = new Logger("PluginManager", logLevel);
319
+ }
320
+ /**
321
+ * Initialize the plugin manager with a plugin
322
+ */
323
+ async initialize(plugin, options) {
324
+ try {
325
+ if (!plugin) {
326
+ const connectorUrl = options.options?.connectorUrl || "https://connectors.myapp.xyz";
327
+ this.connectorLoader = new ConnectorLoader({
328
+ connectorUrl,
329
+ cache: options.options?.cachePlugins !== false
330
+ });
331
+ plugin = await this.connectorLoader.loadConnector(
332
+ options.integration_id
333
+ );
334
+ }
335
+ if (!plugin) {
336
+ throw new StallConnectorError(
337
+ "Failed to load connector plugin",
338
+ ErrorCodes.PLUGIN_NOT_FOUND
339
+ );
340
+ }
341
+ this.plugin = plugin;
342
+ const pluginConfig = {
343
+ connectorId: options.id,
344
+ integrationId: options.integration_id,
345
+ organizationId: options.id,
346
+ // Use the config id as org context
347
+ configuration: options.configuration,
348
+ options: options.options
349
+ };
350
+ await this.plugin.initialize(pluginConfig);
351
+ this.logger.info(
352
+ `Plugin initialized: ${this.plugin.metadata.name}@${this.plugin.metadata.version}`
353
+ );
354
+ this.initialized = true;
355
+ } catch (error) {
356
+ this.logger.error("Failed to initialize plugin manager", error);
357
+ throw error;
358
+ }
359
+ }
360
+ /**
361
+ * Get the loaded plugin
362
+ */
363
+ getPlugin() {
364
+ if (!this.plugin) {
365
+ throw new StallConnectorError(
366
+ "Plugin not initialized",
367
+ ErrorCodes.INVALID_CONFIGURATION
368
+ );
369
+ }
370
+ return this.plugin;
371
+ }
372
+ /**
373
+ * Check if plugin is initialized
374
+ */
375
+ isInitialized() {
376
+ return this.initialized && this.plugin !== null;
377
+ }
378
+ /**
379
+ * Verify the plugin configuration
380
+ */
381
+ async verify() {
382
+ if (!this.isInitialized()) {
383
+ throw new StallConnectorError(
384
+ "Plugin not initialized",
385
+ ErrorCodes.INVALID_CONFIGURATION
386
+ );
387
+ }
388
+ const result = await this.plugin.verify();
389
+ return result.success && result.data === true;
390
+ }
391
+ /**
392
+ * Get metadata about the loaded plugin
393
+ */
394
+ getMetadata() {
395
+ if (!this.plugin) {
396
+ throw new StallConnectorError(
397
+ "Plugin not initialized",
398
+ ErrorCodes.INVALID_CONFIGURATION
399
+ );
400
+ }
401
+ return {
402
+ id: this.plugin.metadata.id,
403
+ name: this.plugin.metadata.name,
404
+ version: this.plugin.metadata.version,
405
+ description: this.plugin.metadata.description,
406
+ supportedModules: this.plugin.getSupportedModules()
407
+ };
408
+ }
409
+ /**
410
+ * Check if a module is supported
411
+ */
412
+ isModuleSupported(module) {
413
+ if (!this.plugin) return false;
414
+ return this.plugin.getSupportedModules().includes(module);
415
+ }
416
+ /**
417
+ * Clear plugin cache (if using remote loading)
418
+ */
419
+ clearCache() {
420
+ if (this.connectorLoader) {
421
+ this.connectorLoader.clearCache();
422
+ this.logger.info("Plugin cache cleared");
423
+ }
424
+ }
425
+ };
426
+
427
+ // src/services/base.service.ts
428
+ var BaseService = class {
429
+ logger;
430
+ pluginManager;
431
+ connectorId;
432
+ constructor(pluginManager, connectorId, serviceName) {
433
+ this.pluginManager = pluginManager;
434
+ this.connectorId = connectorId;
435
+ this.logger = new Logger(serviceName);
436
+ }
437
+ /**
438
+ * Wrap a connector operation with error handling and logging
439
+ */
440
+ async executeOperation(operation, fn) {
441
+ const requestId = generateRequestId();
442
+ const startTime = Date.now();
443
+ try {
444
+ this.logger.debug(`[${requestId}] Starting operation: ${operation}`);
445
+ const result = await fn();
446
+ const duration = Date.now() - startTime;
447
+ this.logger.info(
448
+ `[${requestId}] Operation completed: ${operation} (${duration}ms)`,
449
+ { success: result.success }
450
+ );
451
+ return {
452
+ ...result,
453
+ metadata: {
454
+ ...result.metadata,
455
+ connector_id: this.connectorId,
456
+ operation,
457
+ timestamp: Date.now(),
458
+ request_id: requestId
459
+ }
460
+ };
461
+ } catch (error) {
462
+ const duration = Date.now() - startTime;
463
+ this.logger.error(
464
+ `[${requestId}] Operation failed: ${operation} (${duration}ms)`,
465
+ error
466
+ );
467
+ return createErrorResponse(
468
+ error,
469
+ operation,
470
+ this.connectorId
471
+ );
472
+ }
473
+ }
474
+ /**
475
+ * Check if a module is supported
476
+ */
477
+ isModuleSupported(module) {
478
+ return this.pluginManager.isModuleSupported(module);
479
+ }
480
+ /**
481
+ * Get the plugin instance
482
+ */
483
+ getPlugin() {
484
+ return this.pluginManager.getPlugin();
485
+ }
486
+ /**
487
+ * Verify plugin is ready
488
+ */
489
+ ensurePluginInitialized() {
490
+ if (!this.pluginManager.isInitialized()) {
491
+ throw new StallConnectorError(
492
+ "Plugin not initialized",
493
+ ErrorCodes.INVALID_CONFIGURATION
494
+ );
495
+ }
496
+ }
497
+ };
498
+
499
+ // src/services/orders.service.ts
500
+ var OrdersService = class extends BaseService {
501
+ constructor(pluginManager, connectorId) {
502
+ super(pluginManager, connectorId, "OrdersService");
503
+ }
504
+ /**
505
+ * Create a new order
506
+ */
507
+ async createOrder(order) {
508
+ return this.executeOperation("orders.create", async () => {
509
+ this.ensurePluginInitialized();
510
+ if (!this.isModuleSupported("orders")) {
511
+ return {
512
+ success: false,
513
+ error: {
514
+ code: "MODULE_NOT_SUPPORTED",
515
+ message: "Orders module is not supported by this connector"
516
+ }
517
+ };
518
+ }
519
+ const plugin = this.getPlugin();
520
+ return plugin.orders.create(order);
521
+ });
522
+ }
523
+ /**
524
+ * Get an order by ID
525
+ */
526
+ async getOrder(orderId) {
527
+ return this.executeOperation("orders.get", async () => {
528
+ this.ensurePluginInitialized();
529
+ if (!this.isModuleSupported("orders")) {
530
+ return {
531
+ success: false,
532
+ error: {
533
+ code: "MODULE_NOT_SUPPORTED",
534
+ message: "Orders module is not supported by this connector"
535
+ }
536
+ };
537
+ }
538
+ const plugin = this.getPlugin();
539
+ return plugin.orders.get(orderId);
540
+ });
541
+ }
542
+ /**
543
+ * Update an existing order
544
+ */
545
+ async updateOrder(orderId, order) {
546
+ return this.executeOperation("orders.update", async () => {
547
+ this.ensurePluginInitialized();
548
+ if (!this.isModuleSupported("orders")) {
549
+ return {
550
+ success: false,
551
+ error: {
552
+ code: "MODULE_NOT_SUPPORTED",
553
+ message: "Orders module is not supported by this connector"
554
+ }
555
+ };
556
+ }
557
+ const plugin = this.getPlugin();
558
+ return plugin.orders.update(orderId, order);
559
+ });
560
+ }
561
+ /**
562
+ * Delete/cancel an order
563
+ */
564
+ async deleteOrder(orderId) {
565
+ return this.executeOperation("orders.delete", async () => {
566
+ this.ensurePluginInitialized();
567
+ if (!this.isModuleSupported("orders")) {
568
+ return {
569
+ success: false,
570
+ error: {
571
+ code: "MODULE_NOT_SUPPORTED",
572
+ message: "Orders module is not supported by this connector"
573
+ }
574
+ };
575
+ }
576
+ const plugin = this.getPlugin();
577
+ return plugin.orders.delete(orderId);
578
+ });
579
+ }
580
+ /**
581
+ * List orders with optional filtering
582
+ */
583
+ async listOrders(options) {
584
+ return this.executeOperation("orders.list", async () => {
585
+ this.ensurePluginInitialized();
586
+ if (!this.isModuleSupported("orders")) {
587
+ return {
588
+ success: false,
589
+ error: {
590
+ code: "MODULE_NOT_SUPPORTED",
591
+ message: "Orders module is not supported by this connector"
592
+ }
593
+ };
594
+ }
595
+ const plugin = this.getPlugin();
596
+ return plugin.orders.list(options);
597
+ });
598
+ }
599
+ /**
600
+ * Create a refund for an order
601
+ */
602
+ async createRefund(orderId, refund) {
603
+ return this.executeOperation("refunds.create", async () => {
604
+ this.ensurePluginInitialized();
605
+ if (!this.isModuleSupported("orders")) {
606
+ return {
607
+ success: false,
608
+ error: {
609
+ code: "MODULE_NOT_SUPPORTED",
610
+ message: "Orders module is not supported by this connector"
611
+ }
612
+ };
613
+ }
614
+ const plugin = this.getPlugin();
615
+ return plugin.refunds.create(orderId, refund);
616
+ });
617
+ }
618
+ /**
619
+ * Get a refund by ID
620
+ */
621
+ async getRefund(refundId) {
622
+ return this.executeOperation("refunds.get", async () => {
623
+ this.ensurePluginInitialized();
624
+ if (!this.isModuleSupported("orders")) {
625
+ return {
626
+ success: false,
627
+ error: {
628
+ code: "MODULE_NOT_SUPPORTED",
629
+ message: "Orders module is not supported by this connector"
630
+ }
631
+ };
632
+ }
633
+ const plugin = this.getPlugin();
634
+ return plugin.refunds.get(refundId);
635
+ });
636
+ }
637
+ /**
638
+ * List refunds for an order
639
+ */
640
+ async listRefunds(orderId, options) {
641
+ return this.executeOperation("refunds.list", async () => {
642
+ this.ensurePluginInitialized();
643
+ if (!this.isModuleSupported("orders")) {
644
+ return {
645
+ success: false,
646
+ error: {
647
+ code: "MODULE_NOT_SUPPORTED",
648
+ message: "Orders module is not supported by this connector"
649
+ }
650
+ };
651
+ }
652
+ const plugin = this.getPlugin();
653
+ return plugin.refunds.list(orderId, options);
654
+ });
655
+ }
656
+ };
657
+
658
+ // src/services/products.service.ts
659
+ var ProductsService = class extends BaseService {
660
+ constructor(pluginManager, connectorId) {
661
+ super(pluginManager, connectorId, "ProductsService");
662
+ }
663
+ /**
664
+ * Create a new product
665
+ */
666
+ async createProduct(product) {
667
+ return this.executeOperation("products.create", async () => {
668
+ this.ensurePluginInitialized();
669
+ if (!this.isModuleSupported("products")) {
670
+ return {
671
+ success: false,
672
+ error: {
673
+ code: "MODULE_NOT_SUPPORTED",
674
+ message: "Products module is not supported by this connector"
675
+ }
676
+ };
677
+ }
678
+ const plugin = this.getPlugin();
679
+ return plugin.products.create(product);
680
+ });
681
+ }
682
+ /**
683
+ * Get a product by ID
684
+ */
685
+ async getProduct(productId) {
686
+ return this.executeOperation("products.get", async () => {
687
+ this.ensurePluginInitialized();
688
+ if (!this.isModuleSupported("products")) {
689
+ return {
690
+ success: false,
691
+ error: {
692
+ code: "MODULE_NOT_SUPPORTED",
693
+ message: "Products module is not supported by this connector"
694
+ }
695
+ };
696
+ }
697
+ const plugin = this.getPlugin();
698
+ return plugin.products.get(productId);
699
+ });
700
+ }
701
+ /**
702
+ * Update an existing product
703
+ */
704
+ async updateProduct(productId, product) {
705
+ return this.executeOperation("products.update", async () => {
706
+ this.ensurePluginInitialized();
707
+ if (!this.isModuleSupported("products")) {
708
+ return {
709
+ success: false,
710
+ error: {
711
+ code: "MODULE_NOT_SUPPORTED",
712
+ message: "Products module is not supported by this connector"
713
+ }
714
+ };
715
+ }
716
+ const plugin = this.getPlugin();
717
+ return plugin.products.update(productId, product);
718
+ });
719
+ }
720
+ /**
721
+ * Delete a product
722
+ */
723
+ async deleteProduct(productId) {
724
+ return this.executeOperation("products.delete", async () => {
725
+ this.ensurePluginInitialized();
726
+ if (!this.isModuleSupported("products")) {
727
+ return {
728
+ success: false,
729
+ error: {
730
+ code: "MODULE_NOT_SUPPORTED",
731
+ message: "Products module is not supported by this connector"
732
+ }
733
+ };
734
+ }
735
+ const plugin = this.getPlugin();
736
+ return plugin.products.delete(productId);
737
+ });
738
+ }
739
+ /**
740
+ * List products with optional filtering
741
+ */
742
+ async listProducts(options) {
743
+ return this.executeOperation("products.list", async () => {
744
+ this.ensurePluginInitialized();
745
+ if (!this.isModuleSupported("products")) {
746
+ return {
747
+ success: false,
748
+ error: {
749
+ code: "MODULE_NOT_SUPPORTED",
750
+ message: "Products module is not supported by this connector"
751
+ }
752
+ };
753
+ }
754
+ const plugin = this.getPlugin();
755
+ return plugin.products.list(options);
756
+ });
757
+ }
758
+ };
759
+
760
+ // src/core/sdk.ts
761
+ var StallCore = class _StallCore {
762
+ pluginManager;
763
+ logger;
764
+ options;
765
+ // Service instances
766
+ orders;
767
+ products;
768
+ constructor(options) {
769
+ this.options = options;
770
+ const logLevel = options.options?.logLevel || "info";
771
+ this.logger = new Logger("StallSDK", logLevel);
772
+ this.pluginManager = new PluginManager(logLevel);
773
+ this.orders = new OrdersService(this.pluginManager, options.id);
774
+ this.products = new ProductsService(this.pluginManager, options.id);
775
+ }
776
+ /**
777
+ * Firebase-style initialization with automatic plugin loading
778
+ * Loads connector from public URL: {connectorUrl}/{integration_id}/index.js
779
+ * Default URL: https://connectors.myapp.xyz
780
+ */
781
+ static async initialize(options) {
782
+ const sdk = new _StallCore(options);
783
+ try {
784
+ await sdk.pluginManager.initialize(null, options);
785
+ sdk.logger.info("StallSDK initialized successfully");
786
+ } catch (error) {
787
+ sdk.logger.error("Failed to initialize StallSDK", error);
788
+ throw error;
789
+ }
790
+ return sdk;
791
+ }
792
+ /**
793
+ * Initialize with a provided plugin instance (for testing/custom plugins)
794
+ * Useful for mock plugins or custom implementations
795
+ */
796
+ static async initializeWithPlugin(options, plugin) {
797
+ const sdk = new _StallCore(options);
798
+ try {
799
+ await sdk.pluginManager.initialize(plugin, options);
800
+ sdk.logger.info("StallSDK initialized with custom plugin");
801
+ } catch (error) {
802
+ sdk.logger.error("Failed to initialize StallSDK", error);
803
+ throw error;
804
+ }
805
+ return sdk;
806
+ }
807
+ /**
808
+ * Get plugin metadata
809
+ */
810
+ getMetadata() {
811
+ return this.pluginManager.getMetadata();
812
+ }
813
+ /**
814
+ * Verify the connector configuration
815
+ */
816
+ async verify() {
817
+ return this.pluginManager.verify();
818
+ }
819
+ /**
820
+ * Check if a module is supported
821
+ */
822
+ isModuleSupported(module) {
823
+ return this.pluginManager.isModuleSupported(module);
824
+ }
825
+ /**
826
+ * Check if SDK is initialized
827
+ */
828
+ isInitialized() {
829
+ return this.pluginManager.isInitialized();
830
+ }
831
+ /**
832
+ * Clear plugin cache
833
+ */
834
+ clearCache() {
835
+ this.pluginManager.clearCache();
836
+ }
837
+ /**
838
+ * Get the underlying plugin instance
839
+ */
840
+ getPlugin() {
841
+ return this.pluginManager.getPlugin();
842
+ }
843
+ /**
844
+ * Get SDK configuration
845
+ */
846
+ getConfiguration() {
847
+ return {
848
+ id: this.options.id,
849
+ integration_id: this.options.integration_id,
850
+ created_at: this.options.created_at,
851
+ updated_at: this.options.updated_at
852
+ };
853
+ }
854
+ };
855
+
856
+ // src/plugins/mock-connector.plugin.ts
857
+ var MockConnectorPlugin = class {
858
+ metadata = {
859
+ id: "mock",
860
+ name: "Mock Connector",
861
+ version: "1.0.0",
862
+ description: "Mock connector for development and testing purposes"
863
+ };
864
+ config = null;
865
+ initialized = false;
866
+ async initialize(config) {
867
+ this.config = config;
868
+ this.initialized = true;
869
+ console.log(`Mock connector initialized for: ${config.integrationId}`);
870
+ }
871
+ async verify() {
872
+ if (!this.initialized) {
873
+ return {
874
+ success: false,
875
+ error: {
876
+ code: "NOT_INITIALIZED",
877
+ message: "Plugin not initialized"
878
+ }
879
+ };
880
+ }
881
+ return {
882
+ success: true,
883
+ data: true,
884
+ metadata: {
885
+ connector_id: this.config?.connectorId || "mock",
886
+ operation: "verify",
887
+ timestamp: Date.now(),
888
+ request_id: `req_${Date.now()}`
889
+ }
890
+ };
891
+ }
892
+ getSupportedModules() {
893
+ return ["orders", "products", "customers", "inventory", "refunds", "payments"];
894
+ }
895
+ orders = {
896
+ create: async (order) => ({
897
+ success: true,
898
+ data: {
899
+ ...order,
900
+ id: `mock_order_${Date.now()}`,
901
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
902
+ },
903
+ metadata: {
904
+ connector_id: this.config?.connectorId || "mock",
905
+ operation: "orders.create",
906
+ timestamp: Date.now(),
907
+ request_id: `req_${Date.now()}`
908
+ }
909
+ }),
910
+ get: async (orderId) => ({
911
+ success: true,
912
+ data: {
913
+ id: orderId,
914
+ order_number: `ORD-${orderId}`,
915
+ status: "completed",
916
+ total: 100
917
+ },
918
+ metadata: {
919
+ connector_id: this.config?.connectorId || "mock",
920
+ operation: "orders.get",
921
+ timestamp: Date.now(),
922
+ request_id: `req_${Date.now()}`
923
+ }
924
+ }),
925
+ update: async (orderId, order) => ({
926
+ success: true,
927
+ data: {
928
+ id: orderId,
929
+ ...order,
930
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
931
+ },
932
+ metadata: {
933
+ connector_id: this.config?.connectorId || "mock",
934
+ operation: "orders.update",
935
+ timestamp: Date.now(),
936
+ request_id: `req_${Date.now()}`
937
+ }
938
+ }),
939
+ delete: async (orderId) => ({
940
+ success: true,
941
+ data: null,
942
+ metadata: {
943
+ connector_id: this.config?.connectorId || "mock",
944
+ operation: "orders.delete",
945
+ timestamp: Date.now(),
946
+ request_id: `req_${Date.now()}`
947
+ }
948
+ }),
949
+ list: async (options) => {
950
+ const limit = options?.limit || 10;
951
+ const orders = Array.from({ length: limit }, (_, i) => ({
952
+ id: `mock_order_${i}`,
953
+ order_number: `ORD-${i}`,
954
+ status: "completed",
955
+ total: (i + 1) * 100
956
+ }));
957
+ return {
958
+ success: true,
959
+ data: orders,
960
+ metadata: {
961
+ connector_id: this.config?.connectorId || "mock",
962
+ operation: "orders.list",
963
+ timestamp: Date.now(),
964
+ request_id: `req_${Date.now()}`
965
+ }
966
+ };
967
+ }
968
+ };
969
+ products = {
970
+ create: async (product) => ({
971
+ success: true,
972
+ data: {
973
+ ...product,
974
+ id: `mock_product_${Date.now()}`,
975
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
976
+ },
977
+ metadata: {
978
+ connector_id: this.config?.connectorId || "mock",
979
+ operation: "products.create",
980
+ timestamp: Date.now(),
981
+ request_id: `req_${Date.now()}`
982
+ }
983
+ }),
984
+ get: async (productId) => ({
985
+ success: true,
986
+ data: {
987
+ id: productId,
988
+ title: "Mock Product",
989
+ price: 99.99,
990
+ sku: `MOCK-${productId}`
991
+ },
992
+ metadata: {
993
+ connector_id: this.config?.connectorId || "mock",
994
+ operation: "products.get",
995
+ timestamp: Date.now(),
996
+ request_id: `req_${Date.now()}`
997
+ }
998
+ }),
999
+ update: async (productId, product) => ({
1000
+ success: true,
1001
+ data: {
1002
+ id: productId,
1003
+ ...product,
1004
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1005
+ },
1006
+ metadata: {
1007
+ connector_id: this.config?.connectorId || "mock",
1008
+ operation: "products.update",
1009
+ timestamp: Date.now(),
1010
+ request_id: `req_${Date.now()}`
1011
+ }
1012
+ }),
1013
+ delete: async (productId) => ({
1014
+ success: true,
1015
+ data: null,
1016
+ metadata: {
1017
+ connector_id: this.config?.connectorId || "mock",
1018
+ operation: "products.delete",
1019
+ timestamp: Date.now(),
1020
+ request_id: `req_${Date.now()}`
1021
+ }
1022
+ }),
1023
+ list: async (options) => {
1024
+ const limit = options?.limit || 10;
1025
+ const products = Array.from({ length: limit }, (_, i) => ({
1026
+ id: `mock_product_${i}`,
1027
+ title: `Mock Product ${i}`,
1028
+ price: (i + 1) * 10,
1029
+ sku: `MOCK-${i}`
1030
+ }));
1031
+ return {
1032
+ success: true,
1033
+ data: products,
1034
+ metadata: {
1035
+ connector_id: this.config?.connectorId || "mock",
1036
+ operation: "products.list",
1037
+ timestamp: Date.now(),
1038
+ request_id: `req_${Date.now()}`
1039
+ }
1040
+ };
1041
+ }
1042
+ };
1043
+ customers = {
1044
+ create: async (customer) => ({
1045
+ success: true,
1046
+ data: {
1047
+ ...customer,
1048
+ id: `mock_customer_${Date.now()}`,
1049
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
1050
+ },
1051
+ metadata: {
1052
+ connector_id: this.config?.connectorId || "mock",
1053
+ operation: "customers.create",
1054
+ timestamp: Date.now(),
1055
+ request_id: `req_${Date.now()}`
1056
+ }
1057
+ }),
1058
+ get: async (customerId) => ({
1059
+ success: true,
1060
+ data: {
1061
+ id: customerId,
1062
+ email: "customer@example.com",
1063
+ name: "Mock Customer"
1064
+ },
1065
+ metadata: {
1066
+ connector_id: this.config?.connectorId || "mock",
1067
+ operation: "customers.get",
1068
+ timestamp: Date.now(),
1069
+ request_id: `req_${Date.now()}`
1070
+ }
1071
+ }),
1072
+ update: async (customerId, customer) => ({
1073
+ success: true,
1074
+ data: {
1075
+ id: customerId,
1076
+ ...customer,
1077
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1078
+ },
1079
+ metadata: {
1080
+ connector_id: this.config?.connectorId || "mock",
1081
+ operation: "customers.update",
1082
+ timestamp: Date.now(),
1083
+ request_id: `req_${Date.now()}`
1084
+ }
1085
+ }),
1086
+ delete: async (customerId) => ({
1087
+ success: true,
1088
+ data: null,
1089
+ metadata: {
1090
+ connector_id: this.config?.connectorId || "mock",
1091
+ operation: "customers.delete",
1092
+ timestamp: Date.now(),
1093
+ request_id: `req_${Date.now()}`
1094
+ }
1095
+ }),
1096
+ list: async (options) => {
1097
+ const limit = options?.limit || 10;
1098
+ const customers = Array.from({ length: limit }, (_, i) => ({
1099
+ id: `mock_customer_${i}`,
1100
+ email: `customer${i}@example.com`,
1101
+ name: `Customer ${i}`
1102
+ }));
1103
+ return {
1104
+ success: true,
1105
+ data: customers,
1106
+ metadata: {
1107
+ connector_id: this.config?.connectorId || "mock",
1108
+ operation: "customers.list",
1109
+ timestamp: Date.now(),
1110
+ request_id: `req_${Date.now()}`
1111
+ }
1112
+ };
1113
+ }
1114
+ };
1115
+ inventory = {
1116
+ get: async (productId, variantId) => ({
1117
+ success: true,
1118
+ data: {
1119
+ product_id: productId,
1120
+ variant_id: variantId,
1121
+ in_stock: 100,
1122
+ committed: 0,
1123
+ on_hand: 100
1124
+ },
1125
+ metadata: {
1126
+ connector_id: this.config?.connectorId || "mock",
1127
+ operation: "inventory.get",
1128
+ timestamp: Date.now(),
1129
+ request_id: `req_${Date.now()}`
1130
+ }
1131
+ }),
1132
+ update: async (productId, inventory, variantId) => ({
1133
+ success: true,
1134
+ data: {
1135
+ product_id: productId,
1136
+ variant_id: variantId,
1137
+ ...inventory,
1138
+ updated_at: (/* @__PURE__ */ new Date()).toISOString()
1139
+ },
1140
+ metadata: {
1141
+ connector_id: this.config?.connectorId || "mock",
1142
+ operation: "inventory.update",
1143
+ timestamp: Date.now(),
1144
+ request_id: `req_${Date.now()}`
1145
+ }
1146
+ }),
1147
+ list: async (options) => {
1148
+ const limit = options?.limit || 10;
1149
+ const inventoryItems = Array.from({ length: limit }, (_, i) => ({
1150
+ product_id: `mock_product_${i}`,
1151
+ in_stock: (i + 1) * 10,
1152
+ committed: 0,
1153
+ on_hand: (i + 1) * 10
1154
+ }));
1155
+ return {
1156
+ success: true,
1157
+ data: inventoryItems,
1158
+ metadata: {
1159
+ connector_id: this.config?.connectorId || "mock",
1160
+ operation: "inventory.list",
1161
+ timestamp: Date.now(),
1162
+ request_id: `req_${Date.now()}`
1163
+ }
1164
+ };
1165
+ }
1166
+ };
1167
+ refunds = {
1168
+ create: async (orderId, refund) => ({
1169
+ success: true,
1170
+ data: {
1171
+ ...refund,
1172
+ id: `mock_refund_${Date.now()}`,
1173
+ order_id: orderId,
1174
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
1175
+ },
1176
+ metadata: {
1177
+ connector_id: this.config?.connectorId || "mock",
1178
+ operation: "refunds.create",
1179
+ timestamp: Date.now(),
1180
+ request_id: `req_${Date.now()}`
1181
+ }
1182
+ }),
1183
+ get: async (refundId) => ({
1184
+ success: true,
1185
+ data: {
1186
+ id: refundId,
1187
+ amount: 100,
1188
+ reason: "Customer request",
1189
+ status: "completed"
1190
+ },
1191
+ metadata: {
1192
+ connector_id: this.config?.connectorId || "mock",
1193
+ operation: "refunds.get",
1194
+ timestamp: Date.now(),
1195
+ request_id: `req_${Date.now()}`
1196
+ }
1197
+ }),
1198
+ list: async (orderId, options) => {
1199
+ const limit = options?.limit || 10;
1200
+ const refunds = Array.from({ length: limit }, (_, i) => ({
1201
+ id: `mock_refund_${i}`,
1202
+ order_id: orderId,
1203
+ amount: (i + 1) * 10,
1204
+ reason: "Customer request",
1205
+ status: "completed"
1206
+ }));
1207
+ return {
1208
+ success: true,
1209
+ data: refunds,
1210
+ metadata: {
1211
+ connector_id: this.config?.connectorId || "mock",
1212
+ operation: "refunds.list",
1213
+ timestamp: Date.now(),
1214
+ request_id: `req_${Date.now()}`
1215
+ }
1216
+ };
1217
+ }
1218
+ };
1219
+ payments = {
1220
+ create: async (payment) => ({
1221
+ success: true,
1222
+ data: {
1223
+ ...payment,
1224
+ id: `mock_payment_${Date.now()}`,
1225
+ status: "successful",
1226
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
1227
+ },
1228
+ metadata: {
1229
+ connector_id: this.config?.connectorId || "mock",
1230
+ operation: "payments.create",
1231
+ timestamp: Date.now(),
1232
+ request_id: `req_${Date.now()}`
1233
+ }
1234
+ }),
1235
+ get: async (paymentId) => ({
1236
+ success: true,
1237
+ data: {
1238
+ id: paymentId,
1239
+ amount: 100,
1240
+ status: "successful",
1241
+ method: "card"
1242
+ },
1243
+ metadata: {
1244
+ connector_id: this.config?.connectorId || "mock",
1245
+ operation: "payments.get",
1246
+ timestamp: Date.now(),
1247
+ request_id: `req_${Date.now()}`
1248
+ }
1249
+ }),
1250
+ list: async (options) => {
1251
+ const limit = options?.limit || 10;
1252
+ const payments = Array.from({ length: limit }, (_, i) => ({
1253
+ id: `mock_payment_${i}`,
1254
+ amount: (i + 1) * 100,
1255
+ status: "successful",
1256
+ method: "card"
1257
+ }));
1258
+ return {
1259
+ success: true,
1260
+ data: payments,
1261
+ metadata: {
1262
+ connector_id: this.config?.connectorId || "mock",
1263
+ operation: "payments.list",
1264
+ timestamp: Date.now(),
1265
+ request_id: `req_${Date.now()}`
1266
+ }
1267
+ };
1268
+ }
1269
+ };
7
1270
  };
8
1271
  export {
9
- capitalize,
10
- toPureString
1272
+ BaseService,
1273
+ ConnectorLoader,
1274
+ ErrorCodes,
1275
+ Logger,
1276
+ MockConnectorPlugin,
1277
+ OrdersService,
1278
+ PluginManager,
1279
+ ProductsService,
1280
+ StallConnectorError,
1281
+ StallCore,
1282
+ createErrorResponse,
1283
+ createLogger,
1284
+ generateRequestId,
1285
+ handleConnectorError
11
1286
  };