@objectstack/service-storage 3.0.7 → 3.0.9
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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +16 -0
- package/dist/index.cjs +12 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +12 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/s3-storage-adapter.ts +16 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/service-storage@3.0.
|
|
2
|
+
> @objectstack/service-storage@3.0.9 build /home/runner/work/spec/spec/packages/services/service-storage
|
|
3
3
|
> tsup --config ../../../tsup.config.ts
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/index.ts
|
|
@@ -10,13 +10,13 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[34mCJS[39m Build start
|
|
13
|
-
[32mESM[39m [1mdist/index.js [22m[
|
|
14
|
-
[32mESM[39m [1mdist/index.js.map [22m[
|
|
15
|
-
[32mESM[39m ⚡️ Build success in
|
|
16
|
-
[32mCJS[39m [1mdist/index.cjs [22m[
|
|
17
|
-
[32mCJS[39m [1mdist/index.cjs.map [22m[
|
|
18
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
+
[32mESM[39m [1mdist/index.js [22m[32m4.04 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m10.95 KB[39m
|
|
15
|
+
[32mESM[39m ⚡️ Build success in 48ms
|
|
16
|
+
[32mCJS[39m [1mdist/index.cjs [22m[32m5.36 KB[39m
|
|
17
|
+
[32mCJS[39m [1mdist/index.cjs.map [22m[32m11.56 KB[39m
|
|
18
|
+
[32mCJS[39m ⚡️ Build success in 49ms
|
|
19
19
|
[34mDTS[39m Build start
|
|
20
|
-
[32mDTS[39m ⚡️ Build success in
|
|
21
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
22
|
-
[32mDTS[39m [1mdist/index.d.cts [22m[
|
|
20
|
+
[32mDTS[39m ⚡️ Build success in 13804ms
|
|
21
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m4.20 KB[39m
|
|
22
|
+
[32mDTS[39m [1mdist/index.d.cts [22m[32m4.20 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @objectstack/service-storage
|
|
2
2
|
|
|
3
|
+
## 3.0.9
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [15e0df6]
|
|
8
|
+
- @objectstack/spec@3.0.9
|
|
9
|
+
- @objectstack/core@3.0.9
|
|
10
|
+
|
|
11
|
+
## 3.0.8
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [5a968a2]
|
|
16
|
+
- @objectstack/spec@3.0.8
|
|
17
|
+
- @objectstack/core@3.0.8
|
|
18
|
+
|
|
3
19
|
## 3.0.7
|
|
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 = {
|
package/dist/index.cjs.map
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "3.0.9",
|
|
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,8 +14,8 @@
|
|
|
14
14
|
}
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@objectstack/core": "3.0.
|
|
18
|
-
"@objectstack/spec": "3.0.
|
|
17
|
+
"@objectstack/core": "3.0.9",
|
|
18
|
+
"@objectstack/spec": "3.0.9"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"typescript": "^5.0.0",
|
|
@@ -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
|
}
|