@open-aih/payload-ddb 3.1.0 → 4.1.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/README.md +141 -18
- package/dist/count.d.ts.map +1 -1
- package/dist/count.js +4 -2
- package/dist/count.js.map +1 -1
- package/dist/countGlobalVersions.d.ts.map +1 -1
- package/dist/countGlobalVersions.js +6 -1
- package/dist/countGlobalVersions.js.map +1 -1
- package/dist/countVersions.d.ts.map +1 -1
- package/dist/countVersions.js +6 -1
- package/dist/countVersions.js.map +1 -1
- package/dist/create.d.ts.map +1 -1
- package/dist/create.js +31 -15
- package/dist/create.js.map +1 -1
- package/dist/createGlobal.d.ts.map +1 -1
- package/dist/createGlobal.js +2 -14
- package/dist/createGlobal.js.map +1 -1
- package/dist/createGlobalVersion.d.ts +1 -1
- package/dist/createGlobalVersion.d.ts.map +1 -1
- package/dist/createGlobalVersion.js +3 -41
- package/dist/createGlobalVersion.js.map +1 -1
- package/dist/createVersion.d.ts +6 -5
- package/dist/createVersion.d.ts.map +1 -1
- package/dist/createVersion.js +8 -48
- package/dist/createVersion.js.map +1 -1
- package/dist/deleteMany.d.ts +1 -6
- package/dist/deleteMany.d.ts.map +1 -1
- package/dist/deleteMany.js +8 -19
- package/dist/deleteMany.js.map +1 -1
- package/dist/deleteOne.d.ts.map +1 -1
- package/dist/deleteOne.js +20 -24
- package/dist/deleteOne.js.map +1 -1
- package/dist/deleteVersions.d.ts.map +1 -1
- package/dist/deleteVersions.js +8 -14
- package/dist/deleteVersions.js.map +1 -1
- package/dist/find.d.ts.map +1 -1
- package/dist/find.js +15 -2
- package/dist/find.js.map +1 -1
- package/dist/findDistinct.d.ts.map +1 -1
- package/dist/findDistinct.js +3 -1
- package/dist/findDistinct.js.map +1 -1
- package/dist/findGlobal.d.ts.map +1 -1
- package/dist/findGlobal.js +3 -2
- package/dist/findGlobal.js.map +1 -1
- package/dist/findGlobalVersions.d.ts.map +1 -1
- package/dist/findGlobalVersions.js +10 -2
- package/dist/findGlobalVersions.js.map +1 -1
- package/dist/findOne.d.ts.map +1 -1
- package/dist/findOne.js +17 -6
- package/dist/findOne.js.map +1 -1
- package/dist/findVersions.d.ts.map +1 -1
- package/dist/findVersions.js +10 -2
- package/dist/findVersions.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/queryDrafts.d.ts.map +1 -1
- package/dist/queryDrafts.js +24 -4
- package/dist/queryDrafts.js.map +1 -1
- package/dist/updateGlobal.d.ts +5 -4
- package/dist/updateGlobal.d.ts.map +1 -1
- package/dist/updateGlobal.js +22 -36
- package/dist/updateGlobal.js.map +1 -1
- package/dist/updateGlobalVersion.d.ts.map +1 -1
- package/dist/updateGlobalVersion.js +3 -49
- package/dist/updateGlobalVersion.js.map +1 -1
- package/dist/updateJobs.d.ts +21 -0
- package/dist/updateJobs.d.ts.map +1 -0
- package/dist/updateJobs.js +83 -0
- package/dist/updateJobs.js.map +1 -0
- package/dist/updateMany.d.ts +9 -3
- package/dist/updateMany.d.ts.map +1 -1
- package/dist/updateMany.js +47 -30
- package/dist/updateMany.js.map +1 -1
- package/dist/updateOne.d.ts +6 -1
- package/dist/updateOne.d.ts.map +1 -1
- package/dist/updateOne.js +49 -49
- package/dist/updateOne.js.map +1 -1
- package/dist/updateVersion.d.ts.map +1 -1
- package/dist/updateVersion.js +3 -52
- package/dist/updateVersion.js.map +1 -1
- package/dist/upsert.d.ts +6 -0
- package/dist/upsert.d.ts.map +1 -1
- package/dist/upsert.js +57 -41
- package/dist/upsert.js.map +1 -1
- package/dist/utilities/backfillUniqueGuards.d.ts +42 -0
- package/dist/utilities/backfillUniqueGuards.d.ts.map +1 -0
- package/dist/utilities/backfillUniqueGuards.js +39 -0
- package/dist/utilities/backfillUniqueGuards.js.map +1 -0
- package/dist/utilities/buildFilterExpression.d.ts +29 -14
- package/dist/utilities/buildFilterExpression.d.ts.map +1 -1
- package/dist/utilities/buildFilterExpression.js +55 -14
- package/dist/utilities/buildFilterExpression.js.map +1 -1
- package/dist/utilities/castWhere.d.ts +84 -0
- package/dist/utilities/castWhere.d.ts.map +1 -0
- package/dist/utilities/castWhere.js +254 -0
- package/dist/utilities/castWhere.js.map +1 -0
- package/dist/utilities/findFirst.d.ts +3 -2
- package/dist/utilities/findFirst.d.ts.map +1 -1
- package/dist/utilities/findFirst.js +14 -46
- package/dist/utilities/findFirst.js.map +1 -1
- package/dist/utilities/geo.d.ts +36 -0
- package/dist/utilities/geo.d.ts.map +1 -0
- package/dist/utilities/geo.js +108 -0
- package/dist/utilities/geo.js.map +1 -0
- package/dist/utilities/mapWithConcurrency.d.ts +12 -0
- package/dist/utilities/mapWithConcurrency.d.ts.map +1 -0
- package/dist/utilities/mapWithConcurrency.js +32 -0
- package/dist/utilities/mapWithConcurrency.js.map +1 -0
- package/dist/utilities/matchesWhere.d.ts +25 -5
- package/dist/utilities/matchesWhere.d.ts.map +1 -1
- package/dist/utilities/matchesWhere.js +150 -38
- package/dist/utilities/matchesWhere.js.map +1 -1
- package/dist/utilities/operators.d.ts +22 -12
- package/dist/utilities/operators.d.ts.map +1 -1
- package/dist/utilities/operators.js +36 -31
- package/dist/utilities/operators.js.map +1 -1
- package/dist/utilities/optimistic.d.ts +29 -0
- package/dist/utilities/optimistic.d.ts.map +1 -0
- package/dist/utilities/optimistic.js +58 -0
- package/dist/utilities/optimistic.js.map +1 -0
- package/dist/utilities/prepareWhere.d.ts +63 -0
- package/dist/utilities/prepareWhere.d.ts.map +1 -0
- package/dist/utilities/prepareWhere.js +198 -0
- package/dist/utilities/prepareWhere.js.map +1 -0
- package/dist/utilities/queryCount.d.ts +6 -3
- package/dist/utilities/queryCount.d.ts.map +1 -1
- package/dist/utilities/queryCount.js +16 -45
- package/dist/utilities/queryCount.js.map +1 -1
- package/dist/utilities/queryMatching.d.ts +8 -7
- package/dist/utilities/queryMatching.d.ts.map +1 -1
- package/dist/utilities/queryMatching.js +17 -54
- package/dist/utilities/queryMatching.js.map +1 -1
- package/dist/utilities/queryPartition.d.ts +26 -0
- package/dist/utilities/queryPartition.d.ts.map +1 -0
- package/dist/utilities/queryPartition.js +55 -0
- package/dist/utilities/queryPartition.js.map +1 -0
- package/dist/utilities/resolveJoins.d.ts +37 -0
- package/dist/utilities/resolveJoins.d.ts.map +1 -0
- package/dist/utilities/resolveJoins.js +195 -0
- package/dist/utilities/resolveJoins.js.map +1 -0
- package/dist/utilities/stripInternalKeys.d.ts +17 -3
- package/dist/utilities/stripInternalKeys.d.ts.map +1 -1
- package/dist/utilities/stripInternalKeys.js +25 -4
- package/dist/utilities/stripInternalKeys.js.map +1 -1
- package/dist/utilities/uniqueGuards.d.ts +39 -0
- package/dist/utilities/uniqueGuards.d.ts.map +1 -0
- package/dist/utilities/uniqueGuards.js +106 -0
- package/dist/utilities/uniqueGuards.js.map +1 -0
- package/dist/utilities/updateVersionRow.d.ts +19 -0
- package/dist/utilities/updateVersionRow.d.ts.map +1 -0
- package/dist/utilities/updateVersionRow.js +40 -0
- package/dist/utilities/updateVersionRow.js.map +1 -0
- package/dist/utilities/writeErrors.d.ts +11 -0
- package/dist/utilities/writeErrors.d.ts.map +1 -0
- package/dist/utilities/writeErrors.js +27 -0
- package/dist/utilities/writeErrors.js.map +1 -0
- package/dist/utilities/writeItems.d.ts +124 -0
- package/dist/utilities/writeItems.d.ts.map +1 -0
- package/dist/utilities/writeItems.js +383 -0
- package/dist/utilities/writeItems.js.map +1 -0
- package/package.json +21 -14
package/dist/updateMany.d.ts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import type { UpdateMany } from 'payload';
|
|
2
2
|
/**
|
|
3
3
|
* v1 strategy: query-and-collect matches, sort, slice to `limit`, then
|
|
4
|
-
* read-merge-write each
|
|
5
|
-
* `data` is overlaid on the target and `id` is
|
|
4
|
+
* read-merge-write each, at most `WRITE_CONCURRENCY` at a time. Same merge
|
|
5
|
+
* semantics as `updateOne`: `data` is overlaid on the target and `id` is
|
|
6
|
+
* preserved from the target.
|
|
6
7
|
*
|
|
7
|
-
*
|
|
8
|
+
* Each write is conditional on its row being unchanged since it was read. A
|
|
9
|
+
* row that changed underneath is re-read and re-checked against `where`: if
|
|
10
|
+
* it still matches, the patch is re-applied to the fresh row; if it no longer
|
|
11
|
+
* matches (or is gone), it is skipped and left out of the result.
|
|
12
|
+
*
|
|
13
|
+
* `mapWithConcurrency` is order-preserving, so the returned array matches the
|
|
8
14
|
* sorted target order. Bulk DynamoDB transact-writes (`TransactWriteItems`,
|
|
9
15
|
* 100-item / 4 MB cap) are the natural next optimization but require the
|
|
10
16
|
* transaction wiring we deliberately stubbed out for now.
|
package/dist/updateMany.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateMany.d.ts","sourceRoot":"","sources":["../src/updateMany.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"updateMany.d.ts","sourceRoot":"","sources":["../src/updateMany.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAczC;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,UAAU,EAAE,UA2CxB,CAAA"}
|
package/dist/updateMany.js
CHANGED
|
@@ -1,48 +1,65 @@
|
|
|
1
|
-
import { PutCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
1
|
import { applySorts } from './utilities/applySorts.js';
|
|
3
|
-
import {
|
|
2
|
+
import { mapWithConcurrency } from './utilities/mapWithConcurrency.js';
|
|
3
|
+
import { matchesWhere } from './utilities/matchesWhere.js';
|
|
4
|
+
import { getById, writeWithRetry } from './utilities/optimistic.js';
|
|
5
|
+
import { prepareCollectionWhere, prepareSort } from './utilities/prepareWhere.js';
|
|
4
6
|
import { queryMatching } from './utilities/queryMatching.js';
|
|
5
7
|
import { projectForCollection } from './utilities/resolveSchema.js';
|
|
8
|
+
import { rethrowUniqueViolation } from './utilities/writeErrors.js';
|
|
9
|
+
import { putItem, WRITE_CONCURRENCY } from './utilities/writeItems.js';
|
|
6
10
|
/**
|
|
7
11
|
* v1 strategy: query-and-collect matches, sort, slice to `limit`, then
|
|
8
|
-
* read-merge-write each
|
|
9
|
-
* `data` is overlaid on the target and `id` is
|
|
12
|
+
* read-merge-write each, at most `WRITE_CONCURRENCY` at a time. Same merge
|
|
13
|
+
* semantics as `updateOne`: `data` is overlaid on the target and `id` is
|
|
14
|
+
* preserved from the target.
|
|
10
15
|
*
|
|
11
|
-
*
|
|
16
|
+
* Each write is conditional on its row being unchanged since it was read. A
|
|
17
|
+
* row that changed underneath is re-read and re-checked against `where`: if
|
|
18
|
+
* it still matches, the patch is re-applied to the fresh row; if it no longer
|
|
19
|
+
* matches (or is gone), it is skipped and left out of the result.
|
|
20
|
+
*
|
|
21
|
+
* `mapWithConcurrency` is order-preserving, so the returned array matches the
|
|
12
22
|
* sorted target order. Bulk DynamoDB transact-writes (`TransactWriteItems`,
|
|
13
23
|
* 100-item / 4 MB cap) are the natural next optimization but require the
|
|
14
24
|
* transaction wiring we deliberately stubbed out for now.
|
|
15
|
-
*/ export const updateMany = async function updateMany({ collection, data, limit, returning, sort, where }) {
|
|
16
|
-
const
|
|
17
|
-
if (!docClient) {
|
|
18
|
-
throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
|
|
19
|
-
}
|
|
25
|
+
*/ export const updateMany = async function updateMany({ collection, data, limit, locale, req, returning, sort, where: rawWhere }) {
|
|
26
|
+
const where = await prepareCollectionWhere(this, collection, rawWhere, locale);
|
|
20
27
|
const partition = this.resolvePartition(collection);
|
|
21
28
|
const matched = await queryMatching(this, partition, where);
|
|
22
|
-
applySorts(matched,
|
|
29
|
+
applySorts(matched, prepareSort(this, {
|
|
30
|
+
kind: 'collection',
|
|
31
|
+
slug: collection
|
|
32
|
+
}, sort, locale));
|
|
23
33
|
const targets = limit && limit > 0 ? matched.slice(0, limit) : matched;
|
|
24
34
|
// Hoisted so every row in the batch shares one timestamp — keeps audit-style
|
|
25
35
|
// queries deterministic. Explicit `updatedAt` in `data` still wins.
|
|
26
36
|
const updatedAt = data['updatedAt'] ?? new Date().toISOString();
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
let updated;
|
|
38
|
+
try {
|
|
39
|
+
updated = await mapWithConcurrency(targets, WRITE_CONCURRENCY, (target)=>writeWithRetry(target, async ()=>{
|
|
40
|
+
const fresh = await getById(this, partition, target['id']);
|
|
41
|
+
return fresh && matchesWhere(fresh, where) ? fresh : null;
|
|
42
|
+
}, async (current)=>{
|
|
43
|
+
const merged = {
|
|
44
|
+
...current,
|
|
45
|
+
...data,
|
|
46
|
+
id: current['id'],
|
|
47
|
+
updatedAt
|
|
48
|
+
};
|
|
49
|
+
const projected = projectForCollection(this, collection, merged);
|
|
50
|
+
await putItem(this, partition, projected, {
|
|
51
|
+
replaces: current,
|
|
52
|
+
unique: {
|
|
53
|
+
collection
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return projected;
|
|
57
|
+
}));
|
|
58
|
+
} catch (err) {
|
|
59
|
+
rethrowUniqueViolation(err, collection, req);
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
return returning === false ? null : updated.filter((doc)=>doc !== null);
|
|
46
63
|
};
|
|
47
64
|
|
|
48
65
|
//# sourceMappingURL=updateMany.js.map
|
package/dist/updateMany.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/updateMany.ts"],"sourcesContent":["import type { UpdateMany } from 'payload'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/updateMany.ts"],"sourcesContent":["import type { UpdateMany } from 'payload'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { applySorts } from './utilities/applySorts.js'\nimport { mapWithConcurrency } from './utilities/mapWithConcurrency.js'\nimport { matchesWhere } from './utilities/matchesWhere.js'\nimport { getById, writeWithRetry } from './utilities/optimistic.js'\nimport { prepareCollectionWhere, prepareSort } from './utilities/prepareWhere.js'\nimport { queryMatching } from './utilities/queryMatching.js'\nimport { projectForCollection } from './utilities/resolveSchema.js'\nimport { rethrowUniqueViolation } from './utilities/writeErrors.js'\nimport { putItem, WRITE_CONCURRENCY } from './utilities/writeItems.js'\n\n/**\n * v1 strategy: query-and-collect matches, sort, slice to `limit`, then\n * read-merge-write each, at most `WRITE_CONCURRENCY` at a time. Same merge\n * semantics as `updateOne`: `data` is overlaid on the target and `id` is\n * preserved from the target.\n *\n * Each write is conditional on its row being unchanged since it was read. A\n * row that changed underneath is re-read and re-checked against `where`: if\n * it still matches, the patch is re-applied to the fresh row; if it no longer\n * matches (or is gone), it is skipped and left out of the result.\n *\n * `mapWithConcurrency` is order-preserving, so the returned array matches the\n * sorted target order. Bulk DynamoDB transact-writes (`TransactWriteItems`,\n * 100-item / 4 MB cap) are the natural next optimization but require the\n * transaction wiring we deliberately stubbed out for now.\n */\nexport const updateMany: UpdateMany = async function updateMany(\n this: DynamoAdapter,\n { collection, data, limit, locale, req, returning, sort, where: rawWhere },\n) {\n const where = await prepareCollectionWhere(this, collection, rawWhere, locale)\n const partition = this.resolvePartition(collection)\n const matched = await queryMatching(this, partition, where)\n applySorts(matched, prepareSort(this, { kind: 'collection', slug: collection }, sort, locale))\n\n const targets = limit && limit > 0 ? matched.slice(0, limit) : matched\n\n // Hoisted so every row in the batch shares one timestamp — keeps audit-style\n // queries deterministic. Explicit `updatedAt` in `data` still wins.\n const updatedAt = data['updatedAt'] ?? new Date().toISOString()\n\n let updated: (null | Record<string, unknown>)[]\n try {\n updated = await mapWithConcurrency(targets, WRITE_CONCURRENCY, (target) =>\n writeWithRetry(\n target,\n async () => {\n const fresh = await getById(this, partition, target['id'])\n return fresh && matchesWhere(fresh, where) ? fresh : null\n },\n async (current) => {\n const merged: Record<string, unknown> = {\n ...current,\n ...data,\n id: current['id'],\n updatedAt,\n }\n const projected = projectForCollection(this, collection, merged)\n await putItem(this, partition, projected, { replaces: current, unique: { collection } })\n return projected\n },\n ),\n )\n } catch (err) {\n rethrowUniqueViolation(err, collection, req)\n throw err\n }\n\n return returning === false ? null : updated.filter((doc) => doc !== null)\n}\n"],"names":["applySorts","mapWithConcurrency","matchesWhere","getById","writeWithRetry","prepareCollectionWhere","prepareSort","queryMatching","projectForCollection","rethrowUniqueViolation","putItem","WRITE_CONCURRENCY","updateMany","collection","data","limit","locale","req","returning","sort","where","rawWhere","partition","resolvePartition","matched","kind","slug","targets","slice","updatedAt","Date","toISOString","updated","target","fresh","current","merged","id","projected","replaces","unique","err","filter","doc"],"mappings":"AAIA,SAASA,UAAU,QAAQ,4BAA2B;AACtD,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,YAAY,QAAQ,8BAA6B;AAC1D,SAASC,OAAO,EAAEC,cAAc,QAAQ,4BAA2B;AACnE,SAASC,sBAAsB,EAAEC,WAAW,QAAQ,8BAA6B;AACjF,SAASC,aAAa,QAAQ,+BAA8B;AAC5D,SAASC,oBAAoB,QAAQ,+BAA8B;AACnE,SAASC,sBAAsB,QAAQ,6BAA4B;AACnE,SAASC,OAAO,EAAEC,iBAAiB,QAAQ,4BAA2B;AAEtE;;;;;;;;;;;;;;;CAeC,GACD,OAAO,MAAMC,aAAyB,eAAeA,WAEnD,EAAEC,UAAU,EAAEC,IAAI,EAAEC,KAAK,EAAEC,MAAM,EAAEC,GAAG,EAAEC,SAAS,EAAEC,IAAI,EAAEC,OAAOC,QAAQ,EAAE;IAE1E,MAAMD,QAAQ,MAAMf,uBAAuB,IAAI,EAAEQ,YAAYQ,UAAUL;IACvE,MAAMM,YAAY,IAAI,CAACC,gBAAgB,CAACV;IACxC,MAAMW,UAAU,MAAMjB,cAAc,IAAI,EAAEe,WAAWF;IACrDpB,WAAWwB,SAASlB,YAAY,IAAI,EAAE;QAAEmB,MAAM;QAAcC,MAAMb;IAAW,GAAGM,MAAMH;IAEtF,MAAMW,UAAUZ,SAASA,QAAQ,IAAIS,QAAQI,KAAK,CAAC,GAAGb,SAASS;IAE/D,6EAA6E;IAC7E,oEAAoE;IACpE,MAAMK,YAAYf,IAAI,CAAC,YAAY,IAAI,IAAIgB,OAAOC,WAAW;IAE7D,IAAIC;IACJ,IAAI;QACFA,UAAU,MAAM/B,mBAAmB0B,SAAShB,mBAAmB,CAACsB,SAC9D7B,eACE6B,QACA;gBACE,MAAMC,QAAQ,MAAM/B,QAAQ,IAAI,EAAEmB,WAAWW,MAAM,CAAC,KAAK;gBACzD,OAAOC,SAAShC,aAAagC,OAAOd,SAASc,QAAQ;YACvD,GACA,OAAOC;gBACL,MAAMC,SAAkC;oBACtC,GAAGD,OAAO;oBACV,GAAGrB,IAAI;oBACPuB,IAAIF,OAAO,CAAC,KAAK;oBACjBN;gBACF;gBACA,MAAMS,YAAY9B,qBAAqB,IAAI,EAAEK,YAAYuB;gBACzD,MAAM1B,QAAQ,IAAI,EAAEY,WAAWgB,WAAW;oBAAEC,UAAUJ;oBAASK,QAAQ;wBAAE3B;oBAAW;gBAAE;gBACtF,OAAOyB;YACT;IAGN,EAAE,OAAOG,KAAK;QACZhC,uBAAuBgC,KAAK5B,YAAYI;QACxC,MAAMwB;IACR;IAEA,OAAOvB,cAAc,QAAQ,OAAOc,QAAQU,MAAM,CAAC,CAACC,MAAQA,QAAQ;AACtE,EAAC"}
|
package/dist/updateOne.d.ts
CHANGED
|
@@ -4,7 +4,12 @@ import type { UpdateOne } from 'payload';
|
|
|
4
4
|
* Payload sends a partial `data` patch; DynamoDB `UpdateItem` would let us
|
|
5
5
|
* push the diff server-side, but its expression syntax (reserved words,
|
|
6
6
|
* nested attribute paths, list indexing, removal semantics) is enough work to
|
|
7
|
-
* earn its own milestone.
|
|
7
|
+
* earn its own milestone.
|
|
8
|
+
*
|
|
9
|
+
* The write is conditional on the row still being at the revision that was
|
|
10
|
+
* read. If another writer got in first, the patch is re-applied to the fresh
|
|
11
|
+
* row (`writeWithRetry`), so concurrent updates to different fields both
|
|
12
|
+
* land.
|
|
8
13
|
*
|
|
9
14
|
* `id` on the resulting item always comes from the located target — we don't
|
|
10
15
|
* let `data` rewrite the primary key, which would silently orphan the row.
|
package/dist/updateOne.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateOne.d.ts","sourceRoot":"","sources":["../src/updateOne.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAWxC
|
|
1
|
+
{"version":3,"file":"updateOne.d.ts","sourceRoot":"","sources":["../src/updateOne.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAWxC;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,SAAS,EAAE,SA0CvB,CAAA"}
|
package/dist/updateOne.js
CHANGED
|
@@ -1,68 +1,68 @@
|
|
|
1
|
-
import { GetCommand, PutCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
1
|
import { findFirst } from './utilities/findFirst.js';
|
|
3
|
-
import {
|
|
2
|
+
import { getById, writeWithRetry } from './utilities/optimistic.js';
|
|
3
|
+
import { prepareCollectionWhere } from './utilities/prepareWhere.js';
|
|
4
4
|
import { projectForCollection } from './utilities/resolveSchema.js';
|
|
5
|
-
import {
|
|
5
|
+
import { rethrowUniqueViolation } from './utilities/writeErrors.js';
|
|
6
|
+
import { putItem } from './utilities/writeItems.js';
|
|
6
7
|
/**
|
|
7
8
|
* v1 strategy: read-merge-write rather than building an `UpdateExpression`.
|
|
8
9
|
* Payload sends a partial `data` patch; DynamoDB `UpdateItem` would let us
|
|
9
10
|
* push the diff server-side, but its expression syntax (reserved words,
|
|
10
11
|
* nested attribute paths, list indexing, removal semantics) is enough work to
|
|
11
|
-
* earn its own milestone.
|
|
12
|
+
* earn its own milestone.
|
|
13
|
+
*
|
|
14
|
+
* The write is conditional on the row still being at the revision that was
|
|
15
|
+
* read. If another writer got in first, the patch is re-applied to the fresh
|
|
16
|
+
* row (`writeWithRetry`), so concurrent updates to different fields both
|
|
17
|
+
* land.
|
|
12
18
|
*
|
|
13
19
|
* `id` on the resulting item always comes from the located target — we don't
|
|
14
20
|
* let `data` rewrite the primary key, which would silently orphan the row.
|
|
15
21
|
*/ export const updateOne = async function updateOne(args) {
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const result = await docClient.send(new GetCommand({
|
|
24
|
-
TableName: this.tableName,
|
|
25
|
-
Key: {
|
|
26
|
-
pk: partition,
|
|
27
|
-
sk: String(args.id)
|
|
28
|
-
},
|
|
29
|
-
ConsistentRead: true
|
|
30
|
-
}));
|
|
31
|
-
target = result.Item ? stripInternalKeys(result.Item) : null;
|
|
32
|
-
} else if (args.where) {
|
|
33
|
-
target = await findFirst(this, {
|
|
22
|
+
const { collection, data } = args;
|
|
23
|
+
const partition = this.resolvePartition(collection);
|
|
24
|
+
const byId = args.id !== undefined && args.id !== null;
|
|
25
|
+
const where = byId ? undefined : await prepareCollectionWhere(this, collection, args.where, args.locale);
|
|
26
|
+
const read = ()=>{
|
|
27
|
+
if (byId) return getById(this, partition, args.id);
|
|
28
|
+
return where ? findFirst(this, {
|
|
34
29
|
partition,
|
|
35
|
-
where
|
|
30
|
+
where
|
|
31
|
+
}) : Promise.resolve(null);
|
|
32
|
+
};
|
|
33
|
+
let updated;
|
|
34
|
+
try {
|
|
35
|
+
updated = await writeWithRetry(await read(), read, async (target)=>{
|
|
36
|
+
const merged = {
|
|
37
|
+
...target,
|
|
38
|
+
...data,
|
|
39
|
+
id: target['id'],
|
|
40
|
+
// Bump `updatedAt` automatically. Explicit timestamps in `data` (e.g.
|
|
41
|
+
// from version restores or migrations) still win because they land in
|
|
42
|
+
// the spread above and we only override here when `data` didn't
|
|
43
|
+
// supply one.
|
|
44
|
+
updatedAt: data['updatedAt'] ?? new Date().toISOString()
|
|
45
|
+
};
|
|
46
|
+
// Strip the merged result against the collection schema. Catches both
|
|
47
|
+
// unknown keys in the incoming patch and any pre-existing leaked keys
|
|
48
|
+
// on the persisted target — every update doubles as incremental cleanup.
|
|
49
|
+
const projected = projectForCollection(this, collection, merged);
|
|
50
|
+
await putItem(this, partition, projected, {
|
|
51
|
+
replaces: target,
|
|
52
|
+
unique: {
|
|
53
|
+
collection
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return projected;
|
|
36
57
|
});
|
|
58
|
+
} catch (err) {
|
|
59
|
+
rethrowUniqueViolation(err, collection, args.req);
|
|
60
|
+
throw err;
|
|
37
61
|
}
|
|
38
|
-
if (!
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
const merged = {
|
|
42
|
-
...target,
|
|
43
|
-
...args.data,
|
|
44
|
-
id: target['id'],
|
|
45
|
-
// Bump `updatedAt` automatically. Explicit timestamps in `data` (e.g. from
|
|
46
|
-
// version restores or migrations) still win because they land in the
|
|
47
|
-
// spread above and we only override here when `data` didn't supply one.
|
|
48
|
-
updatedAt: args.data['updatedAt'] ?? new Date().toISOString()
|
|
49
|
-
};
|
|
50
|
-
// Strip the merged result against the collection schema. Catches both
|
|
51
|
-
// unknown keys in the incoming patch and any pre-existing leaked keys on
|
|
52
|
-
// the persisted target — every update doubles as incremental cleanup.
|
|
53
|
-
const projected = projectForCollection(this, args.collection, merged);
|
|
54
|
-
await docClient.send(new PutCommand({
|
|
55
|
-
TableName: this.tableName,
|
|
56
|
-
Item: normalizeForDynamo({
|
|
57
|
-
...projected,
|
|
58
|
-
pk: partition,
|
|
59
|
-
sk: String(projected['id'])
|
|
60
|
-
})
|
|
61
|
-
}));
|
|
62
|
-
if (args.returning === false) {
|
|
62
|
+
if (!updated || args.returning === false) {
|
|
63
63
|
return null;
|
|
64
64
|
}
|
|
65
|
-
return
|
|
65
|
+
return updated;
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
//# sourceMappingURL=updateOne.js.map
|
package/dist/updateOne.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/updateOne.ts"],"sourcesContent":["import type { UpdateOne } from 'payload'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/updateOne.ts"],"sourcesContent":["import type { UpdateOne } from 'payload'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { findFirst } from './utilities/findFirst.js'\nimport { getById, writeWithRetry } from './utilities/optimistic.js'\nimport { prepareCollectionWhere } from './utilities/prepareWhere.js'\nimport { projectForCollection } from './utilities/resolveSchema.js'\nimport { rethrowUniqueViolation } from './utilities/writeErrors.js'\nimport { putItem } from './utilities/writeItems.js'\n\n/**\n * v1 strategy: read-merge-write rather than building an `UpdateExpression`.\n * Payload sends a partial `data` patch; DynamoDB `UpdateItem` would let us\n * push the diff server-side, but its expression syntax (reserved words,\n * nested attribute paths, list indexing, removal semantics) is enough work to\n * earn its own milestone.\n *\n * The write is conditional on the row still being at the revision that was\n * read. If another writer got in first, the patch is re-applied to the fresh\n * row (`writeWithRetry`), so concurrent updates to different fields both\n * land.\n *\n * `id` on the resulting item always comes from the located target — we don't\n * let `data` rewrite the primary key, which would silently orphan the row.\n */\nexport const updateOne: UpdateOne = async function updateOne(this: DynamoAdapter, args) {\n const { collection, data } = args\n const partition = this.resolvePartition(collection)\n\n const byId = args.id !== undefined && args.id !== null\n const where = byId\n ? undefined\n : await prepareCollectionWhere(this, collection, args.where, args.locale)\n const read = () => {\n if (byId) return getById(this, partition, args.id)\n return where ? findFirst(this, { partition, where }) : Promise.resolve(null)\n }\n\n let updated: null | Record<string, unknown>\n try {\n updated = await writeWithRetry(await read(), read, async (target) => {\n const merged: Record<string, unknown> = {\n ...target,\n ...data,\n id: target['id'],\n // Bump `updatedAt` automatically. Explicit timestamps in `data` (e.g.\n // from version restores or migrations) still win because they land in\n // the spread above and we only override here when `data` didn't\n // supply one.\n updatedAt: data['updatedAt'] ?? new Date().toISOString(),\n }\n // Strip the merged result against the collection schema. Catches both\n // unknown keys in the incoming patch and any pre-existing leaked keys\n // on the persisted target — every update doubles as incremental cleanup.\n const projected = projectForCollection(this, collection, merged)\n await putItem(this, partition, projected, { replaces: target, unique: { collection } })\n return projected\n })\n } catch (err) {\n rethrowUniqueViolation(err, collection, args.req)\n throw err\n }\n\n if (!updated || args.returning === false) {\n return null as never\n }\n return updated as never\n}\n"],"names":["findFirst","getById","writeWithRetry","prepareCollectionWhere","projectForCollection","rethrowUniqueViolation","putItem","updateOne","args","collection","data","partition","resolvePartition","byId","id","undefined","where","locale","read","Promise","resolve","updated","target","merged","updatedAt","Date","toISOString","projected","replaces","unique","err","req","returning"],"mappings":"AAIA,SAASA,SAAS,QAAQ,2BAA0B;AACpD,SAASC,OAAO,EAAEC,cAAc,QAAQ,4BAA2B;AACnE,SAASC,sBAAsB,QAAQ,8BAA6B;AACpE,SAASC,oBAAoB,QAAQ,+BAA8B;AACnE,SAASC,sBAAsB,QAAQ,6BAA4B;AACnE,SAASC,OAAO,QAAQ,4BAA2B;AAEnD;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMC,YAAuB,eAAeA,UAA+BC,IAAI;IACpF,MAAM,EAAEC,UAAU,EAAEC,IAAI,EAAE,GAAGF;IAC7B,MAAMG,YAAY,IAAI,CAACC,gBAAgB,CAACH;IAExC,MAAMI,OAAOL,KAAKM,EAAE,KAAKC,aAAaP,KAAKM,EAAE,KAAK;IAClD,MAAME,QAAQH,OACVE,YACA,MAAMZ,uBAAuB,IAAI,EAAEM,YAAYD,KAAKQ,KAAK,EAAER,KAAKS,MAAM;IAC1E,MAAMC,OAAO;QACX,IAAIL,MAAM,OAAOZ,QAAQ,IAAI,EAAEU,WAAWH,KAAKM,EAAE;QACjD,OAAOE,QAAQhB,UAAU,IAAI,EAAE;YAAEW;YAAWK;QAAM,KAAKG,QAAQC,OAAO,CAAC;IACzE;IAEA,IAAIC;IACJ,IAAI;QACFA,UAAU,MAAMnB,eAAe,MAAMgB,QAAQA,MAAM,OAAOI;YACxD,MAAMC,SAAkC;gBACtC,GAAGD,MAAM;gBACT,GAAGZ,IAAI;gBACPI,IAAIQ,MAAM,CAAC,KAAK;gBAChB,sEAAsE;gBACtE,sEAAsE;gBACtE,gEAAgE;gBAChE,cAAc;gBACdE,WAAWd,IAAI,CAAC,YAAY,IAAI,IAAIe,OAAOC,WAAW;YACxD;YACA,sEAAsE;YACtE,sEAAsE;YACtE,yEAAyE;YACzE,MAAMC,YAAYvB,qBAAqB,IAAI,EAAEK,YAAYc;YACzD,MAAMjB,QAAQ,IAAI,EAAEK,WAAWgB,WAAW;gBAAEC,UAAUN;gBAAQO,QAAQ;oBAAEpB;gBAAW;YAAE;YACrF,OAAOkB;QACT;IACF,EAAE,OAAOG,KAAK;QACZzB,uBAAuByB,KAAKrB,YAAYD,KAAKuB,GAAG;QAChD,MAAMD;IACR;IAEA,IAAI,CAACT,WAAWb,KAAKwB,SAAS,KAAK,OAAO;QACxC,OAAO;IACT;IACA,OAAOX;AACT,EAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"updateVersion.d.ts","sourceRoot":"","sources":["../src/updateVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"updateVersion.d.ts","sourceRoot":"","sources":["../src/updateVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAM5C;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,EAAE,aAK3B,CAAA"}
|
package/dist/updateVersion.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { findFirst } from './utilities/findFirst.js';
|
|
3
|
-
import { normalizeForDynamo } from './utilities/normalizeForDynamo.js';
|
|
4
|
-
import { projectVersionRow } from './utilities/resolveSchema.js';
|
|
5
|
-
import { stripInternalKeys } from './utilities/stripInternalKeys.js';
|
|
1
|
+
import { updateVersionRow } from './utilities/updateVersionRow.js';
|
|
6
2
|
/**
|
|
7
3
|
* Read-merge-write against the versions partition. `versionData` is
|
|
8
4
|
* splatted onto the existing row, which means Payload-passed metadata
|
|
@@ -15,55 +11,10 @@ import { stripInternalKeys } from './utilities/stripInternalKeys.js';
|
|
|
15
11
|
* cascade because it knows it's making a new latest; `updateVersion` writes
|
|
16
12
|
* exactly what it's told.
|
|
17
13
|
*/ export const updateVersion = async function updateVersion(args) {
|
|
18
|
-
|
|
19
|
-
if (!docClient) {
|
|
20
|
-
throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
|
|
21
|
-
}
|
|
22
|
-
const partition = this.resolveVersionsPartition(args.collection);
|
|
23
|
-
let target = null;
|
|
24
|
-
if (args.id !== undefined && args.id !== null) {
|
|
25
|
-
const result = await docClient.send(new GetCommand({
|
|
26
|
-
TableName: this.tableName,
|
|
27
|
-
Key: {
|
|
28
|
-
pk: partition,
|
|
29
|
-
sk: String(args.id)
|
|
30
|
-
},
|
|
31
|
-
ConsistentRead: true
|
|
32
|
-
}));
|
|
33
|
-
target = result.Item ? stripInternalKeys(result.Item) : null;
|
|
34
|
-
} else if (args.where) {
|
|
35
|
-
target = await findFirst(this, {
|
|
36
|
-
partition,
|
|
37
|
-
where: args.where
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
if (!target) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
const merged = {
|
|
44
|
-
...target,
|
|
45
|
-
...args.versionData,
|
|
46
|
-
id: target['id']
|
|
47
|
-
};
|
|
48
|
-
// Project both the version-row metadata layer and the inner `version`
|
|
49
|
-
// snapshot. Catches stray keys at either layer plus stale leaks already
|
|
50
|
-
// persisted on the target.
|
|
51
|
-
const projected = projectVersionRow(this, {
|
|
14
|
+
return updateVersionRow(this, {
|
|
52
15
|
kind: 'collection',
|
|
53
16
|
slug: args.collection
|
|
54
|
-
},
|
|
55
|
-
await docClient.send(new PutCommand({
|
|
56
|
-
TableName: this.tableName,
|
|
57
|
-
Item: normalizeForDynamo({
|
|
58
|
-
...projected,
|
|
59
|
-
pk: partition,
|
|
60
|
-
sk: String(projected['id'])
|
|
61
|
-
})
|
|
62
|
-
}));
|
|
63
|
-
if (args.returning === false) {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
return projected;
|
|
17
|
+
}, args);
|
|
67
18
|
};
|
|
68
19
|
|
|
69
20
|
//# sourceMappingURL=updateVersion.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/updateVersion.ts"],"sourcesContent":["import type { UpdateVersion } from 'payload'\n\nimport
|
|
1
|
+
{"version":3,"sources":["../src/updateVersion.ts"],"sourcesContent":["import type { UpdateVersion } from 'payload'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { updateVersionRow } from './utilities/updateVersionRow.js'\n\n/**\n * Read-merge-write against the versions partition. `versionData` is\n * splatted onto the existing row, which means Payload-passed metadata\n * (`createdAt`, `updatedAt`, `latest`, `parent`, `publishedLocale`) and the\n * inner `version` payload all overlay correctly.\n *\n * The adapter does not cascade `latest=true` flips — Payload is responsible\n * for clearing the previous latest by issuing a separate `updateVersion`\n * call. This matches the `createVersion` boundary: `createVersion` owns the\n * cascade because it knows it's making a new latest; `updateVersion` writes\n * exactly what it's told.\n */\nexport const updateVersion: UpdateVersion = async function updateVersion(\n this: DynamoAdapter,\n args,\n) {\n return updateVersionRow(this, { kind: 'collection', slug: args.collection }, args) as never\n}\n"],"names":["updateVersionRow","updateVersion","args","kind","slug","collection"],"mappings":"AAIA,SAASA,gBAAgB,QAAQ,kCAAiC;AAElE;;;;;;;;;;;CAWC,GACD,OAAO,MAAMC,gBAA+B,eAAeA,cAEzDC,IAAI;IAEJ,OAAOF,iBAAiB,IAAI,EAAE;QAAEG,MAAM;QAAcC,MAAMF,KAAKG,UAAU;IAAC,GAAGH;AAC/E,EAAC"}
|
package/dist/upsert.d.ts
CHANGED
|
@@ -2,6 +2,12 @@ import type { Upsert } from 'payload';
|
|
|
2
2
|
/**
|
|
3
3
|
* Locate by `where`; merge-and-put if found, put-with-fresh-id otherwise.
|
|
4
4
|
*
|
|
5
|
+
* Both branches are conditional — the update on the row being unchanged
|
|
6
|
+
* since it was read, the insert on no row having appeared at that id — and
|
|
7
|
+
* the whole locate-then-write is retried when the condition fails, so two
|
|
8
|
+
* concurrent upserts of the same thing converge instead of clobbering each
|
|
9
|
+
* other.
|
|
10
|
+
*
|
|
5
11
|
* Inlined rather than dispatching to `create`/`updateOne` via `.call(this)` —
|
|
6
12
|
* piping through their `this`-typed signatures requires casts that obscure
|
|
7
13
|
* the small amount of logic this function actually owns.
|
package/dist/upsert.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../src/upsert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"upsert.d.ts","sourceRoot":"","sources":["../src/upsert.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAarC;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,MAAM,EAAE,MA+CpB,CAAA"}
|
package/dist/upsert.js
CHANGED
|
@@ -1,55 +1,71 @@
|
|
|
1
|
-
import { PutCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
1
|
import { randomUUID } from 'node:crypto';
|
|
3
2
|
import { findFirst } from './utilities/findFirst.js';
|
|
4
|
-
import {
|
|
3
|
+
import { backoff, conflictError, MAX_WRITE_ATTEMPTS } from './utilities/optimistic.js';
|
|
4
|
+
import { prepareCollectionWhere } from './utilities/prepareWhere.js';
|
|
5
5
|
import { projectForCollection } from './utilities/resolveSchema.js';
|
|
6
|
+
import { rethrowUniqueViolation } from './utilities/writeErrors.js';
|
|
7
|
+
import { isConditionalCheckFailed, putItem } from './utilities/writeItems.js';
|
|
6
8
|
/**
|
|
7
9
|
* Locate by `where`; merge-and-put if found, put-with-fresh-id otherwise.
|
|
8
10
|
*
|
|
11
|
+
* Both branches are conditional — the update on the row being unchanged
|
|
12
|
+
* since it was read, the insert on no row having appeared at that id — and
|
|
13
|
+
* the whole locate-then-write is retried when the condition fails, so two
|
|
14
|
+
* concurrent upserts of the same thing converge instead of clobbering each
|
|
15
|
+
* other.
|
|
16
|
+
*
|
|
9
17
|
* Inlined rather than dispatching to `create`/`updateOne` via `.call(this)` —
|
|
10
18
|
* piping through their `this`-typed signatures requires casts that obscure
|
|
11
19
|
* the small amount of logic this function actually owns.
|
|
12
|
-
*/ export const upsert = async function upsert({ collection, data, returning, where }) {
|
|
13
|
-
const
|
|
14
|
-
if (!docClient) {
|
|
15
|
-
throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
|
|
16
|
-
}
|
|
20
|
+
*/ export const upsert = async function upsert({ collection, data, locale, req, returning, where: rawWhere }) {
|
|
21
|
+
const where = await prepareCollectionWhere(this, collection, rawWhere, locale);
|
|
17
22
|
const partition = this.resolvePartition(collection);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
23
|
+
for(let attempt = 1;; attempt++){
|
|
24
|
+
const found = await findFirst(this, {
|
|
25
|
+
partition,
|
|
26
|
+
where
|
|
27
|
+
});
|
|
28
|
+
const now = new Date().toISOString();
|
|
29
|
+
let item;
|
|
30
|
+
if (found) {
|
|
31
|
+
// Bump `updatedAt` automatically; explicit value in `data` still wins.
|
|
32
|
+
item = {
|
|
33
|
+
...found,
|
|
34
|
+
...data,
|
|
35
|
+
id: found['id'],
|
|
36
|
+
updatedAt: data['updatedAt'] ?? now
|
|
37
|
+
};
|
|
38
|
+
} else {
|
|
39
|
+
const id = data['id'] ?? randomUUID();
|
|
40
|
+
// Same nullish-coalesce pattern as `create`: explicit values in `data`
|
|
41
|
+
// win, but a payload-passed `undefined` doesn't wipe the default.
|
|
42
|
+
item = {
|
|
43
|
+
...data,
|
|
44
|
+
id,
|
|
45
|
+
createdAt: data['createdAt'] ?? now,
|
|
46
|
+
updatedAt: data['updatedAt'] ?? now
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
const projected = projectForCollection(this, collection, item);
|
|
50
|
+
try {
|
|
51
|
+
await putItem(this, partition, projected, {
|
|
52
|
+
...found ? {
|
|
53
|
+
replaces: found
|
|
54
|
+
} : {
|
|
55
|
+
mustNotExist: true
|
|
56
|
+
},
|
|
57
|
+
unique: {
|
|
58
|
+
collection
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
return returning === false ? null : projected;
|
|
62
|
+
} catch (err) {
|
|
63
|
+
rethrowUniqueViolation(err, collection, req);
|
|
64
|
+
if (!isConditionalCheckFailed(err)) throw err;
|
|
65
|
+
if (attempt >= MAX_WRITE_ATTEMPTS) throw conflictError();
|
|
66
|
+
await backoff(attempt);
|
|
67
|
+
}
|
|
42
68
|
}
|
|
43
|
-
const projected = projectForCollection(this, collection, item);
|
|
44
|
-
await docClient.send(new PutCommand({
|
|
45
|
-
TableName: this.tableName,
|
|
46
|
-
Item: normalizeForDynamo({
|
|
47
|
-
...projected,
|
|
48
|
-
pk: partition,
|
|
49
|
-
sk: String(projected['id'])
|
|
50
|
-
})
|
|
51
|
-
}));
|
|
52
|
-
return returning === false ? null : projected;
|
|
53
69
|
};
|
|
54
70
|
|
|
55
71
|
//# sourceMappingURL=upsert.js.map
|
package/dist/upsert.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/upsert.ts"],"sourcesContent":["import type { Upsert } from 'payload'\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../src/upsert.ts"],"sourcesContent":["import type { Upsert } from 'payload'\n\nimport { randomUUID } from 'node:crypto'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { findFirst } from './utilities/findFirst.js'\nimport { backoff, conflictError, MAX_WRITE_ATTEMPTS } from './utilities/optimistic.js'\nimport { prepareCollectionWhere } from './utilities/prepareWhere.js'\nimport { projectForCollection } from './utilities/resolveSchema.js'\nimport { rethrowUniqueViolation } from './utilities/writeErrors.js'\nimport { isConditionalCheckFailed, putItem } from './utilities/writeItems.js'\n\n/**\n * Locate by `where`; merge-and-put if found, put-with-fresh-id otherwise.\n *\n * Both branches are conditional — the update on the row being unchanged\n * since it was read, the insert on no row having appeared at that id — and\n * the whole locate-then-write is retried when the condition fails, so two\n * concurrent upserts of the same thing converge instead of clobbering each\n * other.\n *\n * Inlined rather than dispatching to `create`/`updateOne` via `.call(this)` —\n * piping through their `this`-typed signatures requires casts that obscure\n * the small amount of logic this function actually owns.\n */\nexport const upsert: Upsert = async function upsert(\n this: DynamoAdapter,\n { collection, data, locale, req, returning, where: rawWhere },\n) {\n const where = await prepareCollectionWhere(this, collection, rawWhere, locale)\n const partition = this.resolvePartition(collection)\n\n for (let attempt = 1; ; attempt++) {\n const found = await findFirst(this, { partition, where })\n\n const now = new Date().toISOString()\n let item: Record<string, unknown>\n if (found) {\n // Bump `updatedAt` automatically; explicit value in `data` still wins.\n item = {\n ...found,\n ...data,\n id: found['id'],\n updatedAt: data['updatedAt'] ?? now,\n }\n } else {\n const id = data['id'] ?? randomUUID()\n // Same nullish-coalesce pattern as `create`: explicit values in `data`\n // win, but a payload-passed `undefined` doesn't wipe the default.\n item = {\n ...data,\n id,\n createdAt: data['createdAt'] ?? now,\n updatedAt: data['updatedAt'] ?? now,\n }\n }\n\n const projected = projectForCollection(this, collection, item)\n\n try {\n await putItem(this, partition, projected, {\n ...(found ? { replaces: found } : { mustNotExist: true }),\n unique: { collection },\n })\n return returning === false ? (null as never) : (projected as never)\n } catch (err) {\n rethrowUniqueViolation(err, collection, req)\n if (!isConditionalCheckFailed(err)) throw err\n if (attempt >= MAX_WRITE_ATTEMPTS) throw conflictError()\n await backoff(attempt)\n }\n }\n}\n"],"names":["randomUUID","findFirst","backoff","conflictError","MAX_WRITE_ATTEMPTS","prepareCollectionWhere","projectForCollection","rethrowUniqueViolation","isConditionalCheckFailed","putItem","upsert","collection","data","locale","req","returning","where","rawWhere","partition","resolvePartition","attempt","found","now","Date","toISOString","item","id","updatedAt","createdAt","projected","replaces","mustNotExist","unique","err"],"mappings":"AAEA,SAASA,UAAU,QAAQ,cAAa;AAIxC,SAASC,SAAS,QAAQ,2BAA0B;AACpD,SAASC,OAAO,EAAEC,aAAa,EAAEC,kBAAkB,QAAQ,4BAA2B;AACtF,SAASC,sBAAsB,QAAQ,8BAA6B;AACpE,SAASC,oBAAoB,QAAQ,+BAA8B;AACnE,SAASC,sBAAsB,QAAQ,6BAA4B;AACnE,SAASC,wBAAwB,EAAEC,OAAO,QAAQ,4BAA2B;AAE7E;;;;;;;;;;;;CAYC,GACD,OAAO,MAAMC,SAAiB,eAAeA,OAE3C,EAAEC,UAAU,EAAEC,IAAI,EAAEC,MAAM,EAAEC,GAAG,EAAEC,SAAS,EAAEC,OAAOC,QAAQ,EAAE;IAE7D,MAAMD,QAAQ,MAAMX,uBAAuB,IAAI,EAAEM,YAAYM,UAAUJ;IACvE,MAAMK,YAAY,IAAI,CAACC,gBAAgB,CAACR;IAExC,IAAK,IAAIS,UAAU,IAAKA,UAAW;QACjC,MAAMC,QAAQ,MAAMpB,UAAU,IAAI,EAAE;YAAEiB;YAAWF;QAAM;QAEvD,MAAMM,MAAM,IAAIC,OAAOC,WAAW;QAClC,IAAIC;QACJ,IAAIJ,OAAO;YACT,uEAAuE;YACvEI,OAAO;gBACL,GAAGJ,KAAK;gBACR,GAAGT,IAAI;gBACPc,IAAIL,KAAK,CAAC,KAAK;gBACfM,WAAWf,IAAI,CAAC,YAAY,IAAIU;YAClC;QACF,OAAO;YACL,MAAMI,KAAKd,IAAI,CAAC,KAAK,IAAIZ;YACzB,uEAAuE;YACvE,kEAAkE;YAClEyB,OAAO;gBACL,GAAGb,IAAI;gBACPc;gBACAE,WAAWhB,IAAI,CAAC,YAAY,IAAIU;gBAChCK,WAAWf,IAAI,CAAC,YAAY,IAAIU;YAClC;QACF;QAEA,MAAMO,YAAYvB,qBAAqB,IAAI,EAAEK,YAAYc;QAEzD,IAAI;YACF,MAAMhB,QAAQ,IAAI,EAAES,WAAWW,WAAW;gBACxC,GAAIR,QAAQ;oBAAES,UAAUT;gBAAM,IAAI;oBAAEU,cAAc;gBAAK,CAAC;gBACxDC,QAAQ;oBAAErB;gBAAW;YACvB;YACA,OAAOI,cAAc,QAAS,OAAkBc;QAClD,EAAE,OAAOI,KAAK;YACZ1B,uBAAuB0B,KAAKtB,YAAYG;YACxC,IAAI,CAACN,yBAAyByB,MAAM,MAAMA;YAC1C,IAAIb,WAAWhB,oBAAoB,MAAMD;YACzC,MAAMD,QAAQkB;QAChB;IACF;AACF,EAAC"}
|