@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.esm.js CHANGED
@@ -324,10 +324,79 @@ var ServiceClient = class {
324
324
  );
325
325
  return response.data;
326
326
  }
327
+ /**
328
+ * Get services for a tenant (full list with optional filters)
329
+ * GET /api/v1/services
330
+ *
331
+ * @param options - Query options
332
+ * @param options.tenantId - Tenant ID (UUID) - required if tenantSlug not provided
333
+ * @param options.tenantSlug - Tenant slug - required if tenantId not provided
334
+ * @param options.slotId - Slot ID (DEPRECATED - accepted but ignored for API compatibility)
335
+ * @param options.isActive - Filter by active status
336
+ * @param options.includeItems - Include nested service items (default: false)
337
+ * @param options.page - Page number (default: 1)
338
+ * @param options.limit - Items per page (default: 20, max: 100)
339
+ * @returns Services with optional pagination metadata
340
+ */
341
+ async getServicesByTenant(options) {
342
+ if (!options.tenantId && !options.tenantSlug) {
343
+ return {
344
+ success: false,
345
+ error: {
346
+ code: "MISSING_TENANT_ID",
347
+ message: "tenantId or tenantSlug is required"
348
+ }
349
+ };
350
+ }
351
+ const params = {};
352
+ if (options.tenantId) {
353
+ params.tenant_id = options.tenantId;
354
+ }
355
+ if (options.tenantSlug) {
356
+ params.tenantSlug = options.tenantSlug;
357
+ }
358
+ if (options.slotId) {
359
+ params.slot_id = options.slotId;
360
+ }
361
+ if (options.isActive !== void 0) {
362
+ params.is_active = options.isActive;
363
+ }
364
+ if (options.includeItems) {
365
+ params.includeItems = true;
366
+ }
367
+ if (options.page !== void 0) {
368
+ params.page = options.page;
369
+ }
370
+ if (options.limit !== void 0) {
371
+ params.limit = Math.min(options.limit, 100);
372
+ }
373
+ const response = await this.client.get(
374
+ "/api/v1/services",
375
+ { params }
376
+ );
377
+ return response.data;
378
+ }
379
+ /**
380
+ * Get services for a specific slot
381
+ * GET /api/v1/slots/:id/services
382
+ *
383
+ * Note: Services are tenant-level entities. This endpoint returns all active services
384
+ * for the slot's tenant with items included. The response uses 'items' property (not 'serviceItems').
385
+ *
386
+ * @param slotId - Slot ID (UUID)
387
+ * @returns Services with nested items (always includes items, only active services)
388
+ */
389
+ async getServicesBySlot(slotId) {
390
+ const response = await this.client.get(
391
+ `/api/v1/slots/${slotId}/services`
392
+ );
393
+ return response.data;
394
+ }
327
395
  /**
328
396
  * List services by tenant slug
329
397
  * GET /api/v1/services?tenantSlug=:slug
330
398
  *
399
+ * @deprecated Consider using getServicesByTenant() for better type safety and validation
331
400
  * @param slug - Tenant slug (or use tenant_id in list() method)
332
401
  * @param params - Optional parameters (includeItems, page, limit, is_active)
333
402
  * @returns List of services for the tenant
@@ -343,6 +412,7 @@ var ServiceClient = class {
343
412
  * List services with optional filters
344
413
  * GET /api/v1/services
345
414
  *
415
+ * @deprecated Consider using getServicesByTenant() for better type safety and validation
346
416
  * @param params - Query parameters (tenant_id, tenantSlug, slot_id, page, limit, is_active, includeItems)
347
417
  * @returns Paginated list of services
348
418
  */
@@ -659,8 +729,11 @@ var SlotClient = class {
659
729
  * Get all active services for a slot with their items
660
730
  * GET /api/v1/slots/:id/services
661
731
  *
662
- * @param id - Slot ID
663
- * @returns Services with nested service items
732
+ * Note: Services are tenant-level entities. This endpoint returns all active services
733
+ * for the slot's tenant with items included. The response uses 'items' property (not 'serviceItems').
734
+ *
735
+ * @param id - Slot ID (UUID)
736
+ * @returns Services with nested items (always includes items, only active services)
664
737
  */
665
738
  async getServices(id) {
666
739
  const response = await this.client.get(
@@ -733,9 +806,6 @@ var CustomerClient = class {
733
806
  /**
734
807
  * Create a new customer
735
808
  * POST /api/v1/customers
736
- *
737
- * @param data - Customer creation data (tenant_id, name, email required)
738
- * @returns Created customer
739
809
  */
740
810
  async create(data) {
741
811
  const response = await this.client.post(
@@ -787,11 +857,8 @@ var CustomerClient = class {
787
857
  return response.data;
788
858
  }
789
859
  /**
790
- * Get autocomplete data from existing customers and bookings
791
- * GET /api/v1/customers/autocomplete
792
- *
793
- * @param tenantId - Tenant ID (required)
794
- * @returns Autocomplete data
860
+ * Get autocomplete data from existing customers and bookings.
861
+ * GET /api/v1/customers/autocomplete?tenantId=:tenantId
795
862
  */
796
863
  async autocomplete(tenantId) {
797
864
  const response = await this.client.get(
@@ -801,11 +868,8 @@ var CustomerClient = class {
801
868
  return response.data;
802
869
  }
803
870
  /**
804
- * Get all bookings for a customer
805
- * GET /api/v1/customers/bookings
806
- *
807
- * @param params - Query parameters (email optional, userId optional, tenantId required)
808
- * @returns Customer bookings data
871
+ * Get all bookings for a customer, grouped by slot.
872
+ * GET /api/v1/customers/bookings?tenantId=:id
809
873
  */
810
874
  async getBookings(params) {
811
875
  const response = await this.client.get(
@@ -814,6 +878,85 @@ var CustomerClient = class {
814
878
  );
815
879
  return response.data;
816
880
  }
881
+ // ─── Atomic upsert ────────────────────────────────────────────────────────
882
+ /**
883
+ * Atomic upsert: find an existing customer by email within a tenant, or create one.
884
+ * POST /api/v1/customers/find-or-create
885
+ */
886
+ async findOrCreate(options) {
887
+ const response = await this.client.post(
888
+ "/api/v1/customers/find-or-create",
889
+ options
890
+ );
891
+ return response.data;
892
+ }
893
+ /**
894
+ * Resolve a customer from an IDP identity or email.
895
+ * Tries idpId lookup (customer_users) first, then falls back to email.
896
+ * POST /api/v1/customers/resolve
897
+ */
898
+ async resolve(options) {
899
+ const response = await this.client.post(
900
+ "/api/v1/customers/resolve",
901
+ options
902
+ );
903
+ return response.data;
904
+ }
905
+ // ─── IDP Identity Management ─────────────────────────────────────────────
906
+ /**
907
+ * Get a customer with their linked IDP identities (customer_users rows).
908
+ * GET /api/v1/customers/:id?include_identities=true
909
+ */
910
+ async getWithIdentities(id) {
911
+ const response = await this.client.get(
912
+ `/api/v1/customers/${id}`,
913
+ { params: { include_identities: true } }
914
+ );
915
+ return response.data;
916
+ }
917
+ /**
918
+ * Link an IDP user ID to a customer (creates a customer_users row).
919
+ * POST /api/v1/customers/:id/users
920
+ */
921
+ async linkUser(customerId, idpId, options) {
922
+ const response = await this.client.post(
923
+ `/api/v1/customers/${customerId}/users`,
924
+ { idp_id: idpId, role: options?.role ?? "member", is_primary: options?.is_primary ?? false }
925
+ );
926
+ return response.data;
927
+ }
928
+ /**
929
+ * Unlink an IDP user from a customer (deletes the customer_users row).
930
+ * DELETE /api/v1/customers/:id/users/:idpId
931
+ */
932
+ async unlinkUser(customerId, idpId) {
933
+ const response = await this.client.delete(
934
+ `/api/v1/customers/${customerId}/users/${encodeURIComponent(idpId)}`
935
+ );
936
+ return response.data;
937
+ }
938
+ // ─── Notification Preferences ────────────────────────────────────────────
939
+ /**
940
+ * Get notification preferences for a customer.
941
+ * GET /api/v1/customers/:id/preferences/notifications
942
+ */
943
+ async getNotificationPreferences(customerId) {
944
+ const response = await this.client.get(
945
+ `/api/v1/customers/${customerId}/preferences/notifications`
946
+ );
947
+ return response.data;
948
+ }
949
+ /**
950
+ * Update notification preferences for a customer (partial merge).
951
+ * PATCH /api/v1/customers/:id/preferences/notifications
952
+ */
953
+ async updateNotificationPreferences(customerId, preferences) {
954
+ const response = await this.client.patch(
955
+ `/api/v1/customers/${customerId}/preferences/notifications`,
956
+ preferences
957
+ );
958
+ return response.data;
959
+ }
817
960
  };
818
961
 
819
962
  // src/clients/flow-client.ts
@@ -1244,6 +1387,103 @@ var OrganizationClient = class {
1244
1387
  }
1245
1388
  };
1246
1389
 
1390
+ // src/clients/document-client.ts
1391
+ var DocumentClient = class {
1392
+ constructor(client) {
1393
+ this.base = "/api/v1/artifacts";
1394
+ this.client = client;
1395
+ }
1396
+ /**
1397
+ * Upload a file artifact.
1398
+ * Sends as multipart/form-data.
1399
+ */
1400
+ async upload(options) {
1401
+ const form = new FormData();
1402
+ let blob;
1403
+ if (options.file instanceof Blob) {
1404
+ blob = options.file;
1405
+ } else {
1406
+ blob = new Blob([options.file], { type: options.mime_type ?? "application/octet-stream" });
1407
+ }
1408
+ form.append("file", blob, options.filename);
1409
+ form.append("related_entity_type", options.related_entity_type);
1410
+ form.append("related_entity_id", options.related_entity_id);
1411
+ if (options.label) form.append("label", options.label);
1412
+ if (options.mime_type) form.append("mime_type", options.mime_type);
1413
+ if (options.metadata) form.append("metadata", JSON.stringify(options.metadata));
1414
+ const response = await this.client.post(this.base, form, {
1415
+ headers: { "Content-Type": "multipart/form-data" }
1416
+ });
1417
+ const { data } = response.data;
1418
+ if (!data) throw new Error("No artifact returned from upload");
1419
+ return data;
1420
+ }
1421
+ /**
1422
+ * List artifacts for a specific entity.
1423
+ * Pass `signed: true` to get download URLs in each result.
1424
+ */
1425
+ async listByEntity(entityType, entityId, options) {
1426
+ const response = await this.client.get(
1427
+ this.base,
1428
+ {
1429
+ params: {
1430
+ related_entity_type: entityType,
1431
+ related_entity_id: entityId,
1432
+ ...options
1433
+ }
1434
+ }
1435
+ );
1436
+ return response.data;
1437
+ }
1438
+ /**
1439
+ * List artifacts with full filter options.
1440
+ */
1441
+ async list(options) {
1442
+ const response = await this.client.get(
1443
+ this.base,
1444
+ { params: options }
1445
+ );
1446
+ return response.data;
1447
+ }
1448
+ /**
1449
+ * Get a single artifact by ID.
1450
+ */
1451
+ async get(id, signed = false) {
1452
+ const response = await this.client.get(
1453
+ `${this.base}/${id}`,
1454
+ { params: signed ? { signed: true } : void 0 }
1455
+ );
1456
+ const { data } = response.data;
1457
+ if (!data) throw new Error(`Artifact ${id} not found`);
1458
+ return data;
1459
+ }
1460
+ /**
1461
+ * Get a signed download URL for an artifact.
1462
+ * Returns the full ArtifactWithUrl including `signed_url` and `signed_url_expires_at`.
1463
+ */
1464
+ async getUrl(id) {
1465
+ return this.get(id, true);
1466
+ }
1467
+ /**
1468
+ * Delete an artifact and its stored file.
1469
+ */
1470
+ async delete(id) {
1471
+ await this.client.delete(`${this.base}/${id}`);
1472
+ }
1473
+ /**
1474
+ * Update artifact label or metadata.
1475
+ */
1476
+ async update(id, patch) {
1477
+ const response = await this.client.patch(
1478
+ `${this.base}/${id}`,
1479
+ patch
1480
+ );
1481
+ const { data } = response.data;
1482
+ if (!data) throw new Error(`Artifact ${id} not found`);
1483
+ return data;
1484
+ }
1485
+ };
1486
+
1247
1487
  // src/errors.ts
1248
1488
  var SlotlyApiError = class _SlotlyApiError extends Error {
1249
1489
  constructor(code, message, statusCode, details) {
@@ -1576,7 +1816,8 @@ var useSlotly = (options) => {
1576
1816
  studio: new StudioClient(client),
1577
1817
  notification: new NotificationClient(client),
1578
1818
  dataQuality: new DataQualityClient(client),
1579
- organization: new OrganizationClient(client)
1819
+ organization: new OrganizationClient(client),
1820
+ document: new DocumentClient(client)
1580
1821
  };
1581
1822
  };
1582
1823
  var index_default = useSlotly;