@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.
Files changed (183) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +146 -0
  3. package/dist/connect.d.ts +3 -0
  4. package/dist/connect.d.ts.map +1 -0
  5. package/dist/connect.js +6 -0
  6. package/dist/connect.js.map +1 -0
  7. package/dist/count.d.ts +3 -0
  8. package/dist/count.d.ts.map +1 -0
  9. package/dist/count.js +9 -0
  10. package/dist/count.js.map +1 -0
  11. package/dist/countGlobalVersions.d.ts +3 -0
  12. package/dist/countGlobalVersions.d.ts.map +1 -0
  13. package/dist/countGlobalVersions.js +9 -0
  14. package/dist/countGlobalVersions.js.map +1 -0
  15. package/dist/countVersions.d.ts +3 -0
  16. package/dist/countVersions.d.ts.map +1 -0
  17. package/dist/countVersions.js +9 -0
  18. package/dist/countVersions.js.map +1 -0
  19. package/dist/create.d.ts +3 -0
  20. package/dist/create.d.ts.map +1 -0
  21. package/dist/create.js +38 -0
  22. package/dist/create.js.map +1 -0
  23. package/dist/createGlobal.d.ts +8 -0
  24. package/dist/createGlobal.d.ts.map +1 -0
  25. package/dist/createGlobal.js +29 -0
  26. package/dist/createGlobal.js.map +1 -0
  27. package/dist/createGlobalVersion.d.ts +8 -0
  28. package/dist/createGlobalVersion.d.ts.map +1 -0
  29. package/dist/createGlobalVersion.js +80 -0
  30. package/dist/createGlobalVersion.js.map +1 -0
  31. package/dist/createVersion.d.ts +18 -0
  32. package/dist/createVersion.d.ts.map +1 -0
  33. package/dist/createVersion.js +106 -0
  34. package/dist/createVersion.js.map +1 -0
  35. package/dist/deleteMany.d.ts +12 -0
  36. package/dist/deleteMany.d.ts.map +1 -0
  37. package/dist/deleteMany.js +27 -0
  38. package/dist/deleteMany.js.map +1 -0
  39. package/dist/deleteOne.d.ts +3 -0
  40. package/dist/deleteOne.d.ts.map +1 -0
  41. package/dist/deleteOne.js +48 -0
  42. package/dist/deleteOne.js.map +1 -0
  43. package/dist/deleteVersions.d.ts +9 -0
  44. package/dist/deleteVersions.d.ts.map +1 -0
  45. package/dist/deleteVersions.js +28 -0
  46. package/dist/deleteVersions.js.map +1 -0
  47. package/dist/destroy.d.ts +3 -0
  48. package/dist/destroy.d.ts.map +1 -0
  49. package/dist/destroy.js +10 -0
  50. package/dist/destroy.js.map +1 -0
  51. package/dist/find.d.ts +20 -0
  52. package/dist/find.d.ts.map +1 -0
  53. package/dist/find.js +54 -0
  54. package/dist/find.js.map +1 -0
  55. package/dist/findDistinct.d.ts +13 -0
  56. package/dist/findDistinct.d.ts.map +1 -0
  57. package/dist/findDistinct.js +52 -0
  58. package/dist/findDistinct.js.map +1 -0
  59. package/dist/findGlobal.d.ts +11 -0
  60. package/dist/findGlobal.d.ts.map +1 -0
  61. package/dist/findGlobal.js +34 -0
  62. package/dist/findGlobal.js.map +1 -0
  63. package/dist/findGlobalVersions.d.ts +3 -0
  64. package/dist/findGlobalVersions.d.ts.map +1 -0
  65. package/dist/findGlobalVersions.js +32 -0
  66. package/dist/findGlobalVersions.js.map +1 -0
  67. package/dist/findOne.d.ts +9 -0
  68. package/dist/findOne.d.ts.map +1 -0
  69. package/dist/findOne.js +22 -0
  70. package/dist/findOne.js.map +1 -0
  71. package/dist/findVersions.d.ts +10 -0
  72. package/dist/findVersions.d.ts.map +1 -0
  73. package/dist/findVersions.js +42 -0
  74. package/dist/findVersions.js.map +1 -0
  75. package/dist/index.d.ts +7 -0
  76. package/dist/index.d.ts.map +1 -0
  77. package/dist/index.js +98 -0
  78. package/dist/index.js.map +1 -0
  79. package/dist/init.d.ts +16 -0
  80. package/dist/init.d.ts.map +1 -0
  81. package/dist/init.js +24 -0
  82. package/dist/init.js.map +1 -0
  83. package/dist/queryDrafts.d.ts +3 -0
  84. package/dist/queryDrafts.d.ts.map +1 -0
  85. package/dist/queryDrafts.js +103 -0
  86. package/dist/queryDrafts.js.map +1 -0
  87. package/dist/types.d.ts +119 -0
  88. package/dist/types.d.ts.map +1 -0
  89. package/dist/types.js +3 -0
  90. package/dist/types.js.map +1 -0
  91. package/dist/updateGlobal.d.ts +9 -0
  92. package/dist/updateGlobal.d.ts.map +1 -0
  93. package/dist/updateGlobal.js +45 -0
  94. package/dist/updateGlobal.js.map +1 -0
  95. package/dist/updateGlobalVersion.d.ts +8 -0
  96. package/dist/updateGlobalVersion.d.ts.map +1 -0
  97. package/dist/updateGlobalVersion.js +59 -0
  98. package/dist/updateGlobalVersion.js.map +1 -0
  99. package/dist/updateMany.d.ts +13 -0
  100. package/dist/updateMany.d.ts.map +1 -0
  101. package/dist/updateMany.js +48 -0
  102. package/dist/updateMany.js.map +1 -0
  103. package/dist/updateOne.d.ts +13 -0
  104. package/dist/updateOne.d.ts.map +1 -0
  105. package/dist/updateOne.js +68 -0
  106. package/dist/updateOne.js.map +1 -0
  107. package/dist/updateVersion.d.ts +15 -0
  108. package/dist/updateVersion.d.ts.map +1 -0
  109. package/dist/updateVersion.js +69 -0
  110. package/dist/updateVersion.js.map +1 -0
  111. package/dist/upsert.d.ts +10 -0
  112. package/dist/upsert.d.ts.map +1 -0
  113. package/dist/upsert.js +55 -0
  114. package/dist/upsert.js.map +1 -0
  115. package/dist/utilities/applySorts.d.ts +11 -0
  116. package/dist/utilities/applySorts.d.ts.map +1 -0
  117. package/dist/utilities/applySorts.js +47 -0
  118. package/dist/utilities/applySorts.js.map +1 -0
  119. package/dist/utilities/buildFilterExpression.d.ts +42 -0
  120. package/dist/utilities/buildFilterExpression.d.ts.map +1 -0
  121. package/dist/utilities/buildFilterExpression.js +137 -0
  122. package/dist/utilities/buildFilterExpression.js.map +1 -0
  123. package/dist/utilities/draftsFallback.d.ts +28 -0
  124. package/dist/utilities/draftsFallback.d.ts.map +1 -0
  125. package/dist/utilities/draftsFallback.js +52 -0
  126. package/dist/utilities/draftsFallback.js.map +1 -0
  127. package/dist/utilities/ensureConnected.d.ts +12 -0
  128. package/dist/utilities/ensureConnected.d.ts.map +1 -0
  129. package/dist/utilities/ensureConnected.js +20 -0
  130. package/dist/utilities/ensureConnected.js.map +1 -0
  131. package/dist/utilities/ensureTable.d.ts +16 -0
  132. package/dist/utilities/ensureTable.d.ts.map +1 -0
  133. package/dist/utilities/ensureTable.js +62 -0
  134. package/dist/utilities/ensureTable.js.map +1 -0
  135. package/dist/utilities/findFirst.d.ts +16 -0
  136. package/dist/utilities/findFirst.d.ts.map +1 -0
  137. package/dist/utilities/findFirst.js +79 -0
  138. package/dist/utilities/findFirst.js.map +1 -0
  139. package/dist/utilities/getByPath.d.ts +9 -0
  140. package/dist/utilities/getByPath.d.ts.map +1 -0
  141. package/dist/utilities/getByPath.js +22 -0
  142. package/dist/utilities/getByPath.js.map +1 -0
  143. package/dist/utilities/matchesWhere.d.ts +12 -0
  144. package/dist/utilities/matchesWhere.d.ts.map +1 -0
  145. package/dist/utilities/matchesWhere.js +98 -0
  146. package/dist/utilities/matchesWhere.js.map +1 -0
  147. package/dist/utilities/normalizeForDynamo.d.ts +15 -0
  148. package/dist/utilities/normalizeForDynamo.d.ts.map +1 -0
  149. package/dist/utilities/normalizeForDynamo.js +27 -0
  150. package/dist/utilities/normalizeForDynamo.js.map +1 -0
  151. package/dist/utilities/operators.d.ts +26 -0
  152. package/dist/utilities/operators.d.ts.map +1 -0
  153. package/dist/utilities/operators.js +58 -0
  154. package/dist/utilities/operators.js.map +1 -0
  155. package/dist/utilities/pickConfiguredFields.d.ts +10 -0
  156. package/dist/utilities/pickConfiguredFields.d.ts.map +1 -0
  157. package/dist/utilities/pickConfiguredFields.js +113 -0
  158. package/dist/utilities/pickConfiguredFields.js.map +1 -0
  159. package/dist/utilities/queryCount.d.ts +9 -0
  160. package/dist/utilities/queryCount.d.ts.map +1 -0
  161. package/dist/utilities/queryCount.js +56 -0
  162. package/dist/utilities/queryCount.js.map +1 -0
  163. package/dist/utilities/queryMatching.d.ts +18 -0
  164. package/dist/utilities/queryMatching.d.ts.map +1 -0
  165. package/dist/utilities/queryMatching.js +71 -0
  166. package/dist/utilities/queryMatching.js.map +1 -0
  167. package/dist/utilities/resolveSchema.d.ts +56 -0
  168. package/dist/utilities/resolveSchema.d.ts.map +1 -0
  169. package/dist/utilities/resolveSchema.js +84 -0
  170. package/dist/utilities/resolveSchema.js.map +1 -0
  171. package/dist/utilities/scrubUnknownFields.d.ts +44 -0
  172. package/dist/utilities/scrubUnknownFields.d.ts.map +1 -0
  173. package/dist/utilities/scrubUnknownFields.js +88 -0
  174. package/dist/utilities/scrubUnknownFields.js.map +1 -0
  175. package/dist/utilities/stripInternalKeys.d.ts +7 -0
  176. package/dist/utilities/stripInternalKeys.d.ts.map +1 -0
  177. package/dist/utilities/stripInternalKeys.js +10 -0
  178. package/dist/utilities/stripInternalKeys.js.map +1 -0
  179. package/dist/utilities/whereToId.d.ts +11 -0
  180. package/dist/utilities/whereToId.d.ts.map +1 -0
  181. package/dist/utilities/whereToId.js +38 -0
  182. package/dist/utilities/whereToId.js.map +1 -0
  183. 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,3 @@
1
+ import type { Connect } from 'payload';
2
+ export declare const connect: Connect;
3
+ //# sourceMappingURL=connect.d.ts.map
@@ -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"}
@@ -0,0 +1,6 @@
1
+ import { ensureConnected } from './utilities/ensureConnected.js';
2
+ export const connect = async function() {
3
+ ensureConnected(this);
4
+ };
5
+
6
+ //# sourceMappingURL=connect.js.map
@@ -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"}
@@ -0,0 +1,3 @@
1
+ import type { Count } from 'payload';
2
+ export declare const count: Count;
3
+ //# sourceMappingURL=count.d.ts.map
@@ -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,3 @@
1
+ import type { CountGlobalVersions } from 'payload';
2
+ export declare const countGlobalVersions: CountGlobalVersions;
3
+ //# sourceMappingURL=countGlobalVersions.d.ts.map
@@ -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,3 @@
1
+ import type { CountVersions } from 'payload';
2
+ export declare const countVersions: CountVersions;
3
+ //# sourceMappingURL=countVersions.d.ts.map
@@ -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"}
@@ -0,0 +1,3 @@
1
+ import type { Create } from 'payload';
2
+ export declare const create: Create;
3
+ //# sourceMappingURL=create.d.ts.map
@@ -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"}