@owlmeans/storage-common 0.1.1 → 0.1.3
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 +1 -1
- package/README.md +24 -571
- package/package.json +6 -4
- package/tsconfig.json +3 -4
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2026 OwlMeans Common — Fullstack typescript framework
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -1,599 +1,52 @@
|
|
|
1
1
|
# @owlmeans/storage-common
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shared types and schemas for OwlMeans object storage — file metadata, instances, and status enums.
|
|
4
4
|
|
|
5
5
|
## Overview
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- **Status Management**: File status tracking and lifecycle management
|
|
12
|
-
- **Format Support**: Multiple file formats and encoding support
|
|
13
|
-
- **Scope-Based Access**: Authorization scopes for file access control
|
|
14
|
-
- **Error Types**: Specialized error types for storage operations
|
|
15
|
-
- **Instance Management**: Multiple file instances and versions support
|
|
16
|
-
|
|
17
|
-
This package is part of the OwlMeans storage ecosystem:
|
|
18
|
-
- **@owlmeans/storage-common**: Common storage interfaces *(this package)*
|
|
19
|
-
- **@owlmeans/storage-resource**: Storage resource implementations
|
|
20
|
-
- **@owlmeans/static-resource**: Static resource management
|
|
21
|
-
- **@owlmeans/image-resource**: Image-specific storage features
|
|
7
|
+
- `StoredFileMeta` / `StoredFile` / `StoredFileWithData` — file record types
|
|
8
|
+
- `StoredFileInstance` / `StoredFilePayload` — per-variant instance types (URL + size)
|
|
9
|
+
- `StoredFileStatus` / `StoredFileFormat` — enums for file lifecycle state and encoding
|
|
10
|
+
- Used by `@owlmeans/storage-resource` and `@owlmeans/image-resource`
|
|
22
11
|
|
|
23
12
|
## Installation
|
|
24
13
|
|
|
25
14
|
```bash
|
|
26
|
-
|
|
15
|
+
bun add @owlmeans/storage-common
|
|
27
16
|
```
|
|
28
17
|
|
|
29
|
-
##
|
|
30
|
-
|
|
31
|
-
### Stored Files
|
|
32
|
-
Files are represented with metadata, multiple instances (different sizes/formats), and access control through scopes.
|
|
33
|
-
|
|
34
|
-
### File Instances
|
|
35
|
-
Each file can have multiple instances representing different versions, sizes, or formats of the same file.
|
|
36
|
-
|
|
37
|
-
### Scope-Based Access
|
|
38
|
-
Files use scope-based authorization to control who can access, modify, or delete files.
|
|
18
|
+
## API
|
|
39
19
|
|
|
40
|
-
###
|
|
41
|
-
Files have status indicators to track their lifecycle from upload to processing to availability.
|
|
42
|
-
|
|
43
|
-
## API Reference
|
|
44
|
-
|
|
45
|
-
### Core Types
|
|
46
|
-
|
|
47
|
-
#### `StoredFileMeta`
|
|
48
|
-
|
|
49
|
-
Metadata interface for stored files with authorization and identification.
|
|
20
|
+
### `StoredFileMeta`
|
|
50
21
|
|
|
51
22
|
```typescript
|
|
52
23
|
interface StoredFileMeta {
|
|
53
|
-
entityId?: string
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
alias: string // Unique file identifier
|
|
60
|
-
status: StoredFileStatus // Current file status
|
|
61
|
-
}
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
#### `StoredFileInstance`
|
|
65
|
-
|
|
66
|
-
Represents a specific instance of a stored file.
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
interface StoredFileInstance {
|
|
70
|
-
size: number // File size in bytes
|
|
71
|
-
alias: string // Instance identifier
|
|
72
|
-
url: string // Access URL for this instance
|
|
73
|
-
}
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
#### `StoredFilePayload`
|
|
77
|
-
|
|
78
|
-
Extended instance with actual file data for upload/download operations.
|
|
79
|
-
|
|
80
|
-
```typescript
|
|
81
|
-
interface StoredFilePayload extends StoredFileInstance {
|
|
82
|
-
format?: StoredFileFormat // File format information
|
|
83
|
-
bytes?: Uint8Array // Raw file bytes
|
|
84
|
-
base64?: string // Base64 encoded file data
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
#### `StoredFile`
|
|
89
|
-
|
|
90
|
-
Complete file representation with metadata and all instances.
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
interface StoredFile extends StoredFileMeta {
|
|
94
|
-
instances: { [key: string]: StoredFileInstance }
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
#### `StoredFileWithData`
|
|
99
|
-
|
|
100
|
-
File representation including actual file data for each instance.
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
interface StoredFileWithData extends StoredFileMeta {
|
|
104
|
-
format?: StoredFileFormat
|
|
105
|
-
instances: { [key: string]: StoredFilePayload }
|
|
106
|
-
}
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Enums and Constants
|
|
110
|
-
|
|
111
|
-
#### `StoredFileStatus`
|
|
112
|
-
|
|
113
|
-
Enumeration of possible file statuses.
|
|
114
|
-
|
|
115
|
-
```typescript
|
|
116
|
-
enum StoredFileStatus {
|
|
117
|
-
Uploaded = 'uploaded', // File has been uploaded
|
|
118
|
-
Processing = 'processing', // File is being processed
|
|
119
|
-
Available = 'available', // File is available for use
|
|
120
|
-
Error = 'error', // File processing failed
|
|
121
|
-
Deleted = 'deleted' // File has been deleted
|
|
122
|
-
}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
#### `StoredFileFormat`
|
|
126
|
-
|
|
127
|
-
Enumeration of supported file formats.
|
|
128
|
-
|
|
129
|
-
```typescript
|
|
130
|
-
enum StoredFileFormat {
|
|
131
|
-
Original = 'original', // Original uploaded format
|
|
132
|
-
Thumbnail = 'thumbnail', // Thumbnail version
|
|
133
|
-
Compressed = 'compressed', // Compressed version
|
|
134
|
-
Optimized = 'optimized' // Optimized version
|
|
135
|
-
}
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
### Validation Schemas
|
|
139
|
-
|
|
140
|
-
The package provides AJV schemas for runtime validation:
|
|
141
|
-
|
|
142
|
-
#### `StoredFileMetaSchema`
|
|
143
|
-
|
|
144
|
-
Validates stored file metadata.
|
|
145
|
-
|
|
146
|
-
```typescript
|
|
147
|
-
const StoredFileMetaSchema: JSONSchemaType<StoredFileMeta>
|
|
148
|
-
```
|
|
149
|
-
|
|
150
|
-
#### `StoredFileInstanceSchema`
|
|
151
|
-
|
|
152
|
-
Validates file instance data.
|
|
153
|
-
|
|
154
|
-
```typescript
|
|
155
|
-
const StoredFileInstanceSchema: JSONSchemaType<StoredFileInstance>
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
#### `StoredFilePayloadSchema`
|
|
159
|
-
|
|
160
|
-
Validates file payload including data.
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
const StoredFilePayloadSchema: JSONSchemaType<StoredFilePayload>
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
### Error Types
|
|
167
|
-
|
|
168
|
-
The package defines storage-specific error types for comprehensive error handling.
|
|
169
|
-
|
|
170
|
-
```typescript
|
|
171
|
-
import { StorageError } from '@owlmeans/storage-common'
|
|
172
|
-
|
|
173
|
-
// Storage operation errors
|
|
174
|
-
class StorageError extends OwlMeansError {
|
|
175
|
-
// Storage-specific error handling
|
|
176
|
-
}
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
## Usage Examples
|
|
180
|
-
|
|
181
|
-
### Basic File Metadata Management
|
|
182
|
-
|
|
183
|
-
```typescript
|
|
184
|
-
import {
|
|
185
|
-
StoredFileMeta,
|
|
186
|
-
StoredFileStatus,
|
|
187
|
-
StoredFileMetaSchema
|
|
188
|
-
} from '@owlmeans/storage-common'
|
|
189
|
-
import Ajv from 'ajv'
|
|
190
|
-
|
|
191
|
-
const ajv = new Ajv()
|
|
192
|
-
const validateMeta = ajv.compile(StoredFileMetaSchema)
|
|
193
|
-
|
|
194
|
-
// Create file metadata
|
|
195
|
-
const fileMeta: StoredFileMeta = {
|
|
196
|
-
entityId: 'user-123',
|
|
197
|
-
sourceName: 'document.pdf',
|
|
198
|
-
name: 'Important Document',
|
|
199
|
-
title: 'Project Requirements Document',
|
|
200
|
-
scopes: ['read:documents', 'entity:user-123'],
|
|
201
|
-
mimeType: 'application/pdf',
|
|
202
|
-
alias: 'doc-abc123',
|
|
203
|
-
status: StoredFileStatus.Uploaded
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
// Validate metadata
|
|
207
|
-
if (validateMeta(fileMeta)) {
|
|
208
|
-
console.log('File metadata is valid')
|
|
209
|
-
} else {
|
|
210
|
-
console.error('Validation errors:', validateMeta.errors)
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### File Instance Management
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
import {
|
|
218
|
-
StoredFile,
|
|
219
|
-
StoredFileInstance,
|
|
220
|
-
StoredFileFormat
|
|
221
|
-
} from '@owlmeans/storage-common'
|
|
222
|
-
|
|
223
|
-
// Create file with multiple instances
|
|
224
|
-
const fileWithInstances: StoredFile = {
|
|
225
|
-
// ... metadata
|
|
226
|
-
entityId: 'user-123',
|
|
227
|
-
sourceName: 'photo.jpg',
|
|
228
|
-
name: 'Profile Photo',
|
|
229
|
-
scopes: ['read:photos', 'entity:user-123'],
|
|
230
|
-
mimeType: 'image/jpeg',
|
|
231
|
-
alias: 'photo-xyz789',
|
|
232
|
-
status: StoredFileStatus.Available,
|
|
233
|
-
|
|
234
|
-
instances: {
|
|
235
|
-
original: {
|
|
236
|
-
size: 2048000,
|
|
237
|
-
alias: 'photo-xyz789-original',
|
|
238
|
-
url: 'https://storage.example.com/photos/original/photo-xyz789.jpg'
|
|
239
|
-
},
|
|
240
|
-
thumbnail: {
|
|
241
|
-
size: 51200,
|
|
242
|
-
alias: 'photo-xyz789-thumb',
|
|
243
|
-
url: 'https://storage.example.com/photos/thumbnails/photo-xyz789.jpg'
|
|
244
|
-
},
|
|
245
|
-
compressed: {
|
|
246
|
-
size: 512000,
|
|
247
|
-
alias: 'photo-xyz789-compressed',
|
|
248
|
-
url: 'https://storage.example.com/photos/compressed/photo-xyz789.jpg'
|
|
249
|
-
}
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// Access different file instances
|
|
254
|
-
const originalUrl = fileWithInstances.instances.original.url
|
|
255
|
-
const thumbnailUrl = fileWithInstances.instances.thumbnail.url
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
### File Upload with Payload
|
|
259
|
-
|
|
260
|
-
```typescript
|
|
261
|
-
import {
|
|
262
|
-
StoredFileWithData,
|
|
263
|
-
StoredFilePayload,
|
|
264
|
-
StoredFileFormat
|
|
265
|
-
} from '@owlmeans/storage-common'
|
|
266
|
-
|
|
267
|
-
// File upload with data
|
|
268
|
-
const uploadFile = async (fileData: Uint8Array, metadata: StoredFileMeta): Promise<StoredFileWithData> => {
|
|
269
|
-
const fileWithData: StoredFileWithData = {
|
|
270
|
-
...metadata,
|
|
271
|
-
format: StoredFileFormat.Original,
|
|
272
|
-
instances: {
|
|
273
|
-
original: {
|
|
274
|
-
size: fileData.length,
|
|
275
|
-
alias: `${metadata.alias}-original`,
|
|
276
|
-
url: '', // Will be set after upload
|
|
277
|
-
format: StoredFileFormat.Original,
|
|
278
|
-
bytes: fileData
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return fileWithData
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
// Usage
|
|
287
|
-
const fileBytes = new Uint8Array([/* file data */])
|
|
288
|
-
const metadata: StoredFileMeta = {
|
|
289
|
-
sourceName: 'upload.png',
|
|
290
|
-
name: 'User Upload',
|
|
291
|
-
scopes: ['read:uploads'],
|
|
292
|
-
mimeType: 'image/png',
|
|
293
|
-
alias: 'upload-123',
|
|
294
|
-
status: StoredFileStatus.Uploaded
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const uploadedFile = await uploadFile(fileBytes, metadata)
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### Base64 File Handling
|
|
301
|
-
|
|
302
|
-
```typescript
|
|
303
|
-
import { StoredFilePayload, StoredFileFormat } from '@owlmeans/storage-common'
|
|
304
|
-
|
|
305
|
-
// Convert base64 to file payload
|
|
306
|
-
const base64ToPayload = (base64Data: string, alias: string): StoredFilePayload => {
|
|
307
|
-
// Decode base64 to calculate size
|
|
308
|
-
const bytes = Uint8Array.from(atob(base64Data), c => c.charCodeAt(0))
|
|
309
|
-
|
|
310
|
-
return {
|
|
311
|
-
size: bytes.length,
|
|
312
|
-
alias,
|
|
313
|
-
url: '', // Will be set after storage
|
|
314
|
-
format: StoredFileFormat.Original,
|
|
315
|
-
base64: base64Data,
|
|
316
|
-
bytes
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
// Convert file payload to base64
|
|
321
|
-
const payloadToBase64 = (payload: StoredFilePayload): string => {
|
|
322
|
-
if (payload.base64) {
|
|
323
|
-
return payload.base64
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
if (payload.bytes) {
|
|
327
|
-
return btoa(String.fromCharCode(...payload.bytes))
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
throw new Error('No data available for base64 conversion')
|
|
331
|
-
}
|
|
332
|
-
```
|
|
333
|
-
|
|
334
|
-
### Scope-Based Authorization
|
|
335
|
-
|
|
336
|
-
```typescript
|
|
337
|
-
import { StoredFileMeta } from '@owlmeans/storage-common'
|
|
338
|
-
|
|
339
|
-
class FileAuthorizationManager {
|
|
340
|
-
canAccess(file: StoredFileMeta, userScopes: string[]): boolean {
|
|
341
|
-
return file.scopes.some(scope => userScopes.includes(scope))
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
canModify(file: StoredFileMeta, userScopes: string[], entityId?: string): boolean {
|
|
345
|
-
// Check if user has modify access
|
|
346
|
-
const hasModifyScope = userScopes.some(scope =>
|
|
347
|
-
scope.startsWith('write:') || scope.startsWith('modify:')
|
|
348
|
-
)
|
|
349
|
-
|
|
350
|
-
// Check entity-specific access
|
|
351
|
-
const hasEntityAccess = entityId && file.entityId === entityId &&
|
|
352
|
-
userScopes.includes(`entity:${entityId}`)
|
|
353
|
-
|
|
354
|
-
return hasModifyScope && (hasEntityAccess || !file.entityId)
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
filterAccessibleFiles(files: StoredFileMeta[], userScopes: string[]): StoredFileMeta[] {
|
|
358
|
-
return files.filter(file => this.canAccess(file, userScopes))
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// Usage
|
|
363
|
-
const authManager = new FileAuthorizationManager()
|
|
364
|
-
const userScopes = ['read:documents', 'entity:user-123']
|
|
365
|
-
|
|
366
|
-
const canRead = authManager.canAccess(fileMeta, userScopes)
|
|
367
|
-
const canWrite = authManager.canModify(fileMeta, ['write:documents', 'entity:user-123'], 'user-123')
|
|
368
|
-
```
|
|
369
|
-
|
|
370
|
-
### File Status Management
|
|
371
|
-
|
|
372
|
-
```typescript
|
|
373
|
-
import { StoredFile, StoredFileStatus } from '@owlmeans/storage-common'
|
|
374
|
-
|
|
375
|
-
class FileStatusManager {
|
|
376
|
-
updateStatus(file: StoredFile, newStatus: StoredFileStatus): StoredFile {
|
|
377
|
-
return {
|
|
378
|
-
...file,
|
|
379
|
-
status: newStatus
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
isAvailable(file: StoredFile): boolean {
|
|
384
|
-
return file.status === StoredFileStatus.Available
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
isProcessing(file: StoredFile): boolean {
|
|
388
|
-
return file.status === StoredFileStatus.Processing
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
hasError(file: StoredFile): boolean {
|
|
392
|
-
return file.status === StoredFileStatus.Error
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
getProcessingFiles(files: StoredFile[]): StoredFile[] {
|
|
396
|
-
return files.filter(file => this.isProcessing(file))
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
getAvailableFiles(files: StoredFile[]): StoredFile[] {
|
|
400
|
-
return files.filter(file => this.isAvailable(file))
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
// Usage
|
|
405
|
-
const statusManager = new FileStatusManager()
|
|
406
|
-
|
|
407
|
-
// Update file status after processing
|
|
408
|
-
const processedFile = statusManager.updateStatus(file, StoredFileStatus.Available)
|
|
409
|
-
|
|
410
|
-
// Check file availability
|
|
411
|
-
if (statusManager.isAvailable(file)) {
|
|
412
|
-
console.log('File is ready for use')
|
|
413
|
-
}
|
|
414
|
-
```
|
|
415
|
-
|
|
416
|
-
### File Format Management
|
|
417
|
-
|
|
418
|
-
```typescript
|
|
419
|
-
import {
|
|
420
|
-
StoredFileWithData,
|
|
421
|
-
StoredFileFormat,
|
|
422
|
-
StoredFilePayload
|
|
423
|
-
} from '@owlmeans/storage-common'
|
|
424
|
-
|
|
425
|
-
class FileFormatManager {
|
|
426
|
-
addInstance(
|
|
427
|
-
file: StoredFileWithData,
|
|
428
|
-
format: StoredFileFormat,
|
|
429
|
-
payload: StoredFilePayload
|
|
430
|
-
): StoredFileWithData {
|
|
431
|
-
return {
|
|
432
|
-
...file,
|
|
433
|
-
instances: {
|
|
434
|
-
...file.instances,
|
|
435
|
-
[format]: {
|
|
436
|
-
...payload,
|
|
437
|
-
format
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
getInstanceByFormat(file: StoredFileWithData, format: StoredFileFormat): StoredFilePayload | null {
|
|
444
|
-
return file.instances[format] || null
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
hasFormat(file: StoredFileWithData, format: StoredFileFormat): boolean {
|
|
448
|
-
return format in file.instances
|
|
449
|
-
}
|
|
450
|
-
|
|
451
|
-
getAvailableFormats(file: StoredFileWithData): StoredFileFormat[] {
|
|
452
|
-
return Object.keys(file.instances)
|
|
453
|
-
.filter(key => file.instances[key].format)
|
|
454
|
-
.map(key => file.instances[key].format!)
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
// Usage
|
|
459
|
-
const formatManager = new FileFormatManager()
|
|
460
|
-
|
|
461
|
-
// Add thumbnail instance
|
|
462
|
-
const thumbnailPayload: StoredFilePayload = {
|
|
463
|
-
size: 10240,
|
|
464
|
-
alias: 'file-thumb',
|
|
465
|
-
url: 'https://storage.example.com/thumbnails/file-thumb.jpg',
|
|
466
|
-
format: StoredFileFormat.Thumbnail,
|
|
467
|
-
bytes: thumbnailBytes
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
const fileWithThumbnail = formatManager.addInstance(
|
|
471
|
-
originalFile,
|
|
472
|
-
StoredFileFormat.Thumbnail,
|
|
473
|
-
thumbnailPayload
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
// Get specific format
|
|
477
|
-
const thumbnail = formatManager.getInstanceByFormat(fileWithThumbnail, StoredFileFormat.Thumbnail)
|
|
478
|
-
```
|
|
479
|
-
|
|
480
|
-
### Validation and Error Handling
|
|
481
|
-
|
|
482
|
-
```typescript
|
|
483
|
-
import {
|
|
484
|
-
StoredFileMetaSchema,
|
|
485
|
-
StoredFileInstanceSchema,
|
|
486
|
-
StorageError
|
|
487
|
-
} from '@owlmeans/storage-common'
|
|
488
|
-
import Ajv from 'ajv'
|
|
489
|
-
|
|
490
|
-
const ajv = new Ajv()
|
|
491
|
-
const validateMeta = ajv.compile(StoredFileMetaSchema)
|
|
492
|
-
const validateInstance = ajv.compile(StoredFileInstanceSchema)
|
|
493
|
-
|
|
494
|
-
class FileValidator {
|
|
495
|
-
validateMetadata(meta: unknown): StoredFileMeta {
|
|
496
|
-
if (!validateMeta(meta)) {
|
|
497
|
-
throw new StorageError('Invalid file metadata', {
|
|
498
|
-
code: 'VALIDATION_ERROR',
|
|
499
|
-
details: validateMeta.errors
|
|
500
|
-
})
|
|
501
|
-
}
|
|
502
|
-
return meta as StoredFileMeta
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
validateInstance(instance: unknown): StoredFileInstance {
|
|
506
|
-
if (!validateInstance(instance)) {
|
|
507
|
-
throw new StorageError('Invalid file instance', {
|
|
508
|
-
code: 'VALIDATION_ERROR',
|
|
509
|
-
details: validateInstance.errors
|
|
510
|
-
})
|
|
511
|
-
}
|
|
512
|
-
return instance as StoredFileInstance
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
validateFile(file: unknown): StoredFile {
|
|
516
|
-
const meta = this.validateMetadata(file)
|
|
517
|
-
|
|
518
|
-
if (typeof file === 'object' && file !== null && 'instances' in file) {
|
|
519
|
-
const instances = (file as any).instances
|
|
520
|
-
|
|
521
|
-
if (typeof instances !== 'object') {
|
|
522
|
-
throw new StorageError('Invalid instances object')
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// Validate each instance
|
|
526
|
-
Object.values(instances).forEach(instance => {
|
|
527
|
-
this.validateInstance(instance)
|
|
528
|
-
})
|
|
529
|
-
|
|
530
|
-
return file as StoredFile
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
throw new StorageError('Invalid file structure')
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
// Usage
|
|
538
|
-
const validator = new FileValidator()
|
|
539
|
-
|
|
540
|
-
try {
|
|
541
|
-
const validFile = validator.validateFile(untrustedFileData)
|
|
542
|
-
console.log('File validation passed')
|
|
543
|
-
} catch (error) {
|
|
544
|
-
if (error instanceof StorageError) {
|
|
545
|
-
console.error('Storage validation error:', error.message)
|
|
546
|
-
console.error('Details:', error.details)
|
|
547
|
-
}
|
|
24
|
+
entityId?: string
|
|
25
|
+
name?: string
|
|
26
|
+
scopes: string[]
|
|
27
|
+
mimeType: string
|
|
28
|
+
alias: string
|
|
29
|
+
status: StoredFileStatus
|
|
548
30
|
}
|
|
549
31
|
```
|
|
550
32
|
|
|
551
|
-
|
|
33
|
+
### `StoredFile`
|
|
552
34
|
|
|
553
|
-
|
|
554
|
-
```typescript
|
|
555
|
-
import { ScopeValueSchema } from '@owlmeans/auth'
|
|
556
|
-
import { StoredFileMeta } from '@owlmeans/storage-common'
|
|
35
|
+
Extends `StoredFileMeta` with `instances: { [key: string]: StoredFileInstance }`.
|
|
557
36
|
|
|
558
|
-
|
|
559
|
-
```
|
|
37
|
+
### `StoredFileWithData`
|
|
560
38
|
|
|
561
|
-
|
|
562
|
-
```typescript
|
|
563
|
-
import { OwlMeansError } from '@owlmeans/error'
|
|
564
|
-
import { StorageError } from '@owlmeans/storage-common'
|
|
565
|
-
|
|
566
|
-
// Storage errors extend the OwlMeans error system
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
### Resource Integration
|
|
570
|
-
```typescript
|
|
571
|
-
import { StoredFile } from '@owlmeans/storage-common'
|
|
572
|
-
import { Resource } from '@owlmeans/resource'
|
|
573
|
-
|
|
574
|
-
// Storage resources can use these types for consistent file management
|
|
575
|
-
```
|
|
39
|
+
Extends `StoredFileMeta` with `instances: { [key: string]: StoredFilePayload }` (includes raw data for upload).
|
|
576
40
|
|
|
577
|
-
|
|
41
|
+
### `StoredFileStatus`
|
|
578
42
|
|
|
579
|
-
|
|
580
|
-
2. **Status Tracking**: Always update file status during processing
|
|
581
|
-
3. **Instance Management**: Use appropriate instances for different use cases
|
|
582
|
-
4. **Validation**: Always validate file data using provided schemas
|
|
583
|
-
5. **Error Handling**: Use storage-specific error types for better debugging
|
|
584
|
-
6. **Format Support**: Provide multiple formats for better user experience
|
|
585
|
-
7. **Security**: Validate all file metadata and content before storage
|
|
43
|
+
`Uploaded` | `ProcessingReady` | `Processed` | `Cached` | `Unknown`
|
|
586
44
|
|
|
587
|
-
|
|
45
|
+
### `StoredFileFormat`
|
|
588
46
|
|
|
589
|
-
|
|
590
|
-
- `@owlmeans/auth` - Authentication and authorization utilities
|
|
591
|
-
- `@owlmeans/error` - Error handling system
|
|
592
|
-
- `ajv` - JSON Schema validation (peer dependency)
|
|
47
|
+
`Bytes` | `Base64`
|
|
593
48
|
|
|
594
49
|
## Related Packages
|
|
595
50
|
|
|
596
|
-
- [`@owlmeans/storage-resource`](../storage-resource) -
|
|
597
|
-
- [`@owlmeans/
|
|
598
|
-
- [`@owlmeans/image-resource`](../image-resource) - Image-specific storage
|
|
599
|
-
- [`@owlmeans/auth`](../auth) - Authentication and authorization
|
|
51
|
+
- [`@owlmeans/storage-resource`](../storage-resource) — S3-compatible resource using these types
|
|
52
|
+
- [`@owlmeans/image-resource`](../image-resource) — image-specific extensions of these types
|
package/package.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/storage-common",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.3",
|
|
5
|
+
"license": "MIT",
|
|
5
6
|
"scripts": {
|
|
6
7
|
"build": "tsc -b",
|
|
7
8
|
"dev": "sleep 387 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
|
|
@@ -23,12 +24,13 @@
|
|
|
23
24
|
"ajv": "*"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
|
-
"@owlmeans/auth": "^0.1.
|
|
27
|
-
"@owlmeans/error": "^0.1.
|
|
27
|
+
"@owlmeans/auth": "^0.1.3",
|
|
28
|
+
"@owlmeans/error": "^0.1.3"
|
|
28
29
|
},
|
|
29
30
|
"devDependencies": {
|
|
31
|
+
"@owlmeans/dep-config": "workspace:*",
|
|
30
32
|
"nodemon": "^3.1.11",
|
|
31
|
-
"typescript": "^
|
|
33
|
+
"typescript": "^6.0.2"
|
|
32
34
|
},
|
|
33
35
|
"publishConfig": {
|
|
34
36
|
"access": "public"
|
package/tsconfig.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": [
|
|
3
|
-
"
|
|
3
|
+
"@owlmeans/dep-config/tsconfig.base.json"
|
|
4
4
|
],
|
|
5
5
|
"compilerOptions": {
|
|
6
|
-
"rootDir": "./src/",
|
|
7
|
-
"outDir": "./build/"
|
|
8
|
-
"moduleResolution": "Bundler",
|
|
6
|
+
"rootDir": "./src/",
|
|
7
|
+
"outDir": "./build/"
|
|
9
8
|
},
|
|
10
9
|
"exclude": [
|
|
11
10
|
"./dist/**/*",
|