@ragestudio/scylla-odm 0.3.1 → 0.5.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/dist/client.d.mts +23 -0
- package/dist/client.mjs +112 -0
- package/dist/client.mjs.map +1 -0
- package/dist/cql_gen/create_table.d.mts +7 -0
- package/dist/cql_gen/create_table.mjs +38 -0
- package/dist/cql_gen/create_table.mjs.map +1 -0
- package/dist/index.d.mts +4 -113
- package/dist/index.mjs +5 -515
- package/dist/index.mjs.map +1 -1
- package/dist/model/index.d.mts +38 -0
- package/dist/model/index.mjs +44 -0
- package/dist/model/index.mjs.map +1 -0
- package/dist/operations/countAll.d.mts +7 -0
- package/dist/operations/countAll.mjs +16 -0
- package/dist/operations/countAll.mjs.map +1 -0
- package/dist/operations/delete.d.mts +8 -0
- package/dist/operations/delete.mjs +11 -0
- package/dist/operations/delete.mjs.map +1 -0
- package/dist/operations/find.d.mts +8 -0
- package/dist/operations/find.mjs +22 -0
- package/dist/operations/find.mjs.map +1 -0
- package/dist/operations/findOne.d.mts +8 -0
- package/dist/operations/findOne.mjs +17 -0
- package/dist/operations/findOne.mjs.map +1 -0
- package/dist/operations/sync.d.mts +7 -0
- package/dist/operations/sync.mjs +16 -0
- package/dist/operations/sync.mjs.map +1 -0
- package/dist/operations/tableExists.d.mts +7 -0
- package/dist/operations/tableExists.mjs +19 -0
- package/dist/operations/tableExists.mjs.map +1 -0
- package/dist/operations/update.d.mts +7 -0
- package/dist/operations/update.mjs +18 -0
- package/dist/operations/update.mjs.map +1 -0
- package/dist/result/index.d.mts +17 -0
- package/dist/result/index.mjs +66 -0
- package/dist/result/index.mjs.map +1 -0
- package/dist/schema/index.d.mts +13 -0
- package/dist/schema/index.mjs +16 -0
- package/dist/schema/index.mjs.map +1 -0
- package/dist/types.d.mts +74 -0
- package/dist/types.mjs +34 -0
- package/dist/types.mjs.map +1 -0
- package/dist/utils/buildMapper.d.mts +5 -0
- package/dist/utils/buildMapper.mjs +13 -0
- package/dist/utils/buildMapper.mjs.map +1 -0
- package/dist/utils/fillDefaults.d.mts +5 -0
- package/dist/utils/fillDefaults.mjs +18 -0
- package/dist/utils/fillDefaults.mjs.map +1 -0
- package/dist/utils/loadModels.d.mts +5 -0
- package/dist/utils/loadModels.mjs +30 -0
- package/dist/utils/loadModels.mjs.map +1 -0
- package/dist/utils/queryParser.d.mts +9 -0
- package/dist/utils/queryParser.mjs +94 -0
- package/dist/utils/queryParser.mjs.map +1 -0
- package/dist/utils/typeChecker.d.mts +7 -0
- package/dist/utils/typeChecker.mjs +55 -0
- package/dist/utils/typeChecker.mjs.map +1 -0
- package/package.json +40 -4
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Model } from "../model/index.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/utils/queryParser.d.ts
|
|
4
|
+
declare function queryParser(model: Model<any>, query: any, depth?: number): any;
|
|
5
|
+
declare function isValidFieldName(fields: Record<string, any>, fieldName: string): boolean;
|
|
6
|
+
declare function isValidOperator(operator: string): boolean;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { queryParser as default, isValidFieldName, isValidOperator };
|
|
9
|
+
//# sourceMappingURL=queryParser.d.mts.map
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import Cassandra from "cassandra-driver";
|
|
2
|
+
//#region src/utils/queryParser.ts
|
|
3
|
+
const { q } = Cassandra.mapping;
|
|
4
|
+
const MAX_QUERY_DEPTH = 3;
|
|
5
|
+
const MAX_IN_ELEMENTS = 1e3;
|
|
6
|
+
const VALID_OPERATORS = new Set([
|
|
7
|
+
"$eq",
|
|
8
|
+
"$ne",
|
|
9
|
+
"$gt",
|
|
10
|
+
"$gte",
|
|
11
|
+
"$lt",
|
|
12
|
+
"$lte",
|
|
13
|
+
"$in"
|
|
14
|
+
]);
|
|
15
|
+
function requireNotNull(value, operator) {
|
|
16
|
+
if (value === null || value === void 0) throw new Error(`${operator} operator cannot compare with null or undefined`);
|
|
17
|
+
}
|
|
18
|
+
function buildOperator(operator, opValue) {
|
|
19
|
+
if (!VALID_OPERATORS.has(operator)) throw new Error(`Invalid operator: ${operator}`);
|
|
20
|
+
switch (operator) {
|
|
21
|
+
case "$eq": return opValue;
|
|
22
|
+
case "$ne":
|
|
23
|
+
requireNotNull(opValue, "$ne");
|
|
24
|
+
return q.notEq(opValue);
|
|
25
|
+
case "$in":
|
|
26
|
+
if (!Array.isArray(opValue)) throw new Error("$in operator requires an array");
|
|
27
|
+
if (opValue.length > MAX_IN_ELEMENTS) throw new Error(`$in operator exceeds maximum of ${MAX_IN_ELEMENTS} elements`);
|
|
28
|
+
for (let i = 0; i < opValue.length; i++) if (opValue[i] === null || opValue[i] === void 0) throw new Error(`$in array element at index ${i} cannot be null or undefined`);
|
|
29
|
+
return q.in_(opValue);
|
|
30
|
+
case "$gt":
|
|
31
|
+
requireNotNull(opValue, "$gt");
|
|
32
|
+
return q.gt(opValue);
|
|
33
|
+
case "$gte":
|
|
34
|
+
requireNotNull(opValue, "$gte");
|
|
35
|
+
return q.gte(opValue);
|
|
36
|
+
case "$lt":
|
|
37
|
+
requireNotNull(opValue, "$lt");
|
|
38
|
+
return q.lt(opValue);
|
|
39
|
+
case "$lte":
|
|
40
|
+
requireNotNull(opValue, "$lte");
|
|
41
|
+
return q.lte(opValue);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function queryParser(model, query, depth = 0) {
|
|
45
|
+
if (depth > MAX_QUERY_DEPTH) throw new Error(`Query depth exceeds maximum of ${MAX_QUERY_DEPTH}`);
|
|
46
|
+
if (!query || typeof query !== "object") return query;
|
|
47
|
+
const parsedQuery = {};
|
|
48
|
+
const fields = model.schema.fields;
|
|
49
|
+
for (const field of Object.keys(query)) {
|
|
50
|
+
const value = query[field];
|
|
51
|
+
if (field === "$and") {
|
|
52
|
+
handleAnd(model, value, parsedQuery, depth);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (field === "$or") throw new Error("ScyllaDB does not support OR queries across different columns. Use $in for a single column.");
|
|
56
|
+
if (!isValidFieldName(fields, field)) throw new Error(`Invalid field name: [${field}] or it does not exist in schema`);
|
|
57
|
+
parsedQuery[field] = parseField(value);
|
|
58
|
+
}
|
|
59
|
+
return parsedQuery;
|
|
60
|
+
}
|
|
61
|
+
function handleAnd(model, conditions, parsedQuery, depth) {
|
|
62
|
+
if (!Array.isArray(conditions)) throw new Error("$and operator requires an array");
|
|
63
|
+
if (conditions.length > 10) throw new Error("$and operator exceeds maximum of 10 conditions");
|
|
64
|
+
for (let i = 0; i < conditions.length; i++) {
|
|
65
|
+
const condition = conditions[i];
|
|
66
|
+
if (!condition || typeof condition !== "object") throw new Error(`$and condition at index ${i} must be an object`);
|
|
67
|
+
const parsed = queryParser(model, condition, depth + 1);
|
|
68
|
+
for (const key of Object.keys(parsed)) if (key in parsedQuery) throw new Error(`$and conflict: field "${key}" appears in multiple conditions`);
|
|
69
|
+
Object.assign(parsedQuery, parsed);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function parseField(value) {
|
|
73
|
+
if (value === null || typeof value !== "object" || Array.isArray(value) || value instanceof Date) {
|
|
74
|
+
if (Array.isArray(value)) throw new Error("Array values require explicit operator (e.g., $in)");
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
const compiledOps = Object.keys(value).map((op) => buildOperator(op, value[op]));
|
|
78
|
+
return compiledOps.length === 1 ? compiledOps[0] : q.and(...compiledOps);
|
|
79
|
+
}
|
|
80
|
+
function isValidFieldName(fields, fieldName) {
|
|
81
|
+
for (const pattern of [
|
|
82
|
+
/^[0-9]/,
|
|
83
|
+
/[^a-zA-Z0-9_]/,
|
|
84
|
+
/^(select|insert|update|delete|drop|create|alter|truncate)$/i
|
|
85
|
+
]) if (pattern.test(fieldName)) return false;
|
|
86
|
+
return fieldName in fields;
|
|
87
|
+
}
|
|
88
|
+
function isValidOperator(operator) {
|
|
89
|
+
return VALID_OPERATORS.has(operator);
|
|
90
|
+
}
|
|
91
|
+
//#endregion
|
|
92
|
+
export { queryParser as default, isValidFieldName, isValidOperator };
|
|
93
|
+
|
|
94
|
+
//# sourceMappingURL=queryParser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queryParser.mjs","names":["cassandra"],"sources":["../../src/utils/queryParser.ts"],"sourcesContent":["import type { Model } from \"../model\"\n\n// @ts-ignore\nimport cassandra from \"cassandra-driver\"\nconst { q } = cassandra.mapping\n\nconst MAX_QUERY_DEPTH = 3\nconst MAX_IN_ELEMENTS = 1000\n\nconst VALID_OPERATORS = new Set([\n\t\"$eq\",\n\t\"$ne\",\n\t\"$gt\",\n\t\"$gte\",\n\t\"$lt\",\n\t\"$lte\",\n\t\"$in\",\n])\n\nfunction requireNotNull(value: any, operator: string): void {\n\tif (value === null || value === undefined) {\n\t\tthrow new Error(\n\t\t\t`${operator} operator cannot compare with null or undefined`,\n\t\t)\n\t}\n}\n\nfunction buildOperator(operator: string, opValue: any): any {\n\tif (!VALID_OPERATORS.has(operator)) {\n\t\tthrow new Error(`Invalid operator: ${operator}`)\n\t}\n\n\tswitch (operator) {\n\t\tcase \"$eq\":\n\t\t\treturn opValue\n\n\t\tcase \"$ne\":\n\t\t\trequireNotNull(opValue, \"$ne\")\n\t\t\treturn q.notEq(opValue)\n\n\t\tcase \"$in\":\n\t\t\tif (!Array.isArray(opValue)) {\n\t\t\t\tthrow new Error(\"$in operator requires an array\")\n\t\t\t}\n\t\t\tif (opValue.length > MAX_IN_ELEMENTS) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`$in operator exceeds maximum of ${MAX_IN_ELEMENTS} elements`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tfor (let i = 0; i < opValue.length; i++) {\n\t\t\t\tif (opValue[i] === null || opValue[i] === undefined) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`$in array element at index ${i} cannot be null or undefined`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn q.in_(opValue)\n\n\t\tcase \"$gt\":\n\t\t\trequireNotNull(opValue, \"$gt\")\n\t\t\treturn q.gt(opValue)\n\n\t\tcase \"$gte\":\n\t\t\trequireNotNull(opValue, \"$gte\")\n\t\t\treturn q.gte(opValue)\n\n\t\tcase \"$lt\":\n\t\t\trequireNotNull(opValue, \"$lt\")\n\t\t\treturn q.lt(opValue)\n\n\t\tcase \"$lte\":\n\t\t\trequireNotNull(opValue, \"$lte\")\n\t\t\treturn q.lte(opValue)\n\t}\n}\n\nexport default function queryParser(\n\tmodel: Model<any>,\n\tquery: any,\n\tdepth: number = 0,\n) {\n\tif (depth > MAX_QUERY_DEPTH) {\n\t\tthrow new Error(`Query depth exceeds maximum of ${MAX_QUERY_DEPTH}`)\n\t}\n\n\tif (!query || typeof query !== \"object\") {\n\t\treturn query\n\t}\n\n\tconst parsedQuery: Record<string, any> = {}\n\tconst fields = model.schema.fields\n\n\tfor (const field of Object.keys(query)) {\n\t\tconst value = query[field]\n\n\t\tif (field === \"$and\") {\n\t\t\thandleAnd(model, value, parsedQuery, depth)\n\t\t\tcontinue\n\t\t}\n\n\t\tif (field === \"$or\") {\n\t\t\tthrow new Error(\n\t\t\t\t\"ScyllaDB does not support OR queries across different columns. Use $in for a single column.\",\n\t\t\t)\n\t\t}\n\n\t\tif (!isValidFieldName(fields, field)) {\n\t\t\tthrow new Error(\n\t\t\t\t`Invalid field name: [${field}] or it does not exist in schema`,\n\t\t\t)\n\t\t}\n\n\t\tparsedQuery[field] = parseField(value)\n\t}\n\n\treturn parsedQuery\n}\n\nfunction handleAnd(\n\tmodel: Model<any>,\n\tconditions: any,\n\tparsedQuery: Record<string, any>,\n\tdepth: number,\n) {\n\tif (!Array.isArray(conditions)) {\n\t\tthrow new Error(\"$and operator requires an array\")\n\t}\n\tif (conditions.length > 10) {\n\t\tthrow new Error(\"$and operator exceeds maximum of 10 conditions\")\n\t}\n\n\tfor (let i = 0; i < conditions.length; i++) {\n\t\tconst condition = conditions[i]\n\t\tif (!condition || typeof condition !== \"object\") {\n\t\t\tthrow new Error(`$and condition at index ${i} must be an object`)\n\t\t}\n\n\t\tconst parsed = queryParser(model, condition, depth + 1)\n\t\tfor (const key of Object.keys(parsed)) {\n\t\t\tif (key in parsedQuery) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`$and conflict: field \"${key}\" appears in multiple conditions`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\tObject.assign(parsedQuery, parsed)\n\t}\n}\n\nfunction parseField(value: any): any {\n\tif (\n\t\tvalue === null ||\n\t\ttypeof value !== \"object\" ||\n\t\tArray.isArray(value) ||\n\t\tvalue instanceof Date\n\t) {\n\t\tif (Array.isArray(value)) {\n\t\t\tthrow new Error(\n\t\t\t\t\"Array values require explicit operator (e.g., $in)\",\n\t\t\t)\n\t\t}\n\t\treturn value\n\t}\n\n\tconst operators = Object.keys(value)\n\tconst compiledOps = operators.map((op) => buildOperator(op, value[op]))\n\n\treturn compiledOps.length === 1\n\t\t? compiledOps[0]\n\t\t: (q.and as any)(...compiledOps)\n}\n\nexport function isValidFieldName(\n\tfields: Record<string, any>,\n\tfieldName: string,\n): boolean {\n\tconst invalidPatterns = [\n\t\t/^[0-9]/,\n\t\t/[^a-zA-Z0-9_]/,\n\t\t/^(select|insert|update|delete|drop|create|alter|truncate)$/i,\n\t]\n\n\tfor (const pattern of invalidPatterns) {\n\t\tif (pattern.test(fieldName)) return false\n\t}\n\n\treturn fieldName in fields\n}\n\nexport function isValidOperator(operator: string): boolean {\n\treturn VALID_OPERATORS.has(operator)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { isValidFieldName } from "./queryParser.mjs";
|
|
2
|
+
import Cassandra from "cassandra-driver";
|
|
3
|
+
//#region src/utils/typeChecker.ts
|
|
4
|
+
const { types } = Cassandra;
|
|
5
|
+
const stringTypes = new Set([
|
|
6
|
+
"ascii",
|
|
7
|
+
"text",
|
|
8
|
+
"varchar",
|
|
9
|
+
"inet"
|
|
10
|
+
]);
|
|
11
|
+
const intTypes = new Set([
|
|
12
|
+
"int",
|
|
13
|
+
"smallint",
|
|
14
|
+
"tinyint"
|
|
15
|
+
]);
|
|
16
|
+
const floatTypes = new Set(["double", "float"]);
|
|
17
|
+
const longTypes = new Set(["bigint", "counter"]);
|
|
18
|
+
function isValidValue(value, expectedType) {
|
|
19
|
+
if (value === null || value === void 0) return true;
|
|
20
|
+
if (stringTypes.has(expectedType)) return typeof value === "string";
|
|
21
|
+
if (intTypes.has(expectedType)) return Number.isInteger(value);
|
|
22
|
+
if (floatTypes.has(expectedType)) return typeof value === "number";
|
|
23
|
+
if (longTypes.has(expectedType)) return typeof value === "bigint" || typeof value === "number" || value instanceof types.Long;
|
|
24
|
+
switch (expectedType) {
|
|
25
|
+
case "boolean": return typeof value === "boolean";
|
|
26
|
+
case "decimal": return typeof value === "number" || typeof value === "string" || value instanceof types.BigDecimal;
|
|
27
|
+
case "varint": return typeof value === "bigint" || typeof value === "number" || value instanceof types.Integer;
|
|
28
|
+
case "timestamp": return value instanceof Date || typeof value === "number" || typeof value === "string";
|
|
29
|
+
case "date": return typeof value === "string" || value instanceof types.LocalDate;
|
|
30
|
+
case "time": return typeof value === "string" || value instanceof types.LocalTime;
|
|
31
|
+
case "uuid": return typeof value === "string" || value instanceof types.Uuid;
|
|
32
|
+
case "timeuuid": return typeof value === "string" || value instanceof types.TimeUuid;
|
|
33
|
+
case "blob": return Buffer.isBuffer(value) || value instanceof Uint8Array;
|
|
34
|
+
}
|
|
35
|
+
if (expectedType.startsWith("list<") || expectedType.startsWith("set<")) return Array.isArray(value) || value instanceof Set;
|
|
36
|
+
if (expectedType.startsWith("map<")) return typeof value === "object" && !Array.isArray(value) && value !== null;
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
function typeChecker(model, data) {
|
|
40
|
+
if (!data || typeof data !== "object" || Array.isArray(data)) throw new TypeError(`[${model.name}] Validation error: Data payload must be an object`);
|
|
41
|
+
const fields = model.schema.fields;
|
|
42
|
+
for (const [key, value] of Object.entries(data)) {
|
|
43
|
+
if (!isValidFieldName(fields, key)) throw new Error(`[${model.name}] Validation error: Field '${key}' does not exist in schema`);
|
|
44
|
+
const expectedType = (fields[key].type || "text").toLowerCase();
|
|
45
|
+
if (!isValidValue(value, expectedType)) {
|
|
46
|
+
const receivedType = Array.isArray(value) ? "array" : typeof value;
|
|
47
|
+
throw new TypeError(`[${model.name}] Validation error: Invalid type for field '${key}'. Expected[${expectedType}], but received [${receivedType}]`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { typeChecker as default };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=typeChecker.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typeChecker.mjs","names":["cassandra"],"sources":["../../src/utils/typeChecker.ts"],"sourcesContent":["// @ts-ignore\nimport cassandra from \"cassandra-driver\"\nconst { types } = cassandra\nimport type { Model } from \"../model\"\nimport { isValidFieldName } from \"./queryParser\"\n\nconst stringTypes = new Set([\"ascii\", \"text\", \"varchar\", \"inet\"])\nconst intTypes = new Set([\"int\", \"smallint\", \"tinyint\"])\nconst floatTypes = new Set([\"double\", \"float\"])\nconst longTypes = new Set([\"bigint\", \"counter\"])\n\nfunction isValidValue(value: any, expectedType: string): boolean {\n\tif (value === null || value === undefined) return true\n\n\tif (stringTypes.has(expectedType)) return typeof value === \"string\"\n\tif (intTypes.has(expectedType)) return Number.isInteger(value)\n\tif (floatTypes.has(expectedType)) return typeof value === \"number\"\n\tif (longTypes.has(expectedType)) {\n\t\treturn (\n\t\t\ttypeof value === \"bigint\" ||\n\t\t\ttypeof value === \"number\" ||\n\t\t\tvalue instanceof types.Long\n\t\t)\n\t}\n\n\tswitch (expectedType) {\n\t\tcase \"boolean\":\n\t\t\treturn typeof value === \"boolean\"\n\t\tcase \"decimal\":\n\t\t\treturn (\n\t\t\t\ttypeof value === \"number\" ||\n\t\t\t\ttypeof value === \"string\" ||\n\t\t\t\tvalue instanceof types.BigDecimal\n\t\t\t)\n\t\tcase \"varint\":\n\t\t\treturn (\n\t\t\t\ttypeof value === \"bigint\" ||\n\t\t\t\ttypeof value === \"number\" ||\n\t\t\t\tvalue instanceof types.Integer\n\t\t\t)\n\t\tcase \"timestamp\":\n\t\t\treturn (\n\t\t\t\tvalue instanceof Date ||\n\t\t\t\ttypeof value === \"number\" ||\n\t\t\t\ttypeof value === \"string\"\n\t\t\t)\n\t\tcase \"date\":\n\t\t\treturn typeof value === \"string\" || value instanceof types.LocalDate\n\t\tcase \"time\":\n\t\t\treturn typeof value === \"string\" || value instanceof types.LocalTime\n\t\tcase \"uuid\":\n\t\t\treturn typeof value === \"string\" || value instanceof types.Uuid\n\t\tcase \"timeuuid\":\n\t\t\treturn typeof value === \"string\" || value instanceof types.TimeUuid\n\t\tcase \"blob\":\n\t\t\treturn Buffer.isBuffer(value) || value instanceof Uint8Array\n\t}\n\n\tif (expectedType.startsWith(\"list<\") || expectedType.startsWith(\"set<\")) {\n\t\treturn Array.isArray(value) || value instanceof Set\n\t}\n\tif (expectedType.startsWith(\"map<\")) {\n\t\treturn (\n\t\t\ttypeof value === \"object\" && !Array.isArray(value) && value !== null\n\t\t)\n\t}\n\n\treturn false\n}\n\nexport default function typeChecker(model: Model<any>, data: any): boolean {\n\tif (!data || typeof data !== \"object\" || Array.isArray(data)) {\n\t\tthrow new TypeError(\n\t\t\t`[${model.name}] Validation error: Data payload must be an object`,\n\t\t)\n\t}\n\n\tconst fields = model.schema.fields\n\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tif (!isValidFieldName(fields, key)) {\n\t\t\tthrow new Error(\n\t\t\t\t`[${model.name}] Validation error: Field '${key}' does not exist in schema`,\n\t\t\t)\n\t\t}\n\n\t\tconst fieldConfig = fields[key]\n\t\tconst expectedType = (fieldConfig.type || \"text\").toLowerCase()\n\n\t\tif (!isValidValue(value, expectedType)) {\n\t\t\tconst receivedType = Array.isArray(value) ? \"array\" : typeof value\n\t\t\tthrow new TypeError(\n\t\t\t\t`[${model.name}] Validation error: Invalid type for field '${key}'. ` +\n\t\t\t\t\t`Expected[${expectedType}], but received [${receivedType}]`,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn true\n}\n"],"mappings":";;;AAEA,MAAM,EAAE,UAAUA;AAIlB,MAAM,cAAc,IAAI,IAAI;CAAC;CAAS;CAAQ;CAAW;CAAO,CAAC;AACjE,MAAM,WAAW,IAAI,IAAI;CAAC;CAAO;CAAY;CAAU,CAAC;AACxD,MAAM,aAAa,IAAI,IAAI,CAAC,UAAU,QAAQ,CAAC;AAC/C,MAAM,YAAY,IAAI,IAAI,CAAC,UAAU,UAAU,CAAC;AAEhD,SAAS,aAAa,OAAY,cAA+B;AAChE,KAAI,UAAU,QAAQ,UAAU,KAAA,EAAW,QAAO;AAElD,KAAI,YAAY,IAAI,aAAa,CAAE,QAAO,OAAO,UAAU;AAC3D,KAAI,SAAS,IAAI,aAAa,CAAE,QAAO,OAAO,UAAU,MAAM;AAC9D,KAAI,WAAW,IAAI,aAAa,CAAE,QAAO,OAAO,UAAU;AAC1D,KAAI,UAAU,IAAI,aAAa,CAC9B,QACC,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,iBAAiB,MAAM;AAIzB,SAAQ,cAAR;EACC,KAAK,UACJ,QAAO,OAAO,UAAU;EACzB,KAAK,UACJ,QACC,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,iBAAiB,MAAM;EAEzB,KAAK,SACJ,QACC,OAAO,UAAU,YACjB,OAAO,UAAU,YACjB,iBAAiB,MAAM;EAEzB,KAAK,YACJ,QACC,iBAAiB,QACjB,OAAO,UAAU,YACjB,OAAO,UAAU;EAEnB,KAAK,OACJ,QAAO,OAAO,UAAU,YAAY,iBAAiB,MAAM;EAC5D,KAAK,OACJ,QAAO,OAAO,UAAU,YAAY,iBAAiB,MAAM;EAC5D,KAAK,OACJ,QAAO,OAAO,UAAU,YAAY,iBAAiB,MAAM;EAC5D,KAAK,WACJ,QAAO,OAAO,UAAU,YAAY,iBAAiB,MAAM;EAC5D,KAAK,OACJ,QAAO,OAAO,SAAS,MAAM,IAAI,iBAAiB;;AAGpD,KAAI,aAAa,WAAW,QAAQ,IAAI,aAAa,WAAW,OAAO,CACtE,QAAO,MAAM,QAAQ,MAAM,IAAI,iBAAiB;AAEjD,KAAI,aAAa,WAAW,OAAO,CAClC,QACC,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM,IAAI,UAAU;AAIlE,QAAO;;AAGR,SAAwB,YAAY,OAAmB,MAAoB;AAC1E,KAAI,CAAC,QAAQ,OAAO,SAAS,YAAY,MAAM,QAAQ,KAAK,CAC3D,OAAM,IAAI,UACT,IAAI,MAAM,KAAK,oDACf;CAGF,MAAM,SAAS,MAAM,OAAO;AAE5B,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,KAAK,EAAE;AAChD,MAAI,CAAC,iBAAiB,QAAQ,IAAI,CACjC,OAAM,IAAI,MACT,IAAI,MAAM,KAAK,6BAA6B,IAAI,4BAChD;EAIF,MAAM,gBADc,OAAO,KACO,QAAQ,QAAQ,aAAa;AAE/D,MAAI,CAAC,aAAa,OAAO,aAAa,EAAE;GACvC,MAAM,eAAe,MAAM,QAAQ,MAAM,GAAG,UAAU,OAAO;AAC7D,SAAM,IAAI,UACT,IAAI,MAAM,KAAK,8CAA8C,IAAI,cACpD,aAAa,mBAAmB,aAAa,GAC1D;;;AAIH,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,13 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ragestudio/scylla-odm",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "An ODM for ScyllaDB",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "RageStudio",
|
|
7
7
|
"type": "module",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
8
|
+
"types": "dist/index.d.mts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.mts",
|
|
12
|
+
"default": "./dist/index.mjs"
|
|
13
|
+
},
|
|
14
|
+
"./client": {
|
|
15
|
+
"types": "./dist/client.d.mts",
|
|
16
|
+
"default": "./dist/client.mjs"
|
|
17
|
+
},
|
|
18
|
+
"./model": {
|
|
19
|
+
"types": "./dist/model/index.d.mts",
|
|
20
|
+
"default": "./dist/model/index.mjs"
|
|
21
|
+
},
|
|
22
|
+
"./schema": {
|
|
23
|
+
"types": "./dist/schema/index.d.mts",
|
|
24
|
+
"default": "./dist/schema/index.mjs"
|
|
25
|
+
},
|
|
26
|
+
"./result": {
|
|
27
|
+
"types": "./dist/result/index.d.mts",
|
|
28
|
+
"default": "./dist/result/index.mjs"
|
|
29
|
+
},
|
|
30
|
+
"./types": {
|
|
31
|
+
"types": "./dist/types.d.mts",
|
|
32
|
+
"default": "./dist/types.mjs"
|
|
33
|
+
},
|
|
34
|
+
"./utils/*": {
|
|
35
|
+
"types": "./dist/utils/*.d.mts",
|
|
36
|
+
"default": "./dist/utils/*.mjs"
|
|
37
|
+
},
|
|
38
|
+
"./operations/*": {
|
|
39
|
+
"types": "./dist/operations/*.d.mts",
|
|
40
|
+
"default": "./dist/operations/*.mjs"
|
|
41
|
+
},
|
|
42
|
+
"./cql_gen/*": {
|
|
43
|
+
"types": "./dist/cql_gen/*.d.mts",
|
|
44
|
+
"default": "./dist/cql_gen/*.mjs"
|
|
45
|
+
}
|
|
46
|
+
},
|
|
11
47
|
"files": [
|
|
12
48
|
"dist"
|
|
13
49
|
],
|