@cloud-cli/env 1.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.
@@ -0,0 +1,13 @@
1
+ name: CICD
2
+ on:
3
+ push:
4
+ branches:
5
+ - main
6
+ jobs:
7
+ release:
8
+ uses: homebots/workflows/.github/workflows/node-build-release.yml@master
9
+ with:
10
+ branches: main
11
+ secrets:
12
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
13
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
package/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # env
2
+
3
+ Storage for app environment variables
@@ -0,0 +1,22 @@
1
+ export interface App {
2
+ app: string;
3
+ }
4
+ export interface KeyValue {
5
+ key: string;
6
+ value?: string;
7
+ }
8
+ export interface AppKeyValue extends App, KeyValue {
9
+ }
10
+ declare function reload(): void;
11
+ declare function show(options: App): Promise<unknown>;
12
+ declare function set(options: AppKeyValue): Promise<unknown>;
13
+ declare function remove(options: AppKeyValue): Promise<unknown>;
14
+ declare function get(options: Omit<AppKeyValue, 'value'>): Promise<unknown>;
15
+ declare const _default: {
16
+ reload: typeof reload;
17
+ show: typeof show;
18
+ get: typeof get;
19
+ set: typeof set;
20
+ delete: typeof remove;
21
+ };
22
+ export default _default;
package/dist/index.js ADDED
@@ -0,0 +1,104 @@
1
+ import sqlite from 'sqlite3';
2
+ import { join } from 'path';
3
+ const appNotSpecifiedError = new Error('App not specified');
4
+ const keyNotSpecifiedError = new Error('Key not specified');
5
+ let db;
6
+ const columns = {
7
+ App: 'app',
8
+ Key: 'key',
9
+ Value: 'value',
10
+ };
11
+ function close() {
12
+ try {
13
+ if (db) {
14
+ db.close();
15
+ }
16
+ }
17
+ catch { }
18
+ ;
19
+ }
20
+ function reload() {
21
+ close();
22
+ const inMemory = !!process.env.IN_MEMORY_DB;
23
+ if (inMemory) {
24
+ sqlite.verbose();
25
+ }
26
+ const databasePath = inMemory ? ':memory:' : join(process.cwd(), 'env.db');
27
+ db = new sqlite.Database(databasePath);
28
+ db.run(`CREATE TABLE IF NOT EXISTS env (
29
+ ${columns.App} TEXT,
30
+ ${columns.Key} TEXT,
31
+ ${columns.Value} TEXT,
32
+ UNIQUE (${columns.App}, ${columns.Key})
33
+ )`);
34
+ }
35
+ async function show(options) {
36
+ return new Promise((resolve, reject) => {
37
+ const { app } = options;
38
+ if (!app) {
39
+ return reject(appNotSpecifiedError);
40
+ }
41
+ db
42
+ .prepare(`
43
+ SELECT
44
+ ${columns.App},
45
+ ${columns.Key},
46
+ ${columns.Value}
47
+ FROM env WHERE
48
+ ${columns.App} = ?`)
49
+ .all([app], (error, rows) => error ? reject(error) : resolve(rows));
50
+ });
51
+ }
52
+ async function set(options) {
53
+ const { app, key, value } = options;
54
+ if (!app) {
55
+ return Promise.reject(appNotSpecifiedError);
56
+ }
57
+ if (!key) {
58
+ return Promise.reject(keyNotSpecifiedError);
59
+ }
60
+ return query('REPLACE INTO env VALUES (?, ?, ?)', [app, key, value]);
61
+ }
62
+ async function remove(options) {
63
+ const { app, key } = options;
64
+ if (!app) {
65
+ return Promise.reject(appNotSpecifiedError);
66
+ }
67
+ if (!key) {
68
+ return Promise.reject(keyNotSpecifiedError);
69
+ }
70
+ return query(`DELETE FROM env WHERE ${columns.App} = ? AND ${columns.Key} = ?`, [app, key]);
71
+ }
72
+ async function get(options) {
73
+ return new Promise((resolve, reject) => {
74
+ const { app, key } = options;
75
+ if (!app) {
76
+ return reject(appNotSpecifiedError);
77
+ }
78
+ if (!key) {
79
+ return reject(keyNotSpecifiedError);
80
+ }
81
+ db.serialize(() => {
82
+ db
83
+ .prepare(`
84
+ SELECT
85
+ ${columns.App},
86
+ ${columns.Key},
87
+ ${columns.Value}
88
+ FROM env WHERE
89
+ ${columns.App} = ? AND ${columns.Key} = ?`)
90
+ .all([app, key], (error, rows) => (error ? reject(error) : resolve(rows[0])));
91
+ });
92
+ });
93
+ }
94
+ async function query(statement, inputs) {
95
+ return new Promise((resolve, reject) => {
96
+ db.serialize(() => {
97
+ db
98
+ .prepare(statement)
99
+ .run(inputs)
100
+ .finalize((error) => (error ? reject(error) : resolve(null)));
101
+ });
102
+ });
103
+ }
104
+ export default { reload, show, get, set, delete: remove };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@cloud-cli/env",
3
+ "version": "1.0.0",
4
+ "description": "Storage for app environment variables",
5
+ "main": "dist/index.js",
6
+ "type": "module",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "test": "jest",
10
+ "tdd": "jest --watchAll",
11
+ "build": "rm -rf ./dist/* && tsc --project ./tsconfig.json"
12
+ },
13
+ "keywords": [
14
+ "cloudy"
15
+ ],
16
+ "license": "MIT",
17
+ "devDependencies": {
18
+ "@babel/core": "^7.21.0",
19
+ "@babel/preset-env": "^7.20.2",
20
+ "@babel/preset-typescript": "^7.21.0",
21
+ "@types/jest": "^27.0.1",
22
+ "@types/node": "^14.18.21",
23
+ "babel-jest": "^29.4.3",
24
+ "jest": "^29.4.3",
25
+ "nodemon": "^2.0.16",
26
+ "ts-jest": "^29.0.5",
27
+ "ts-jest-resolver": "^2.0.0",
28
+ "ts-node": "^9.1.1",
29
+ "typescript": "^4.8.2"
30
+ },
31
+ "repository": {
32
+ "type": "git",
33
+ "url": "https://github.com/cloud-cli/env.git"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "dependencies": {
39
+ "sqlite3": "^5.1.4"
40
+ }
41
+ }
@@ -0,0 +1,28 @@
1
+ import env from './index';
2
+
3
+ beforeAll(() => {
4
+ process.env.IN_MEMORY_DB = true;
5
+ });
6
+
7
+ const app = { app: 'test' };
8
+ const envVariable = { app: 'test', key: 'key', value: 'ok' };
9
+
10
+ describe('env', () => {
11
+ it('should store variables for an app', async () => {
12
+ env.reload();
13
+ await expect(env.set({ app: '', key: '' })).rejects.toEqual(new Error('App not specified'));
14
+ await expect(env.set({ app: 'test', key: '' })).rejects.toEqual(new Error('Key not specified'));
15
+ await expect(env.get({ app: 'test', key: 'key' })).resolves.toEqual(undefined);
16
+
17
+ // upsert
18
+ await expect(env.set(envVariable)).resolves.toBeNull();
19
+ await expect(env.set(envVariable)).resolves.toBeNull();
20
+
21
+ await expect(env.get({ app: 'test', key: 'key' })).resolves.toEqual(envVariable);
22
+
23
+ await expect(env.show(app)).resolves.toEqual([envVariable]);
24
+
25
+ await expect(env.delete(envVariable)).resolves.toBeNull();
26
+ await expect(env.show(app)).resolves.toEqual([]);
27
+ });
28
+ });
package/src/index.ts ADDED
@@ -0,0 +1,140 @@
1
+ import sqlite from 'sqlite3';
2
+ import { join } from 'path';
3
+
4
+ const appNotSpecifiedError = new Error('App not specified');
5
+ const keyNotSpecifiedError = new Error('Key not specified');
6
+ let db: sqlite.Database;
7
+
8
+ const columns = {
9
+ App: 'app',
10
+ Key: 'key',
11
+ Value: 'value',
12
+ };
13
+
14
+ export interface App {
15
+ app: string;
16
+ }
17
+
18
+ export interface KeyValue {
19
+ key: string;
20
+ value?: string;
21
+ }
22
+
23
+ export interface AppKeyValue extends App, KeyValue { }
24
+
25
+ function close() {
26
+ try {
27
+ if (db) {
28
+ db.close();
29
+ }
30
+ } catch { };
31
+ }
32
+
33
+ function reload() {
34
+ close();
35
+
36
+ const inMemory = !!process.env.IN_MEMORY_DB;
37
+
38
+ if (inMemory) {
39
+ sqlite.verbose();
40
+ }
41
+
42
+ const databasePath = inMemory ? ':memory:' : join(process.cwd(), 'env.db');
43
+
44
+ db = new sqlite.Database(databasePath);
45
+ db.run(`CREATE TABLE IF NOT EXISTS env (
46
+ ${columns.App} TEXT,
47
+ ${columns.Key} TEXT,
48
+ ${columns.Value} TEXT,
49
+ UNIQUE (${columns.App}, ${columns.Key})
50
+ )`);
51
+ }
52
+
53
+ async function show(options: App) {
54
+ return new Promise((resolve, reject) => {
55
+ const { app } = options;
56
+
57
+ if (!app) {
58
+ return reject(appNotSpecifiedError);
59
+ }
60
+
61
+ db
62
+ .prepare(`
63
+ SELECT
64
+ ${columns.App},
65
+ ${columns.Key},
66
+ ${columns.Value}
67
+ FROM env WHERE
68
+ ${columns.App} = ?`
69
+ )
70
+ .all([app], (error, rows) => error ? reject(error) : resolve(rows));
71
+ });
72
+ }
73
+
74
+ async function set(options: AppKeyValue) {
75
+ const { app, key, value } = options;
76
+
77
+ if (!app) {
78
+ return Promise.reject(appNotSpecifiedError);
79
+ }
80
+
81
+ if (!key) {
82
+ return Promise.reject(keyNotSpecifiedError);
83
+ }
84
+
85
+ return query('REPLACE INTO env VALUES (?, ?, ?)', [app, key, value]);
86
+ }
87
+
88
+ async function remove(options: AppKeyValue) {
89
+ const { app, key } = options;
90
+
91
+ if (!app) {
92
+ return Promise.reject(appNotSpecifiedError);
93
+ }
94
+
95
+ if (!key) {
96
+ return Promise.reject(keyNotSpecifiedError);
97
+ }
98
+
99
+ return query(`DELETE FROM env WHERE ${columns.App} = ? AND ${columns.Key} = ?`, [app, key])
100
+ }
101
+
102
+ async function get(options: Omit<AppKeyValue, 'value'>) {
103
+ return new Promise((resolve, reject) => {
104
+ const { app, key } = options;
105
+
106
+ if (!app) {
107
+ return reject(appNotSpecifiedError);
108
+ }
109
+
110
+ if (!key) {
111
+ return reject(keyNotSpecifiedError);
112
+ }
113
+
114
+ db.serialize(() => {
115
+ db
116
+ .prepare(`
117
+ SELECT
118
+ ${columns.App},
119
+ ${columns.Key},
120
+ ${columns.Value}
121
+ FROM env WHERE
122
+ ${columns.App} = ? AND ${columns.Key} = ?`
123
+ )
124
+ .all([app, key], (error, rows) => (error ? reject(error) : resolve(rows[0])));
125
+ });
126
+ });
127
+ }
128
+
129
+ async function query(statement, inputs) {
130
+ return new Promise((resolve, reject) => {
131
+ db.serialize(() => {
132
+ db
133
+ .prepare(statement)
134
+ .run(inputs)
135
+ .finalize((error) => (error ? reject(error) : resolve(null)));
136
+ });
137
+ })
138
+ }
139
+
140
+ export default { reload, show, get, set, delete: remove };
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "moduleResolution": "node",
4
+ "module": "esnext",
5
+ "target": "es2020",
6
+ "lib": ["ESNext"],
7
+ "esModuleInterop": true,
8
+ "noUnusedLocals": true,
9
+ "noUnusedParameters": true,
10
+ "outDir": "./dist",
11
+ "declaration": true
12
+ },
13
+ "include": ["./src/**/*.ts"]
14
+ }