@atscript/moost-db 0.1.83 → 0.1.85
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/index.cjs +64 -3
- package/dist/index.d.cts +14 -0
- package/dist/index.d.mts +14 -0
- package/dist/index.mjs +64 -3
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -1510,7 +1510,8 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
|
|
|
1510
1510
|
fields,
|
|
1511
1511
|
type: this.getSerializedType(),
|
|
1512
1512
|
actions: this.buildActions(),
|
|
1513
|
-
crud: this.buildCrud()
|
|
1513
|
+
crud: this.buildCrud(),
|
|
1514
|
+
versionColumn: this.readable.versionColumn
|
|
1514
1515
|
};
|
|
1515
1516
|
}
|
|
1516
1517
|
buildCrud() {
|
|
@@ -1561,6 +1562,29 @@ registerAsDbReadableController(AsDbReadableController);
|
|
|
1561
1562
|
//#endregion
|
|
1562
1563
|
//#region src/as-db.controller.ts
|
|
1563
1564
|
var _ref$2, _ref2$1;
|
|
1565
|
+
/**
|
|
1566
|
+
* Strips the version field from a write body and lifts it to `$cas`, in place.
|
|
1567
|
+
* Returns `true` iff a `$cas` predicate was actually attached — callers use
|
|
1568
|
+
* this to gate the 404/409 disambiguation `findOne`.
|
|
1569
|
+
*
|
|
1570
|
+
* Per §6.2 of VERSION_PROPOSAL.md, the moost-db controller treats `version` in
|
|
1571
|
+
* a write body as a `$cas` directive rather than a SET. No-op (returns `false`)
|
|
1572
|
+
* when:
|
|
1573
|
+
* - the payload isn't an object (rejected downstream by the SDK validator),
|
|
1574
|
+
* - the version field is absent (presence-based opt-out → last-write-wins),
|
|
1575
|
+
* - the value is not a finite number (the SDK will reject loudly via
|
|
1576
|
+
* `$cas` validation — we don't shadow that with a controller-local 400).
|
|
1577
|
+
*/
|
|
1578
|
+
function liftVersionToCas(payload, versionColumn) {
|
|
1579
|
+
if (payload === null || typeof payload !== "object") return false;
|
|
1580
|
+
const obj = payload;
|
|
1581
|
+
if (!(versionColumn in obj)) return false;
|
|
1582
|
+
const versionValue = obj[versionColumn];
|
|
1583
|
+
if (typeof versionValue !== "number" || !Number.isFinite(versionValue)) return false;
|
|
1584
|
+
delete obj[versionColumn];
|
|
1585
|
+
obj.$cas = { [versionColumn]: versionValue };
|
|
1586
|
+
return true;
|
|
1587
|
+
}
|
|
1564
1588
|
let AsDbController = class AsDbController extends AsDbReadableController {
|
|
1565
1589
|
/** Reference to the underlying table (typed for write access). */
|
|
1566
1590
|
get table() {
|
|
@@ -1607,29 +1631,66 @@ let AsDbController = class AsDbController extends AsDbReadableController {
|
|
|
1607
1631
|
}
|
|
1608
1632
|
/**
|
|
1609
1633
|
* **PUT /** — fully replaces one or many records matched by primary key.
|
|
1634
|
+
*
|
|
1635
|
+
* When the table opts into OCC (`@db.column.version`), a top-level `version`
|
|
1636
|
+
* field in the body is auto-lifted to `$cas` (§6.2 of VERSION_PROPOSAL.md).
|
|
1637
|
+
* On `matchedCount === 0` for a CAS-protected write, this disambiguates
|
|
1638
|
+
* 404 (row gone) vs 409 (version mismatch) via a single `findOne`.
|
|
1610
1639
|
*/
|
|
1611
1640
|
async replace(payload) {
|
|
1641
|
+
const versionColumn = this.table.versionColumn;
|
|
1612
1642
|
if (Array.isArray(payload)) {
|
|
1613
1643
|
const data = await this.onWrite("replaceMany", payload);
|
|
1614
1644
|
if (data === void 0) return new _moostjs_event_http.HttpError(500, "Not saved");
|
|
1645
|
+
if (versionColumn !== void 0) for (const item of data) liftVersionToCas(item, versionColumn);
|
|
1615
1646
|
return await this.table.bulkReplace(data);
|
|
1616
1647
|
}
|
|
1617
1648
|
const data = await this.onWrite("replace", payload);
|
|
1618
1649
|
if (data === void 0) return new _moostjs_event_http.HttpError(500, "Not saved");
|
|
1619
|
-
|
|
1650
|
+
const hadCasLift = versionColumn !== void 0 && liftVersionToCas(data, versionColumn);
|
|
1651
|
+
const result = await this.table.replaceOne(data);
|
|
1652
|
+
if (hadCasLift && result.matchedCount === 0) return await this._disambiguateMismatch(data, versionColumn);
|
|
1653
|
+
return result;
|
|
1620
1654
|
}
|
|
1621
1655
|
/**
|
|
1622
1656
|
* **PATCH /** — partially updates one or many records matched by primary key.
|
|
1657
|
+
*
|
|
1658
|
+
* Same OCC semantics as {@link replace} (§6.2 / §6.3).
|
|
1623
1659
|
*/
|
|
1624
1660
|
async update(payload) {
|
|
1661
|
+
const versionColumn = this.table.versionColumn;
|
|
1625
1662
|
if (Array.isArray(payload)) {
|
|
1626
1663
|
const data = await this.onWrite("updateMany", payload);
|
|
1627
1664
|
if (data === void 0) return new _moostjs_event_http.HttpError(500, "Not saved");
|
|
1665
|
+
if (versionColumn !== void 0) for (const item of data) liftVersionToCas(item, versionColumn);
|
|
1628
1666
|
return await this.table.bulkUpdate(data);
|
|
1629
1667
|
}
|
|
1630
1668
|
const data = await this.onWrite("update", payload);
|
|
1631
1669
|
if (data === void 0) return new _moostjs_event_http.HttpError(500, "Not saved");
|
|
1632
|
-
|
|
1670
|
+
const hadCasLift = versionColumn !== void 0 && liftVersionToCas(data, versionColumn);
|
|
1671
|
+
const result = await this.table.updateOne(data);
|
|
1672
|
+
if (hadCasLift && result.matchedCount === 0) return await this._disambiguateMismatch(data, versionColumn);
|
|
1673
|
+
return result;
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Disambiguates a `matchedCount === 0` result on a CAS-protected write:
|
|
1677
|
+
* returns 404 when the row is genuinely missing, 409 with
|
|
1678
|
+
* `{ error: "version_mismatch", currentVersion: N }` when it's present
|
|
1679
|
+
* but the supplied version is stale (§6.3).
|
|
1680
|
+
*/
|
|
1681
|
+
async _disambiguateMismatch(data, versionColumn) {
|
|
1682
|
+
const filter = this.table.resolveIdFilter(data);
|
|
1683
|
+
const row = filter ? await this.table.findOne({
|
|
1684
|
+
filter,
|
|
1685
|
+
controls: {}
|
|
1686
|
+
}) : null;
|
|
1687
|
+
if (row === null) return new _moostjs_event_http.HttpError(404);
|
|
1688
|
+
return new _moostjs_event_http.HttpError(409, {
|
|
1689
|
+
message: "version_mismatch",
|
|
1690
|
+
statusCode: 409,
|
|
1691
|
+
kind: "version_mismatch",
|
|
1692
|
+
currentVersion: row[versionColumn]
|
|
1693
|
+
});
|
|
1633
1694
|
}
|
|
1634
1695
|
/**
|
|
1635
1696
|
* **DELETE /:id** — removes a single record by primary key.
|
package/dist/index.d.cts
CHANGED
|
@@ -323,12 +323,26 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
323
323
|
insert(payload: unknown): Promise<unknown>;
|
|
324
324
|
/**
|
|
325
325
|
* **PUT /** — fully replaces one or many records matched by primary key.
|
|
326
|
+
*
|
|
327
|
+
* When the table opts into OCC (`@db.column.version`), a top-level `version`
|
|
328
|
+
* field in the body is auto-lifted to `$cas` (§6.2 of VERSION_PROPOSAL.md).
|
|
329
|
+
* On `matchedCount === 0` for a CAS-protected write, this disambiguates
|
|
330
|
+
* 404 (row gone) vs 409 (version mismatch) via a single `findOne`.
|
|
326
331
|
*/
|
|
327
332
|
replace(payload: unknown): Promise<unknown>;
|
|
328
333
|
/**
|
|
329
334
|
* **PATCH /** — partially updates one or many records matched by primary key.
|
|
335
|
+
*
|
|
336
|
+
* Same OCC semantics as {@link replace} (§6.2 / §6.3).
|
|
330
337
|
*/
|
|
331
338
|
update(payload: unknown): Promise<unknown>;
|
|
339
|
+
/**
|
|
340
|
+
* Disambiguates a `matchedCount === 0` result on a CAS-protected write:
|
|
341
|
+
* returns 404 when the row is genuinely missing, 409 with
|
|
342
|
+
* `{ error: "version_mismatch", currentVersion: N }` when it's present
|
|
343
|
+
* but the supplied version is stale (§6.3).
|
|
344
|
+
*/
|
|
345
|
+
protected _disambiguateMismatch(data: unknown, versionColumn: string): Promise<HttpError>;
|
|
332
346
|
/**
|
|
333
347
|
* **DELETE /:id** — removes a single record by primary key.
|
|
334
348
|
*/
|
package/dist/index.d.mts
CHANGED
|
@@ -323,12 +323,26 @@ declare class AsDbController<T extends TAtscriptAnnotatedType = TAtscriptAnnotat
|
|
|
323
323
|
insert(payload: unknown): Promise<unknown>;
|
|
324
324
|
/**
|
|
325
325
|
* **PUT /** — fully replaces one or many records matched by primary key.
|
|
326
|
+
*
|
|
327
|
+
* When the table opts into OCC (`@db.column.version`), a top-level `version`
|
|
328
|
+
* field in the body is auto-lifted to `$cas` (§6.2 of VERSION_PROPOSAL.md).
|
|
329
|
+
* On `matchedCount === 0` for a CAS-protected write, this disambiguates
|
|
330
|
+
* 404 (row gone) vs 409 (version mismatch) via a single `findOne`.
|
|
326
331
|
*/
|
|
327
332
|
replace(payload: unknown): Promise<unknown>;
|
|
328
333
|
/**
|
|
329
334
|
* **PATCH /** — partially updates one or many records matched by primary key.
|
|
335
|
+
*
|
|
336
|
+
* Same OCC semantics as {@link replace} (§6.2 / §6.3).
|
|
330
337
|
*/
|
|
331
338
|
update(payload: unknown): Promise<unknown>;
|
|
339
|
+
/**
|
|
340
|
+
* Disambiguates a `matchedCount === 0` result on a CAS-protected write:
|
|
341
|
+
* returns 404 when the row is genuinely missing, 409 with
|
|
342
|
+
* `{ error: "version_mismatch", currentVersion: N }` when it's present
|
|
343
|
+
* but the supplied version is stale (§6.3).
|
|
344
|
+
*/
|
|
345
|
+
protected _disambiguateMismatch(data: unknown, versionColumn: string): Promise<HttpError>;
|
|
332
346
|
/**
|
|
333
347
|
* **DELETE /:id** — removes a single record by primary key.
|
|
334
348
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -1509,7 +1509,8 @@ let AsDbReadableController = class AsDbReadableController extends AsReadableCont
|
|
|
1509
1509
|
fields,
|
|
1510
1510
|
type: this.getSerializedType(),
|
|
1511
1511
|
actions: this.buildActions(),
|
|
1512
|
-
crud: this.buildCrud()
|
|
1512
|
+
crud: this.buildCrud(),
|
|
1513
|
+
versionColumn: this.readable.versionColumn
|
|
1513
1514
|
};
|
|
1514
1515
|
}
|
|
1515
1516
|
buildCrud() {
|
|
@@ -1560,6 +1561,29 @@ registerAsDbReadableController(AsDbReadableController);
|
|
|
1560
1561
|
//#endregion
|
|
1561
1562
|
//#region src/as-db.controller.ts
|
|
1562
1563
|
var _ref$2, _ref2$1;
|
|
1564
|
+
/**
|
|
1565
|
+
* Strips the version field from a write body and lifts it to `$cas`, in place.
|
|
1566
|
+
* Returns `true` iff a `$cas` predicate was actually attached — callers use
|
|
1567
|
+
* this to gate the 404/409 disambiguation `findOne`.
|
|
1568
|
+
*
|
|
1569
|
+
* Per §6.2 of VERSION_PROPOSAL.md, the moost-db controller treats `version` in
|
|
1570
|
+
* a write body as a `$cas` directive rather than a SET. No-op (returns `false`)
|
|
1571
|
+
* when:
|
|
1572
|
+
* - the payload isn't an object (rejected downstream by the SDK validator),
|
|
1573
|
+
* - the version field is absent (presence-based opt-out → last-write-wins),
|
|
1574
|
+
* - the value is not a finite number (the SDK will reject loudly via
|
|
1575
|
+
* `$cas` validation — we don't shadow that with a controller-local 400).
|
|
1576
|
+
*/
|
|
1577
|
+
function liftVersionToCas(payload, versionColumn) {
|
|
1578
|
+
if (payload === null || typeof payload !== "object") return false;
|
|
1579
|
+
const obj = payload;
|
|
1580
|
+
if (!(versionColumn in obj)) return false;
|
|
1581
|
+
const versionValue = obj[versionColumn];
|
|
1582
|
+
if (typeof versionValue !== "number" || !Number.isFinite(versionValue)) return false;
|
|
1583
|
+
delete obj[versionColumn];
|
|
1584
|
+
obj.$cas = { [versionColumn]: versionValue };
|
|
1585
|
+
return true;
|
|
1586
|
+
}
|
|
1563
1587
|
let AsDbController = class AsDbController extends AsDbReadableController {
|
|
1564
1588
|
/** Reference to the underlying table (typed for write access). */
|
|
1565
1589
|
get table() {
|
|
@@ -1606,29 +1630,66 @@ let AsDbController = class AsDbController extends AsDbReadableController {
|
|
|
1606
1630
|
}
|
|
1607
1631
|
/**
|
|
1608
1632
|
* **PUT /** — fully replaces one or many records matched by primary key.
|
|
1633
|
+
*
|
|
1634
|
+
* When the table opts into OCC (`@db.column.version`), a top-level `version`
|
|
1635
|
+
* field in the body is auto-lifted to `$cas` (§6.2 of VERSION_PROPOSAL.md).
|
|
1636
|
+
* On `matchedCount === 0` for a CAS-protected write, this disambiguates
|
|
1637
|
+
* 404 (row gone) vs 409 (version mismatch) via a single `findOne`.
|
|
1609
1638
|
*/
|
|
1610
1639
|
async replace(payload) {
|
|
1640
|
+
const versionColumn = this.table.versionColumn;
|
|
1611
1641
|
if (Array.isArray(payload)) {
|
|
1612
1642
|
const data = await this.onWrite("replaceMany", payload);
|
|
1613
1643
|
if (data === void 0) return new HttpError(500, "Not saved");
|
|
1644
|
+
if (versionColumn !== void 0) for (const item of data) liftVersionToCas(item, versionColumn);
|
|
1614
1645
|
return await this.table.bulkReplace(data);
|
|
1615
1646
|
}
|
|
1616
1647
|
const data = await this.onWrite("replace", payload);
|
|
1617
1648
|
if (data === void 0) return new HttpError(500, "Not saved");
|
|
1618
|
-
|
|
1649
|
+
const hadCasLift = versionColumn !== void 0 && liftVersionToCas(data, versionColumn);
|
|
1650
|
+
const result = await this.table.replaceOne(data);
|
|
1651
|
+
if (hadCasLift && result.matchedCount === 0) return await this._disambiguateMismatch(data, versionColumn);
|
|
1652
|
+
return result;
|
|
1619
1653
|
}
|
|
1620
1654
|
/**
|
|
1621
1655
|
* **PATCH /** — partially updates one or many records matched by primary key.
|
|
1656
|
+
*
|
|
1657
|
+
* Same OCC semantics as {@link replace} (§6.2 / §6.3).
|
|
1622
1658
|
*/
|
|
1623
1659
|
async update(payload) {
|
|
1660
|
+
const versionColumn = this.table.versionColumn;
|
|
1624
1661
|
if (Array.isArray(payload)) {
|
|
1625
1662
|
const data = await this.onWrite("updateMany", payload);
|
|
1626
1663
|
if (data === void 0) return new HttpError(500, "Not saved");
|
|
1664
|
+
if (versionColumn !== void 0) for (const item of data) liftVersionToCas(item, versionColumn);
|
|
1627
1665
|
return await this.table.bulkUpdate(data);
|
|
1628
1666
|
}
|
|
1629
1667
|
const data = await this.onWrite("update", payload);
|
|
1630
1668
|
if (data === void 0) return new HttpError(500, "Not saved");
|
|
1631
|
-
|
|
1669
|
+
const hadCasLift = versionColumn !== void 0 && liftVersionToCas(data, versionColumn);
|
|
1670
|
+
const result = await this.table.updateOne(data);
|
|
1671
|
+
if (hadCasLift && result.matchedCount === 0) return await this._disambiguateMismatch(data, versionColumn);
|
|
1672
|
+
return result;
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Disambiguates a `matchedCount === 0` result on a CAS-protected write:
|
|
1676
|
+
* returns 404 when the row is genuinely missing, 409 with
|
|
1677
|
+
* `{ error: "version_mismatch", currentVersion: N }` when it's present
|
|
1678
|
+
* but the supplied version is stale (§6.3).
|
|
1679
|
+
*/
|
|
1680
|
+
async _disambiguateMismatch(data, versionColumn) {
|
|
1681
|
+
const filter = this.table.resolveIdFilter(data);
|
|
1682
|
+
const row = filter ? await this.table.findOne({
|
|
1683
|
+
filter,
|
|
1684
|
+
controls: {}
|
|
1685
|
+
}) : null;
|
|
1686
|
+
if (row === null) return new HttpError(404);
|
|
1687
|
+
return new HttpError(409, {
|
|
1688
|
+
message: "version_mismatch",
|
|
1689
|
+
statusCode: 409,
|
|
1690
|
+
kind: "version_mismatch",
|
|
1691
|
+
currentVersion: row[versionColumn]
|
|
1692
|
+
});
|
|
1632
1693
|
}
|
|
1633
1694
|
/**
|
|
1634
1695
|
* **DELETE /:id** — removes a single record by primary key.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/moost-db",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"description": "Generic database controller for Moost with Atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"annotations",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@wooksjs/event-core": "^0.7.13",
|
|
59
59
|
"@wooksjs/http-body": "^0.7.13",
|
|
60
60
|
"moost": "^0.6.15",
|
|
61
|
-
"@atscript/db": "^0.1.
|
|
61
|
+
"@atscript/db": "^0.1.85"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"postinstall": "asc -f dts",
|