@slotchain/sdk 1.1.3 → 1.2.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.js CHANGED
@@ -365,10 +365,79 @@ var ServiceClient = class {
365
365
  );
366
366
  return response.data;
367
367
  }
368
+ /**
369
+ * Get services for a tenant (full list with optional filters)
370
+ * GET /api/v1/services
371
+ *
372
+ * @param options - Query options
373
+ * @param options.tenantId - Tenant ID (UUID) - required if tenantSlug not provided
374
+ * @param options.tenantSlug - Tenant slug - required if tenantId not provided
375
+ * @param options.slotId - Slot ID (DEPRECATED - accepted but ignored for API compatibility)
376
+ * @param options.isActive - Filter by active status
377
+ * @param options.includeItems - Include nested service items (default: false)
378
+ * @param options.page - Page number (default: 1)
379
+ * @param options.limit - Items per page (default: 20, max: 100)
380
+ * @returns Services with optional pagination metadata
381
+ */
382
+ async getServicesByTenant(options) {
383
+ if (!options.tenantId && !options.tenantSlug) {
384
+ return {
385
+ success: false,
386
+ error: {
387
+ code: "MISSING_TENANT_ID",
388
+ message: "tenantId or tenantSlug is required"
389
+ }
390
+ };
391
+ }
392
+ const params = {};
393
+ if (options.tenantId) {
394
+ params.tenant_id = options.tenantId;
395
+ }
396
+ if (options.tenantSlug) {
397
+ params.tenantSlug = options.tenantSlug;
398
+ }
399
+ if (options.slotId) {
400
+ params.slot_id = options.slotId;
401
+ }
402
+ if (options.isActive !== void 0) {
403
+ params.is_active = options.isActive;
404
+ }
405
+ if (options.includeItems) {
406
+ params.includeItems = true;
407
+ }
408
+ if (options.page !== void 0) {
409
+ params.page = options.page;
410
+ }
411
+ if (options.limit !== void 0) {
412
+ params.limit = Math.min(options.limit, 100);
413
+ }
414
+ const response = await this.client.get(
415
+ "/api/v1/services",
416
+ { params }
417
+ );
418
+ return response.data;
419
+ }
420
+ /**
421
+ * Get services for a specific slot
422
+ * GET /api/v1/slots/:id/services
423
+ *
424
+ * Note: Services are tenant-level entities. This endpoint returns all active services
425
+ * for the slot's tenant with items included. The response uses 'items' property (not 'serviceItems').
426
+ *
427
+ * @param slotId - Slot ID (UUID)
428
+ * @returns Services with nested items (always includes items, only active services)
429
+ */
430
+ async getServicesBySlot(slotId) {
431
+ const response = await this.client.get(
432
+ `/api/v1/slots/${slotId}/services`
433
+ );
434
+ return response.data;
435
+ }
368
436
  /**
369
437
  * List services by tenant slug
370
438
  * GET /api/v1/services?tenantSlug=:slug
371
439
  *
440
+ * @deprecated Consider using getServicesByTenant() for better type safety and validation
372
441
  * @param slug - Tenant slug (or use tenant_id in list() method)
373
442
  * @param params - Optional parameters (includeItems, page, limit, is_active)
374
443
  * @returns List of services for the tenant
@@ -384,6 +453,7 @@ var ServiceClient = class {
384
453
  * List services with optional filters
385
454
  * GET /api/v1/services
386
455
  *
456
+ * @deprecated Consider using getServicesByTenant() for better type safety and validation
387
457
  * @param params - Query parameters (tenant_id, tenantSlug, slot_id, page, limit, is_active, includeItems)
388
458
  * @returns Paginated list of services
389
459
  */
@@ -700,8 +770,11 @@ var SlotClient = class {
700
770
  * Get all active services for a slot with their items
701
771
  * GET /api/v1/slots/:id/services
702
772
  *
703
- * @param id - Slot ID
704
- * @returns Services with nested service items
773
+ * Note: Services are tenant-level entities. This endpoint returns all active services
774
+ * for the slot's tenant with items included. The response uses 'items' property (not 'serviceItems').
775
+ *
776
+ * @param id - Slot ID (UUID)
777
+ * @returns Services with nested items (always includes items, only active services)
705
778
  */
706
779
  async getServices(id) {
707
780
  const response = await this.client.get(
@@ -774,9 +847,6 @@ var CustomerClient = class {
774
847
  /**
775
848
  * Create a new customer
776
849
  * POST /api/v1/customers
777
- *
778
- * @param data - Customer creation data (tenant_id, name, email required)
779
- * @returns Created customer
780
850
  */
781
851
  async create(data) {
782
852
  const response = await this.client.post(
@@ -828,11 +898,8 @@ var CustomerClient = class {
828
898
  return response.data;
829
899
  }
830
900
  /**
831
- * Get autocomplete data from existing customers and bookings
832
- * GET /api/v1/customers/autocomplete
833
- *
834
- * @param tenantId - Tenant ID (required)
835
- * @returns Autocomplete data
901
+ * Get autocomplete data from existing customers and bookings.
902
+ * GET /api/v1/customers/autocomplete?tenantId=:tenantId
836
903
  */
837
904
  async autocomplete(tenantId) {
838
905
  const response = await this.client.get(
@@ -842,11 +909,8 @@ var CustomerClient = class {
842
909
  return response.data;
843
910
  }
844
911
  /**
845
- * Get all bookings for a customer
846
- * GET /api/v1/customers/bookings
847
- *
848
- * @param params - Query parameters (email optional, userId optional, tenantId required)
849
- * @returns Customer bookings data
912
+ * Get all bookings for a customer, grouped by slot.
913
+ * GET /api/v1/customers/bookings?tenantId=:id
850
914
  */
851
915
  async getBookings(params) {
852
916
  const response = await this.client.get(
@@ -855,6 +919,85 @@ var CustomerClient = class {
855
919
  );
856
920
  return response.data;
857
921
  }
922
+ // ─── Atomic upsert ────────────────────────────────────────────────────────
923
+ /**
924
+ * Atomic upsert: find an existing customer by email within a tenant, or create one.
925
+ * POST /api/v1/customers/find-or-create
926
+ */
927
+ async findOrCreate(options) {
928
+ const response = await this.client.post(
929
+ "/api/v1/customers/find-or-create",
930
+ options
931
+ );
932
+ return response.data;
933
+ }
934
+ /**
935
+ * Resolve a customer from an IDP identity or email.
936
+ * Tries idpId lookup (customer_users) first, then falls back to email.
937
+ * POST /api/v1/customers/resolve
938
+ */
939
+ async resolve(options) {
940
+ const response = await this.client.post(
941
+ "/api/v1/customers/resolve",
942
+ options
943
+ );
944
+ return response.data;
945
+ }
946
+ // ─── IDP Identity Management ─────────────────────────────────────────────
947
+ /**
948
+ * Get a customer with their linked IDP identities (customer_users rows).
949
+ * GET /api/v1/customers/:id?include_identities=true
950
+ */
951
+ async getWithIdentities(id) {
952
+ const response = await this.client.get(
953
+ `/api/v1/customers/${id}`,
954
+ { params: { include_identities: true } }
955
+ );
956
+ return response.data;
957
+ }
958
+ /**
959
+ * Link an IDP user ID to a customer (creates a customer_users row).
960
+ * POST /api/v1/customers/:id/users
961
+ */
962
+ async linkUser(customerId, idpId, options) {
963
+ const response = await this.client.post(
964
+ `/api/v1/customers/${customerId}/users`,
965
+ { idp_id: idpId, role: options?.role ?? "member", is_primary: options?.is_primary ?? false }
966
+ );
967
+ return response.data;
968
+ }
969
+ /**
970
+ * Unlink an IDP user from a customer (deletes the customer_users row).
971
+ * DELETE /api/v1/customers/:id/users/:idpId
972
+ */
973
+ async unlinkUser(customerId, idpId) {
974
+ const response = await this.client.delete(
975
+ `/api/v1/customers/${customerId}/users/${encodeURIComponent(idpId)}`
976
+ );
977
+ return response.data;
978
+ }
979
+ // ─── Notification Preferences ────────────────────────────────────────────
980
+ /**
981
+ * Get notification preferences for a customer.
982
+ * GET /api/v1/customers/:id/preferences/notifications
983
+ */
984
+ async getNotificationPreferences(customerId) {
985
+ const response = await this.client.get(
986
+ `/api/v1/customers/${customerId}/preferences/notifications`
987
+ );
988
+ return response.data;
989
+ }
990
+ /**
991
+ * Update notification preferences for a customer (partial merge).
992
+ * PATCH /api/v1/customers/:id/preferences/notifications
993
+ */
994
+ async updateNotificationPreferences(customerId, preferences) {
995
+ const response = await this.client.patch(
996
+ `/api/v1/customers/${customerId}/preferences/notifications`,
997
+ preferences
998
+ );
999
+ return response.data;
1000
+ }
858
1001
  };
859
1002
 
860
1003
  // src/clients/flow-client.ts
@@ -1285,6 +1428,103 @@ var OrganizationClient = class {
1285
1428
  }
1286
1429
  };
1287
1430
 
1431
+ // src/clients/document-client.ts
1432
+ var DocumentClient = class {
1433
+ constructor(client) {
1434
+ this.base = "/api/v1/artifacts";
1435
+ this.client = client;
1436
+ }
1437
+ /**
1438
+ * Upload a file artifact.
1439
+ * Sends as multipart/form-data.
1440
+ */
1441
+ async upload(options) {
1442
+ const form = new FormData();
1443
+ let blob;
1444
+ if (options.file instanceof Blob) {
1445
+ blob = options.file;
1446
+ } else {
1447
+ blob = new Blob([options.file], { type: options.mime_type ?? "application/octet-stream" });
1448
+ }
1449
+ form.append("file", blob, options.filename);
1450
+ form.append("related_entity_type", options.related_entity_type);
1451
+ form.append("related_entity_id", options.related_entity_id);
1452
+ if (options.label) form.append("label", options.label);
1453
+ if (options.mime_type) form.append("mime_type", options.mime_type);
1454
+ if (options.metadata) form.append("metadata", JSON.stringify(options.metadata));
1455
+ const response = await this.client.post(this.base, form, {
1456
+ headers: { "Content-Type": "multipart/form-data" }
1457
+ });
1458
+ const { data } = response.data;
1459
+ if (!data) throw new Error("No artifact returned from upload");
1460
+ return data;
1461
+ }
1462
+ /**
1463
+ * List artifacts for a specific entity.
1464
+ * Pass `signed: true` to get download URLs in each result.
1465
+ */
1466
+ async listByEntity(entityType, entityId, options) {
1467
+ const response = await this.client.get(
1468
+ this.base,
1469
+ {
1470
+ params: {
1471
+ related_entity_type: entityType,
1472
+ related_entity_id: entityId,
1473
+ ...options
1474
+ }
1475
+ }
1476
+ );
1477
+ return response.data;
1478
+ }
1479
+ /**
1480
+ * List artifacts with full filter options.
1481
+ */
1482
+ async list(options) {
1483
+ const response = await this.client.get(
1484
+ this.base,
1485
+ { params: options }
1486
+ );
1487
+ return response.data;
1488
+ }
1489
+ /**
1490
+ * Get a single artifact by ID.
1491
+ */
1492
+ async get(id, signed = false) {
1493
+ const response = await this.client.get(
1494
+ `${this.base}/${id}`,
1495
+ { params: signed ? { signed: true } : void 0 }
1496
+ );
1497
+ const { data } = response.data;
1498
+ if (!data) throw new Error(`Artifact ${id} not found`);
1499
+ return data;
1500
+ }
1501
+ /**
1502
+ * Get a signed download URL for an artifact.
1503
+ * Returns the full ArtifactWithUrl including `signed_url` and `signed_url_expires_at`.
1504
+ */
1505
+ async getUrl(id) {
1506
+ return this.get(id, true);
1507
+ }
1508
+ /**
1509
+ * Delete an artifact and its stored file.
1510
+ */
1511
+ async delete(id) {
1512
+ await this.client.delete(`${this.base}/${id}`);
1513
+ }
1514
+ /**
1515
+ * Update artifact label or metadata.
1516
+ */
1517
+ async update(id, patch) {
1518
+ const response = await this.client.patch(
1519
+ `${this.base}/${id}`,
1520
+ patch
1521
+ );
1522
+ const { data } = response.data;
1523
+ if (!data) throw new Error(`Artifact ${id} not found`);
1524
+ return data;
1525
+ }
1526
+ };
1527
+
1288
1528
  // src/errors.ts
1289
1529
  var SlotlyApiError = class _SlotlyApiError extends Error {
1290
1530
  constructor(code, message, statusCode, details) {
@@ -1617,7 +1857,8 @@ var useSlotly = (options) => {
1617
1857
  studio: new StudioClient(client),
1618
1858
  notification: new NotificationClient(client),
1619
1859
  dataQuality: new DataQualityClient(client),
1620
- organization: new OrganizationClient(client)
1860
+ organization: new OrganizationClient(client),
1861
+ document: new DocumentClient(client)
1621
1862
  };
1622
1863
  };
1623
1864
  var index_default = useSlotly;