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