snapshot of https://github.com/ueberdosis/hocuspocus.git @ cf3cfa355b4d3bb61c6b6c80071f71783e32470e (2026-09-15)
Some checks failed
build / lint (22) (push) Failing after 17s
build / test (22) (push) Failing after 8s
build / lint (23) (push) Failing after 2s
build / build (22) (push) Has been skipped
build / build (23) (push) Has been skipped
build / test (23) (push) Failing after 4s
docsearch / docsearch (push) Failing after 1s
Some checks failed
build / lint (22) (push) Failing after 17s
build / test (22) (push) Failing after 8s
build / lint (23) (push) Failing after 2s
build / build (22) (push) Has been skipped
build / build (23) (push) Has been skipped
build / test (23) (push) Failing after 4s
docsearch / docsearch (push) Failing after 1s
This commit is contained in:
337
packages/extension-s3/CHANGELOG.md
Normal file
337
packages/extension-s3/CHANGELOG.md
Normal file
@@ -0,0 +1,337 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [4.7.0](https://github.com/ueberdosis/hocuspocus/compare/v4.6.0...v4.7.0) (2026-09-09)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.6.0](https://github.com/ueberdosis/hocuspocus/compare/v4.5.0...v4.6.0) (2026-08-10)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.5.0](https://github.com/ueberdosis/hocuspocus/compare/v4.4.0...v4.5.0) (2026-08-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* update packages via audit --fix ([1dc9ca0](https://github.com/ueberdosis/hocuspocus/commit/1dc9ca0ff35f1033136473d134cee8cb6b336281))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.4.0](https://github.com/ueberdosis/hocuspocus/compare/v4.3.0...v4.4.0) (2026-07-13)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.3.0](https://github.com/ueberdosis/hocuspocus/compare/v4.2.0...v4.3.0) (2026-06-18)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.2.0](https://github.com/ueberdosis/hocuspocus/compare/v4.1.2...v4.2.0) (2026-06-12)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.1.2](https://github.com/ueberdosis/hocuspocus/compare/v4.1.1...v4.1.2) (2026-06-11)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [4.1.1](https://github.com/ueberdosis/hocuspocus/compare/v4.1.0...v4.1.1) (2026-06-10)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.1.0](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0...v4.1.0) (2026-05-20)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.7...v4.0.0) (2026-04-23)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.7](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.6...v4.0.0-rc.7) (2026-04-22)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.6](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.5...v4.0.0-rc.6) (2026-04-20)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.5](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.4...v4.0.0-rc.5) (2026-04-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.4](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.3...v4.0.0-rc.4) (2026-04-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.3](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.2...v4.0.0-rc.3) (2026-04-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.2](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.1...v4.0.0-rc.2) (2026-04-08)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.1](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.0...v4.0.0-rc.1) (2026-03-30)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.0](https://github.com/ueberdosis/hocuspocus/compare/v3.4.4...v4.0.0-rc.0) (2026-03-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* migrate to pnpm ([#1061](https://github.com/ueberdosis/hocuspocus/issues/1061)) ([dcaab57](https://github.com/ueberdosis/hocuspocus/commit/dcaab571bad694e48efd41bbb1d675855a7e2055))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.5](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.4...v4.0.0-rc.5) (2026-03-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.4](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.3...v4.0.0-rc.4) (2026-03-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.3](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.2...v4.0.0-rc.3) (2026-03-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.2](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.1...v4.0.0-rc.2) (2026-03-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.1](https://github.com/ueberdosis/hocuspocus/compare/v4.0.0-rc.0...v4.0.0-rc.1) (2026-03-16)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [4.0.0-rc.0](https://github.com/ueberdosis/hocuspocus/compare/v3.4.4...v4.0.0-rc.0) (2026-02-18)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* migrate to pnpm ([#1061](https://github.com/ueberdosis/hocuspocus/issues/1061)) ([dcaab57](https://github.com/ueberdosis/hocuspocus/commit/dcaab571bad694e48efd41bbb1d675855a7e2055))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.6-rc.2](https://github.com/ueberdosis/hocuspocus/compare/v3.4.6-rc.1...v3.4.6-rc.2) (2026-02-03)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.6-rc.1](https://github.com/ueberdosis/hocuspocus/compare/v3.4.6-rc.0...v3.4.6-rc.1) (2026-02-03)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.6-rc.0](https://github.com/ueberdosis/hocuspocus/compare/v3.4.5-rc.0...v3.4.6-rc.0) (2026-02-03)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.5-rc.0](https://github.com/ueberdosis/hocuspocus/compare/v3.4.4...v3.4.5-rc.0) (2026-02-03)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.4](https://github.com/ueberdosis/hocuspocus/compare/v3.4.3...v3.4.4) (2026-01-25)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.3](https://github.com/ueberdosis/hocuspocus/compare/v3.4.2...v3.4.3) (2025-12-15)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.2](https://github.com/ueberdosis/hocuspocus/compare/v3.4.1...v3.4.2) (2025-12-15)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.4.1](https://github.com/ueberdosis/hocuspocus/compare/v3.4.0...v3.4.1) (2025-12-09)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.4.0](https://github.com/ueberdosis/hocuspocus/compare/v3.3.2...v3.4.0) (2025-10-24)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.2](https://github.com/ueberdosis/hocuspocus/compare/v3.3.1...v3.3.2) (2025-10-23)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.3.1](https://github.com/ueberdosis/hocuspocus/compare/v3.3.0...v3.3.1) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# [3.3.0](https://github.com/ueberdosis/hocuspocus/compare/v3.2.6...v3.3.0) (2025-10-22)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.2.6](https://github.com/ueberdosis/hocuspocus/compare/v3.2.5...v3.2.6) (2025-10-17)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.2.5](https://github.com/ueberdosis/hocuspocus/compare/v3.2.4...v3.2.5) (2025-10-07)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.2.4](https://github.com/ueberdosis/hocuspocus/compare/v3.2.3...v3.2.4) (2025-09-30)
|
||||
|
||||
**Note:** Version bump only for package @hocuspocus/extension-s3
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# Changelog
|
||||
|
||||
## [3.2.3] - 2024-08-10
|
||||
|
||||
### Added
|
||||
- Initial release of @hocuspocus/extension-s3
|
||||
- Support for Amazon S3 and S3-compatible storage services (MinIO, DigitalOcean Spaces, etc.)
|
||||
- Configurable bucket, region, and key prefix
|
||||
- Support for custom S3 clients
|
||||
- IAM role support for AWS environments
|
||||
- Proper error handling for missing documents
|
||||
171
packages/extension-s3/README.md
Normal file
171
packages/extension-s3/README.md
Normal file
@@ -0,0 +1,171 @@
|
||||
# @hocuspocus/extension-s3
|
||||
[](https://www.npmjs.com/package/@hocuspocus/extension-s3)
|
||||
[](https://npmcharts.com/compare/tiptap?minimal=true)
|
||||
[](https://www.npmjs.com/package/@hocuspocus/extension-s3)
|
||||
[](https://github.com/sponsors/ueberdosis)
|
||||
|
||||
A S3-compatible persistence driver for [Hocuspocus](https://github.com/ueberdosis/hocuspocus). Stores Y.js documents in Amazon S3 or S3-compatible storage (MinIO, DigitalOcean Spaces, Cloudflare R2, Backblaze B2, etc.).
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @hocuspocus/extension-s3
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Basic AWS S3 Configuration
|
||||
|
||||
```javascript
|
||||
import { Server } from '@hocuspocus/server'
|
||||
import { S3 } from '@hocuspocus/extension-s3'
|
||||
|
||||
const server = new Server({
|
||||
extensions: [
|
||||
new S3({
|
||||
bucket: 'my-documents-bucket',
|
||||
region: 'us-east-1',
|
||||
credentials: {
|
||||
accessKeyId: 'your-access-key',
|
||||
secretAccessKey: 'your-secret-key'
|
||||
}
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
server.listen()
|
||||
```
|
||||
|
||||
### MinIO Configuration
|
||||
|
||||
For local development with MinIO, ensure you set `forcePathStyle: true`:
|
||||
|
||||
```javascript
|
||||
const server = new Server({
|
||||
extensions: [
|
||||
new S3({
|
||||
bucket: 'hocuspocus-documents',
|
||||
endpoint: 'http://localhost:9000',
|
||||
forcePathStyle: true,
|
||||
credentials: {
|
||||
accessKeyId: 'minioadmin',
|
||||
secretAccessKey: 'minioadmin'
|
||||
}
|
||||
}),
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
## Configuration Options
|
||||
|
||||
- **bucket** (required): S3 bucket name where documents will be stored
|
||||
- **region**: AWS region (default: 'us-east-1')
|
||||
- **prefix**: Key prefix for documents (default: 'hocuspocus-documents/')
|
||||
- **credentials**: AWS credentials object with accessKeyId and secretAccessKey
|
||||
- **endpoint**: S3 endpoint URL (for S3-compatible services)
|
||||
- **forcePathStyle**: Use path-style URLs (required for MinIO, default: false)
|
||||
- **s3Client**: Custom S3Client instance
|
||||
|
||||
## Document Storage
|
||||
|
||||
Documents are stored as binary files in S3 with the naming convention:
|
||||
`{prefix}{documentName}.bin`
|
||||
|
||||
## Scaling with Redis
|
||||
|
||||
For horizontal scaling, combine S3 with the Redis extension. Redis handles real-time synchronization while S3 provides persistent storage.
|
||||
|
||||
```javascript
|
||||
import { Server } from '@hocuspocus/server'
|
||||
import { Logger } from '@hocuspocus/extension-logger'
|
||||
import { Redis } from '@hocuspocus/extension-redis'
|
||||
import { S3 } from '@hocuspocus/extension-s3'
|
||||
|
||||
// Server 1
|
||||
const server1 = new Server({
|
||||
name: "server-1",
|
||||
port: 8001,
|
||||
extensions: [
|
||||
new Logger(),
|
||||
new Redis({
|
||||
host: "127.0.0.1",
|
||||
port: 6379,
|
||||
}),
|
||||
new S3({
|
||||
bucket: 'hocuspocus-documents',
|
||||
endpoint: 'http://localhost:9000', // MinIO
|
||||
forcePathStyle: true,
|
||||
credentials: {
|
||||
accessKeyId: 'minioadmin',
|
||||
secretAccessKey: 'minioadmin'
|
||||
}
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
||||
// Server 2 - same configuration with different name/port
|
||||
const server2 = new Server({
|
||||
name: "server-2",
|
||||
port: 8002,
|
||||
extensions: [/* same extensions */],
|
||||
})
|
||||
|
||||
server1.listen()
|
||||
server2.listen()
|
||||
```
|
||||
|
||||
## CLI Usage
|
||||
|
||||
You can also use the S3 extension with the Hocuspocus CLI:
|
||||
|
||||
```bash
|
||||
# Basic usage
|
||||
hocuspocus --s3 --s3-bucket my-documents
|
||||
|
||||
# MinIO setup (forcePathStyle automatically enabled)
|
||||
hocuspocus --s3 --s3-bucket hocuspocus-documents --s3-endpoint http://localhost:9000
|
||||
```
|
||||
|
||||
## Development
|
||||
|
||||
### Quick Start
|
||||
|
||||
For local development with MinIO (S3-compatible storage):
|
||||
|
||||
```bash
|
||||
# Set up development environment
|
||||
npm run dev:setup
|
||||
|
||||
# Test S3 configuration
|
||||
npm run dev:test-s3
|
||||
|
||||
# Run S3 playground examples
|
||||
npm run playground:s3
|
||||
```
|
||||
|
||||
## Complete Documentation
|
||||
|
||||
For detailed documentation, examples, best practices, and troubleshooting, see:
|
||||
[📚 S3 Extension Documentation](https://tiptap.dev/docs/hocuspocus/server/extensions/s3)
|
||||
|
||||
## IAM Permissions
|
||||
|
||||
Your AWS credentials or IAM role needs the following permissions:
|
||||
|
||||
```json
|
||||
{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:GetObject", "s3:PutObject", "s3:HeadObject"],
|
||||
"Resource": "arn:aws:s3:::your-bucket-name/*"
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Action": ["s3:HeadBucket"],
|
||||
"Resource": "arn:aws:s3:::your-bucket-name"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
42
packages/extension-s3/package.json
Normal file
42
packages/extension-s3/package.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "@hocuspocus/extension-s3",
|
||||
"description": "a S3-compatible persistence driver for Hocuspocus",
|
||||
"version": "4.7.0",
|
||||
"homepage": "https://hocuspocus.dev",
|
||||
"keywords": [
|
||||
"hocuspocus",
|
||||
"yjs",
|
||||
"s3",
|
||||
"persistence"
|
||||
],
|
||||
"license": "MIT",
|
||||
"type": "module",
|
||||
"main": "dist/hocuspocus-s3.cjs",
|
||||
"module": "dist/hocuspocus-s3.esm.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
"source": {
|
||||
"import": "./src/index.ts"
|
||||
},
|
||||
"default": {
|
||||
"import": "./dist/hocuspocus-s3.esm.js",
|
||||
"require": "./dist/hocuspocus-s3.cjs",
|
||||
"types": "./dist/index.d.ts"
|
||||
}
|
||||
},
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.990.0",
|
||||
"@hocuspocus/extension-database": "workspace:^",
|
||||
"kleur": "^4.1.5"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"url": "https://github.com/ueberdosis/hocuspocus"
|
||||
}
|
||||
}
|
||||
197
packages/extension-s3/src/S3.ts
Normal file
197
packages/extension-s3/src/S3.ts
Normal file
@@ -0,0 +1,197 @@
|
||||
import type { DatabaseConfiguration } from "@hocuspocus/extension-database";
|
||||
import { Database } from "@hocuspocus/extension-database";
|
||||
import {
|
||||
S3Client,
|
||||
GetObjectCommand,
|
||||
PutObjectCommand,
|
||||
HeadObjectCommand,
|
||||
} from "@aws-sdk/client-s3";
|
||||
import kleur from "kleur";
|
||||
|
||||
export interface S3Configuration extends DatabaseConfiguration {
|
||||
/**
|
||||
* AWS S3 region
|
||||
*/
|
||||
region?: string;
|
||||
/**
|
||||
* S3 bucket name
|
||||
*/
|
||||
bucket: string;
|
||||
/**
|
||||
* S3 key prefix for documents (optional)
|
||||
*/
|
||||
prefix?: string;
|
||||
/**
|
||||
* AWS credentials
|
||||
*/
|
||||
credentials?: {
|
||||
accessKeyId: string;
|
||||
secretAccessKey: string;
|
||||
};
|
||||
/**
|
||||
* S3 endpoint URL (for S3-compatible services like MinIO)
|
||||
*/
|
||||
endpoint?: string;
|
||||
/**
|
||||
* Force path style URLs (required for MinIO)
|
||||
*/
|
||||
forcePathStyle?: boolean;
|
||||
/**
|
||||
* Custom S3 client
|
||||
*/
|
||||
s3Client?: S3Client;
|
||||
}
|
||||
|
||||
export class S3 extends Database {
|
||||
private s3Client?: S3Client;
|
||||
|
||||
configuration: S3Configuration = {
|
||||
region: "us-east-1",
|
||||
bucket: "",
|
||||
prefix: "hocuspocus-documents/",
|
||||
forcePathStyle: false,
|
||||
fetch: async ({ documentName }) => {
|
||||
const key = this.getObjectKey(documentName);
|
||||
|
||||
try {
|
||||
const command = new GetObjectCommand({
|
||||
Bucket: this.configuration.bucket,
|
||||
Key: key,
|
||||
});
|
||||
|
||||
const response = await this.s3Client!.send(command);
|
||||
|
||||
if (response.Body) {
|
||||
// Convert stream to Uint8Array
|
||||
const chunks: Uint8Array[] = [];
|
||||
const reader = response.Body.transformToWebStream().getReader();
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
if (done) break;
|
||||
chunks.push(value);
|
||||
}
|
||||
|
||||
// Combine all chunks into a single Uint8Array
|
||||
const totalLength = chunks.reduce(
|
||||
(acc, chunk) => acc + chunk.length,
|
||||
0,
|
||||
);
|
||||
const result = new Uint8Array(totalLength);
|
||||
let offset = 0;
|
||||
|
||||
for (const chunk of chunks) {
|
||||
result.set(chunk, offset);
|
||||
offset += chunk.length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
return null;
|
||||
} catch (error: any) {
|
||||
if (
|
||||
error.name === "NoSuchKey" ||
|
||||
error.$metadata?.httpStatusCode === 404
|
||||
) {
|
||||
// Document doesn't exist yet, return null
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
store: async ({ documentName, state }) => {
|
||||
const key = this.getObjectKey(documentName);
|
||||
|
||||
const command = new PutObjectCommand({
|
||||
Bucket: this.configuration.bucket,
|
||||
Key: key,
|
||||
Body: state,
|
||||
ContentType: "application/octet-stream",
|
||||
});
|
||||
|
||||
await this.s3Client!.send(command);
|
||||
},
|
||||
};
|
||||
|
||||
constructor(configuration: Partial<S3Configuration>) {
|
||||
super({});
|
||||
|
||||
this.configuration = {
|
||||
...this.configuration,
|
||||
...configuration,
|
||||
};
|
||||
|
||||
// Validate required configuration
|
||||
if (!this.configuration.bucket) {
|
||||
throw new Error("S3 bucket name is required");
|
||||
}
|
||||
}
|
||||
|
||||
private getObjectKey(documentName: string): string {
|
||||
const prefix = this.configuration.prefix || "";
|
||||
return `${prefix}${documentName}.bin`;
|
||||
}
|
||||
|
||||
async onConfigure() {
|
||||
// Use custom S3 client if provided, otherwise create one
|
||||
if (this.configuration.s3Client) {
|
||||
this.s3Client = this.configuration.s3Client;
|
||||
} else {
|
||||
const clientConfig: any = {
|
||||
region: this.configuration.region,
|
||||
};
|
||||
|
||||
if (this.configuration.credentials) {
|
||||
clientConfig.credentials = this.configuration.credentials;
|
||||
}
|
||||
|
||||
if (this.configuration.endpoint) {
|
||||
clientConfig.endpoint = this.configuration.endpoint;
|
||||
clientConfig.forcePathStyle = this.configuration.forcePathStyle;
|
||||
}
|
||||
|
||||
this.s3Client = new S3Client(clientConfig);
|
||||
}
|
||||
|
||||
// Test S3 connection by checking if bucket exists
|
||||
try {
|
||||
const command = new HeadObjectCommand({
|
||||
Bucket: this.configuration.bucket,
|
||||
Key: "test-connection", // This will likely return 404, but that's fine
|
||||
});
|
||||
|
||||
await this.s3Client.send(command);
|
||||
} catch (error: any) {
|
||||
// 404 is expected for the test key, any other error indicates connection issues
|
||||
if (error.$metadata?.httpStatusCode !== 404) {
|
||||
// Don't show credential errors as connection failures in development
|
||||
if (error.message?.includes("Could not load credentials")) {
|
||||
console.warn(` ${kleur.yellow("S3 warning:")} ${error.message}`);
|
||||
console.warn(
|
||||
` ${kleur.yellow("Note:")} Ensure AWS credentials are properly configured for production use`,
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
` ${kleur.red("S3 connection failed:")} ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onListen() {
|
||||
const endpoint =
|
||||
this.configuration.endpoint ||
|
||||
`https://s3.${this.configuration.region}.amazonaws.com`;
|
||||
console.log(
|
||||
` ${kleur.green("S3 extension configured:")} bucket=${this.configuration.bucket}, endpoint=${endpoint}`,
|
||||
);
|
||||
|
||||
if (this.configuration.prefix) {
|
||||
console.log(
|
||||
` ${kleur.blue("S3 key prefix:")} ${this.configuration.prefix}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
1
packages/extension-s3/src/index.ts
Normal file
1
packages/extension-s3/src/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./S3.ts";
|
||||
Reference in New Issue
Block a user