@promind/honey 1.51.0 → 1.52.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/module.js CHANGED
@@ -166,6 +166,7 @@ var $486a90d70dc9bc85$export$2e2bcd8739ae039 = $486a90d70dc9bc85$var$ExpressApp;
166
166
 
167
167
 
168
168
 
169
+
169
170
  const $5f19ecb00e146b8f$var$formatters = {
170
171
  string: String,
171
172
  number: Number,
@@ -238,6 +239,60 @@ const $5f19ecb00e146b8f$export$390602d0ccf68ce6 = (filterParams, filter, req)=>{
238
239
  }
239
240
  return result;
240
241
  };
242
+ /**
243
+ * Returns true if a FilterNode is an $and group.
244
+ */ function $5f19ecb00e146b8f$var$isAndGroup(node) {
245
+ return '$and' in node;
246
+ }
247
+ function $5f19ecb00e146b8f$export$d623e78f08e28a37(node, query) {
248
+ if ($5f19ecb00e146b8f$var$isAndGroup(node)) {
249
+ const resolvedChildren = [];
250
+ for (const child of node.$and){
251
+ const resolved = $5f19ecb00e146b8f$export$d623e78f08e28a37(child, query);
252
+ if (resolved === null) return null; // missing required param — whole AND fails
253
+ resolvedChildren.push(resolved);
254
+ }
255
+ return {
256
+ $and: resolvedChildren
257
+ };
258
+ }
259
+ // Leaf condition — there should be exactly one field key
260
+ const condition = node;
261
+ const entries = Object.entries(condition);
262
+ if (entries.length === 0) return null;
263
+ const [field, spec] = entries[0];
264
+ let resolvedValue;
265
+ if (spec.value === 'as-is') {
266
+ // Use overrideValue directly (call if function)
267
+ if (typeof spec.overrideValue === 'function') resolvedValue = spec.overrideValue();
268
+ else resolvedValue = spec.overrideValue;
269
+ } else {
270
+ // Read from query; if missing, skip this node
271
+ const rawValue = query[field];
272
+ if (typeof rawValue === 'undefined') return null;
273
+ const valueFormatter = $5f19ecb00e146b8f$var$formatters[spec.value];
274
+ resolvedValue = valueFormatter ? valueFormatter(rawValue) : rawValue;
275
+ }
276
+ return {
277
+ field: field,
278
+ operator: spec.operator,
279
+ value: resolvedValue
280
+ };
281
+ }
282
+ function $5f19ecb00e146b8f$export$da09f390d3fe1499(presetName, presets, query) {
283
+ if (!presetName) return null;
284
+ if (!presets || !(presetName in presets)) throw new (0, $eb799189062d1297$export$2e2bcd8739ae039)(`Unknown filter preset: "${presetName}"`, 400);
285
+ const preset = presets[presetName];
286
+ const orBranches = [];
287
+ for (const node of preset){
288
+ const resolved = $5f19ecb00e146b8f$export$d623e78f08e28a37(node, query);
289
+ if (resolved !== null) orBranches.push(resolved);
290
+ }
291
+ if (orBranches.length === 0) return null;
292
+ return {
293
+ $or: orBranches
294
+ };
295
+ }
241
296
 
242
297
 
243
298
  function $e54b5bd1ebc8718c$export$2e2bcd8739ae039({ db: db, resource: resource, params: params, message: message, processResponseData: processResponseData, processErrorResponse: processErrorResponse }) {
@@ -337,7 +392,7 @@ function $68f7d8a50ef9b68c$export$7b6dde2038c44e91({ db: db, resource: resource,
337
392
  }
338
393
  };
339
394
  }
340
- function $68f7d8a50ef9b68c$export$7b48d281d65870ad({ db: db, resource: resource, fields: fields, filterQuery: filterQuery, format: format, processResponseData: processResponseData, processErrorResponse: processErrorResponse, joins: joins, shouldErrorOnNotFound: shouldErrorOnNotFound }) {
395
+ function $68f7d8a50ef9b68c$export$7b48d281d65870ad({ db: db, resource: resource, fields: fields, filterQuery: filterQuery, format: format, processResponseData: processResponseData, processErrorResponse: processErrorResponse, joins: joins, shouldErrorOnNotFound: shouldErrorOnNotFound, filterPresets: filterPresets }) {
341
396
  return async function(req, res, next) {
342
397
  try {
343
398
  const page = Number(req.query.page);
@@ -347,7 +402,10 @@ function $68f7d8a50ef9b68c$export$7b48d281d65870ad({ db: db, resource: resource,
347
402
  limit: limit || 10
348
403
  } : undefined;
349
404
  const filter = filterQuery && (0, $5f19ecb00e146b8f$export$390602d0ccf68ce6)(req.query, filterQuery, req);
350
- let data = await db.read(resource, fields, filter, paginate, format, joins);
405
+ // Resolve optional ?preset= query param additive, does not touch filter
406
+ const presetName = typeof req.query.preset === 'string' ? req.query.preset : undefined;
407
+ const resolvedOrGroup = (0, $5f19ecb00e146b8f$export$da09f390d3fe1499)(presetName, filterPresets, req.query);
408
+ let data = await db.read(resource, fields, filter, paginate, format, joins, resolvedOrGroup ?? undefined);
351
409
  let total = 0;
352
410
  if (paginate) {
353
411
  total = Number(data[0]?.['honey_total_count'] || 0);
@@ -711,7 +769,7 @@ class $c492fd1e48c9c1cf$export$2e2bcd8739ae039 {
711
769
  exitMiddleware: exitMiddleware
712
770
  });
713
771
  }
714
- get({ resource: resource, fields: fields, filter: filter, format: format, middleware: middleware, pathOverride: pathOverride, exitMiddleware: exitMiddleware, methodOverride: methodOverride, processResponseData: processResponseData, processErrorResponse: processErrorResponse, table: table, joins: joins, shouldErrorOnNotFound: shouldErrorOnNotFound = true }) {
772
+ get({ resource: resource, fields: fields, filter: filter, format: format, middleware: middleware, pathOverride: pathOverride, exitMiddleware: exitMiddleware, methodOverride: methodOverride, processResponseData: processResponseData, processErrorResponse: processErrorResponse, table: table, joins: joins, shouldErrorOnNotFound: shouldErrorOnNotFound = true, filterPresets: filterPresets }) {
715
773
  const path = pathOverride || `/${resource}`;
716
774
  resource = table || resource;
717
775
  const controller = (0, $68f7d8a50ef9b68c$export$7b48d281d65870ad)({
@@ -723,7 +781,8 @@ class $c492fd1e48c9c1cf$export$2e2bcd8739ae039 {
723
781
  processResponseData: processResponseData,
724
782
  processErrorResponse: processErrorResponse,
725
783
  joins: joins,
726
- shouldErrorOnNotFound: shouldErrorOnNotFound
784
+ shouldErrorOnNotFound: shouldErrorOnNotFound,
785
+ filterPresets: filterPresets
727
786
  });
728
787
  this.crud({
729
788
  method: methodOverride || 'get',
@@ -941,6 +1000,39 @@ const $bf24bb53f537fa46$export$be3654efea499c50 = (results, joins = [])=>{
941
1000
  });
942
1001
  return nestedResults;
943
1002
  };
1003
+ // ── Preset condition group helpers (additive — applyFilter unchanged) ────────
1004
+ /**
1005
+ * Returns true if a ResolvedNode is an AND group.
1006
+ */ function $bf24bb53f537fa46$var$isResolvedAndGroup(node) {
1007
+ return '$and' in node;
1008
+ }
1009
+ /**
1010
+ * Recursively applies a single ResolvedNode to a knex builder.
1011
+ * AND groups are applied as nested `.where()` calls; leaves are plain conditions.
1012
+ */ function $bf24bb53f537fa46$var$applyConditionGroup(builder, node) {
1013
+ if ($bf24bb53f537fa46$var$isResolvedAndGroup(node)) builder.where((andBuilder)=>{
1014
+ node.$and.forEach((child)=>$bf24bb53f537fa46$var$applyConditionGroup(andBuilder, child));
1015
+ });
1016
+ else {
1017
+ const leaf = node;
1018
+ if ([
1019
+ 'like',
1020
+ 'not like'
1021
+ ].includes(leaf.operator)) builder.where(leaf.field, leaf.operator === 'like' ? 'ilike' : 'not ilike', `%${leaf.value}%`);
1022
+ else builder.where(leaf.field, leaf.operator, leaf.value);
1023
+ }
1024
+ }
1025
+ const $bf24bb53f537fa46$export$647274967602ffc7 = (query, orGroup)=>{
1026
+ query.where((outerBuilder)=>{
1027
+ orGroup.$or.forEach((branch, index)=>{
1028
+ if (index === 0) $bf24bb53f537fa46$var$applyConditionGroup(outerBuilder, branch);
1029
+ else outerBuilder.orWhere((branchBuilder)=>{
1030
+ $bf24bb53f537fa46$var$applyConditionGroup(branchBuilder, branch);
1031
+ });
1032
+ });
1033
+ });
1034
+ return query;
1035
+ };
944
1036
  const $bf24bb53f537fa46$export$1b001418845b471c = (query, filter = {})=>{
945
1037
  Object.entries(filter).forEach(([field, data])=>{
946
1038
  if (field === '$or') query.where((builder)=>{
@@ -973,7 +1065,7 @@ const $bf24bb53f537fa46$export$53b231d60a00019b = (table, data)=>{
973
1065
  replacements: replacements
974
1066
  };
975
1067
  };
976
- const $bf24bb53f537fa46$export$60d67f341ad4c4 = (table, fields, filter, paginate, format, joins)=>{
1068
+ const $bf24bb53f537fa46$export$60d67f341ad4c4 = (table, fields, filter, paginate, format, joins, resolvedOrGroup)=>{
977
1069
  const knex = (0, $254994df946b8040$export$a858f29c02f648c6)();
978
1070
  // Handle field aliasing for joins
979
1071
  const formattedFields = fields.map((field)=>{
@@ -989,24 +1081,25 @@ const $bf24bb53f537fa46$export$60d67f341ad4c4 = (table, fields, filter, paginate
989
1081
  // Add total count if pagination is needed
990
1082
  if (paginate) q = knex(table).select([
991
1083
  ...formattedFields.map((field)=>{
992
- // formattedFields entries are either plain strings (e.g. "users.id") or
993
- // knex.raw objects (produced above for join-aliased fields).
994
- // For plain strings we must NOT use ?? binding on a dotted value like
995
- // "users.id" because knex wraps the whole string in double-quotes,
996
- // producing the invalid identifier "users.id" instead of "users"."id".
997
- // Passing the string directly to knex.raw as a literal SQL fragment is safe
998
- // because formattedFields values are generated internally (not user input).
999
- if (typeof field !== 'string') // Already a knex.raw object (join-aliased field) — use as-is
1084
+ // formattedFields entries are either plain strings (e.g. "users.id",
1085
+ // "users.firstName", or "bookings.scheduledAt as bookings__hj__scheduledAt")
1086
+ // or knex.raw objects. We must double-quote each identifier part so that
1087
+ // mixed-case column names (e.g. firstName, scheduledAt) are preserved and
1088
+ // not lowercased by PostgreSQL.
1089
+ if (typeof field !== 'string') // Already a knex.raw object use as-is
1000
1090
  return field;
1001
1091
  const [actualField, alias] = field.split(' as ');
1002
- if (alias) // Has an explicit alias: quote each part of the column reference
1003
- // then attach the alias as a plain identifier
1004
- return knex.raw(`${actualField} as ??`, [
1092
+ // Quote each part of a dotted identifier (e.g. "table"."column")
1093
+ const quotedField = actualField.includes('.') ? knex.raw('??.??', actualField.split('.')) : knex.raw('??', [
1094
+ actualField
1095
+ ]);
1096
+ if (alias) // Has an explicit alias: emit "table"."column" as "alias"
1097
+ return knex.raw(`${quotedField.toSQL().sql} as ??`, [
1098
+ ...quotedField.toSQL().bindings,
1005
1099
  alias
1006
1100
  ]);
1007
- // No alias: emit the field as a literal SQL fragment so dotted names
1008
- // like "users.id" are kept as-is and not collapsed into one quoted token
1009
- return knex.raw(field);
1101
+ // No alias: just emit the properly-quoted column reference
1102
+ return quotedField;
1010
1103
  }),
1011
1104
  knex.raw('count(*) OVER() AS honey_total_count')
1012
1105
  ]);
@@ -1041,6 +1134,8 @@ const $bf24bb53f537fa46$export$60d67f341ad4c4 = (table, fields, filter, paginate
1041
1134
  });
1042
1135
  // Apply filters
1043
1136
  if (filter) q = $bf24bb53f537fa46$export$1b001418845b471c(q, filter);
1137
+ // Apply resolved preset OR group (additive — only when a preset is active)
1138
+ if (resolvedOrGroup) q = $bf24bb53f537fa46$export$647274967602ffc7(q, resolvedOrGroup);
1044
1139
  // Apply sorting
1045
1140
  if (format?.sort && format.sortField) q.orderBy(format.sortField, format.sort);
1046
1141
  // Apply pagination
@@ -1128,8 +1223,8 @@ const $bf24bb53f537fa46$export$65b914f34962db1e = (table, data, conflictTarget,
1128
1223
 
1129
1224
 
1130
1225
  class $094f143c50bc237e$export$2e2bcd8739ae039 {
1131
- async read(table, fields, filter, paginate, format, joins) {
1132
- const { query: query, replacements: replacements } = (0, $bf24bb53f537fa46$export$60d67f341ad4c4)(table, fields, filter, paginate, format, joins);
1226
+ async read(table, fields, filter, paginate, format, joins, resolvedOrGroup) {
1227
+ const { query: query, replacements: replacements } = (0, $bf24bb53f537fa46$export$60d67f341ad4c4)(table, fields, filter, paginate, format, joins, resolvedOrGroup);
1133
1228
  const result = await (0, $b05f0a427b6f271e$export$2e2bcd8739ae039).db.query(query, {
1134
1229
  type: (0, $dTx2I$QueryTypes).SELECT,
1135
1230
  raw: true,