fastkv 1.0.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,9 +6,9 @@
6
6
 
7
7
  FastKV is a key-value store which offers the following features:
8
8
 
9
- - Supports temporary in-memory storage 🕒
10
- - Supports persistent storage with JSON files 📁
11
- - Lightweight with no dependencies ⚡
9
+ - Supports temporary in-memory storage 🕒
10
+ - Supports persistent storage with JSON files 📁
11
+ - Lightweight with no dependencies ⚡
12
12
 
13
13
  ## Installation
14
14
 
@@ -20,6 +20,8 @@ npm install fastkv
20
20
  yarn add fastkv
21
21
  # or
22
22
  pnpm add fastkv
23
+ # or
24
+ bun add fastkv
23
25
  ```
24
26
 
25
27
  ## Example
@@ -35,7 +37,7 @@ const kv = new KV('./db.json'); // Save the data. Path resolves with process.cwd
35
37
  const cache = new KV('::memory::'); // Keep the data in the system's memory.
36
38
 
37
39
  // Set data
38
- kv.set('userSettings', { theme: 'dark' }); // -> KV
40
+ kv.set('userSettings', { theme: 'dark' });
39
41
 
40
42
  // Retreive data by a key
41
43
  kv.get('userSettings'); // -> { theme: 'dark' }
@@ -44,5 +46,5 @@ kv.get('userSettings'); // -> { theme: 'dark' }
44
46
  kv.all(); // -> [{ key: 'userSettings', value: { theme: 'dark' } }]
45
47
 
46
48
  // Clear the store
47
- kv.clear(); // -> KV
49
+ kv.clear();
48
50
  ```
package/dist/index.d.mts CHANGED
@@ -1,43 +1,31 @@
1
- type Entry<T> = {
1
+ type Entry<T = any> = {
2
+ __id: string;
2
3
  key: string;
3
4
  value: T;
4
5
  };
5
- /**
6
- * Represents a key-value store.
7
- * Initializing with `::memory::` uses in-memory storage.
8
- */
6
+ type DeepPartial<T> = {
7
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
8
+ };
9
9
  declare class KV<T = any> {
10
10
  #private;
11
- /**
12
- * Creates a new instance of the class.
13
- * @param location - Where the data will be stored.
14
- * Can be `::memory::` or a file location.
15
- */
16
- constructor(location?: string);
17
- /**
18
- * Sets a value to a key.
19
- * @param key - The name of the key.
20
- * @param value - The value to store.
21
- * @returns The value.
22
- */
23
- set(key: string, value: T): T;
24
- /**
25
- * Gets data by a key.
26
- * @param key - The existing key's name.
27
- * @returns The found data, or null.
28
- */
29
- get<U = T>(key: string): U | null;
30
- /**
31
- * Gets all data in the store.
32
- * @returns An array of Entry objects [{ key: string, value: T }].
33
- */
34
- all(): Entry<T>[];
35
- /**
36
- * Clears all data in the store.
37
- * @returns The KV instance.
38
- */
39
- 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>;
40
29
  }
41
- declare const _default: KV<any>;
42
30
 
43
- export { KV, _default as default };
31
+ export { KV, KV as default };
package/dist/index.d.ts CHANGED
@@ -1,43 +1,31 @@
1
- type Entry<T> = {
1
+ type Entry<T = any> = {
2
+ __id: string;
2
3
  key: string;
3
4
  value: T;
4
5
  };
5
- /**
6
- * Represents a key-value store.
7
- * Initializing with `::memory::` uses in-memory storage.
8
- */
6
+ type DeepPartial<T> = {
7
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
8
+ };
9
9
  declare class KV<T = any> {
10
10
  #private;
11
- /**
12
- * Creates a new instance of the class.
13
- * @param location - Where the data will be stored.
14
- * Can be `::memory::` or a file location.
15
- */
16
- constructor(location?: string);
17
- /**
18
- * Sets a value to a key.
19
- * @param key - The name of the key.
20
- * @param value - The value to store.
21
- * @returns The value.
22
- */
23
- set(key: string, value: T): T;
24
- /**
25
- * Gets data by a key.
26
- * @param key - The existing key's name.
27
- * @returns The found data, or null.
28
- */
29
- get<U = T>(key: string): U | null;
30
- /**
31
- * Gets all data in the store.
32
- * @returns An array of Entry objects [{ key: string, value: T }].
33
- */
34
- all(): Entry<T>[];
35
- /**
36
- * Clears all data in the store.
37
- * @returns The KV instance.
38
- */
39
- 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>;
40
29
  }
41
- declare const _default: KV<any>;
42
30
 
43
- export { KV, _default as default };
31
+ export { KV, KV as default };
package/dist/index.js CHANGED
@@ -1 +1,151 @@
1
- "use strict";var l=Object.create;var e=Object.defineProperty;var p=Object.getOwnPropertyDescriptor;var m=Object.getOwnPropertyNames;var y=Object.getPrototypeOf,f=Object.prototype.hasOwnProperty;var d=(r,t)=>{for(var i in t)e(r,i,{get:t[i],enumerable:!0})},c=(r,t,i,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of m(t))!f.call(r,s)&&s!==i&&e(r,s,{get:()=>t[s],enumerable:!(o=p(t,s))||o.enumerable});return r};var u=(r,t,i)=>(i=r!=null?l(y(r)):{},c(t||!r||!r.__esModule?e(i,"default",{value:r,enumerable:!0}):i,r)),g=r=>c(e({},"__esModule",{value:!0}),r);var b={};d(b,{KV:()=>n,default:()=>T});module.exports=g(b);var h=u(require("fs")),a=u(require("path")),n=class{#t;#r;constructor(t="::memory::"){this.#t=t,this.#r={},t!=="::memory::"&&(this.#t=a.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.entries(this.#r).map(([t,i])=>({key:t,value:i}))}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 i=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,r){return this.#r[t]=r,this.#i(),r}get(t){return this.#r[t]??null}all(){return Object.entries(this.#r).map(([t,r])=>({key:t,value:r}))}clear(){return this.#r={},this.#i(),this}},a=new i;export{i as KV,a 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": "1.0.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
  }