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 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
- for (const [key, value] of Object.entries(args.filters)) {
1943
- if (options.allowedFilters.includes(key) && anyTable[key]) {
1944
- conditions.push(eq(anyTable[key], value));
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 [data, countResult] = await Promise.all([
1952
- db.select().from(anyTable).where(whereClause).orderBy(desc(defaultOrderCol)),
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 [results, countResult] = await Promise.all([
1981
- ctx.db.select().from(anyTable).where(whereClause).orderBy(orderByClause).limit(limit).offset(offset),
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gencow",
3
- "version": "0.1.106",
3
+ "version": "0.1.108",
4
4
  "description": "Gencow — AI Backend Engine",
5
5
  "type": "module",
6
6
  "bin": {
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
- for (const [key, value] of Object.entries(args.filters)) {
1994
- if (options.allowedFilters.includes(key) && anyTable[key]) {
1995
- conditions.push(eq(anyTable[key], value));
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 [data, countResult] = await Promise.all([
2003
- db.select().from(anyTable).where(whereClause).orderBy(desc(defaultOrderCol)),
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 [results, countResult] = await Promise.all([
2032
- ctx.db.select().from(anyTable).where(whereClause).orderBy(orderByClause).limit(limit).offset(offset),
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,