FreeGrid Node.js server helper
==============================

freegrid-query.js   one ESM file, no dependencies, Node 18+

  readRequest(body)    reads restDatasource's request (paging, sort, filters, quick filter, advanced filter, grouping)
  createQuery(opts)    turns it into SQL: { where, orderBy, page, args, countArgs, group }
  toRows(rows, total)  the { rows, total } response, dates as "YYYY-MM-DD"
  readSave(body)       { created, updated, deleted } from saveChanges()

  const query = createQuery({
    columns: { orderNo: { sql: 'ORDER_NO', type: 'text' }, qty: { sql: 'QTY', type: 'number' } },
    dialect: 'limit',          // or 'standard' (OFFSET ? ROWS FETCH NEXT ? ROWS ONLY)
    placeholder: 'question',   // or 'dollar' for pg ($1, $2...)
  });
  const sql = query.build(readRequest(req.body));
  db.prepare('SELECT ... FROM ORDERS' + sql.where + sql.orderBy + sql.page).all(sql.args);

Works with better-sqlite3, mysql2, pg and other drivers; Express, Fastify, Koa or plain node:http.

Only the columns you list can be sorted, filtered or grouped; every value is a bound parameter.
A bad number or date, an unknown filter op or too many values raises GridError: answer 400 { message }.
LIKE searches use ESCAPE '!', so % and _ typed in a filter are matched as text.

What the grid sends and what to answer, with a full example server:
  https://freegrid.app/docs/server/     (English)
  https://freegrid.app/ko/docs/server/  (한국어)

Use is covered by the FreeGrid license: https://freegrid.app/pricing/
