@howells/stow-server 0.3.2 → 0.6.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 CHANGED
@@ -48,6 +48,11 @@ interface UploadResult {
48
48
  size: number;
49
49
  url: string | null;
50
50
  }
51
+ /** Response returned when an upload is queued for async processing. */
52
+ interface QueuedResult {
53
+ jobId: string;
54
+ status: "queued";
55
+ }
51
56
  /** Bucket metadata returned by bucket APIs. */
52
57
  interface BucketResult {
53
58
  allowedTypes?: string[] | null;
@@ -173,12 +178,18 @@ interface FileColorProfile {
173
178
  interface ListFilesItem {
174
179
  colorProfile?: FileColorProfile | null;
175
180
  colors?: FileColor[];
181
+ contentType?: string;
176
182
  duration?: number | null;
183
+ embeddingStatus?: string | null;
177
184
  height?: number | null;
178
185
  key: string;
179
186
  lastModified: string;
180
187
  metadata?: Record<string, string>;
181
188
  size: number;
189
+ /** Tags attached to this file. Present when `include: ["tags"]` is requested. */
190
+ tags?: FileTag[];
191
+ /** Taxonomy classifications. Present when `include: ["taxonomies"]` is requested. */
192
+ taxonomies?: FileTaxonomy[];
182
193
  url: string | null;
183
194
  width?: number | null;
184
195
  }
@@ -208,6 +219,25 @@ interface ListDropsResult {
208
219
  limit: number;
209
220
  };
210
221
  }
222
+ /** A tag attached to a file, returned when `include: ["tags"]` is requested. */
223
+ interface FileTag {
224
+ color: string | null;
225
+ id: string;
226
+ name: string;
227
+ slug: string;
228
+ }
229
+ /** A taxonomy classification for a file, returned when `include: ["taxonomies"]` is requested. */
230
+ interface FileTaxonomy {
231
+ group: {
232
+ id: string;
233
+ name: string;
234
+ slug: string;
235
+ };
236
+ id: string;
237
+ name: string;
238
+ similarity: number;
239
+ slug: string;
240
+ }
211
241
  /** Full file detail payload returned by `getFile()`. */
212
242
  interface FileResult {
213
243
  colorProfile: FileColorProfile | null;
@@ -220,6 +250,10 @@ interface FileResult {
220
250
  key: string;
221
251
  metadata: Record<string, string> | null;
222
252
  size: number;
253
+ /** Tags attached to this file. Present when `include: ["tags"]` is requested. */
254
+ tags?: FileTag[];
255
+ /** Taxonomy classifications. Present when `include: ["taxonomies"]` is requested. */
256
+ taxonomies?: FileTaxonomy[];
223
257
  url: string | null;
224
258
  width: number | null;
225
259
  }
@@ -350,6 +384,8 @@ interface SearchFilters {
350
384
  }
351
385
  /** Optional enrichment blocks that can be included in search responses. */
352
386
  type SearchIncludeField = "tags" | "taxonomies" | "colors" | "colorProfile";
387
+ /** Optional enrichment blocks that can be included in file get/list responses. */
388
+ type FileIncludeField = "tags" | "taxonomies";
353
389
  /** Input payload for text-based semantic search. */
354
390
  interface TextSearchRequest {
355
391
  bucket?: string;
@@ -637,6 +673,25 @@ declare class StowServer {
637
673
  /** Request AI-generated alt text for images */
638
674
  altText?: boolean;
639
675
  }): Promise<UploadResult>;
676
+ /**
677
+ * Queue a URL upload for async processing via BullMQ.
678
+ *
679
+ * Unlike `uploadFromUrl()`, this returns immediately with a job ID.
680
+ * The actual fetch, validation, and upload happen in a background worker.
681
+ * Use this for bulk imports where you don't need immediate confirmation.
682
+ */
683
+ queueUploadFromUrl(url: string, filename: string, options?: {
684
+ bucket?: string;
685
+ metadata?: Record<string, string>;
686
+ /** Headers to forward when fetching the URL (e.g. User-Agent, Referer) */
687
+ headers?: Record<string, string>;
688
+ /** Request AI-generated title for images */
689
+ title?: boolean;
690
+ /** Request AI-generated description for images */
691
+ describe?: boolean;
692
+ /** Request AI-generated alt text for images */
693
+ altText?: boolean;
694
+ }): Promise<QueuedResult>;
640
695
  /**
641
696
  * Get a presigned URL for direct client-side upload.
642
697
  *
@@ -654,6 +709,8 @@ declare class StowServer {
654
709
  confirmUpload(request: ConfirmUploadRequest): Promise<UploadResult>;
655
710
  /**
656
711
  * List files in the bucket
712
+ *
713
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
657
714
  */
658
715
  listFiles(options?: {
659
716
  prefix?: string;
@@ -662,6 +719,8 @@ declare class StowServer {
662
719
  bucket?: string;
663
720
  /** Filter by tag slug */
664
721
  tag?: string;
722
+ /** Enrichment fields to include in each file item */
723
+ include?: FileIncludeField[];
665
724
  }): Promise<ListFilesResult>;
666
725
  /**
667
726
  * Delete a file by key
@@ -680,9 +739,12 @@ declare class StowServer {
680
739
  }>;
681
740
  /**
682
741
  * Get a single file by key
742
+ *
743
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
683
744
  */
684
745
  getFile(key: string, options?: {
685
746
  bucket?: string;
747
+ include?: FileIncludeField[];
686
748
  }): Promise<FileResult>;
687
749
  /**
688
750
  * Extract color palette from an image file.
@@ -704,6 +766,27 @@ declare class StowServer {
704
766
  embed(key: string, options?: {
705
767
  bucket?: string;
706
768
  }): Promise<TaskTriggerResult>;
769
+ /**
770
+ * Generate a title for an image file using AI vision.
771
+ * Requires a searchable bucket.
772
+ */
773
+ generateTitle(key: string, options?: {
774
+ bucket?: string;
775
+ }): Promise<TaskTriggerResult>;
776
+ /**
777
+ * Generate a description for an image file using AI vision.
778
+ * Requires a searchable bucket.
779
+ */
780
+ generateDescription(key: string, options?: {
781
+ bucket?: string;
782
+ }): Promise<TaskTriggerResult>;
783
+ /**
784
+ * Generate alt text for an image file using AI vision.
785
+ * Requires a searchable bucket.
786
+ */
787
+ generateAltText(key: string, options?: {
788
+ bucket?: string;
789
+ }): Promise<TaskTriggerResult>;
707
790
  /**
708
791
  * Replace a file's content by fetching from a new URL.
709
792
  *
@@ -835,4 +918,4 @@ declare class StowServer {
835
918
  private renameProfileCluster;
836
919
  }
837
920
 
838
- export { type BucketResult, type ColorSearchRequest, type ColorSearchResult, type ColorSearchResultItem, type ConfirmUploadRequest, type CreateBucketRequest, type DeleteProfileSignalsResult, type DiverseSearchRequest, type Drop, type DropResult, type FileColor, type FileColorProfile, type FileResult, type FilteredMetadata, type ListBucketsResult, type ListDropsResult, type ListFilesItem, type ListFilesResult, type PresignDedupeResult, type PresignNewResult, type PresignRequest, type PresignResult, type ProfileClusterResult, type ProfileCreateRequest, type ProfileFilesResult, type ProfileResult, type ProfileSignalInput, type ProfileSignalResult, type ProfileSignalType, type ProfileSignalsResponse, type ReclusterRequest, type ReclusterResult, type RenameClusterRequest, type ReplaceResult, type SearchFilters, type SearchIncludeField, type SearchResultItem, type SimilarSearchRequest, type SimilarSearchResult, StowError, StowServer, type StowServerConfig, type TaskTriggerResult, type TextSearchRequest, type TransformOptions, type UpdateBucketRequest, type UploadResult, type WhoamiResult };
921
+ export { type BucketResult, type ColorSearchRequest, type ColorSearchResult, type ColorSearchResultItem, type ConfirmUploadRequest, type CreateBucketRequest, type DeleteProfileSignalsResult, type DiverseSearchRequest, type Drop, type DropResult, type FileColor, type FileColorProfile, type FileIncludeField, type FileResult, type FileTag, type FileTaxonomy, type FilteredMetadata, type ListBucketsResult, type ListDropsResult, type ListFilesItem, type ListFilesResult, type PresignDedupeResult, type PresignNewResult, type PresignRequest, type PresignResult, type ProfileClusterResult, type ProfileCreateRequest, type ProfileFilesResult, type ProfileResult, type ProfileSignalInput, type ProfileSignalResult, type ProfileSignalType, type ProfileSignalsResponse, type QueuedResult, type ReclusterRequest, type ReclusterResult, type RenameClusterRequest, type ReplaceResult, type SearchFilters, type SearchIncludeField, type SearchResultItem, type SimilarSearchRequest, type SimilarSearchResult, StowError, StowServer, type StowServerConfig, type TaskTriggerResult, type TextSearchRequest, type TransformOptions, type UpdateBucketRequest, type UploadResult, type WhoamiResult };
package/dist/index.d.ts CHANGED
@@ -48,6 +48,11 @@ interface UploadResult {
48
48
  size: number;
49
49
  url: string | null;
50
50
  }
51
+ /** Response returned when an upload is queued for async processing. */
52
+ interface QueuedResult {
53
+ jobId: string;
54
+ status: "queued";
55
+ }
51
56
  /** Bucket metadata returned by bucket APIs. */
52
57
  interface BucketResult {
53
58
  allowedTypes?: string[] | null;
@@ -173,12 +178,18 @@ interface FileColorProfile {
173
178
  interface ListFilesItem {
174
179
  colorProfile?: FileColorProfile | null;
175
180
  colors?: FileColor[];
181
+ contentType?: string;
176
182
  duration?: number | null;
183
+ embeddingStatus?: string | null;
177
184
  height?: number | null;
178
185
  key: string;
179
186
  lastModified: string;
180
187
  metadata?: Record<string, string>;
181
188
  size: number;
189
+ /** Tags attached to this file. Present when `include: ["tags"]` is requested. */
190
+ tags?: FileTag[];
191
+ /** Taxonomy classifications. Present when `include: ["taxonomies"]` is requested. */
192
+ taxonomies?: FileTaxonomy[];
182
193
  url: string | null;
183
194
  width?: number | null;
184
195
  }
@@ -208,6 +219,25 @@ interface ListDropsResult {
208
219
  limit: number;
209
220
  };
210
221
  }
222
+ /** A tag attached to a file, returned when `include: ["tags"]` is requested. */
223
+ interface FileTag {
224
+ color: string | null;
225
+ id: string;
226
+ name: string;
227
+ slug: string;
228
+ }
229
+ /** A taxonomy classification for a file, returned when `include: ["taxonomies"]` is requested. */
230
+ interface FileTaxonomy {
231
+ group: {
232
+ id: string;
233
+ name: string;
234
+ slug: string;
235
+ };
236
+ id: string;
237
+ name: string;
238
+ similarity: number;
239
+ slug: string;
240
+ }
211
241
  /** Full file detail payload returned by `getFile()`. */
212
242
  interface FileResult {
213
243
  colorProfile: FileColorProfile | null;
@@ -220,6 +250,10 @@ interface FileResult {
220
250
  key: string;
221
251
  metadata: Record<string, string> | null;
222
252
  size: number;
253
+ /** Tags attached to this file. Present when `include: ["tags"]` is requested. */
254
+ tags?: FileTag[];
255
+ /** Taxonomy classifications. Present when `include: ["taxonomies"]` is requested. */
256
+ taxonomies?: FileTaxonomy[];
223
257
  url: string | null;
224
258
  width: number | null;
225
259
  }
@@ -350,6 +384,8 @@ interface SearchFilters {
350
384
  }
351
385
  /** Optional enrichment blocks that can be included in search responses. */
352
386
  type SearchIncludeField = "tags" | "taxonomies" | "colors" | "colorProfile";
387
+ /** Optional enrichment blocks that can be included in file get/list responses. */
388
+ type FileIncludeField = "tags" | "taxonomies";
353
389
  /** Input payload for text-based semantic search. */
354
390
  interface TextSearchRequest {
355
391
  bucket?: string;
@@ -637,6 +673,25 @@ declare class StowServer {
637
673
  /** Request AI-generated alt text for images */
638
674
  altText?: boolean;
639
675
  }): Promise<UploadResult>;
676
+ /**
677
+ * Queue a URL upload for async processing via BullMQ.
678
+ *
679
+ * Unlike `uploadFromUrl()`, this returns immediately with a job ID.
680
+ * The actual fetch, validation, and upload happen in a background worker.
681
+ * Use this for bulk imports where you don't need immediate confirmation.
682
+ */
683
+ queueUploadFromUrl(url: string, filename: string, options?: {
684
+ bucket?: string;
685
+ metadata?: Record<string, string>;
686
+ /** Headers to forward when fetching the URL (e.g. User-Agent, Referer) */
687
+ headers?: Record<string, string>;
688
+ /** Request AI-generated title for images */
689
+ title?: boolean;
690
+ /** Request AI-generated description for images */
691
+ describe?: boolean;
692
+ /** Request AI-generated alt text for images */
693
+ altText?: boolean;
694
+ }): Promise<QueuedResult>;
640
695
  /**
641
696
  * Get a presigned URL for direct client-side upload.
642
697
  *
@@ -654,6 +709,8 @@ declare class StowServer {
654
709
  confirmUpload(request: ConfirmUploadRequest): Promise<UploadResult>;
655
710
  /**
656
711
  * List files in the bucket
712
+ *
713
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
657
714
  */
658
715
  listFiles(options?: {
659
716
  prefix?: string;
@@ -662,6 +719,8 @@ declare class StowServer {
662
719
  bucket?: string;
663
720
  /** Filter by tag slug */
664
721
  tag?: string;
722
+ /** Enrichment fields to include in each file item */
723
+ include?: FileIncludeField[];
665
724
  }): Promise<ListFilesResult>;
666
725
  /**
667
726
  * Delete a file by key
@@ -680,9 +739,12 @@ declare class StowServer {
680
739
  }>;
681
740
  /**
682
741
  * Get a single file by key
742
+ *
743
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
683
744
  */
684
745
  getFile(key: string, options?: {
685
746
  bucket?: string;
747
+ include?: FileIncludeField[];
686
748
  }): Promise<FileResult>;
687
749
  /**
688
750
  * Extract color palette from an image file.
@@ -704,6 +766,27 @@ declare class StowServer {
704
766
  embed(key: string, options?: {
705
767
  bucket?: string;
706
768
  }): Promise<TaskTriggerResult>;
769
+ /**
770
+ * Generate a title for an image file using AI vision.
771
+ * Requires a searchable bucket.
772
+ */
773
+ generateTitle(key: string, options?: {
774
+ bucket?: string;
775
+ }): Promise<TaskTriggerResult>;
776
+ /**
777
+ * Generate a description for an image file using AI vision.
778
+ * Requires a searchable bucket.
779
+ */
780
+ generateDescription(key: string, options?: {
781
+ bucket?: string;
782
+ }): Promise<TaskTriggerResult>;
783
+ /**
784
+ * Generate alt text for an image file using AI vision.
785
+ * Requires a searchable bucket.
786
+ */
787
+ generateAltText(key: string, options?: {
788
+ bucket?: string;
789
+ }): Promise<TaskTriggerResult>;
707
790
  /**
708
791
  * Replace a file's content by fetching from a new URL.
709
792
  *
@@ -835,4 +918,4 @@ declare class StowServer {
835
918
  private renameProfileCluster;
836
919
  }
837
920
 
838
- export { type BucketResult, type ColorSearchRequest, type ColorSearchResult, type ColorSearchResultItem, type ConfirmUploadRequest, type CreateBucketRequest, type DeleteProfileSignalsResult, type DiverseSearchRequest, type Drop, type DropResult, type FileColor, type FileColorProfile, type FileResult, type FilteredMetadata, type ListBucketsResult, type ListDropsResult, type ListFilesItem, type ListFilesResult, type PresignDedupeResult, type PresignNewResult, type PresignRequest, type PresignResult, type ProfileClusterResult, type ProfileCreateRequest, type ProfileFilesResult, type ProfileResult, type ProfileSignalInput, type ProfileSignalResult, type ProfileSignalType, type ProfileSignalsResponse, type ReclusterRequest, type ReclusterResult, type RenameClusterRequest, type ReplaceResult, type SearchFilters, type SearchIncludeField, type SearchResultItem, type SimilarSearchRequest, type SimilarSearchResult, StowError, StowServer, type StowServerConfig, type TaskTriggerResult, type TextSearchRequest, type TransformOptions, type UpdateBucketRequest, type UploadResult, type WhoamiResult };
921
+ export { type BucketResult, type ColorSearchRequest, type ColorSearchResult, type ColorSearchResultItem, type ConfirmUploadRequest, type CreateBucketRequest, type DeleteProfileSignalsResult, type DiverseSearchRequest, type Drop, type DropResult, type FileColor, type FileColorProfile, type FileIncludeField, type FileResult, type FileTag, type FileTaxonomy, type FilteredMetadata, type ListBucketsResult, type ListDropsResult, type ListFilesItem, type ListFilesResult, type PresignDedupeResult, type PresignNewResult, type PresignRequest, type PresignResult, type ProfileClusterResult, type ProfileCreateRequest, type ProfileFilesResult, type ProfileResult, type ProfileSignalInput, type ProfileSignalResult, type ProfileSignalType, type ProfileSignalsResponse, type QueuedResult, type ReclusterRequest, type ReclusterResult, type RenameClusterRequest, type ReplaceResult, type SearchFilters, type SearchIncludeField, type SearchResultItem, type SimilarSearchRequest, type SimilarSearchResult, StowError, StowServer, type StowServerConfig, type TaskTriggerResult, type TextSearchRequest, type TransformOptions, type UpdateBucketRequest, type UploadResult, type WhoamiResult };
package/dist/index.js CHANGED
@@ -72,6 +72,10 @@ var uploadResultSchema = import_zod.z.object({
72
72
  metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
73
73
  deduped: import_zod.z.boolean().optional()
74
74
  });
75
+ var queuedResultSchema = import_zod.z.object({
76
+ status: import_zod.z.literal("queued"),
77
+ jobId: import_zod.z.string()
78
+ });
75
79
  var bucketSchema = import_zod.z.object({
76
80
  id: import_zod.z.string(),
77
81
  name: import_zod.z.string(),
@@ -105,6 +109,23 @@ var whoamiSchema = import_zod.z.object({
105
109
  permissions: import_zod.z.record(import_zod.z.string(), import_zod.z.boolean())
106
110
  }).optional()
107
111
  });
112
+ var fileTagSchema = import_zod.z.object({
113
+ id: import_zod.z.string(),
114
+ name: import_zod.z.string(),
115
+ slug: import_zod.z.string(),
116
+ color: import_zod.z.string().nullable()
117
+ });
118
+ var fileTaxonomySchema = import_zod.z.object({
119
+ id: import_zod.z.string(),
120
+ name: import_zod.z.string(),
121
+ slug: import_zod.z.string(),
122
+ similarity: import_zod.z.number(),
123
+ group: import_zod.z.object({
124
+ id: import_zod.z.string(),
125
+ name: import_zod.z.string(),
126
+ slug: import_zod.z.string()
127
+ })
128
+ });
108
129
  var listFilesSchema = import_zod.z.object({
109
130
  files: import_zod.z.array(
110
131
  import_zod.z.object({
@@ -112,12 +133,16 @@ var listFilesSchema = import_zod.z.object({
112
133
  size: import_zod.z.number(),
113
134
  lastModified: import_zod.z.string(),
114
135
  url: import_zod.z.string().nullable(),
136
+ contentType: import_zod.z.string().optional(),
115
137
  width: import_zod.z.number().nullable().optional(),
116
138
  height: import_zod.z.number().nullable().optional(),
117
139
  duration: import_zod.z.number().nullable().optional(),
140
+ embeddingStatus: import_zod.z.string().nullable().optional(),
118
141
  metadata: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
119
142
  colorProfile: fileColorProfileSchema.nullable().optional(),
120
- colors: import_zod.z.array(fileColorSchema).optional()
143
+ colors: import_zod.z.array(fileColorSchema).optional(),
144
+ tags: import_zod.z.array(fileTagSchema).optional(),
145
+ taxonomies: import_zod.z.array(fileTaxonomySchema).optional()
121
146
  })
122
147
  ),
123
148
  nextCursor: import_zod.z.string().nullable()
@@ -192,7 +217,9 @@ var fileResultSchema = import_zod.z.object({
192
217
  colorProfile: fileColorProfileSchema.nullable(),
193
218
  colors: import_zod.z.array(fileColorSchema),
194
219
  embeddingStatus: import_zod.z.string().nullable(),
195
- createdAt: import_zod.z.string()
220
+ createdAt: import_zod.z.string(),
221
+ tags: import_zod.z.array(fileTagSchema).optional(),
222
+ taxonomies: import_zod.z.array(fileTaxonomySchema).optional()
196
223
  });
197
224
  var profileClusterResultSchema = import_zod.z.object({
198
225
  id: import_zod.z.string(),
@@ -540,6 +567,33 @@ var StowServer = class {
540
567
  ...result.metadata ? { metadata: result.metadata } : {}
541
568
  };
542
569
  }
570
+ /**
571
+ * Queue a URL upload for async processing via BullMQ.
572
+ *
573
+ * Unlike `uploadFromUrl()`, this returns immediately with a job ID.
574
+ * The actual fetch, validation, and upload happen in a background worker.
575
+ * Use this for bulk imports where you don't need immediate confirmation.
576
+ */
577
+ async queueUploadFromUrl(url, filename, options) {
578
+ return this.request(
579
+ this.withBucket("/api/upload", options?.bucket),
580
+ {
581
+ method: "POST",
582
+ headers: { "Content-Type": "application/json" },
583
+ body: JSON.stringify({
584
+ url,
585
+ filename,
586
+ async: true,
587
+ ...options?.metadata ? { metadata: options.metadata } : {},
588
+ ...options?.headers ? { headers: options.headers } : {},
589
+ ...options?.title ? { title: true } : {},
590
+ ...options?.describe ? { describe: true } : {},
591
+ ...options?.altText ? { altText: true } : {}
592
+ })
593
+ },
594
+ queuedResultSchema
595
+ );
596
+ }
543
597
  /**
544
598
  * Get a presigned URL for direct client-side upload.
545
599
  *
@@ -615,6 +669,8 @@ var StowServer = class {
615
669
  }
616
670
  /**
617
671
  * List files in the bucket
672
+ *
673
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
618
674
  */
619
675
  listFiles(options) {
620
676
  const params = new URLSearchParams();
@@ -630,6 +686,9 @@ var StowServer = class {
630
686
  if (options?.tag) {
631
687
  params.set("tag", options.tag);
632
688
  }
689
+ if (options?.include?.length) {
690
+ params.set("include", options.include.join(","));
691
+ }
633
692
  const path = `/api/files?${params}`;
634
693
  return this.request(
635
694
  this.withBucket(path, options?.bucket),
@@ -659,9 +718,16 @@ var StowServer = class {
659
718
  }
660
719
  /**
661
720
  * Get a single file by key
721
+ *
722
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
662
723
  */
663
724
  getFile(key, options) {
664
- const path = `/api/files/${encodeURIComponent(key)}`;
725
+ const params = new URLSearchParams();
726
+ if (options?.include?.length) {
727
+ params.set("include", options.include.join(","));
728
+ }
729
+ const qs = params.toString();
730
+ const path = `/api/files/${encodeURIComponent(key)}${qs ? `?${qs}` : ""}`;
665
731
  return this.request(
666
732
  this.withBucket(path, options?.bucket),
667
733
  { method: "GET" },
@@ -673,7 +739,7 @@ var StowServer = class {
673
739
  * Requires a searchable bucket.
674
740
  */
675
741
  extractColors(key, options) {
676
- const path = `/api/files/${encodeURIComponent(key)}/extract-colors`;
742
+ const path = `/api/files/${encodeURIComponent(key)}/colors`;
677
743
  return this.request(
678
744
  this.withBucket(path, options?.bucket),
679
745
  { method: "POST" },
@@ -684,7 +750,7 @@ var StowServer = class {
684
750
  * Extract dimensions (width/height) from an image or video file.
685
751
  */
686
752
  extractDimensions(key, options) {
687
- const path = `/api/files/${encodeURIComponent(key)}/extract-dimensions`;
753
+ const path = `/api/files/${encodeURIComponent(key)}/dimensions`;
688
754
  return this.request(
689
755
  this.withBucket(path, options?.bucket),
690
756
  { method: "POST" },
@@ -696,7 +762,43 @@ var StowServer = class {
696
762
  * Requires a searchable bucket.
697
763
  */
698
764
  embed(key, options) {
699
- const path = `/api/files/${encodeURIComponent(key)}/embed`;
765
+ const path = `/api/files/${encodeURIComponent(key)}/embedding`;
766
+ return this.request(
767
+ this.withBucket(path, options?.bucket),
768
+ { method: "POST" },
769
+ taskTriggerResultSchema
770
+ );
771
+ }
772
+ /**
773
+ * Generate a title for an image file using AI vision.
774
+ * Requires a searchable bucket.
775
+ */
776
+ generateTitle(key, options) {
777
+ const path = `/api/files/${encodeURIComponent(key)}/title`;
778
+ return this.request(
779
+ this.withBucket(path, options?.bucket),
780
+ { method: "POST" },
781
+ taskTriggerResultSchema
782
+ );
783
+ }
784
+ /**
785
+ * Generate a description for an image file using AI vision.
786
+ * Requires a searchable bucket.
787
+ */
788
+ generateDescription(key, options) {
789
+ const path = `/api/files/${encodeURIComponent(key)}/description`;
790
+ return this.request(
791
+ this.withBucket(path, options?.bucket),
792
+ { method: "POST" },
793
+ taskTriggerResultSchema
794
+ );
795
+ }
796
+ /**
797
+ * Generate alt text for an image file using AI vision.
798
+ * Requires a searchable bucket.
799
+ */
800
+ generateAltText(key, options) {
801
+ const path = `/api/files/${encodeURIComponent(key)}/alt-text`;
700
802
  return this.request(
701
803
  this.withBucket(path, options?.bucket),
702
804
  { method: "POST" },
package/dist/index.mjs CHANGED
@@ -47,6 +47,10 @@ var uploadResultSchema = z.object({
47
47
  metadata: z.record(z.string(), z.string()).optional(),
48
48
  deduped: z.boolean().optional()
49
49
  });
50
+ var queuedResultSchema = z.object({
51
+ status: z.literal("queued"),
52
+ jobId: z.string()
53
+ });
50
54
  var bucketSchema = z.object({
51
55
  id: z.string(),
52
56
  name: z.string(),
@@ -80,6 +84,23 @@ var whoamiSchema = z.object({
80
84
  permissions: z.record(z.string(), z.boolean())
81
85
  }).optional()
82
86
  });
87
+ var fileTagSchema = z.object({
88
+ id: z.string(),
89
+ name: z.string(),
90
+ slug: z.string(),
91
+ color: z.string().nullable()
92
+ });
93
+ var fileTaxonomySchema = z.object({
94
+ id: z.string(),
95
+ name: z.string(),
96
+ slug: z.string(),
97
+ similarity: z.number(),
98
+ group: z.object({
99
+ id: z.string(),
100
+ name: z.string(),
101
+ slug: z.string()
102
+ })
103
+ });
83
104
  var listFilesSchema = z.object({
84
105
  files: z.array(
85
106
  z.object({
@@ -87,12 +108,16 @@ var listFilesSchema = z.object({
87
108
  size: z.number(),
88
109
  lastModified: z.string(),
89
110
  url: z.string().nullable(),
111
+ contentType: z.string().optional(),
90
112
  width: z.number().nullable().optional(),
91
113
  height: z.number().nullable().optional(),
92
114
  duration: z.number().nullable().optional(),
115
+ embeddingStatus: z.string().nullable().optional(),
93
116
  metadata: z.record(z.string(), z.string()).optional(),
94
117
  colorProfile: fileColorProfileSchema.nullable().optional(),
95
- colors: z.array(fileColorSchema).optional()
118
+ colors: z.array(fileColorSchema).optional(),
119
+ tags: z.array(fileTagSchema).optional(),
120
+ taxonomies: z.array(fileTaxonomySchema).optional()
96
121
  })
97
122
  ),
98
123
  nextCursor: z.string().nullable()
@@ -167,7 +192,9 @@ var fileResultSchema = z.object({
167
192
  colorProfile: fileColorProfileSchema.nullable(),
168
193
  colors: z.array(fileColorSchema),
169
194
  embeddingStatus: z.string().nullable(),
170
- createdAt: z.string()
195
+ createdAt: z.string(),
196
+ tags: z.array(fileTagSchema).optional(),
197
+ taxonomies: z.array(fileTaxonomySchema).optional()
171
198
  });
172
199
  var profileClusterResultSchema = z.object({
173
200
  id: z.string(),
@@ -515,6 +542,33 @@ var StowServer = class {
515
542
  ...result.metadata ? { metadata: result.metadata } : {}
516
543
  };
517
544
  }
545
+ /**
546
+ * Queue a URL upload for async processing via BullMQ.
547
+ *
548
+ * Unlike `uploadFromUrl()`, this returns immediately with a job ID.
549
+ * The actual fetch, validation, and upload happen in a background worker.
550
+ * Use this for bulk imports where you don't need immediate confirmation.
551
+ */
552
+ async queueUploadFromUrl(url, filename, options) {
553
+ return this.request(
554
+ this.withBucket("/api/upload", options?.bucket),
555
+ {
556
+ method: "POST",
557
+ headers: { "Content-Type": "application/json" },
558
+ body: JSON.stringify({
559
+ url,
560
+ filename,
561
+ async: true,
562
+ ...options?.metadata ? { metadata: options.metadata } : {},
563
+ ...options?.headers ? { headers: options.headers } : {},
564
+ ...options?.title ? { title: true } : {},
565
+ ...options?.describe ? { describe: true } : {},
566
+ ...options?.altText ? { altText: true } : {}
567
+ })
568
+ },
569
+ queuedResultSchema
570
+ );
571
+ }
518
572
  /**
519
573
  * Get a presigned URL for direct client-side upload.
520
574
  *
@@ -590,6 +644,8 @@ var StowServer = class {
590
644
  }
591
645
  /**
592
646
  * List files in the bucket
647
+ *
648
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
593
649
  */
594
650
  listFiles(options) {
595
651
  const params = new URLSearchParams();
@@ -605,6 +661,9 @@ var StowServer = class {
605
661
  if (options?.tag) {
606
662
  params.set("tag", options.tag);
607
663
  }
664
+ if (options?.include?.length) {
665
+ params.set("include", options.include.join(","));
666
+ }
608
667
  const path = `/api/files?${params}`;
609
668
  return this.request(
610
669
  this.withBucket(path, options?.bucket),
@@ -634,9 +693,16 @@ var StowServer = class {
634
693
  }
635
694
  /**
636
695
  * Get a single file by key
696
+ *
697
+ * @param options.include - Optional enrichment fields: `"tags"`, `"taxonomies"`
637
698
  */
638
699
  getFile(key, options) {
639
- const path = `/api/files/${encodeURIComponent(key)}`;
700
+ const params = new URLSearchParams();
701
+ if (options?.include?.length) {
702
+ params.set("include", options.include.join(","));
703
+ }
704
+ const qs = params.toString();
705
+ const path = `/api/files/${encodeURIComponent(key)}${qs ? `?${qs}` : ""}`;
640
706
  return this.request(
641
707
  this.withBucket(path, options?.bucket),
642
708
  { method: "GET" },
@@ -648,7 +714,7 @@ var StowServer = class {
648
714
  * Requires a searchable bucket.
649
715
  */
650
716
  extractColors(key, options) {
651
- const path = `/api/files/${encodeURIComponent(key)}/extract-colors`;
717
+ const path = `/api/files/${encodeURIComponent(key)}/colors`;
652
718
  return this.request(
653
719
  this.withBucket(path, options?.bucket),
654
720
  { method: "POST" },
@@ -659,7 +725,7 @@ var StowServer = class {
659
725
  * Extract dimensions (width/height) from an image or video file.
660
726
  */
661
727
  extractDimensions(key, options) {
662
- const path = `/api/files/${encodeURIComponent(key)}/extract-dimensions`;
728
+ const path = `/api/files/${encodeURIComponent(key)}/dimensions`;
663
729
  return this.request(
664
730
  this.withBucket(path, options?.bucket),
665
731
  { method: "POST" },
@@ -671,7 +737,43 @@ var StowServer = class {
671
737
  * Requires a searchable bucket.
672
738
  */
673
739
  embed(key, options) {
674
- const path = `/api/files/${encodeURIComponent(key)}/embed`;
740
+ const path = `/api/files/${encodeURIComponent(key)}/embedding`;
741
+ return this.request(
742
+ this.withBucket(path, options?.bucket),
743
+ { method: "POST" },
744
+ taskTriggerResultSchema
745
+ );
746
+ }
747
+ /**
748
+ * Generate a title for an image file using AI vision.
749
+ * Requires a searchable bucket.
750
+ */
751
+ generateTitle(key, options) {
752
+ const path = `/api/files/${encodeURIComponent(key)}/title`;
753
+ return this.request(
754
+ this.withBucket(path, options?.bucket),
755
+ { method: "POST" },
756
+ taskTriggerResultSchema
757
+ );
758
+ }
759
+ /**
760
+ * Generate a description for an image file using AI vision.
761
+ * Requires a searchable bucket.
762
+ */
763
+ generateDescription(key, options) {
764
+ const path = `/api/files/${encodeURIComponent(key)}/description`;
765
+ return this.request(
766
+ this.withBucket(path, options?.bucket),
767
+ { method: "POST" },
768
+ taskTriggerResultSchema
769
+ );
770
+ }
771
+ /**
772
+ * Generate alt text for an image file using AI vision.
773
+ * Requires a searchable bucket.
774
+ */
775
+ generateAltText(key, options) {
776
+ const path = `/api/files/${encodeURIComponent(key)}/alt-text`;
675
777
  return this.request(
676
778
  this.withBucket(path, options?.bucket),
677
779
  { method: "POST" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@howells/stow-server",
3
- "version": "0.3.2",
3
+ "version": "0.6.0",
4
4
  "description": "Server-side SDK for Stow file storage",
5
5
  "license": "MIT",
6
6
  "repository": {