fastkv 0.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,8 +6,9 @@
6
6
 
7
7
  FastKV is a key-value store which offers the following features:
8
8
 
9
- - Persistent storage in JSON files 📁
10
- - Caching with in-memory storage 🕒
9
+ - Supports temporary in-memory storage 🕒
10
+ - Supports persistent storage with JSON files 📁
11
+ - Lightweight with no dependencies ⚡
11
12
 
12
13
  ## Installation
13
14
 
@@ -19,6 +20,8 @@ npm install fastkv
19
20
  yarn add fastkv
20
21
  # or
21
22
  pnpm add fastkv
23
+ # or
24
+ bun add fastkv
22
25
  ```
23
26
 
24
27
  ## Example
@@ -28,19 +31,20 @@ import { KV } from 'fastkv';
28
31
 
29
32
  // For TypeScript users, the KV supports generics:
30
33
  // const example = new KV<string>();
34
+ // Or: example.get<string>('my-key-name');
31
35
 
32
36
  const kv = new KV('./db.json'); // Save the data. Path resolves with process.cwd()
33
37
  const cache = new KV('::memory::'); // Keep the data in the system's memory.
34
38
 
35
39
  // Set data
36
- await kv.set('userSettings', { theme: 'dark' }); // => Promise<KV>
40
+ kv.set('userSettings', { theme: 'dark' });
37
41
 
38
42
  // Retreive data by a key
39
- await kv.get('userSettings'); // => Promise resolving to { theme: 'dark' }
43
+ kv.get('userSettings'); // -> { theme: 'dark' }
40
44
 
41
45
  // Retreive all data
42
- await kv.all(); // => Promise resolving an array: [{ key: 'userSettings', value: { theme: 'dark' } }]
46
+ kv.all(); // -> [{ key: 'userSettings', value: { theme: 'dark' } }]
43
47
 
44
48
  // Clear the store
45
- await kv.clear(); // => Promise<KV>
49
+ kv.clear();
46
50
  ```
package/dist/index.d.mts CHANGED
@@ -1,39 +1,31 @@
1
- /**
2
- * Represents a key-value store.
3
- * Initializing with `::memory::` uses in-memory storage.
4
- */
1
+ type Entry<T = any> = {
2
+ __id: string;
3
+ key: string;
4
+ value: T;
5
+ };
6
+ type DeepPartial<T> = {
7
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
8
+ };
5
9
  declare class KV<T = any> {
6
10
  #private;
7
- /**
8
- * Creates a new instance of the class.
9
- * @param location - Where the data will be stored.
10
- * Can be `::memory::` or a file location.
11
- */
12
- constructor(location?: string);
13
- /**
14
- * Sets a value to a key.
15
- * @param key - The name of the key.
16
- * @param value - The value to store.
17
- * @returns The value.
18
- */
19
- set(key: string, value: T): T;
20
- /**
21
- * Gets data by a key.
22
- * @param key - The existing key's name.
23
- * @returns The found data, or null.
24
- */
25
- get(key: string): T | null;
26
- /**
27
- * Gets all data in the store.
28
- * @returns An array of data.
29
- */
30
- all(): T[];
31
- /**
32
- * Clears all data in the store.
33
- * @returns The KV instance.
34
- */
35
- clear(): this;
11
+ path?: string;
12
+ constructor(options?: {
13
+ path?: string;
14
+ prettify?: boolean;
15
+ });
16
+ set(key: string, value: T): Promise<Entry<T>>;
17
+ upsert(options: {
18
+ where: DeepPartial<T> | string;
19
+ create?: DeepPartial<T>;
20
+ update?: DeepPartial<T>;
21
+ }): Promise<Entry<T>>;
22
+ find(options: {
23
+ in?: "key" | "value";
24
+ where: T | string | number | boolean;
25
+ }): Promise<Entry<T>[]>;
26
+ get(query: string): Promise<Entry<T> | T | null>;
27
+ all(): Promise<Entry<T>[]>;
28
+ clear(): Promise<void>;
36
29
  }
37
- declare const _default: KV<any>;
38
30
 
39
- export { KV, _default as default };
31
+ export { KV, KV as default };
package/dist/index.d.ts CHANGED
@@ -1,39 +1,31 @@
1
- /**
2
- * Represents a key-value store.
3
- * Initializing with `::memory::` uses in-memory storage.
4
- */
1
+ type Entry<T = any> = {
2
+ __id: string;
3
+ key: string;
4
+ value: T;
5
+ };
6
+ type DeepPartial<T> = {
7
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
8
+ };
5
9
  declare class KV<T = any> {
6
10
  #private;
7
- /**
8
- * Creates a new instance of the class.
9
- * @param location - Where the data will be stored.
10
- * Can be `::memory::` or a file location.
11
- */
12
- constructor(location?: string);
13
- /**
14
- * Sets a value to a key.
15
- * @param key - The name of the key.
16
- * @param value - The value to store.
17
- * @returns The value.
18
- */
19
- set(key: string, value: T): T;
20
- /**
21
- * Gets data by a key.
22
- * @param key - The existing key's name.
23
- * @returns The found data, or null.
24
- */
25
- get(key: string): T | null;
26
- /**
27
- * Gets all data in the store.
28
- * @returns An array of data.
29
- */
30
- all(): T[];
31
- /**
32
- * Clears all data in the store.
33
- * @returns The KV instance.
34
- */
35
- clear(): this;
11
+ path?: string;
12
+ constructor(options?: {
13
+ path?: string;
14
+ prettify?: boolean;
15
+ });
16
+ set(key: string, value: T): Promise<Entry<T>>;
17
+ upsert(options: {
18
+ where: DeepPartial<T> | string;
19
+ create?: DeepPartial<T>;
20
+ update?: DeepPartial<T>;
21
+ }): Promise<Entry<T>>;
22
+ find(options: {
23
+ in?: "key" | "value";
24
+ where: T | string | number | boolean;
25
+ }): Promise<Entry<T>[]>;
26
+ get(query: string): Promise<Entry<T> | T | null>;
27
+ all(): Promise<Entry<T>[]>;
28
+ clear(): Promise<void>;
36
29
  }
37
- declare const _default: KV<any>;
38
30
 
39
- export { KV, _default as default };
31
+ export { KV, KV as default };
package/dist/index.js CHANGED
@@ -1 +1,151 @@
1
- "use strict";var a=Object.create;var e=Object.defineProperty;var m=Object.getOwnPropertyDescriptor;var p=Object.getOwnPropertyNames;var f=Object.getPrototypeOf,d=Object.prototype.hasOwnProperty;var y=(r,t)=>{for(var i in t)e(r,i,{get:t[i],enumerable:!0})},o=(r,t,i,c)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of p(t))!d.call(r,s)&&s!==i&&e(r,s,{get:()=>t[s],enumerable:!(c=m(t,s))||c.enumerable});return r};var l=(r,t,i)=>(i=r!=null?a(f(r)):{},o(t||!r||!r.__esModule?e(i,"default",{value:r,enumerable:!0}):i,r)),g=r=>o(e({},"__esModule",{value:!0}),r);var b={};y(b,{KV:()=>n,default:()=>T});module.exports=g(b);var h=l(require("fs")),u=l(require("path")),n=class{#t;#r;constructor(t="::memory::"){this.#t=t,this.#r={},t!="::memory::"&&(this.#t=u.default.resolve(process.cwd(),this.#t),this.#r=this.#s())}#s(){if(this.#t==="::memory::")return{};try{let t=h.default.readFileSync(this.#t,"utf8");return JSON.parse(t)||{}}catch{return{}}}#i(){this.#t!=="::memory::"&&h.default.writeFileSync(this.#t,JSON.stringify(this.#r),"utf8")}set(t,i){return this.#r[t]=i,this.#i(),i}get(t){return this.#r[t]??null}all(){return Object.values(this.#r)}clear(){return this.#r={},this.#i(),this}},T=new n;0&&(module.exports={KV});
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ KV: () => KV,
34
+ default: () => index_default
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+ var import_node_crypto = require("crypto");
38
+ var import_node_path = __toESM(require("path"));
39
+ var import_promises = __toESM(require("fs/promises"));
40
+ var KV = class {
41
+ path;
42
+ #data = [];
43
+ #ready;
44
+ #prettify;
45
+ constructor(options) {
46
+ if (options?.prettify) {
47
+ this.#prettify = options?.prettify;
48
+ } else {
49
+ this.#prettify = false;
50
+ }
51
+ if (options?.path) {
52
+ this.path = import_node_path.default.resolve(process.cwd(), options.path);
53
+ this.#ready = this.#loadFromFile();
54
+ } else {
55
+ this.#ready = Promise.resolve();
56
+ }
57
+ }
58
+ async #loadFromFile() {
59
+ try {
60
+ const content = await import_promises.default.readFile(this.path, "utf8");
61
+ this.#data = JSON.parse(content);
62
+ } catch {
63
+ this.#data = [];
64
+ await this.#saveToFile();
65
+ }
66
+ }
67
+ async #saveToFile() {
68
+ if (!this.path) return;
69
+ await import_promises.default.writeFile(
70
+ this.path,
71
+ JSON.stringify(this.#data, null, this.#prettify ? 2 : 0),
72
+ "utf8"
73
+ );
74
+ }
75
+ #resolvePath(object, path2) {
76
+ return path2.split(".").reduce((acc, part) => acc ? acc[part] : void 0, object);
77
+ }
78
+ #matches(value, where) {
79
+ if (typeof value !== typeof where) return false;
80
+ if (typeof value === "object" && value !== null && where !== null) {
81
+ return Object.entries(where).every(([k, v]) => this.#matches(value[k], v));
82
+ }
83
+ return value === where;
84
+ }
85
+ async set(key, value) {
86
+ await this.#ready;
87
+ const entry = this.#data.find((entry2) => entry2.key === key);
88
+ if (entry) {
89
+ entry.value = value;
90
+ await this.#saveToFile();
91
+ return entry;
92
+ }
93
+ const newEntry = { __id: (0, import_node_crypto.randomUUID)(), key, value };
94
+ this.#data.push(newEntry);
95
+ await this.#saveToFile();
96
+ return newEntry;
97
+ }
98
+ async upsert(options) {
99
+ await this.#ready;
100
+ let entry;
101
+ if (typeof options.where === "string") {
102
+ entry = this.#data.find((entry2) => entry2.key === options.where);
103
+ } else {
104
+ entry = this.#data.find((entry2) => this.#matches(entry2.value, options.where));
105
+ }
106
+ if (entry) {
107
+ if (options.update) {
108
+ entry.value = { ...entry.value, ...options.update };
109
+ }
110
+ } else {
111
+ const key = typeof options.where === "string" ? options.where : (0, import_node_crypto.randomUUID)();
112
+ entry = { __id: (0, import_node_crypto.randomUUID)(), key, value: { ...options.create ?? {} } };
113
+ this.#data.push(entry);
114
+ }
115
+ await this.#saveToFile();
116
+ return entry;
117
+ }
118
+ async find(options) {
119
+ await this.#ready;
120
+ const field = options.in ?? "value";
121
+ const where = options.where;
122
+ return this.#data.filter((entry) => {
123
+ if (field === "key") {
124
+ return typeof where === "string" && entry.key.includes(where);
125
+ }
126
+ return this.#matches(entry.value, where);
127
+ });
128
+ }
129
+ async get(query) {
130
+ await this.#ready;
131
+ const [baseKey, ...rest] = query.split(".");
132
+ const entry = this.#data.find((entry2) => entry2.key === baseKey);
133
+ if (!entry) return null;
134
+ if (rest.length === 0) return entry;
135
+ return this.#resolvePath({ value: entry.value }, rest.join(".")) ?? null;
136
+ }
137
+ async all() {
138
+ await this.#ready;
139
+ return [...this.#data];
140
+ }
141
+ async clear() {
142
+ await this.#ready;
143
+ this.#data = [];
144
+ await this.#saveToFile();
145
+ }
146
+ };
147
+ var index_default = KV;
148
+ // Annotate the CommonJS export names for ESM import in node:
149
+ 0 && (module.exports = {
150
+ KV
151
+ });
package/dist/index.mjs CHANGED
@@ -1 +1,116 @@
1
- import s from"node:fs";import e from"node:path";var r=class{#t;#r;constructor(t="::memory::"){this.#t=t,this.#r={},t!="::memory::"&&(this.#t=e.resolve(process.cwd(),this.#t),this.#r=this.#s())}#s(){if(this.#t==="::memory::")return{};try{let t=s.readFileSync(this.#t,"utf8");return JSON.parse(t)||{}}catch{return{}}}#i(){this.#t!=="::memory::"&&s.writeFileSync(this.#t,JSON.stringify(this.#r),"utf8")}set(t,i){return this.#r[t]=i,this.#i(),i}get(t){return this.#r[t]??null}all(){return Object.values(this.#r)}clear(){return this.#r={},this.#i(),this}},u=new r;export{r as KV,u as default};
1
+ // src/index.ts
2
+ import { randomUUID } from "crypto";
3
+ import path from "path";
4
+ import fs from "fs/promises";
5
+ var KV = class {
6
+ path;
7
+ #data = [];
8
+ #ready;
9
+ #prettify;
10
+ constructor(options) {
11
+ if (options?.prettify) {
12
+ this.#prettify = options?.prettify;
13
+ } else {
14
+ this.#prettify = false;
15
+ }
16
+ if (options?.path) {
17
+ this.path = path.resolve(process.cwd(), options.path);
18
+ this.#ready = this.#loadFromFile();
19
+ } else {
20
+ this.#ready = Promise.resolve();
21
+ }
22
+ }
23
+ async #loadFromFile() {
24
+ try {
25
+ const content = await fs.readFile(this.path, "utf8");
26
+ this.#data = JSON.parse(content);
27
+ } catch {
28
+ this.#data = [];
29
+ await this.#saveToFile();
30
+ }
31
+ }
32
+ async #saveToFile() {
33
+ if (!this.path) return;
34
+ await fs.writeFile(
35
+ this.path,
36
+ JSON.stringify(this.#data, null, this.#prettify ? 2 : 0),
37
+ "utf8"
38
+ );
39
+ }
40
+ #resolvePath(object, path2) {
41
+ return path2.split(".").reduce((acc, part) => acc ? acc[part] : void 0, object);
42
+ }
43
+ #matches(value, where) {
44
+ if (typeof value !== typeof where) return false;
45
+ if (typeof value === "object" && value !== null && where !== null) {
46
+ return Object.entries(where).every(([k, v]) => this.#matches(value[k], v));
47
+ }
48
+ return value === where;
49
+ }
50
+ async set(key, value) {
51
+ await this.#ready;
52
+ const entry = this.#data.find((entry2) => entry2.key === key);
53
+ if (entry) {
54
+ entry.value = value;
55
+ await this.#saveToFile();
56
+ return entry;
57
+ }
58
+ const newEntry = { __id: randomUUID(), key, value };
59
+ this.#data.push(newEntry);
60
+ await this.#saveToFile();
61
+ return newEntry;
62
+ }
63
+ async upsert(options) {
64
+ await this.#ready;
65
+ let entry;
66
+ if (typeof options.where === "string") {
67
+ entry = this.#data.find((entry2) => entry2.key === options.where);
68
+ } else {
69
+ entry = this.#data.find((entry2) => this.#matches(entry2.value, options.where));
70
+ }
71
+ if (entry) {
72
+ if (options.update) {
73
+ entry.value = { ...entry.value, ...options.update };
74
+ }
75
+ } else {
76
+ const key = typeof options.where === "string" ? options.where : randomUUID();
77
+ entry = { __id: randomUUID(), key, value: { ...options.create ?? {} } };
78
+ this.#data.push(entry);
79
+ }
80
+ await this.#saveToFile();
81
+ return entry;
82
+ }
83
+ async find(options) {
84
+ await this.#ready;
85
+ const field = options.in ?? "value";
86
+ const where = options.where;
87
+ return this.#data.filter((entry) => {
88
+ if (field === "key") {
89
+ return typeof where === "string" && entry.key.includes(where);
90
+ }
91
+ return this.#matches(entry.value, where);
92
+ });
93
+ }
94
+ async get(query) {
95
+ await this.#ready;
96
+ const [baseKey, ...rest] = query.split(".");
97
+ const entry = this.#data.find((entry2) => entry2.key === baseKey);
98
+ if (!entry) return null;
99
+ if (rest.length === 0) return entry;
100
+ return this.#resolvePath({ value: entry.value }, rest.join(".")) ?? null;
101
+ }
102
+ async all() {
103
+ await this.#ready;
104
+ return [...this.#data];
105
+ }
106
+ async clear() {
107
+ await this.#ready;
108
+ this.#data = [];
109
+ await this.#saveToFile();
110
+ }
111
+ };
112
+ var index_default = KV;
113
+ export {
114
+ KV,
115
+ index_default as default
116
+ };
package/package.json CHANGED
@@ -1,11 +1,18 @@
1
1
  {
2
2
  "name": "fastkv",
3
- "version": "0.2.0",
3
+ "version": "2.0.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
+ "files": [
8
+ "dist",
9
+ "package.json",
10
+ "README.md"
11
+ ],
7
12
  "description": "A key-value store, helpful for caches in apps.",
8
- "main": "dist/index.js",
13
+ "main": "./dist/index.js",
14
+ "module": "./dist/index.mjs",
15
+ "types": "./dist/index.d.ts",
9
16
  "repository": {
10
17
  "url": "https://github.com/m1-dev/fastkv"
11
18
  },
@@ -14,16 +21,29 @@
14
21
  "database",
15
22
  "json"
16
23
  ],
24
+ "prettier": {
25
+ "tabWidth": 4,
26
+ "printWidth": 95,
27
+ "arrowParens": "always",
28
+ "trailingComma": "es5",
29
+ "singleQuote": false,
30
+ "jsxSingleQuote": false,
31
+ "useTabs": false,
32
+ "semi": true
33
+ },
17
34
  "license": "MIT",
18
35
  "devDependencies": {
19
- "@types/node": "^20.10.5",
20
- "@underctrl/tsconfig": "^0.0.1",
36
+ "@types/node": "^20.11.16",
37
+ "prettier": "^3.2.5",
21
38
  "tsup": "^8.0.1",
22
39
  "typescript": "^5.3.3"
23
40
  },
24
41
  "scripts": {
25
42
  "build": "tsup",
26
- "lint": "tsc --noEmit true",
27
- "prepublish": "pnpm run build"
43
+ "lint": "tsc --noEmit; prettier . --check --ignore-path=.prettierignore",
44
+ "prepublish": "pnpm run build",
45
+ "format": "prettier . --write --ignore-path=.prettierignore",
46
+ "deploy:fastkv:beta": "pnpm publish --no-git-checks --access public --tag beta",
47
+ "deploy:fastkv:stable": "pnpm publish --no-git-checks --access public"
28
48
  }
29
49
  }