@open-aih/payload-ddb 3.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/LICENSE +21 -0
- package/README.md +146 -0
- package/dist/connect.d.ts +3 -0
- package/dist/connect.d.ts.map +1 -0
- package/dist/connect.js +6 -0
- package/dist/connect.js.map +1 -0
- package/dist/count.d.ts +3 -0
- package/dist/count.d.ts.map +1 -0
- package/dist/count.js +9 -0
- package/dist/count.js.map +1 -0
- package/dist/countGlobalVersions.d.ts +3 -0
- package/dist/countGlobalVersions.d.ts.map +1 -0
- package/dist/countGlobalVersions.js +9 -0
- package/dist/countGlobalVersions.js.map +1 -0
- package/dist/countVersions.d.ts +3 -0
- package/dist/countVersions.d.ts.map +1 -0
- package/dist/countVersions.js +9 -0
- package/dist/countVersions.js.map +1 -0
- package/dist/create.d.ts +3 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +38 -0
- package/dist/create.js.map +1 -0
- package/dist/createGlobal.d.ts +8 -0
- package/dist/createGlobal.d.ts.map +1 -0
- package/dist/createGlobal.js +29 -0
- package/dist/createGlobal.js.map +1 -0
- package/dist/createGlobalVersion.d.ts +8 -0
- package/dist/createGlobalVersion.d.ts.map +1 -0
- package/dist/createGlobalVersion.js +80 -0
- package/dist/createGlobalVersion.js.map +1 -0
- package/dist/createVersion.d.ts +18 -0
- package/dist/createVersion.d.ts.map +1 -0
- package/dist/createVersion.js +106 -0
- package/dist/createVersion.js.map +1 -0
- package/dist/deleteMany.d.ts +12 -0
- package/dist/deleteMany.d.ts.map +1 -0
- package/dist/deleteMany.js +27 -0
- package/dist/deleteMany.js.map +1 -0
- package/dist/deleteOne.d.ts +3 -0
- package/dist/deleteOne.d.ts.map +1 -0
- package/dist/deleteOne.js +48 -0
- package/dist/deleteOne.js.map +1 -0
- package/dist/deleteVersions.d.ts +9 -0
- package/dist/deleteVersions.d.ts.map +1 -0
- package/dist/deleteVersions.js +28 -0
- package/dist/deleteVersions.js.map +1 -0
- package/dist/destroy.d.ts +3 -0
- package/dist/destroy.d.ts.map +1 -0
- package/dist/destroy.js +10 -0
- package/dist/destroy.js.map +1 -0
- package/dist/find.d.ts +20 -0
- package/dist/find.d.ts.map +1 -0
- package/dist/find.js +54 -0
- package/dist/find.js.map +1 -0
- package/dist/findDistinct.d.ts +13 -0
- package/dist/findDistinct.d.ts.map +1 -0
- package/dist/findDistinct.js +52 -0
- package/dist/findDistinct.js.map +1 -0
- package/dist/findGlobal.d.ts +11 -0
- package/dist/findGlobal.d.ts.map +1 -0
- package/dist/findGlobal.js +34 -0
- package/dist/findGlobal.js.map +1 -0
- package/dist/findGlobalVersions.d.ts +3 -0
- package/dist/findGlobalVersions.d.ts.map +1 -0
- package/dist/findGlobalVersions.js +32 -0
- package/dist/findGlobalVersions.js.map +1 -0
- package/dist/findOne.d.ts +9 -0
- package/dist/findOne.d.ts.map +1 -0
- package/dist/findOne.js +22 -0
- package/dist/findOne.js.map +1 -0
- package/dist/findVersions.d.ts +10 -0
- package/dist/findVersions.d.ts.map +1 -0
- package/dist/findVersions.js +42 -0
- package/dist/findVersions.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +98 -0
- package/dist/index.js.map +1 -0
- package/dist/init.d.ts +16 -0
- package/dist/init.d.ts.map +1 -0
- package/dist/init.js +24 -0
- package/dist/init.js.map +1 -0
- package/dist/queryDrafts.d.ts +3 -0
- package/dist/queryDrafts.d.ts.map +1 -0
- package/dist/queryDrafts.js +103 -0
- package/dist/queryDrafts.js.map +1 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/updateGlobal.d.ts +9 -0
- package/dist/updateGlobal.d.ts.map +1 -0
- package/dist/updateGlobal.js +45 -0
- package/dist/updateGlobal.js.map +1 -0
- package/dist/updateGlobalVersion.d.ts +8 -0
- package/dist/updateGlobalVersion.d.ts.map +1 -0
- package/dist/updateGlobalVersion.js +59 -0
- package/dist/updateGlobalVersion.js.map +1 -0
- package/dist/updateMany.d.ts +13 -0
- package/dist/updateMany.d.ts.map +1 -0
- package/dist/updateMany.js +48 -0
- package/dist/updateMany.js.map +1 -0
- package/dist/updateOne.d.ts +13 -0
- package/dist/updateOne.d.ts.map +1 -0
- package/dist/updateOne.js +68 -0
- package/dist/updateOne.js.map +1 -0
- package/dist/updateVersion.d.ts +15 -0
- package/dist/updateVersion.d.ts.map +1 -0
- package/dist/updateVersion.js +69 -0
- package/dist/updateVersion.js.map +1 -0
- package/dist/upsert.d.ts +10 -0
- package/dist/upsert.d.ts.map +1 -0
- package/dist/upsert.js +55 -0
- package/dist/upsert.js.map +1 -0
- package/dist/utilities/applySorts.d.ts +11 -0
- package/dist/utilities/applySorts.d.ts.map +1 -0
- package/dist/utilities/applySorts.js +47 -0
- package/dist/utilities/applySorts.js.map +1 -0
- package/dist/utilities/buildFilterExpression.d.ts +42 -0
- package/dist/utilities/buildFilterExpression.d.ts.map +1 -0
- package/dist/utilities/buildFilterExpression.js +137 -0
- package/dist/utilities/buildFilterExpression.js.map +1 -0
- package/dist/utilities/draftsFallback.d.ts +28 -0
- package/dist/utilities/draftsFallback.d.ts.map +1 -0
- package/dist/utilities/draftsFallback.js +52 -0
- package/dist/utilities/draftsFallback.js.map +1 -0
- package/dist/utilities/ensureConnected.d.ts +12 -0
- package/dist/utilities/ensureConnected.d.ts.map +1 -0
- package/dist/utilities/ensureConnected.js +20 -0
- package/dist/utilities/ensureConnected.js.map +1 -0
- package/dist/utilities/ensureTable.d.ts +16 -0
- package/dist/utilities/ensureTable.d.ts.map +1 -0
- package/dist/utilities/ensureTable.js +62 -0
- package/dist/utilities/ensureTable.js.map +1 -0
- package/dist/utilities/findFirst.d.ts +16 -0
- package/dist/utilities/findFirst.d.ts.map +1 -0
- package/dist/utilities/findFirst.js +79 -0
- package/dist/utilities/findFirst.js.map +1 -0
- package/dist/utilities/getByPath.d.ts +9 -0
- package/dist/utilities/getByPath.d.ts.map +1 -0
- package/dist/utilities/getByPath.js +22 -0
- package/dist/utilities/getByPath.js.map +1 -0
- package/dist/utilities/matchesWhere.d.ts +12 -0
- package/dist/utilities/matchesWhere.d.ts.map +1 -0
- package/dist/utilities/matchesWhere.js +98 -0
- package/dist/utilities/matchesWhere.js.map +1 -0
- package/dist/utilities/normalizeForDynamo.d.ts +15 -0
- package/dist/utilities/normalizeForDynamo.d.ts.map +1 -0
- package/dist/utilities/normalizeForDynamo.js +27 -0
- package/dist/utilities/normalizeForDynamo.js.map +1 -0
- package/dist/utilities/operators.d.ts +26 -0
- package/dist/utilities/operators.d.ts.map +1 -0
- package/dist/utilities/operators.js +58 -0
- package/dist/utilities/operators.js.map +1 -0
- package/dist/utilities/pickConfiguredFields.d.ts +10 -0
- package/dist/utilities/pickConfiguredFields.d.ts.map +1 -0
- package/dist/utilities/pickConfiguredFields.js +113 -0
- package/dist/utilities/pickConfiguredFields.js.map +1 -0
- package/dist/utilities/queryCount.d.ts +9 -0
- package/dist/utilities/queryCount.d.ts.map +1 -0
- package/dist/utilities/queryCount.js +56 -0
- package/dist/utilities/queryCount.js.map +1 -0
- package/dist/utilities/queryMatching.d.ts +18 -0
- package/dist/utilities/queryMatching.d.ts.map +1 -0
- package/dist/utilities/queryMatching.js +71 -0
- package/dist/utilities/queryMatching.js.map +1 -0
- package/dist/utilities/resolveSchema.d.ts +56 -0
- package/dist/utilities/resolveSchema.d.ts.map +1 -0
- package/dist/utilities/resolveSchema.js +84 -0
- package/dist/utilities/resolveSchema.js.map +1 -0
- package/dist/utilities/scrubUnknownFields.d.ts +44 -0
- package/dist/utilities/scrubUnknownFields.d.ts.map +1 -0
- package/dist/utilities/scrubUnknownFields.js +88 -0
- package/dist/utilities/scrubUnknownFields.js.map +1 -0
- package/dist/utilities/stripInternalKeys.d.ts +7 -0
- package/dist/utilities/stripInternalKeys.d.ts.map +1 -0
- package/dist/utilities/stripInternalKeys.js +10 -0
- package/dist/utilities/stripInternalKeys.js.map +1 -0
- package/dist/utilities/whereToId.d.ts +11 -0
- package/dist/utilities/whereToId.d.ts.map +1 -0
- package/dist/utilities/whereToId.js +38 -0
- package/dist/utilities/whereToId.js.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dbyron10
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# payload-ddb
|
|
2
|
+
|
|
3
|
+
An unofficial database adapter for [PayloadCMS](https://payloadcms.com) that
|
|
4
|
+
stores data in [Amazon DynamoDB](https://aws.amazon.com/dynamodb/).
|
|
5
|
+
|
|
6
|
+
> **Status:** all `BaseDatabaseAdapter` methods are implemented end-to-end. The
|
|
7
|
+
> common access patterns (CRUD, drafts, versions, globals, locales-as-fields)
|
|
8
|
+
> work today. The current strategy is "correct first, optimize later" — single
|
|
9
|
+
> shared table with a `Query`-per-collection access pattern, no GSIs, no range
|
|
10
|
+
> operators in `FilterExpression`. Suitable for development and
|
|
11
|
+
> small-to-medium workloads; large collections will want the optimization
|
|
12
|
+
> milestones tracked in the issues.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add @aih-software/payload-ddb
|
|
18
|
+
# or
|
|
19
|
+
npm install @aih-software/payload-ddb
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Quick start (DynamoDB Local)
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { buildConfig } from 'payload'
|
|
26
|
+
import { dynamoAdapter } from '@aih-software/payload-ddb'
|
|
27
|
+
|
|
28
|
+
export default buildConfig({
|
|
29
|
+
db: dynamoAdapter({
|
|
30
|
+
ensureTables: true, // auto-create the table on startup; turn off in prod
|
|
31
|
+
clientConfig: {
|
|
32
|
+
endpoint: 'http://localhost:8000',
|
|
33
|
+
region: 'us-east-1',
|
|
34
|
+
credentials: {
|
|
35
|
+
accessKeyId: 'fake',
|
|
36
|
+
secretAccessKey: 'fake',
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
// ...rest of config
|
|
41
|
+
})
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Production setup
|
|
45
|
+
|
|
46
|
+
The single backing table should be provisioned out-of-band (CDK, Terraform,
|
|
47
|
+
CloudFormation) so the adapter doesn't need IAM permissions to create or
|
|
48
|
+
describe tables.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
db: dynamoAdapter({
|
|
52
|
+
// ensureTables defaults to false — leave it off
|
|
53
|
+
tableName: 'payload',
|
|
54
|
+
clientConfig: {
|
|
55
|
+
region: 'us-east-1',
|
|
56
|
+
// credentials resolved from the default chain (IAM role, env, etc.)
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Provision the table with a composite key:
|
|
62
|
+
- Partition key: `pk` (String)
|
|
63
|
+
- Sort key: `sk` (String)
|
|
64
|
+
- Billing: on-demand (`PAY_PER_REQUEST`) or provisioned, your call
|
|
65
|
+
|
|
66
|
+
Every collection, global, and versions stream lives under a different `pk`
|
|
67
|
+
value in the same physical table.
|
|
68
|
+
|
|
69
|
+
## Configuration
|
|
70
|
+
|
|
71
|
+
| Option | Type | Default | Description |
|
|
72
|
+
|---|---|---|---|
|
|
73
|
+
| `clientConfig` | `DynamoDBClientConfig` | `{}` | Passed straight to `new DynamoDBClient(...)`. Region, endpoint, credentials, retry strategy, etc. |
|
|
74
|
+
| `client` | `DynamoDBClient` | — | Pre-built client. If supplied, `clientConfig` is ignored and the adapter does **not** call `.destroy()` on shutdown. |
|
|
75
|
+
| `translateConfig` | `TranslateConfig` | `removeUndefinedValues: true`, `convertClassInstanceToMap: true` | Marshalling options forwarded to `DynamoDBDocumentClient.from`. |
|
|
76
|
+
| `tableName` | `string` | `'payload'` | The single DynamoDB table backing every collection, global, and versions stream. |
|
|
77
|
+
| `ensureTables` | `boolean` | `false` | When true, auto-creates the table at init if it doesn't exist. Dev-loop convenience; turn off in production. |
|
|
78
|
+
|
|
79
|
+
## Data layout
|
|
80
|
+
|
|
81
|
+
- **One DynamoDB table for everything.** Every row carries a synthetic
|
|
82
|
+
`pk`/`sk` pair that the adapter strips on read so callers never see them.
|
|
83
|
+
- **Collection documents:** `pk = "<slug>"`, `sk = "<id>"`.
|
|
84
|
+
- **Globals:** `pk = "<slug>"`, `sk = "<slug>"` — single-row partition since
|
|
85
|
+
globals are singletons.
|
|
86
|
+
- **Versions:** `pk = "<slug>_versions"`, `sk = "<versionId>"` for both
|
|
87
|
+
collections and globals.
|
|
88
|
+
- **Sort key is always String.** Number-typed collection ids are coerced to
|
|
89
|
+
strings for `sk` while the original numeric `id` attribute is preserved on
|
|
90
|
+
the row.
|
|
91
|
+
- **No GSIs or LSIs in v2.** Per-collection reads are `Query`s scoped by `pk`;
|
|
92
|
+
predicates beyond `pk` get pushed to `FilterExpression` (DynamoDB still
|
|
93
|
+
reads every row in the partition before filtering).
|
|
94
|
+
|
|
95
|
+
## What's implemented
|
|
96
|
+
|
|
97
|
+
All `BaseDatabaseAdapter` methods: `create`, `find`, `findOne`, `findDistinct`,
|
|
98
|
+
`count`, `updateOne`, `updateMany`, `deleteOne`, `deleteMany`, `upsert`,
|
|
99
|
+
`createGlobal`/`findGlobal`/`updateGlobal`, the five collection version methods,
|
|
100
|
+
the four global version methods, and `queryDrafts`.
|
|
101
|
+
|
|
102
|
+
Supported `where` operators: `equals`, `not_equals`, `greater_than`,
|
|
103
|
+
`greater_than_equal`, `less_than`, `less_than_equal`, `in`, `not_in`, `exists`,
|
|
104
|
+
`like`, plus `and` / `or` composition. Anything else throws by design so
|
|
105
|
+
coverage gaps surface loudly. `like` is case-insensitive and evaluated in JS
|
|
106
|
+
(DynamoDB's `contains()` is case-sensitive, so the adapter doesn't push it
|
|
107
|
+
down).
|
|
108
|
+
|
|
109
|
+
## Known limits / future work
|
|
110
|
+
|
|
111
|
+
- **No GSI routing.** Every non-id read is a `Query` against the collection's
|
|
112
|
+
partition (with `FilterExpression` pushdown — DynamoDB still reads every
|
|
113
|
+
row in the partition internally, but only matching rows come back over the
|
|
114
|
+
wire). Adding GSIs that mirror Payload's common access patterns
|
|
115
|
+
(e.g. `email` for auth, `slug` for public-facing collections) is the
|
|
116
|
+
highest-impact remaining optimization milestone.
|
|
117
|
+
- **Payload-level transactions are no-op.** `beginTransaction` returns `null`;
|
|
118
|
+
commit/rollback do nothing. Per-method atomicity (e.g. `createVersion`'s
|
|
119
|
+
flip+put) uses DynamoDB `TransactWriteItems` already; full Payload-context
|
|
120
|
+
transaction buffering across multiple adapter calls is its own milestone.
|
|
121
|
+
|
|
122
|
+
## Development
|
|
123
|
+
|
|
124
|
+
The package ships with a vitest integration test harness that boots Payload
|
|
125
|
+
against DynamoDB Local. Each spec file uses a unique table name so vitest
|
|
126
|
+
workers can run in parallel against a single shared container. Also make sure port 8000 is open.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
pnpm install
|
|
130
|
+
pnpm docker:start # boots amazon/dynamodb-local on :8001
|
|
131
|
+
pnpm test # runs every test/**/*.int.spec.ts once
|
|
132
|
+
pnpm test:watch # vitest in watch mode
|
|
133
|
+
pnpm typecheck:test # tsc -p test/tsconfig.json
|
|
134
|
+
pnpm docker:stop # tears the container down
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
`pnpm test` fails fast with a friendly message if the container isn't
|
|
138
|
+
reachable. Override the host/port with `PAYLOAD_DDB_TEST_HOST` /
|
|
139
|
+
`PAYLOAD_DDB_TEST_PORT` if 8001 conflicts on your machine.
|
|
140
|
+
|
|
141
|
+
See [`test/README.md`](./test/README.md) for the harness layout and how to
|
|
142
|
+
add a new spec.
|
|
143
|
+
|
|
144
|
+
## License
|
|
145
|
+
|
|
146
|
+
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.d.ts","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAMtC,eAAO,MAAM,OAAO,EAAE,OAErB,CAAA"}
|
package/dist/connect.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connect.ts"],"sourcesContent":["import type { Connect } from 'payload'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { ensureConnected } from './utilities/ensureConnected.js'\n\nexport const connect: Connect = async function (this: DynamoAdapter) {\n ensureConnected(this)\n}\n"],"names":["ensureConnected","connect"],"mappings":"AAIA,SAASA,eAAe,QAAQ,iCAAgC;AAEhE,OAAO,MAAMC,UAAmB;IAC9BD,gBAAgB,IAAI;AACtB,EAAC"}
|
package/dist/count.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"count.d.ts","sourceRoot":"","sources":["../src/count.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAMpC,eAAO,MAAM,KAAK,EAAE,KAGnB,CAAA"}
|
package/dist/count.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { queryCount } from './utilities/queryCount.js';
|
|
2
|
+
export const count = async function count({ collection, where }) {
|
|
3
|
+
const totalDocs = await queryCount(this, this.resolvePartition(collection), where);
|
|
4
|
+
return {
|
|
5
|
+
totalDocs
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=count.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/count.ts"],"sourcesContent":["import type { Count } from 'payload'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { queryCount } from './utilities/queryCount.js'\n\nexport const count: Count = async function count(this: DynamoAdapter, { collection, where }) {\n const totalDocs = await queryCount(this, this.resolvePartition(collection), where)\n return { totalDocs }\n}\n"],"names":["queryCount","count","collection","where","totalDocs","resolvePartition"],"mappings":"AAIA,SAASA,UAAU,QAAQ,4BAA2B;AAEtD,OAAO,MAAMC,QAAe,eAAeA,MAA2B,EAAEC,UAAU,EAAEC,KAAK,EAAE;IACzF,MAAMC,YAAY,MAAMJ,WAAW,IAAI,EAAE,IAAI,CAACK,gBAAgB,CAACH,aAAaC;IAC5E,OAAO;QAAEC;IAAU;AACrB,EAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countGlobalVersions.d.ts","sourceRoot":"","sources":["../src/countGlobalVersions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAMlD,eAAO,MAAM,mBAAmB,EAAE,mBAMjC,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { queryCount } from './utilities/queryCount.js';
|
|
2
|
+
export const countGlobalVersions = async function countGlobalVersions({ global, where }) {
|
|
3
|
+
const totalDocs = await queryCount(this, this.resolveVersionsPartition(global), where);
|
|
4
|
+
return {
|
|
5
|
+
totalDocs
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=countGlobalVersions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/countGlobalVersions.ts"],"sourcesContent":["import type { CountGlobalVersions } from 'payload'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { queryCount } from './utilities/queryCount.js'\n\nexport const countGlobalVersions: CountGlobalVersions = async function countGlobalVersions(\n this: DynamoAdapter,\n { global, where },\n) {\n const totalDocs = await queryCount(this, this.resolveVersionsPartition(global), where)\n return { totalDocs }\n}\n"],"names":["queryCount","countGlobalVersions","global","where","totalDocs","resolveVersionsPartition"],"mappings":"AAIA,SAASA,UAAU,QAAQ,4BAA2B;AAEtD,OAAO,MAAMC,sBAA2C,eAAeA,oBAErE,EAAEC,MAAM,EAAEC,KAAK,EAAE;IAEjB,MAAMC,YAAY,MAAMJ,WAAW,IAAI,EAAE,IAAI,CAACK,wBAAwB,CAACH,SAASC;IAChF,OAAO;QAAEC;IAAU;AACrB,EAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"countVersions.d.ts","sourceRoot":"","sources":["../src/countVersions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAM5C,eAAO,MAAM,aAAa,EAAE,aAM3B,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { queryCount } from './utilities/queryCount.js';
|
|
2
|
+
export const countVersions = async function countVersions({ collection, where }) {
|
|
3
|
+
const totalDocs = await queryCount(this, this.resolveVersionsPartition(collection), where);
|
|
4
|
+
return {
|
|
5
|
+
totalDocs
|
|
6
|
+
};
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
//# sourceMappingURL=countVersions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/countVersions.ts"],"sourcesContent":["import type { CountVersions } from 'payload'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { queryCount } from './utilities/queryCount.js'\n\nexport const countVersions: CountVersions = async function countVersions(\n this: DynamoAdapter,\n { collection, where },\n) {\n const totalDocs = await queryCount(this, this.resolveVersionsPartition(collection), where)\n return { totalDocs }\n}\n"],"names":["queryCount","countVersions","collection","where","totalDocs","resolveVersionsPartition"],"mappings":"AAIA,SAASA,UAAU,QAAQ,4BAA2B;AAEtD,OAAO,MAAMC,gBAA+B,eAAeA,cAEzD,EAAEC,UAAU,EAAEC,KAAK,EAAE;IAErB,MAAMC,YAAY,MAAMJ,WAAW,IAAI,EAAE,IAAI,CAACK,wBAAwB,CAACH,aAAaC;IACpF,OAAO;QAAEC;IAAU;AACrB,EAAC"}
|
package/dist/create.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create.d.ts","sourceRoot":"","sources":["../src/create.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AAUrC,eAAO,MAAM,MAAM,EAAE,MAuCpB,CAAA"}
|
package/dist/create.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { PutCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { normalizeForDynamo } from './utilities/normalizeForDynamo.js';
|
|
4
|
+
import { projectForCollection } from './utilities/resolveSchema.js';
|
|
5
|
+
export const create = async function create({ collection, customID, data, returning }) {
|
|
6
|
+
const docClient = this.docClient;
|
|
7
|
+
if (!docClient) {
|
|
8
|
+
throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
|
|
9
|
+
}
|
|
10
|
+
const id = customID ?? data['id'] ?? randomUUID();
|
|
11
|
+
const now = new Date().toISOString();
|
|
12
|
+
// Spread `data` first, then nullish-coalesce timestamps. Explicit values
|
|
13
|
+
// in `data` (versions/restore, migrations backdating) still win, but a
|
|
14
|
+
// payload-passed `data.createdAt = undefined` no longer wipes the default
|
|
15
|
+
// back to undefined — which the marshaller would then drop with
|
|
16
|
+
// `removeUndefinedValues: true`, leaving the row without timestamps.
|
|
17
|
+
const item = {
|
|
18
|
+
...data,
|
|
19
|
+
id,
|
|
20
|
+
createdAt: data['createdAt'] ?? now,
|
|
21
|
+
updatedAt: data['updatedAt'] ?? now
|
|
22
|
+
};
|
|
23
|
+
// Project against the collection's declared fields. DDB has no schema
|
|
24
|
+
// layer, so without this any stray request-body key (notoriously the
|
|
25
|
+
// registration form's `confirm-password`) would persist verbatim.
|
|
26
|
+
const projected = projectForCollection(this, collection, item);
|
|
27
|
+
await docClient.send(new PutCommand({
|
|
28
|
+
TableName: this.tableName,
|
|
29
|
+
Item: normalizeForDynamo({
|
|
30
|
+
...projected,
|
|
31
|
+
pk: this.resolvePartition(collection),
|
|
32
|
+
sk: String(id)
|
|
33
|
+
})
|
|
34
|
+
}));
|
|
35
|
+
return returning === false ? null : projected;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
//# sourceMappingURL=create.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/create.ts"],"sourcesContent":["import type { Create } from 'payload'\n\nimport { PutCommand } from '@aws-sdk/lib-dynamodb'\nimport { randomUUID } from 'node:crypto'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { normalizeForDynamo } from './utilities/normalizeForDynamo.js'\nimport { projectForCollection } from './utilities/resolveSchema.js'\n\nexport const create: Create = async function create(\n this: DynamoAdapter,\n { collection, customID, data, returning },\n) {\n const docClient = this.docClient\n if (!docClient) {\n throw new Error('payload-ddb: docClient is not initialized — call connect() first.')\n }\n\n const id = customID ?? data['id'] ?? randomUUID()\n const now = new Date().toISOString()\n // Spread `data` first, then nullish-coalesce timestamps. Explicit values\n // in `data` (versions/restore, migrations backdating) still win, but a\n // payload-passed `data.createdAt = undefined` no longer wipes the default\n // back to undefined — which the marshaller would then drop with\n // `removeUndefinedValues: true`, leaving the row without timestamps.\n const item: Record<string, unknown> = {\n ...data,\n id,\n createdAt: data['createdAt'] ?? now,\n updatedAt: data['updatedAt'] ?? now,\n }\n // Project against the collection's declared fields. DDB has no schema\n // layer, so without this any stray request-body key (notoriously the\n // registration form's `confirm-password`) would persist verbatim.\n const projected = projectForCollection(this, collection, item)\n\n await docClient.send(\n new PutCommand({\n TableName: this.tableName,\n Item: normalizeForDynamo({\n ...projected,\n pk: this.resolvePartition(collection),\n sk: String(id),\n }),\n }),\n )\n\n return returning === false ? (null as never) : projected\n}\n"],"names":["PutCommand","randomUUID","normalizeForDynamo","projectForCollection","create","collection","customID","data","returning","docClient","Error","id","now","Date","toISOString","item","createdAt","updatedAt","projected","send","TableName","tableName","Item","pk","resolvePartition","sk","String"],"mappings":"AAEA,SAASA,UAAU,QAAQ,wBAAuB;AAClD,SAASC,UAAU,QAAQ,cAAa;AAIxC,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,oBAAoB,QAAQ,+BAA8B;AAEnE,OAAO,MAAMC,SAAiB,eAAeA,OAE3C,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,IAAI,EAAEC,SAAS,EAAE;IAEzC,MAAMC,YAAY,IAAI,CAACA,SAAS;IAChC,IAAI,CAACA,WAAW;QACd,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,KAAKL,YAAYC,IAAI,CAAC,KAAK,IAAIN;IACrC,MAAMW,MAAM,IAAIC,OAAOC,WAAW;IAClC,yEAAyE;IACzE,uEAAuE;IACvE,0EAA0E;IAC1E,gEAAgE;IAChE,qEAAqE;IACrE,MAAMC,OAAgC;QACpC,GAAGR,IAAI;QACPI;QACAK,WAAWT,IAAI,CAAC,YAAY,IAAIK;QAChCK,WAAWV,IAAI,CAAC,YAAY,IAAIK;IAClC;IACA,sEAAsE;IACtE,qEAAqE;IACrE,kEAAkE;IAClE,MAAMM,YAAYf,qBAAqB,IAAI,EAAEE,YAAYU;IAEzD,MAAMN,UAAUU,IAAI,CAClB,IAAInB,WAAW;QACboB,WAAW,IAAI,CAACC,SAAS;QACzBC,MAAMpB,mBAAmB;YACvB,GAAGgB,SAAS;YACZK,IAAI,IAAI,CAACC,gBAAgB,CAACnB;YAC1BoB,IAAIC,OAAOf;QACb;IACF;IAGF,OAAOH,cAAc,QAAS,OAAiBU;AACjD,EAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CreateGlobal } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Insert a global's singleton row. The slug is forced as the row's `id` —
|
|
4
|
+
* this is what makes `findGlobal`/`updateGlobal` deterministic single-key
|
|
5
|
+
* reads (`pk = slug`, `sk = slug`).
|
|
6
|
+
*/
|
|
7
|
+
export declare const createGlobal: CreateGlobal;
|
|
8
|
+
//# sourceMappingURL=createGlobal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createGlobal.d.ts","sourceRoot":"","sources":["../src/createGlobal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAS3C;;;;GAIG;AACH,eAAO,MAAM,YAAY,EAAE,YAwB1B,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { PutCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import { normalizeForDynamo } from './utilities/normalizeForDynamo.js';
|
|
3
|
+
import { projectForGlobal } from './utilities/resolveSchema.js';
|
|
4
|
+
/**
|
|
5
|
+
* Insert a global's singleton row. The slug is forced as the row's `id` —
|
|
6
|
+
* this is what makes `findGlobal`/`updateGlobal` deterministic single-key
|
|
7
|
+
* reads (`pk = slug`, `sk = slug`).
|
|
8
|
+
*/ export const createGlobal = async function createGlobal({ slug, data, returning }) {
|
|
9
|
+
const docClient = this.docClient;
|
|
10
|
+
if (!docClient) {
|
|
11
|
+
throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
|
|
12
|
+
}
|
|
13
|
+
const item = {
|
|
14
|
+
...data,
|
|
15
|
+
id: slug
|
|
16
|
+
};
|
|
17
|
+
const projected = projectForGlobal(this, slug, item);
|
|
18
|
+
await docClient.send(new PutCommand({
|
|
19
|
+
TableName: this.tableName,
|
|
20
|
+
Item: normalizeForDynamo({
|
|
21
|
+
...projected,
|
|
22
|
+
pk: this.resolvePartition(slug),
|
|
23
|
+
sk: slug
|
|
24
|
+
})
|
|
25
|
+
}));
|
|
26
|
+
return returning === false ? null : projected;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=createGlobal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/createGlobal.ts"],"sourcesContent":["import type { CreateGlobal } from 'payload'\n\nimport { PutCommand } from '@aws-sdk/lib-dynamodb'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { normalizeForDynamo } from './utilities/normalizeForDynamo.js'\nimport { projectForGlobal } from './utilities/resolveSchema.js'\n\n/**\n * Insert a global's singleton row. The slug is forced as the row's `id` —\n * this is what makes `findGlobal`/`updateGlobal` deterministic single-key\n * reads (`pk = slug`, `sk = slug`).\n */\nexport const createGlobal: CreateGlobal = async function createGlobal(\n this: DynamoAdapter,\n { slug, data, returning },\n) {\n const docClient = this.docClient\n if (!docClient) {\n throw new Error('payload-ddb: docClient is not initialized — call connect() first.')\n }\n\n const item: Record<string, unknown> = { ...data, id: slug }\n const projected = projectForGlobal(this, slug, item)\n\n await docClient.send(\n new PutCommand({\n TableName: this.tableName,\n Item: normalizeForDynamo({\n ...projected,\n pk: this.resolvePartition(slug),\n sk: slug,\n }),\n }),\n )\n\n return returning === false ? (null as never) : (projected as never)\n}\n"],"names":["PutCommand","normalizeForDynamo","projectForGlobal","createGlobal","slug","data","returning","docClient","Error","item","id","projected","send","TableName","tableName","Item","pk","resolvePartition","sk"],"mappings":"AAEA,SAASA,UAAU,QAAQ,wBAAuB;AAIlD,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,gBAAgB,QAAQ,+BAA8B;AAE/D;;;;CAIC,GACD,OAAO,MAAMC,eAA6B,eAAeA,aAEvD,EAAEC,IAAI,EAAEC,IAAI,EAAEC,SAAS,EAAE;IAEzB,MAAMC,YAAY,IAAI,CAACA,SAAS;IAChC,IAAI,CAACA,WAAW;QACd,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,OAAgC;QAAE,GAAGJ,IAAI;QAAEK,IAAIN;IAAK;IAC1D,MAAMO,YAAYT,iBAAiB,IAAI,EAAEE,MAAMK;IAE/C,MAAMF,UAAUK,IAAI,CAClB,IAAIZ,WAAW;QACba,WAAW,IAAI,CAACC,SAAS;QACzBC,MAAMd,mBAAmB;YACvB,GAAGU,SAAS;YACZK,IAAI,IAAI,CAACC,gBAAgB,CAACb;YAC1Bc,IAAId;QACN;IACF;IAGF,OAAOE,cAAc,QAAS,OAAkBK;AAClD,EAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { CreateGlobalVersion } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Like `createVersion` but for global singletons: there's no `parent`, and
|
|
4
|
+
* the "latest" scope is the entire global's versions partition. Same atomic
|
|
5
|
+
* flip+put via `TransactWriteItems`. See `createVersion` for rationale.
|
|
6
|
+
*/
|
|
7
|
+
export declare const createGlobalVersion: CreateGlobalVersion;
|
|
8
|
+
//# sourceMappingURL=createGlobalVersion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createGlobalVersion.d.ts","sourceRoot":"","sources":["../src/createGlobalVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAWlD;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,mBA2EjC,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { findFirst } from './utilities/findFirst.js';
|
|
4
|
+
import { normalizeForDynamo } from './utilities/normalizeForDynamo.js';
|
|
5
|
+
import { projectVersionSnapshot } from './utilities/resolveSchema.js';
|
|
6
|
+
/**
|
|
7
|
+
* Like `createVersion` but for global singletons: there's no `parent`, and
|
|
8
|
+
* the "latest" scope is the entire global's versions partition. Same atomic
|
|
9
|
+
* flip+put via `TransactWriteItems`. See `createVersion` for rationale.
|
|
10
|
+
*/ export const createGlobalVersion = async function createGlobalVersion({ autosave, createdAt, globalSlug, publishedLocale, returning, snapshot, updatedAt, versionData }) {
|
|
11
|
+
const docClient = this.docClient;
|
|
12
|
+
if (!docClient) {
|
|
13
|
+
throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
|
|
14
|
+
}
|
|
15
|
+
const partition = this.resolveVersionsPartition(globalSlug);
|
|
16
|
+
const previousLatest = await findFirst(this, {
|
|
17
|
+
partition,
|
|
18
|
+
where: {
|
|
19
|
+
latest: {
|
|
20
|
+
equals: true
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
const id = randomUUID();
|
|
25
|
+
const sanitizedVersionData = projectVersionSnapshot(this, {
|
|
26
|
+
kind: 'global',
|
|
27
|
+
slug: globalSlug
|
|
28
|
+
}, versionData);
|
|
29
|
+
const item = {
|
|
30
|
+
id,
|
|
31
|
+
version: sanitizedVersionData,
|
|
32
|
+
createdAt,
|
|
33
|
+
updatedAt,
|
|
34
|
+
latest: true,
|
|
35
|
+
autosave,
|
|
36
|
+
...snapshot ? {
|
|
37
|
+
snapshot: true
|
|
38
|
+
} : {},
|
|
39
|
+
...publishedLocale !== undefined ? {
|
|
40
|
+
publishedLocale
|
|
41
|
+
} : {}
|
|
42
|
+
};
|
|
43
|
+
const putItem = normalizeForDynamo({
|
|
44
|
+
...item,
|
|
45
|
+
pk: partition,
|
|
46
|
+
sk: id
|
|
47
|
+
});
|
|
48
|
+
const transactItems = [];
|
|
49
|
+
if (previousLatest) {
|
|
50
|
+
transactItems.push({
|
|
51
|
+
Update: {
|
|
52
|
+
TableName: this.tableName,
|
|
53
|
+
Key: {
|
|
54
|
+
pk: partition,
|
|
55
|
+
sk: String(previousLatest['id'])
|
|
56
|
+
},
|
|
57
|
+
UpdateExpression: 'SET #latest = :false',
|
|
58
|
+
ExpressionAttributeNames: {
|
|
59
|
+
'#latest': 'latest'
|
|
60
|
+
},
|
|
61
|
+
ExpressionAttributeValues: {
|
|
62
|
+
':false': false
|
|
63
|
+
},
|
|
64
|
+
ConditionExpression: 'attribute_exists(pk)'
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
transactItems.push({
|
|
69
|
+
Put: {
|
|
70
|
+
TableName: this.tableName,
|
|
71
|
+
Item: putItem
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
await docClient.send(new TransactWriteCommand({
|
|
75
|
+
TransactItems: transactItems
|
|
76
|
+
}));
|
|
77
|
+
return returning === false ? null : item;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=createGlobalVersion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/createGlobalVersion.ts"],"sourcesContent":["import type { CreateGlobalVersion } from 'payload'\n\nimport { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'\nimport { randomUUID } from 'node:crypto'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { findFirst } from './utilities/findFirst.js'\nimport { normalizeForDynamo } from './utilities/normalizeForDynamo.js'\nimport { projectVersionSnapshot } from './utilities/resolveSchema.js'\n\n/**\n * Like `createVersion` but for global singletons: there's no `parent`, and\n * the \"latest\" scope is the entire global's versions partition. Same atomic\n * flip+put via `TransactWriteItems`. See `createVersion` for rationale.\n */\nexport const createGlobalVersion: CreateGlobalVersion = async function createGlobalVersion(\n this: DynamoAdapter,\n {\n autosave,\n createdAt,\n globalSlug,\n publishedLocale,\n returning,\n snapshot,\n updatedAt,\n versionData,\n },\n) {\n const docClient = this.docClient\n if (!docClient) {\n throw new Error('payload-ddb: docClient is not initialized — call connect() first.')\n }\n\n const partition = this.resolveVersionsPartition(globalSlug)\n\n const previousLatest = await findFirst(this, {\n partition,\n where: { latest: { equals: true } },\n })\n\n const id = randomUUID()\n const sanitizedVersionData = projectVersionSnapshot(\n this,\n { kind: 'global', slug: globalSlug },\n versionData as Record<string, unknown>,\n )\n const item: Record<string, unknown> = {\n id,\n version: sanitizedVersionData,\n createdAt,\n updatedAt,\n latest: true,\n autosave,\n ...(snapshot ? { snapshot: true } : {}),\n ...(publishedLocale !== undefined ? { publishedLocale } : {}),\n }\n\n const putItem = normalizeForDynamo({\n ...item,\n pk: partition,\n sk: id,\n })\n\n const transactItems: NonNullable<\n ConstructorParameters<typeof TransactWriteCommand>[0]['TransactItems']\n > = []\n\n if (previousLatest) {\n transactItems.push({\n Update: {\n TableName: this.tableName,\n Key: { pk: partition, sk: String(previousLatest['id']) },\n UpdateExpression: 'SET #latest = :false',\n ExpressionAttributeNames: { '#latest': 'latest' },\n ExpressionAttributeValues: { ':false': false },\n ConditionExpression: 'attribute_exists(pk)',\n },\n })\n }\n\n transactItems.push({\n Put: {\n TableName: this.tableName,\n Item: putItem,\n },\n })\n\n await docClient.send(new TransactWriteCommand({ TransactItems: transactItems }))\n\n return returning === false ? (null as never) : (item as never)\n}\n"],"names":["TransactWriteCommand","randomUUID","findFirst","normalizeForDynamo","projectVersionSnapshot","createGlobalVersion","autosave","createdAt","globalSlug","publishedLocale","returning","snapshot","updatedAt","versionData","docClient","Error","partition","resolveVersionsPartition","previousLatest","where","latest","equals","id","sanitizedVersionData","kind","slug","item","version","undefined","putItem","pk","sk","transactItems","push","Update","TableName","tableName","Key","String","UpdateExpression","ExpressionAttributeNames","ExpressionAttributeValues","ConditionExpression","Put","Item","send","TransactItems"],"mappings":"AAEA,SAASA,oBAAoB,QAAQ,wBAAuB;AAC5D,SAASC,UAAU,QAAQ,cAAa;AAIxC,SAASC,SAAS,QAAQ,2BAA0B;AACpD,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,sBAAsB,QAAQ,+BAA8B;AAErE;;;;CAIC,GACD,OAAO,MAAMC,sBAA2C,eAAeA,oBAErE,EACEC,QAAQ,EACRC,SAAS,EACTC,UAAU,EACVC,eAAe,EACfC,SAAS,EACTC,QAAQ,EACRC,SAAS,EACTC,WAAW,EACZ;IAED,MAAMC,YAAY,IAAI,CAACA,SAAS;IAChC,IAAI,CAACA,WAAW;QACd,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,YAAY,IAAI,CAACC,wBAAwB,CAACT;IAEhD,MAAMU,iBAAiB,MAAMhB,UAAU,IAAI,EAAE;QAC3Cc;QACAG,OAAO;YAAEC,QAAQ;gBAAEC,QAAQ;YAAK;QAAE;IACpC;IAEA,MAAMC,KAAKrB;IACX,MAAMsB,uBAAuBnB,uBAC3B,IAAI,EACJ;QAAEoB,MAAM;QAAUC,MAAMjB;IAAW,GACnCK;IAEF,MAAMa,OAAgC;QACpCJ;QACAK,SAASJ;QACThB;QACAK;QACAQ,QAAQ;QACRd;QACA,GAAIK,WAAW;YAAEA,UAAU;QAAK,IAAI,CAAC,CAAC;QACtC,GAAIF,oBAAoBmB,YAAY;YAAEnB;QAAgB,IAAI,CAAC,CAAC;IAC9D;IAEA,MAAMoB,UAAU1B,mBAAmB;QACjC,GAAGuB,IAAI;QACPI,IAAId;QACJe,IAAIT;IACN;IAEA,MAAMU,gBAEF,EAAE;IAEN,IAAId,gBAAgB;QAClBc,cAAcC,IAAI,CAAC;YACjBC,QAAQ;gBACNC,WAAW,IAAI,CAACC,SAAS;gBACzBC,KAAK;oBAAEP,IAAId;oBAAWe,IAAIO,OAAOpB,cAAc,CAAC,KAAK;gBAAE;gBACvDqB,kBAAkB;gBAClBC,0BAA0B;oBAAE,WAAW;gBAAS;gBAChDC,2BAA2B;oBAAE,UAAU;gBAAM;gBAC7CC,qBAAqB;YACvB;QACF;IACF;IAEAV,cAAcC,IAAI,CAAC;QACjBU,KAAK;YACHR,WAAW,IAAI,CAACC,SAAS;YACzBQ,MAAMf;QACR;IACF;IAEA,MAAMf,UAAU+B,IAAI,CAAC,IAAI7C,qBAAqB;QAAE8C,eAAed;IAAc;IAE7E,OAAOtB,cAAc,QAAS,OAAkBgB;AAClD,EAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { CreateVersion } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* Insert a new version for a collection's parent doc, maintaining the
|
|
4
|
+
* `latest=true` invariant per parent.
|
|
5
|
+
*
|
|
6
|
+
* Two round trips:
|
|
7
|
+
* 1. `Query` the versions partition for the previous `latest=true` row.
|
|
8
|
+
* 2. `TransactWriteItems` to flip that row's `latest` to false and put the
|
|
9
|
+
* new row in one atomic call. If a crash occurs mid-flow neither
|
|
10
|
+
* mutation lands, so we never end up with two `latest=true` rows or
|
|
11
|
+
* zero. The Update carries `attribute_exists(pk)` so the transaction
|
|
12
|
+
* fails cleanly if the previous row was deleted between (1) and (2).
|
|
13
|
+
*
|
|
14
|
+
* `autosave` is persisted on the row even though it isn't surfaced in
|
|
15
|
+
* `TypeWithVersion` — `findVersions` filters by it.
|
|
16
|
+
*/
|
|
17
|
+
export declare const createVersion: CreateVersion;
|
|
18
|
+
//# sourceMappingURL=createVersion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createVersion.d.ts","sourceRoot":"","sources":["../src/createVersion.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAW5C;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,aAAa,EAAE,aAmF3B,CAAA"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { TransactWriteCommand } from '@aws-sdk/lib-dynamodb';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { findFirst } from './utilities/findFirst.js';
|
|
4
|
+
import { normalizeForDynamo } from './utilities/normalizeForDynamo.js';
|
|
5
|
+
import { projectVersionSnapshot } from './utilities/resolveSchema.js';
|
|
6
|
+
/**
|
|
7
|
+
* Insert a new version for a collection's parent doc, maintaining the
|
|
8
|
+
* `latest=true` invariant per parent.
|
|
9
|
+
*
|
|
10
|
+
* Two round trips:
|
|
11
|
+
* 1. `Query` the versions partition for the previous `latest=true` row.
|
|
12
|
+
* 2. `TransactWriteItems` to flip that row's `latest` to false and put the
|
|
13
|
+
* new row in one atomic call. If a crash occurs mid-flow neither
|
|
14
|
+
* mutation lands, so we never end up with two `latest=true` rows or
|
|
15
|
+
* zero. The Update carries `attribute_exists(pk)` so the transaction
|
|
16
|
+
* fails cleanly if the previous row was deleted between (1) and (2).
|
|
17
|
+
*
|
|
18
|
+
* `autosave` is persisted on the row even though it isn't surfaced in
|
|
19
|
+
* `TypeWithVersion` — `findVersions` filters by it.
|
|
20
|
+
*/ export const createVersion = async function createVersion({ autosave, collectionSlug, createdAt, parent, publishedLocale, returning, snapshot, updatedAt, versionData }) {
|
|
21
|
+
const docClient = this.docClient;
|
|
22
|
+
if (!docClient) {
|
|
23
|
+
throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
|
|
24
|
+
}
|
|
25
|
+
const partition = this.resolveVersionsPartition(collectionSlug);
|
|
26
|
+
const previousLatest = await findFirst(this, {
|
|
27
|
+
partition,
|
|
28
|
+
where: {
|
|
29
|
+
and: [
|
|
30
|
+
{
|
|
31
|
+
parent: {
|
|
32
|
+
equals: parent
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
latest: {
|
|
37
|
+
equals: true
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
const id = randomUUID();
|
|
44
|
+
// Project the snapshot against the parent collection's schema so that any
|
|
45
|
+
// unknown keys (e.g. registration-form `confirm-password`) don't ride into
|
|
46
|
+
// the version row alongside the legitimate doc state.
|
|
47
|
+
const sanitizedVersionData = projectVersionSnapshot(this, {
|
|
48
|
+
kind: 'collection',
|
|
49
|
+
slug: collectionSlug
|
|
50
|
+
}, versionData);
|
|
51
|
+
const item = {
|
|
52
|
+
id,
|
|
53
|
+
parent,
|
|
54
|
+
version: sanitizedVersionData,
|
|
55
|
+
createdAt,
|
|
56
|
+
updatedAt,
|
|
57
|
+
latest: true,
|
|
58
|
+
autosave,
|
|
59
|
+
...snapshot ? {
|
|
60
|
+
snapshot: true
|
|
61
|
+
} : {},
|
|
62
|
+
...publishedLocale !== undefined ? {
|
|
63
|
+
publishedLocale
|
|
64
|
+
} : {}
|
|
65
|
+
};
|
|
66
|
+
const putItem = normalizeForDynamo({
|
|
67
|
+
...item,
|
|
68
|
+
pk: partition,
|
|
69
|
+
sk: id
|
|
70
|
+
});
|
|
71
|
+
const transactItems = [];
|
|
72
|
+
if (previousLatest) {
|
|
73
|
+
transactItems.push({
|
|
74
|
+
Update: {
|
|
75
|
+
TableName: this.tableName,
|
|
76
|
+
Key: {
|
|
77
|
+
pk: partition,
|
|
78
|
+
sk: String(previousLatest['id'])
|
|
79
|
+
},
|
|
80
|
+
UpdateExpression: 'SET #latest = :false',
|
|
81
|
+
ExpressionAttributeNames: {
|
|
82
|
+
'#latest': 'latest'
|
|
83
|
+
},
|
|
84
|
+
ExpressionAttributeValues: {
|
|
85
|
+
':false': false
|
|
86
|
+
},
|
|
87
|
+
// Fail the whole transaction if the previous row was deleted between
|
|
88
|
+
// our read and this transaction. Better to error and let the caller
|
|
89
|
+
// retry than to silently leave the new version with a phantom flip.
|
|
90
|
+
ConditionExpression: 'attribute_exists(pk)'
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
transactItems.push({
|
|
95
|
+
Put: {
|
|
96
|
+
TableName: this.tableName,
|
|
97
|
+
Item: putItem
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
await docClient.send(new TransactWriteCommand({
|
|
101
|
+
TransactItems: transactItems
|
|
102
|
+
}));
|
|
103
|
+
return returning === false ? null : item;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
//# sourceMappingURL=createVersion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/createVersion.ts"],"sourcesContent":["import type { CreateVersion } from 'payload'\n\nimport { TransactWriteCommand } from '@aws-sdk/lib-dynamodb'\nimport { randomUUID } from 'node:crypto'\n\nimport type { DynamoAdapter } from './types.js'\n\nimport { findFirst } from './utilities/findFirst.js'\nimport { normalizeForDynamo } from './utilities/normalizeForDynamo.js'\nimport { projectVersionSnapshot } from './utilities/resolveSchema.js'\n\n/**\n * Insert a new version for a collection's parent doc, maintaining the\n * `latest=true` invariant per parent.\n *\n * Two round trips:\n * 1. `Query` the versions partition for the previous `latest=true` row.\n * 2. `TransactWriteItems` to flip that row's `latest` to false and put the\n * new row in one atomic call. If a crash occurs mid-flow neither\n * mutation lands, so we never end up with two `latest=true` rows or\n * zero. The Update carries `attribute_exists(pk)` so the transaction\n * fails cleanly if the previous row was deleted between (1) and (2).\n *\n * `autosave` is persisted on the row even though it isn't surfaced in\n * `TypeWithVersion` — `findVersions` filters by it.\n */\nexport const createVersion: CreateVersion = async function createVersion(\n this: DynamoAdapter,\n {\n autosave,\n collectionSlug,\n createdAt,\n parent,\n publishedLocale,\n returning,\n snapshot,\n updatedAt,\n versionData,\n },\n) {\n const docClient = this.docClient\n if (!docClient) {\n throw new Error('payload-ddb: docClient is not initialized — call connect() first.')\n }\n\n const partition = this.resolveVersionsPartition(collectionSlug)\n\n const previousLatest = await findFirst(this, {\n partition,\n where: { and: [{ parent: { equals: parent } }, { latest: { equals: true } }] },\n })\n\n const id = randomUUID()\n // Project the snapshot against the parent collection's schema so that any\n // unknown keys (e.g. registration-form `confirm-password`) don't ride into\n // the version row alongside the legitimate doc state.\n const sanitizedVersionData = projectVersionSnapshot(\n this,\n { kind: 'collection', slug: collectionSlug },\n versionData as Record<string, unknown>,\n )\n const item: Record<string, unknown> = {\n id,\n parent,\n version: sanitizedVersionData,\n createdAt,\n updatedAt,\n latest: true,\n autosave,\n ...(snapshot ? { snapshot: true } : {}),\n ...(publishedLocale !== undefined ? { publishedLocale } : {}),\n }\n\n const putItem = normalizeForDynamo({\n ...item,\n pk: partition,\n sk: id,\n })\n\n const transactItems: NonNullable<\n ConstructorParameters<typeof TransactWriteCommand>[0]['TransactItems']\n > = []\n\n if (previousLatest) {\n transactItems.push({\n Update: {\n TableName: this.tableName,\n Key: { pk: partition, sk: String(previousLatest['id']) },\n UpdateExpression: 'SET #latest = :false',\n ExpressionAttributeNames: { '#latest': 'latest' },\n ExpressionAttributeValues: { ':false': false },\n // Fail the whole transaction if the previous row was deleted between\n // our read and this transaction. Better to error and let the caller\n // retry than to silently leave the new version with a phantom flip.\n ConditionExpression: 'attribute_exists(pk)',\n },\n })\n }\n\n transactItems.push({\n Put: {\n TableName: this.tableName,\n Item: putItem,\n },\n })\n\n await docClient.send(new TransactWriteCommand({ TransactItems: transactItems }))\n\n return returning === false ? (null as never) : (item as never)\n}\n"],"names":["TransactWriteCommand","randomUUID","findFirst","normalizeForDynamo","projectVersionSnapshot","createVersion","autosave","collectionSlug","createdAt","parent","publishedLocale","returning","snapshot","updatedAt","versionData","docClient","Error","partition","resolveVersionsPartition","previousLatest","where","and","equals","latest","id","sanitizedVersionData","kind","slug","item","version","undefined","putItem","pk","sk","transactItems","push","Update","TableName","tableName","Key","String","UpdateExpression","ExpressionAttributeNames","ExpressionAttributeValues","ConditionExpression","Put","Item","send","TransactItems"],"mappings":"AAEA,SAASA,oBAAoB,QAAQ,wBAAuB;AAC5D,SAASC,UAAU,QAAQ,cAAa;AAIxC,SAASC,SAAS,QAAQ,2BAA0B;AACpD,SAASC,kBAAkB,QAAQ,oCAAmC;AACtE,SAASC,sBAAsB,QAAQ,+BAA8B;AAErE;;;;;;;;;;;;;;CAcC,GACD,OAAO,MAAMC,gBAA+B,eAAeA,cAEzD,EACEC,QAAQ,EACRC,cAAc,EACdC,SAAS,EACTC,MAAM,EACNC,eAAe,EACfC,SAAS,EACTC,QAAQ,EACRC,SAAS,EACTC,WAAW,EACZ;IAED,MAAMC,YAAY,IAAI,CAACA,SAAS;IAChC,IAAI,CAACA,WAAW;QACd,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,YAAY,IAAI,CAACC,wBAAwB,CAACX;IAEhD,MAAMY,iBAAiB,MAAMjB,UAAU,IAAI,EAAE;QAC3Ce;QACAG,OAAO;YAAEC,KAAK;gBAAC;oBAAEZ,QAAQ;wBAAEa,QAAQb;oBAAO;gBAAE;gBAAG;oBAAEc,QAAQ;wBAAED,QAAQ;oBAAK;gBAAE;aAAE;QAAC;IAC/E;IAEA,MAAME,KAAKvB;IACX,0EAA0E;IAC1E,2EAA2E;IAC3E,sDAAsD;IACtD,MAAMwB,uBAAuBrB,uBAC3B,IAAI,EACJ;QAAEsB,MAAM;QAAcC,MAAMpB;IAAe,GAC3CO;IAEF,MAAMc,OAAgC;QACpCJ;QACAf;QACAoB,SAASJ;QACTjB;QACAK;QACAU,QAAQ;QACRjB;QACA,GAAIM,WAAW;YAAEA,UAAU;QAAK,IAAI,CAAC,CAAC;QACtC,GAAIF,oBAAoBoB,YAAY;YAAEpB;QAAgB,IAAI,CAAC,CAAC;IAC9D;IAEA,MAAMqB,UAAU5B,mBAAmB;QACjC,GAAGyB,IAAI;QACPI,IAAIf;QACJgB,IAAIT;IACN;IAEA,MAAMU,gBAEF,EAAE;IAEN,IAAIf,gBAAgB;QAClBe,cAAcC,IAAI,CAAC;YACjBC,QAAQ;gBACNC,WAAW,IAAI,CAACC,SAAS;gBACzBC,KAAK;oBAAEP,IAAIf;oBAAWgB,IAAIO,OAAOrB,cAAc,CAAC,KAAK;gBAAE;gBACvDsB,kBAAkB;gBAClBC,0BAA0B;oBAAE,WAAW;gBAAS;gBAChDC,2BAA2B;oBAAE,UAAU;gBAAM;gBAC7C,qEAAqE;gBACrE,oEAAoE;gBACpE,oEAAoE;gBACpEC,qBAAqB;YACvB;QACF;IACF;IAEAV,cAAcC,IAAI,CAAC;QACjBU,KAAK;YACHR,WAAW,IAAI,CAACC,SAAS;YACzBQ,MAAMf;QACR;IACF;IAEA,MAAMhB,UAAUgC,IAAI,CAAC,IAAI/C,qBAAqB;QAAEgD,eAAed;IAAc;IAE7E,OAAOvB,cAAc,QAAS,OAAkBiB;AAClD,EAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DeleteMany } from 'payload';
|
|
2
|
+
/**
|
|
3
|
+
* v1 strategy: query-and-collect matches, then delete each by composite key
|
|
4
|
+
* in parallel.
|
|
5
|
+
*
|
|
6
|
+
* `BatchWriteItem` could halve the request count (25 deletes per call) but
|
|
7
|
+
* has partial-failure semantics that need retry logic — out of scope until
|
|
8
|
+
* we wire up retries. Sequential parallel `DeleteCommand`s are simple and
|
|
9
|
+
* correct.
|
|
10
|
+
*/
|
|
11
|
+
export declare const deleteMany: DeleteMany;
|
|
12
|
+
//# sourceMappingURL=deleteMany.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deleteMany.d.ts","sourceRoot":"","sources":["../src/deleteMany.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAQzC;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,EAAE,UAsBxB,CAAA"}
|