# FOUNDRY

Provides commonly-used data structures suitable for distributed applications thanks to foundationdb.

## Usage

### Installation

**Before installing the package, you need to install the foundationdb client on your machine**. Please follow [this guide](https://apple.github.io/foundationdb/api-general.html#installing-foundationdb-client-binaries) to install it.

Install foundry with your package manager:

Using npm

```sh
npm install --save @feedloop/foundry
```

Using yarn

```sh
yarn add @feedloop/foundry
```

### Initialization

Foundry uses nodejs foundationdb client that looks for configs in several [ways](https://www.npmjs.com/package/foundationdb#connecting-to-your-cluster).

> Set FDB_API_VERSION to your environment variable to select the foundationdb api version, defaults to version 620.

```ts
import Foundry from "@feedloop/foundry";

// somewhere in your async scope
const foundry = await Foundry.init("your-database-name");
```

### Usage

```ts
import { Set } from "@feedloop/foundry";

// somewhere in your async scope
const hasBar = await foundry.exec(async (factory) => {
  const set = factory.open(Set, "my-set");
  await set.add("foo", "bar", "baz");
  return set.has("bar");
});

// hasBar will be `true`
```

All foundry's operations must be run within the `foundry.exec` scope because they will run in the same transaction. Each transaction will be re-run when a conflict is detected, so keep it simple to reduce client overhead.

## Available Data Structures

Foundry provides commonly used data structures suitable for distributed applications by utilizing foundationdb's transaction excellence.

### List

```ts
import { List } from "@feedloop/foundry";

// somewhere in your async scope
await foundry.exec(async (factory) => {
  const list = factory.open<List<string>>(List, "list1");
  await list.push("a", "b", "c"); // ["a", "b", "c"]
  const secondItem = await list.at(1); // returns "b"
  const last = await list.pop(); // returns "c"
  const len = await list.length(); // returns 2
});
```

### Set

```ts
import { Set } from "@feedloop/foundry";

// somewhere in your async scope
await foundry.exec(async (factory) => {
  const set = factory.open(Set, "my-set");
  await set.add("foo", "bar", "baz"); // ["foo", "bar", "baz"]
  await set.remove("bar"); // ["foo", "baz"]
  await set.size(); // 2
});
```

### Map

```ts
import { Map } from "@feedloop/foundry";

// somewhere in your async scope
await foundry.exec(async (factory) => {
  const map = factory.open(Map, "my-map");
  await map.set("foo", 1); // {foo: 1}
  await map.set("bar", 2); // {foo: 1, bar: 2}
  await map.delete("bar"); // {bar: 2}
  await map.size(); // 2
});
```

### PersistentQueue

```ts
import { PersistentQueue } from "@feedloop/foundry";

// somewhere in your async scope
await foundry.exec(async (factory) => {
  const queue = factory.open(PersistentQueue, "my-queue");
  await queue.write("foo"); // ["foo"]
  await queue.write("bar"); // ["foo", "bar"]

  // reader1 consuming messages
  await queue.read("reader1", 1, true); // returns ["foo"]
  await queue.readOffset("reader1"); // returns 1
  await queue.read("reader1", 1, true); // returns ["bar"]
  await queue.read("reader1", 1, true); // returns []
  await queue.readOffset("reader1"); // returns 2

  // reader2 consuming messages
  await queue.read("reader2", 2, true); // returns ["foo", "bar"]
  await queue.read("reader2", 2, true); // returns []
  await queue.size(); // return 2
});
```

### CronJob

```ts
import { CronJob } from "@feedloop/foundry";

// somewhere in your async scope
// set a cron schedule
await foundry.exec(async (factory) => {
  const cronjob = factory.open(CronJob, "my-cronjob");
  // At every 5th minute (https://crontab.guru/#*/5_*_*_*_*)
  await cronjob.schedule("5 * * * *");
});

// setup a polling to check for jobs
setInterval(async () => {
  const hasJob = await foundry.exec(async (factory) => {
    const cronjob = factory.open(CronJob, "my-cronjob");
    return cronjob.take();
  });
  if (hasJob) {
    console.log("I have a job!"); // will run at every 5th minute
  }
}, 1000 * 60); // run every 1 minute
```
