@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.d.mts +428 -40
- package/dist/index.d.ts +428 -40
- package/dist/index.js +461 -50
- package/dist/index.mjs +461 -50
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ __export(index_exports, {
|
|
|
24
24
|
StowServer: () => StowServer
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
var import_node_crypto = require("crypto");
|
|
27
28
|
var import_zod = require("zod");
|
|
28
29
|
var StowError = class extends Error {
|
|
29
30
|
status;
|
|
@@ -35,12 +36,74 @@ var StowError = class extends Error {
|
|
|
35
36
|
this.code = code;
|
|
36
37
|
}
|
|
37
38
|
};
|
|
39
|
+
var fileColorSchema = import_zod.z.object({
|
|
40
|
+
position: import_zod.z.number().int(),
|
|
41
|
+
proportion: import_zod.z.number(),
|
|
42
|
+
hex: import_zod.z.string(),
|
|
43
|
+
name: import_zod.z.string().nullable(),
|
|
44
|
+
hsl: import_zod.z.object({ h: import_zod.z.number(), s: import_zod.z.number(), l: import_zod.z.number() }),
|
|
45
|
+
oklab: import_zod.z.object({ L: import_zod.z.number(), a: import_zod.z.number(), b: import_zod.z.number() }).nullable(),
|
|
46
|
+
oklch: import_zod.z.object({ l: import_zod.z.number(), c: import_zod.z.number(), h: import_zod.z.number() }).nullable()
|
|
47
|
+
});
|
|
48
|
+
var fileColorProfileSchema = import_zod.z.object({
|
|
49
|
+
palette: import_zod.z.object({
|
|
50
|
+
mood: import_zod.z.string(),
|
|
51
|
+
brightness: import_zod.z.number(),
|
|
52
|
+
temperature: import_zod.z.number(),
|
|
53
|
+
vibrancy: import_zod.z.number(),
|
|
54
|
+
complexity: import_zod.z.number(),
|
|
55
|
+
dominantFamily: import_zod.z.string().nullable()
|
|
56
|
+
}),
|
|
57
|
+
backgroundHex: import_zod.z.string().nullable(),
|
|
58
|
+
accent: import_zod.z.object({
|
|
59
|
+
hex: import_zod.z.string(),
|
|
60
|
+
name: import_zod.z.string().nullable(),
|
|
61
|
+
oklab: import_zod.z.object({ L: import_zod.z.number(), a: import_zod.z.number(), b: import_zod.z.number() }).nullable(),
|
|
62
|
+
oklch: import_zod.z.object({ l: import_zod.z.number(), c: import_zod.z.number(), h: import_zod.z.number() }).nullable()
|
|
63
|
+
}).nullable(),
|
|
64
|
+
extractedAt: import_zod.z.string(),
|
|
65
|
+
colorCount: import_zod.z.number().int()
|
|
66
|
+
});
|
|
38
67
|
var uploadResultSchema = import_zod.z.object({
|
|
39
68
|
key: import_zod.z.string(),
|
|
40
69
|
url: import_zod.z.string().nullable(),
|
|
41
70
|
size: import_zod.z.number(),
|
|
42
71
|
contentType: import_zod.z.string().optional(),
|
|
43
|
-
metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional()
|
|
72
|
+
metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
73
|
+
deduped: import_zod.z.boolean().optional()
|
|
74
|
+
});
|
|
75
|
+
var bucketSchema = import_zod.z.object({
|
|
76
|
+
id: import_zod.z.string(),
|
|
77
|
+
name: import_zod.z.string(),
|
|
78
|
+
description: import_zod.z.string().nullable().optional(),
|
|
79
|
+
isPublic: import_zod.z.boolean().optional(),
|
|
80
|
+
searchable: import_zod.z.boolean().optional(),
|
|
81
|
+
allowedTypes: import_zod.z.array(import_zod.z.string()).nullable().optional(),
|
|
82
|
+
maxFileSize: import_zod.z.coerce.number().nullable().optional(),
|
|
83
|
+
storageQuota: import_zod.z.coerce.number().nullable().optional(),
|
|
84
|
+
fileCountLimit: import_zod.z.coerce.number().nullable().optional(),
|
|
85
|
+
fileCount: import_zod.z.coerce.number().optional(),
|
|
86
|
+
usageBytes: import_zod.z.coerce.number().optional(),
|
|
87
|
+
createdAt: import_zod.z.string().optional()
|
|
88
|
+
});
|
|
89
|
+
var listBucketsSchema = import_zod.z.object({
|
|
90
|
+
buckets: import_zod.z.array(bucketSchema)
|
|
91
|
+
});
|
|
92
|
+
var bucketResponseSchema = import_zod.z.object({
|
|
93
|
+
bucket: bucketSchema
|
|
94
|
+
});
|
|
95
|
+
var whoamiSchema = import_zod.z.object({
|
|
96
|
+
user: import_zod.z.object({ email: import_zod.z.string() }),
|
|
97
|
+
stats: import_zod.z.object({
|
|
98
|
+
totalBytes: import_zod.z.coerce.number(),
|
|
99
|
+
totalFiles: import_zod.z.coerce.number(),
|
|
100
|
+
bucketCount: import_zod.z.coerce.number()
|
|
101
|
+
}),
|
|
102
|
+
key: import_zod.z.object({
|
|
103
|
+
name: import_zod.z.string(),
|
|
104
|
+
scope: import_zod.z.string(),
|
|
105
|
+
permissions: import_zod.z.record(import_zod.z.string(), import_zod.z.boolean())
|
|
106
|
+
}).optional()
|
|
44
107
|
});
|
|
45
108
|
var listFilesSchema = import_zod.z.object({
|
|
46
109
|
files: import_zod.z.array(
|
|
@@ -49,11 +112,26 @@ var listFilesSchema = import_zod.z.object({
|
|
|
49
112
|
size: import_zod.z.number(),
|
|
50
113
|
lastModified: import_zod.z.string(),
|
|
51
114
|
url: import_zod.z.string().nullable(),
|
|
52
|
-
|
|
115
|
+
width: import_zod.z.number().nullable().optional(),
|
|
116
|
+
height: import_zod.z.number().nullable().optional(),
|
|
117
|
+
duration: import_zod.z.number().nullable().optional(),
|
|
118
|
+
metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
|
|
119
|
+
colorProfile: fileColorProfileSchema.nullable().optional(),
|
|
120
|
+
colors: import_zod.z.array(fileColorSchema).optional()
|
|
53
121
|
})
|
|
54
122
|
),
|
|
55
123
|
nextCursor: import_zod.z.string().nullable()
|
|
56
124
|
});
|
|
125
|
+
var taskTriggerResultSchema = import_zod.z.object({
|
|
126
|
+
key: import_zod.z.string(),
|
|
127
|
+
triggered: import_zod.z.string()
|
|
128
|
+
});
|
|
129
|
+
var replaceResultSchema = import_zod.z.object({
|
|
130
|
+
key: import_zod.z.string(),
|
|
131
|
+
size: import_zod.z.number(),
|
|
132
|
+
contentType: import_zod.z.string(),
|
|
133
|
+
triggered: import_zod.z.array(import_zod.z.string())
|
|
134
|
+
});
|
|
57
135
|
var errorSchema = import_zod.z.object({
|
|
58
136
|
error: import_zod.z.string(),
|
|
59
137
|
code: import_zod.z.string().optional()
|
|
@@ -81,7 +159,6 @@ var listDropsSchema = import_zod.z.object({
|
|
|
81
159
|
var presignNewResultSchema = import_zod.z.object({
|
|
82
160
|
fileKey: import_zod.z.string(),
|
|
83
161
|
uploadUrl: import_zod.z.string(),
|
|
84
|
-
r2Key: import_zod.z.string(),
|
|
85
162
|
confirmUrl: import_zod.z.string(),
|
|
86
163
|
dedupe: import_zod.z.literal(false).optional()
|
|
87
164
|
});
|
|
@@ -108,14 +185,31 @@ var fileResultSchema = import_zod.z.object({
|
|
|
108
185
|
size: import_zod.z.number(),
|
|
109
186
|
contentType: import_zod.z.string(),
|
|
110
187
|
url: import_zod.z.string().nullable(),
|
|
188
|
+
width: import_zod.z.number().nullable(),
|
|
189
|
+
height: import_zod.z.number().nullable(),
|
|
190
|
+
duration: import_zod.z.number().nullable(),
|
|
111
191
|
metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).nullable(),
|
|
192
|
+
colorProfile: fileColorProfileSchema.nullable(),
|
|
193
|
+
colors: import_zod.z.array(fileColorSchema),
|
|
112
194
|
embeddingStatus: import_zod.z.string().nullable(),
|
|
113
195
|
createdAt: import_zod.z.string()
|
|
114
196
|
});
|
|
197
|
+
var profileClusterResultSchema = import_zod.z.object({
|
|
198
|
+
id: import_zod.z.string(),
|
|
199
|
+
index: import_zod.z.number().int(),
|
|
200
|
+
name: import_zod.z.string().nullable(),
|
|
201
|
+
description: import_zod.z.string().nullable(),
|
|
202
|
+
signalCount: import_zod.z.number().int(),
|
|
203
|
+
totalWeight: import_zod.z.number(),
|
|
204
|
+
nameGeneratedAt: import_zod.z.string().nullable()
|
|
205
|
+
});
|
|
115
206
|
var profileResultSchema = import_zod.z.object({
|
|
116
207
|
id: import_zod.z.string(),
|
|
117
208
|
name: import_zod.z.string().nullable(),
|
|
118
209
|
fileCount: import_zod.z.number(),
|
|
210
|
+
signalCount: import_zod.z.number(),
|
|
211
|
+
vector: import_zod.z.array(import_zod.z.number()).nullable(),
|
|
212
|
+
clusters: import_zod.z.array(profileClusterResultSchema).optional(),
|
|
119
213
|
createdAt: import_zod.z.string(),
|
|
120
214
|
updatedAt: import_zod.z.string()
|
|
121
215
|
});
|
|
@@ -123,6 +217,38 @@ var profileFilesResultSchema = import_zod.z.object({
|
|
|
123
217
|
id: import_zod.z.string(),
|
|
124
218
|
fileCount: import_zod.z.number()
|
|
125
219
|
});
|
|
220
|
+
var profileSignalResultSchema = import_zod.z.object({
|
|
221
|
+
id: import_zod.z.string(),
|
|
222
|
+
fileKey: import_zod.z.string(),
|
|
223
|
+
type: import_zod.z.enum([
|
|
224
|
+
"view",
|
|
225
|
+
"view_long",
|
|
226
|
+
"click",
|
|
227
|
+
"like",
|
|
228
|
+
"save",
|
|
229
|
+
"choose",
|
|
230
|
+
"purchase",
|
|
231
|
+
"share",
|
|
232
|
+
"dismiss",
|
|
233
|
+
"skip",
|
|
234
|
+
"reject",
|
|
235
|
+
"report",
|
|
236
|
+
"custom"
|
|
237
|
+
]),
|
|
238
|
+
weight: import_zod.z.number()
|
|
239
|
+
});
|
|
240
|
+
var profileSignalsResponseSchema = import_zod.z.object({
|
|
241
|
+
profileId: import_zod.z.string(),
|
|
242
|
+
signals: import_zod.z.array(profileSignalResultSchema),
|
|
243
|
+
totalSignals: import_zod.z.number(),
|
|
244
|
+
vectorUpdated: import_zod.z.boolean()
|
|
245
|
+
});
|
|
246
|
+
var deleteProfileSignalsResponseSchema = import_zod.z.object({
|
|
247
|
+
profileId: import_zod.z.string(),
|
|
248
|
+
removed: import_zod.z.number(),
|
|
249
|
+
totalSignals: import_zod.z.number(),
|
|
250
|
+
vectorUpdated: import_zod.z.boolean()
|
|
251
|
+
});
|
|
126
252
|
var StowServer = class {
|
|
127
253
|
apiKey;
|
|
128
254
|
baseUrl;
|
|
@@ -149,6 +275,88 @@ var StowServer = class {
|
|
|
149
275
|
getBaseUrl() {
|
|
150
276
|
return this.baseUrl;
|
|
151
277
|
}
|
|
278
|
+
/**
|
|
279
|
+
* Return account usage and API key info for the current credential.
|
|
280
|
+
*/
|
|
281
|
+
whoami() {
|
|
282
|
+
return this.request("/api/whoami", { method: "GET" }, whoamiSchema);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* List all buckets available to the current organization.
|
|
286
|
+
*/
|
|
287
|
+
listBuckets() {
|
|
288
|
+
return this.request("/api/buckets", { method: "GET" }, listBucketsSchema);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Create a new bucket.
|
|
292
|
+
*/
|
|
293
|
+
async createBucket(request) {
|
|
294
|
+
const result = await this.request(
|
|
295
|
+
"/api/buckets",
|
|
296
|
+
{
|
|
297
|
+
method: "POST",
|
|
298
|
+
headers: { "Content-Type": "application/json" },
|
|
299
|
+
body: JSON.stringify(request)
|
|
300
|
+
},
|
|
301
|
+
bucketResponseSchema
|
|
302
|
+
);
|
|
303
|
+
return result.bucket;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Get bucket details by id.
|
|
307
|
+
*/
|
|
308
|
+
async getBucket(id) {
|
|
309
|
+
const result = await this.request(
|
|
310
|
+
`/api/buckets/${encodeURIComponent(id)}`,
|
|
311
|
+
{ method: "GET" },
|
|
312
|
+
bucketResponseSchema
|
|
313
|
+
);
|
|
314
|
+
return result.bucket;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Update bucket settings by id.
|
|
318
|
+
*/
|
|
319
|
+
async updateBucket(id, updates) {
|
|
320
|
+
const result = await this.request(
|
|
321
|
+
`/api/buckets/${encodeURIComponent(id)}`,
|
|
322
|
+
{
|
|
323
|
+
method: "PATCH",
|
|
324
|
+
headers: { "Content-Type": "application/json" },
|
|
325
|
+
body: JSON.stringify(updates)
|
|
326
|
+
},
|
|
327
|
+
bucketResponseSchema
|
|
328
|
+
);
|
|
329
|
+
return result.bucket;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Rename/update a bucket by current bucket name.
|
|
333
|
+
*/
|
|
334
|
+
async updateBucketByName(name, updates) {
|
|
335
|
+
const result = await this.request(
|
|
336
|
+
`/api/buckets/_?bucket=${encodeURIComponent(name)}`,
|
|
337
|
+
{
|
|
338
|
+
method: "PATCH",
|
|
339
|
+
headers: { "Content-Type": "application/json" },
|
|
340
|
+
body: JSON.stringify(updates)
|
|
341
|
+
},
|
|
342
|
+
bucketResponseSchema
|
|
343
|
+
);
|
|
344
|
+
return result.bucket;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Rename a bucket by current bucket name.
|
|
348
|
+
*/
|
|
349
|
+
renameBucket(name, newName) {
|
|
350
|
+
return this.updateBucketByName(name, { name: newName });
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Delete a bucket by id.
|
|
354
|
+
*/
|
|
355
|
+
async deleteBucket(id) {
|
|
356
|
+
await this.request(`/api/buckets/${encodeURIComponent(id)}`, {
|
|
357
|
+
method: "DELETE"
|
|
358
|
+
});
|
|
359
|
+
}
|
|
152
360
|
/**
|
|
153
361
|
* Resolve the effective bucket for this request.
|
|
154
362
|
* Per-call override > constructor default.
|
|
@@ -174,6 +382,7 @@ var StowServer = class {
|
|
|
174
382
|
* - AbortController timeout (default 30s).
|
|
175
383
|
* - Consumer can pass `signal` in options to cancel.
|
|
176
384
|
*/
|
|
385
|
+
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: retry + timeout + error normalization intentionally handled in one request pipeline
|
|
177
386
|
async request(path, options, schema) {
|
|
178
387
|
const maxAttempts = this.retries + 1;
|
|
179
388
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
@@ -208,6 +417,13 @@ var StowServer = class {
|
|
|
208
417
|
if (err instanceof StowError) {
|
|
209
418
|
throw err;
|
|
210
419
|
}
|
|
420
|
+
if (err instanceof import_zod.z.ZodError) {
|
|
421
|
+
throw new StowError(
|
|
422
|
+
"Invalid response format",
|
|
423
|
+
500,
|
|
424
|
+
"INVALID_RESPONSE"
|
|
425
|
+
);
|
|
426
|
+
}
|
|
211
427
|
if (err instanceof DOMException || err instanceof Error && err.name === "AbortError") {
|
|
212
428
|
throw new StowError("Request timed out", 408, "TIMEOUT");
|
|
213
429
|
}
|
|
@@ -233,32 +449,58 @@ var StowServer = class {
|
|
|
233
449
|
* Upload a file directly from the server
|
|
234
450
|
*/
|
|
235
451
|
async uploadFile(file, options) {
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
452
|
+
const filename = options?.filename || "file";
|
|
453
|
+
const buffer = Buffer.isBuffer(file) ? file : Buffer.from(await file.arrayBuffer());
|
|
454
|
+
const contentType = options?.contentType || (!Buffer.isBuffer(file) && file.type ? file.type : "application/octet-stream");
|
|
455
|
+
const contentHash = (0, import_node_crypto.createHash)("sha256").update(buffer).digest("hex");
|
|
456
|
+
const presign = await this.getPresignedUrl({
|
|
457
|
+
filename,
|
|
458
|
+
contentType,
|
|
459
|
+
size: buffer.length,
|
|
460
|
+
...options?.route ? { route: options.route } : {},
|
|
461
|
+
...options?.bucket ? { bucket: options.bucket } : {},
|
|
462
|
+
...options?.metadata ? { metadata: options.metadata } : {},
|
|
463
|
+
contentHash
|
|
464
|
+
});
|
|
465
|
+
if (presign.dedupe) {
|
|
466
|
+
return {
|
|
467
|
+
key: presign.key,
|
|
468
|
+
url: presign.url,
|
|
469
|
+
size: presign.size,
|
|
470
|
+
contentType: presign.contentType,
|
|
471
|
+
deduped: true
|
|
472
|
+
};
|
|
243
473
|
}
|
|
244
|
-
|
|
245
|
-
|
|
474
|
+
const uploadRes = await fetch(presign.uploadUrl, {
|
|
475
|
+
method: "PUT",
|
|
476
|
+
headers: { "Content-Type": contentType },
|
|
477
|
+
body: new Uint8Array(buffer)
|
|
478
|
+
});
|
|
479
|
+
if (!uploadRes.ok) {
|
|
480
|
+
throw new StowError("Failed to upload to storage", uploadRes.status);
|
|
246
481
|
}
|
|
247
|
-
|
|
248
|
-
this.withBucket(
|
|
482
|
+
return this.request(
|
|
483
|
+
this.withBucket(
|
|
484
|
+
presign.confirmUrl || "/api/presign/confirm",
|
|
485
|
+
options?.bucket
|
|
486
|
+
),
|
|
249
487
|
{
|
|
250
488
|
method: "POST",
|
|
251
|
-
|
|
489
|
+
headers: { "Content-Type": "application/json" },
|
|
490
|
+
body: JSON.stringify({
|
|
491
|
+
fileKey: presign.fileKey,
|
|
492
|
+
size: buffer.length,
|
|
493
|
+
contentType,
|
|
494
|
+
...options?.metadata ? { metadata: options.metadata } : {},
|
|
495
|
+
contentHash,
|
|
496
|
+
skipVerify: true,
|
|
497
|
+
...options?.title ? { title: true } : {},
|
|
498
|
+
...options?.describe ? { describe: true } : {},
|
|
499
|
+
...options?.altText ? { altText: true } : {}
|
|
500
|
+
})
|
|
252
501
|
},
|
|
253
|
-
|
|
502
|
+
confirmResultSchema
|
|
254
503
|
);
|
|
255
|
-
return {
|
|
256
|
-
key: result.key,
|
|
257
|
-
url: result.url,
|
|
258
|
-
size: result.size,
|
|
259
|
-
contentType: result.contentType || options?.contentType || "application/octet-stream",
|
|
260
|
-
...result.metadata ? { metadata: result.metadata } : {}
|
|
261
|
-
};
|
|
262
504
|
}
|
|
263
505
|
/**
|
|
264
506
|
* Upload a file from a URL (server-side fetch + upload)
|
|
@@ -273,7 +515,10 @@ var StowServer = class {
|
|
|
273
515
|
url,
|
|
274
516
|
filename,
|
|
275
517
|
...options?.metadata ? { metadata: options.metadata } : {},
|
|
276
|
-
...options?.headers ? { headers: options.headers } : {}
|
|
518
|
+
...options?.headers ? { headers: options.headers } : {},
|
|
519
|
+
...options?.title ? { title: true } : {},
|
|
520
|
+
...options?.describe ? { describe: true } : {},
|
|
521
|
+
...options?.altText ? { altText: true } : {}
|
|
277
522
|
})
|
|
278
523
|
},
|
|
279
524
|
uploadResultSchema
|
|
@@ -296,7 +541,15 @@ var StowServer = class {
|
|
|
296
541
|
* 4. Client calls confirmUpload to finalize
|
|
297
542
|
*/
|
|
298
543
|
getPresignedUrl(request) {
|
|
299
|
-
const {
|
|
544
|
+
const {
|
|
545
|
+
filename,
|
|
546
|
+
contentType,
|
|
547
|
+
size,
|
|
548
|
+
route,
|
|
549
|
+
bucket,
|
|
550
|
+
metadata,
|
|
551
|
+
contentHash
|
|
552
|
+
} = request;
|
|
300
553
|
return this.request(
|
|
301
554
|
this.withBucket("/api/presign", bucket),
|
|
302
555
|
{
|
|
@@ -327,7 +580,10 @@ var StowServer = class {
|
|
|
327
580
|
metadata,
|
|
328
581
|
skipVerify,
|
|
329
582
|
deferKvSync,
|
|
330
|
-
contentHash
|
|
583
|
+
contentHash,
|
|
584
|
+
title,
|
|
585
|
+
describe,
|
|
586
|
+
altText
|
|
331
587
|
} = request;
|
|
332
588
|
return this.request(
|
|
333
589
|
this.withBucket("/api/presign/confirm", bucket),
|
|
@@ -341,7 +597,10 @@ var StowServer = class {
|
|
|
341
597
|
...metadata ? { metadata } : {},
|
|
342
598
|
...skipVerify ? { skipVerify } : {},
|
|
343
599
|
...deferKvSync ? { deferKvSync } : {},
|
|
344
|
-
...contentHash ? { contentHash } : {}
|
|
600
|
+
...contentHash ? { contentHash } : {},
|
|
601
|
+
...title ? { title } : {},
|
|
602
|
+
...describe ? { describe } : {},
|
|
603
|
+
...altText ? { altText } : {}
|
|
345
604
|
})
|
|
346
605
|
},
|
|
347
606
|
confirmResultSchema
|
|
@@ -383,7 +642,7 @@ var StowServer = class {
|
|
|
383
642
|
/**
|
|
384
643
|
* Update metadata on an existing file
|
|
385
644
|
*/
|
|
386
|
-
|
|
645
|
+
updateFileMetadata(key, metadata, options) {
|
|
387
646
|
const path = `/api/files/${encodeURIComponent(key)}`;
|
|
388
647
|
return this.request(this.withBucket(path, options?.bucket), {
|
|
389
648
|
method: "PATCH",
|
|
@@ -394,7 +653,7 @@ var StowServer = class {
|
|
|
394
653
|
/**
|
|
395
654
|
* Get a single file by key
|
|
396
655
|
*/
|
|
397
|
-
|
|
656
|
+
getFile(key, options) {
|
|
398
657
|
const path = `/api/files/${encodeURIComponent(key)}`;
|
|
399
658
|
return this.request(
|
|
400
659
|
this.withBucket(path, options?.bucket),
|
|
@@ -402,6 +661,60 @@ var StowServer = class {
|
|
|
402
661
|
fileResultSchema
|
|
403
662
|
);
|
|
404
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* Extract color palette from an image file.
|
|
666
|
+
* Requires a searchable bucket.
|
|
667
|
+
*/
|
|
668
|
+
extractColors(key) {
|
|
669
|
+
return this.request(
|
|
670
|
+
`/api/files/${encodeURIComponent(key)}/extract-colors`,
|
|
671
|
+
{ method: "POST" },
|
|
672
|
+
taskTriggerResultSchema
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* Extract dimensions (width/height) from an image or video file.
|
|
677
|
+
*/
|
|
678
|
+
extractDimensions(key) {
|
|
679
|
+
return this.request(
|
|
680
|
+
`/api/files/${encodeURIComponent(key)}/extract-dimensions`,
|
|
681
|
+
{ method: "POST" },
|
|
682
|
+
taskTriggerResultSchema
|
|
683
|
+
);
|
|
684
|
+
}
|
|
685
|
+
/**
|
|
686
|
+
* Generate a vector embedding for an image file.
|
|
687
|
+
* Requires a searchable bucket.
|
|
688
|
+
*/
|
|
689
|
+
embed(key) {
|
|
690
|
+
return this.request(
|
|
691
|
+
`/api/files/${encodeURIComponent(key)}/embed`,
|
|
692
|
+
{ method: "POST" },
|
|
693
|
+
taskTriggerResultSchema
|
|
694
|
+
);
|
|
695
|
+
}
|
|
696
|
+
/**
|
|
697
|
+
* Replace a file's content by fetching from a new URL.
|
|
698
|
+
*
|
|
699
|
+
* Keeps the same file key but replaces the stored object and resets all
|
|
700
|
+
* derived data (dimensions, embeddings, colors, AI metadata). Processing
|
|
701
|
+
* tasks are re-dispatched as if the file were newly uploaded.
|
|
702
|
+
*/
|
|
703
|
+
replaceFile(key, url, options) {
|
|
704
|
+
const path = `/api/files/${encodeURIComponent(key)}/replace`;
|
|
705
|
+
return this.request(
|
|
706
|
+
this.withBucket(path, options?.bucket),
|
|
707
|
+
{
|
|
708
|
+
method: "PUT",
|
|
709
|
+
headers: { "Content-Type": "application/json" },
|
|
710
|
+
body: JSON.stringify({
|
|
711
|
+
url,
|
|
712
|
+
...options?.headers ? { headers: options.headers } : {}
|
|
713
|
+
})
|
|
714
|
+
},
|
|
715
|
+
replaceResultSchema
|
|
716
|
+
);
|
|
717
|
+
}
|
|
405
718
|
/**
|
|
406
719
|
* Get a transform URL for an image.
|
|
407
720
|
*
|
|
@@ -413,25 +726,23 @@ var StowServer = class {
|
|
|
413
726
|
* @param options - Transform options (width, height, quality, format)
|
|
414
727
|
*/
|
|
415
728
|
getTransformUrl(url, options) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
params.set("w", options.width.toString());
|
|
729
|
+
if (!(options && (options.width || options.height || options.quality || options.format))) {
|
|
730
|
+
return url;
|
|
419
731
|
}
|
|
420
|
-
|
|
421
|
-
|
|
732
|
+
const parsed = new URL(url);
|
|
733
|
+
if (options.width) {
|
|
734
|
+
parsed.searchParams.set("w", String(options.width));
|
|
422
735
|
}
|
|
423
|
-
if (options
|
|
424
|
-
|
|
736
|
+
if (options.height) {
|
|
737
|
+
parsed.searchParams.set("h", String(options.height));
|
|
425
738
|
}
|
|
426
|
-
if (options
|
|
427
|
-
|
|
739
|
+
if (options.quality) {
|
|
740
|
+
parsed.searchParams.set("q", String(options.quality));
|
|
428
741
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
return url;
|
|
742
|
+
if (options.format) {
|
|
743
|
+
parsed.searchParams.set("f", options.format);
|
|
432
744
|
}
|
|
433
|
-
|
|
434
|
-
return `${url}${separator}${query}`;
|
|
745
|
+
return parsed.toString();
|
|
435
746
|
}
|
|
436
747
|
// ============================================================
|
|
437
748
|
// TAGS - Org-scoped labels for file organization
|
|
@@ -493,10 +804,13 @@ var StowServer = class {
|
|
|
493
804
|
get search() {
|
|
494
805
|
return {
|
|
495
806
|
similar: (params) => this.searchSimilar(params),
|
|
496
|
-
|
|
807
|
+
diverse: (params) => this.searchDiverse(params ?? {}),
|
|
808
|
+
text: (params) => this.searchText(params),
|
|
809
|
+
color: (params) => this.searchColor(params)
|
|
497
810
|
};
|
|
498
811
|
}
|
|
499
812
|
searchSimilar(params) {
|
|
813
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
500
814
|
return this.request("/api/search/similar", {
|
|
501
815
|
method: "POST",
|
|
502
816
|
headers: { "Content-Type": "application/json" },
|
|
@@ -504,19 +818,62 @@ var StowServer = class {
|
|
|
504
818
|
...params.fileKey ? { fileKey: params.fileKey } : {},
|
|
505
819
|
...params.vector ? { vector: params.vector } : {},
|
|
506
820
|
...params.profileId ? { profileId: params.profileId } : {},
|
|
507
|
-
...params.
|
|
508
|
-
...params.
|
|
821
|
+
...params.clusterId ? { clusterId: params.clusterId } : {},
|
|
822
|
+
...params.clusterIds?.length ? { clusterIds: params.clusterIds } : {},
|
|
823
|
+
...bucket ? { bucket } : {},
|
|
824
|
+
...params.limit ? { limit: params.limit } : {},
|
|
825
|
+
...params.excludeKeys?.length ? { excludeKeys: params.excludeKeys } : {},
|
|
826
|
+
...params.filters ? { filters: params.filters } : {},
|
|
827
|
+
...params.include?.length ? { include: params.include } : {}
|
|
828
|
+
})
|
|
829
|
+
});
|
|
830
|
+
}
|
|
831
|
+
searchDiverse(params) {
|
|
832
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
833
|
+
return this.request("/api/search/diverse", {
|
|
834
|
+
method: "POST",
|
|
835
|
+
headers: { "Content-Type": "application/json" },
|
|
836
|
+
body: JSON.stringify({
|
|
837
|
+
...params.fileKey ? { fileKey: params.fileKey } : {},
|
|
838
|
+
...params.vector ? { vector: params.vector } : {},
|
|
839
|
+
...params.profileId ? { profileId: params.profileId } : {},
|
|
840
|
+
...params.clusterId ? { clusterId: params.clusterId } : {},
|
|
841
|
+
...params.clusterIds?.length ? { clusterIds: params.clusterIds } : {},
|
|
842
|
+
...bucket ? { bucket } : {},
|
|
843
|
+
...params.limit ? { limit: params.limit } : {},
|
|
844
|
+
...params.lambda !== void 0 ? { lambda: params.lambda } : {},
|
|
845
|
+
...params.excludeKeys?.length ? { excludeKeys: params.excludeKeys } : {},
|
|
846
|
+
...params.filters ? { filters: params.filters } : {},
|
|
847
|
+
...params.include?.length ? { include: params.include } : {}
|
|
509
848
|
})
|
|
510
849
|
});
|
|
511
850
|
}
|
|
512
851
|
searchText(params) {
|
|
852
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
513
853
|
return this.request("/api/search/text", {
|
|
514
854
|
method: "POST",
|
|
515
855
|
headers: { "Content-Type": "application/json" },
|
|
516
856
|
body: JSON.stringify({
|
|
517
857
|
query: params.query,
|
|
518
|
-
...
|
|
519
|
-
...params.limit ? { limit: params.limit } : {}
|
|
858
|
+
...bucket ? { bucket } : {},
|
|
859
|
+
...params.limit ? { limit: params.limit } : {},
|
|
860
|
+
...params.filters ? { filters: params.filters } : {},
|
|
861
|
+
...params.include?.length ? { include: params.include } : {}
|
|
862
|
+
})
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
searchColor(params) {
|
|
866
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
867
|
+
return this.request("/api/search/color", {
|
|
868
|
+
method: "POST",
|
|
869
|
+
headers: { "Content-Type": "application/json" },
|
|
870
|
+
body: JSON.stringify({
|
|
871
|
+
...params.hex ? { hex: params.hex } : {},
|
|
872
|
+
...params.oklab ? { oklab: params.oklab } : {},
|
|
873
|
+
...bucket ? { bucket } : {},
|
|
874
|
+
...params.limit ? { limit: params.limit } : {},
|
|
875
|
+
...params.minProportion !== void 0 ? { minProportion: params.minProportion } : {},
|
|
876
|
+
...params.dominantOnly ? { dominantOnly: params.dominantOnly } : {}
|
|
520
877
|
})
|
|
521
878
|
});
|
|
522
879
|
}
|
|
@@ -557,7 +914,7 @@ var StowServer = class {
|
|
|
557
914
|
throw new StowError("Failed to upload to storage", putRes.status);
|
|
558
915
|
}
|
|
559
916
|
return this.request(
|
|
560
|
-
"/api/drops/presign/confirm",
|
|
917
|
+
presign.confirmUrl || "/api/drops/presign/confirm",
|
|
561
918
|
{
|
|
562
919
|
method: "POST",
|
|
563
920
|
headers: { "Content-Type": "application/json" },
|
|
@@ -599,7 +956,12 @@ var StowServer = class {
|
|
|
599
956
|
get: (id) => this.getProfile(id),
|
|
600
957
|
delete: (id) => this.deleteProfile(id),
|
|
601
958
|
addFiles: (id, fileKeys, bucket) => this.addProfileFiles(id, fileKeys, bucket),
|
|
602
|
-
removeFiles: (id, fileKeys, bucket) => this.removeProfileFiles(id, fileKeys, bucket)
|
|
959
|
+
removeFiles: (id, fileKeys, bucket) => this.removeProfileFiles(id, fileKeys, bucket),
|
|
960
|
+
signal: (id, signals, bucket) => this.signalProfile(id, signals, bucket),
|
|
961
|
+
deleteSignals: (id, signalIds) => this.deleteProfileSignals(id, signalIds),
|
|
962
|
+
clusters: (id) => this.getProfileClusters(id),
|
|
963
|
+
recluster: (id, params) => this.reclusterProfile(id, params),
|
|
964
|
+
renameCluster: (profileId, clusterId, params) => this.renameProfileCluster(profileId, clusterId, params)
|
|
603
965
|
};
|
|
604
966
|
}
|
|
605
967
|
createProfile(params) {
|
|
@@ -657,6 +1019,55 @@ var StowServer = class {
|
|
|
657
1019
|
profileFilesResultSchema
|
|
658
1020
|
);
|
|
659
1021
|
}
|
|
1022
|
+
signalProfile(id, signals, bucket) {
|
|
1023
|
+
return this.request(
|
|
1024
|
+
`/api/profiles/${encodeURIComponent(id)}/signals`,
|
|
1025
|
+
{
|
|
1026
|
+
method: "POST",
|
|
1027
|
+
headers: { "Content-Type": "application/json" },
|
|
1028
|
+
body: JSON.stringify({
|
|
1029
|
+
signals,
|
|
1030
|
+
...bucket ? { bucket } : {}
|
|
1031
|
+
})
|
|
1032
|
+
},
|
|
1033
|
+
profileSignalsResponseSchema
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
deleteProfileSignals(id, signalIds) {
|
|
1037
|
+
return this.request(
|
|
1038
|
+
`/api/profiles/${encodeURIComponent(id)}/signals`,
|
|
1039
|
+
{
|
|
1040
|
+
method: "DELETE",
|
|
1041
|
+
headers: { "Content-Type": "application/json" },
|
|
1042
|
+
body: JSON.stringify({ signalIds })
|
|
1043
|
+
},
|
|
1044
|
+
deleteProfileSignalsResponseSchema
|
|
1045
|
+
);
|
|
1046
|
+
}
|
|
1047
|
+
getProfileClusters(id) {
|
|
1048
|
+
return this.request(`/api/profiles/${encodeURIComponent(id)}/clusters`, {
|
|
1049
|
+
method: "GET"
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
reclusterProfile(id, params) {
|
|
1053
|
+
return this.request(`/api/profiles/${encodeURIComponent(id)}/clusters`, {
|
|
1054
|
+
method: "POST",
|
|
1055
|
+
headers: { "Content-Type": "application/json" },
|
|
1056
|
+
body: JSON.stringify({
|
|
1057
|
+
...params?.clusterCount !== void 0 ? { clusterCount: params.clusterCount } : {}
|
|
1058
|
+
})
|
|
1059
|
+
});
|
|
1060
|
+
}
|
|
1061
|
+
renameProfileCluster(profileId, clusterId, params) {
|
|
1062
|
+
return this.request(
|
|
1063
|
+
`/api/profiles/${encodeURIComponent(profileId)}/clusters/${encodeURIComponent(clusterId)}`,
|
|
1064
|
+
{
|
|
1065
|
+
method: "PUT",
|
|
1066
|
+
headers: { "Content-Type": "application/json" },
|
|
1067
|
+
body: JSON.stringify(params)
|
|
1068
|
+
}
|
|
1069
|
+
);
|
|
1070
|
+
}
|
|
660
1071
|
};
|
|
661
1072
|
// Annotate the CommonJS export names for ESM import in node:
|
|
662
1073
|
0 && (module.exports = {
|