@stacksjs/storage 0.74.41 → 0.74.43
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/file-info.d.ts +52 -0
- package/dist/file-info.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/package.json +10 -10
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** The presentation category for a mime type. */
|
|
2
|
+
export declare function fileKind(mime: string): FileKind;
|
|
3
|
+
/**
|
|
4
|
+
* Whether a preview can be rendered from the bytes alone.
|
|
5
|
+
*
|
|
6
|
+
* `false` does not mean "no preview is possible" - it means one cannot be made
|
|
7
|
+
* here, without a transcoder, a renderer or a headless browser. A caller that
|
|
8
|
+
* has those can still produce one; the point is that this module does not
|
|
9
|
+
* pretend it did.
|
|
10
|
+
*
|
|
11
|
+
* SVG is excluded from previewable despite being an image: rendering
|
|
12
|
+
* attacker-supplied SVG is script execution, and the decision to do it anyway
|
|
13
|
+
* belongs to the caller who knows where the file came from.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isPreviewable(mime: string): boolean;
|
|
16
|
+
/** Resolve the mime type from bytes first, then the name. */
|
|
17
|
+
export declare function resolveMime(bytes: Uint8Array, name?: string): { mime: string, source: FileInfo['mimeSource'] };
|
|
18
|
+
/**
|
|
19
|
+
* Describe a file from its bytes.
|
|
20
|
+
*
|
|
21
|
+
* `name` is optional and used only for the extension fallback, so this works
|
|
22
|
+
* for an in-memory upload that has no path yet.
|
|
23
|
+
*
|
|
24
|
+
* Image metadata is read through `ts-images`, imported lazily: a caller asking
|
|
25
|
+
* about a PDF should not pay to load an image decoder. A decode failure is not
|
|
26
|
+
* an error - a truncated or malformed image is still a file with a size and a
|
|
27
|
+
* type, and returning that beats throwing on the one case a file manager most
|
|
28
|
+
* needs to display.
|
|
29
|
+
*/
|
|
30
|
+
export declare function fileInfoFromBytes(bytes: Uint8Array, name?: string): Promise<FileInfo>;
|
|
31
|
+
/** Describe a file on disk. */
|
|
32
|
+
export declare function fileInfo(path: string): Promise<FileInfo>;
|
|
33
|
+
export declare interface FileInfo {
|
|
34
|
+
mime: string
|
|
35
|
+
mimeSource: 'magic-bytes' | 'extension' | 'fallback'
|
|
36
|
+
size: number
|
|
37
|
+
kind: FileKind
|
|
38
|
+
previewable: boolean
|
|
39
|
+
image?: ImageInfo
|
|
40
|
+
}
|
|
41
|
+
/** What `ts-images` reports about a decoded image. */
|
|
42
|
+
export declare interface ImageInfo {
|
|
43
|
+
width: number
|
|
44
|
+
height: number
|
|
45
|
+
format: string
|
|
46
|
+
channels?: number
|
|
47
|
+
bitDepth?: number
|
|
48
|
+
hasAlpha?: boolean
|
|
49
|
+
colorSpace?: string
|
|
50
|
+
}
|
|
51
|
+
/** What a UI branches on: how to present the file, not what encodes it. */
|
|
52
|
+
export type FileKind = 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'archive' | 'font' | 'other';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{extname}from"node:path";import{detectMimeFromMagicBytes}from"./mime-verify";const EXTENSION_MIME={".txt":"text/plain",".md":"text/markdown",".csv":"text/csv",".json":"application/json",".xml":"application/xml",".svg":"image/svg+xml",".html":"text/html",".css":"text/css",".js":"text/javascript",".ts":"text/typescript",".yaml":"application/yaml",".yml":"application/yaml"},KIND_BY_TYPE=[[/^image\//,"image"],[/^video\//,"video"],[/^audio\//,"audio"],[/^font\/|^application\/(?:x-)?font/,"font"],[/^application\/pdf$/,"pdf"],[/^application\/(?:zip|x-tar|gzip|x-7z|x-rar|x-bzip)/,"archive"],[/^text\/|^application\/(?:json|xml|yaml|javascript|typescript)/,"text"]];export function fileKind(mime){for(const[pattern,kind]of KIND_BY_TYPE)if(pattern.test(mime))return kind;return"other"}export function isPreviewable(mime){if(mime==="image/svg+xml")return!1;return fileKind(mime)==="image"||fileKind(mime)==="text"}export function resolveMime(bytes,name){const sniffed=detectMimeFromMagicBytes(bytes);if(sniffed)return{mime:sniffed,source:"magic-bytes"};if(name){const byExtension=EXTENSION_MIME[extname(name).toLowerCase()];if(byExtension)return{mime:byExtension,source:"extension"}}return{mime:"application/octet-stream",source:"fallback"}}export async function fileInfoFromBytes(bytes,name){const{mime,source}=resolveMime(bytes,name),kind=fileKind(mime),info={mime,mimeSource:source,size:bytes.byteLength,kind,previewable:isPreviewable(mime)};if(kind==="image"&&mime!=="image/svg+xml")try{const{getMetadata}=await import("ts-images");info.image=await getMetadata(bytes)}catch{}return info}export async function fileInfo(path){const file=Bun.file(path),bytes=new Uint8Array(await file.arrayBuffer());return fileInfoFromBytes(bytes,path)}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ export type { DiskName, KnownDisks } from './types/filesystem';
|
|
|
8
8
|
export type { SignedTokenVerification } from './signed-url';
|
|
9
9
|
export type { SignedUrlOptions } from './types';
|
|
10
10
|
export type { ParsedDiskPath } from './path-sanitize';
|
|
11
|
+
export type { FileInfo, FileKind, ImageInfo } from './file-info';
|
|
11
12
|
export type { MimeVerifyResult } from './mime-verify';
|
|
12
13
|
export type { S3PresignedPostInput, S3PresignedPostResult } from './s3-presigned-post';
|
|
13
14
|
export type {
|
|
@@ -54,6 +55,7 @@ export { parseDiskPath, PathSanitizeError, sanitizePresignedDir, sanitizePresign
|
|
|
54
55
|
// MIME re-verification — server-side check that uploaded bytes match
|
|
55
56
|
// the claimed content type. Run this after a presigned upload
|
|
56
57
|
// completes since `Content-Type` on a presigned PUT is caller-attested.
|
|
58
|
+
export { fileInfo, fileInfoFromBytes, fileKind, isPreviewable, resolveMime } from './file-info';
|
|
57
59
|
export { detectMimeFromMagicBytes, verifyUploadedMime } from './mime-verify';
|
|
58
60
|
// S3 presigned-POST policy signer (stacksjs/stacks#1888 Phase B).
|
|
59
61
|
// Exposed as a standalone for callers that have their own S3 client
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export*from"./copy";export*from"./delete";export*from"./files";export*from"./folders";export*from"./fs";export*from"./glob";export*from"./hash";export*from"./helpers";export* as storage from"./storage";export*from"./zip";export{ensureBucket,ensureConfiguredBuckets}from"./provision";export*from"./adapters";export*from"./types";export*from"./drivers";export{serveFile}from"./static-serve";export{Storage,StorageManager}from"./facade";export{UploadedFile,uploadedFile,uploadedFiles}from"./uploaded-file";export{azureDisk,backblazeDisk,configFromEnv,filebaseDisk,gcsDisk,hetznerDisk,localDisk,r2Disk,s3Disk}from"./types/filesystem";export{clearRevokedSignedStorageTokens,createSignedStorageToken,isSignedStorageTokenRevoked,revokeSignedStorageToken,verifySignedStorageToken}from"./signed-url";export{parseDiskPath,PathSanitizeError,sanitizePresignedDir,sanitizePresignedFilename}from"./path-sanitize";export{detectMimeFromMagicBytes,verifyUploadedMime}from"./mime-verify";export{signS3PresignedPost}from"./s3-presigned-post";
|
|
1
|
+
export*from"./copy";export*from"./delete";export*from"./files";export*from"./folders";export*from"./fs";export*from"./glob";export*from"./hash";export*from"./helpers";export* as storage from"./storage";export*from"./zip";export{ensureBucket,ensureConfiguredBuckets}from"./provision";export*from"./adapters";export*from"./types";export*from"./drivers";export{serveFile}from"./static-serve";export{Storage,StorageManager}from"./facade";export{UploadedFile,uploadedFile,uploadedFiles}from"./uploaded-file";export{azureDisk,backblazeDisk,configFromEnv,filebaseDisk,gcsDisk,hetznerDisk,localDisk,r2Disk,s3Disk}from"./types/filesystem";export{clearRevokedSignedStorageTokens,createSignedStorageToken,isSignedStorageTokenRevoked,revokeSignedStorageToken,verifySignedStorageToken}from"./signed-url";export{parseDiskPath,PathSanitizeError,sanitizePresignedDir,sanitizePresignedFilename}from"./path-sanitize";export{fileInfo,fileInfoFromBytes,fileKind,isPreviewable,resolveMime}from"./file-info";export{detectMimeFromMagicBytes,verifyUploadedMime}from"./mime-verify";export{signS3PresignedPost}from"./s3-presigned-post";
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/storage",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.74.
|
|
5
|
+
"version": "0.74.43",
|
|
6
6
|
"description": "The Stacks file system.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -61,20 +61,20 @@
|
|
|
61
61
|
"prepublishOnly": "bun run build"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@stacksjs/arrays": "0.74.
|
|
65
|
-
"@stacksjs/cli": "0.74.
|
|
66
|
-
"@stacksjs/config": "0.74.
|
|
67
|
-
"@stacksjs/env": "0.74.
|
|
68
|
-
"@stacksjs/error-handling": "0.74.
|
|
69
|
-
"@stacksjs/logging": "0.74.
|
|
70
|
-
"@stacksjs/path": "0.74.
|
|
71
|
-
"@stacksjs/strings": "0.74.
|
|
64
|
+
"@stacksjs/arrays": "0.74.43",
|
|
65
|
+
"@stacksjs/cli": "0.74.43",
|
|
66
|
+
"@stacksjs/config": "0.74.43",
|
|
67
|
+
"@stacksjs/env": "0.74.43",
|
|
68
|
+
"@stacksjs/error-handling": "0.74.43",
|
|
69
|
+
"@stacksjs/logging": "0.74.43",
|
|
70
|
+
"@stacksjs/path": "0.74.43",
|
|
71
|
+
"@stacksjs/strings": "0.74.43",
|
|
72
72
|
"@stacksjs/ts-cloud": "^0.16.0",
|
|
73
73
|
"@stacksjs/ts-xml": "^0.1.0",
|
|
74
74
|
"ts-images": "^0.2.11"
|
|
75
75
|
},
|
|
76
76
|
"devDependencies": {
|
|
77
|
-
"@stacksjs/types": "0.74.
|
|
77
|
+
"@stacksjs/types": "0.74.43",
|
|
78
78
|
"better-dx": "^0.2.24"
|
|
79
79
|
}
|
|
80
80
|
}
|