@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
@@ -0,0 +1,28 @@
1
+ import type { Where } from 'payload';
2
+ import type { DynamoAdapter } from '../types.js';
3
+ /**
4
+ * Returns true when the named collection has `versions.drafts` enabled in
5
+ * the Payload config. Read methods that union main-partition rows with
6
+ * latest-version rows gate on this so non-drafts collections stay on the
7
+ * fast single-partition path.
8
+ */
9
+ export declare function collectionHasDrafts(adapter: DynamoAdapter, collectionSlug: string): boolean;
10
+ /**
11
+ * Pull `latest=true` rows from the versions partition for any parent that
12
+ * isn't already represented in `mainRows`, project each row's `version`
13
+ * payload up to top-level (so it looks like a doc), filter by `where` in
14
+ * JS, and return the supplemental docs ready to merge into `mainRows`.
15
+ *
16
+ * The intentional asymmetry: when a parent appears in both partitions, the
17
+ * main-partition row wins. `find`/`findOne` reflect persistent state, and
18
+ * for a doc that has been published the main row IS that state. The
19
+ * version-only path is what catches drafts-only docs (created but never
20
+ * published, so no main-partition row exists).
21
+ *
22
+ * `where` was already pushed to DynamoDB for the main-partition Query but
23
+ * can't be reused here — version-row attributes live nested under
24
+ * `version.*` until projection. We re-evaluate in JS against the projected
25
+ * docs to keep semantics consistent.
26
+ */
27
+ export declare function fetchDraftsOnlySupplements(adapter: DynamoAdapter, collectionSlug: string, mainRows: Record<string, unknown>[], where: undefined | Where): Promise<Record<string, unknown>[]>;
28
+ //# sourceMappingURL=draftsFallback.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"draftsFallback.d.ts","sourceRoot":"","sources":["../../src/utilities/draftsFallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAKhD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAI3F;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EACnC,KAAK,EAAE,SAAS,GAAG,KAAK,GACvB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAoBpC"}
@@ -0,0 +1,52 @@
1
+ import { matchesWhere } from './matchesWhere.js';
2
+ import { queryMatching } from './queryMatching.js';
3
+ /**
4
+ * Returns true when the named collection has `versions.drafts` enabled in
5
+ * the Payload config. Read methods that union main-partition rows with
6
+ * latest-version rows gate on this so non-drafts collections stay on the
7
+ * fast single-partition path.
8
+ */ export function collectionHasDrafts(adapter, collectionSlug) {
9
+ const cfg = adapter.payload.config.collections.find((c)=>c.slug === collectionSlug);
10
+ if (!cfg?.versions) return false;
11
+ return cfg.versions.drafts !== false;
12
+ }
13
+ /**
14
+ * Pull `latest=true` rows from the versions partition for any parent that
15
+ * isn't already represented in `mainRows`, project each row's `version`
16
+ * payload up to top-level (so it looks like a doc), filter by `where` in
17
+ * JS, and return the supplemental docs ready to merge into `mainRows`.
18
+ *
19
+ * The intentional asymmetry: when a parent appears in both partitions, the
20
+ * main-partition row wins. `find`/`findOne` reflect persistent state, and
21
+ * for a doc that has been published the main row IS that state. The
22
+ * version-only path is what catches drafts-only docs (created but never
23
+ * published, so no main-partition row exists).
24
+ *
25
+ * `where` was already pushed to DynamoDB for the main-partition Query but
26
+ * can't be reused here — version-row attributes live nested under
27
+ * `version.*` until projection. We re-evaluate in JS against the projected
28
+ * docs to keep semantics consistent.
29
+ */ export async function fetchDraftsOnlySupplements(adapter, collectionSlug, mainRows, where) {
30
+ const versionsPartition = adapter.resolveVersionsPartition(collectionSlug);
31
+ const latestRows = await queryMatching(adapter, versionsPartition, {
32
+ latest: {
33
+ equals: true
34
+ }
35
+ });
36
+ const seen = new Set(mainRows.map((row)=>row['id']));
37
+ const supplements = [];
38
+ for (const row of latestRows){
39
+ if (seen.has(row['parent'])) continue;
40
+ const version = row['version'];
41
+ if (!version || typeof version !== 'object') continue;
42
+ const projected = {
43
+ ...version,
44
+ id: row['parent']
45
+ };
46
+ if (where && !matchesWhere(projected, where)) continue;
47
+ supplements.push(projected);
48
+ }
49
+ return supplements;
50
+ }
51
+
52
+ //# sourceMappingURL=draftsFallback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/draftsFallback.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nimport type { DynamoAdapter } from '../types.js'\n\nimport { matchesWhere } from './matchesWhere.js'\nimport { queryMatching } from './queryMatching.js'\n\n/**\n * Returns true when the named collection has `versions.drafts` enabled in\n * the Payload config. Read methods that union main-partition rows with\n * latest-version rows gate on this so non-drafts collections stay on the\n * fast single-partition path.\n */\nexport function collectionHasDrafts(adapter: DynamoAdapter, collectionSlug: string): boolean {\n const cfg = adapter.payload.config.collections.find((c) => c.slug === collectionSlug)\n if (!cfg?.versions) return false\n return cfg.versions.drafts !== false\n}\n\n/**\n * Pull `latest=true` rows from the versions partition for any parent that\n * isn't already represented in `mainRows`, project each row's `version`\n * payload up to top-level (so it looks like a doc), filter by `where` in\n * JS, and return the supplemental docs ready to merge into `mainRows`.\n *\n * The intentional asymmetry: when a parent appears in both partitions, the\n * main-partition row wins. `find`/`findOne` reflect persistent state, and\n * for a doc that has been published the main row IS that state. The\n * version-only path is what catches drafts-only docs (created but never\n * published, so no main-partition row exists).\n *\n * `where` was already pushed to DynamoDB for the main-partition Query but\n * can't be reused here — version-row attributes live nested under\n * `version.*` until projection. We re-evaluate in JS against the projected\n * docs to keep semantics consistent.\n */\nexport async function fetchDraftsOnlySupplements(\n adapter: DynamoAdapter,\n collectionSlug: string,\n mainRows: Record<string, unknown>[],\n where: undefined | Where,\n): Promise<Record<string, unknown>[]> {\n const versionsPartition = adapter.resolveVersionsPartition(collectionSlug)\n const latestRows = await queryMatching(adapter, versionsPartition, {\n latest: { equals: true },\n })\n\n const seen = new Set<unknown>(mainRows.map((row) => row['id']))\n const supplements: Record<string, unknown>[] = []\n for (const row of latestRows) {\n if (seen.has(row['parent'])) continue\n const version = row['version']\n if (!version || typeof version !== 'object') continue\n const projected: Record<string, unknown> = {\n ...(version as Record<string, unknown>),\n id: row['parent'],\n }\n if (where && !matchesWhere(projected, where)) continue\n supplements.push(projected)\n }\n return supplements\n}\n"],"names":["matchesWhere","queryMatching","collectionHasDrafts","adapter","collectionSlug","cfg","payload","config","collections","find","c","slug","versions","drafts","fetchDraftsOnlySupplements","mainRows","where","versionsPartition","resolveVersionsPartition","latestRows","latest","equals","seen","Set","map","row","supplements","has","version","projected","id","push"],"mappings":"AAIA,SAASA,YAAY,QAAQ,oBAAmB;AAChD,SAASC,aAAa,QAAQ,qBAAoB;AAElD;;;;;CAKC,GACD,OAAO,SAASC,oBAAoBC,OAAsB,EAAEC,cAAsB;IAChF,MAAMC,MAAMF,QAAQG,OAAO,CAACC,MAAM,CAACC,WAAW,CAACC,IAAI,CAAC,CAACC,IAAMA,EAAEC,IAAI,KAAKP;IACtE,IAAI,CAACC,KAAKO,UAAU,OAAO;IAC3B,OAAOP,IAAIO,QAAQ,CAACC,MAAM,KAAK;AACjC;AAEA;;;;;;;;;;;;;;;;CAgBC,GACD,OAAO,eAAeC,2BACpBX,OAAsB,EACtBC,cAAsB,EACtBW,QAAmC,EACnCC,KAAwB;IAExB,MAAMC,oBAAoBd,QAAQe,wBAAwB,CAACd;IAC3D,MAAMe,aAAa,MAAMlB,cAAcE,SAASc,mBAAmB;QACjEG,QAAQ;YAAEC,QAAQ;QAAK;IACzB;IAEA,MAAMC,OAAO,IAAIC,IAAaR,SAASS,GAAG,CAAC,CAACC,MAAQA,GAAG,CAAC,KAAK;IAC7D,MAAMC,cAAyC,EAAE;IACjD,KAAK,MAAMD,OAAON,WAAY;QAC5B,IAAIG,KAAKK,GAAG,CAACF,GAAG,CAAC,SAAS,GAAG;QAC7B,MAAMG,UAAUH,GAAG,CAAC,UAAU;QAC9B,IAAI,CAACG,WAAW,OAAOA,YAAY,UAAU;QAC7C,MAAMC,YAAqC;YACzC,GAAID,OAAO;YACXE,IAAIL,GAAG,CAAC,SAAS;QACnB;QACA,IAAIT,SAAS,CAAChB,aAAa6B,WAAWb,QAAQ;QAC9CU,YAAYK,IAAI,CAACF;IACnB;IACA,OAAOH;AACT"}
@@ -0,0 +1,12 @@
1
+ import type { DynamoAdapter } from '../types.js';
2
+ /**
3
+ * Idempotently set up `client` and `docClient` on the adapter. Called by both
4
+ * `init` and `connect` because Payload's lifecycle invokes them in
5
+ * `init → connect` order, and `init` (where table provisioning lives) needs a
6
+ * working client before `connect` runs.
7
+ *
8
+ * If the user supplied their own `client`, we honor it and don't take
9
+ * ownership for `destroy`.
10
+ */
11
+ export declare function ensureConnected(adapter: DynamoAdapter): void;
12
+ //# sourceMappingURL=ensureConnected.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensureConnected.d.ts","sourceRoot":"","sources":["../../src/utilities/ensureConnected.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CAS5D"}
@@ -0,0 +1,20 @@
1
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
3
+ /**
4
+ * Idempotently set up `client` and `docClient` on the adapter. Called by both
5
+ * `init` and `connect` because Payload's lifecycle invokes them in
6
+ * `init → connect` order, and `init` (where table provisioning lives) needs a
7
+ * working client before `connect` runs.
8
+ *
9
+ * If the user supplied their own `client`, we honor it and don't take
10
+ * ownership for `destroy`.
11
+ */ export function ensureConnected(adapter) {
12
+ if (adapter.docClient) return;
13
+ if (!adapter.client) {
14
+ adapter.client = new DynamoDBClient(adapter.clientConfig);
15
+ adapter.ownsClient = true;
16
+ }
17
+ adapter.docClient = DynamoDBDocumentClient.from(adapter.client, adapter.translateConfig);
18
+ }
19
+
20
+ //# sourceMappingURL=ensureConnected.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/ensureConnected.ts"],"sourcesContent":["import { DynamoDBClient } from '@aws-sdk/client-dynamodb'\nimport { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'\n\nimport type { DynamoAdapter } from '../types.js'\n\n/**\n * Idempotently set up `client` and `docClient` on the adapter. Called by both\n * `init` and `connect` because Payload's lifecycle invokes them in\n * `init → connect` order, and `init` (where table provisioning lives) needs a\n * working client before `connect` runs.\n *\n * If the user supplied their own `client`, we honor it and don't take\n * ownership for `destroy`.\n */\nexport function ensureConnected(adapter: DynamoAdapter): void {\n if (adapter.docClient) return\n\n if (!adapter.client) {\n adapter.client = new DynamoDBClient(adapter.clientConfig)\n adapter.ownsClient = true\n }\n\n adapter.docClient = DynamoDBDocumentClient.from(adapter.client, adapter.translateConfig)\n}\n"],"names":["DynamoDBClient","DynamoDBDocumentClient","ensureConnected","adapter","docClient","client","clientConfig","ownsClient","from","translateConfig"],"mappings":"AAAA,SAASA,cAAc,QAAQ,2BAA0B;AACzD,SAASC,sBAAsB,QAAQ,wBAAuB;AAI9D;;;;;;;;CAQC,GACD,OAAO,SAASC,gBAAgBC,OAAsB;IACpD,IAAIA,QAAQC,SAAS,EAAE;IAEvB,IAAI,CAACD,QAAQE,MAAM,EAAE;QACnBF,QAAQE,MAAM,GAAG,IAAIL,eAAeG,QAAQG,YAAY;QACxDH,QAAQI,UAAU,GAAG;IACvB;IAEAJ,QAAQC,SAAS,GAAGH,uBAAuBO,IAAI,CAACL,QAAQE,MAAM,EAAEF,QAAQM,eAAe;AACzF"}
@@ -0,0 +1,16 @@
1
+ import type { DynamoAdapter } from '../types.js';
2
+ /**
3
+ * Idempotently provision the single backing table with our composite-key
4
+ * schema: `pk` (String) partition + `sk` (String) sort, on-demand
5
+ * (`PAY_PER_REQUEST`) billing.
6
+ *
7
+ * Behavior:
8
+ * - DescribeTable first; return early if the table already exists.
9
+ * - Otherwise CreateTable + wait until ACTIVE (up to 60s).
10
+ *
11
+ * Sort key is always String. Collections that opt into Number IDs still work
12
+ * — the adapter coerces ids into strings for `sk` while keeping the original
13
+ * type on the `id` attribute.
14
+ */
15
+ export declare function ensureTable(adapter: DynamoAdapter, tableName: string): Promise<void>;
16
+ //# sourceMappingURL=ensureTable.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensureTable.d.ts","sourceRoot":"","sources":["../../src/utilities/ensureTable.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;;;;;;;;;;GAYG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiC1F"}
@@ -0,0 +1,62 @@
1
+ import { CreateTableCommand, DescribeTableCommand, waitUntilTableExists } from '@aws-sdk/client-dynamodb';
2
+ /**
3
+ * Idempotently provision the single backing table with our composite-key
4
+ * schema: `pk` (String) partition + `sk` (String) sort, on-demand
5
+ * (`PAY_PER_REQUEST`) billing.
6
+ *
7
+ * Behavior:
8
+ * - DescribeTable first; return early if the table already exists.
9
+ * - Otherwise CreateTable + wait until ACTIVE (up to 60s).
10
+ *
11
+ * Sort key is always String. Collections that opt into Number IDs still work
12
+ * — the adapter coerces ids into strings for `sk` while keeping the original
13
+ * type on the `id` attribute.
14
+ */ export async function ensureTable(adapter, tableName) {
15
+ const client = adapter.client;
16
+ if (!client) {
17
+ throw new Error('payload-ddb: client is not initialized — call connect() first.');
18
+ }
19
+ try {
20
+ await client.send(new DescribeTableCommand({
21
+ TableName: tableName
22
+ }));
23
+ return;
24
+ } catch (err) {
25
+ if (!(err instanceof Error) || err.name !== 'ResourceNotFoundException') {
26
+ throw err;
27
+ }
28
+ }
29
+ adapter.payload.logger.info(`payload-ddb: creating table \`${tableName}\``);
30
+ await client.send(new CreateTableCommand({
31
+ TableName: tableName,
32
+ AttributeDefinitions: [
33
+ {
34
+ AttributeName: 'pk',
35
+ AttributeType: 'S'
36
+ },
37
+ {
38
+ AttributeName: 'sk',
39
+ AttributeType: 'S'
40
+ }
41
+ ],
42
+ KeySchema: [
43
+ {
44
+ AttributeName: 'pk',
45
+ KeyType: 'HASH'
46
+ },
47
+ {
48
+ AttributeName: 'sk',
49
+ KeyType: 'RANGE'
50
+ }
51
+ ],
52
+ BillingMode: 'PAY_PER_REQUEST'
53
+ }));
54
+ await waitUntilTableExists({
55
+ client,
56
+ maxWaitTime: 60
57
+ }, {
58
+ TableName: tableName
59
+ });
60
+ }
61
+
62
+ //# sourceMappingURL=ensureTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/ensureTable.ts"],"sourcesContent":["import {\n CreateTableCommand,\n DescribeTableCommand,\n waitUntilTableExists,\n} from '@aws-sdk/client-dynamodb'\n\nimport type { DynamoAdapter } from '../types.js'\n\n/**\n * Idempotently provision the single backing table with our composite-key\n * schema: `pk` (String) partition + `sk` (String) sort, on-demand\n * (`PAY_PER_REQUEST`) billing.\n *\n * Behavior:\n * - DescribeTable first; return early if the table already exists.\n * - Otherwise CreateTable + wait until ACTIVE (up to 60s).\n *\n * Sort key is always String. Collections that opt into Number IDs still work\n * — the adapter coerces ids into strings for `sk` while keeping the original\n * type on the `id` attribute.\n */\nexport async function ensureTable(adapter: DynamoAdapter, tableName: string): Promise<void> {\n const client = adapter.client\n if (!client) {\n throw new Error('payload-ddb: client is not initialized — call connect() first.')\n }\n\n try {\n await client.send(new DescribeTableCommand({ TableName: tableName }))\n return\n } catch (err) {\n if (!(err instanceof Error) || err.name !== 'ResourceNotFoundException') {\n throw err\n }\n }\n\n adapter.payload.logger.info(`payload-ddb: creating table \\`${tableName}\\``)\n\n await client.send(\n new CreateTableCommand({\n TableName: tableName,\n AttributeDefinitions: [\n { AttributeName: 'pk', AttributeType: 'S' },\n { AttributeName: 'sk', AttributeType: 'S' },\n ],\n KeySchema: [\n { AttributeName: 'pk', KeyType: 'HASH' },\n { AttributeName: 'sk', KeyType: 'RANGE' },\n ],\n BillingMode: 'PAY_PER_REQUEST',\n }),\n )\n\n await waitUntilTableExists({ client, maxWaitTime: 60 }, { TableName: tableName })\n}\n"],"names":["CreateTableCommand","DescribeTableCommand","waitUntilTableExists","ensureTable","adapter","tableName","client","Error","send","TableName","err","name","payload","logger","info","AttributeDefinitions","AttributeName","AttributeType","KeySchema","KeyType","BillingMode","maxWaitTime"],"mappings":"AAAA,SACEA,kBAAkB,EAClBC,oBAAoB,EACpBC,oBAAoB,QACf,2BAA0B;AAIjC;;;;;;;;;;;;CAYC,GACD,OAAO,eAAeC,YAAYC,OAAsB,EAAEC,SAAiB;IACzE,MAAMC,SAASF,QAAQE,MAAM;IAC7B,IAAI,CAACA,QAAQ;QACX,MAAM,IAAIC,MAAM;IAClB;IAEA,IAAI;QACF,MAAMD,OAAOE,IAAI,CAAC,IAAIP,qBAAqB;YAAEQ,WAAWJ;QAAU;QAClE;IACF,EAAE,OAAOK,KAAK;QACZ,IAAI,CAAEA,CAAAA,eAAeH,KAAI,KAAMG,IAAIC,IAAI,KAAK,6BAA6B;YACvE,MAAMD;QACR;IACF;IAEAN,QAAQQ,OAAO,CAACC,MAAM,CAACC,IAAI,CAAC,CAAC,8BAA8B,EAAET,UAAU,EAAE,CAAC;IAE1E,MAAMC,OAAOE,IAAI,CACf,IAAIR,mBAAmB;QACrBS,WAAWJ;QACXU,sBAAsB;YACpB;gBAAEC,eAAe;gBAAMC,eAAe;YAAI;YAC1C;gBAAED,eAAe;gBAAMC,eAAe;YAAI;SAC3C;QACDC,WAAW;YACT;gBAAEF,eAAe;gBAAMG,SAAS;YAAO;YACvC;gBAAEH,eAAe;gBAAMG,SAAS;YAAQ;SACzC;QACDC,aAAa;IACf;IAGF,MAAMlB,qBAAqB;QAAEI;QAAQe,aAAa;IAAG,GAAG;QAAEZ,WAAWJ;IAAU;AACjF"}
@@ -0,0 +1,16 @@
1
+ import type { Where } from 'payload';
2
+ import type { DynamoAdapter } from '../types.js';
3
+ /**
4
+ * Locate the first item in a partition matching `where`. Used by `findOne`
5
+ * directly and by `deleteOne`/`updateOne` to capture the doc before mutating.
6
+ *
7
+ * Fast path: if `where` is a pure id-equality, do a single `GetItem` with the
8
+ * full composite key.
9
+ * Slow path: paginate `Query` (scoped by `pk = partition`) with
10
+ * `FilterExpression` pushdown and stop on the first matching row in any page.
11
+ */
12
+ export declare function findFirst(adapter: DynamoAdapter, args: {
13
+ partition: string;
14
+ where: undefined | Where;
15
+ }): Promise<null | Record<string, unknown>>;
16
+ //# sourceMappingURL=findFirst.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findFirst.d.ts","sourceRoot":"","sources":["../../src/utilities/findFirst.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAIpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAShD;;;;;;;;GAQG;AACH,wBAAsB,SAAS,CAC7B,OAAO,EAAE,aAAa,EACtB,IAAI,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,SAAS,GAAG,KAAK,CAAA;CAAE,GACpD,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CA+DzC"}
@@ -0,0 +1,79 @@
1
+ import { GetCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
2
+ import { buildFilterExpression } from './buildFilterExpression.js';
3
+ import { matchesWhere } from './matchesWhere.js';
4
+ import { whereHasJsOnlyOperator } from './operators.js';
5
+ import { queryMatching } from './queryMatching.js';
6
+ import { stripInternalKeys } from './stripInternalKeys.js';
7
+ import { whereToId } from './whereToId.js';
8
+ /**
9
+ * Locate the first item in a partition matching `where`. Used by `findOne`
10
+ * directly and by `deleteOne`/`updateOne` to capture the doc before mutating.
11
+ *
12
+ * Fast path: if `where` is a pure id-equality, do a single `GetItem` with the
13
+ * full composite key.
14
+ * Slow path: paginate `Query` (scoped by `pk = partition`) with
15
+ * `FilterExpression` pushdown and stop on the first matching row in any page.
16
+ */ export async function findFirst(adapter, args) {
17
+ const docClient = adapter.docClient;
18
+ if (!docClient) {
19
+ throw new Error('payload-ddb: docClient is not initialized — call connect() first.');
20
+ }
21
+ const { partition } = args;
22
+ const id = whereToId(args.where);
23
+ if (id !== null) {
24
+ const result = await docClient.send(new GetCommand({
25
+ TableName: adapter.tableName,
26
+ Key: {
27
+ pk: partition,
28
+ sk: String(id)
29
+ },
30
+ // Strong consistency — Payload assumes read-after-write visibility.
31
+ ConsistentRead: true
32
+ }));
33
+ return result.Item ? stripInternalKeys(result.Item) : null;
34
+ }
35
+ // JS-only operator (e.g. `like`) somewhere in `where` — fetch the partition
36
+ // and pick the first JS match. Loses the "stop on first hit per page"
37
+ // optimization but keeps semantics consistent with `queryMatching`.
38
+ if (whereHasJsOnlyOperator(args.where)) {
39
+ const all = await queryMatching(adapter, partition, undefined);
40
+ for (const row of all){
41
+ if (matchesWhere(row, args.where)) return row;
42
+ }
43
+ return null;
44
+ }
45
+ const filter = buildFilterExpression(args.where);
46
+ // Always-false predicate (e.g. `in: []`) → no document can match.
47
+ if (filter === null) return null;
48
+ let exclusiveStartKey;
49
+ while(true){
50
+ const result = await docClient.send(new QueryCommand({
51
+ TableName: adapter.tableName,
52
+ KeyConditionExpression: '#pk = :pk',
53
+ ExpressionAttributeNames: {
54
+ '#pk': 'pk',
55
+ ...filter?.names ?? {}
56
+ },
57
+ ExpressionAttributeValues: {
58
+ ':pk': partition,
59
+ ...filter?.values ?? {}
60
+ },
61
+ ConsistentRead: true,
62
+ ...filter ? {
63
+ FilterExpression: filter.expression
64
+ } : {},
65
+ ...exclusiveStartKey ? {
66
+ ExclusiveStartKey: exclusiveStartKey
67
+ } : {}
68
+ }));
69
+ for (const item of result.Items ?? []){
70
+ return stripInternalKeys(item);
71
+ }
72
+ if (!result.LastEvaluatedKey) {
73
+ return null;
74
+ }
75
+ exclusiveStartKey = result.LastEvaluatedKey;
76
+ }
77
+ }
78
+
79
+ //# sourceMappingURL=findFirst.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/findFirst.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nimport { GetCommand, QueryCommand } from '@aws-sdk/lib-dynamodb'\n\nimport type { DynamoAdapter } from '../types.js'\n\nimport { buildFilterExpression } from './buildFilterExpression.js'\nimport { matchesWhere } from './matchesWhere.js'\nimport { whereHasJsOnlyOperator } from './operators.js'\nimport { queryMatching } from './queryMatching.js'\nimport { stripInternalKeys } from './stripInternalKeys.js'\nimport { whereToId } from './whereToId.js'\n\n/**\n * Locate the first item in a partition matching `where`. Used by `findOne`\n * directly and by `deleteOne`/`updateOne` to capture the doc before mutating.\n *\n * Fast path: if `where` is a pure id-equality, do a single `GetItem` with the\n * full composite key.\n * Slow path: paginate `Query` (scoped by `pk = partition`) with\n * `FilterExpression` pushdown and stop on the first matching row in any page.\n */\nexport async function findFirst(\n adapter: DynamoAdapter,\n args: { partition: string; where: undefined | Where },\n): Promise<null | Record<string, unknown>> {\n const docClient = adapter.docClient\n if (!docClient) {\n throw new Error('payload-ddb: docClient is not initialized — call connect() first.')\n }\n\n const { partition } = args\n const id = whereToId(args.where)\n\n if (id !== null) {\n const result = await docClient.send(\n new GetCommand({\n TableName: adapter.tableName,\n Key: { pk: partition, sk: String(id) },\n // Strong consistency — Payload assumes read-after-write visibility.\n ConsistentRead: true,\n }),\n )\n return result.Item ? stripInternalKeys(result.Item) : null\n }\n\n // JS-only operator (e.g. `like`) somewhere in `where` — fetch the partition\n // and pick the first JS match. Loses the \"stop on first hit per page\"\n // optimization but keeps semantics consistent with `queryMatching`.\n if (whereHasJsOnlyOperator(args.where)) {\n const all = await queryMatching(adapter, partition, undefined)\n for (const row of all) {\n if (matchesWhere(row, args.where)) return row\n }\n return null\n }\n\n const filter = buildFilterExpression(args.where)\n // Always-false predicate (e.g. `in: []`) → no document can match.\n if (filter === null) return null\n let exclusiveStartKey: Record<string, unknown> | undefined\n\n while (true) {\n const result = await docClient.send(\n new QueryCommand({\n TableName: adapter.tableName,\n KeyConditionExpression: '#pk = :pk',\n ExpressionAttributeNames: {\n '#pk': 'pk',\n ...(filter?.names ?? {}),\n },\n ExpressionAttributeValues: {\n ':pk': partition,\n ...(filter?.values ?? {}),\n },\n ConsistentRead: true,\n ...(filter ? { FilterExpression: filter.expression } : {}),\n ...(exclusiveStartKey ? { ExclusiveStartKey: exclusiveStartKey } : {}),\n }),\n )\n for (const item of result.Items ?? []) {\n return stripInternalKeys(item)\n }\n if (!result.LastEvaluatedKey) {\n return null\n }\n exclusiveStartKey = result.LastEvaluatedKey\n }\n}\n"],"names":["GetCommand","QueryCommand","buildFilterExpression","matchesWhere","whereHasJsOnlyOperator","queryMatching","stripInternalKeys","whereToId","findFirst","adapter","args","docClient","Error","partition","id","where","result","send","TableName","tableName","Key","pk","sk","String","ConsistentRead","Item","all","undefined","row","filter","exclusiveStartKey","KeyConditionExpression","ExpressionAttributeNames","names","ExpressionAttributeValues","values","FilterExpression","expression","ExclusiveStartKey","item","Items","LastEvaluatedKey"],"mappings":"AAEA,SAASA,UAAU,EAAEC,YAAY,QAAQ,wBAAuB;AAIhE,SAASC,qBAAqB,QAAQ,6BAA4B;AAClE,SAASC,YAAY,QAAQ,oBAAmB;AAChD,SAASC,sBAAsB,QAAQ,iBAAgB;AACvD,SAASC,aAAa,QAAQ,qBAAoB;AAClD,SAASC,iBAAiB,QAAQ,yBAAwB;AAC1D,SAASC,SAAS,QAAQ,iBAAgB;AAE1C;;;;;;;;CAQC,GACD,OAAO,eAAeC,UACpBC,OAAsB,EACtBC,IAAqD;IAErD,MAAMC,YAAYF,QAAQE,SAAS;IACnC,IAAI,CAACA,WAAW;QACd,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAM,EAAEC,SAAS,EAAE,GAAGH;IACtB,MAAMI,KAAKP,UAAUG,KAAKK,KAAK;IAE/B,IAAID,OAAO,MAAM;QACf,MAAME,SAAS,MAAML,UAAUM,IAAI,CACjC,IAAIjB,WAAW;YACbkB,WAAWT,QAAQU,SAAS;YAC5BC,KAAK;gBAAEC,IAAIR;gBAAWS,IAAIC,OAAOT;YAAI;YACrC,oEAAoE;YACpEU,gBAAgB;QAClB;QAEF,OAAOR,OAAOS,IAAI,GAAGnB,kBAAkBU,OAAOS,IAAI,IAAI;IACxD;IAEA,4EAA4E;IAC5E,sEAAsE;IACtE,oEAAoE;IACpE,IAAIrB,uBAAuBM,KAAKK,KAAK,GAAG;QACtC,MAAMW,MAAM,MAAMrB,cAAcI,SAASI,WAAWc;QACpD,KAAK,MAAMC,OAAOF,IAAK;YACrB,IAAIvB,aAAayB,KAAKlB,KAAKK,KAAK,GAAG,OAAOa;QAC5C;QACA,OAAO;IACT;IAEA,MAAMC,SAAS3B,sBAAsBQ,KAAKK,KAAK;IAC/C,kEAAkE;IAClE,IAAIc,WAAW,MAAM,OAAO;IAC5B,IAAIC;IAEJ,MAAO,KAAM;QACX,MAAMd,SAAS,MAAML,UAAUM,IAAI,CACjC,IAAIhB,aAAa;YACfiB,WAAWT,QAAQU,SAAS;YAC5BY,wBAAwB;YACxBC,0BAA0B;gBACxB,OAAO;gBACP,GAAIH,QAAQI,SAAS,CAAC,CAAC;YACzB;YACAC,2BAA2B;gBACzB,OAAOrB;gBACP,GAAIgB,QAAQM,UAAU,CAAC,CAAC;YAC1B;YACAX,gBAAgB;YAChB,GAAIK,SAAS;gBAAEO,kBAAkBP,OAAOQ,UAAU;YAAC,IAAI,CAAC,CAAC;YACzD,GAAIP,oBAAoB;gBAAEQ,mBAAmBR;YAAkB,IAAI,CAAC,CAAC;QACvE;QAEF,KAAK,MAAMS,QAAQvB,OAAOwB,KAAK,IAAI,EAAE,CAAE;YACrC,OAAOlC,kBAAkBiC;QAC3B;QACA,IAAI,CAACvB,OAAOyB,gBAAgB,EAAE;YAC5B,OAAO;QACT;QACAX,oBAAoBd,OAAOyB,gBAAgB;IAC7C;AACF"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Read a dotted path from a record. Returns `undefined` if any segment along
3
+ * the way is missing or non-object.
4
+ *
5
+ * Intentionally simple — does not support array index syntax, escaped dots,
6
+ * or symbol keys. Field paths in Payload's `Where` are dotted strings.
7
+ */
8
+ export declare function getByPath(source: unknown, path: string): unknown;
9
+ //# sourceMappingURL=getByPath.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"getByPath.d.ts","sourceRoot":"","sources":["../../src/utilities/getByPath.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAahE"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Read a dotted path from a record. Returns `undefined` if any segment along
3
+ * the way is missing or non-object.
4
+ *
5
+ * Intentionally simple — does not support array index syntax, escaped dots,
6
+ * or symbol keys. Field paths in Payload's `Where` are dotted strings.
7
+ */ export function getByPath(source, path) {
8
+ if (source === null || typeof source !== 'object') {
9
+ return undefined;
10
+ }
11
+ const segments = path.split('.');
12
+ let cursor = source;
13
+ for (const segment of segments){
14
+ if (cursor === null || typeof cursor !== 'object') {
15
+ return undefined;
16
+ }
17
+ cursor = cursor[segment];
18
+ }
19
+ return cursor;
20
+ }
21
+
22
+ //# sourceMappingURL=getByPath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/getByPath.ts"],"sourcesContent":["/**\n * Read a dotted path from a record. Returns `undefined` if any segment along\n * the way is missing or non-object.\n *\n * Intentionally simple — does not support array index syntax, escaped dots,\n * or symbol keys. Field paths in Payload's `Where` are dotted strings.\n */\nexport function getByPath(source: unknown, path: string): unknown {\n if (source === null || typeof source !== 'object') {\n return undefined\n }\n const segments = path.split('.')\n let cursor: unknown = source\n for (const segment of segments) {\n if (cursor === null || typeof cursor !== 'object') {\n return undefined\n }\n cursor = (cursor as Record<string, unknown>)[segment]\n }\n return cursor\n}\n"],"names":["getByPath","source","path","undefined","segments","split","cursor","segment"],"mappings":"AAAA;;;;;;CAMC,GACD,OAAO,SAASA,UAAUC,MAAe,EAAEC,IAAY;IACrD,IAAID,WAAW,QAAQ,OAAOA,WAAW,UAAU;QACjD,OAAOE;IACT;IACA,MAAMC,WAAWF,KAAKG,KAAK,CAAC;IAC5B,IAAIC,SAAkBL;IACtB,KAAK,MAAMM,WAAWH,SAAU;QAC9B,IAAIE,WAAW,QAAQ,OAAOA,WAAW,UAAU;YACjD,OAAOH;QACT;QACAG,SAAS,AAACA,MAAkC,CAACC,QAAQ;IACvD;IACA,OAAOD;AACT"}
@@ -0,0 +1,12 @@
1
+ import type { Where } from 'payload';
2
+ /**
3
+ * In-memory predicate that evaluates a Payload `Where` against a fetched item.
4
+ * Used as a fallback when the query cannot be reduced to a `GetItem` and we
5
+ * have to `Scan` the table.
6
+ *
7
+ * Supports: `equals`, `not_equals`, `exists`, `in`, `not_in`, `and`, `or`.
8
+ * Throws on any other operator so we surface coverage gaps instead of
9
+ * silently returning wrong results.
10
+ */
11
+ export declare function matchesWhere(item: Record<string, unknown>, where: Where | undefined): boolean;
12
+ //# sourceMappingURL=matchesWhere.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchesWhere.d.ts","sourceRoot":"","sources":["../../src/utilities/matchesWhere.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAKpC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,CA8C7F"}
@@ -0,0 +1,98 @@
1
+ import { getByPath } from './getByPath.js';
2
+ import { SUPPORTED_OPERATORS, unsupportedOperatorError } from './operators.js';
3
+ /**
4
+ * In-memory predicate that evaluates a Payload `Where` against a fetched item.
5
+ * Used as a fallback when the query cannot be reduced to a `GetItem` and we
6
+ * have to `Scan` the table.
7
+ *
8
+ * Supports: `equals`, `not_equals`, `exists`, `in`, `not_in`, `and`, `or`.
9
+ * Throws on any other operator so we surface coverage gaps instead of
10
+ * silently returning wrong results.
11
+ */ export function matchesWhere(item, where) {
12
+ if (!where) {
13
+ return true;
14
+ }
15
+ for (const [key, raw] of Object.entries(where)){
16
+ if (key === 'and') {
17
+ if (!Array.isArray(raw)) continue;
18
+ for (const sub of raw){
19
+ if (!matchesWhere(item, sub)) return false;
20
+ }
21
+ continue;
22
+ }
23
+ if (key === 'or') {
24
+ if (!Array.isArray(raw)) continue;
25
+ let any = false;
26
+ for (const sub of raw){
27
+ if (matchesWhere(item, sub)) {
28
+ any = true;
29
+ break;
30
+ }
31
+ }
32
+ if (!any) return false;
33
+ continue;
34
+ }
35
+ if (!raw || typeof raw !== 'object') {
36
+ continue;
37
+ }
38
+ const fieldValue = getByPath(item, key);
39
+ const operators = raw;
40
+ for (const [operator, expected] of Object.entries(operators)){
41
+ if (!SUPPORTED_OPERATORS.has(operator)) {
42
+ throw unsupportedOperatorError(operator, key);
43
+ }
44
+ if (!evaluate(fieldValue, operator, expected)) {
45
+ return false;
46
+ }
47
+ }
48
+ }
49
+ return true;
50
+ }
51
+ function evaluate(actual, operator, expected) {
52
+ switch(operator){
53
+ case 'equals':
54
+ return actual === expected;
55
+ case 'not_equals':
56
+ return actual !== expected;
57
+ case 'greater_than':
58
+ return compareLoose(actual, expected) > 0;
59
+ case 'greater_than_equal':
60
+ return compareLoose(actual, expected) >= 0;
61
+ case 'less_than':
62
+ return compareLoose(actual, expected) < 0;
63
+ case 'less_than_equal':
64
+ return compareLoose(actual, expected) <= 0;
65
+ case 'exists':
66
+ return expected ? actual !== undefined && actual !== null : actual === undefined || actual === null;
67
+ case 'in':
68
+ return Array.isArray(expected) && expected.includes(actual);
69
+ case 'not_in':
70
+ return Array.isArray(expected) && !expected.includes(actual);
71
+ case 'like':
72
+ {
73
+ // Empty needle means "no constraint" — mirrors SQL `LIKE '%%'` and what
74
+ // Payload's admin search produces when the search input is cleared.
75
+ if (typeof expected !== 'string' || expected === '') return true;
76
+ if (typeof actual !== 'string') return false;
77
+ return actual.toLowerCase().includes(expected.toLowerCase());
78
+ }
79
+ default:
80
+ return false;
81
+ }
82
+ }
83
+ /**
84
+ * Loose ordered comparison matching DynamoDB's `<`/`>` semantics: numbers and
85
+ * strings (including ISO date strings) compare naturally; nullish on either
86
+ * side yields a non-match by returning `NaN`. Lets `greater_than`/`less_than`
87
+ * mirror the server-side filter evaluator behavior.
88
+ */ function compareLoose(a, b) {
89
+ if (a === undefined || a === null || b === undefined || b === null) return NaN;
90
+ if (typeof a === 'number' && typeof b === 'number') return a - b;
91
+ const as = String(a);
92
+ const bs = String(b);
93
+ if (as < bs) return -1;
94
+ if (as > bs) return 1;
95
+ return 0;
96
+ }
97
+
98
+ //# sourceMappingURL=matchesWhere.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/matchesWhere.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nimport { getByPath } from './getByPath.js'\nimport { SUPPORTED_OPERATORS, unsupportedOperatorError } from './operators.js'\n\n/**\n * In-memory predicate that evaluates a Payload `Where` against a fetched item.\n * Used as a fallback when the query cannot be reduced to a `GetItem` and we\n * have to `Scan` the table.\n *\n * Supports: `equals`, `not_equals`, `exists`, `in`, `not_in`, `and`, `or`.\n * Throws on any other operator so we surface coverage gaps instead of\n * silently returning wrong results.\n */\nexport function matchesWhere(item: Record<string, unknown>, where: Where | undefined): boolean {\n if (!where) {\n return true\n }\n\n for (const [key, raw] of Object.entries(where)) {\n if (key === 'and') {\n if (!Array.isArray(raw)) continue\n for (const sub of raw) {\n if (!matchesWhere(item, sub)) return false\n }\n continue\n }\n\n if (key === 'or') {\n if (!Array.isArray(raw)) continue\n let any = false\n for (const sub of raw) {\n if (matchesWhere(item, sub)) {\n any = true\n break\n }\n }\n if (!any) return false\n continue\n }\n\n if (!raw || typeof raw !== 'object') {\n continue\n }\n\n const fieldValue = getByPath(item, key)\n const operators = raw as Record<string, unknown>\n\n for (const [operator, expected] of Object.entries(operators)) {\n if (!SUPPORTED_OPERATORS.has(operator)) {\n throw unsupportedOperatorError(operator, key)\n }\n\n if (!evaluate(fieldValue, operator, expected)) {\n return false\n }\n }\n }\n\n return true\n}\n\nfunction evaluate(actual: unknown, operator: string, expected: unknown): boolean {\n switch (operator) {\n case 'equals':\n return actual === expected\n case 'not_equals':\n return actual !== expected\n case 'greater_than':\n return compareLoose(actual, expected) > 0\n case 'greater_than_equal':\n return compareLoose(actual, expected) >= 0\n case 'less_than':\n return compareLoose(actual, expected) < 0\n case 'less_than_equal':\n return compareLoose(actual, expected) <= 0\n case 'exists':\n return expected ? actual !== undefined && actual !== null : actual === undefined || actual === null\n case 'in':\n return Array.isArray(expected) && expected.includes(actual)\n case 'not_in':\n return Array.isArray(expected) && !expected.includes(actual)\n case 'like': {\n // Empty needle means \"no constraint\" — mirrors SQL `LIKE '%%'` and what\n // Payload's admin search produces when the search input is cleared.\n if (typeof expected !== 'string' || expected === '') return true\n if (typeof actual !== 'string') return false\n return actual.toLowerCase().includes(expected.toLowerCase())\n }\n default:\n return false\n }\n}\n\n/**\n * Loose ordered comparison matching DynamoDB's `<`/`>` semantics: numbers and\n * strings (including ISO date strings) compare naturally; nullish on either\n * side yields a non-match by returning `NaN`. Lets `greater_than`/`less_than`\n * mirror the server-side filter evaluator behavior.\n */\nfunction compareLoose(a: unknown, b: unknown): number {\n if (a === undefined || a === null || b === undefined || b === null) return NaN\n if (typeof a === 'number' && typeof b === 'number') return a - b\n const as = String(a)\n const bs = String(b)\n if (as < bs) return -1\n if (as > bs) return 1\n return 0\n}\n"],"names":["getByPath","SUPPORTED_OPERATORS","unsupportedOperatorError","matchesWhere","item","where","key","raw","Object","entries","Array","isArray","sub","any","fieldValue","operators","operator","expected","has","evaluate","actual","compareLoose","undefined","includes","toLowerCase","a","b","NaN","as","String","bs"],"mappings":"AAEA,SAASA,SAAS,QAAQ,iBAAgB;AAC1C,SAASC,mBAAmB,EAAEC,wBAAwB,QAAQ,iBAAgB;AAE9E;;;;;;;;CAQC,GACD,OAAO,SAASC,aAAaC,IAA6B,EAAEC,KAAwB;IAClF,IAAI,CAACA,OAAO;QACV,OAAO;IACT;IAEA,KAAK,MAAM,CAACC,KAAKC,IAAI,IAAIC,OAAOC,OAAO,CAACJ,OAAQ;QAC9C,IAAIC,QAAQ,OAAO;YACjB,IAAI,CAACI,MAAMC,OAAO,CAACJ,MAAM;YACzB,KAAK,MAAMK,OAAOL,IAAK;gBACrB,IAAI,CAACJ,aAAaC,MAAMQ,MAAM,OAAO;YACvC;YACA;QACF;QAEA,IAAIN,QAAQ,MAAM;YAChB,IAAI,CAACI,MAAMC,OAAO,CAACJ,MAAM;YACzB,IAAIM,MAAM;YACV,KAAK,MAAMD,OAAOL,IAAK;gBACrB,IAAIJ,aAAaC,MAAMQ,MAAM;oBAC3BC,MAAM;oBACN;gBACF;YACF;YACA,IAAI,CAACA,KAAK,OAAO;YACjB;QACF;QAEA,IAAI,CAACN,OAAO,OAAOA,QAAQ,UAAU;YACnC;QACF;QAEA,MAAMO,aAAad,UAAUI,MAAME;QACnC,MAAMS,YAAYR;QAElB,KAAK,MAAM,CAACS,UAAUC,SAAS,IAAIT,OAAOC,OAAO,CAACM,WAAY;YAC5D,IAAI,CAACd,oBAAoBiB,GAAG,CAACF,WAAW;gBACtC,MAAMd,yBAAyBc,UAAUV;YAC3C;YAEA,IAAI,CAACa,SAASL,YAAYE,UAAUC,WAAW;gBAC7C,OAAO;YACT;QACF;IACF;IAEA,OAAO;AACT;AAEA,SAASE,SAASC,MAAe,EAAEJ,QAAgB,EAAEC,QAAiB;IACpE,OAAQD;QACN,KAAK;YACH,OAAOI,WAAWH;QACpB,KAAK;YACH,OAAOG,WAAWH;QACpB,KAAK;YACH,OAAOI,aAAaD,QAAQH,YAAY;QAC1C,KAAK;YACH,OAAOI,aAAaD,QAAQH,aAAa;QAC3C,KAAK;YACH,OAAOI,aAAaD,QAAQH,YAAY;QAC1C,KAAK;YACH,OAAOI,aAAaD,QAAQH,aAAa;QAC3C,KAAK;YACH,OAAOA,WAAWG,WAAWE,aAAaF,WAAW,OAAOA,WAAWE,aAAaF,WAAW;QACjG,KAAK;YACH,OAAOV,MAAMC,OAAO,CAACM,aAAaA,SAASM,QAAQ,CAACH;QACtD,KAAK;YACH,OAAOV,MAAMC,OAAO,CAACM,aAAa,CAACA,SAASM,QAAQ,CAACH;QACvD,KAAK;YAAQ;gBACX,wEAAwE;gBACxE,oEAAoE;gBACpE,IAAI,OAAOH,aAAa,YAAYA,aAAa,IAAI,OAAO;gBAC5D,IAAI,OAAOG,WAAW,UAAU,OAAO;gBACvC,OAAOA,OAAOI,WAAW,GAAGD,QAAQ,CAACN,SAASO,WAAW;YAC3D;QACA;YACE,OAAO;IACX;AACF;AAEA;;;;;CAKC,GACD,SAASH,aAAaI,CAAU,EAAEC,CAAU;IAC1C,IAAID,MAAMH,aAAaG,MAAM,QAAQC,MAAMJ,aAAaI,MAAM,MAAM,OAAOC;IAC3E,IAAI,OAAOF,MAAM,YAAY,OAAOC,MAAM,UAAU,OAAOD,IAAIC;IAC/D,MAAME,KAAKC,OAAOJ;IAClB,MAAMK,KAAKD,OAAOH;IAClB,IAAIE,KAAKE,IAAI,OAAO,CAAC;IACrB,IAAIF,KAAKE,IAAI,OAAO;IACpB,OAAO;AACT"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Convert values that DynamoDB's marshaller can't represent natively into a
3
+ * shape it can. Today this is just `Date → ISO string`, but the same hook is
4
+ * the right place for any future "Payload sends X, Dynamo wants Y" coercions
5
+ * (e.g. `BigInt`).
6
+ *
7
+ * Why this exists: with `convertClassInstanceToMap: true` (our default
8
+ * translate option), a `Date` instance has no enumerable own properties and
9
+ * marshals as an empty map `{}`. That stores fine but compares as type `M`,
10
+ * which `FilterExpression` operators like `>` reject. Stringifying to ISO
11
+ * keeps Mongo-parity ordering (lex compare on ISO strings = chronological
12
+ * order) and round-trips cleanly back to a `Date` in user code.
13
+ */
14
+ export declare function normalizeForDynamo<T>(value: T): T;
15
+ //# sourceMappingURL=normalizeForDynamo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalizeForDynamo.d.ts","sourceRoot":"","sources":["../../src/utilities/normalizeForDynamo.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAYjD"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Convert values that DynamoDB's marshaller can't represent natively into a
3
+ * shape it can. Today this is just `Date → ISO string`, but the same hook is
4
+ * the right place for any future "Payload sends X, Dynamo wants Y" coercions
5
+ * (e.g. `BigInt`).
6
+ *
7
+ * Why this exists: with `convertClassInstanceToMap: true` (our default
8
+ * translate option), a `Date` instance has no enumerable own properties and
9
+ * marshals as an empty map `{}`. That stores fine but compares as type `M`,
10
+ * which `FilterExpression` operators like `>` reject. Stringifying to ISO
11
+ * keeps Mongo-parity ordering (lex compare on ISO strings = chronological
12
+ * order) and round-trips cleanly back to a `Date` in user code.
13
+ */ export function normalizeForDynamo(value) {
14
+ if (value === null || value === undefined) return value;
15
+ if (value instanceof Date) return value.toISOString();
16
+ if (Array.isArray(value)) return value.map(normalizeForDynamo);
17
+ if (typeof value === 'object') {
18
+ const out = {};
19
+ for (const [k, v] of Object.entries(value)){
20
+ out[k] = normalizeForDynamo(v);
21
+ }
22
+ return out;
23
+ }
24
+ return value;
25
+ }
26
+
27
+ //# sourceMappingURL=normalizeForDynamo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/normalizeForDynamo.ts"],"sourcesContent":["/**\n * Convert values that DynamoDB's marshaller can't represent natively into a\n * shape it can. Today this is just `Date → ISO string`, but the same hook is\n * the right place for any future \"Payload sends X, Dynamo wants Y\" coercions\n * (e.g. `BigInt`).\n *\n * Why this exists: with `convertClassInstanceToMap: true` (our default\n * translate option), a `Date` instance has no enumerable own properties and\n * marshals as an empty map `{}`. That stores fine but compares as type `M`,\n * which `FilterExpression` operators like `>` reject. Stringifying to ISO\n * keeps Mongo-parity ordering (lex compare on ISO strings = chronological\n * order) and round-trips cleanly back to a `Date` in user code.\n */\nexport function normalizeForDynamo<T>(value: T): T {\n if (value === null || value === undefined) return value\n if (value instanceof Date) return value.toISOString() as T\n if (Array.isArray(value)) return value.map(normalizeForDynamo) as T\n if (typeof value === 'object') {\n const out: Record<string, unknown> = {}\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = normalizeForDynamo(v)\n }\n return out as T\n }\n return value\n}\n"],"names":["normalizeForDynamo","value","undefined","Date","toISOString","Array","isArray","map","out","k","v","Object","entries"],"mappings":"AAAA;;;;;;;;;;;;CAYC,GACD,OAAO,SAASA,mBAAsBC,KAAQ;IAC5C,IAAIA,UAAU,QAAQA,UAAUC,WAAW,OAAOD;IAClD,IAAIA,iBAAiBE,MAAM,OAAOF,MAAMG,WAAW;IACnD,IAAIC,MAAMC,OAAO,CAACL,QAAQ,OAAOA,MAAMM,GAAG,CAACP;IAC3C,IAAI,OAAOC,UAAU,UAAU;QAC7B,MAAMO,MAA+B,CAAC;QACtC,KAAK,MAAM,CAACC,GAAGC,EAAE,IAAIC,OAAOC,OAAO,CAACX,OAAmC;YACrEO,GAAG,CAACC,EAAE,GAAGT,mBAAmBU;QAC9B;QACA,OAAOF;IACT;IACA,OAAOP;AACT"}
@@ -0,0 +1,26 @@
1
+ import type { Where } from 'payload';
2
+ /**
3
+ * The where-operator coverage shared by `matchesWhere` (in-JS evaluator) and
4
+ * `buildFilterExpression` (DynamoDB pushdown). Keeping the set in one place
5
+ * keeps the two paths honest — if an operator is supported in one, it should
6
+ * be supported in the other, since callers can't know which path runs.
7
+ */
8
+ export declare const SUPPORTED_OPERATORS: Set<string>;
9
+ /**
10
+ * Operators we can't faithfully express in a DynamoDB `FilterExpression` and
11
+ * therefore evaluate in JS after fetching candidate rows. `like` is here
12
+ * because Payload's contract is case-insensitive substring matching while
13
+ * DynamoDB's `contains()` is case-sensitive — pushing it down would silently
14
+ * change semantics. When `where` contains any of these, the read helpers
15
+ * `Query` the partition without server-side filtering and apply
16
+ * `matchesWhere` in memory.
17
+ */
18
+ export declare const JS_ONLY_OPERATORS: Set<string>;
19
+ export declare const SUPPORTED_OPERATORS_DESCRIPTION: string;
20
+ export declare function unsupportedOperatorError(operator: string, field: string): Error;
21
+ /**
22
+ * Walk a `Where` and return true if any leaf clause uses an operator that
23
+ * forces JS-side evaluation. Recurses into `and`/`or` groups.
24
+ */
25
+ export declare function whereHasJsOnlyOperator(where: undefined | Where): boolean;
26
+ //# sourceMappingURL=operators.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"operators.d.ts","sourceRoot":"","sources":["../../src/utilities/operators.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,aAW9B,CAAA;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,iBAAiB,aAAoB,CAAA;AAElD,eAAO,MAAM,+BAA+B,QAAoD,CAAA;AAEhG,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAK/E;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,GAAG,OAAO,CAkBxE"}