@objectstack/service-storage 3.0.8 → 3.0.10

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.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/service-storage@3.0.8 build /home/runner/work/spec/spec/packages/services/service-storage
2
+ > @objectstack/service-storage@3.0.10 build /home/runner/work/spec/spec/packages/services/service-storage
3
3
  > tsup --config ../../../tsup.config.ts
4
4
 
5
5
  CLI Building entry: src/index.ts
@@ -10,13 +10,13 @@
10
10
  CLI Cleaning output folder
11
11
  ESM Build start
12
12
  CJS Build start
13
- CJS dist/index.cjs 4.85 KB
14
- CJS dist/index.cjs.map 10.56 KB
15
- CJS ⚡️ Build success in 74ms
16
- ESM dist/index.js 3.52 KB
17
- ESM dist/index.js.map 9.96 KB
18
- ESM ⚡️ Build success in 77ms
13
+ ESM dist/index.js 4.04 KB
14
+ ESM dist/index.js.map 10.95 KB
15
+ ESM ⚡️ Build success in 93ms
16
+ CJS dist/index.cjs 5.36 KB
17
+ CJS dist/index.cjs.map 11.56 KB
18
+ CJS ⚡️ Build success in 95ms
19
19
  DTS Build start
20
- DTS ⚡️ Build success in 7345ms
21
- DTS dist/index.d.ts 3.83 KB
22
- DTS dist/index.d.cts 3.83 KB
20
+ DTS ⚡️ Build success in 7790ms
21
+ DTS dist/index.d.ts 4.20 KB
22
+ DTS dist/index.d.cts 4.20 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # @objectstack/service-storage
2
2
 
3
+ ## 3.0.10
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [d1e5d31]
8
+ - @objectstack/spec@3.0.10
9
+ - @objectstack/core@3.0.10
10
+
11
+ ## 3.0.9
12
+
13
+ ### Patch Changes
14
+
15
+ - Updated dependencies [15e0df6]
16
+ - @objectstack/spec@3.0.9
17
+ - @objectstack/core@3.0.9
18
+
3
19
  ## 3.0.8
4
20
 
5
21
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -147,6 +147,18 @@ var S3StorageAdapter = class {
147
147
  async getSignedUrl(_key, _expiresIn) {
148
148
  throw new Error("S3StorageAdapter not yet implemented");
149
149
  }
150
+ async initiateChunkedUpload(_key, _options) {
151
+ throw new Error("S3StorageAdapter.initiateChunkedUpload not yet implemented");
152
+ }
153
+ async uploadChunk(_uploadId, _partNumber, _data) {
154
+ throw new Error("S3StorageAdapter.uploadChunk not yet implemented");
155
+ }
156
+ async completeChunkedUpload(_uploadId, _parts) {
157
+ throw new Error("S3StorageAdapter.completeChunkedUpload not yet implemented");
158
+ }
159
+ async abortChunkedUpload(_uploadId) {
160
+ throw new Error("S3StorageAdapter.abortChunkedUpload not yet implemented");
161
+ }
150
162
  };
151
163
  // Annotate the CommonJS export names for ESM import in node:
152
164
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/local-storage-adapter.ts","../src/storage-service-plugin.ts","../src/s3-storage-adapter.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport { StorageServicePlugin } from './storage-service-plugin.js';\nexport type { StorageServicePluginOptions } from './storage-service-plugin.js';\nexport { LocalStorageAdapter } from './local-storage-adapter.js';\nexport type { LocalStorageAdapterOptions } from './local-storage-adapter.js';\nexport { S3StorageAdapter } from './s3-storage-adapter.js';\nexport type { S3StorageAdapterOptions } from './s3-storage-adapter.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { promises as fs } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration options for LocalStorageAdapter.\n */\nexport interface LocalStorageAdapterOptions {\n /** Root directory for file storage */\n rootDir: string;\n}\n\n/**\n * Local filesystem storage adapter implementing IStorageService.\n *\n * Stores files on the local disk under a configurable root directory.\n * Suitable for development, testing, and single-server deployments.\n */\nexport class LocalStorageAdapter implements IStorageService {\n private readonly rootDir: string;\n\n constructor(options: LocalStorageAdapterOptions) {\n this.rootDir = options.rootDir;\n }\n\n private resolvePath(key: string): string {\n return join(this.rootDir, key);\n }\n\n async upload(key: string, data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.mkdir(dirname(filePath), { recursive: true });\n\n if (data instanceof Buffer) {\n await fs.writeFile(filePath, data);\n } else {\n // Convert ReadableStream to Buffer\n const chunks: Uint8Array[] = [];\n const reader = (data as ReadableStream).getReader();\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) chunks.push(result.value);\n }\n await fs.writeFile(filePath, Buffer.concat(chunks));\n }\n }\n\n async download(key: string): Promise<Buffer> {\n const filePath = this.resolvePath(key);\n return fs.readFile(filePath);\n }\n\n async delete(key: string): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.unlink(filePath);\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n await fs.access(this.resolvePath(key));\n return true;\n } catch {\n return false;\n }\n }\n\n async getInfo(key: string): Promise<StorageFileInfo> {\n const filePath = this.resolvePath(key);\n const stat = await fs.stat(filePath);\n return {\n key,\n size: stat.size,\n lastModified: stat.mtime,\n };\n }\n\n async list(prefix: string): Promise<StorageFileInfo[]> {\n const dirPath = this.resolvePath(prefix);\n try {\n const entries = await fs.readdir(dirPath);\n const results: StorageFileInfo[] = [];\n for (const entry of entries) {\n const fullKey = prefix ? `${prefix}/${entry}` : entry;\n try {\n const info = await this.getInfo(fullKey);\n results.push(info);\n } catch {\n // Skip entries that can't be stat'd\n }\n }\n return results;\n } catch {\n return [];\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { LocalStorageAdapter } from './local-storage-adapter.js';\nimport type { LocalStorageAdapterOptions } from './local-storage-adapter.js';\n\n/**\n * Configuration options for the StorageServicePlugin.\n */\nexport interface StorageServicePluginOptions {\n /** Storage adapter type (default: 'local') */\n adapter?: 'local' | 's3';\n /** Options for the local storage adapter */\n local?: LocalStorageAdapterOptions;\n /** S3 configuration (used when adapter is 's3') */\n s3?: { bucket: string; region: string; endpoint?: string };\n}\n\n/**\n * StorageServicePlugin — Production IStorageService implementation.\n *\n * Registers a file storage service with the kernel during the init phase.\n * Supports local filesystem and S3 adapters.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { StorageServicePlugin } from '@objectstack/service-storage';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new StorageServicePlugin({\n * adapter: 'local',\n * local: { rootDir: './uploads' },\n * }));\n * await kernel.bootstrap();\n *\n * const storage = kernel.getService('file-storage');\n * await storage.upload('file.txt', Buffer.from('hello'));\n * ```\n */\nexport class StorageServicePlugin implements Plugin {\n name = 'com.objectstack.service.storage';\n version = '1.0.0';\n type = 'standard';\n\n private readonly options: StorageServicePluginOptions;\n\n constructor(options: StorageServicePluginOptions = {}) {\n this.options = { adapter: 'local', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const adapter = this.options.adapter;\n if (adapter === 's3') {\n throw new Error(\n 'S3 storage adapter is not yet implemented. ' +\n 'Use adapter: \"local\" or provide a custom IStorageService via ctx.registerService(\"file-storage\", impl).'\n );\n }\n\n const rootDir = this.options.local?.rootDir ?? './storage';\n const storage = new LocalStorageAdapter({ rootDir });\n ctx.registerService('file-storage', storage);\n ctx.logger.info(`StorageServicePlugin: registered local storage adapter (root: ${rootDir})`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration for the S3 storage adapter.\n */\nexport interface S3StorageAdapterOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (e.g. 'us-east-1') */\n region: string;\n /** Optional endpoint URL for S3-compatible services (MinIO, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n}\n\n/**\n * S3 storage adapter skeleton implementing IStorageService.\n *\n * This is a placeholder for future AWS S3 integration.\n * Concrete implementation will use the `@aws-sdk/client-s3` package.\n *\n * @example\n * ```ts\n * const storage = new S3StorageAdapter({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * });\n * await storage.upload('path/to/file.txt', buffer);\n * ```\n */\nexport class S3StorageAdapter implements IStorageService {\n private readonly bucket: string;\n private readonly region: string;\n\n constructor(options: S3StorageAdapterOptions) {\n this.bucket = options.bucket;\n this.region = options.region;\n }\n\n async upload(_key: string, _data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n throw new Error(`S3StorageAdapter not yet implemented (bucket: ${this.bucket}, region: ${this.region})`);\n }\n\n async download(_key: string): Promise<Buffer> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async delete(_key: string): Promise<void> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async exists(_key: string): Promise<boolean> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getInfo(_key: string): Promise<StorageFileInfo> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async list(_prefix: string): Promise<StorageFileInfo[]> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getSignedUrl(_key: string, _expiresIn: number): Promise<string> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n}\n"],"mappings":"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","names":["fs"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/local-storage-adapter.ts","../src/storage-service-plugin.ts","../src/s3-storage-adapter.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nexport { StorageServicePlugin } from './storage-service-plugin.js';\nexport type { StorageServicePluginOptions } from './storage-service-plugin.js';\nexport { LocalStorageAdapter } from './local-storage-adapter.js';\nexport type { LocalStorageAdapterOptions } from './local-storage-adapter.js';\nexport { S3StorageAdapter } from './s3-storage-adapter.js';\nexport type { S3StorageAdapterOptions } from './s3-storage-adapter.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { promises as fs } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration options for LocalStorageAdapter.\n */\nexport interface LocalStorageAdapterOptions {\n /** Root directory for file storage */\n rootDir: string;\n}\n\n/**\n * Local filesystem storage adapter implementing IStorageService.\n *\n * Stores files on the local disk under a configurable root directory.\n * Suitable for development, testing, and single-server deployments.\n */\nexport class LocalStorageAdapter implements IStorageService {\n private readonly rootDir: string;\n\n constructor(options: LocalStorageAdapterOptions) {\n this.rootDir = options.rootDir;\n }\n\n private resolvePath(key: string): string {\n return join(this.rootDir, key);\n }\n\n async upload(key: string, data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.mkdir(dirname(filePath), { recursive: true });\n\n if (data instanceof Buffer) {\n await fs.writeFile(filePath, data);\n } else {\n // Convert ReadableStream to Buffer\n const chunks: Uint8Array[] = [];\n const reader = (data as ReadableStream).getReader();\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) chunks.push(result.value);\n }\n await fs.writeFile(filePath, Buffer.concat(chunks));\n }\n }\n\n async download(key: string): Promise<Buffer> {\n const filePath = this.resolvePath(key);\n return fs.readFile(filePath);\n }\n\n async delete(key: string): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.unlink(filePath);\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n await fs.access(this.resolvePath(key));\n return true;\n } catch {\n return false;\n }\n }\n\n async getInfo(key: string): Promise<StorageFileInfo> {\n const filePath = this.resolvePath(key);\n const stat = await fs.stat(filePath);\n return {\n key,\n size: stat.size,\n lastModified: stat.mtime,\n };\n }\n\n async list(prefix: string): Promise<StorageFileInfo[]> {\n const dirPath = this.resolvePath(prefix);\n try {\n const entries = await fs.readdir(dirPath);\n const results: StorageFileInfo[] = [];\n for (const entry of entries) {\n const fullKey = prefix ? `${prefix}/${entry}` : entry;\n try {\n const info = await this.getInfo(fullKey);\n results.push(info);\n } catch {\n // Skip entries that can't be stat'd\n }\n }\n return results;\n } catch {\n return [];\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { LocalStorageAdapter } from './local-storage-adapter.js';\nimport type { LocalStorageAdapterOptions } from './local-storage-adapter.js';\n\n/**\n * Configuration options for the StorageServicePlugin.\n */\nexport interface StorageServicePluginOptions {\n /** Storage adapter type (default: 'local') */\n adapter?: 'local' | 's3';\n /** Options for the local storage adapter */\n local?: LocalStorageAdapterOptions;\n /** S3 configuration (used when adapter is 's3') */\n s3?: { bucket: string; region: string; endpoint?: string };\n}\n\n/**\n * StorageServicePlugin — Production IStorageService implementation.\n *\n * Registers a file storage service with the kernel during the init phase.\n * Supports local filesystem and S3 adapters.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { StorageServicePlugin } from '@objectstack/service-storage';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new StorageServicePlugin({\n * adapter: 'local',\n * local: { rootDir: './uploads' },\n * }));\n * await kernel.bootstrap();\n *\n * const storage = kernel.getService('file-storage');\n * await storage.upload('file.txt', Buffer.from('hello'));\n * ```\n */\nexport class StorageServicePlugin implements Plugin {\n name = 'com.objectstack.service.storage';\n version = '1.0.0';\n type = 'standard';\n\n private readonly options: StorageServicePluginOptions;\n\n constructor(options: StorageServicePluginOptions = {}) {\n this.options = { adapter: 'local', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const adapter = this.options.adapter;\n if (adapter === 's3') {\n throw new Error(\n 'S3 storage adapter is not yet implemented. ' +\n 'Use adapter: \"local\" or provide a custom IStorageService via ctx.registerService(\"file-storage\", impl).'\n );\n }\n\n const rootDir = this.options.local?.rootDir ?? './storage';\n const storage = new LocalStorageAdapter({ rootDir });\n ctx.registerService('file-storage', storage);\n ctx.logger.info(`StorageServicePlugin: registered local storage adapter (root: ${rootDir})`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration for the S3 storage adapter.\n */\nexport interface S3StorageAdapterOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (e.g. 'us-east-1') */\n region: string;\n /** Optional endpoint URL for S3-compatible services (MinIO, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n}\n\n/**\n * S3 storage adapter skeleton implementing IStorageService.\n *\n * This is a placeholder for future AWS S3 integration.\n * Concrete implementation will use the `@aws-sdk/client-s3` package.\n *\n * @example\n * ```ts\n * const storage = new S3StorageAdapter({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * });\n * await storage.upload('path/to/file.txt', buffer);\n * ```\n */\nexport class S3StorageAdapter implements IStorageService {\n private readonly bucket: string;\n private readonly region: string;\n\n constructor(options: S3StorageAdapterOptions) {\n this.bucket = options.bucket;\n this.region = options.region;\n }\n\n async upload(_key: string, _data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n throw new Error(`S3StorageAdapter not yet implemented (bucket: ${this.bucket}, region: ${this.region})`);\n }\n\n async download(_key: string): Promise<Buffer> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async delete(_key: string): Promise<void> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async exists(_key: string): Promise<boolean> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getInfo(_key: string): Promise<StorageFileInfo> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async list(_prefix: string): Promise<StorageFileInfo[]> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getSignedUrl(_key: string, _expiresIn: number): Promise<string> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async initiateChunkedUpload(_key: string, _options?: StorageUploadOptions): Promise<string> {\n throw new Error('S3StorageAdapter.initiateChunkedUpload not yet implemented');\n }\n\n async uploadChunk(_uploadId: string, _partNumber: number, _data: Buffer): Promise<string> {\n throw new Error('S3StorageAdapter.uploadChunk not yet implemented');\n }\n\n async completeChunkedUpload(_uploadId: string, _parts: Array<{ partNumber: number; eTag: string }>): Promise<string> {\n throw new Error('S3StorageAdapter.completeChunkedUpload not yet implemented');\n }\n\n async abortChunkedUpload(_uploadId: string): Promise<void> {\n throw new Error('S3StorageAdapter.abortChunkedUpload not yet implemented');\n }\n}\n"],"mappings":"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","names":["fs"]}
package/dist/index.d.cts CHANGED
@@ -113,6 +113,13 @@ declare class S3StorageAdapter implements IStorageService {
113
113
  getInfo(_key: string): Promise<StorageFileInfo>;
114
114
  list(_prefix: string): Promise<StorageFileInfo[]>;
115
115
  getSignedUrl(_key: string, _expiresIn: number): Promise<string>;
116
+ initiateChunkedUpload(_key: string, _options?: StorageUploadOptions): Promise<string>;
117
+ uploadChunk(_uploadId: string, _partNumber: number, _data: Buffer): Promise<string>;
118
+ completeChunkedUpload(_uploadId: string, _parts: Array<{
119
+ partNumber: number;
120
+ eTag: string;
121
+ }>): Promise<string>;
122
+ abortChunkedUpload(_uploadId: string): Promise<void>;
116
123
  }
117
124
 
118
125
  export { LocalStorageAdapter, type LocalStorageAdapterOptions, S3StorageAdapter, type S3StorageAdapterOptions, StorageServicePlugin, type StorageServicePluginOptions };
package/dist/index.d.ts CHANGED
@@ -113,6 +113,13 @@ declare class S3StorageAdapter implements IStorageService {
113
113
  getInfo(_key: string): Promise<StorageFileInfo>;
114
114
  list(_prefix: string): Promise<StorageFileInfo[]>;
115
115
  getSignedUrl(_key: string, _expiresIn: number): Promise<string>;
116
+ initiateChunkedUpload(_key: string, _options?: StorageUploadOptions): Promise<string>;
117
+ uploadChunk(_uploadId: string, _partNumber: number, _data: Buffer): Promise<string>;
118
+ completeChunkedUpload(_uploadId: string, _parts: Array<{
119
+ partNumber: number;
120
+ eTag: string;
121
+ }>): Promise<string>;
122
+ abortChunkedUpload(_uploadId: string): Promise<void>;
116
123
  }
117
124
 
118
125
  export { LocalStorageAdapter, type LocalStorageAdapterOptions, S3StorageAdapter, type S3StorageAdapterOptions, StorageServicePlugin, type StorageServicePluginOptions };
package/dist/index.js CHANGED
@@ -119,6 +119,18 @@ var S3StorageAdapter = class {
119
119
  async getSignedUrl(_key, _expiresIn) {
120
120
  throw new Error("S3StorageAdapter not yet implemented");
121
121
  }
122
+ async initiateChunkedUpload(_key, _options) {
123
+ throw new Error("S3StorageAdapter.initiateChunkedUpload not yet implemented");
124
+ }
125
+ async uploadChunk(_uploadId, _partNumber, _data) {
126
+ throw new Error("S3StorageAdapter.uploadChunk not yet implemented");
127
+ }
128
+ async completeChunkedUpload(_uploadId, _parts) {
129
+ throw new Error("S3StorageAdapter.completeChunkedUpload not yet implemented");
130
+ }
131
+ async abortChunkedUpload(_uploadId) {
132
+ throw new Error("S3StorageAdapter.abortChunkedUpload not yet implemented");
133
+ }
122
134
  };
123
135
  export {
124
136
  LocalStorageAdapter,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/local-storage-adapter.ts","../src/storage-service-plugin.ts","../src/s3-storage-adapter.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { promises as fs } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration options for LocalStorageAdapter.\n */\nexport interface LocalStorageAdapterOptions {\n /** Root directory for file storage */\n rootDir: string;\n}\n\n/**\n * Local filesystem storage adapter implementing IStorageService.\n *\n * Stores files on the local disk under a configurable root directory.\n * Suitable for development, testing, and single-server deployments.\n */\nexport class LocalStorageAdapter implements IStorageService {\n private readonly rootDir: string;\n\n constructor(options: LocalStorageAdapterOptions) {\n this.rootDir = options.rootDir;\n }\n\n private resolvePath(key: string): string {\n return join(this.rootDir, key);\n }\n\n async upload(key: string, data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.mkdir(dirname(filePath), { recursive: true });\n\n if (data instanceof Buffer) {\n await fs.writeFile(filePath, data);\n } else {\n // Convert ReadableStream to Buffer\n const chunks: Uint8Array[] = [];\n const reader = (data as ReadableStream).getReader();\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) chunks.push(result.value);\n }\n await fs.writeFile(filePath, Buffer.concat(chunks));\n }\n }\n\n async download(key: string): Promise<Buffer> {\n const filePath = this.resolvePath(key);\n return fs.readFile(filePath);\n }\n\n async delete(key: string): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.unlink(filePath);\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n await fs.access(this.resolvePath(key));\n return true;\n } catch {\n return false;\n }\n }\n\n async getInfo(key: string): Promise<StorageFileInfo> {\n const filePath = this.resolvePath(key);\n const stat = await fs.stat(filePath);\n return {\n key,\n size: stat.size,\n lastModified: stat.mtime,\n };\n }\n\n async list(prefix: string): Promise<StorageFileInfo[]> {\n const dirPath = this.resolvePath(prefix);\n try {\n const entries = await fs.readdir(dirPath);\n const results: StorageFileInfo[] = [];\n for (const entry of entries) {\n const fullKey = prefix ? `${prefix}/${entry}` : entry;\n try {\n const info = await this.getInfo(fullKey);\n results.push(info);\n } catch {\n // Skip entries that can't be stat'd\n }\n }\n return results;\n } catch {\n return [];\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { LocalStorageAdapter } from './local-storage-adapter.js';\nimport type { LocalStorageAdapterOptions } from './local-storage-adapter.js';\n\n/**\n * Configuration options for the StorageServicePlugin.\n */\nexport interface StorageServicePluginOptions {\n /** Storage adapter type (default: 'local') */\n adapter?: 'local' | 's3';\n /** Options for the local storage adapter */\n local?: LocalStorageAdapterOptions;\n /** S3 configuration (used when adapter is 's3') */\n s3?: { bucket: string; region: string; endpoint?: string };\n}\n\n/**\n * StorageServicePlugin — Production IStorageService implementation.\n *\n * Registers a file storage service with the kernel during the init phase.\n * Supports local filesystem and S3 adapters.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { StorageServicePlugin } from '@objectstack/service-storage';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new StorageServicePlugin({\n * adapter: 'local',\n * local: { rootDir: './uploads' },\n * }));\n * await kernel.bootstrap();\n *\n * const storage = kernel.getService('file-storage');\n * await storage.upload('file.txt', Buffer.from('hello'));\n * ```\n */\nexport class StorageServicePlugin implements Plugin {\n name = 'com.objectstack.service.storage';\n version = '1.0.0';\n type = 'standard';\n\n private readonly options: StorageServicePluginOptions;\n\n constructor(options: StorageServicePluginOptions = {}) {\n this.options = { adapter: 'local', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const adapter = this.options.adapter;\n if (adapter === 's3') {\n throw new Error(\n 'S3 storage adapter is not yet implemented. ' +\n 'Use adapter: \"local\" or provide a custom IStorageService via ctx.registerService(\"file-storage\", impl).'\n );\n }\n\n const rootDir = this.options.local?.rootDir ?? './storage';\n const storage = new LocalStorageAdapter({ rootDir });\n ctx.registerService('file-storage', storage);\n ctx.logger.info(`StorageServicePlugin: registered local storage adapter (root: ${rootDir})`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration for the S3 storage adapter.\n */\nexport interface S3StorageAdapterOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (e.g. 'us-east-1') */\n region: string;\n /** Optional endpoint URL for S3-compatible services (MinIO, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n}\n\n/**\n * S3 storage adapter skeleton implementing IStorageService.\n *\n * This is a placeholder for future AWS S3 integration.\n * Concrete implementation will use the `@aws-sdk/client-s3` package.\n *\n * @example\n * ```ts\n * const storage = new S3StorageAdapter({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * });\n * await storage.upload('path/to/file.txt', buffer);\n * ```\n */\nexport class S3StorageAdapter implements IStorageService {\n private readonly bucket: string;\n private readonly region: string;\n\n constructor(options: S3StorageAdapterOptions) {\n this.bucket = options.bucket;\n this.region = options.region;\n }\n\n async upload(_key: string, _data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n throw new Error(`S3StorageAdapter not yet implemented (bucket: ${this.bucket}, region: ${this.region})`);\n }\n\n async download(_key: string): Promise<Buffer> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async delete(_key: string): Promise<void> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async exists(_key: string): Promise<boolean> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getInfo(_key: string): Promise<StorageFileInfo> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async list(_prefix: string): Promise<StorageFileInfo[]> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getSignedUrl(_key: string, _expiresIn: number): Promise<string> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/local-storage-adapter.ts","../src/storage-service-plugin.ts","../src/s3-storage-adapter.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { promises as fs } from 'node:fs';\nimport { join, dirname } from 'node:path';\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration options for LocalStorageAdapter.\n */\nexport interface LocalStorageAdapterOptions {\n /** Root directory for file storage */\n rootDir: string;\n}\n\n/**\n * Local filesystem storage adapter implementing IStorageService.\n *\n * Stores files on the local disk under a configurable root directory.\n * Suitable for development, testing, and single-server deployments.\n */\nexport class LocalStorageAdapter implements IStorageService {\n private readonly rootDir: string;\n\n constructor(options: LocalStorageAdapterOptions) {\n this.rootDir = options.rootDir;\n }\n\n private resolvePath(key: string): string {\n return join(this.rootDir, key);\n }\n\n async upload(key: string, data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.mkdir(dirname(filePath), { recursive: true });\n\n if (data instanceof Buffer) {\n await fs.writeFile(filePath, data);\n } else {\n // Convert ReadableStream to Buffer\n const chunks: Uint8Array[] = [];\n const reader = (data as ReadableStream).getReader();\n let done = false;\n while (!done) {\n const result = await reader.read();\n done = result.done;\n if (result.value) chunks.push(result.value);\n }\n await fs.writeFile(filePath, Buffer.concat(chunks));\n }\n }\n\n async download(key: string): Promise<Buffer> {\n const filePath = this.resolvePath(key);\n return fs.readFile(filePath);\n }\n\n async delete(key: string): Promise<void> {\n const filePath = this.resolvePath(key);\n await fs.unlink(filePath);\n }\n\n async exists(key: string): Promise<boolean> {\n try {\n await fs.access(this.resolvePath(key));\n return true;\n } catch {\n return false;\n }\n }\n\n async getInfo(key: string): Promise<StorageFileInfo> {\n const filePath = this.resolvePath(key);\n const stat = await fs.stat(filePath);\n return {\n key,\n size: stat.size,\n lastModified: stat.mtime,\n };\n }\n\n async list(prefix: string): Promise<StorageFileInfo[]> {\n const dirPath = this.resolvePath(prefix);\n try {\n const entries = await fs.readdir(dirPath);\n const results: StorageFileInfo[] = [];\n for (const entry of entries) {\n const fullKey = prefix ? `${prefix}/${entry}` : entry;\n try {\n const info = await this.getInfo(fullKey);\n results.push(info);\n } catch {\n // Skip entries that can't be stat'd\n }\n }\n return results;\n } catch {\n return [];\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { LocalStorageAdapter } from './local-storage-adapter.js';\nimport type { LocalStorageAdapterOptions } from './local-storage-adapter.js';\n\n/**\n * Configuration options for the StorageServicePlugin.\n */\nexport interface StorageServicePluginOptions {\n /** Storage adapter type (default: 'local') */\n adapter?: 'local' | 's3';\n /** Options for the local storage adapter */\n local?: LocalStorageAdapterOptions;\n /** S3 configuration (used when adapter is 's3') */\n s3?: { bucket: string; region: string; endpoint?: string };\n}\n\n/**\n * StorageServicePlugin — Production IStorageService implementation.\n *\n * Registers a file storage service with the kernel during the init phase.\n * Supports local filesystem and S3 adapters.\n *\n * @example\n * ```ts\n * import { ObjectKernel } from '@objectstack/core';\n * import { StorageServicePlugin } from '@objectstack/service-storage';\n *\n * const kernel = new ObjectKernel();\n * kernel.use(new StorageServicePlugin({\n * adapter: 'local',\n * local: { rootDir: './uploads' },\n * }));\n * await kernel.bootstrap();\n *\n * const storage = kernel.getService('file-storage');\n * await storage.upload('file.txt', Buffer.from('hello'));\n * ```\n */\nexport class StorageServicePlugin implements Plugin {\n name = 'com.objectstack.service.storage';\n version = '1.0.0';\n type = 'standard';\n\n private readonly options: StorageServicePluginOptions;\n\n constructor(options: StorageServicePluginOptions = {}) {\n this.options = { adapter: 'local', ...options };\n }\n\n async init(ctx: PluginContext): Promise<void> {\n const adapter = this.options.adapter;\n if (adapter === 's3') {\n throw new Error(\n 'S3 storage adapter is not yet implemented. ' +\n 'Use adapter: \"local\" or provide a custom IStorageService via ctx.registerService(\"file-storage\", impl).'\n );\n }\n\n const rootDir = this.options.local?.rootDir ?? './storage';\n const storage = new LocalStorageAdapter({ rootDir });\n ctx.registerService('file-storage', storage);\n ctx.logger.info(`StorageServicePlugin: registered local storage adapter (root: ${rootDir})`);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IStorageService, StorageUploadOptions, StorageFileInfo } from '@objectstack/spec/contracts';\n\n/**\n * Configuration for the S3 storage adapter.\n */\nexport interface S3StorageAdapterOptions {\n /** S3 bucket name */\n bucket: string;\n /** AWS region (e.g. 'us-east-1') */\n region: string;\n /** Optional endpoint URL for S3-compatible services (MinIO, etc.) */\n endpoint?: string;\n /** AWS access key ID */\n accessKeyId?: string;\n /** AWS secret access key */\n secretAccessKey?: string;\n}\n\n/**\n * S3 storage adapter skeleton implementing IStorageService.\n *\n * This is a placeholder for future AWS S3 integration.\n * Concrete implementation will use the `@aws-sdk/client-s3` package.\n *\n * @example\n * ```ts\n * const storage = new S3StorageAdapter({\n * bucket: 'my-bucket',\n * region: 'us-east-1',\n * });\n * await storage.upload('path/to/file.txt', buffer);\n * ```\n */\nexport class S3StorageAdapter implements IStorageService {\n private readonly bucket: string;\n private readonly region: string;\n\n constructor(options: S3StorageAdapterOptions) {\n this.bucket = options.bucket;\n this.region = options.region;\n }\n\n async upload(_key: string, _data: Buffer | ReadableStream, _options?: StorageUploadOptions): Promise<void> {\n throw new Error(`S3StorageAdapter not yet implemented (bucket: ${this.bucket}, region: ${this.region})`);\n }\n\n async download(_key: string): Promise<Buffer> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async delete(_key: string): Promise<void> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async exists(_key: string): Promise<boolean> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getInfo(_key: string): Promise<StorageFileInfo> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async list(_prefix: string): Promise<StorageFileInfo[]> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async getSignedUrl(_key: string, _expiresIn: number): Promise<string> {\n throw new Error('S3StorageAdapter not yet implemented');\n }\n\n async initiateChunkedUpload(_key: string, _options?: StorageUploadOptions): Promise<string> {\n throw new Error('S3StorageAdapter.initiateChunkedUpload not yet implemented');\n }\n\n async uploadChunk(_uploadId: string, _partNumber: number, _data: Buffer): Promise<string> {\n throw new Error('S3StorageAdapter.uploadChunk not yet implemented');\n }\n\n async completeChunkedUpload(_uploadId: string, _parts: Array<{ partNumber: number; eTag: string }>): Promise<string> {\n throw new Error('S3StorageAdapter.completeChunkedUpload not yet implemented');\n }\n\n async abortChunkedUpload(_uploadId: string): Promise<void> {\n throw new Error('S3StorageAdapter.abortChunkedUpload not yet implemented');\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/service-storage",
3
- "version": "3.0.8",
3
+ "version": "3.0.10",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Storage Service for ObjectStack — implements IStorageService with local filesystem and S3 adapter skeleton",
6
6
  "type": "module",
@@ -14,13 +14,13 @@
14
14
  }
15
15
  },
16
16
  "dependencies": {
17
- "@objectstack/core": "3.0.8",
18
- "@objectstack/spec": "3.0.8"
17
+ "@objectstack/core": "3.0.10",
18
+ "@objectstack/spec": "3.0.10"
19
19
  },
20
20
  "devDependencies": {
21
21
  "typescript": "^5.0.0",
22
22
  "vitest": "^4.0.18",
23
- "@types/node": "^25.2.3"
23
+ "@types/node": "^25.3.0"
24
24
  },
25
25
  "scripts": {
26
26
  "build": "tsup --config ../../../tsup.config.ts",
@@ -69,4 +69,20 @@ export class S3StorageAdapter implements IStorageService {
69
69
  async getSignedUrl(_key: string, _expiresIn: number): Promise<string> {
70
70
  throw new Error('S3StorageAdapter not yet implemented');
71
71
  }
72
+
73
+ async initiateChunkedUpload(_key: string, _options?: StorageUploadOptions): Promise<string> {
74
+ throw new Error('S3StorageAdapter.initiateChunkedUpload not yet implemented');
75
+ }
76
+
77
+ async uploadChunk(_uploadId: string, _partNumber: number, _data: Buffer): Promise<string> {
78
+ throw new Error('S3StorageAdapter.uploadChunk not yet implemented');
79
+ }
80
+
81
+ async completeChunkedUpload(_uploadId: string, _parts: Array<{ partNumber: number; eTag: string }>): Promise<string> {
82
+ throw new Error('S3StorageAdapter.completeChunkedUpload not yet implemented');
83
+ }
84
+
85
+ async abortChunkedUpload(_uploadId: string): Promise<void> {
86
+ throw new Error('S3StorageAdapter.abortChunkedUpload not yet implemented');
87
+ }
72
88
  }