@dwtechs/antity-pgsql 0.16.0 → 0.17.1

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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
  [![License: MIT](https://img.shields.io/npm/l/@dwtechs/antity-pgsql.svg?color=brightgreen)](https://opensource.org/licenses/MIT)
3
3
  [![npm version](https://badge.fury.io/js/%40dwtechs%2Fantity-pgsql.svg)](https://www.npmjs.com/package/@dwtechs/antity-pgsql)
4
4
  [![last version release date](https://img.shields.io/github/release-date/DWTechs/Antity-pgsql.js)](https://www.npmjs.com/package/@dwtechs/antity-pgsql)
5
- ![Jest:coverage](https://img.shields.io/badge/Jest:coverage-54%25-brightgreen.svg)
5
+ ![Jest:coverage](https://img.shields.io/badge/Jest:coverage-87%25-brightgreen.svg)
6
6
 
7
7
  - [Synopsis](#synopsis)
8
8
  - [Support](#support)
@@ -311,10 +311,10 @@ Using substacks simplifies your route definitions and ensures consistent data pr
311
311
  ### Query Methods
312
312
 
313
313
  - **query.select()**: Generates a SELECT query. When the `rows` parameter is provided (not null), pagination is automatically enabled and the query includes `COUNT(*) OVER () AS total` to return the total number of rows. The total count is extracted from results and returned separately from the row data.
314
- - **query.insert()**: Generates an INSERT query. Accepts an array of `Row` objects with properties matching the entity definition. Consumer fields are appended directly to the query arguments — row objects are **not mutated**. Optionally appends `consumer.id` and `consumer.nickname` for history tracking. Supports `RETURNING` clause via the `rtn` parameter.
315
- - **query.update()**: Generates an UPDATE query using CASE statements. Accepts an array of `Row` objects with `id` property. Optionally appends `consumer.id` and `consumer.nickname` for history tracking.
316
- - **query.upsert()**: Generates an INSERT ... ON CONFLICT ... DO UPDATE query. (See [Upsert](#upsert-insert-or-update) section below.) Accepts an array of `Row` objects and a `conflictTarget` (single column name or array of column names) that defines uniqueness. If a conflict occurs on the specified column(s), the row is updated; otherwise, it is inserted. Properties are automatically included if they have both INSERT and UPDATE operations. Consumer fields are appended directly to the query arguments — row objects are **not mutated**. Optionally appends `consumer.id` and `consumer.nickname` for history tracking. Supports `RETURNING` clause via the `rtn` parameter.
317
- - **query.archive()**: Generates a simplified `UPDATE ... SET archived = true WHERE id IN (...)` query. Accepts an array of `Row` objects with `id` property. Optionally appends `consumer.id` and `consumer.nickname` for history tracking. Does not require an `archived` field in the rows — it is set directly in the SQL.
314
+ - **query.insert()**: Generates an INSERT query. Accepts an array of `Row` objects with properties matching the entity definition. Consumer fields are appended directly to the query arguments — row objects are **not mutated**. Optionally appends `consumer.id` as `creatorId` and `consumer.nickname` as `name` for audit tracking. Supports `RETURNING` clause via the `rtn` parameter.
315
+ - **query.update()**: Generates an UPDATE query using CASE statements. Accepts an array of `Row` objects with `id` property. Optionally appends `consumer.id` as `updaterId` and `consumer.nickname` as `name` for audit tracking.
316
+ - **query.upsert()**: Generates an INSERT ... ON CONFLICT ... DO UPDATE query. (See [Upsert](#upsert-insert-or-update) section below.) Accepts an array of `Row` objects and a `conflictTarget` (single column name or array of column names) that defines uniqueness. If a conflict occurs on the specified column(s), the row is updated; otherwise, it is inserted. Properties are automatically included if they have both INSERT and UPDATE operations. Consumer fields are appended directly to the query arguments — row objects are **not mutated**. Optionally appends `consumer.id` as `creatorId` and `consumer.nickname` as `name` for audit tracking. Supports `RETURNING` clause via the `rtn` parameter.
317
+ - **query.archive()**: Generates a simplified `UPDATE ... SET archived = true WHERE id IN (...)` query. Accepts an array of `Row` objects with `id` property. Optionally appends `consumer.id` as `updaterId` and `consumer.nickname` as `name` for audit tracking. Does not require an `archived` field in the rows — it is set directly in the SQL.
318
318
  - **sync()**: Atomically synchronises the table with the provided rows inside a single PostgreSQL transaction. Missing rows are inserted, existing rows are updated, and rows absent from the list are deleted. Accepts optional `idField` (default `'id'`) and `filters` to restrict the scope of managed rows. Stores the result in `res.locals.rows` and a summary `{ inserted, updated, deleted }` in `res.locals.sync`.
319
319
  - **delete()**: Deletes rows by their IDs. Expects `req.body.rows` to be an array of objects with `id` property: `[{id: 1}, {id: 2}]`
320
320
  - **deleteArchive()**: Deletes archived rows that were archived before a specific date using a PostgreSQL SECURITY DEFINER function. Expects `req.body.date` to be a Date object.
@@ -376,7 +376,7 @@ res.locals.sync // { inserted: 1, updated: 1, deleted: 1 }
376
376
  - **Atomic**: All insert / update / delete operations are wrapped in a single transaction.
377
377
  - **Filter scope**: When `filters` are provided, only rows matching the filter are considered "managed". Rows outside the filter are never touched.
378
378
  - **Property selection**: Insert uses `INSERT` properties; update uses `UPDATE` properties — same as the standalone `add` and `update` middlewares.
379
- - **Consumer tracking**: `consumer.id` and `consumer.nickname` from `res.locals.consumer` are forwarded to inserts and updates for history tracking.
379
+ - **Consumer tracking**: `consumer.id` and `consumer.nickname` from `res.locals.consumer` are forwarded to inserts as `creatorId`/`name` and to updates as `updaterId`/`name` for audit tracking.
380
380
 
381
381
  ### Upsert (Insert or Update)
382
382
 
@@ -440,13 +440,13 @@ const { query, args } = entity.query.upsert(
440
440
  'RETURNING id' // return clause (optional)
441
441
  );
442
442
  // Generates:
443
- // INSERT INTO public.users (name, email, "consumerId", "consumerName")
443
+ // INSERT INTO public.users (name, email, creatorId, name)
444
444
  // VALUES ($1, $2, $3, $4)
445
445
  // ON CONFLICT (id) DO UPDATE SET
446
446
  // name = EXCLUDED.name,
447
447
  // email = EXCLUDED.email,
448
- // "consumerId" = EXCLUDED."consumerId",
449
- // "consumerName" = EXCLUDED."consumerName"
448
+ // creatorId = EXCLUDED.creatorId,
449
+ // name = EXCLUDED.name
450
450
  // RETURNING id
451
451
  ```
452
452
 
@@ -305,7 +305,7 @@ class Insert {
305
305
  if (consumerId !== undefined && consumerName !== undefined) {
306
306
  propsToUse.push("consumerId", "consumerName");
307
307
  nbProps += 2;
308
- cols += `, "consumerId", "consumerName"`;
308
+ cols += `, creatorId, name`;
309
309
  }
310
310
  let query = `INSERT INTO ${quoteIfUppercase(schema)}.${quoteIfUppercase(table)} (${cols}) VALUES `;
311
311
  const args = [];
@@ -365,13 +365,14 @@ class Update {
365
365
  for (const p of propsToUse) {
366
366
  if (rows[0][p] === undefined)
367
367
  continue;
368
- query += `${quoteIfUppercase(p)} = CASE `;
368
+ const colName = p === "consumerId" ? "updaterId" : p === "consumerName" ? "name" : quoteIfUppercase(p);
369
+ query += `${colName} = CASE `;
369
370
  for (let j = 0; j < l; j++) {
370
371
  const row = rows[j];
371
372
  query += `WHEN id = $${j + 1} THEN $${i++} `;
372
373
  args.push(row[p]);
373
374
  }
374
- query += `ELSE ${quoteIfUppercase(p)} END, `;
375
+ query += `ELSE ${colName} END, `;
375
376
  }
376
377
  query = `${query.slice(0, -2)} WHERE id IN ${$i(l, 0)}`;
377
378
  return { query, args };
@@ -412,9 +413,9 @@ class Upsert {
412
413
  let cols = this._cols;
413
414
  if (consumerId !== undefined && consumerName !== undefined) {
414
415
  propsToUse.push("consumerId", "consumerName");
415
- quotedPropsToUse.push(`"consumerId"`, `"consumerName"`);
416
+ quotedPropsToUse.push(`creatorId`, `name`);
416
417
  nbProps += 2;
417
- cols += `, "consumerId", "consumerName"`;
418
+ cols += `, creatorId, name`;
418
419
  }
419
420
  const conflictColumns = Array.isArray(conflictTarget)
420
421
  ? conflictTarget.map(col => quoteIfUppercase(col)).join(", ")
@@ -471,7 +472,7 @@ class Archive {
471
472
  const args = rows.map(row => row.id);
472
473
  let query = `UPDATE ${quoteIfUppercase(schema)}.${quoteIfUppercase(table)} SET archived = true`;
473
474
  if (consumerId !== undefined && consumerName !== undefined) {
474
- query += `, ${quoteIfUppercase("consumerId")} = $${l + 1}, ${quoteIfUppercase("consumerName")} = $${l + 2}`;
475
+ query += `, updaterId = $${l + 1}, name = $${l + 2}`;
475
476
  args.push(consumerId, consumerName);
476
477
  }
477
478
  query += ` WHERE id IN ${$i(l, 0)}`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dwtechs/antity-pgsql",
3
- "version": "0.16.0",
3
+ "version": "0.17.1",
4
4
  "description": "Open source library to add PostgreSQL support to @dwtechs/Antity entities.",
5
5
  "keywords": [
6
6
  "entities"