gencow 0.1.106 → 0.1.108
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/bin/gencow.mjs +2 -0
- package/core/index.js +80 -15
- package/package.json +1 -1
- package/server/index.js +81 -15
- package/server/index.js.map +2 -2
package/bin/gencow.mjs
CHANGED
|
@@ -796,6 +796,8 @@ export default defineConfig({
|
|
|
796
796
|
dialect: "postgresql",
|
|
797
797
|
schema: ["./gencow/schema.ts", "./gencow/auth-schema.ts"],
|
|
798
798
|
out: "./gencow/migrations",
|
|
799
|
+
// _system_* 테이블은 Gencow 플랫폼 내부 테이블 — drizzle-kit이 무시하도록 필터링
|
|
800
|
+
tablesFilter: ["!_system_*"],
|
|
799
801
|
// generate는 DB 연결 없이 스키마 파일만 비교하여 SQL 생성.
|
|
800
802
|
// push(로컬 전용)는 DB 연결 필요.
|
|
801
803
|
...(process.env.DATABASE_URL
|
package/core/index.js
CHANGED
|
@@ -1904,12 +1904,77 @@ function createRlsDb(db, userId) {
|
|
|
1904
1904
|
}
|
|
1905
1905
|
|
|
1906
1906
|
// ../core/src/crud.ts
|
|
1907
|
-
import { eq, desc, asc, ilike, or, and, count as drizzleCount, getTableName } from "drizzle-orm";
|
|
1907
|
+
import { eq, ne, gt, gte, lt, lte, desc, asc, like, ilike, inArray, notInArray, or, and, count as drizzleCount, getTableName } from "drizzle-orm";
|
|
1908
1908
|
function detectIdType(column) {
|
|
1909
1909
|
const colType = column.dataType;
|
|
1910
1910
|
if (colType === "string") return v.string();
|
|
1911
1911
|
return v.number();
|
|
1912
1912
|
}
|
|
1913
|
+
var MAX_FILTER_DEPTH = 5;
|
|
1914
|
+
var FILTER_OPS = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "like", "ilike"];
|
|
1915
|
+
function isValidFilterOp(op) {
|
|
1916
|
+
return FILTER_OPS.includes(op);
|
|
1917
|
+
}
|
|
1918
|
+
function applyFilterOp(col, op, value) {
|
|
1919
|
+
switch (op) {
|
|
1920
|
+
case "eq":
|
|
1921
|
+
return eq(col, value);
|
|
1922
|
+
case "ne":
|
|
1923
|
+
return ne(col, value);
|
|
1924
|
+
case "gt":
|
|
1925
|
+
return gt(col, value);
|
|
1926
|
+
case "gte":
|
|
1927
|
+
return gte(col, value);
|
|
1928
|
+
case "lt":
|
|
1929
|
+
return lt(col, value);
|
|
1930
|
+
case "lte":
|
|
1931
|
+
return lte(col, value);
|
|
1932
|
+
case "in":
|
|
1933
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
1934
|
+
return inArray(col, value);
|
|
1935
|
+
case "nin":
|
|
1936
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
1937
|
+
return notInArray(col, value);
|
|
1938
|
+
case "like":
|
|
1939
|
+
if (typeof value !== "string") return void 0;
|
|
1940
|
+
return like(col, value);
|
|
1941
|
+
case "ilike":
|
|
1942
|
+
if (typeof value !== "string") return void 0;
|
|
1943
|
+
return ilike(col, value);
|
|
1944
|
+
default:
|
|
1945
|
+
return void 0;
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
function parseFilterNode(node, table, allowedFields, depth = 0) {
|
|
1949
|
+
if (depth > MAX_FILTER_DEPTH) return void 0;
|
|
1950
|
+
const conditions = [];
|
|
1951
|
+
for (const [key, val] of Object.entries(node)) {
|
|
1952
|
+
if (key === "OR") {
|
|
1953
|
+
if (!Array.isArray(val)) continue;
|
|
1954
|
+
const orConds = val.filter((child) => child != null && typeof child === "object" && !Array.isArray(child)).map((child) => parseFilterNode(child, table, allowedFields, depth + 1)).filter((c) => c !== void 0);
|
|
1955
|
+
if (orConds.length > 0) conditions.push(or(...orConds));
|
|
1956
|
+
continue;
|
|
1957
|
+
}
|
|
1958
|
+
if (key === "AND") {
|
|
1959
|
+
if (!Array.isArray(val)) continue;
|
|
1960
|
+
const andConds = val.filter((child) => child != null && typeof child === "object" && !Array.isArray(child)).map((child) => parseFilterNode(child, table, allowedFields, depth + 1)).filter((c) => c !== void 0);
|
|
1961
|
+
if (andConds.length > 0) conditions.push(and(...andConds));
|
|
1962
|
+
continue;
|
|
1963
|
+
}
|
|
1964
|
+
if (allowedFields && !allowedFields.includes(key)) continue;
|
|
1965
|
+
const col = table[key];
|
|
1966
|
+
if (!col) continue;
|
|
1967
|
+
if (val != null && typeof val === "object" && !Array.isArray(val) && "op" in val) {
|
|
1968
|
+
const { op, value } = val;
|
|
1969
|
+
if (!isValidFilterOp(op)) continue;
|
|
1970
|
+
const cond = applyFilterOp(col, op, value);
|
|
1971
|
+
if (cond) conditions.push(cond);
|
|
1972
|
+
continue;
|
|
1973
|
+
}
|
|
1974
|
+
conditions.push(eq(col, val));
|
|
1975
|
+
}
|
|
1976
|
+
return conditions.length > 0 ? and(...conditions) : void 0;
|
|
1977
|
+
}
|
|
1913
1978
|
function crud(table, options) {
|
|
1914
1979
|
const anyTable = table;
|
|
1915
1980
|
const tableName = getTableName(table);
|
|
@@ -1938,20 +2003,20 @@ function crud(table, options) {
|
|
|
1938
2003
|
);
|
|
1939
2004
|
conditions.push(or(...searchConds));
|
|
1940
2005
|
}
|
|
1941
|
-
if (args?.filters && options?.allowedFilters?.length) {
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
2006
|
+
if (args?.filters && typeof args.filters === "object" && options?.allowedFilters?.length) {
|
|
2007
|
+
const allowedFields = options.allowedFilters;
|
|
2008
|
+
const filterCondition = parseFilterNode(
|
|
2009
|
+
args.filters,
|
|
2010
|
+
anyTable,
|
|
2011
|
+
allowedFields
|
|
2012
|
+
);
|
|
2013
|
+
if (filterCondition) conditions.push(filterCondition);
|
|
1947
2014
|
}
|
|
1948
2015
|
return conditions.length > 0 ? and(...conditions) : void 0;
|
|
1949
2016
|
}
|
|
1950
2017
|
async function fetchListWithTotal(db, whereClause) {
|
|
1951
|
-
const
|
|
1952
|
-
|
|
1953
|
-
db.select({ count: drizzleCount() }).from(anyTable).where(whereClause)
|
|
1954
|
-
]);
|
|
2018
|
+
const data = await db.select().from(anyTable).where(whereClause).orderBy(desc(defaultOrderCol));
|
|
2019
|
+
const countResult = await db.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
1955
2020
|
return { data, total: Number(countResult[0]?.count ?? 0) };
|
|
1956
2021
|
}
|
|
1957
2022
|
const listDef = query(`${prefix}.list`, {
|
|
@@ -1977,10 +2042,8 @@ function crud(table, options) {
|
|
|
1977
2042
|
} else {
|
|
1978
2043
|
orderByClause = desc(defaultOrderCol);
|
|
1979
2044
|
}
|
|
1980
|
-
const
|
|
1981
|
-
|
|
1982
|
-
ctx.db.select({ count: drizzleCount() }).from(anyTable).where(whereClause)
|
|
1983
|
-
]);
|
|
2045
|
+
const results = await ctx.db.select().from(anyTable).where(whereClause).orderBy(orderByClause).limit(limit).offset(offset);
|
|
2046
|
+
const countResult = await ctx.db.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
1984
2047
|
return {
|
|
1985
2048
|
data: results,
|
|
1986
2049
|
total: Number(countResult[0]?.count ?? 0)
|
|
@@ -2071,6 +2134,7 @@ function crud(table, options) {
|
|
|
2071
2134
|
}
|
|
2072
2135
|
export {
|
|
2073
2136
|
GencowValidationError,
|
|
2137
|
+
applyFilterOp,
|
|
2074
2138
|
buildRealtimeCtx,
|
|
2075
2139
|
createRlsDb,
|
|
2076
2140
|
createScheduler,
|
|
@@ -2091,6 +2155,7 @@ export {
|
|
|
2091
2155
|
mutation,
|
|
2092
2156
|
ownerRls,
|
|
2093
2157
|
parseArgs,
|
|
2158
|
+
parseFilterNode,
|
|
2094
2159
|
query,
|
|
2095
2160
|
registerClient,
|
|
2096
2161
|
subscribe,
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -1955,12 +1955,75 @@ var init_rls_db = __esm({
|
|
|
1955
1955
|
});
|
|
1956
1956
|
|
|
1957
1957
|
// ../core/src/crud.ts
|
|
1958
|
-
import { eq, desc, asc, ilike, or, and, count as drizzleCount, getTableName } from "drizzle-orm";
|
|
1958
|
+
import { eq, ne, gt, gte, lt, lte, desc, asc, like, ilike, inArray, notInArray, or, and, count as drizzleCount, getTableName } from "drizzle-orm";
|
|
1959
1959
|
function detectIdType(column) {
|
|
1960
1960
|
const colType = column.dataType;
|
|
1961
1961
|
if (colType === "string") return v.string();
|
|
1962
1962
|
return v.number();
|
|
1963
1963
|
}
|
|
1964
|
+
function isValidFilterOp(op) {
|
|
1965
|
+
return FILTER_OPS.includes(op);
|
|
1966
|
+
}
|
|
1967
|
+
function applyFilterOp(col, op, value) {
|
|
1968
|
+
switch (op) {
|
|
1969
|
+
case "eq":
|
|
1970
|
+
return eq(col, value);
|
|
1971
|
+
case "ne":
|
|
1972
|
+
return ne(col, value);
|
|
1973
|
+
case "gt":
|
|
1974
|
+
return gt(col, value);
|
|
1975
|
+
case "gte":
|
|
1976
|
+
return gte(col, value);
|
|
1977
|
+
case "lt":
|
|
1978
|
+
return lt(col, value);
|
|
1979
|
+
case "lte":
|
|
1980
|
+
return lte(col, value);
|
|
1981
|
+
case "in":
|
|
1982
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
1983
|
+
return inArray(col, value);
|
|
1984
|
+
case "nin":
|
|
1985
|
+
if (!Array.isArray(value) || value.length === 0) return void 0;
|
|
1986
|
+
return notInArray(col, value);
|
|
1987
|
+
case "like":
|
|
1988
|
+
if (typeof value !== "string") return void 0;
|
|
1989
|
+
return like(col, value);
|
|
1990
|
+
case "ilike":
|
|
1991
|
+
if (typeof value !== "string") return void 0;
|
|
1992
|
+
return ilike(col, value);
|
|
1993
|
+
default:
|
|
1994
|
+
return void 0;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
function parseFilterNode(node, table, allowedFields, depth = 0) {
|
|
1998
|
+
if (depth > MAX_FILTER_DEPTH) return void 0;
|
|
1999
|
+
const conditions = [];
|
|
2000
|
+
for (const [key, val] of Object.entries(node)) {
|
|
2001
|
+
if (key === "OR") {
|
|
2002
|
+
if (!Array.isArray(val)) continue;
|
|
2003
|
+
const orConds = val.filter((child) => child != null && typeof child === "object" && !Array.isArray(child)).map((child) => parseFilterNode(child, table, allowedFields, depth + 1)).filter((c) => c !== void 0);
|
|
2004
|
+
if (orConds.length > 0) conditions.push(or(...orConds));
|
|
2005
|
+
continue;
|
|
2006
|
+
}
|
|
2007
|
+
if (key === "AND") {
|
|
2008
|
+
if (!Array.isArray(val)) continue;
|
|
2009
|
+
const andConds = val.filter((child) => child != null && typeof child === "object" && !Array.isArray(child)).map((child) => parseFilterNode(child, table, allowedFields, depth + 1)).filter((c) => c !== void 0);
|
|
2010
|
+
if (andConds.length > 0) conditions.push(and(...andConds));
|
|
2011
|
+
continue;
|
|
2012
|
+
}
|
|
2013
|
+
if (allowedFields && !allowedFields.includes(key)) continue;
|
|
2014
|
+
const col = table[key];
|
|
2015
|
+
if (!col) continue;
|
|
2016
|
+
if (val != null && typeof val === "object" && !Array.isArray(val) && "op" in val) {
|
|
2017
|
+
const { op, value } = val;
|
|
2018
|
+
if (!isValidFilterOp(op)) continue;
|
|
2019
|
+
const cond = applyFilterOp(col, op, value);
|
|
2020
|
+
if (cond) conditions.push(cond);
|
|
2021
|
+
continue;
|
|
2022
|
+
}
|
|
2023
|
+
conditions.push(eq(col, val));
|
|
2024
|
+
}
|
|
2025
|
+
return conditions.length > 0 ? and(...conditions) : void 0;
|
|
2026
|
+
}
|
|
1964
2027
|
function crud(table, options) {
|
|
1965
2028
|
const anyTable = table;
|
|
1966
2029
|
const tableName = getTableName(table);
|
|
@@ -1989,20 +2052,20 @@ function crud(table, options) {
|
|
|
1989
2052
|
);
|
|
1990
2053
|
conditions.push(or(...searchConds));
|
|
1991
2054
|
}
|
|
1992
|
-
if (args?.filters && options?.allowedFilters?.length) {
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
2055
|
+
if (args?.filters && typeof args.filters === "object" && options?.allowedFilters?.length) {
|
|
2056
|
+
const allowedFields = options.allowedFilters;
|
|
2057
|
+
const filterCondition = parseFilterNode(
|
|
2058
|
+
args.filters,
|
|
2059
|
+
anyTable,
|
|
2060
|
+
allowedFields
|
|
2061
|
+
);
|
|
2062
|
+
if (filterCondition) conditions.push(filterCondition);
|
|
1998
2063
|
}
|
|
1999
2064
|
return conditions.length > 0 ? and(...conditions) : void 0;
|
|
2000
2065
|
}
|
|
2001
2066
|
async function fetchListWithTotal(db, whereClause) {
|
|
2002
|
-
const
|
|
2003
|
-
|
|
2004
|
-
db.select({ count: drizzleCount() }).from(anyTable).where(whereClause)
|
|
2005
|
-
]);
|
|
2067
|
+
const data = await db.select().from(anyTable).where(whereClause).orderBy(desc(defaultOrderCol));
|
|
2068
|
+
const countResult = await db.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
2006
2069
|
return { data, total: Number(countResult[0]?.count ?? 0) };
|
|
2007
2070
|
}
|
|
2008
2071
|
const listDef = query(`${prefix}.list`, {
|
|
@@ -2028,10 +2091,8 @@ function crud(table, options) {
|
|
|
2028
2091
|
} else {
|
|
2029
2092
|
orderByClause = desc(defaultOrderCol);
|
|
2030
2093
|
}
|
|
2031
|
-
const
|
|
2032
|
-
|
|
2033
|
-
ctx.db.select({ count: drizzleCount() }).from(anyTable).where(whereClause)
|
|
2034
|
-
]);
|
|
2094
|
+
const results = await ctx.db.select().from(anyTable).where(whereClause).orderBy(orderByClause).limit(limit).offset(offset);
|
|
2095
|
+
const countResult = await ctx.db.select({ count: drizzleCount() }).from(anyTable).where(whereClause);
|
|
2035
2096
|
return {
|
|
2036
2097
|
data: results,
|
|
2037
2098
|
total: Number(countResult[0]?.count ?? 0)
|
|
@@ -2120,11 +2181,14 @@ function crud(table, options) {
|
|
|
2120
2181
|
remove: removeDef
|
|
2121
2182
|
};
|
|
2122
2183
|
}
|
|
2184
|
+
var MAX_FILTER_DEPTH, FILTER_OPS;
|
|
2123
2185
|
var init_crud = __esm({
|
|
2124
2186
|
"../core/src/crud.ts"() {
|
|
2125
2187
|
"use strict";
|
|
2126
2188
|
init_reactive();
|
|
2127
2189
|
init_v();
|
|
2190
|
+
MAX_FILTER_DEPTH = 5;
|
|
2191
|
+
FILTER_OPS = ["eq", "ne", "gt", "gte", "lt", "lte", "in", "nin", "like", "ilike"];
|
|
2128
2192
|
}
|
|
2129
2193
|
});
|
|
2130
2194
|
|
|
@@ -2132,6 +2196,7 @@ var init_crud = __esm({
|
|
|
2132
2196
|
var src_exports = {};
|
|
2133
2197
|
__export(src_exports, {
|
|
2134
2198
|
GencowValidationError: () => GencowValidationError,
|
|
2199
|
+
applyFilterOp: () => applyFilterOp,
|
|
2135
2200
|
buildRealtimeCtx: () => buildRealtimeCtx,
|
|
2136
2201
|
createRlsDb: () => createRlsDb,
|
|
2137
2202
|
createScheduler: () => createScheduler,
|
|
@@ -2152,6 +2217,7 @@ __export(src_exports, {
|
|
|
2152
2217
|
mutation: () => mutation,
|
|
2153
2218
|
ownerRls: () => ownerRls,
|
|
2154
2219
|
parseArgs: () => parseArgs,
|
|
2220
|
+
parseFilterNode: () => parseFilterNode,
|
|
2155
2221
|
query: () => query,
|
|
2156
2222
|
registerClient: () => registerClient,
|
|
2157
2223
|
subscribe: () => subscribe,
|