uranio 0.1.19 → 0.1.21

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,328 @@
1
+ /**
2
+ *
3
+ * JS Object to SQL expanded query converter module
4
+ *
5
+ * @packageDocumentation
6
+ *
7
+ */
8
+
9
+ import * as t from '../types/index';
10
+
11
+ export function compose_select<S extends t.atom>({
12
+ projection = ['*'],
13
+ table,
14
+ where,
15
+ order,
16
+ limit,
17
+ }: {
18
+ projection?: string[];
19
+ table: string;
20
+ where?: t.Where<S>;
21
+ order?: t.OrderBy;
22
+ limit?: string;
23
+ }) {
24
+ let query_string = 'SELECT';
25
+ query_string += _resolve_projection(projection);
26
+ query_string += ` FROM \`${table}\``;
27
+ if(where && typeof where === 'object' && Object.entries(where).length > 0){
28
+ query_string += ` WHERE ` + _resolve_where<S>(where);
29
+ }
30
+ query_string += _resolve_order(order);
31
+ query_string += _resolve_limit(limit);
32
+ return query_string;
33
+ }
34
+
35
+ export function compose_update<S extends t.atom>({
36
+ table,
37
+ update,
38
+ where,
39
+ }: {
40
+ table: string;
41
+ update: Partial<S>;
42
+ where?: t.Where<S>;
43
+ }) {
44
+ let query_string = 'UPDATE';
45
+ query_string += ` \`${table}\``;
46
+ query_string += ` SET`;
47
+ query_string += _resolve_update(update);
48
+ query_string += ` WHERE ` + _resolve_where<S>(where);
49
+ return query_string;
50
+ }
51
+
52
+ export function compose_delete<S extends t.atom>({
53
+ table,
54
+ where,
55
+ }: {
56
+ table: string;
57
+ where?: t.Where<S>;
58
+ }) {
59
+ let query_string = 'DELETE';
60
+ query_string += ` FROM \`${table}\``;
61
+ query_string += ` WHERE ` + _resolve_where<S>(where);
62
+ return query_string;
63
+ }
64
+
65
+ export function compose_insert<S extends t.atom>({
66
+ table,
67
+ columns,
68
+ records,
69
+ }: {
70
+ table: string;
71
+ columns: (keyof S)[];
72
+ records: Partial<S>[];
73
+ }) {
74
+ let query_string = 'INSERT INTO';
75
+ query_string += ` \`${table}\``;
76
+ query_string += _resolve_columns(columns);
77
+ query_string += _resolve_records(columns, records);
78
+ return query_string;
79
+ }
80
+
81
+ function _resolve_where<S extends t.atom>(where?: t.Where<S>): string {
82
+ if (
83
+ !where ||
84
+ typeof where !== 'object' ||
85
+ Object.entries(where).length === 0
86
+ ) {
87
+ return '';
88
+ }
89
+ const where_parts: string[] = [];
90
+ // console.log(`t.Where param: ${JSON.stringify(where)}`);
91
+ for (const [key, value] of Object.entries(where)) {
92
+ // console.log(`Key: ${key}, Value: ${JSON.stringify(value)}`);
93
+ if (t.root_operators.includes(key as t.RootOperator)) {
94
+ // console.log(`Key is in root operators`);
95
+ if (!Array.isArray(value)) {
96
+ throw new Error(
97
+ `Invalid t.Where. Root operator '${key}' must have an Array as value`
98
+ );
99
+ }
100
+ const queries = value.map((el) => _resolve_where(el));
101
+ // console.log(`Queries: ${queries}`);
102
+ switch (key) {
103
+ case '$and': {
104
+ const joined_queries = queries.join(' AND ');
105
+ where_parts.push(`(${joined_queries})`);
106
+ break;
107
+ }
108
+ case '$or': {
109
+ const joined_queries = queries.join(' OR ');
110
+ where_parts.push(`(${joined_queries})`);
111
+ break;
112
+ }
113
+ case '$nor': {
114
+ const joined_queries = queries.join(' NOR ');
115
+ where_parts.push(`(${joined_queries})`);
116
+ break;
117
+ }
118
+ default: {
119
+ throw new Error(`Invalid root operator`);
120
+ }
121
+ }
122
+ // console.log(`t.Where parts: ${where_parts}`);
123
+ const final_where = where_parts.join(` AND `);
124
+ return final_where;
125
+ }
126
+ // if (filter_operators.includes(key as FilterOperator)) {
127
+ // throw new Error(
128
+ // `Invalid key. Key cannot be equal to an operator. Using '${key}'`
129
+ // );
130
+ // }
131
+ if(value instanceof RegExp){
132
+ let query_string = `\`${key}\``;
133
+ query_string += ` REGEXP ` + _format_value(value);
134
+ where_parts.push(query_string);
135
+ continue;
136
+ }
137
+ if (value && typeof value === 'object') {
138
+ // console.log(`Type of value is object --> using filter operator...`);
139
+ if (Object.entries(value).length === 0) {
140
+ throw new Error(`Cannot compare to empty object value`);
141
+ }
142
+ let query_string = '';
143
+ query_string += '' + _resolve_filter(key, value as t.Operator);
144
+ where_parts.push(query_string);
145
+ continue;
146
+ }
147
+ let query_string = `\`${key}\``;
148
+ query_string += ` = ` + _format_value(value);
149
+ where_parts.push(query_string);
150
+ }
151
+ const final_where = where_parts.map((el) => `${el}`).join(` AND `);
152
+ return final_where;
153
+ }
154
+
155
+ function _resolve_columns<S extends t.atom>(columns: (keyof S)[]): string {
156
+ return ' (' + columns.map((el) => '`' + String(el) + '`').join(', ') + ')';
157
+ }
158
+
159
+ function _resolve_records<S extends t.atom>(
160
+ columns: (keyof S)[],
161
+ records: Partial<S>[]
162
+ ): string {
163
+ const record_strings: string[] = [];
164
+ for (let record of records) {
165
+ const record_parts: string[] = [];
166
+ for (let column of columns) {
167
+ record_parts.push(_format_value(record[column]));
168
+ }
169
+ const joined_record = record_parts.join(', ');
170
+ record_strings.push(joined_record);
171
+ }
172
+ const full_records = record_strings.map((el) => '(' + el + ')').join(', ');
173
+ return ` VALUES ${full_records}`;
174
+ }
175
+
176
+ function _resolve_update<S extends t.atom>(update: Partial<S>): string {
177
+ const update_parts: string[] = [];
178
+ for (const [key, value] of Object.entries(update)) {
179
+ update_parts.push(`${key} = ${_format_value(value)}`);
180
+ }
181
+ return ' ' + update_parts.join(', ');
182
+ }
183
+
184
+ function _resolve_order(order?: t.OrderBy): string {
185
+ if (
186
+ !order ||
187
+ typeof order !== 'object' ||
188
+ Object.entries(order).length === 0
189
+ ) {
190
+ return '';
191
+ }
192
+ let query_string = '';
193
+ let order_strings: string[] = [];
194
+ for (const [column, direction] of Object.entries(order)) {
195
+ order_strings.push(`\`${column}\` ${direction.toUpperCase()}`);
196
+ }
197
+ const final_order = order_strings.join(', ');
198
+ query_string += ` ORDER BY ${final_order}`;
199
+ return query_string;
200
+ }
201
+
202
+ function _resolve_projection(projection: string[]): string {
203
+ let query_string = '';
204
+ const projection_string = projection.join(', ');
205
+ query_string += ` ${projection_string}`;
206
+ return query_string;
207
+ }
208
+
209
+ function _resolve_limit(limit?: string): string {
210
+ if (typeof limit !== 'string' || limit === '') {
211
+ return '';
212
+ }
213
+ return ` LIMIT ${limit}`;
214
+ }
215
+
216
+ function _resolve_filter(column: string, operator: t.Operator) {
217
+ let filter_strings: string[] = [];
218
+ for (let [key, value] of Object.entries(operator)) {
219
+ switch (key) {
220
+ case '$eq': {
221
+ filter_strings.push(`\`${column}\` = ${_format_value(value)}`);
222
+ break;
223
+ }
224
+ case '$gt': {
225
+ filter_strings.push(`\`${column}\` > ${_format_value(value)}`);
226
+ break;
227
+ }
228
+ case '$gte': {
229
+ filter_strings.push(`\`${column}\` >= ${_format_value(value)}`);
230
+ break;
231
+ }
232
+ case '$in': {
233
+ filter_strings.push(`\`${column}\` IN ${_format_value(value)}`);
234
+ break;
235
+ }
236
+ case '$lt': {
237
+ filter_strings.push(`\`${column}\` < ${_format_value(value)}`);
238
+ break;
239
+ }
240
+ case '$lte': {
241
+ filter_strings.push(`\`${column}\` <= ${_format_value(value)}`);
242
+ break;
243
+ }
244
+ case '$ne': {
245
+ filter_strings.push(`\`${column}\` <> ${_format_value(value)}`);
246
+ break;
247
+ }
248
+ case '$nin': {
249
+ filter_strings.push(`\`${column}\` NOT IN ${_format_value(value)}`);
250
+ break;
251
+ }
252
+ // Recursive
253
+ case '$not': {
254
+ filter_strings.push(
255
+ `\`${column}\` NOT (${_resolve_filter(column, value)})`
256
+ );
257
+ break;
258
+ }
259
+ case '$exists': {
260
+ if (value === true) {
261
+ filter_strings.push(`\`${column}\` IS NOT NULL`);
262
+ } else {
263
+ filter_strings.push(`\`${column}\` IS NULL`);
264
+ }
265
+ break;
266
+ }
267
+ default: {
268
+ throw new Error(`Invalid filter operator`);
269
+ }
270
+ }
271
+ }
272
+ const filter_string = filter_strings.join(` AND `);
273
+ return filter_string;
274
+ }
275
+
276
+ function _format_value(value: unknown): string {
277
+ let query_string = '';
278
+ if (Array.isArray(value)) {
279
+ return `(${value.toString()})`;
280
+ }
281
+ if(value instanceof RegExp){
282
+ return value.source;
283
+ }
284
+ switch (typeof value) {
285
+ case 'string': {
286
+ query_string = `"${value.replaceAll('"', '"')}"`;
287
+ return query_string;
288
+ }
289
+ default: {
290
+ return String(value);
291
+ }
292
+ }
293
+ }
294
+
295
+ // function _is_projection_type(type: unknown): boolean {
296
+ // if (projection_type.includes(type as ProjectionType)) {
297
+ // return true;
298
+ // }
299
+ // return false;
300
+ // }
301
+
302
+ // function _is_order_type(type: unknown): boolean {
303
+ // if (order_type.includes(type as ProjectionType)) {
304
+ // return true;
305
+ // }
306
+ // return false;
307
+ // }
308
+
309
+ // function _is_limit_type(type: unknown): boolean {
310
+ // if (limit_type.includes(type as ProjectionType)) {
311
+ // return true;
312
+ // }
313
+ // return false;
314
+ // }
315
+
316
+ // function _is_from_type(type: unknown): boolean {
317
+ // if (from_type.includes(type as FromType)) {
318
+ // return true;
319
+ // }
320
+ // return false;
321
+ // }
322
+
323
+ // function _assert_type(type: unknown): asserts type is SQLType {
324
+ // if (sql_type.includes(type as SQLType)) {
325
+ // return;
326
+ // }
327
+ // throw new Error(`Invalid SQL type`);
328
+ // }
@@ -0,0 +1,13 @@
1
+ /**
2
+ *
3
+ * SQL index module
4
+ *
5
+ * @packageDocumentation
6
+ *
7
+ */
8
+
9
+ import * as full from './full_query';
10
+ export {full};
11
+
12
+ import * as param from './param_query';
13
+ export {param};
@@ -0,0 +1,245 @@
1
+ /**
2
+ *
3
+ * JS Object to SQL parametrized query converter module
4
+ *
5
+ * @packageDocumentation
6
+ *
7
+ */
8
+
9
+ // import crypto from 'crypto';
10
+
11
+ import * as t from '../types/index';
12
+
13
+ import * as full from './full_query';
14
+
15
+ export function compose_select<S extends t.atom>({
16
+ projection = ['*'],
17
+ table,
18
+ where,
19
+ order,
20
+ limit,
21
+ }: {
22
+ projection?: string[];
23
+ table: string;
24
+ where?: t.Where<S>;
25
+ order?: t.OrderBy;
26
+ limit?: string;
27
+ }) {
28
+ // console.log(projection, table, JSON.stringify(where), order, limit);
29
+ const full_query = full.compose_select({
30
+ projection,
31
+ table,
32
+ where,
33
+ order,
34
+ limit,
35
+ });
36
+ const value_map = _generate_value_map_from_query(where);
37
+ const param_query = _replace_value_with_param(full_query, value_map);
38
+ const map = _transform_map_to_object(value_map);
39
+ return {query: param_query, map};
40
+ }
41
+
42
+ export function compose_update<S extends t.atom>({
43
+ table,
44
+ update,
45
+ where,
46
+ }: {
47
+ table: string;
48
+ update: Partial<S>;
49
+ where?: t.Where<S>;
50
+ }) {
51
+ const full_query = full.compose_update({
52
+ table,
53
+ update,
54
+ where,
55
+ });
56
+ const query_value_map = _generate_value_map_from_query(where);
57
+ const update_value_map = _generate_value_map_from_update(update);
58
+ for(const [k, v] of update_value_map){
59
+ query_value_map.set(k, v);
60
+ }
61
+ const param_query = _replace_value_with_param(full_query, query_value_map);
62
+ const map = _transform_map_to_object(query_value_map);
63
+ return {query: param_query, map};
64
+ }
65
+
66
+ export function compose_delete<S extends t.atom>({
67
+ table,
68
+ where,
69
+ }: {
70
+ table: string;
71
+ where?: t.Where<S>;
72
+ }) {
73
+ const full_query = full.compose_delete({
74
+ table,
75
+ where,
76
+ });
77
+ const query_value_map = _generate_value_map_from_query(where);
78
+ const param_query = _replace_value_with_param(full_query, query_value_map);
79
+ const map = _transform_map_to_object(query_value_map);
80
+ return {query: param_query, map};
81
+ }
82
+
83
+ export function compose_insert<S extends t.atom>({
84
+ table,
85
+ columns,
86
+ records,
87
+ }: {
88
+ table: string;
89
+ columns: (keyof S)[];
90
+ records: Partial<S>[];
91
+ }) {
92
+ const full_query = full.compose_insert({
93
+ table,
94
+ columns,
95
+ records
96
+ });
97
+ const splitted_query = full_query.split(' VALUES ');
98
+ const pre_query = splitted_query[0];
99
+ const param_query = `${pre_query} VALUES ?`;
100
+ return {query: param_query, query_records: records};
101
+ }
102
+
103
+ let id_count = 0;
104
+ function _generate_unique_consecutive_id():string{
105
+ const id = `x${String(id_count).padStart(4,'0')}`;
106
+ id_count++;
107
+ return id;
108
+ }
109
+
110
+ // function _generate_unique_id(): string {
111
+ // const now = Date.now();
112
+ // const hash = crypto.createHash('sha256');
113
+ // hash.update(now.toString());
114
+ // const digested = hash.digest('hex');
115
+ // return 'x' + digested.substring(0, 3) + _generate_random_string(4);
116
+ // }
117
+
118
+ function _generate_value_map_from_update<S extends t.atom>(
119
+ update: Partial<S>
120
+ ): Map<string, any> {
121
+ const value_map = new Map<string, any>();
122
+ for (const [_key, value] of Object.entries(update)) {
123
+ const id = _generate_unique_consecutive_id();
124
+ value_map.set(id, value);
125
+ }
126
+ return value_map;
127
+ }
128
+
129
+ function _generate_value_map_from_query<S extends t.atom>(
130
+ where?: t.Where<S>
131
+ ): Map<string, any> {
132
+ const value_map = new Map<string, any>();
133
+ if (!where) {
134
+ return value_map;
135
+ }
136
+ for (const [key, value] of Object.entries(where)) {
137
+ // key is a root operator: $and, $or, ...
138
+ if (t.root_operators.includes(key as t.RootOperator)) {
139
+ // console.log(`Key is in Root Operator: [${key}]`);
140
+ if (!Array.isArray(value)) {
141
+ throw new Error(
142
+ `Invalid t.Where. Root operator '${key}' must have an Array as value`
143
+ );
144
+ }
145
+ const sub_maps = value.map((el) => _generate_value_map_from_query(el));
146
+ for (const sub_map of sub_maps) {
147
+ for (const [_k, v] of sub_map) {
148
+ const id = _generate_unique_consecutive_id();
149
+ value_map.set(id, v);
150
+ }
151
+ }
152
+ continue;
153
+ }
154
+ // value is a RegExp
155
+ if(value instanceof RegExp){
156
+ const id = _generate_unique_consecutive_id();
157
+ value_map.set(id, value);
158
+ continue;
159
+ }
160
+ // value is an object therefor is a filter: {$gt: 2, $lt: 20}
161
+ if (value && typeof value === 'object') {
162
+ if (Object.entries(value).length === 0) {
163
+ throw new Error(`Cannot compare to empty object value`);
164
+ }
165
+ const filter_map = _generate_map_from_filter(
166
+ // key,
167
+ value as Record<t.FilterOperator, string>
168
+ );
169
+ for (const [_k, v] of filter_map) {
170
+ const id = _generate_unique_consecutive_id();
171
+ value_map.set(id, v);
172
+ }
173
+ continue;
174
+ }
175
+ // key value are a simple equality: {foo: 123}
176
+ const id = _generate_unique_consecutive_id();
177
+ value_map.set(id, value);
178
+ }
179
+ return value_map;
180
+ }
181
+
182
+ function _generate_map_from_filter(
183
+ // key: string,
184
+ filter: Record<t.FilterOperator, string>
185
+ ): Map<string, any> {
186
+ const filter_map = new Map<string, any>();
187
+ for (const [_operator, v] of Object.entries(filter)) {
188
+ const id = _generate_unique_consecutive_id();
189
+ filter_map.set(id, v);
190
+ }
191
+ return filter_map;
192
+ }
193
+
194
+ function _replace_value_with_param(
195
+ full_query: string,
196
+ value_map: Map<string, any>
197
+ ): string {
198
+ let final_query = full_query;
199
+ for (const [k, v] of value_map) {
200
+ if (Array.isArray(v)) {
201
+ for(const array_value of v){
202
+ const map = new Map();
203
+ map.set('a', array_value);
204
+ final_query = _replace_value_with_param(final_query, map);
205
+ }
206
+ continue;
207
+ }
208
+ if(v instanceof RegExp){
209
+ final_query = final_query.replaceAll(` ${v.source}`, ` :${k}`);
210
+ }
211
+ switch (typeof v) {
212
+ case 'string': {
213
+ final_query = final_query.replaceAll(`"${v}"`, `:${k}`);
214
+ break;
215
+ }
216
+ default: {
217
+ final_query = final_query.replaceAll(` ${v}`, ` :${k}`);
218
+ }
219
+ }
220
+ }
221
+ return final_query;
222
+ }
223
+
224
+ function _transform_map_to_object(map: Map<string, any>): Record<string, any>{
225
+ const object_map = Object.fromEntries(map);
226
+ for(const [k,v] of Object.entries(object_map)){
227
+ if(v instanceof RegExp){
228
+ object_map[k] = v.source;
229
+ }
230
+ }
231
+ return object_map;
232
+ }
233
+
234
+ // function _generate_random_string(length = 7): string {
235
+ // const characters =
236
+ // 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
237
+ // let result = '';
238
+
239
+ // for (let i = 0; i < length; i++) {
240
+ // const randomIndex = Math.floor(Math.random() * characters.length);
241
+ // result += characters.charAt(randomIndex);
242
+ // }
243
+
244
+ // return result;
245
+ // }
@@ -0,0 +1,28 @@
1
+ /**
2
+ *
3
+ * Atom client interface module
4
+ *
5
+ * @packageDocumentation
6
+ *
7
+ */
8
+
9
+ // import {atom, Where, Shape, OrderBy} from './index';
10
+
11
+ // export interface AtomClient<S extends atom> {
12
+ // get_atom(where: Where<S>): Promise<S | null>;
13
+ // get_atoms({
14
+ // where,
15
+ // order,
16
+ // limit
17
+ // }:{
18
+ // where: Where<S>,
19
+ // order?: OrderBy,
20
+ // limit?: string
21
+ // }): Promise<S[]>;
22
+ // put_atom(shape: Shape<S>): Promise<any>;
23
+ // put_atoms(atoms: Shape<S>[]): Promise<any>;
24
+ // update_atom(where: Where<S>, atom: Partial<S>): Promise<any>;
25
+ // update_atoms(where: Where<S>, atom: Partial<S>): Promise<any>;
26
+ // delete_atom(where: Where<S>): Promise<any>;
27
+ // delete_atoms(where: Where<S>): Promise<any>;
28
+ // }
@@ -0,0 +1,33 @@
1
+ /**
2
+ *
3
+ * Types module
4
+ *
5
+ * @packageDocumentation
6
+ *
7
+ */
8
+
9
+ export * from './where';
10
+
11
+ // export * from './atom';
12
+
13
+ export * from './sql';
14
+
15
+ export interface atom {}
16
+
17
+ export interface mysql_atom extends atom {}
18
+
19
+ export interface mongodb_atom extends atom {
20
+ _id: primary<string>;
21
+ }
22
+
23
+ export type unique<T> = T & {__uranio: 'unique'};
24
+
25
+ // export type Shape<A extends atom> = Omit<A, '_id'>;
26
+
27
+ export type primary<T> = T & {__uranio: 'primary'};
28
+
29
+ type PrimaryAttribute<A extends atom> = {
30
+ [K in keyof A]: A[K] extends {__uranio: 'primary'} ? K : never;
31
+ }[keyof A];
32
+
33
+ export type Shape<A extends atom> = Omit<A, PrimaryAttribute<A>>;
@@ -0,0 +1,34 @@
1
+ /**
2
+ *
3
+ * SQL type module
4
+ *
5
+ * @packageDocumentation
6
+ */
7
+
8
+ export const root_operators = ['$and', '$nor', '$or'] as const;
9
+
10
+ export const filter_operators = [
11
+ '$eq',
12
+ '$gt',
13
+ '$gte',
14
+ '$in',
15
+ '$lt',
16
+ '$lte',
17
+ '$ne',
18
+ '$nin',
19
+ '$not',
20
+ '$exists',
21
+ ] as const;
22
+
23
+ export type FilterOperator = (typeof filter_operators)[number];
24
+
25
+ export type RootOperator = (typeof root_operators)[number];
26
+
27
+ export type Operator = {
28
+ [k in FilterOperator]: any;
29
+ };
30
+
31
+ export type OrderBy = {
32
+ [k: string]: 'asc' | 'desc';
33
+ };
34
+