Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

firestorm-db

Package Overview
Dependencies
Maintainers
1
Versions
19
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

firestorm-db - npm Package Compare versions

Comparing version 1.12.0 to 1.13.0

14

package.json
{
"name": "firestorm-db",
"version": "1.12.0",
"version": "1.13.0",
"description": "Self hosted Firestore-like database with API endpoints based on micro bulk operations",

@@ -14,3 +14,3 @@ "main": "src/index.js",

"jsdoc": "jsdoc src/index.js -c jsdoc.json -R README.md -t ./node_modules/docdash -d out",
"nodemon_jsdoc": "nodemon -x npm run jsdoc --watch src/index.js --watch jsdoc.json",
"nodemon_jsdoc": "nodemon -x npm run jsdoc --watch src/index.js --watch jsdoc.json --watch README.md",
"types": "npx tsc",

@@ -43,3 +43,4 @@ "prettier": "prettier \"{,!(node_modules)/**/}*.{js,ts}\" --config .prettierrc --write",

"dependencies": {
"axios": "^1.6.7"
"axios": "^1.6.8",
"form-data": "^4.0.0"
},

@@ -49,12 +50,11 @@ "devDependencies": {

"docdash": "^2.0.2",
"form-data": "^4.0.0",
"glob": "^10.3.10",
"glob": "^10.3.12",
"jsdoc": "^4.0.2",
"jsdoc-to-markdown": "^8.0.1",
"mocha": "^10.3.0",
"mocha": "^10.4.0",
"nyc": "^15.1.0",
"prettier": "^3.2.5",
"recursive-copy": "^2.0.14",
"typescript": "^5.3.3"
"typescript": "^5.4.5"
}
}

@@ -6,59 +6,54 @@ <div align="center">

<a href="https://www.npmjs.com/package/firestorm-db" target="_blank" ><img alt="npm" src="https://img.shields.io/npm/v/firestorm-db?color=cb0000&logo=npm&style=flat-square"></a>
<a href="https://www.npmjs.com/package/firestorm-db" target="_blank">
<img alt="npm" src="https://img.shields.io/npm/v/firestorm-db?color=cb0000&logo=npm&style=flat-square">
</a>
<img alt="GitHub file size in bytes" src="https://img.shields.io/github/size/TheRolfFR/firestorm-db/src%2Findex.js?color=43A047&label=Script%20size&logoColor=green&style=flat-square">
<a href="https://github.com/TheRolfFR/firestorm-db/blob/main/CHANGELOG.md"> <img alt="Static Badge" src="https://img.shields.io/badge/Changelog-Read_here-blue?style=flat-square"></a>
<a href="https://github.com/TheRolfFR/firestorm-db/actions/workflows/testjs.yml"> <img src="https://img.shields.io/github/actions/workflow/status/TheRolfFR/firestorm-db/testjs.yml?style=flat-square" alt="Tests" /></a>
<a href="https://github.com/TheRolfFR/firestorm-db/blob/main/CHANGELOG.md">
<img alt="Changelog" src="https://img.shields.io/badge/Changelog-Read_here-blue?style=flat-square">
</a>
<a href="https://github.com/TheRolfFR/firestorm-db/actions/workflows/testjs.yml">
<img src="https://img.shields.io/github/actions/workflow/status/TheRolfFR/firestorm-db/testjs.yml?style=flat-square" alt="Tests" />
</a>
</div>
_Self hosted Firestore-like database with API endpoints based on micro bulk operations_
*Self hosted Firestore-like database with API endpoints based on micro bulk operations*
## Installation
# Installation
Installing the JavaScript client is as simple as running:
```sh
npm install firestorm-db
```
npm install --save firestorm-db
```
# JavaScript Part
Information about installing Firestorm server-side is given in the [PHP](#php-backend) section.
The JavaScript [index.js](./src/index.js) file is just an [Axios](https://www.npmjs.com/package/axios) wrapper of the library.
# JavaScript Client
## How to use it
The JavaScript [index.js](./src/index.js) file is simply an [Axios](https://www.npmjs.com/package/axios) wrapper of the PHP backend.
First, you need to configure your API address, and your token if needed:
## JavaScript setup
First, set your API address (and your writing token if needed) using the `address()` and `token()` functions:
```js
require("dotenv").config(); // add some env variables
// only needed in Node.js, including the script tag in a browser is enough otherwise.
const firestorm = require("firestorm-db");
// ex: 'http://example.com/path/to/firestorm/root/'
firestorm.address(process.env.FIRESTORM_URL);
firestorm.address("http://example.com/path/to/firestorm/root/");
// only necessary if you want to write or access private collections
// must match token stored in tokens.php file
firestorm.token(process.env.FIRESTORM_TOKEN);
firestorm.token("my_secret_token_probably_from_an_env_file");
```
Now you can use Firestorm to its full potential:
Now you can use Firestorm to its full potential.
```js
const firestorm = require("firestorm-db");
## Create your first collection
// returns a Collection instance
const userCollection = firestorm.collection("users");
Firestorm is based around the concept of a `Collection`, which is akin to an SQL table or Firestore document. The Firestorm collection constructor takes one required argument and one optional argument:
// all methods return promises
userCollection
.readRaw()
.then((res) => console.log(res))
.catch((err) => console.error(err));
```
- The name of the collection as a `string`.
- A method adder, which lets you inject methods to query results. It's implemented similarly to [`Array.prototype.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), taking a queried element as an argument, modifying the element with methods and data inside a callback, and returning the modified element at the end.
### Collection constructor
A collection takes one required argument and one optional argument:
- The name of the collection as a `String`.
- The method adder, which lets you inject methods to query results. It's implemented similarly to [`Array.prototype.map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), taking an outputted element as an argument, modifying the element with methods and data inside a callback, and returning the modified element at the end.
```js

@@ -68,3 +63,4 @@ const firestorm = require("firestorm-db");

const userCollection = firestorm.collection("users", (el) => {
el.hello = () => console.log(`${el.name} says hello!`);
// assumes you have a 'users' table with a printable field called 'name'
el.hello = () => `${el.name} says hello!`;
// return the modified element back with the injected method

@@ -74,156 +70,168 @@ return el;

// if you have a 'users' table with a printable field named name
// all methods return promises
const johnDoe = await userCollection.get(123456789);
// gives { name: "John Doe", hello: function}
// gives { name: "John Doe", hello: Function }
johnDoe.hello(); // prints out "John Doe says hello!"
johnDoe.hello(); // "John Doe says hello!"
```
Available methods for a collection:
## Read operations
### Read operations
| Name | Parameters | Description |
| ------------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
| sha1() | none | Get the sha1 hash of the file. Can be used to compare file content without downloading the JSON. |
| readRaw(original) | original?: `boolean` | Read the entire collection. `original` disables ID field injection, for non-relational collections. |
| get(key) | key: `string \| number` | Get an element from the collection by its key. |
| searchKeys(keys) | keys: `string[] \| number[]` | Get multiple elements from the collection by their keys. |
| search(options, random) | options: `SearchOption[]` random?:`boolean \| number` | Search through the collection. You can randomize the output order with random as true or a given seed. |
| select(option) | option: `SelectOption` | Get only selected fields from the collection. Essentially an upgraded version of readRaw. |
| values(option) | option: `ValueOption` | Get all distinct non-null values for a given key across a collection. |
| random(max, seed, offset) | max?: `number >= -1` seed?: `number` offset?: `number >= 0` | Read random elements of the collection. |
| Name | Parameters | Description |
| ----------------------------- | ------------------------------------------------------------| --------------------------------------------------------------------------------------------------------------------- |
| sha1() | none | Get the sha1 hash of the file. Can be used to see if same file content without downloading the file. |
| readRaw() | none | Returns the whole content of the JSON. ID values are injected for easier iteration, so this may be different to sha1. |
| get(id) | id: `string \| number` | Get an element from the collection. |
| search(searchOptions, random) | searchOptions: `SearchOption[]` random?:`boolean \| number` | Search through the collection You can randomize the output order with random as true or a given seed. |
| searchKeys(keys) | keys: `string[] \| number[]` | Search specific keys through the collection. |
| select(selectOption) | selectOption: `{ fields: string[] }` | Get only selected fields from the collection Essentially an upgraded version of readRaw. |
| values(valueOption) | valueOption: `{ field: string, flatten?: boolean }` | Get all distinct non-null values for a given key across a collection. |
| random(max, seed, offset) | max?: `number >= -1` seed?: `number` offset?:`number >= 0` | Reads random entries of collection. |
## Search options
The search method can take one or more options to filter entries in a collection. A search option takes a `field` with a `criteria` and compares it to a `value`. You can also use the boolean `ignoreCase` option for string values.
There are more options available than the Firestore `where` command, allowing you to get better and faster search results.
Not all criteria are available depending the field type. There are more options available than the firestore `where` command, allowing you to get better and faster search results.
The search method can take one or more options to filter entries in a collection. A search option takes a `field` with a `criteria` and compares it to a `value`. You can also use the boolean `ignoreCase` option for string values. Available criteria depends on the field type.
### All search options available
| Criteria | Types allowed | Description |
| ----------------------- | ----------------------------- | --------------------------------------------------------------- |
| `'!='` | `boolean`, `number`, `string` | Entry field's value is different from yours |
| `'=='` | `boolean`, `number`, `string` | Entry field's value is equal to yours |
| `'>='` | `number`, `string` | Entry field's value is greater or equal than yours |
| `'<='` | `number`, `string` | Entry field's value is equal to than yours |
| `'>'` | `number`, `string` | Entry field's value is greater than yours |
| `'<'` | `number`, `string` | Entry field's value is lower than yours |
| `'in'` | `number`, `string` | Entry field's value is in the array of values you gave |
| `'includes'` | `string` | Entry field's value includes your substring |
| `'startsWith'` | `string` | Entry field's value starts with your substring |
| `'endsWith'` | `string` | Entry field's value ends with your substring |
| `'array-contains'` | `Array` | Entry field's array contains your value |
| `'array-contains-none'` | `Array` | Entry field's array contains no values from your array |
| `'array-contains-any'` | `Array` | Entry field's array contains at least one value from your array |
| `'array-length-eq'` | `number` | Entry field's array size is equal to your value |
| `'array-length-df'` | `number` | Entry field's array size is different from your value |
| `'array-length-lt'` | `number` | Entry field's array size is lower than your value |
| `'array-length-gt'` | `number` | Entry field's array size is greater than your value |
| `'array-length-le'` | `number` | Entry field's array size is lower or equal to your value |
| `'array-length-ge'` | `number` | Entry field's array size is greater or equal to your value |
| Criteria | Types allowed | Description |
| ---------------------- | ----------------------------- | --------------------------------------------------------------------------------- |
| `'!='` | `boolean`, `number`, `string` | Entry field's value is different from yours |
| `'=='` | `boolean`, `number`, `string` | Entry field's value is equal to yours |
| `'>='` | `number`, `string` | Entry field's value is greater or equal than yours |
| `'<='` | `number`, `string` | Entry field's value is equal to than yours |
| `'>'` | `number`, `string` | Entry field's value is greater than yours |
| `'<'` | `number`, `string` | Entry field's value is lower than yours |
| `'in'` | `number`, `string` | Entry field's value is in the array of values you gave |
| `'includes'` | `string` | Entry field's value includes your substring |
| `'startsWith'` | `string` | Entry field's value starts with your substring |
| `'endsWith'` | `string` | Entry field's value ends with your substring |
| `'array-contains'` | `Array` | Entry field's array contains your value |
| `'array-contains-any'` | `Array` | Entry field's array ends contains your one value of more inside your values array |
| `'array-length-eq'` | `number` | Entry field's array size is equal to your value |
| `'array-length-df'` | `number` | Entry field's array size is different from your value |
| `'array-length-lt'` | `number` | Entry field's array size is lower than your value |
| `'array-length-gt'` | `number` | Entry field's array size is lower greater than your value |
| `'array-length-le'` | `number` | Entry field's array size is lower or equal to your value |
| `'array-length-ge'` | `number` | Entry field's array size is greater or equal to your value |
## Write operations
### Write operations
| Name | Parameters | Description |
| ----------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------------- |
| writeRaw(value) | value: `Object` | Set the entire content of the collection. **⚠️ Very dangerous! ⚠️** |
| add(value) | value: `Object` | Append a value to the collection. Only works if `autoKey` is enabled server-side. |
| addBulk(values) | values: `Object[]` | Append multiple values to the collection. Only works if `autoKey` is enabled server-side. |
| remove(key) | key: `string \| number` | Remove an element from the collection by its key. |
| removeBulk(keys) | keys: `string[] \| number[]` | Remove multiple elements from the collection by their keys. |
| set(key, value) | key: `string \| number`, value: `Object` | Set a value in the collection by its key. |
| setBulk(keys, values) | keys: `string[] \| number[]`, values: `Object[]` | Set multiple values in the collection by their keys. |
| editField(obj) | option: `EditFieldOption` | Edit an element's field in the collection. |
| editFieldBulk(objArray) | options: `EditFieldOption[]` | Edit multiple elements' fields in the collection. |
| Name | Parameters | Description |
| ----------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------------- |
| writeRaw() | none | Set the entire JSON file contents **⚠️ Very dangerous! ⚠️** |
| add(value) | value: `Object` | Adds one element with autoKey into the collection |
| addBulk(values) | value: `Object[]` | Adds multiple elements with autoKey into the collection |
| remove(key) | key: `string \| number` | Remove one element from the collection with the corresponding key |
| removeBulk(keys) | keys: `string[] \| number[]` | Remove multiple elements from the collection with the corresponding keys |
| set(key, value) | key: `string \| number`, value: `Object` | Sets one element with its key and value into the collection |
| setBulk(keys, values) | keys: `string[] \| number[]`, values: `Object[]` | Sets multiple elements with their corresponding keys and values into the collection |
| editField(obj) | obj: `EditObject` | Changes one field of a given element in a collection |
| editFieldBulk(objArray) | objArray: `EditObject[]` | Changes one field per element in a collection |
## Edit field options
### Edit field operations
Edit objects have an `id` of the element, a `field` to edit, an `operation` with what to do to this field, and a possible `value`. Here is a list of operations:
| Operation | Needs value | Types allowed | Description |
| -------------- | ----------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `set` | Yes | `any` | Sets a field to a given value. |
| `remove` | No | `any` | Removes a field from the element. |
| `append` | Yes | `string` | Appends a new string at the end of the string field. |
| `invert` | No | `any` | Inverts the state of a boolean field. |
| `increment` | No | `number` | Adds a number to the field, default is 1. |
| `decrement` | No | `number` | Removes a number from the field, default is 1. |
| `array-push ` | Yes | `any` | Push an element to the end of an array field. |
| `array-delete` | Yes | `number` | Removes an element at a certain index in an array field. Check the PHP [array_splice](https://www.php.net/manual/function.array-splice) offset for more info. |
| `array-splice` | Yes | `[number, number]` | Removes certain elements. Check the PHP [array_splice](https://www.php.net/manual/function.array-splice) offset and length for more info. |
| Operation | Needs value | Allowed value types | Description |
| -------------- | ----------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------|
| `set` | Yes | `any` | Sets a value to a given field. |
| `remove` | No | *N/A* | Removes a field from the element. |
| `append` | Yes | `string` | Appends a string to the end of a string field. |
| `invert` | No | *N/A* | Inverts the state of a boolean field. |
| `increment` | No | `number` | Adds a number to the field (default: 1). |
| `decrement` | No | `number` | Removes a number from the field (default: 1). |
| `array-push ` | Yes | `any` | Pushes an element to the end of an array field. |
| `array-delete` | Yes | `number` | Removes an array element by index. |
| `array-splice` | Yes | `[number, number, any?]` | Last argument is optional. Check the PHP [array_splice](https://www.php.net/manual/function.array-splice) documentation for more info. |
<br>
Various other methods and constants exist in the JavaScript client, which will make more sense once you learn what's actually happening behind the scenes.
# PHP Part
# PHP Backend
The PHP files are the ones handling files, read and writes. They also handle `GET` and `POST` requests to manipulate the database.
Firestorm's PHP files handle files, read, and writes, through `GET` and `POST` requests sent by the JavaScript client. All JavaScript methods correspond to an equivalent Axios request to the relevant PHP file.
## PHP setup
The developer has to create two main files at the root of their Firestorm setup: `tokens.php` and `config.php`.
The server-side files to handle requests can be found and copied to your hosting platform [here](./php/). The two files that need editing are `tokens.php` and `config.php`.
`tokens.php` will contain the tokens inside a `$db_tokens` value array with the tokens to use. You will use these tokens to write data or read private tables.
- `tokens.php` contains writing tokens declared in a `$db_tokens` array. These correspond to the tokens used with `firestorm.token()` in the JavaScript client.
- `config.php` stores all of your collections. This file needs to declare a `$database_list` associative array of `JSONDatabase` instances.
`config.php` stores all of your collections config. You will create a `$database_list` variable with an array of `JSONDatabase` instances
```php
<?php
// config.php
require_once('./classes/JSONDatabase.php');
require_once './classes/JSONDatabase.php';
$database_list = array();
// without constructor
$tmp = new JSONDatabase;
$tmp->folderPath = './files/';
$tmp->fileName = 'users';
$tmp->autoKey = false;
$tmp->fileName = 'orders';
$tmp->autoKey = true;
$tmp->autoIncrement = false;
$database_list[$tmp->fileName] = $tmp;
$tmp = new JSONDatabase;
// with constructor ($fileName, $autoKey = true, $autoIncrement = true)
$tmp = new JSONDatabase('users', false);
$tmp->folderPath = './files/';
$tmp->fileName = 'paths';
$tmp->autoKey = true;
$database_list[$tmp->fileName] = $tmp;
?>
```
The database will be stored in `<folderPath>/<filename>.json` and `autoKey` allows or forbids some write operations.
- The database will be stored in `<folderPath>/<filename>.json` (default folder: `./files/`).
- `autoKey` controls whether to automatically generate the key name or to have explicit key names (default: `true`).
- `autoIncrement` controls whether to simply start generating key names from zero or to use a [random ID](https://www.php.net/manual/en/function.uniqid.php) each time (default: `true`).
- The key in the `$database_list` array is what the collection should be referred to in the JavaScript collection constructor. This can be different from the JSON filename if needed.
## Firestorm Files
If you're working with multiple collections, it's probably easier to initialize them all in the array constructor directly:
File API functions are detailed in the `files.php` PHP script. If you do not want to include this functionality, then just delete this file.
```php
// config.php
<?php
require_once './classes/JSONDatabase.php';
$database_list = array(
'orders' => new JSONDatabase('orders', true),
'users' => new JSONDatabase('users', false),
)
```
You have to add 2 new configuration variables to your `config.php` file:
## Permissions
```php
// whitelist of correct extensions
$authorized_file_extension = array('.txt', '.png');
The PHP scripts used to write and read files need permissions to edit the JSON files. You can give Firestorm rights to a folder with the following command:
// subfolder of uploads location, must start with dirname($_SERVER['SCRIPT_FILENAME'])
// to force a subfolder of Firestorm installation
$STORAGE_LOCATION = dirname($_SERVER['SCRIPT_FILENAME']) . '/uploads/';
```sh
sudo chown -R www-data "/path/to/firestorm/root/"
```
You can use the wrapper functions in order to upload, get and delete a file.
If the folder is accessible from server url, you can directly type its address.
# Firestorm Files
### File rights
Firestorm's file APIs are implemented in `files.php`. If you don't need file-related features, then simply delete this file.
The PHP scripts create folders and files, so the script will fail if the PHP user doesn't have write permissions.
You can give rights to a folder with the following command:
To work with files server-side, you need two new configuration variables in `config.php`:
```php
// Extension whitelist
$authorized_file_extension = array('.txt', '.png', '.jpg', '.jpeg');
// Root directory for where files should be uploaded
// ($_SERVER['SCRIPT_FILENAME']) is a shortcut to the root Firestorm directory.
$STORAGE_LOCATION = dirname($_SERVER['SCRIPT_FILENAME']) . '/uploads/';
```
sudo chown -R www-data "/path/to/uploads/"
```
### Upload a file
From there, you can use the functions in `firestorm.files` (detailed below) from the JavaScript client.
In order to upload a file, you have to give the function a `FormData` object. This class is generated from forms and is [native in modern browsers](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData) but in Node.js can be imported with the [form-data package](https://www.npmjs.com/package/form-data).
## Upload a file
The uploaded file content can be a [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob), a [Buffer](https://nodejs.org/api/buffer.html) or an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).
`firestorm.files.upload` uses a `FormData` object to represent an uploaded file. This class is generated from forms and is [native in modern browsers](https://developer.mozilla.org/en-US/docs/Web/API/FormData/FormData), and with Node.js can be installed with the [form-data](https://www.npmjs.com/package/form-data) package.
There is additionally an overwrite option in order to avoid big mistakes and allow unique file names.
The uploaded file content can be a [String](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String), a [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob), a [Buffer](https://nodejs.org/api/buffer.html), or an [ArrayBuffer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer).
There is additionally an overwrite option in order to avoid mistakes.
```js
const FormData = require("form-data");
const firestorm = require("firestorm-db");

@@ -235,4 +243,7 @@ firestorm.address("ADDRESS_VALUE");

form.append("path", "/quote.txt");
form.append("file", "but your kids are gonna love it.", "quote.txt"); // make sure to set a temporary name to the file
form.append("overwrite", "true"); // override optional argument (do not append to set to false)
// make sure to set a temporary file name
form.append("file", "but your kids are gonna love it.", "quote.txt");
// override is false by default; don't append it if you don't need to
form.append("overwrite", "true");
const uploadPromise = firestorm.files.upload(form);

@@ -247,3 +258,3 @@

You can get a file via its direct file URL location or its content with a request.
`firestorm.files.get` takes a file's direct URL location or its content as its parameter. If your upload folder is accessible from a server URL, you can directly use its address to retrieve the file without this method.

@@ -257,3 +268,3 @@ ```js

getPromise
.then((fileContent) => console.log(fileContent)) // 'but your kids are gonna love it.
.then((fileContent) => console.log(fileContent)) // but your kids are gonna love it.
.catch((err) => console.error(err));

@@ -264,3 +275,3 @@ ```

Because I am a nice guy, I thought about deletion too. So I figured I would put a method to delete the files too.
`firestorm.files.delete` has the same interface as `firestorm.files.get`, but as the name suggests, it deletes the file.

@@ -279,30 +290,180 @@ ```js

## Memory warning
# TypeScript Support
Handling big collections can cause memory allocation issues like:
Firestorm ships with TypeScript support out of the box.
## Collection types
Collections in TypeScript take a generic parameter `T`, which is the type of each element in the collection. If you aren't using a relational collection, this can simply be set to `any`.
```ts
import firestorm from "firestorm-db";
firestorm.address("ADDRESS_VALUE");
interface User {
name: string;
password: string;
pets: string[];
}
const userCollection = firestorm.collection<User>("users");
const johnDoe = await userCollection.get(123456789);
// type: { name: string, password: string, pets: string[] }
```
Fatal error:
Allowed memory size of 134217728 bytes exhausted (tried to allocate 32360168 bytes)
Injected methods should also be stored in this interface. They'll get filtered out from write operations to prevent false positives:
```ts
import firestorm from "firestorm-db";
firestorm.address("ADDRESS_VALUE");
interface User {
name: string;
hello(): string;
}
const userCollection = firestorm.collection("users", (el) => {
// interface types should agree with injected methods
el.hello = () => `${el.name} says hello!`;
return el;
});
const johnDoe = await userCollection.get(123456789);
const hello = johnDoe.hello(); // type: string
await userCollection.add({
name: "Mary Doe",
// error: 'hello' does not exist in type 'Addable<User>'.
hello() {
return "Mary Doe says hello!"
}
})
```
If you encounter a memory allocation issue, you have to allow more memory through `/etc/php/7.4/apache2/php.ini` with a bigger value:
## Additional types
Additional types exist for search criteria options, write method return types, configuration methods, the file handler, etc.
```ts
import firestorm from "firestorm-db";
const address = firestorm.address("ADDRESS_VALUE");
// type: string
const deleteConfirmation = await firestorm.files.delete("/quote.txt");
// type: firestorm.WriteConfirmation
```
memory_limit = 256M
# Advanced Features
## `ID_FIELD` and its meaning
There's a constant in Firestorm called `ID_FIELD`, which is a JavaScript-side property added afterwards to each query element.
Its value will always be the key of the element its in, which allows you to use `Object.values` on results without worrying about losing the elements' key names. Additionally, it can be used in the method adder in the constructor, and is convenient for collections where the key name is significant.
```js
const userCollection = firestorm.collection("users", (el) => {
el.basicInfo = () => `${el.name} (${el[firestorm.ID_FIELD]})`;
return el;
});
const returnedID = await userCollection.add({ name: "Bob", age: 30 });
const returnedUser = await userCollection.get(returnedID);
console.log(returnedID === returnedUser[firestorm.ID_FIELD]); // true
returnedUser.basicInfo(); // Bob (123456789)
```
## API endpoints
As it's entirely a JavaScript construct, `ID_FIELD` values will never be in your collection.
All Firestorm methods correspond to an equivalent Axios request to the relevant PHP file. Read requests are `GET` requests and write requests are `POST` requests with provided JSON data.
## Add and set operations
The first keys in the request will always be the same:
You may have noticed two different methods that seem to do the same thing: `add` and `set` (and their corresponding bulk variants). The key difference is that `add` is used on collections where `autoKey` is enabled, and `set` is used on collections where `autoKey` is disabled. `autoIncrement` doesn't affect this behavior.
For instance, the following PHP configuration will disable add operations:
```php
$database_list['users'] = new JSONDatabase('users', false);
```
```js
const userCollection = firestorm.collection("users");
// Error: Automatic key generation is disabled
await userCollection.add({ name: "John Doe", age: 30 });
```
Add operations return the generated ID of the added element, since it isn't known at add time, but set operations simply return a confirmation. If you want to get an element after it's been set, use the ID passed into the method.
```js
// this will not work, since set operations don't return the ID
userCollection.set(123, { name: "John Doe", age: 30 })
.then((id) => userCollection.get(id));
```
## Combining collections
Using add methods in the constructor, you can link multiple collections together.
```js
const orders = firestorm.collection("orders");
// using the example of a customer having orders
const customers = firestorm.collection("customers", (el) => {
el.getOrders = () => orders.search([
{
field: "customer",
criteria: "==",
// assuming the customers field in the orders collection is a user ID
value: el[firestorm.ID_FIELD]
}
])
return el;
})
const johnDoe = await customers.get(123456789);
// returns orders where the customer field is John Doe's ID
await johnDoe.getOrders();
```
This functionality is particularly useful for complex data hierarchies that store fields as ID values to other collections, and is the main reason why add methods exist in the first place. It can also be used to split deeply nested data structures to increase server-side performance by only loading collections when necessary.
## Manually sending data
Each operation type requests a different file. In the JavaScript client, the corresponding file gets appended onto your base Firestorm address.
- Read requests are `GET` requests sent to `<your_address_here>/get.php`.
- Write requests are `POST` requests sent to `<your_address_here>/post.php` with JSON data.
- File requests are sent to `<your_address_here>/files.php` with form data.
The first keys in a Firestorm request will always be the same regardless of its type, and further keys will depend on the specific method:
```json
{
"collection": "<collectionName>",
"token": "<writeTokenIfNecessary>",
"command": "<methodName>",
...
"collection": "<collectionName>",
"token": "<writeTokenIfNecessary>",
"command": "<methodName>",
...
}
```
PHP grabs the `JSONDatabase` instance created in `config.php` using the `collection` key in the request as the `$database_list` key name. From there, the `token` is used to validate the request if needed and the `command` is found and executed.
## Memory management
Handling very large collections can cause memory allocation issues:
```
Fatal error:
Allowed memory size of 134217728 bytes exhausted (tried to allocate 32360168 bytes)
```
If you encounter a memory allocation issue, simply change the memory limit in `/etc/php/7.4/apache2/php.ini` to be bigger:
```
memory_limit = 256M
```
If this doesn't help, considering splitting your collection into smaller collections and linking them together with methods.

@@ -0,4 +1,7 @@

const IS_NODE = typeof process === "object";
try {
if (typeof process === "object") var axios = require("axios").default;
} catch (_error) {}
// ambient axios context in browser
if (IS_NODE) var axios = require("axios").default;
} catch {}

@@ -8,3 +11,3 @@ /**

* @property {string} field - The field to be searched for
* @property {"!=" | "==" | ">=" | "<=" | "<" | ">" | "in" | "includes" | "startsWith" | "endsWith" | "array-contains" | "array-contains-any" | "array-length-(eq|df|gt|lt|ge|le)"} criteria - Search criteria to filter results
* @property {"!=" | "==" | ">=" | "<=" | "<" | ">" | "in" | "includes" | "startsWith" | "endsWith" | "array-contains" | "array-contains-none" | "array-contains-any" | "array-length-(eq|df|gt|lt|ge|le)"} criteria - Search criteria to filter results
* @property {string | number | boolean | Array} value - The value to be searched for

@@ -15,3 +18,3 @@ * @property {boolean} [ignoreCase] - Is it case sensitive? (default true)

/**
* @typedef {Object} EditObject
* @typedef {Object} EditFieldOption
* @property {string | number} id - The affected element

@@ -24,3 +27,3 @@ * @property {string} field - The field to edit

/**
* @typedef {Object} ValueObject
* @typedef {Object} ValueOption
* @property {string} field - Field to search

@@ -36,3 +39,3 @@ * @property {boolean} [flatten] - Flatten array fields? (default false)

/**
* @typedef {WriteConfirmation}
* @typedef {Object} WriteConfirmation
* @property {string} message - Write status

@@ -78,3 +81,3 @@ */

* @ignore
* @param {Promise<AxiosPromise>} request The Axios concerned request
* @param {AxiosPromise} request - Axios request promise
*/

@@ -90,3 +93,3 @@ const __extract_data = (request) => {

/**
* Class representing a collection
* Represents a Firestorm Collection
* @template T

@@ -101,5 +104,5 @@ */

constructor(name, addMethods = (el) => el) {
if (name === undefined) throw new SyntaxError("Collection must have a name");
if (!name) throw new SyntaxError("Collection must have a name");
if (typeof addMethods !== "function")
throw new TypeError("Collection must have an addMethods of type Function");
throw new TypeError("Collection add methods must be a function");
this.addMethods = addMethods;

@@ -113,15 +116,20 @@ this.collectionName = name;

* @ignore
* @param {AxiosPromise} req - Incoming request
* @returns {Object | Object[]}
* @param {any} el - Value to add methods to
* @param {boolean} [nested] - Nest the methods inside an object
* @returns {any}
*/
__add_methods(req) {
if (!(req instanceof Promise)) req = Promise.resolve(req);
return req.then((el) => {
if (Array.isArray(el)) return el.map((el) => this.addMethods(el));
el[Object.keys(el)[0]][ID_FIELD_NAME] = Object.keys(el)[0];
el = el[Object.keys(el)[0]];
__add_methods(el, nested = true) {
// can't add properties on falsy values
if (!el) return el;
if (Array.isArray(el)) return el.map((el) => this.addMethods(el));
// nested objects
if (nested && typeof el === "object") {
Object.keys(el).forEach((k) => {
el[k] = this.addMethods(el[k]);
});
return el;
}
// else on the object itself
return this.addMethods(el);
});
// add directly to single object
return this.addMethods(el);
}

@@ -133,3 +141,3 @@

* @ignore
* @param {AxiosPromise} request - The Axios concerned request
* @param {AxiosPromise} request - Axios request promise
*/

@@ -141,40 +149,92 @@ __extract_data(request) {

/**
* Send get request and extract data from response
* Send GET request with provided data and return extracted response
* @private
* @ignore
* @param {Object} data - Body data
* @returns {Promise<Object|Object[]>} data out
* @param {string} command - The read command name
* @param {Object} [data] - Body data
* @param {boolean} [objectLike] - Reject if an object or array isn't being returned
* @returns {Promise<any>} Extracted response
*/
__get_request(data) {
const request =
typeof process === "object"
? axios.get(readAddress(), {
data: data,
})
: axios.post(readAddress(), data);
return this.__extract_data(request);
__get_request(command, data = {}, objectLike = true) {
const obj = {
collection: this.collectionName,
command: command,
...data,
};
const request = IS_NODE
? axios.get(readAddress(), { data: obj })
: axios.post(readAddress(), obj);
return this.__extract_data(request).then((res) => {
// reject php error strings if enforcing return type
if (objectLike && typeof res !== "object") return Promise.reject(res);
return res;
});
}
/**
* Get an element from the collection
* @param {string | number} id - The ID of the element you want to get
* @returns {Promise<T>} Corresponding value
* Generate POST data with provided data
* @private
* @ignore
* @param {string} command - The write command name
* @param {Object} [value] - The value for the command
* @param {boolean} [multiple] - Used to delete multiple
* @returns {Object} Write data object
*/
get(id) {
return this.__get_request({
__write_data(command, value = undefined, multiple = false) {
const obj = {
token: writeToken(),
collection: this.collectionName,
command: "get",
id: id,
}).then((res) => this.__add_methods(res));
command: command,
};
// clone/serialize data if possible (prevents mutating data)
if (value) value = JSON.parse(JSON.stringify(value));
if (multiple && Array.isArray(value)) {
value.forEach((v) => {
if (typeof v === "object" && !Array.isArray(v) && v != null) delete v[ID_FIELD_NAME];
});
} else if (
multiple === false &&
value !== null &&
value !== undefined &&
typeof value !== "number" &&
typeof value !== "string" &&
!Array.isArray(value)
) {
if (typeof value === "object") value = { ...value };
delete value[ID_FIELD_NAME];
}
if (value) {
if (multiple) obj.values = value;
else obj.value = value;
}
return obj;
}
/**
* Get the sha1 hash of the file
* - Can be used to see if same file content without downloading the file
* Get the sha1 hash of the JSON
* - Can be used to compare file content without downloading the file
* @returns {string} The sha1 hash of the file
*/
sha1() {
return this.__get_request({
collection: this.collectionName,
command: "sha1",
// string value is correct so we don't need validation
return this.__get_request("sha1", {}, false);
}
/**
* Get an element from the collection by its key
* @param {string | number} key - Key to search
* @returns {Promise<T>} The found element
*/
get(key) {
return this.__get_request("get", {
id: key,
}).then((res) => {
const firstKey = Object.keys(res)[0];
res[firstKey][ID_FIELD_NAME] = firstKey;
res = res[firstKey];
return this.__add_methods(res, false);
});

@@ -184,34 +244,48 @@ }

/**
* Search through collection
* @param {SearchOption[]} searchOptions - Array of search options
* Get multiple elements from the collection by their keys
* @param {string[] | number[]} keys - Array of keys to search
* @returns {Promise<T[]>} The found elements
*/
searchKeys(keys) {
if (!Array.isArray(keys)) return Promise.reject(new TypeError("Incorrect keys"));
return this.__get_request("searchKeys", {
search: keys,
}).then((res) => {
const arr = Object.entries(res).map(([id, value]) => {
value[ID_FIELD_NAME] = id;
return value;
});
return this.__add_methods(arr);
});
}
/**
* Search through the collection
* @param {SearchOption[]} options - Array of search options
* @param {boolean | number} [random] - Random result seed, disabled by default, but can activated with true or a given seed
* @returns {Promise<T[]>} The found elements
*/
search(searchOptions, random = false) {
if (!Array.isArray(searchOptions))
return Promise.reject(new Error("searchOptions shall be an array"));
search(options, random = false) {
if (!Array.isArray(options))
return Promise.reject(new TypeError("searchOptions shall be an array"));
searchOptions.forEach((searchOption) => {
if (
searchOption.field === undefined ||
searchOption.criteria === undefined ||
searchOption.value === undefined
)
return Promise.reject(new Error("Missing fields in searchOptions array"));
options.forEach((option) => {
if (option.field === undefined || option.criteria === undefined || option.value === undefined)
return Promise.reject(new TypeError("Missing fields in searchOptions array"));
if (typeof searchOption.field !== "string")
if (typeof option.field !== "string")
return Promise.reject(
new Error(`${JSON.stringify(searchOption)} search option field is not a string`),
new TypeError(`${JSON.stringify(option)} search option field is not a string`),
);
if (searchOption.criteria == "in" && !Array.isArray(searchOption.value))
return Promise.reject(new Error("in takes an array of values"));
if (option.criteria == "in" && !Array.isArray(option.value))
return Promise.reject(new TypeError("in takes an array of values"));
//TODO: add more strict value field warnings in JS and PHP
// TODO: add more strict value field warnings in JS and PHP
});
const params = {
collection: this.collectionName,
command: "search",
search: searchOptions,
search: options,
};

@@ -226,3 +300,3 @@

return Promise.reject(
new Error("random takes as parameter true, false or an integer value"),
new TypeError("random takes as parameter true, false or an integer value"),
);

@@ -233,3 +307,3 @@ params.random = { seed };

return this.__get_request(params).then((res) => {
return this.__get_request("search", params).then((res) => {
const arr = Object.entries(res).map(([id, value]) => {

@@ -245,39 +319,16 @@ value[ID_FIELD_NAME] = id;

/**
* Search specific keys through collection
* @param {string[] | number[]} keys - Array of keys to search
* @returns {Promise<T[]>} The found elements
* Read the entire collection
* @param {boolean} [original] - Disable ID field injection for easier iteration (default false)
* @returns {Promise<Record<string, T>>} The entire collection
*/
searchKeys(keys) {
if (!Array.isArray(keys)) return Promise.reject("Incorrect keys");
readRaw(original = false) {
return this.__get_request("read_raw").then((data) => {
if (original) return this.__add_methods(data);
return this.__get_request({
collection: this.collectionName,
command: "searchKeys",
search: keys,
}).then((res) => {
const arr = Object.entries(res).map(([id, value]) => {
value[ID_FIELD_NAME] = id;
return value;
});
return this.__add_methods(arr);
});
}
/**
* Returns the whole content of the JSON
* @returns {Promise<Record<string, T>>} The entire collection
*/
readRaw() {
return this.__get_request({
collection: this.collectionName,
command: "read_raw",
}).then((data) => {
// preserve as object
Object.keys(data).forEach((key) => {
data[key][ID_FIELD_NAME] = key;
this.addMethods(data[key]);
});
return data;
return this.__add_methods(data);
});

@@ -287,3 +338,4 @@ }

/**
* Returns the whole content of the JSON
* Read the entire collection
* - ID values are injected for easier iteration, so this may be different from {@link sha1}
* @deprecated Use {@link readRaw} instead

@@ -298,19 +350,15 @@ * @returns {Promise<Record<string, T>>} The entire collection

* Get only selected fields from the collection
* - Essentially an upgraded version of readRaw
* @param {SelectOption} selectOption - Select options
* - Essentially an upgraded version of {@link readRaw}
* @param {SelectOption} option - The fields you want to select
* @returns {Promise<Record<string, Partial<T>>>} Selected fields
*/
select(selectOption) {
if (!selectOption) selectOption = {};
return this.__get_request({
collection: this.collectionName,
command: "select",
select: selectOption,
select(option) {
if (!option) option = {};
return this.__get_request("select", {
select: option,
}).then((data) => {
Object.keys(data).forEach((key) => {
data[key][ID_FIELD_NAME] = key;
this.addMethods(data[key]);
});
return data;
return this.__add_methods(data);
});

@@ -321,15 +369,14 @@ }

* Get all distinct non-null values for a given key across a collection
* @param {ValueObject} valueOption - Value options
* @param {ValueOption} option - Value options
* @returns {Promise<T[]>} Array of unique values
*/
values(valueOption) {
if (!valueOption) return Promise.reject("Value option must be provided");
if (typeof valueOption.field !== "string") return Promise.reject("Field must be a string");
if (valueOption.flatten !== undefined && typeof valueOption.flatten !== "boolean")
return Promise.reject("Flatten must be a boolean");
values(option) {
if (!option) return Promise.reject(new TypeError("Value option must be provided"));
if (typeof option.field !== "string")
return Promise.reject(new TypeError("Field must be a string"));
if (option.flatten !== undefined && typeof option.flatten !== "boolean")
return Promise.reject(new TypeError("Flatten must be a boolean"));
return this.__get_request({
collection: this.collectionName,
command: "values",
values: valueOption,
return this.__get_request("values", {
values: option,
}).then((data) =>

@@ -342,3 +389,3 @@ // no ID_FIELD or method injection since no ids are returned

/**
* Returns random max entries offsets with a given seed
* Read random elements of the collection
* @param {number} max - The maximum number of entries

@@ -353,3 +400,3 @@ * @param {number} seed - The seed to use

if (typeof max !== "number" || !Number.isInteger(max) || max < -1)
return Promise.reject(new Error("Expected integer >= -1 for the max"));
return Promise.reject(new TypeError("Expected integer >= -1 for the max"));
params.max = max;

@@ -361,10 +408,10 @@ }

if (hasOffset && !hasSeed)
return Promise.reject(new Error("You can't put an offset without a seed"));
return Promise.reject(new TypeError("You can't put an offset without a seed"));
if (hasOffset && (typeof offset !== "number" || !Number.isInteger(offset) || offset < 0))
return Promise.reject(new Error("Expected integer >= -1 for the max"));
return Promise.reject(new TypeError("Expected integer >= -1 for the max"));
if (hasSeed) {
if (typeof seed !== "number" || !Number.isInteger(seed))
return Promise.reject(new Error("Expected integer for the seed"));
return Promise.reject(new TypeError("Expected integer for the seed"));

@@ -376,5 +423,3 @@ if (!hasOffset) offset = 0;

return this.__get_request({
collection: this.collectionName,
command: "random",
return this.__get_request("random", {
random: params,

@@ -384,6 +429,5 @@ }).then((data) => {

data[key][ID_FIELD_NAME] = key;
this.addMethods(data[key]);
});
return data;
return this.__add_methods(data);
});

@@ -393,43 +437,4 @@ }

/**
* Creates write requests with given value
* @private
* @ignore
* @param {string} command The write command you want
* @param {Object} [value] The value for this command
* @param {boolean | undefined} multiple if I need to delete multiple
* @returns {Object} Write data object
*/
__write_data(command, value = undefined, multiple = false) {
const obj = {
token: writeToken(),
collection: this.collectionName,
command: command,
};
if (multiple === true && Array.isArray(value)) {
// solves errors with undefined and null values
value.forEach((v) => {
if (typeof value != "number" && typeof value != "string" && !Array.isArray(value))
delete v[ID_FIELD_NAME];
});
} else if (
multiple === false &&
value != null &&
value != undefined &&
typeof value != "number" &&
typeof value != "string" &&
!Array.isArray(value)
) {
// solves errors with undefined and null values
delete value[ID_FIELD_NAME];
}
if (value) {
if (multiple) obj["values"] = value;
else obj["value"] = value;
}
return obj;
}
/**
* Set the entire JSON file contents
* Set the entire content of the collection.
* - Only use this method if you know what you are doing!
* @param {Record<string, T>} value - The value to write

@@ -439,5 +444,4 @@ * @returns {Promise<WriteConfirmation>} Write confirmation

writeRaw(value) {
if (value === undefined || value === null) {
return Promise.reject(new Error("writeRaw value must not be undefined or null"));
}
if (value === undefined || value === null)
return Promise.reject(new TypeError("writeRaw value must not be undefined or null"));
return this.__extract_data(axios.post(writeAddress(), this.__write_data("write_raw", value)));

@@ -447,3 +451,4 @@ }

/**
* Set the entire JSON file contents
* Set the entire content of the collection.
* - Only use this method if you know what you are doing!
* @deprecated Use {@link writeRaw} instead

@@ -458,5 +463,6 @@ * @param {Record<string, T>} value - The value to write

/**
* Automatically add a value to the JSON file
* Append a value to the collection
* - Only works if autoKey is enabled server-side
* @param {T} value - The value (without methods) to add
* @returns {Promise<string>} The generated ID of the added element
* @returns {Promise<string>} The generated key of the added element
*/

@@ -469,3 +475,3 @@ add(value) {

if (typeof res != "object" || !("id" in res) || typeof res.id != "string")
return Promise.reject(new Error("Incorrect result"));
return Promise.reject(res);
return res.id;

@@ -476,5 +482,6 @@ });

/**
* Automatically add multiple values to the JSON file
* @param {Object[]} values - The values (without methods) to add
* @returns {Promise<string[]>} The generated IDs of the added elements
* Append multiple values to the collection
* - Only works if autoKey is enabled server-side
* @param {T[]} values - The values (without methods) to add
* @returns {Promise<string[]>} The generated keys of the added elements
*/

@@ -488,3 +495,3 @@ addBulk(values) {

/**
* Remove an element from the collection by its ID
* Remove an element from the collection by its key
* @param {string | number} key The key from the entry to remove

@@ -498,3 +505,3 @@ * @returns {Promise<WriteConfirmation>} Write confirmation

/**
* Remove multiple elements from the collection by their IDs
* Remove multiple elements from the collection by their keys
* @param {string[] | number[]} keys The key from the entries to remove

@@ -508,4 +515,4 @@ * @returns {Promise<WriteConfirmation>} Write confirmation

/**
* Set a value in the collection by ID
* @param {string} key - The ID of the element you want to edit
* Set a value in the collection by key
* @param {string} key - The key of the element you want to edit
* @param {T} value - The value (without methods) you want to edit

@@ -521,4 +528,4 @@ * @returns {Promise<WriteConfirmation>} Write confirmation

/**
* Set multiple values in the collection by their IDs
* @param {string[]} keys - The IDs of the elements you want to edit
* Set multiple values in the collection by their keys
* @param {string[]} keys - The keys of the elements you want to edit
* @param {T[]} values - The values (without methods) you want to edit

@@ -534,8 +541,8 @@ * @returns {Promise<WriteConfirmation>} Write confirmation

/**
* Edit one field of the collection
* @param {EditObject} obj - The edit object
* @returns {Promise<{ success: boolean }>} Edit confirmation
* Edit an element's field in the collection
* @param {EditFieldOption} option - The edit object
* @returns {Promise<WriteConfirmation>} Edit confirmation
*/
editField(obj) {
const data = this.__write_data("editField", obj, null);
editField(option) {
const data = this.__write_data("editField", option, null);
return this.__extract_data(axios.post(writeAddress(), data));

@@ -545,8 +552,8 @@ }

/**
* Changes one field from an element in this collection
* @param {EditObject[]} objArray The edit object array with operations
* @returns {Promise<{ success: boolean[] }>} Edit confirmation
* Edit multiple elements' fields in the collection
* @param {EditFieldOption[]} options - The edit objects
* @returns {Promise<WriteConfirmation>} Edit confirmation
*/
editFieldBulk(objArray) {
const data = this.__write_data("editFieldBulk", objArray, undefined);
editFieldBulk(options) {
const data = this.__write_data("editFieldBulk", options, undefined);
return this.__extract_data(axios.post(writeAddress(), data));

@@ -561,3 +568,3 @@ }

/**
* Change the current Firestorm address
* Change or get the current Firestorm address
* @param {string} [newValue] - The new Firestorm address

@@ -575,3 +582,3 @@ * @returns {string} The stored Firestorm address

/**
* Change the current Firestorm token
* Change or get the current Firestorm token
* @param {string} [newValue] - The new Firestorm write token

@@ -592,3 +599,3 @@ * @returns {string} The stored Firestorm write token

* @param {Function} [addMethods] - Additional methods and data to add to the objects
* @returns {Collection<T>} The collection
* @returns {Collection<T>} The collection instance
*/

@@ -601,5 +608,6 @@ collection(name, addMethods = (el) => el) {

* Create a temporary Firestorm collection with no methods
* @deprecated Use {@link collection} with no second argument instead
* @template T
* @param {string} name - The table name to get
* @returns {Collection<T>} The collection
* @returns {Collection<T>} The table instance
*/

@@ -610,3 +618,3 @@ table(name) {

/** Value for the id field when researching content */
/** Value for the ID field when searching content */
ID_FIELD: ID_FIELD_NAME,

@@ -624,3 +632,3 @@

* @memberof firestorm.files
* @param {string} path - The file path wanted
* @param {string} path - The wanted file path
* @returns {Promise<any>} File contents

@@ -641,3 +649,3 @@ */

* @memberof firestorm.files
* @param {FormData} form - The form data with path, filename, and file
* @param {FormData} form - Form data with path, filename, and file
* @returns {Promise<WriteConfirmation>} Write confirmation

@@ -657,3 +665,3 @@ */

/**
* Deletes a file by path
* Delete a file by its path
* @memberof firestorm.files

@@ -676,6 +684,5 @@ * @param {string} path - The file path to delete

// browser check
try {
if (typeof process === "object") module.exports = firestorm;
} catch (_error) {
// normal browser
}
if (IS_NODE) module.exports = firestorm;
} catch {}

@@ -0,1 +1,3 @@

import * as NodeFormData from "form-data";
export type NumberCriteria =

@@ -25,2 +27,3 @@ | "==" /** Value is equal to the provided value */

| "array-contains" /** Value is in the given array */
| "array-contains-none" /** No value of the array is in the given array */
| "array-contains-any" /** Any value of the array is in the given array */

@@ -95,3 +98,3 @@ | "array-length-eq" /** Array length is equal to the provided value */

export type EditField<T> = {
export type EditFieldOption<T> = {
[K in keyof T]: BaseEditField<T> &

@@ -129,4 +132,4 @@ (

field: Field<Array<unknown>, T>;
operation: "array-slice";
value: [number, number];
operation: "array-splice";
value: [number, number] | [number, number, T[Field<Array<unknown>, T>][any]];
}

@@ -177,3 +180,3 @@ );

export type Addable<T> = Omit<RemoveMethods<T>, "id">;
/** ID field can be provided in request */
/** ID field known at add time */
export type Settable<T> = Addable<T> & {

@@ -183,3 +186,10 @@ id?: number | string;

export class Collection<T> {
/**
* Represents a Firestorm Collection
* @template T Type of collection element
*/
declare class Collection<T> {
/** Name of the Firestorm collection */
public readonly collectionName: string;
/**

@@ -193,18 +203,25 @@ * Create a new Firestorm collection instance

/**
* Get an element from the collection
* @param id - The ID of the element you want to get
* @returns Corresponding value
* Get the sha1 hash of the collection
* - Can be used to compare file content without downloading the file
* @returns The sha1 hash of the file
*/
public get(id: string | number): Promise<T>;
public sha1(): string;
/**
* Get the sha1 hash of the file
* - Can be used to see if same file content without downloading the file
* @returns The sha1 hash of the file
* Get an element from the collection by its key
* @param key - Key to search
* @returns The found element
*/
public sha1(): string;
public get(key: string | number): Promise<T>;
/**
* Get multiple elements from the collection by their keys
* @param keys - Array of keys to search
* @returns The found elements
*/
public searchKeys(keys: string[] | number[]): Promise<T[]>;
/**
* Search through the collection
* @param options - Array of searched options
* @param options - Array of search options
* @param random - Random result seed, disabled by default, but can activated with true or a given seed

@@ -214,3 +231,3 @@ * @returns The found elements

public search(
options: SearchOption<RemoveMethods<T> & { id: string | number }>[],
options: SearchOption<RemoveMethods<T> & { id: string }>[],
random?: boolean | number,

@@ -220,16 +237,11 @@ ): Promise<T[]>;

/**
* Search specific keys through the collection
* @param keys - Array of keys to search
* @returns The found elements
*/
public searchKeys(keys: string[] | number[]): Promise<T[]>;
/**
* Returns the whole content of the JSON
* Read the entire collection
* @param original - Disable ID field injection for easier iteration (default false)
* @returns The entire collection
*/
public readRaw(): Promise<Record<string, T>>;
public readRaw(original?: boolean): Promise<Record<string, T>>;
/**
* Returns the whole content of the JSON
* Read the entire collection
* - ID values are injected for easier iteration, so this may be different from {@link sha1}
* @deprecated Use {@link readRaw} instead

@@ -242,4 +254,4 @@ * @returns The entire collection

* Get only selected fields from the collection
* - Essentially an upgraded version of readRaw
* @param option - The option you want to select
* - Essentially an upgraded version of {@link readRaw}
* @param option - The fields you want to select
* @returns Selected fields

@@ -249,3 +261,3 @@ */

option: SelectOption<K>,
): Promise<Record<string, Pick<T & { id: string | number }, K[number]>>>;
): Promise<Record<string, Pick<T & { id: string }, K[number]>>>;

@@ -262,3 +274,3 @@ /**

/**
* Get random max entries offset with a given seed
* Read random elements of the collection
* @param max - The maximum number of entries

@@ -272,3 +284,4 @@ * @param seed - The seed to use

/**
* Set the entire JSON file contents
* Set the entire content of the collection.
* - Only use this method if you know what you are doing!
* @param value - The value to write

@@ -280,3 +293,4 @@ * @returns Write confirmation

/**
* Set the entire JSON file contents
* Set the entire content of the collection.
* - Only use this method if you know what you are doing!
* @deprecated Use {@link writeRaw} instead

@@ -289,5 +303,6 @@ * @param value - The value to write

/**
* Automatically add a value to the JSON file
* Append a value to the collection
* - Only works if autoKey is enabled server-side
* @param value - The value (without methods) to add
* @returns The generated ID of the added element
* @returns The generated key of the added element
*/

@@ -297,5 +312,6 @@ public add(value: Addable<T>): Promise<string>;

/**
* Automatically add multiple values to the JSON file
* Append multiple values to the collection
* - Only works if autoKey is enabled server-side
* @param values - The values (without methods) to add
* @returns The generated IDs of the added elements
* @returns The generated keys of the added elements
*/

@@ -305,51 +321,51 @@ public addBulk(values: Addable<T>[]): Promise<string[]>;

/**
* Remove an element from the collection by its ID
* @param id - The ID of the element you want to remove
* Remove an element from the collection by its key
* @param key - The key of the element you want to remove
* @returns Write confirmation
*/
public remove(id: string | number): Promise<WriteConfirmation>;
public remove(key: string | number): Promise<WriteConfirmation>;
/**
* Remove multiple elements from the collection by their IDs
* @param ids - The IDs of the elements you want to remove
* Remove multiple elements from the collection by their keys
* @param keys - The keys of the elements you want to remove
* @returns Write confirmation
*/
public removeBulk(ids: string[] | number[]): Promise<WriteConfirmation>;
public removeBulk(keys: string[] | number[]): Promise<WriteConfirmation>;
/**
* Set a value in the collection by ID
* @param id - The ID of the element you want to edit
* @param value - The value (without methods) you want to edit
* Set a value in the collection by its key
* @param key - The key of the element you want to set
* @param value - The value (without methods) you want to set
* @returns Write confirmation
*/
public set(id: string | number, value: Settable<T>): Promise<WriteConfirmation>;
public set(key: string | number, value: Settable<T>): Promise<WriteConfirmation>;
/**
* Set multiple values in the collection by their IDs
* @param ids - The IDs of the elements you want to edit
* @param values - The values (without methods) you want to edit
* Set multiple values in the collection by their keys
* @param keys - The keys of the elements you want to set
* @param values - The values (without methods) you want to set
* @returns Write confirmation
*/
public setBulk(ids: string[] | number[], values: Settable<T>[]): Promise<WriteConfirmation>;
public setBulk(keys: string[] | number[], values: Settable<T>[]): Promise<WriteConfirmation>;
/**
* Edit one field of the collection
* @param edit - The edit object
* Edit an element's field in the collection
* @param option - The edit object
* @returns Edit confirmation
*/
public editField(edit: EditField<RemoveMethods<T>>): Promise<{ success: boolean }>;
public editField(option: EditFieldOption<RemoveMethods<T>>): Promise<WriteConfirmation>;
/**
* Change one field from multiple elements of the collection
* @param edits - The edit objects
* Edit multiple elements' fields in the collection
* @param options - The edit objects
* @returns Edit confirmation
*/
public editFieldBulk(edits: EditField<RemoveMethods<T>>[]): Promise<{ success: boolean[] }>;
public editFieldBulk(options: EditFieldOption<RemoveMethods<T>>[]): Promise<WriteConfirmation>;
}
/** Value for the id field when searching content */
/** Value for the ID field when searching content */
export const ID_FIELD: string;
/**
* Change the current Firestorm address
* Change or get the current Firestorm address
* @param value - The new Firestorm address

@@ -361,3 +377,3 @@ * @returns The stored Firestorm address

/**
* Change the current Firestorm token
* Change or get the current Firestorm token
* @param value - The new Firestorm write token

@@ -372,3 +388,3 @@ * @returns The stored Firestorm write token

* @param addMethods - Additional methods and data to add to the objects
* @returns The collection
* @returns The collection instance
*/

@@ -379,6 +395,7 @@ export function collection<T>(value: string, addMethods?: CollectionMethods<T>): Collection<T>;

* Create a temporary Firestorm collection with no methods
* @deprecated Use {@link collection} with no second argument instead
* @param table - The table name to get
* @returns The collection
* @returns The table instance
*/
export function table<T>(table: string): Promise<Collection<T>>;
export function table<T>(table: string): Collection<T>;

@@ -391,3 +408,3 @@ /**

* Get a file by its path
* @param path - The file path wanted
* @param path - The wanted file path
* @returns File contents

@@ -399,9 +416,9 @@ */

* Upload a file
* @param form - The form data with path, filename, and file
* @param form - Form data with path, filename, and file
* @returns Write confirmation
*/
upload(form: FormData): Promise<WriteConfirmation>;
upload(form: FormData | NodeFormData): Promise<WriteConfirmation>;
/**
* Deletes a file by path
* Delete a file by its path
* @param path - The file path to delete

@@ -408,0 +425,0 @@ * @returns Write confirmation

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc