feat(storage-fs): adapt to the new storage option and rename the package

This commit is contained in:
Joakim Carlstein 2023-11-22 11:25:05 +01:00
parent 8e87ade5c0
commit 7d8ac9b185
8 changed files with 52 additions and 41 deletions

View file

@ -0,0 +1,25 @@
# @emigrate/storage-fs
## 0.1.1
### Patch Changes
- e5eec7c: Throw a more descriptive error when a file lock couldn't be acquired
- Updated dependencies [8f35812]
- @emigrate/plugin-tools@0.3.0
## 0.1.0
### Minor Changes
- 0c49249: Implement a first version of the File System Storage plugin for simple migration setups
### Patch Changes
- Updated dependencies [1799b6e]
- Updated dependencies [3e0ff07]
- Updated dependencies [23a323c]
- Updated dependencies [62bd5a4]
- Updated dependencies [81fde2e]
- Updated dependencies [9f5abf7]
- @emigrate/plugin-tools@0.2.0

View file

@ -0,0 +1,30 @@
# @emigrate/storage-fs
A file system storage plugin for Emigrate, suitable for simple migration setups. To support containerized environments, it is recommended to use a database storage plugin instead.
## Installation
Install the storage plugin in your project, alongside the Emigrate CLI:
```bash
npm install --save-dev @emigrate/cli @emigrate/storage-fs
```
## Usage
Configure the storage in your `emigrate.config.js` file:
```js
import storageFs from '@emigrate/storage-fs';
export default {
directory: 'migrations',
storage: storageFs({ filename: '.migrated.json' }),
};
```
Or use the CLI option `--storage` (or `-s`):
```bash
emigrate up --storage fs # the @emigrate/storage- prefix is optional
```

View file

@ -0,0 +1,46 @@
{
"name": "@emigrate/storage-fs",
"version": "0.1.1",
"publishConfig": {
"access": "public"
},
"description": "A storage plugin for Emigrate for storing the migration history in a file",
"main": "dist/index.js",
"types": "dist/index.d.js",
"type": "module",
"exports": {
".": {
"import": "./dist/index.js",
"types": "./dist/index.d.ts"
}
},
"files": [
"dist"
],
"scripts": {
"build": "tsc --pretty",
"build:watch": "tsc --pretty --watch",
"lint": "xo --cwd=../.. $(pwd)"
},
"keywords": [
"emigrate",
"emigrate-storage",
"plugin",
"migrations",
"storage"
],
"author": "Aboviq AB <dev@aboviq.com> (https://www.aboviq.com)",
"homepage": "https://github.com/aboviq/emigrate/tree/main/packages/storage-fs#readme",
"repository": "https://github.com/aboviq/emigrate/tree/main/packages/storage-fs",
"bugs": "https://github.com/aboviq/emigrate/issues",
"license": "MIT",
"dependencies": {
"@emigrate/plugin-tools": "workspace:*"
},
"devDependencies": {
"@emigrate/tsconfig": "workspace:*"
},
"volta": {
"extends": "../../package.json"
}
}

View file

@ -0,0 +1,94 @@
import fs from 'node:fs/promises';
import path from 'node:path';
import process from 'node:process';
import { type EmigrateStorage, type MigrationStatus } from '@emigrate/plugin-tools/types';
export type StorageFsOptions = {
filename: string;
};
type SerializedError = {
name: string;
message: string;
stack?: string;
};
export default function storageFs({ filename }: StorageFsOptions): EmigrateStorage {
const filePath = path.resolve(process.cwd(), filename);
const lockFilePath = `${filePath}.lock`;
const read = async (): Promise<
Record<string, { status: MigrationStatus; date: string; error?: SerializedError }>
> => {
try {
const contents = await fs.readFile(filePath, 'utf8');
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
return JSON.parse(contents);
} catch {
return {};
}
};
let lastUpdate: Promise<void> = Promise.resolve();
const update = async (migration: string, status: MigrationStatus, error?: Error) => {
lastUpdate = lastUpdate.then(async () => {
const history = await read();
const newHistory = {
...history,
[migration]: {
status,
date: new Date().toISOString(),
error: error ? { name: error.name, message: error.message, stack: error.stack } : undefined,
},
};
await fs.writeFile(filePath, JSON.stringify(newHistory, undefined, 2));
});
return lastUpdate;
};
return {
async initializeStorage() {
return {
async lock(migrations) {
try {
const fd = await fs.open(lockFilePath, 'wx');
await fd.close();
return migrations;
} catch {
throw new Error('Could not acquire file lock for migrations');
}
},
async unlock() {
try {
await fs.unlink(lockFilePath);
} catch {
// Ignore
}
},
async *getHistory() {
const history = await read();
yield* Object.entries(history).map(([name, { status, date, error }]) => ({
name,
status,
error,
date: new Date(date),
}));
},
async onSuccess(migration) {
await update(migration.name, 'done');
},
async onError(migration, error) {
await update(migration.name, 'failed', error);
},
};
},
};
}

View file

@ -0,0 +1,8 @@
{
"extends": "@emigrate/tsconfig/build.json",
"compilerOptions": {
"outDir": "dist"
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}