@dwtechs/antity-pgsql 0.17.1 → 0.17.2

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
@@ -123,6 +123,24 @@ router.get("/:id/history", ..., entity.getHistory);
123
123
 
124
124
  ```
125
125
 
126
+ ### Expected table structure
127
+
128
+ ```sql
129
+ CREATE TABLE IF NOT EXISTS "service" (
130
+ id SERIAL PRIMARY KEY,
131
+ name varchar(20) NOT NULL,
132
+ pattern TEXT,
133
+ archived BOOLEAN DEFAULT FALSE,
134
+ "archivedAt" TIMESTAMP,
135
+ "creatorId" INT,
136
+ "creatorName" TEXT,
137
+ "updaterId" INT,
138
+ "updaterName" TEXT,
139
+ "createdAt" TIMESTAMP DEFAULT NOW(),
140
+ "updatedAt" TIMESTAMP NULL
141
+ );
142
+ ```
143
+
126
144
  ## API Reference
127
145
 
128
146
 
@@ -311,10 +329,10 @@ Using substacks simplifies your route definitions and ensures consistent data pr
311
329
  ### Query Methods
312
330
 
313
331
  - **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` 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.
332
+ - **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 `creatorName` for audit tracking. Supports `RETURNING` clause via the `rtn` parameter.
333
+ - **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 `updaterName` for audit tracking.
334
+ - **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 `creatorName` for audit tracking. Supports `RETURNING` clause via the `rtn` parameter.
335
+ - **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 `updaterName` for audit tracking. Does not require an `archived` field in the rows — it is set directly in the SQL.
318
336
  - **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
337
  - **delete()**: Deletes rows by their IDs. Expects `req.body.rows` to be an array of objects with `id` property: `[{id: 1}, {id: 2}]`
320
338
  - **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 +394,7 @@ res.locals.sync // { inserted: 1, updated: 1, deleted: 1 }
376
394
  - **Atomic**: All insert / update / delete operations are wrapped in a single transaction.
377
395
  - **Filter scope**: When `filters` are provided, only rows matching the filter are considered "managed". Rows outside the filter are never touched.
378
396
  - **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 as `creatorId`/`name` and to updates as `updaterId`/`name` for audit tracking.
397
+ - **Consumer tracking**: `consumer.id` and `consumer.nickname` from `res.locals.consumer` are forwarded to inserts as `creatorId`/`creatorName` and to updates as `updaterId`/`updaterName` for audit tracking.
380
398
 
381
399
  ### Upsert (Insert or Update)
382
400
 
@@ -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 += `, creatorId, name`;
308
+ cols += `, "creatorId", "creatorName"`;
309
309
  }
310
310
  let query = `INSERT INTO ${quoteIfUppercase(schema)}.${quoteIfUppercase(table)} (${cols}) VALUES `;
311
311
  const args = [];
@@ -365,7 +365,7 @@ class Update {
365
365
  for (const p of propsToUse) {
366
366
  if (rows[0][p] === undefined)
367
367
  continue;
368
- const colName = p === "consumerId" ? "updaterId" : p === "consumerName" ? "name" : quoteIfUppercase(p);
368
+ const colName = p === "consumerId" ? '"updaterId"' : p === "consumerName" ? '"updaterName"' : quoteIfUppercase(p);
369
369
  query += `${colName} = CASE `;
370
370
  for (let j = 0; j < l; j++) {
371
371
  const row = rows[j];
@@ -413,9 +413,9 @@ class Upsert {
413
413
  let cols = this._cols;
414
414
  if (consumerId !== undefined && consumerName !== undefined) {
415
415
  propsToUse.push("consumerId", "consumerName");
416
- quotedPropsToUse.push(`creatorId`, `name`);
416
+ quotedPropsToUse.push(`"creatorId"`, `"creatorName"`);
417
417
  nbProps += 2;
418
- cols += `, creatorId, name`;
418
+ cols += `, "creatorId", "creatorName"`;
419
419
  }
420
420
  const conflictColumns = Array.isArray(conflictTarget)
421
421
  ? conflictTarget.map(col => quoteIfUppercase(col)).join(", ")
@@ -472,7 +472,7 @@ class Archive {
472
472
  const args = rows.map(row => row.id);
473
473
  let query = `UPDATE ${quoteIfUppercase(schema)}.${quoteIfUppercase(table)} SET archived = true`;
474
474
  if (consumerId !== undefined && consumerName !== undefined) {
475
- query += `, updaterId = $${l + 1}, name = $${l + 2}`;
475
+ query += `, "updaterId" = $${l + 1}, "updaterName" = $${l + 2}`;
476
476
  args.push(consumerId, consumerName);
477
477
  }
478
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.17.1",
3
+ "version": "0.17.2",
4
4
  "description": "Open source library to add PostgreSQL support to @dwtechs/Antity entities.",
5
5
  "keywords": [
6
6
  "entities"