@howells/stow-server 0.1.2 → 0.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/LICENSE +21 -0
- package/dist/index.d.mts +421 -40
- package/dist/index.d.ts +421 -40
- package/dist/index.js +441 -50
- package/dist/index.mjs +441 -50
- package/package.json +11 -11
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 reprocessResultSchema = import_zod.z.object({
|
|
126
|
+
key: import_zod.z.string(),
|
|
127
|
+
triggered: import_zod.z.array(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,40 @@ var StowServer = class {
|
|
|
402
661
|
fileResultSchema
|
|
403
662
|
);
|
|
404
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* Reprocess a file: reset all derived data (embeddings, colors, dimensions,
|
|
666
|
+
* AI metadata, taxonomies) and re-trigger processing tasks.
|
|
667
|
+
*/
|
|
668
|
+
reprocessFile(key, options) {
|
|
669
|
+
const path = `/api/files/${encodeURIComponent(key)}/reprocess`;
|
|
670
|
+
return this.request(
|
|
671
|
+
this.withBucket(path, options?.bucket),
|
|
672
|
+
{ method: "POST" },
|
|
673
|
+
reprocessResultSchema
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
/**
|
|
677
|
+
* Replace a file's content by fetching from a new URL.
|
|
678
|
+
*
|
|
679
|
+
* Keeps the same file key but replaces the stored object and resets all
|
|
680
|
+
* derived data (dimensions, embeddings, colors, AI metadata). Processing
|
|
681
|
+
* tasks are re-dispatched as if the file were newly uploaded.
|
|
682
|
+
*/
|
|
683
|
+
replaceFile(key, url, options) {
|
|
684
|
+
const path = `/api/files/${encodeURIComponent(key)}/replace`;
|
|
685
|
+
return this.request(
|
|
686
|
+
this.withBucket(path, options?.bucket),
|
|
687
|
+
{
|
|
688
|
+
method: "PUT",
|
|
689
|
+
headers: { "Content-Type": "application/json" },
|
|
690
|
+
body: JSON.stringify({
|
|
691
|
+
url,
|
|
692
|
+
...options?.headers ? { headers: options.headers } : {}
|
|
693
|
+
})
|
|
694
|
+
},
|
|
695
|
+
replaceResultSchema
|
|
696
|
+
);
|
|
697
|
+
}
|
|
405
698
|
/**
|
|
406
699
|
* Get a transform URL for an image.
|
|
407
700
|
*
|
|
@@ -413,25 +706,23 @@ var StowServer = class {
|
|
|
413
706
|
* @param options - Transform options (width, height, quality, format)
|
|
414
707
|
*/
|
|
415
708
|
getTransformUrl(url, options) {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
params.set("w", options.width.toString());
|
|
709
|
+
if (!(options && (options.width || options.height || options.quality || options.format))) {
|
|
710
|
+
return url;
|
|
419
711
|
}
|
|
420
|
-
|
|
421
|
-
|
|
712
|
+
const parsed = new URL(url);
|
|
713
|
+
if (options.width) {
|
|
714
|
+
parsed.searchParams.set("w", String(options.width));
|
|
422
715
|
}
|
|
423
|
-
if (options
|
|
424
|
-
|
|
716
|
+
if (options.height) {
|
|
717
|
+
parsed.searchParams.set("h", String(options.height));
|
|
425
718
|
}
|
|
426
|
-
if (options
|
|
427
|
-
|
|
719
|
+
if (options.quality) {
|
|
720
|
+
parsed.searchParams.set("q", String(options.quality));
|
|
428
721
|
}
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
return url;
|
|
722
|
+
if (options.format) {
|
|
723
|
+
parsed.searchParams.set("f", options.format);
|
|
432
724
|
}
|
|
433
|
-
|
|
434
|
-
return `${url}${separator}${query}`;
|
|
725
|
+
return parsed.toString();
|
|
435
726
|
}
|
|
436
727
|
// ============================================================
|
|
437
728
|
// TAGS - Org-scoped labels for file organization
|
|
@@ -493,10 +784,13 @@ var StowServer = class {
|
|
|
493
784
|
get search() {
|
|
494
785
|
return {
|
|
495
786
|
similar: (params) => this.searchSimilar(params),
|
|
496
|
-
|
|
787
|
+
diverse: (params) => this.searchDiverse(params ?? {}),
|
|
788
|
+
text: (params) => this.searchText(params),
|
|
789
|
+
color: (params) => this.searchColor(params)
|
|
497
790
|
};
|
|
498
791
|
}
|
|
499
792
|
searchSimilar(params) {
|
|
793
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
500
794
|
return this.request("/api/search/similar", {
|
|
501
795
|
method: "POST",
|
|
502
796
|
headers: { "Content-Type": "application/json" },
|
|
@@ -504,19 +798,62 @@ var StowServer = class {
|
|
|
504
798
|
...params.fileKey ? { fileKey: params.fileKey } : {},
|
|
505
799
|
...params.vector ? { vector: params.vector } : {},
|
|
506
800
|
...params.profileId ? { profileId: params.profileId } : {},
|
|
507
|
-
...params.
|
|
508
|
-
...params.
|
|
801
|
+
...params.clusterId ? { clusterId: params.clusterId } : {},
|
|
802
|
+
...params.clusterIds?.length ? { clusterIds: params.clusterIds } : {},
|
|
803
|
+
...bucket ? { bucket } : {},
|
|
804
|
+
...params.limit ? { limit: params.limit } : {},
|
|
805
|
+
...params.excludeKeys?.length ? { excludeKeys: params.excludeKeys } : {},
|
|
806
|
+
...params.filters ? { filters: params.filters } : {},
|
|
807
|
+
...params.include?.length ? { include: params.include } : {}
|
|
808
|
+
})
|
|
809
|
+
});
|
|
810
|
+
}
|
|
811
|
+
searchDiverse(params) {
|
|
812
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
813
|
+
return this.request("/api/search/diverse", {
|
|
814
|
+
method: "POST",
|
|
815
|
+
headers: { "Content-Type": "application/json" },
|
|
816
|
+
body: JSON.stringify({
|
|
817
|
+
...params.fileKey ? { fileKey: params.fileKey } : {},
|
|
818
|
+
...params.vector ? { vector: params.vector } : {},
|
|
819
|
+
...params.profileId ? { profileId: params.profileId } : {},
|
|
820
|
+
...params.clusterId ? { clusterId: params.clusterId } : {},
|
|
821
|
+
...params.clusterIds?.length ? { clusterIds: params.clusterIds } : {},
|
|
822
|
+
...bucket ? { bucket } : {},
|
|
823
|
+
...params.limit ? { limit: params.limit } : {},
|
|
824
|
+
...params.lambda !== void 0 ? { lambda: params.lambda } : {},
|
|
825
|
+
...params.excludeKeys?.length ? { excludeKeys: params.excludeKeys } : {},
|
|
826
|
+
...params.filters ? { filters: params.filters } : {},
|
|
827
|
+
...params.include?.length ? { include: params.include } : {}
|
|
509
828
|
})
|
|
510
829
|
});
|
|
511
830
|
}
|
|
512
831
|
searchText(params) {
|
|
832
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
513
833
|
return this.request("/api/search/text", {
|
|
514
834
|
method: "POST",
|
|
515
835
|
headers: { "Content-Type": "application/json" },
|
|
516
836
|
body: JSON.stringify({
|
|
517
837
|
query: params.query,
|
|
518
|
-
...
|
|
519
|
-
...params.limit ? { limit: params.limit } : {}
|
|
838
|
+
...bucket ? { bucket } : {},
|
|
839
|
+
...params.limit ? { limit: params.limit } : {},
|
|
840
|
+
...params.filters ? { filters: params.filters } : {},
|
|
841
|
+
...params.include?.length ? { include: params.include } : {}
|
|
842
|
+
})
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
searchColor(params) {
|
|
846
|
+
const bucket = this.resolveBucket(params.bucket);
|
|
847
|
+
return this.request("/api/search/color", {
|
|
848
|
+
method: "POST",
|
|
849
|
+
headers: { "Content-Type": "application/json" },
|
|
850
|
+
body: JSON.stringify({
|
|
851
|
+
...params.hex ? { hex: params.hex } : {},
|
|
852
|
+
...params.oklab ? { oklab: params.oklab } : {},
|
|
853
|
+
...bucket ? { bucket } : {},
|
|
854
|
+
...params.limit ? { limit: params.limit } : {},
|
|
855
|
+
...params.minProportion !== void 0 ? { minProportion: params.minProportion } : {},
|
|
856
|
+
...params.dominantOnly ? { dominantOnly: params.dominantOnly } : {}
|
|
520
857
|
})
|
|
521
858
|
});
|
|
522
859
|
}
|
|
@@ -557,7 +894,7 @@ var StowServer = class {
|
|
|
557
894
|
throw new StowError("Failed to upload to storage", putRes.status);
|
|
558
895
|
}
|
|
559
896
|
return this.request(
|
|
560
|
-
"/api/drops/presign/confirm",
|
|
897
|
+
presign.confirmUrl || "/api/drops/presign/confirm",
|
|
561
898
|
{
|
|
562
899
|
method: "POST",
|
|
563
900
|
headers: { "Content-Type": "application/json" },
|
|
@@ -599,7 +936,12 @@ var StowServer = class {
|
|
|
599
936
|
get: (id) => this.getProfile(id),
|
|
600
937
|
delete: (id) => this.deleteProfile(id),
|
|
601
938
|
addFiles: (id, fileKeys, bucket) => this.addProfileFiles(id, fileKeys, bucket),
|
|
602
|
-
removeFiles: (id, fileKeys, bucket) => this.removeProfileFiles(id, fileKeys, bucket)
|
|
939
|
+
removeFiles: (id, fileKeys, bucket) => this.removeProfileFiles(id, fileKeys, bucket),
|
|
940
|
+
signal: (id, signals, bucket) => this.signalProfile(id, signals, bucket),
|
|
941
|
+
deleteSignals: (id, signalIds) => this.deleteProfileSignals(id, signalIds),
|
|
942
|
+
clusters: (id) => this.getProfileClusters(id),
|
|
943
|
+
recluster: (id, params) => this.reclusterProfile(id, params),
|
|
944
|
+
renameCluster: (profileId, clusterId, params) => this.renameProfileCluster(profileId, clusterId, params)
|
|
603
945
|
};
|
|
604
946
|
}
|
|
605
947
|
createProfile(params) {
|
|
@@ -657,6 +999,55 @@ var StowServer = class {
|
|
|
657
999
|
profileFilesResultSchema
|
|
658
1000
|
);
|
|
659
1001
|
}
|
|
1002
|
+
signalProfile(id, signals, bucket) {
|
|
1003
|
+
return this.request(
|
|
1004
|
+
`/api/profiles/${encodeURIComponent(id)}/signals`,
|
|
1005
|
+
{
|
|
1006
|
+
method: "POST",
|
|
1007
|
+
headers: { "Content-Type": "application/json" },
|
|
1008
|
+
body: JSON.stringify({
|
|
1009
|
+
signals,
|
|
1010
|
+
...bucket ? { bucket } : {}
|
|
1011
|
+
})
|
|
1012
|
+
},
|
|
1013
|
+
profileSignalsResponseSchema
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
deleteProfileSignals(id, signalIds) {
|
|
1017
|
+
return this.request(
|
|
1018
|
+
`/api/profiles/${encodeURIComponent(id)}/signals`,
|
|
1019
|
+
{
|
|
1020
|
+
method: "DELETE",
|
|
1021
|
+
headers: { "Content-Type": "application/json" },
|
|
1022
|
+
body: JSON.stringify({ signalIds })
|
|
1023
|
+
},
|
|
1024
|
+
deleteProfileSignalsResponseSchema
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
getProfileClusters(id) {
|
|
1028
|
+
return this.request(`/api/profiles/${encodeURIComponent(id)}/clusters`, {
|
|
1029
|
+
method: "GET"
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
reclusterProfile(id, params) {
|
|
1033
|
+
return this.request(`/api/profiles/${encodeURIComponent(id)}/clusters`, {
|
|
1034
|
+
method: "POST",
|
|
1035
|
+
headers: { "Content-Type": "application/json" },
|
|
1036
|
+
body: JSON.stringify({
|
|
1037
|
+
...params?.clusterCount !== void 0 ? { clusterCount: params.clusterCount } : {}
|
|
1038
|
+
})
|
|
1039
|
+
});
|
|
1040
|
+
}
|
|
1041
|
+
renameProfileCluster(profileId, clusterId, params) {
|
|
1042
|
+
return this.request(
|
|
1043
|
+
`/api/profiles/${encodeURIComponent(profileId)}/clusters/${encodeURIComponent(clusterId)}`,
|
|
1044
|
+
{
|
|
1045
|
+
method: "PUT",
|
|
1046
|
+
headers: { "Content-Type": "application/json" },
|
|
1047
|
+
body: JSON.stringify(params)
|
|
1048
|
+
}
|
|
1049
|
+
);
|
|
1050
|
+
}
|
|
660
1051
|
};
|
|
661
1052
|
// Annotate the CommonJS export names for ESM import in node:
|
|
662
1053
|
0 && (module.exports = {
|