chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
+398
View File
@@ -0,0 +1,398 @@
/**
* Checks whether a string is a valid file extension (lowercase alphanumeric only).
* Rejects extensions containing spaces, punctuation, or other non-alphanumeric characters
* that arise from non-filename document names (e.g. "Sim.ai <> RVTech").
*/
export function isAlphanumericExtension(ext: string): boolean {
return /^[a-z0-9]+$/.test(ext)
}
function extractExtension(fileName: string): string {
const lastDot = fileName.lastIndexOf('.')
return lastDot !== -1 ? fileName.slice(lastDot + 1).toLowerCase() : ''
}
export const MAX_FILE_SIZE = 100 * 1024 * 1024 // 100MB
export const SUPPORTED_DOCUMENT_EXTENSIONS = [
'pdf',
'csv',
'doc',
'docx',
'txt',
'md',
'xlsx',
'xls',
'ppt',
'pptx',
'html',
'htm',
'json',
'jsonl',
'yaml',
'yml',
] as const
export const SUPPORTED_CODE_EXTENSIONS = [
'mdx',
'xml',
'css',
'scss',
'less',
'js',
'jsx',
'ts',
'tsx',
'py',
'rb',
'go',
'rs',
'java',
'kt',
'swift',
'c',
'cpp',
'h',
'hpp',
'cs',
'php',
'sh',
'bash',
'zsh',
'fish',
'sql',
'graphql',
'gql',
'toml',
'ini',
'conf',
'cfg',
'env',
'log',
'diff',
'patch',
'dockerfile',
'makefile',
'gitignore',
'editorconfig',
'prettierrc',
'eslintrc',
'mmd',
] as const
export type SupportedCodeExtension = (typeof SUPPORTED_CODE_EXTENSIONS)[number]
export const SUPPORTED_AUDIO_EXTENSIONS = [
'mp3',
'm4a',
'wav',
'webm',
'ogg',
'flac',
'aac',
'opus',
] as const
export const SUPPORTED_VIDEO_EXTENSIONS = ['mp4', 'mov', 'avi', 'mkv', 'webm'] as const
export const SUPPORTED_IMAGE_EXTENSIONS = [
'png',
'jpg',
'jpeg',
'gif',
'webp',
'svg',
'bmp',
'tif',
'tiff',
'heic',
'heif',
'avif',
'ico',
] as const
export type SupportedDocumentExtension = (typeof SUPPORTED_DOCUMENT_EXTENSIONS)[number]
export type SupportedAudioExtension = (typeof SUPPORTED_AUDIO_EXTENSIONS)[number]
export type SupportedVideoExtension = (typeof SUPPORTED_VIDEO_EXTENSIONS)[number]
export type SupportedImageExtension = (typeof SUPPORTED_IMAGE_EXTENSIONS)[number]
export type SupportedMediaExtension =
| SupportedDocumentExtension
| SupportedAudioExtension
| SupportedVideoExtension
| SupportedImageExtension
export const SUPPORTED_MIME_TYPES: Record<SupportedDocumentExtension, string[]> = {
pdf: ['application/pdf', 'application/x-pdf'],
csv: ['text/csv', 'application/csv', 'text/comma-separated-values'],
doc: ['application/msword', 'application/doc', 'application/vnd.ms-word'],
docx: [
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/octet-stream',
],
txt: ['text/plain', 'text/x-plain', 'application/txt'],
md: [
'text/markdown',
'text/x-markdown',
'text/plain',
'application/markdown',
'application/octet-stream',
],
xlsx: [
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'application/octet-stream',
],
xls: [
'application/vnd.ms-excel',
'application/excel',
'application/x-excel',
'application/x-msexcel',
],
ppt: ['application/vnd.ms-powerpoint', 'application/powerpoint', 'application/x-mspowerpoint'],
pptx: [
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/octet-stream',
],
html: ['text/html', 'application/xhtml+xml'],
htm: ['text/html', 'application/xhtml+xml'],
json: ['application/json', 'text/json', 'application/x-json'],
jsonl: ['application/jsonl', 'application/x-jsonlines', 'text/jsonl', 'application/octet-stream'],
yaml: ['text/yaml', 'text/x-yaml', 'application/yaml', 'application/x-yaml'],
yml: ['text/yaml', 'text/x-yaml', 'application/yaml', 'application/x-yaml'],
}
export const SUPPORTED_AUDIO_MIME_TYPES: Record<SupportedAudioExtension, string[]> = {
mp3: ['audio/mpeg', 'audio/mp3'],
m4a: ['audio/mp4', 'audio/x-m4a', 'audio/m4a'],
wav: ['audio/wav', 'audio/wave', 'audio/x-wav'],
webm: ['audio/webm'],
ogg: ['audio/ogg', 'audio/vorbis'],
flac: ['audio/flac', 'audio/x-flac'],
aac: ['audio/aac', 'audio/x-aac'],
opus: ['audio/opus'],
}
export const SUPPORTED_VIDEO_MIME_TYPES: Record<SupportedVideoExtension, string[]> = {
mp4: ['video/mp4', 'video/mpeg'],
mov: ['video/quicktime', 'video/x-quicktime'],
avi: ['video/x-msvideo', 'video/avi'],
mkv: ['video/x-matroska'],
webm: ['video/webm'],
}
export const ACCEPTED_FILE_TYPES = Object.values(SUPPORTED_MIME_TYPES).flat()
export const ACCEPTED_AUDIO_TYPES = Object.values(SUPPORTED_AUDIO_MIME_TYPES).flat()
export const ACCEPTED_VIDEO_TYPES = Object.values(SUPPORTED_VIDEO_MIME_TYPES).flat()
export const ACCEPTED_MEDIA_TYPES = [
...ACCEPTED_FILE_TYPES,
...ACCEPTED_AUDIO_TYPES,
...ACCEPTED_VIDEO_TYPES,
]
export const ACCEPTED_FILE_EXTENSIONS = SUPPORTED_DOCUMENT_EXTENSIONS.map((ext) => `.${ext}`)
export const ACCEPT_ATTRIBUTE = [...ACCEPTED_FILE_TYPES, ...ACCEPTED_FILE_EXTENSIONS].join(',')
const SUPPORTED_IMAGE_MIME_TYPES = [
'image/jpeg',
'image/png',
'image/gif',
'image/webp',
'image/svg+xml',
'image/bmp',
'image/tiff',
'image/heic',
'image/heif',
'image/avif',
'image/x-icon',
'image/vnd.microsoft.icon',
]
export const CHAT_ACCEPT_ATTRIBUTE = [
ACCEPT_ATTRIBUTE,
...SUPPORTED_IMAGE_MIME_TYPES,
...SUPPORTED_IMAGE_EXTENSIONS.map((ext) => `.${ext}`),
].join(',')
export interface FileValidationError {
code: 'UNSUPPORTED_FILE_TYPE' | 'MIME_TYPE_MISMATCH'
message: string
supportedTypes: string[]
}
export const SUPPORTED_ATTACHMENT_EXTENSIONS = Array.from(
new Set<string>([
...SUPPORTED_DOCUMENT_EXTENSIONS,
...SUPPORTED_CODE_EXTENSIONS,
...SUPPORTED_IMAGE_EXTENSIONS,
...SUPPORTED_AUDIO_EXTENSIONS,
...SUPPORTED_VIDEO_EXTENSIONS,
])
) as readonly string[]
/**
* Validate that a file's extension is allowed as a chat/mothership attachment.
*
* Permits documents, code, images, audio, and video — anything users would
* reasonably attach to a chat message. Rejects executables and unknown types.
*/
export function validateAttachmentFileType(fileName: string): FileValidationError | null {
const raw = extractExtension(fileName)
const extension = isAlphanumericExtension(raw) ? raw : ''
if (!SUPPORTED_ATTACHMENT_EXTENSIONS.includes(extension)) {
return {
code: 'UNSUPPORTED_FILE_TYPE',
message: `Unsupported file type${extension ? `: ${extension}` : ` for "${fileName}"`}. Supported types include documents, code, images, audio, and video.`,
supportedTypes: [...SUPPORTED_ATTACHMENT_EXTENSIONS],
}
}
return null
}
/**
* Validate if a file type is supported for document processing
*/
export function validateFileType(fileName: string, mimeType: string): FileValidationError | null {
const raw = extractExtension(fileName)
const extension = (isAlphanumericExtension(raw) ? raw : '') as SupportedDocumentExtension
if (!SUPPORTED_DOCUMENT_EXTENSIONS.includes(extension)) {
return {
code: 'UNSUPPORTED_FILE_TYPE',
message: `Unsupported file type${extension ? `: ${extension}` : ` for "${fileName}"`}. Supported types are: ${SUPPORTED_DOCUMENT_EXTENSIONS.join(', ')}`,
supportedTypes: [...SUPPORTED_DOCUMENT_EXTENSIONS],
}
}
const baseMimeType = mimeType.split(';')[0].trim()
// Allow empty MIME types if the extension is supported (browsers often don't recognize certain file types)
if (!baseMimeType) {
return null
}
const allowedMimeTypes = SUPPORTED_MIME_TYPES[extension]
if (!allowedMimeTypes.includes(baseMimeType)) {
return {
code: 'MIME_TYPE_MISMATCH',
message: `MIME type ${baseMimeType} does not match file extension ${extension}. Expected: ${allowedMimeTypes.join(', ')}`,
supportedTypes: allowedMimeTypes,
}
}
return null
}
/**
* Check if file extension is supported
*/
export function isSupportedExtension(extension: string): extension is SupportedDocumentExtension {
return SUPPORTED_DOCUMENT_EXTENSIONS.includes(
extension.toLowerCase() as SupportedDocumentExtension
)
}
/**
* Get supported MIME types for an extension
*/
export function getSupportedMimeTypes(extension: string): string[] {
if (isSupportedExtension(extension)) {
return SUPPORTED_MIME_TYPES[extension as SupportedDocumentExtension]
}
if (SUPPORTED_AUDIO_EXTENSIONS.includes(extension as SupportedAudioExtension)) {
return SUPPORTED_AUDIO_MIME_TYPES[extension as SupportedAudioExtension]
}
if (SUPPORTED_VIDEO_EXTENSIONS.includes(extension as SupportedVideoExtension)) {
return SUPPORTED_VIDEO_MIME_TYPES[extension as SupportedVideoExtension]
}
return []
}
/**
* Check if file extension is a supported audio extension
*/
export function isSupportedAudioExtension(extension: string): extension is SupportedAudioExtension {
return SUPPORTED_AUDIO_EXTENSIONS.includes(extension.toLowerCase() as SupportedAudioExtension)
}
/**
* Check if file extension is a supported video extension
*/
export function isSupportedVideoExtension(extension: string): extension is SupportedVideoExtension {
return SUPPORTED_VIDEO_EXTENSIONS.includes(extension.toLowerCase() as SupportedVideoExtension)
}
/**
* Validate if an audio/video file type is supported for STT processing
*/
const PNG_MAGIC_BYTES = Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a])
/**
* Validate that a buffer contains valid PNG data by checking magic bytes
*/
export function isValidPng(buffer: Buffer): boolean {
return buffer.length >= 8 && buffer.subarray(0, 8).equals(PNG_MAGIC_BYTES)
}
/**
* Detect a renderable raster image from its leading bytes, returning the canonical MIME type or
* `null` when the content is not one of the inline-renderable image formats (PNG, JPEG, GIF, WebP).
*
* The stored `contentType` is client-declared and never sniffed at upload time, so any path that
* renders a file inline for a less-trusted audience (e.g. images embedded in a public share) must
* derive the served type from the bytes themselves — a file claiming `image/png` could be HTML, SVG,
* or a script. SVG is deliberately excluded: it can carry script and is not a raster format.
*/
export function sniffImageContentType(buffer: Buffer): string | null {
if (isValidPng(buffer)) return 'image/png'
if (buffer.length >= 3 && buffer[0] === 0xff && buffer[1] === 0xd8 && buffer[2] === 0xff) {
return 'image/jpeg'
}
if (buffer.length >= 6) {
const header = buffer.toString('latin1', 0, 6)
if (header === 'GIF87a' || header === 'GIF89a') return 'image/gif'
}
if (
buffer.length >= 12 &&
buffer.toString('latin1', 0, 4) === 'RIFF' &&
buffer.toString('latin1', 8, 12) === 'WEBP'
) {
return 'image/webp'
}
return null
}
export function validateMediaFileType(
fileName: string,
mimeType: string
): FileValidationError | null {
const raw = extractExtension(fileName)
const extension = isAlphanumericExtension(raw) ? raw : ''
const isAudio = SUPPORTED_AUDIO_EXTENSIONS.includes(extension as SupportedAudioExtension)
const isVideo = SUPPORTED_VIDEO_EXTENSIONS.includes(extension as SupportedVideoExtension)
if (!isAudio && !isVideo) {
return {
code: 'UNSUPPORTED_FILE_TYPE',
message: `Unsupported media file type${extension ? `: ${extension}` : ` for "${fileName}"`}. Supported audio types: ${SUPPORTED_AUDIO_EXTENSIONS.join(', ')}. Supported video types: ${SUPPORTED_VIDEO_EXTENSIONS.join(', ')}`,
supportedTypes: [...SUPPORTED_AUDIO_EXTENSIONS, ...SUPPORTED_VIDEO_EXTENSIONS],
}
}
const baseMimeType = mimeType.split(';')[0].trim()
const allowedMimeTypes = isAudio
? SUPPORTED_AUDIO_MIME_TYPES[extension as SupportedAudioExtension]
: SUPPORTED_VIDEO_MIME_TYPES[extension as SupportedVideoExtension]
if (!allowedMimeTypes.includes(baseMimeType)) {
return {
code: 'MIME_TYPE_MISMATCH',
message: `MIME type ${baseMimeType} does not match file extension ${extension}. Expected: ${allowedMimeTypes.join(', ')}`,
supportedTypes: allowedMimeTypes,
}
}
return null
}