@howells/stow-server 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,4 +1,5 @@
1
1
  // src/index.ts
2
+ import { createHash } from "crypto";
2
3
  import { z } from "zod";
3
4
  var StowError = class extends Error {
4
5
  status;
@@ -10,12 +11,74 @@ var StowError = class extends Error {
10
11
  this.code = code;
11
12
  }
12
13
  };
14
+ var fileColorSchema = z.object({
15
+ position: z.number().int(),
16
+ proportion: z.number(),
17
+ hex: z.string(),
18
+ name: z.string().nullable(),
19
+ hsl: z.object({ h: z.number(), s: z.number(), l: z.number() }),
20
+ oklab: z.object({ L: z.number(), a: z.number(), b: z.number() }).nullable(),
21
+ oklch: z.object({ l: z.number(), c: z.number(), h: z.number() }).nullable()
22
+ });
23
+ var fileColorProfileSchema = z.object({
24
+ palette: z.object({
25
+ mood: z.string(),
26
+ brightness: z.number(),
27
+ temperature: z.number(),
28
+ vibrancy: z.number(),
29
+ complexity: z.number(),
30
+ dominantFamily: z.string().nullable()
31
+ }),
32
+ backgroundHex: z.string().nullable(),
33
+ accent: z.object({
34
+ hex: z.string(),
35
+ name: z.string().nullable(),
36
+ oklab: z.object({ L: z.number(), a: z.number(), b: z.number() }).nullable(),
37
+ oklch: z.object({ l: z.number(), c: z.number(), h: z.number() }).nullable()
38
+ }).nullable(),
39
+ extractedAt: z.string(),
40
+ colorCount: z.number().int()
41
+ });
13
42
  var uploadResultSchema = z.object({
14
43
  key: z.string(),
15
44
  url: z.string().nullable(),
16
45
  size: z.number(),
17
46
  contentType: z.string().optional(),
18
- metadata: z.record(z.string(), z.string()).optional()
47
+ metadata: z.record(z.string(), z.string()).optional(),
48
+ deduped: z.boolean().optional()
49
+ });
50
+ var bucketSchema = z.object({
51
+ id: z.string(),
52
+ name: z.string(),
53
+ description: z.string().nullable().optional(),
54
+ isPublic: z.boolean().optional(),
55
+ searchable: z.boolean().optional(),
56
+ allowedTypes: z.array(z.string()).nullable().optional(),
57
+ maxFileSize: z.coerce.number().nullable().optional(),
58
+ storageQuota: z.coerce.number().nullable().optional(),
59
+ fileCountLimit: z.coerce.number().nullable().optional(),
60
+ fileCount: z.coerce.number().optional(),
61
+ usageBytes: z.coerce.number().optional(),
62
+ createdAt: z.string().optional()
63
+ });
64
+ var listBucketsSchema = z.object({
65
+ buckets: z.array(bucketSchema)
66
+ });
67
+ var bucketResponseSchema = z.object({
68
+ bucket: bucketSchema
69
+ });
70
+ var whoamiSchema = z.object({
71
+ user: z.object({ email: z.string() }),
72
+ stats: z.object({
73
+ totalBytes: z.coerce.number(),
74
+ totalFiles: z.coerce.number(),
75
+ bucketCount: z.coerce.number()
76
+ }),
77
+ key: z.object({
78
+ name: z.string(),
79
+ scope: z.string(),
80
+ permissions: z.record(z.string(), z.boolean())
81
+ }).optional()
19
82
  });
20
83
  var listFilesSchema = z.object({
21
84
  files: z.array(
@@ -24,11 +87,26 @@ var listFilesSchema = z.object({
24
87
  size: z.number(),
25
88
  lastModified: z.string(),
26
89
  url: z.string().nullable(),
27
- metadata: z.record(z.string(), z.string()).optional()
90
+ width: z.number().nullable().optional(),
91
+ height: z.number().nullable().optional(),
92
+ duration: z.number().nullable().optional(),
93
+ metadata: z.record(z.string(), z.string()).optional(),
94
+ colorProfile: fileColorProfileSchema.nullable().optional(),
95
+ colors: z.array(fileColorSchema).optional()
28
96
  })
29
97
  ),
30
98
  nextCursor: z.string().nullable()
31
99
  });
100
+ var taskTriggerResultSchema = z.object({
101
+ key: z.string(),
102
+ triggered: z.string()
103
+ });
104
+ var replaceResultSchema = z.object({
105
+ key: z.string(),
106
+ size: z.number(),
107
+ contentType: z.string(),
108
+ triggered: z.array(z.string())
109
+ });
32
110
  var errorSchema = z.object({
33
111
  error: z.string(),
34
112
  code: z.string().optional()
@@ -56,7 +134,6 @@ var listDropsSchema = z.object({
56
134
  var presignNewResultSchema = z.object({
57
135
  fileKey: z.string(),
58
136
  uploadUrl: z.string(),
59
- r2Key: z.string(),
60
137
  confirmUrl: z.string(),
61
138
  dedupe: z.literal(false).optional()
62
139
  });
@@ -83,14 +160,31 @@ var fileResultSchema = z.object({
83
160
  size: z.number(),
84
161
  contentType: z.string(),
85
162
  url: z.string().nullable(),
163
+ width: z.number().nullable(),
164
+ height: z.number().nullable(),
165
+ duration: z.number().nullable(),
86
166
  metadata: z.record(z.string(), z.string()).nullable(),
167
+ colorProfile: fileColorProfileSchema.nullable(),
168
+ colors: z.array(fileColorSchema),
87
169
  embeddingStatus: z.string().nullable(),
88
170
  createdAt: z.string()
89
171
  });
172
+ var profileClusterResultSchema = z.object({
173
+ id: z.string(),
174
+ index: z.number().int(),
175
+ name: z.string().nullable(),
176
+ description: z.string().nullable(),
177
+ signalCount: z.number().int(),
178
+ totalWeight: z.number(),
179
+ nameGeneratedAt: z.string().nullable()
180
+ });
90
181
  var profileResultSchema = z.object({
91
182
  id: z.string(),
92
183
  name: z.string().nullable(),
93
184
  fileCount: z.number(),
185
+ signalCount: z.number(),
186
+ vector: z.array(z.number()).nullable(),
187
+ clusters: z.array(profileClusterResultSchema).optional(),
94
188
  createdAt: z.string(),
95
189
  updatedAt: z.string()
96
190
  });
@@ -98,6 +192,38 @@ var profileFilesResultSchema = z.object({
98
192
  id: z.string(),
99
193
  fileCount: z.number()
100
194
  });
195
+ var profileSignalResultSchema = z.object({
196
+ id: z.string(),
197
+ fileKey: z.string(),
198
+ type: z.enum([
199
+ "view",
200
+ "view_long",
201
+ "click",
202
+ "like",
203
+ "save",
204
+ "choose",
205
+ "purchase",
206
+ "share",
207
+ "dismiss",
208
+ "skip",
209
+ "reject",
210
+ "report",
211
+ "custom"
212
+ ]),
213
+ weight: z.number()
214
+ });
215
+ var profileSignalsResponseSchema = z.object({
216
+ profileId: z.string(),
217
+ signals: z.array(profileSignalResultSchema),
218
+ totalSignals: z.number(),
219
+ vectorUpdated: z.boolean()
220
+ });
221
+ var deleteProfileSignalsResponseSchema = z.object({
222
+ profileId: z.string(),
223
+ removed: z.number(),
224
+ totalSignals: z.number(),
225
+ vectorUpdated: z.boolean()
226
+ });
101
227
  var StowServer = class {
102
228
  apiKey;
103
229
  baseUrl;
@@ -124,6 +250,88 @@ var StowServer = class {
124
250
  getBaseUrl() {
125
251
  return this.baseUrl;
126
252
  }
253
+ /**
254
+ * Return account usage and API key info for the current credential.
255
+ */
256
+ whoami() {
257
+ return this.request("/api/whoami", { method: "GET" }, whoamiSchema);
258
+ }
259
+ /**
260
+ * List all buckets available to the current organization.
261
+ */
262
+ listBuckets() {
263
+ return this.request("/api/buckets", { method: "GET" }, listBucketsSchema);
264
+ }
265
+ /**
266
+ * Create a new bucket.
267
+ */
268
+ async createBucket(request) {
269
+ const result = await this.request(
270
+ "/api/buckets",
271
+ {
272
+ method: "POST",
273
+ headers: { "Content-Type": "application/json" },
274
+ body: JSON.stringify(request)
275
+ },
276
+ bucketResponseSchema
277
+ );
278
+ return result.bucket;
279
+ }
280
+ /**
281
+ * Get bucket details by id.
282
+ */
283
+ async getBucket(id) {
284
+ const result = await this.request(
285
+ `/api/buckets/${encodeURIComponent(id)}`,
286
+ { method: "GET" },
287
+ bucketResponseSchema
288
+ );
289
+ return result.bucket;
290
+ }
291
+ /**
292
+ * Update bucket settings by id.
293
+ */
294
+ async updateBucket(id, updates) {
295
+ const result = await this.request(
296
+ `/api/buckets/${encodeURIComponent(id)}`,
297
+ {
298
+ method: "PATCH",
299
+ headers: { "Content-Type": "application/json" },
300
+ body: JSON.stringify(updates)
301
+ },
302
+ bucketResponseSchema
303
+ );
304
+ return result.bucket;
305
+ }
306
+ /**
307
+ * Rename/update a bucket by current bucket name.
308
+ */
309
+ async updateBucketByName(name, updates) {
310
+ const result = await this.request(
311
+ `/api/buckets/_?bucket=${encodeURIComponent(name)}`,
312
+ {
313
+ method: "PATCH",
314
+ headers: { "Content-Type": "application/json" },
315
+ body: JSON.stringify(updates)
316
+ },
317
+ bucketResponseSchema
318
+ );
319
+ return result.bucket;
320
+ }
321
+ /**
322
+ * Rename a bucket by current bucket name.
323
+ */
324
+ renameBucket(name, newName) {
325
+ return this.updateBucketByName(name, { name: newName });
326
+ }
327
+ /**
328
+ * Delete a bucket by id.
329
+ */
330
+ async deleteBucket(id) {
331
+ await this.request(`/api/buckets/${encodeURIComponent(id)}`, {
332
+ method: "DELETE"
333
+ });
334
+ }
127
335
  /**
128
336
  * Resolve the effective bucket for this request.
129
337
  * Per-call override > constructor default.
@@ -149,6 +357,7 @@ var StowServer = class {
149
357
  * - AbortController timeout (default 30s).
150
358
  * - Consumer can pass `signal` in options to cancel.
151
359
  */
360
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: retry + timeout + error normalization intentionally handled in one request pipeline
152
361
  async request(path, options, schema) {
153
362
  const maxAttempts = this.retries + 1;
154
363
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
@@ -183,6 +392,13 @@ var StowServer = class {
183
392
  if (err instanceof StowError) {
184
393
  throw err;
185
394
  }
395
+ if (err instanceof z.ZodError) {
396
+ throw new StowError(
397
+ "Invalid response format",
398
+ 500,
399
+ "INVALID_RESPONSE"
400
+ );
401
+ }
186
402
  if (err instanceof DOMException || err instanceof Error && err.name === "AbortError") {
187
403
  throw new StowError("Request timed out", 408, "TIMEOUT");
188
404
  }
@@ -208,32 +424,58 @@ var StowServer = class {
208
424
  * Upload a file directly from the server
209
425
  */
210
426
  async uploadFile(file, options) {
211
- const formData = new FormData();
212
- const blob = Buffer.isBuffer(file) ? new Blob([new Uint8Array(file)], {
213
- type: options?.contentType || "application/octet-stream"
214
- }) : file;
215
- formData.append("file", blob, options?.filename || "file");
216
- if (options?.route) {
217
- formData.append("route", options.route);
427
+ const filename = options?.filename || "file";
428
+ const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
429
+ const contentType = options?.contentType || (!Buffer.isBuffer(file) && file.type ? file.type : "application/octet-stream");
430
+ const contentHash = createHash("sha256").update(buffer).digest("hex");
431
+ const presign = await this.getPresignedUrl({
432
+ filename,
433
+ contentType,
434
+ size: buffer.length,
435
+ ...options?.route ? { route: options.route } : {},
436
+ ...options?.bucket ? { bucket: options.bucket } : {},
437
+ ...options?.metadata ? { metadata: options.metadata } : {},
438
+ contentHash
439
+ });
440
+ if (presign.dedupe) {
441
+ return {
442
+ key: presign.key,
443
+ url: presign.url,
444
+ size: presign.size,
445
+ contentType: presign.contentType,
446
+ deduped: true
447
+ };
218
448
  }
219
- if (options?.metadata) {
220
- formData.append("metadata", JSON.stringify(options.metadata));
449
+ const uploadRes = await fetch(presign.uploadUrl, {
450
+ method: "PUT",
451
+ headers: { "Content-Type": contentType },
452
+ body: new Uint8Array(buffer)
453
+ });
454
+ if (!uploadRes.ok) {
455
+ throw new StowError("Failed to upload to storage", uploadRes.status);
221
456
  }
222
- const result = await this.request(
223
- this.withBucket("/api/upload", options?.bucket),
457
+ return this.request(
458
+ this.withBucket(
459
+ presign.confirmUrl || "/api/presign/confirm",
460
+ options?.bucket
461
+ ),
224
462
  {
225
463
  method: "POST",
226
- body: formData
464
+ headers: { "Content-Type": "application/json" },
465
+ body: JSON.stringify({
466
+ fileKey: presign.fileKey,
467
+ size: buffer.length,
468
+ contentType,
469
+ ...options?.metadata ? { metadata: options.metadata } : {},
470
+ contentHash,
471
+ skipVerify: true,
472
+ ...options?.title ? { title: true } : {},
473
+ ...options?.describe ? { describe: true } : {},
474
+ ...options?.altText ? { altText: true } : {}
475
+ })
227
476
  },
228
- uploadResultSchema
477
+ confirmResultSchema
229
478
  );
230
- return {
231
- key: result.key,
232
- url: result.url,
233
- size: result.size,
234
- contentType: result.contentType || options?.contentType || "application/octet-stream",
235
- ...result.metadata ? { metadata: result.metadata } : {}
236
- };
237
479
  }
238
480
  /**
239
481
  * Upload a file from a URL (server-side fetch + upload)
@@ -248,7 +490,10 @@ var StowServer = class {
248
490
  url,
249
491
  filename,
250
492
  ...options?.metadata ? { metadata: options.metadata } : {},
251
- ...options?.headers ? { headers: options.headers } : {}
493
+ ...options?.headers ? { headers: options.headers } : {},
494
+ ...options?.title ? { title: true } : {},
495
+ ...options?.describe ? { describe: true } : {},
496
+ ...options?.altText ? { altText: true } : {}
252
497
  })
253
498
  },
254
499
  uploadResultSchema
@@ -271,7 +516,15 @@ var StowServer = class {
271
516
  * 4. Client calls confirmUpload to finalize
272
517
  */
273
518
  getPresignedUrl(request) {
274
- const { filename, contentType, size, route, bucket, metadata, contentHash } = request;
519
+ const {
520
+ filename,
521
+ contentType,
522
+ size,
523
+ route,
524
+ bucket,
525
+ metadata,
526
+ contentHash
527
+ } = request;
275
528
  return this.request(
276
529
  this.withBucket("/api/presign", bucket),
277
530
  {
@@ -302,7 +555,10 @@ var StowServer = class {
302
555
  metadata,
303
556
  skipVerify,
304
557
  deferKvSync,
305
- contentHash
558
+ contentHash,
559
+ title,
560
+ describe,
561
+ altText
306
562
  } = request;
307
563
  return this.request(
308
564
  this.withBucket("/api/presign/confirm", bucket),
@@ -316,7 +572,10 @@ var StowServer = class {
316
572
  ...metadata ? { metadata } : {},
317
573
  ...skipVerify ? { skipVerify } : {},
318
574
  ...deferKvSync ? { deferKvSync } : {},
319
- ...contentHash ? { contentHash } : {}
575
+ ...contentHash ? { contentHash } : {},
576
+ ...title ? { title } : {},
577
+ ...describe ? { describe } : {},
578
+ ...altText ? { altText } : {}
320
579
  })
321
580
  },
322
581
  confirmResultSchema
@@ -358,7 +617,7 @@ var StowServer = class {
358
617
  /**
359
618
  * Update metadata on an existing file
360
619
  */
361
- async updateFileMetadata(key, metadata, options) {
620
+ updateFileMetadata(key, metadata, options) {
362
621
  const path = `/api/files/${encodeURIComponent(key)}`;
363
622
  return this.request(this.withBucket(path, options?.bucket), {
364
623
  method: "PATCH",
@@ -369,7 +628,7 @@ var StowServer = class {
369
628
  /**
370
629
  * Get a single file by key
371
630
  */
372
- async getFile(key, options) {
631
+ getFile(key, options) {
373
632
  const path = `/api/files/${encodeURIComponent(key)}`;
374
633
  return this.request(
375
634
  this.withBucket(path, options?.bucket),
@@ -377,6 +636,60 @@ var StowServer = class {
377
636
  fileResultSchema
378
637
  );
379
638
  }
639
+ /**
640
+ * Extract color palette from an image file.
641
+ * Requires a searchable bucket.
642
+ */
643
+ extractColors(key) {
644
+ return this.request(
645
+ `/api/files/${encodeURIComponent(key)}/extract-colors`,
646
+ { method: "POST" },
647
+ taskTriggerResultSchema
648
+ );
649
+ }
650
+ /**
651
+ * Extract dimensions (width/height) from an image or video file.
652
+ */
653
+ extractDimensions(key) {
654
+ return this.request(
655
+ `/api/files/${encodeURIComponent(key)}/extract-dimensions`,
656
+ { method: "POST" },
657
+ taskTriggerResultSchema
658
+ );
659
+ }
660
+ /**
661
+ * Generate a vector embedding for an image file.
662
+ * Requires a searchable bucket.
663
+ */
664
+ embed(key) {
665
+ return this.request(
666
+ `/api/files/${encodeURIComponent(key)}/embed`,
667
+ { method: "POST" },
668
+ taskTriggerResultSchema
669
+ );
670
+ }
671
+ /**
672
+ * Replace a file's content by fetching from a new URL.
673
+ *
674
+ * Keeps the same file key but replaces the stored object and resets all
675
+ * derived data (dimensions, embeddings, colors, AI metadata). Processing
676
+ * tasks are re-dispatched as if the file were newly uploaded.
677
+ */
678
+ replaceFile(key, url, options) {
679
+ const path = `/api/files/${encodeURIComponent(key)}/replace`;
680
+ return this.request(
681
+ this.withBucket(path, options?.bucket),
682
+ {
683
+ method: "PUT",
684
+ headers: { "Content-Type": "application/json" },
685
+ body: JSON.stringify({
686
+ url,
687
+ ...options?.headers ? { headers: options.headers } : {}
688
+ })
689
+ },
690
+ replaceResultSchema
691
+ );
692
+ }
380
693
  /**
381
694
  * Get a transform URL for an image.
382
695
  *
@@ -388,25 +701,23 @@ var StowServer = class {
388
701
  * @param options - Transform options (width, height, quality, format)
389
702
  */
390
703
  getTransformUrl(url, options) {
391
- const params = new URLSearchParams();
392
- if (options?.width) {
393
- params.set("w", options.width.toString());
704
+ if (!(options && (options.width || options.height || options.quality || options.format))) {
705
+ return url;
394
706
  }
395
- if (options?.height) {
396
- params.set("h", options.height.toString());
707
+ const parsed = new URL(url);
708
+ if (options.width) {
709
+ parsed.searchParams.set("w", String(options.width));
397
710
  }
398
- if (options?.quality) {
399
- params.set("q", options.quality.toString());
711
+ if (options.height) {
712
+ parsed.searchParams.set("h", String(options.height));
400
713
  }
401
- if (options?.format) {
402
- params.set("f", options.format);
714
+ if (options.quality) {
715
+ parsed.searchParams.set("q", String(options.quality));
403
716
  }
404
- const query = params.toString();
405
- if (!query) {
406
- return url;
717
+ if (options.format) {
718
+ parsed.searchParams.set("f", options.format);
407
719
  }
408
- const separator = url.includes("?") ? "&" : "?";
409
- return `${url}${separator}${query}`;
720
+ return parsed.toString();
410
721
  }
411
722
  // ============================================================
412
723
  // TAGS - Org-scoped labels for file organization
@@ -468,10 +779,13 @@ var StowServer = class {
468
779
  get search() {
469
780
  return {
470
781
  similar: (params) => this.searchSimilar(params),
471
- text: (params) => this.searchText(params)
782
+ diverse: (params) => this.searchDiverse(params ?? {}),
783
+ text: (params) => this.searchText(params),
784
+ color: (params) => this.searchColor(params)
472
785
  };
473
786
  }
474
787
  searchSimilar(params) {
788
+ const bucket = this.resolveBucket(params.bucket);
475
789
  return this.request("/api/search/similar", {
476
790
  method: "POST",
477
791
  headers: { "Content-Type": "application/json" },
@@ -479,19 +793,62 @@ var StowServer = class {
479
793
  ...params.fileKey ? { fileKey: params.fileKey } : {},
480
794
  ...params.vector ? { vector: params.vector } : {},
481
795
  ...params.profileId ? { profileId: params.profileId } : {},
482
- ...params.bucket ? { bucket: params.bucket } : {},
483
- ...params.limit ? { limit: params.limit } : {}
796
+ ...params.clusterId ? { clusterId: params.clusterId } : {},
797
+ ...params.clusterIds?.length ? { clusterIds: params.clusterIds } : {},
798
+ ...bucket ? { bucket } : {},
799
+ ...params.limit ? { limit: params.limit } : {},
800
+ ...params.excludeKeys?.length ? { excludeKeys: params.excludeKeys } : {},
801
+ ...params.filters ? { filters: params.filters } : {},
802
+ ...params.include?.length ? { include: params.include } : {}
803
+ })
804
+ });
805
+ }
806
+ searchDiverse(params) {
807
+ const bucket = this.resolveBucket(params.bucket);
808
+ return this.request("/api/search/diverse", {
809
+ method: "POST",
810
+ headers: { "Content-Type": "application/json" },
811
+ body: JSON.stringify({
812
+ ...params.fileKey ? { fileKey: params.fileKey } : {},
813
+ ...params.vector ? { vector: params.vector } : {},
814
+ ...params.profileId ? { profileId: params.profileId } : {},
815
+ ...params.clusterId ? { clusterId: params.clusterId } : {},
816
+ ...params.clusterIds?.length ? { clusterIds: params.clusterIds } : {},
817
+ ...bucket ? { bucket } : {},
818
+ ...params.limit ? { limit: params.limit } : {},
819
+ ...params.lambda !== void 0 ? { lambda: params.lambda } : {},
820
+ ...params.excludeKeys?.length ? { excludeKeys: params.excludeKeys } : {},
821
+ ...params.filters ? { filters: params.filters } : {},
822
+ ...params.include?.length ? { include: params.include } : {}
484
823
  })
485
824
  });
486
825
  }
487
826
  searchText(params) {
827
+ const bucket = this.resolveBucket(params.bucket);
488
828
  return this.request("/api/search/text", {
489
829
  method: "POST",
490
830
  headers: { "Content-Type": "application/json" },
491
831
  body: JSON.stringify({
492
832
  query: params.query,
493
- ...params.bucket ? { bucket: params.bucket } : {},
494
- ...params.limit ? { limit: params.limit } : {}
833
+ ...bucket ? { bucket } : {},
834
+ ...params.limit ? { limit: params.limit } : {},
835
+ ...params.filters ? { filters: params.filters } : {},
836
+ ...params.include?.length ? { include: params.include } : {}
837
+ })
838
+ });
839
+ }
840
+ searchColor(params) {
841
+ const bucket = this.resolveBucket(params.bucket);
842
+ return this.request("/api/search/color", {
843
+ method: "POST",
844
+ headers: { "Content-Type": "application/json" },
845
+ body: JSON.stringify({
846
+ ...params.hex ? { hex: params.hex } : {},
847
+ ...params.oklab ? { oklab: params.oklab } : {},
848
+ ...bucket ? { bucket } : {},
849
+ ...params.limit ? { limit: params.limit } : {},
850
+ ...params.minProportion !== void 0 ? { minProportion: params.minProportion } : {},
851
+ ...params.dominantOnly ? { dominantOnly: params.dominantOnly } : {}
495
852
  })
496
853
  });
497
854
  }
@@ -532,7 +889,7 @@ var StowServer = class {
532
889
  throw new StowError("Failed to upload to storage", putRes.status);
533
890
  }
534
891
  return this.request(
535
- "/api/drops/presign/confirm",
892
+ presign.confirmUrl || "/api/drops/presign/confirm",
536
893
  {
537
894
  method: "POST",
538
895
  headers: { "Content-Type": "application/json" },
@@ -574,7 +931,12 @@ var StowServer = class {
574
931
  get: (id) => this.getProfile(id),
575
932
  delete: (id) => this.deleteProfile(id),
576
933
  addFiles: (id, fileKeys, bucket) => this.addProfileFiles(id, fileKeys, bucket),
577
- removeFiles: (id, fileKeys, bucket) => this.removeProfileFiles(id, fileKeys, bucket)
934
+ removeFiles: (id, fileKeys, bucket) => this.removeProfileFiles(id, fileKeys, bucket),
935
+ signal: (id, signals, bucket) => this.signalProfile(id, signals, bucket),
936
+ deleteSignals: (id, signalIds) => this.deleteProfileSignals(id, signalIds),
937
+ clusters: (id) => this.getProfileClusters(id),
938
+ recluster: (id, params) => this.reclusterProfile(id, params),
939
+ renameCluster: (profileId, clusterId, params) => this.renameProfileCluster(profileId, clusterId, params)
578
940
  };
579
941
  }
580
942
  createProfile(params) {
@@ -632,6 +994,55 @@ var StowServer = class {
632
994
  profileFilesResultSchema
633
995
  );
634
996
  }
997
+ signalProfile(id, signals, bucket) {
998
+ return this.request(
999
+ `/api/profiles/${encodeURIComponent(id)}/signals`,
1000
+ {
1001
+ method: "POST",
1002
+ headers: { "Content-Type": "application/json" },
1003
+ body: JSON.stringify({
1004
+ signals,
1005
+ ...bucket ? { bucket } : {}
1006
+ })
1007
+ },
1008
+ profileSignalsResponseSchema
1009
+ );
1010
+ }
1011
+ deleteProfileSignals(id, signalIds) {
1012
+ return this.request(
1013
+ `/api/profiles/${encodeURIComponent(id)}/signals`,
1014
+ {
1015
+ method: "DELETE",
1016
+ headers: { "Content-Type": "application/json" },
1017
+ body: JSON.stringify({ signalIds })
1018
+ },
1019
+ deleteProfileSignalsResponseSchema
1020
+ );
1021
+ }
1022
+ getProfileClusters(id) {
1023
+ return this.request(`/api/profiles/${encodeURIComponent(id)}/clusters`, {
1024
+ method: "GET"
1025
+ });
1026
+ }
1027
+ reclusterProfile(id, params) {
1028
+ return this.request(`/api/profiles/${encodeURIComponent(id)}/clusters`, {
1029
+ method: "POST",
1030
+ headers: { "Content-Type": "application/json" },
1031
+ body: JSON.stringify({
1032
+ ...params?.clusterCount !== void 0 ? { clusterCount: params.clusterCount } : {}
1033
+ })
1034
+ });
1035
+ }
1036
+ renameProfileCluster(profileId, clusterId, params) {
1037
+ return this.request(
1038
+ `/api/profiles/${encodeURIComponent(profileId)}/clusters/${encodeURIComponent(clusterId)}`,
1039
+ {
1040
+ method: "PUT",
1041
+ headers: { "Content-Type": "application/json" },
1042
+ body: JSON.stringify(params)
1043
+ }
1044
+ );
1045
+ }
635
1046
  };
636
1047
  export {
637
1048
  StowError,