Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"private": true,
"main": "index.js",
"scripts": {
"s3": "vite-node ./src/s3-adapter.ts",
"fs": "vite-node ./src/fs-adapter.ts"
"s3": "vite-node ./src/s3-provider.ts",
"fs": "vite-node ./src/fs-provider.ts"
},
"keywords": [],
"author": "",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createClient } from "clyve";
import { FileSystemAdapter } from "clyve/adapters";
import { FileSystemProvider } from "clyve/providers";
import "dotenv/config";

type MySchema = {
Expand All @@ -14,8 +14,8 @@ type MySchema = {
};
};

const adapter = new FileSystemAdapter("./data");
const db = createClient<MySchema>(adapter);
const provider = new FileSystemProvider("./data");
const db = createClient<MySchema>(provider);

await db.users.upsert({
id: "1",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { S3Client } from "@aws-sdk/client-s3";
import { createClient } from "clyve";
import { S3Adapter } from "clyve/adapters";
import { S3Provider } from "clyve/providers";
import "dotenv/config";

type MySchema = {
Expand All @@ -24,8 +24,8 @@ export const s3Client = new S3Client({
},
});

const adapter = new S3Adapter(s3Client, "scoreboard-app");
const db = createClient<MySchema>(adapter);
const provider = new S3Provider(s3Client, "scoreboard-app");
const db = createClient<MySchema>(provider);

await db.users.upsert({
id: "1",
Expand Down
20 changes: 10 additions & 10 deletions packages/clyve/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Clyve
A lightweight client for using either AWS S3 or the filesystem as a database via adapters. Perfect for quick MVPs or prototypes, it lets you store, retrieve, and manage JSON objects without a full database setup. While not suited for production, it takes advantage of S3’s scalability or the simplicity of the filesystem, enabling easy CRUD operations on structured data.
A lightweight client for using either AWS S3 or the filesystem as a database via providers. Perfect for quick MVPs or prototypes, it lets you store, retrieve, and manage JSON objects without a full database setup. While not suited for production, it takes advantage of S3’s scalability or the simplicity of the filesystem, enabling easy CRUD operations on structured data.

## Key Features
- 🕒 Quick to prototype and iterate with.
Expand All @@ -19,16 +19,16 @@ Install required packages
npm install clyve
```

Install the S3 client if you want to use the S3 adapter
Install the S3 client if you want to use the S3 provider
```bash
npm install @aws-sdk/client-s3
```

## Usage with S3 Adapter
## Usage with S3 provider
```typescript
import { S3Client } from "@aws-sdk/client-s3";
import { createClient } from "clyve";
import { S3Adapter } from "clyve/adapters";
import { S3Provider } from "clyve/providers";

// Create an S3 client.
export const s3Client = new S3Client({
Expand All @@ -55,14 +55,14 @@ type MySchema = {

// Create Clyve client.
const bucketName = "my-bucket";
const adapter = new S3Adapter(s3Client, bucketName);
const db = createClient<MySchema>(adapter);
const provider = new S3Provider(s3Client, bucketName);
const db = createClient<MySchema>(provider);
```

## Usage with file system adapter
## Usage with file system provider
```typescript
import { createClient } from "clyve";
import { FileSystemAdapter } from "clyve/adapters";
import { FileSystemProvider } from "clyve/providers";

// Create your schema type, id is required in every model.
type MySchema = {
Expand All @@ -78,8 +78,8 @@ type MySchema = {
};

// Create Clyve client.
const adapter = new FileSystemAdapter("./data");
const db = createClient<MySchema>(adapter);
const provider = new FileSystemProvider("./data");
const db = createClient<MySchema>(provider);
```

## Operations
Expand Down
11 changes: 6 additions & 5 deletions packages/clyve/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "clyve",
"type": "module",
"version": "1.1.1",
"version": "1.1.3",
"description": "A lightweight TypeScript client that uses AWS S3 or the file system as a schema-driven, JSON-based database.",
"homepage": "https://github.com/feelixe/clyve",
"repository": {
Expand All @@ -19,9 +19,9 @@
"import": "./dist/errors.js",
"types": "./dist/errors.d.ts"
},
"./adapters": {
"import": "./dist/adapters/index.js",
"types": "./dist/adapters/index.d.ts"
"./providers": {
"import": "./dist/providers/index.js",
"types": "./dist/providers/index.d.ts"
}
},
"files": [
Expand All @@ -31,7 +31,7 @@
"package.json"
],
"scripts": {
"build": "tsc",
"build": "rimraf dist && tsc",
"dev": "tsc --watch --incremental --preserveWatchOutput"
},
"keywords": [],
Expand All @@ -40,6 +40,7 @@
"devDependencies": {
"@aws-sdk/client-s3": "^3.352.0",
"@types/node": "^22.10.2",
"rimraf": "^6.0.1",
"typescript": "^5.7.2",
"vite-node": "^2.1.8"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/clyve/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CollectionObjectReadOnlyError } from "./errors.js";
import { Adapter } from "./adapters/types.js";
import { Provider } from "./providers/types.js";
import { Model } from "./model.js";
import { Operations } from "./operations.js";

Expand All @@ -25,8 +25,8 @@ export type ClyveClient<T extends Schema> = {
};
};

export function createClient<T extends Schema>(adapter: Adapter) {
const operations = new Operations(adapter);
export function createClient<T extends Schema>(provider: Provider) {
const operations = new Operations(provider);

return new Proxy(
{},
Expand Down
38 changes: 19 additions & 19 deletions packages/clyve/src/operations.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,41 @@
import { Adapter } from "./adapters/types.js";
import { Provider } from "./providers/types.js";
import { DuplicateKeyError, KeyDoesNotExistError } from "./errors.js";
import { Model } from "./model.js";

export class Operations {
private adapter: Adapter;
private provider: Provider;

constructor(adapter: Adapter) {
this.adapter = adapter;
constructor(provider: Provider) {
this.provider = provider;
}

async getById(collection: string, id: string) {
return await this.adapter.getByKey(`${collection}/${id}.json`);
return await this.provider.getByKey(`${collection}/${id}.json`);
}

async get(collection: string, id: string) {
return await this.getById(collection, id);
}

async all(collection: string) {
const keys = await this.adapter.keys(collection);
return await Promise.all(keys.map((key) => this.adapter.getByKey(key)));
const keys = await this.provider.keys(collection);
return await Promise.all(keys.map((key) => this.provider.getByKey(key)));
}

async create(collection: string, data: Model) {
const doesKeyAlreadyExist = await this.adapter.exists(collection, data.id);
const doesKeyAlreadyExist = await this.provider.exists(collection, data.id);
if (doesKeyAlreadyExist) {
throw new DuplicateKeyError(
`Key ${collection}/${data.id}.json already exists`
);
}

return await this.adapter.upsert(collection, data);
return await this.provider.upsert(collection, data);
}

async createMany(collection: string, data: Array<Model>) {
const entriesExist = await Promise.all(
data.map((data) => this.adapter.exists(collection, data.id))
data.map((data) => this.provider.exists(collection, data.id))
);
const someExist = entriesExist.some((exists) => exists);
if (someExist) {
Expand All @@ -45,17 +45,17 @@ export class Operations {
}

return await Promise.all(
data.map((data) => this.adapter.upsert(collection, data))
data.map((data) => this.provider.upsert(collection, data))
);
}

async deleteObject(collection: string, id: string) {
await this.adapter.deleteObject(collection, id);
await this.provider.deleteObject(collection, id);
}

async deleteMany(collection: string, ids: Array<string>) {
const entriesExist = await Promise.all(
ids.map((id) => this.adapter.exists(collection, id))
ids.map((id) => this.provider.exists(collection, id))
);

const allExist = entriesExist.every((exists) => exists);
Expand All @@ -67,12 +67,12 @@ export class Operations {
}

await Promise.all(
ids.map((id) => this.adapter.deleteObject(collection, id))
ids.map((id) => this.provider.deleteObject(collection, id))
);
}

async allIds(collection: string) {
const keys = await this.adapter.keys(collection);
const keys = await this.provider.keys(collection);
const fileNames = keys.map((key) => key.split("/")[1]);
const ids = fileNames.map((fileName) => fileName.split(".")[0]);
return ids;
Expand All @@ -81,17 +81,17 @@ export class Operations {
async deleteAll(collection: string) {
const ids = await this.allIds(collection);
await Promise.all(
ids.map((id) => this.adapter.deleteObject(collection, id))
ids.map((id) => this.provider.deleteObject(collection, id))
);
}

async count(collection: string) {
const keys = await this.adapter.keys(collection);
const keys = await this.provider.keys(collection);
return keys.length;
}

async exists(collection: string, id: string) {
return await this.adapter.exists(collection, id);
return await this.provider.exists(collection, id);
}

async update(collection: string, data: Model) {
Expand All @@ -106,7 +106,7 @@ export class Operations {
}

async upsert(collection: string, data: Model) {
return await this.adapter.upsert(collection, data);
return await this.provider.upsert(collection, data);
}

async edit(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { Model } from "../model.js";
import { KeyDoesNotExistError } from "../errors.js";
import { Adapter } from "./types.js";
import { Provider } from "./types.js";
import { promises as fs } from "node:fs";
import path from "node:path";

export class FileSystemAdapter implements Adapter {
export class FileSystemProvider implements Provider {
private basePath: string;

constructor(basePath: string) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@ import {
PutObjectCommand,
S3Client,
} from "@aws-sdk/client-s3";
import { Adapter } from "./types.js";
import { Provider } from "./types.js";
import { KeyDoesNotExistError, NoBodyError } from "../errors.js";
import { Model } from "../model.js";

export type S3AdapterConstructorParams = [s3Client: S3Client, bucket: string];
export type S3ProviderConstructorParams = [s3Client: S3Client, bucket: string];

export class S3Adapter implements Adapter {
export class S3Provider implements Provider {
private client: S3Client;
private bucket: string;

constructor(...args: S3AdapterConstructorParams) {
constructor(...args: S3ProviderConstructorParams) {
const [s3Client, bucket] = args;
this.client = s3Client;
this.bucket = bucket;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Model } from "model.js";

export interface Adapter {
export interface Provider {
getByKey: (key: string) => Promise<Model>;
exists: (collection: string, id: string) => Promise<boolean>;
keys: (collection: string) => Promise<string[]>;
Expand Down
Loading
Loading