Socket
Socket
Sign inDemoInstall

orma

Package Overview
Dependencies
Maintainers
2
Versions
233
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

orma - npm Package Compare versions

Comparing version 1.0.41 to 1.0.43

build/query/json_sql.d.ts

5

build/helpers/helpers.d.ts

@@ -6,5 +6,7 @@ declare type type_string = 'Object' | 'Number' | 'Boolean' | 'String' | 'Null' | 'Array' | 'RegExp' | 'Function' | 'Undefined';

export declare const deep_set: (path_array: (string | number)[], value: any, obj: any) => void;
export declare const is_simple_object: (val: any) => boolean;
export declare const deep_get: (path_array: (string | number)[], obj: any, default_value?: any) => any;
/**
* Like a map, but works on deeply nested objects and arrays. Processor function is run from the most deeply nested keys to the least deeply nested ones
* Like a map, but works on deeply nested objects and arrays. Processor function runs on a depth first search, i.e.
* the processor will only be called on an element after it has been called on all the children.
* @param item can be an object or array

@@ -23,3 +25,4 @@ * @param processor this function will run on every object, array and primitive value found

export declare const deep_for_each: (item: any, processor: (value: any, path: (string | number)[]) => void, current_path?: any[]) => void;
export declare const get_lower_paths: (item: Record<any, any> | any[], path: (string | number)[]) => any[][];
export declare const clone: (obj: any) => any;
export {};

28

build/helpers/helpers.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.clone = exports.deep_for_each = exports.deep_map = exports.deep_get = exports.deep_set = exports.last = exports.drop = exports.type = void 0;
exports.clone = exports.get_lower_paths = exports.deep_for_each = exports.deep_map = exports.deep_get = exports.is_simple_object = exports.deep_set = exports.last = exports.drop = exports.type = void 0;
const type = (value) => {

@@ -29,3 +29,3 @@ return value === null

const is_array = Array.isArray(pointer);
const is_object = exports.type(pointer) === 'Object';
const is_object = exports.is_simple_object(pointer);
// if (is_array && type(path_el) !== 'Number') {

@@ -44,4 +44,3 @@ // throw new Error('Trying to path into an array without a number index')

}
const child_type = exports.type(pointer[path_el]);
const child_is_primitive = child_type !== 'Object' && child_type !== 'Array';
const child_is_primitive = !exports.is_simple_object(pointer[path_el]) && !Array.isArray(pointer[path_el]);
if (!contains_path_el || child_is_primitive) {

@@ -57,2 +56,5 @@ pointer[path_el] = next_el_default;

exports.deep_set = deep_set;
// from https://stackoverflow.com/a/16608074
const is_simple_object = val => (!!val) && (val.constructor === Object);
exports.is_simple_object = is_simple_object;
const deep_get = (path_array, obj, default_value = undefined) => {

@@ -62,3 +64,3 @@ let pointer = obj;

const is_array = Array.isArray(pointer);
const is_object = exports.type(pointer) === 'Object';
const is_object = exports.is_simple_object(pointer);
// if (is_array && type(path_el) !== 'Number') {

@@ -82,3 +84,4 @@ // throw new Error('Trying to path into an array without a number index')

/**
* Like a map, but works on deeply nested objects and arrays. Processor function is run from the most deeply nested keys to the least deeply nested ones
* Like a map, but works on deeply nested objects and arrays. Processor function runs on a depth first search, i.e.
* the processor will only be called on an element after it has been called on all the children.
* @param item can be an object or array

@@ -97,3 +100,3 @@ * @param processor this function will run on every object, array and primitive value found

}
else if (typeof item === 'object') {
else if (exports.is_simple_object(item)) {
mapped_item = Object.keys(item).reduce((acc, key) => {

@@ -123,4 +126,4 @@ const new_path = [...current_path, key];

const deep_for_each = (item, processor, current_path = []) => {
const is_object = typeof item === 'object' && !Array.isArray(item) && item !== null && !(item instanceof Date);
const is_array = typeof item === 'object' && Array.isArray(item);
const is_object = exports.is_simple_object(item);
const is_array = Array.isArray(item);
const is_primitive = !is_object && !is_array;

@@ -144,2 +147,9 @@ if (is_object) {

exports.deep_for_each = deep_for_each;
const get_lower_paths = (item, path) => {
const keys = Array.isArray(item)
? item.map((_, i) => [...path, i])
: Object.keys(item);
return keys.map(key => [...path, key]);
};
exports.get_lower_paths = get_lower_paths;
/*

@@ -146,0 +156,0 @@ From https://github.com/angus-c/just/blob/master/packages/collection-clone/index.js

export declare const orma_introspect: (db: string, fn: (s: string[]) => Promise<Record<string, unknown>[][]>) => Promise<import("./introspector/introspector").orma_schema>;
export declare const orma_query: (raw_query: any, orma_schema: any, query_function: (sql_string: string[]) => Promise<Record<string, unknown>[][]>) => Promise<{}>;
export declare const orma_query: (raw_query: any, orma_schema: any, query_function: (sql_string: string[]) => Promise<Record<string, unknown>[][]>, escaping_function: (value: any) => any) => Promise<{}>;
export declare const orma_mutate: (mutation: any, mutate_fn: import("./mutate/mutate").mutate_fn, escape_fn: import("./mutate/mutate").escape_fn, orma_schema: import("./introspector/introspector").orma_schema) => Promise<any>;

@@ -9,3 +9,3 @@ "use strict";

const toposort_1 = require("../helpers/toposort");
const query_1 = require("../query/query");
const json_sql_1 = require("../query/json_sql");
const orma_mutate = async (mutation, mutate_fn, escape_fn, orma_schema) => {

@@ -95,3 +95,3 @@ // [[{"operation":"create","paths":[...]]}],[{"operation":"create","paths":[...]}]]

.filter(key => !identifying_keys.includes(key))
.filter(key => typeof record[key] !== 'object' || record[key] instanceof Date)
.filter(key => !helpers_1.is_simple_object(record[key]) && !Array.isArray(record[key]))
.filter(key => !schema_helpers_1.is_reserved_keyword(key));

@@ -111,3 +111,3 @@ return {

command_json_escaped: update_ast_escaped,
command_sql: query_1.json_to_sql(update_ast_escaped)
command_sql: json_sql_1.json_to_sql(update_ast_escaped)
};

@@ -136,3 +136,3 @@ });

command_json_escaped: el,
command_sql: query_1.json_to_sql(el)
command_sql: json_sql_1.json_to_sql(el)
}));

@@ -151,3 +151,3 @@ };

const keys_to_insert = Object.keys(record)
.filter(key => typeof record[key] !== 'object' || record[key] instanceof Date)
.filter(key => !helpers_1.is_simple_object(record[key]) && !Array.isArray(record[key]))
.filter(key => !schema_helpers_1.is_reserved_keyword(key));

@@ -173,3 +173,3 @@ keys_to_insert.forEach(key => acc.add(key));

command_json_escaped,
command_sql: query_1.json_to_sql(command_json_escaped)
command_sql: json_sql_1.json_to_sql(command_json_escaped)
}

@@ -324,7 +324,4 @@ ];

var _a;
if (typeof value !== 'object' ||
Array.isArray(value) ||
path.length === 0 ||
value === null ||
value instanceof Date) {
if (!helpers_1.is_simple_object(value) ||
path.length === 0) {
return; // not pointing to a single row

@@ -331,0 +328,0 @@ }

@@ -1,47 +0,1 @@

declare type select_expr = {};
declare type unary_function = {
ascii: string | field;
} | {
bin: number | field;
} | {
bit_length: string | field;
} | {
char: (number | field)[];
} | {
character_length: string | field;
} | {
concat: (string | field)[];
} | {
concat_ws: (string | field)[];
};
declare type string_like = string | Date | number | {
field: string;
} | {
ascii: string_like;
} | {
bin: number_like;
} | {
char: number_like[];
} | {
character_length: string_like;
} | {
concat: string_like[];
} | {
concat_ws: string_like[];
} | {
elt: string_like[];
} | {};
declare type number_like = string | Date | number | {
field: string;
} | {};
declare type literal = string_literal | number | Date | {
hex: string;
} | {
bit: string;
} | boolean | null;
declare type string_literal = string | {
field: string;
};
declare type field = {
field: string;
};
export {};

@@ -1,4 +0,46 @@

// interface query {
// meta: query_meta
// [entity_name: string]: query | query_meta
// }
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const test_schema = {
products: {
id: {},
vendor_id: {
references: {
vendors: {
id: {}
}
}
}
},
vendors: {
id: {}
},
images: {
id: {},
product_id: {
references: {
products: {
id: {}
}
}
}
},
image_urls: {
image_id: {
references: {
images: {
id: {}
}
}
}
}
};
// test: can only put existing entities
var obj = {};
obj = { products: {}, vendors: {} }; // good
// @ts-expect-error
obj = { a_fake_entity: {} }; // bad
// test: can only nest connected entities
obj = { products: { vendors: {} } }; // good
obj = { products: { images: {} } }; // good
// @ ts-expect-error
obj = { products: { image_urls: {} } }; // bad
import { orma_schema } from '../introspector/introspector';
declare type expression = {
[commands: string]: expression | expression[];
} | primitive;
declare type primitive = string | number | Date | Array<any>;
export declare const json_to_sql: (expression: expression, path?: any[]) => any;
export declare const get_query_plan: (query: any) => string[][][];
export declare const is_subquery: (subquery: any) => boolean;
export declare const get_real_parent_name: (path: (string | number)[], query: any) => any;
export declare const get_real_entity_name: (path: (string | number)[], query: any) => any;
export declare const get_subquery_sql: (query: any, subquery_path: string[], previous_results: (string[] | Record<string, unknown>[])[][], orma_schema: orma_schema) => string;
/**
* transforms a query into a simplified json sql. This is still json, but can be parsed directly into sql (so no subqueries, $from is always there etc.)
*/
export declare const query_to_json_sql: (query: any, subquery_path: string[], previous_results: (string[] | Record<string, unknown>[])[][], orma_schema: orma_schema) => Record<string, any>;
export declare const select_to_json_sql: (query: any, subquery_path: string[], orma_schema: orma_schema) => any[];
export declare const where_to_json_sql: (query: any, subquery_path: string[], previous_results: (string[] | Record<string, unknown>[])[][], orma_schema: orma_schema) => any;
export declare const having_to_json_sql: (query: any, subquery_path: string[], orma_schema: orma_schema) => any;
export declare const combine_where_clauses: (where1: Record<string, unknown>, where2: Record<string, unknown>, connective: '$and' | '$or') => Record<string, unknown>;
/**
* The first argument to the $any_path macro is a list of connected entities, with the
* first one being connected to the currently scoped entity. The second argument is a where clause. This will be scoped to the last table in the first argument.
* This will then filter all the current entities, where there is at least one connected current_entity -> entity1 -> entity2 that matches the provided where clause
*
* @example
* {
* $where: {
* $any_path: [['entity1', 'entity2'], {
* ...where_clause_on_entity2
* }]
* }
* }
*
* @param where a where clause
* @param current_entity the root entity, since the path in the $any_path clause only starts from subsequent tables
* @param is_having if true, will use $having. Otherwise will use $where
* @returns a modified where clause
*/
export declare const convert_any_path_macro: (where: any, root_entity: string, is_having: boolean, orma_schema: orma_schema) => any;
export declare const orma_nester: (results: [string[], Record<string, unknown>[]][], orma_schema: orma_schema) => {};
export declare const orma_query: (raw_query: any, orma_schema: any, query_function: (sql_string: string[]) => Promise<Record<string, unknown>[][]>) => Promise<{}>;
export {};
export declare const orma_query: (raw_query: any, orma_schema: any, query_function: (sql_string: string[]) => Promise<Record<string, unknown>[][]>, escaping_function: (value: any) => any) => Promise<{}>;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.orma_query = exports.orma_nester = exports.convert_any_path_macro = exports.combine_where_clauses = exports.having_to_json_sql = exports.where_to_json_sql = exports.select_to_json_sql = exports.query_to_json_sql = exports.get_subquery_sql = exports.get_real_entity_name = exports.get_real_parent_name = exports.is_subquery = exports.get_query_plan = exports.json_to_sql = void 0;
exports.orma_query = exports.orma_nester = exports.having_to_json_sql = exports.get_real_entity_name = exports.get_real_parent_name = void 0;
const helpers_1 = require("../helpers/helpers");
const nester_1 = require("../helpers/nester");
const schema_helpers_1 = require("../helpers/schema_helpers");
// this is a simple parser function, whatever comes in the json object is placed as-is in the output sql string.
// more complicated rules (such as adding a 'from' clause, or adding group-by columns on select *) is handled while the query is still a json object
const json_to_sql = (expression, path = []) => {
// strings and other non-objects are returned as-is
const is_object = typeof expression === 'object' && !Array.isArray(expression);
if (!is_object) {
return expression;
}
const sorted_commands = Object.keys(expression).sort((command1, command2) => {
// unspecified commands go at the beginning
const i1 = command_order[command1] || -1;
const i2 = command_order[command2] || -1;
return i1 - i2;
});
const parsed_commands = sorted_commands
.map(command => {
if (expression[command] === undefined) {
return '';
}
const command_parser = sql_command_parsers[command];
if (!command_parser) {
throw new Error(`Cannot find command parser for ${command}.`);
}
const args = expression[command];
const parsed_args = Array.isArray(args)
? args.map((arg, i) => exports.json_to_sql(arg, [...path, command, i]))
: exports.json_to_sql(args, [...path, command]);
return command_parser(parsed_args, path);
})
.filter(el => el !== '');
return parsed_commands.join(' ');
};
exports.json_to_sql = json_to_sql;
const command_order_array = [
'$delete_from',
'$update',
'$set',
'$select',
'$from',
'$where',
'$group_by',
'$having',
'$order_by',
'$limit',
'$offset',
'$insert_into',
'$values'
];
const command_order = command_order_array.reduce((acc, val, i) => {
acc[val] = i;
return acc;
}, {});
/*
{
$eq: ['variant_id', {
$any: {
$select...
}
}]
}
*/
const sql_command_parsers = {
$select: args => `SELECT ${args.join(', ')}`,
$as: args => `(${args[0]}) AS ${args[1]}`,
$from: args => `FROM ${args}`,
$where: args => `WHERE ${args}`,
$having: args => `HAVING ${args}`,
$in: (args, path) => `${args[0]}${helpers_1.last(path) === '$not' ? ' NOT' : ''} IN (${args[1]})`,
$group_by: args => `GROUP BY ${args.join(', ')}`,
$order_by: args => `ORDER BY ${args.join(', ')}`,
$asc: args => `${args} ASC`,
$desc: args => `${args} DESC`,
$and: (args, path) => {
const res = `(${args.join(') AND (')})`;
return helpers_1.last(path) === '$not' ? `NOT (${res})` : res;
},
$or: (args, path) => {
const res = `(${args.join(') OR (')})`;
return helpers_1.last(path) === '$not' ? `NOT (${res})` : res;
},
$any: args => `ANY (${args})`,
$all: args => `ALL (${args})`,
$eq: (args, path) => args[1] === null
? `${args[0]}${helpers_1.last(path) === '$not' ? ' NOT' : ''} IS NULL`
: `${args[0]} ${helpers_1.last(path) === '$not' ? '!' : ''}= ${args[1]}`,
$gt: (args, path) => `${args[0]} ${helpers_1.last(path) === '$not' ? '<=' : '>'} ${args[1]}`,
$lt: (args, path) => `${args[0]} ${helpers_1.last(path) === '$not' ? '>=' : '<'} ${args[1]}`,
$gte: (args, path) => `${args[0]} ${helpers_1.last(path) === '$not' ? '<' : '>='} ${args[1]}`,
$lte: (args, path) => `${args[0]} ${helpers_1.last(path) === '$not' ? '>' : '<='} ${args[1]}`,
$exists: (args, path) => `${helpers_1.last(path) === '$not' ? 'NOT ' : ''}EXISTS (${args})`,
$limit: args => `LIMIT ${args}`,
$offset: args => `OFFSET ${args}`,
$like: (args, path) => {
const string_arg = args[1].toString();
const search_value = string_arg.replace(/^\'/, '').replace(/\'$/, ''); // get rid of quotes if they were put there by escape()
return `${args[0]}${helpers_1.last(path) === '$not' ? ' NOT' : ''} LIKE '%${search_value}%'`;
},
$not: args => args,
$sum: args => `SUM(${args})`,
// not: {
// in: args => `${args[0]} NOT IN (${args[1]})`,
// and: args => `NOT ((${args.join(') AND (')}))`,
// or: args => `NOT ((${args.join(') OR (')}))`,
// eq: args => args[1] === null ? `${args[0]} IS NOT NULL` : `${args[0]} != ${args[1]}`,
// gt: args => `${args[0]} <= ${args[1]}`,
// lt: args => `${args[0]} >= ${args[1]}`,
// gte: args => `${args[0]} < ${args[1]}`,
// lte: args => `${args[0]} > ${args[1]}`,
// exists: args => `NOT EXISTS (${args})`,
// }
$insert_into: ([table_name, [...columns]]) => `INSERT INTO ${table_name} (${columns.join(', ')})`,
$values: (values) => `VALUES ${values.map(inner_values => `(${inner_values.join(', ')})`).join(', ')}`,
$update: table_name => `UPDATE ${table_name}`,
$set: (items) => `SET ${items.map(([column, value]) => `${column} = ${value}`).join(', ')}`,
$delete_from: table_name => `DELETE FROM ${table_name}`
};
/*
[
[{
sql: 'SELECT'
}]
]
*/
const get_query_plan = (query) => {
const query_plan = [];
helpers_1.deep_for_each(query, (value, path) => {
if (typeof value === 'object') {
const query = value;
const has_filter = value.$where || value.$having;
const child_paths = Object.keys(value)
.map(child_key => {
if (exports.is_subquery(value[child_key])) {
return [...path, child_key];
}
})
.filter(el => el !== undefined);
if (child_paths.length === 0) {
return; // subquery with nothing else nested on
}
const is_root = path.length === 0; // this is to start off the query plan (cant append if there is nothing there)
// entities directly under the root need to ge after because all entitiesneed at least one ancestor which is already queried
// so we can search for those ancestor ids. This ensures that at least the root will have already been searched.
const is_root_child = path.length === 1;
// if this has a $where, or its the root/root child, then all the child paths go to a new tier. Otherwise, the child paths are appended on to the last tier
if (has_filter || is_root || is_root_child) {
query_plan.push(child_paths);
}
else {
helpers_1.last(query_plan).push(...child_paths);
}
}
});
return query_plan;
};
exports.get_query_plan = get_query_plan;
const is_subquery = (subquery) => {
if (typeof subquery !== 'object' || Array.isArray(subquery)) {
return false;
}
const subquery_keys = Object.keys(subquery);
return subquery_keys.some(key => !schema_helpers_1.is_reserved_keyword(key)) || subquery_keys.length === 0;
};
exports.is_subquery = is_subquery;
const json_sql_1 = require("./json_sql");
const any_path_macro_1 = require("./macros/any_path_macro");
const escaping_macros_1 = require("./macros/escaping_macros");
const nesting_macro_1 = require("./macros/nesting_macro");
const select_macro_1 = require("./macros/select_macro");
const query_plan_1 = require("./query_plan");
// This function will default to the from clause

@@ -187,88 +25,44 @@ const get_real_parent_name = (path, query) => {

exports.get_real_entity_name = get_real_entity_name;
const get_subquery_sql = (query, subquery_path, previous_results, orma_schema) => {
const json_sql = exports.query_to_json_sql(query, subquery_path, previous_results, orma_schema);
const sql = exports.json_to_sql(json_sql);
return sql;
};
exports.get_subquery_sql = get_subquery_sql;
/**
* transforms a query into a simplified json sql. This is still json, but can be parsed directly into sql (so no subqueries, $from is always there etc.)
*/
const query_to_json_sql = (query, subquery_path, previous_results, orma_schema) => {
var _a;
const subquery = helpers_1.deep_get(subquery_path, query);
const reserved_commands = Object.keys(subquery).filter(schema_helpers_1.is_reserved_keyword);
const reserved_json = reserved_commands.reduce((previous, key) => {
return Object.assign(Object.assign({}, previous), { [key]: subquery[key] });
}, {});
const $select = exports.select_to_json_sql(query, subquery_path, orma_schema);
const $from = (_a = subquery.$from) !== null && _a !== void 0 ? _a : helpers_1.last(subquery_path);
const $where = exports.where_to_json_sql(query, subquery_path, previous_results, orma_schema);
const $having = exports.having_to_json_sql(query, subquery_path, orma_schema);
const json_sql = Object.assign({}, reserved_json);
if ($select) {
json_sql.$select = $select;
}
if ($from) {
json_sql.$from = $from;
}
if ($where) {
json_sql.$where = $where;
}
if ($having) {
json_sql.$having = $having;
}
return json_sql;
};
exports.query_to_json_sql = query_to_json_sql;
const select_to_json_sql = (query, subquery_path, orma_schema) => {
const subquery = helpers_1.deep_get(subquery_path, query);
const entity_name = helpers_1.last(subquery_path);
const $select = Object.keys(subquery).flatMap(key => {
var _a;
if (schema_helpers_1.is_reserved_keyword(key)) {
return [];
}
if (subquery[key] === true) {
return key;
}
if (typeof subquery[key] === 'string') {
return { $as: [subquery[key], key] };
}
if (typeof subquery[key] === 'object' && !exports.is_subquery(subquery[key])) {
return { $as: [subquery[key], key] };
}
if (typeof subquery[key] === 'object' && exports.is_subquery(subquery[key])) {
const lower_subquery = subquery[key];
const lower_subquery_entity = (_a = lower_subquery.$from) !== null && _a !== void 0 ? _a : key;
const edge_to_lower_table = schema_helpers_1.get_direct_edge(entity_name, lower_subquery_entity, orma_schema);
return edge_to_lower_table.from_field;
}
return []; // subqueries are not handled here
});
if (subquery_path.length > 1) {
const higher_entity = subquery_path[subquery_path.length - 2];
const edge_to_higher_entity = schema_helpers_1.get_direct_edge(entity_name, higher_entity, orma_schema);
$select.push(edge_to_higher_entity.from_field);
}
return [...new Set($select)]; // unique values
};
exports.select_to_json_sql = select_to_json_sql;
const where_to_json_sql = (query, subquery_path, previous_results, orma_schema) => {
const subquery = helpers_1.deep_get(subquery_path, query);
let $where = subquery.$where;
const is_root_subquery = subquery_path.length <= 1;
if (!is_root_subquery) {
const nesting_ancestor_index = get_nesting_ancestor_index(query, subquery_path);
const ancestor_path = subquery_path.slice(0, nesting_ancestor_index + 1);
const ancestor_rows = previous_results
.filter(previous_result => previous_result[0].toString() === ancestor_path.toString())
.map(previous_result => previous_result[1])[0];
const path_to_ancestor = subquery_path.slice(nesting_ancestor_index, Infinity).reverse();
const ancestor_where_clause = get_ancestor_where_clause(ancestor_rows, path_to_ancestor, orma_schema);
$where = exports.combine_where_clauses($where, ancestor_where_clause, '$and');
}
return $where;
};
exports.where_to_json_sql = where_to_json_sql;
// export const get_subquery_sql = (
// query,
// subquery_path: string[],
// previous_results: (string[] | Record<string, unknown>[])[][],
// orma_schema: orma_schema
// ): string => {
// const json_sql = query_to_json_sql(query, subquery_path, previous_results, orma_schema)
// const sql = json_to_sql(json_sql)
// return sql
// }
// /**
// * transforms a query into a simplified json sql. This is still json, but can be parsed directly into sql (so no subqueries, $from is always there etc.)
// */
// export const query_to_json_sql = (
// query,
// subquery_path: string[],
// previous_results: (string[] | Record<string, unknown>[])[][],
// orma_schema: orma_schema
// ): Record<string, any> => {
// const subquery = deep_get(subquery_path, query)
// // strip sub subqueries from the subquery
// const reserved_commands = Object.keys(subquery).filter(is_reserved_keyword)
// const reserved_json = reserved_commands.reduce((previous, key) => {
// return {
// ...previous,
// [key]: subquery[key]
// }
// }, {})
// //
// const $select = select_to_json_sql(query, subquery_path, orma_schema)
// const $from = subquery.$from ?? last(subquery_path)
// const $where = where_to_json_sql(query, subquery_path, previous_results, orma_schema)
// const $having = having_to_json_sql(query, subquery_path, orma_schema)
// const json_sql: Record<string, unknown> = {
// ...reserved_json,
// ...($select && { $select }),
// ...($from && { $from }),
// ...($where && { $where }),
// ...($having && { $having })
// }
// return json_sql
// }
const having_to_json_sql = (query, subquery_path, orma_schema) => {

@@ -280,142 +74,2 @@ const subquery = helpers_1.deep_get(subquery_path, query);

exports.having_to_json_sql = having_to_json_sql;
/* gives a query that contains both queries combined with the either '$and' or '$or'.
* Prevents combine_with duplication if one of the qureies, for instance, already has an '$and' clause
*/
const combine_where_clauses = (where1, where2, connective) => {
if (!where1) {
return where2;
}
if (!where2) {
return where1;
}
if (where1[connective] && where2[connective]) {
const where1_items = where1[connective];
const where2_items = where2[connective];
return {
[connective]: [...where1_items, ...where2_items]
};
}
if (where1[connective]) {
const where1_items = where1[connective];
return {
[connective]: [...where1_items, where2]
};
}
if (where2[connective]) {
const where2_items = where2[connective];
return {
[connective]: [where1, ...where2_items]
};
}
return {
[connective]: [where1, where2]
};
};
exports.combine_where_clauses = combine_where_clauses;
/**
* The first argument to the $any_path macro is a list of connected entities, with the
* first one being connected to the currently scoped entity. The second argument is a where clause. This will be scoped to the last table in the first argument.
* This will then filter all the current entities, where there is at least one connected current_entity -> entity1 -> entity2 that matches the provided where clause
*
* @example
* {
* $where: {
* $any_path: [['entity1', 'entity2'], {
* ...where_clause_on_entity2
* }]
* }
* }
*
* @param where a where clause
* @param current_entity the root entity, since the path in the $any_path clause only starts from subsequent tables
* @param is_having if true, will use $having. Otherwise will use $where
* @returns a modified where clause
*/
const convert_any_path_macro = (where, root_entity, is_having, orma_schema) => {
if (!where) {
return where;
}
const processor = (value, path) => {
var _a;
if (typeof value === 'object' && value.$any) {
const [any_path, subquery] = value.$any;
const previous_entities = path.flatMap((path_el, i) => {
if (path_el === '$any') {
const path_segment = path.slice(0, i + 1);
const previous_any = helpers_1.deep_get(path_segment, where);
return helpers_1.last(previous_any[0]);
}
else {
return [];
}
});
const current_entity = (_a = helpers_1.last(previous_entities)) !== null && _a !== void 0 ? _a : root_entity;
const full_path = [current_entity].concat(any_path);
const edge_path = schema_helpers_1.get_edge_path(full_path, orma_schema).reverse();
const query = edge_path.reduce((acc, edge) => {
return {
$in: [
edge.from_field,
{
$select: [edge.to_field],
$from: edge.to_entity,
[is_having ? '$having' : '$where']: acc
}
]
};
}, subquery);
return query;
}
else {
return value;
}
};
return helpers_1.deep_map(where, processor);
};
exports.convert_any_path_macro = convert_any_path_macro;
/**
* Gets the closest ancestor that satisfies either of two conditions:
* 1. has a where or having clause
* 2. is the root ancestor
*
* These are the ancestors that will be split into sequential queries, so we do a server-side nesting for them,
* rather than duplicating these queries in the database
*
* @returns The index in the subquery_path of the nesting ancestor
*/
const get_nesting_ancestor_index = (query, subquery_path) => {
for (let i = subquery_path.length - 1; i >= 0; i--) {
const subpath = subquery_path.slice(0, i + 1);
const subquery = helpers_1.deep_get(subpath, query);
if (subquery.$where || subquery.$having) {
return i;
}
}
return 0;
};
/**
* Generates a where clause that restricts rows to only be ones connected to a given ancestor through a given route
* @param ancestor_rows The foreign key values in these will be inserted into the where clause
* @param path_to_ancestor This should start with the current table and end with the ancestor table
* @returns A where clause
*/
const get_ancestor_where_clause = (ancestor_rows, path_to_ancestor, orma_schema) => {
const ancestor_name = helpers_1.last(path_to_ancestor);
const table_under_ancestor = path_to_ancestor[path_to_ancestor.length - 2];
const last_edge_to_ancestor = schema_helpers_1.get_direct_edge(table_under_ancestor, ancestor_name, orma_schema);
if (ancestor_rows === undefined || ancestor_rows.length === 0) {
throw Error(`No ancestor rows provided for ${ancestor_name}`);
}
const ancestor_linking_key_values = ancestor_rows.map(row => row[last_edge_to_ancestor.to_field]);
const any_path = path_to_ancestor.slice(1, path_to_ancestor.length - 1);
const ancestor_query = exports.convert_any_path_macro({
$any: [
any_path,
{
$in: [last_edge_to_ancestor.from_field, ancestor_linking_key_values]
}
]
}, path_to_ancestor[0], false, orma_schema);
return ancestor_query;
};
const orma_nester = (results, orma_schema) => {

@@ -442,18 +96,17 @@ // get data in the right format for the nester

// export const orma_query = async <schema>(raw_query: validate_query<schema>, orma_schema: validate_orma_schema<schema>, query_function: (sql_string: string) => Promise<Record<string, unknown>[]>) => {
const orma_query = async (raw_query, orma_schema, query_function) => {
const query = helpers_1.clone(raw_query); // clone query so we can apply macros without mutating user input
const query_plan = exports.get_query_plan(query);
const orma_query = async (raw_query, orma_schema, query_function, escaping_function) => {
const query = helpers_1.clone(raw_query); // clone query so we can apply macros without mutating the actual input query
escaping_macros_1.apply_field_macro(query);
any_path_macro_1.apply_any_path_macro(query, orma_schema);
select_macro_1.apply_select_macro(query, orma_schema);
const query_plan = query_plan_1.get_query_plan(query);
let results = [];
// Sequential for query plan
for (let i = 0; i < query_plan.length; i++) {
const paths = query_plan[i];
for (const paths of query_plan) {
const sql_strings = paths.map(path => {
// apply macros
const where = helpers_1.deep_get([...path, '$where'], query);
const macrod_where = exports.convert_any_path_macro(where, helpers_1.last(path), false, orma_schema);
helpers_1.deep_set([...path, '$where'], macrod_where, query);
const having = helpers_1.deep_get([...path, '$having'], query);
const macrod_having = exports.convert_any_path_macro(having, helpers_1.last(path), false, orma_schema);
helpers_1.deep_set([...path, '$having'], macrod_having, query);
return exports.get_subquery_sql(query, path, results, orma_schema);
// the nesting macro needs previous results, so we cant do it in the beginning
nesting_macro_1.apply_nesting_macro(query, path, results, orma_schema);
const subquery = helpers_1.deep_get(path, query);
escaping_macros_1.apply_escaping_macro(subquery, (value, path) => escaping_function(value));
return json_sql_1.json_to_sql(subquery);
});

@@ -469,53 +122,1 @@ // Promise.all for each element in query plan

exports.orma_query = orma_query;
// const test = orma_query({}, {
// products: { id: {} },
// variants: {
// id: {},
// product_id: {
// // references: { products: { id: {} } }
// references: { products: {id: {}} }
// }
// },
// images: {
// id: {},
// variant_id: {
// references: { variants: { id8: {} }, hi: {} }
// }
// },
// images2: {
// id: {},
// variant_id: {
// // a@ts-expect-error
// references: { oops: { id: {} } }
// }
// },
// images3: {
// id: {},
// variant_id: {
// // a@ts-expect-error
// references: { variants: { id: {} }, hi: {}, }
// }
// }
// }, (s) => ([{ a: 'hi' }]))
// type validate_query<schema> = {
// [entity in keyof schema]: boolean
// }
// const test2 = orma_query({
// variants: true,
// products: true,
// poop: true,
// }, {
// variants: {id: {}},
// products: {id: {}},
// }, (s) => Promise.resolve([{}]))
/*
- get all data
- nest data
*/

@@ -5,3 +5,2 @@ "use strict";

const mocha_1 = require("mocha");
const sql_formatter_1 = require("sql-formatter");
const query_1 = require("./query");

@@ -43,443 +42,2 @@ mocha_1.describe('query', () => {

};
mocha_1.describe('json_to_sql', () => {
mocha_1.test('joins commands', () => {
const json = {
$select: ['a'],
$from: 'b'
};
const sql = sql_formatter_1.format(query_1.json_to_sql(json));
const goal = sql_formatter_1.format(`SELECT a FROM b`);
chai_1.expect(sql).to.equal(goal);
});
mocha_1.test('nested command work', () => {
const json = {
$where: {
$eq: ['a', 'b']
}
};
const sql = sql_formatter_1.format(query_1.json_to_sql(json));
const goal = sql_formatter_1.format('WHERE a = b');
chai_1.expect(sql).to.equal(goal);
});
mocha_1.test("'not' command works", () => {
const json = {
$not: {
$in: ['a', [1, 2]]
}
};
const sql = sql_formatter_1.format(query_1.json_to_sql(json));
const goal = sql_formatter_1.format('a NOT IN (1, 2)');
chai_1.expect(sql).to.equal(goal);
});
mocha_1.test('ignores undefined properties', () => {
const json = {
$having: undefined
};
const sql = sql_formatter_1.format(query_1.json_to_sql(json));
const goal = sql_formatter_1.format('');
chai_1.expect(sql).to.equal(goal);
});
});
mocha_1.describe(query_1.get_query_plan.name, () => {
mocha_1.test('splits by $where clause and $having', () => {
const query = {
vendors: {
products: {
$where: { $eq: ['id', 0] },
vins: {
id: true
},
images: {
image_urls: {
$having: { $eq: ['id', 0] },
id: true
}
}
}
}
};
const result = query_1.get_query_plan(query);
// the split happens at variants because it has a where clause
const goal = [
[['vendors']],
[['vendors', 'products']],
[
['vendors', 'products', 'vins'],
['vendors', 'products', 'images'],
['vendors', 'products', 'images', 'image_urls']
] // these are queried concurrently
];
chai_1.expect(result).to.deep.equal(goal);
});
mocha_1.test('handles multiple top level props', () => {
const query = {
vendors: {
id: true
},
products: {
id: true
}
};
const result = query_1.get_query_plan(query);
const goal = [[['vendors'], ['products']]];
chai_1.expect(result).to.deep.equal(goal);
});
mocha_1.test('handles renamed queries', () => {
const query = {
my_products: {
$from: 'products',
id: true
}
};
const result = query_1.get_query_plan(query);
const goal = [[['my_products']]];
chai_1.expect(result).to.deep.equal(goal);
});
});
mocha_1.describe('is_subquery', () => {
mocha_1.test('is subquery', () => {
const result = query_1.is_subquery({
$from: 'products',
id: {}
});
chai_1.expect(result).to.equal(true);
});
mocha_1.test('not subquery', () => {
const result = query_1.is_subquery({
$from: 'products'
});
chai_1.expect(result).to.equal(false);
});
});
mocha_1.describe('convert_any_clauses', () => {
mocha_1.test('multiple any clauses', () => {
const where = {
$and: [
{
$any: [
['images'],
{
$eq: ['id', 1]
}
]
},
{
$any: [
['vendors'],
{
$eq: ['id', 1]
}
]
}
]
};
const converted_where = query_1.convert_any_path_macro(where, 'products', false, orma_schema);
const goal = {
$and: [
{
$in: [
'id',
{
$select: ['product_id'],
$from: 'images',
$where: {
$eq: ['id', 1]
}
}
]
},
{
$in: [
'vendor_id',
{
$select: ['id'],
$from: 'vendors',
$where: {
$eq: ['id', 1]
}
}
]
}
]
};
chai_1.expect(converted_where).to.deep.equal(goal);
});
mocha_1.test('deep any path', () => {
const where = {
$any: [
['images', 'image_urls'],
{
$eq: ['id', 1]
}
]
};
const converted_where = query_1.convert_any_path_macro(where, 'products', false, orma_schema);
const goal = {
$in: [
'id',
{
$select: ['product_id'],
$from: 'images',
$where: {
$in: [
'id',
{
$select: ['image_id'],
$from: 'image_urls',
$where: {
$eq: ['id', 1]
}
}
]
}
}
]
};
chai_1.expect(converted_where).to.deep.equal(goal);
});
mocha_1.test('nested anys', () => {
const where = {
$any: [
['images'],
{
$any: [
['image_urls'],
{
$eq: ['id', 1]
}
]
}
]
};
const converted_where = query_1.convert_any_path_macro(where, 'products', false, orma_schema);
const goal = {
$in: [
'id',
{
$select: ['product_id'],
$from: 'images',
$where: {
$in: [
'id',
{
$select: ['image_id'],
$from: 'image_urls',
$where: {
$eq: ['id', 1]
}
}
]
}
}
]
};
chai_1.expect(converted_where).to.deep.equal(goal);
});
mocha_1.test('uses having', () => {
const where = {
$any: [
['images'],
{
$eq: ['id', 1]
}
]
};
const converted_where = query_1.convert_any_path_macro(where, 'products', true, orma_schema);
const goal = {
$in: [
'id',
{
$select: ['product_id'],
$from: 'images',
$having: {
$eq: ['id', 1]
}
}
]
};
chai_1.expect(converted_where).to.deep.equal(goal);
});
});
mocha_1.describe('query_to_json_sql', () => {
mocha_1.test('handles selects/handles root', () => {
const query = {
products: {
id: true,
my_title: 'title',
total_quantity: {
$sum: 'quantity'
}
}
};
const json_sql = query_1.query_to_json_sql(query, ['products'], [], {});
const goal = {
$select: [
'id',
{ $as: ['title', 'my_title'] },
{ $as: [{ $sum: 'quantity' }, 'total_quantity'] }
],
$from: 'products'
};
chai_1.expect(json_sql).to.deep.equal(goal);
});
mocha_1.test('handles root nesting', () => {
const query = {
products: {
id: true,
images: {
id: true,
product_id: true
}
}
};
const previous_results = [[['products'], [{ id: 1 }, { id: 2 }]]];
const json_sql = query_1.query_to_json_sql(query, ['products', 'images'], previous_results, orma_schema);
const goal = {
$select: ['id', 'product_id'],
$from: 'images',
$where: {
$in: ['product_id', [1, 2]]
}
};
chai_1.expect(json_sql).to.deep.equal(goal);
});
mocha_1.test('handles adding foreign keys', () => {
var _a;
const query = {
products: {
images: { url: true }
}
};
const previous_results = [[['products'], [{ id: 1 }, { id: 2 }]]];
const json_sql1 = query_1.query_to_json_sql(query, ['products'], previous_results, orma_schema);
const goal1 = {
$select: ['id'],
$from: 'products'
};
const json_sql2 = query_1.query_to_json_sql(query, ['products', 'images'], previous_results, orma_schema);
(_a = json_sql2 === null || json_sql2 === void 0 ? void 0 : json_sql2.$select) === null || _a === void 0 ? void 0 : _a.sort();
const goal2 = {
$select: ['product_id', 'url'].sort(),
$from: 'images',
$where: {
$in: ['product_id', [1, 2]]
}
};
chai_1.expect(json_sql1).to.deep.equal(goal1);
chai_1.expect(json_sql2).to.deep.equal(goal2);
});
mocha_1.test('handles deep nesting', () => {
var _a;
const query = {
products: {
images: {
image_urls: {
id: true
}
}
}
};
const previous_results = [[['products'], [{ id: 1 }, { id: 2 }]]];
const json_sql = query_1.query_to_json_sql(query, ['products', 'images', 'image_urls'], previous_results, orma_schema);
(_a = json_sql === null || json_sql === void 0 ? void 0 : json_sql.$select) === null || _a === void 0 ? void 0 : _a.sort();
const goal = {
$select: ['image_id', 'id'].sort(),
$from: 'image_urls',
$where: {
$in: [
'image_id',
{
$select: ['id'],
$from: 'images',
$where: {
$in: ['product_id', [1, 2]]
}
}
]
}
};
chai_1.expect(json_sql).to.deep.equal(goal);
});
mocha_1.test('handles nesting under where clause', () => {
const query = {
products: {
images: {
$where: { $gt: ['id', 0] },
image_urls: {}
}
}
};
const previous_results = [
[['products'], [{ id: 1 }, { id: 2 }]],
[['products', 'images'], [{ id: 3 }]]
];
const json_sql = query_1.query_to_json_sql(query, ['products', 'images', 'image_urls'], previous_results, orma_schema);
const goal = {
$select: ['image_id'],
$from: 'image_urls',
$where: {
$in: ['image_id', [3]]
}
};
chai_1.expect(json_sql).to.deep.equal(goal);
});
mocha_1.test('ignores undefined where/having clauses', () => {
const query = {
products: {
images: {
$where: undefined,
$having: undefined,
image_urls: {}
}
}
};
const previous_results = [
[['products'], [{ id: 1 }, { id: 2 }]],
[['products', 'images'], [{ id: 3 }]]
];
const json_sql = query_1.query_to_json_sql(query, ['products', 'images', 'image_urls'], previous_results, orma_schema);
const goal = {
$select: ['image_id'],
$from: 'image_urls',
$where: {
$in: [
'image_id',
{
$select: ['id'],
$from: 'images',
$where: {
$in: ['product_id', [1, 2]]
}
}
]
}
};
chai_1.expect(json_sql).to.deep.equal(goal);
});
mocha_1.test("respects 'from' clause", () => {
const query = {
my_products: {
id: true,
$from: 'products'
}
};
const json_sql = query_1.query_to_json_sql(query, ['my_products'], [], {});
const goal = {
$select: ['id'],
$from: 'products'
};
chai_1.expect(json_sql).to.deep.equal(goal);
});
mocha_1.test.skip("handles 'any' clause", () => {
const query = {
$where: {
$any: []
},
id: true
};
const json_sql = query_1.query_to_json_sql(query, ['products'], [], {});
const goal = {};
chai_1.expect(json_sql).to.deep.equal(goal);
});
});
mocha_1.describe(query_1.orma_nester.name, () => {

@@ -610,3 +168,3 @@ mocha_1.test('nests restults', () => {

return Promise.resolve([]);
});
}, el => el);
chai_1.expect(actual_query).to.deep.equal('SELECT id FROM calls');

@@ -613,0 +171,0 @@ });

@@ -7,2 +7,3 @@ "use strict";

const query_1 = require("./query");
const query_helpers_1 = require("./query_helpers");
const validator = (query, schema) => {

@@ -13,4 +14,4 @@ let errors = [];

const is_boolean_resolver = val === true;
const is_virtual_column_resolver = typeof val === 'object' && !query_1.is_subquery(val);
const is_subquery_resolver = typeof val === 'object' && query_1.is_subquery(val);
const is_virtual_column_resolver = helpers_1.is_simple_object(val) && !query_helpers_1.is_subquery(val);
const is_subquery_resolver = helpers_1.is_simple_object(val) && query_helpers_1.is_subquery(val);
const is_clauses_resolver = '?';

@@ -17,0 +18,0 @@ if (is_boolean_resolver) {

{
"name": "orma",
"version": "1.0.41",
"version": "1.0.43",
"description": "A declarative relational syncronous orm",

@@ -11,2 +11,3 @@ "main": "build/index.js",

"nt": "nodemon --exec nyc --reporter html npm run test",
"ntt": "nodemon --exec npm run test",
"coverage-watch": "live-server coverage"

@@ -13,0 +14,0 @@ },

## Orma
Orma is a light-weight declarative ORM for sql databases.
Orma uses a json syntax to represent queries and mutations.
Orma uses json syntax to represent queries and mutations.
Queries are objects specifying which fields to query. Only fields which

@@ -6,0 +6,0 @@ are requested will be selected. Symbols with a $ are called macros and are used to represent abstractions to the sql AST. Sql keywords can be accessed with the $ prefix and snake case. (eg $group_by, $limit, $where)

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