@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
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
- Common storage interfaces and utilities for OwlMeans file storage systems. This package provides shared types, validation schemas, and error handling for object storage solutions including S3-compatible storage, image storage, and file management systems.
3
+ Shared types and schemas for OwlMeans object storage file metadata, instances, and status enums.
4
4
 
5
5
  ## Overview
6
6
 
7
- The `@owlmeans/storage-common` package serves as the foundation for OwlMeans storage implementations. It provides:
8
-
9
- - **File Storage Interfaces**: Common types for stored files and file metadata
10
- - **Validation Schemas**: AJV schemas for file validation and integrity
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
- npm install @owlmeans/storage-common ajv
15
+ bun add @owlmeans/storage-common
27
16
  ```
28
17
 
29
- ## Core Concepts
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
- ### Status Tracking
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 // Associated entity identifier
54
- sourceName?: string // Original filename from upload
55
- name?: string // Display name for the file
56
- title?: string // Human-readable title
57
- scopes: string[] // Authorization scopes
58
- mimeType: string // MIME type of the file
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
- ## Integration with Other Packages
33
+ ### `StoredFile`
552
34
 
553
- ### Authentication Integration
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
- // File scopes use authentication scope validation
559
- ```
37
+ ### `StoredFileWithData`
560
38
 
561
- ### Error System Integration
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
- ## Best Practices
41
+ ### `StoredFileStatus`
578
42
 
579
- 1. **Scope Management**: Use specific scopes for fine-grained access control
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
- ## Dependencies
45
+ ### `StoredFileFormat`
588
46
 
589
- This package depends on:
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) - Storage resource implementations
597
- - [`@owlmeans/static-resource`](../static-resource) - Static resource management
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.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.1",
27
- "@owlmeans/error": "^0.1.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": "^5.8.3"
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
- "../tsconfig.default.json",
3
+ "@owlmeans/dep-config/tsconfig.base.json"
4
4
  ],
5
5
  "compilerOptions": {
6
- "rootDir": "./src/", /* Specify the root folder within your source files. */
7
- "outDir": "./build/", /* Specify an output folder for all emitted files. */
8
- "moduleResolution": "Bundler",
6
+ "rootDir": "./src/",
7
+ "outDir": "./build/"
9
8
  },
10
9
  "exclude": [
11
10
  "./dist/**/*",