@open-aih/payload-ddb 3.1.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +141 -18
  2. package/dist/count.d.ts.map +1 -1
  3. package/dist/count.js +4 -2
  4. package/dist/count.js.map +1 -1
  5. package/dist/countGlobalVersions.d.ts.map +1 -1
  6. package/dist/countGlobalVersions.js +6 -1
  7. package/dist/countGlobalVersions.js.map +1 -1
  8. package/dist/countVersions.d.ts.map +1 -1
  9. package/dist/countVersions.js +6 -1
  10. package/dist/countVersions.js.map +1 -1
  11. package/dist/create.d.ts.map +1 -1
  12. package/dist/create.js +31 -15
  13. package/dist/create.js.map +1 -1
  14. package/dist/createGlobal.d.ts.map +1 -1
  15. package/dist/createGlobal.js +2 -14
  16. package/dist/createGlobal.js.map +1 -1
  17. package/dist/createGlobalVersion.d.ts +1 -1
  18. package/dist/createGlobalVersion.d.ts.map +1 -1
  19. package/dist/createGlobalVersion.js +3 -41
  20. package/dist/createGlobalVersion.js.map +1 -1
  21. package/dist/createVersion.d.ts +6 -5
  22. package/dist/createVersion.d.ts.map +1 -1
  23. package/dist/createVersion.js +8 -48
  24. package/dist/createVersion.js.map +1 -1
  25. package/dist/deleteMany.d.ts +1 -6
  26. package/dist/deleteMany.d.ts.map +1 -1
  27. package/dist/deleteMany.js +8 -19
  28. package/dist/deleteMany.js.map +1 -1
  29. package/dist/deleteOne.d.ts.map +1 -1
  30. package/dist/deleteOne.js +20 -24
  31. package/dist/deleteOne.js.map +1 -1
  32. package/dist/deleteVersions.d.ts.map +1 -1
  33. package/dist/deleteVersions.js +8 -14
  34. package/dist/deleteVersions.js.map +1 -1
  35. package/dist/find.d.ts.map +1 -1
  36. package/dist/find.js +15 -2
  37. package/dist/find.js.map +1 -1
  38. package/dist/findDistinct.d.ts.map +1 -1
  39. package/dist/findDistinct.js +3 -1
  40. package/dist/findDistinct.js.map +1 -1
  41. package/dist/findGlobal.d.ts.map +1 -1
  42. package/dist/findGlobal.js +3 -2
  43. package/dist/findGlobal.js.map +1 -1
  44. package/dist/findGlobalVersions.d.ts.map +1 -1
  45. package/dist/findGlobalVersions.js +10 -2
  46. package/dist/findGlobalVersions.js.map +1 -1
  47. package/dist/findOne.d.ts.map +1 -1
  48. package/dist/findOne.js +17 -6
  49. package/dist/findOne.js.map +1 -1
  50. package/dist/findVersions.d.ts.map +1 -1
  51. package/dist/findVersions.js +10 -2
  52. package/dist/findVersions.js.map +1 -1
  53. package/dist/index.d.ts +2 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +7 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/queryDrafts.d.ts.map +1 -1
  58. package/dist/queryDrafts.js +24 -4
  59. package/dist/queryDrafts.js.map +1 -1
  60. package/dist/updateGlobal.d.ts +5 -4
  61. package/dist/updateGlobal.d.ts.map +1 -1
  62. package/dist/updateGlobal.js +22 -36
  63. package/dist/updateGlobal.js.map +1 -1
  64. package/dist/updateGlobalVersion.d.ts.map +1 -1
  65. package/dist/updateGlobalVersion.js +3 -49
  66. package/dist/updateGlobalVersion.js.map +1 -1
  67. package/dist/updateJobs.d.ts +21 -0
  68. package/dist/updateJobs.d.ts.map +1 -0
  69. package/dist/updateJobs.js +83 -0
  70. package/dist/updateJobs.js.map +1 -0
  71. package/dist/updateMany.d.ts +9 -3
  72. package/dist/updateMany.d.ts.map +1 -1
  73. package/dist/updateMany.js +47 -30
  74. package/dist/updateMany.js.map +1 -1
  75. package/dist/updateOne.d.ts +6 -1
  76. package/dist/updateOne.d.ts.map +1 -1
  77. package/dist/updateOne.js +49 -49
  78. package/dist/updateOne.js.map +1 -1
  79. package/dist/updateVersion.d.ts.map +1 -1
  80. package/dist/updateVersion.js +3 -52
  81. package/dist/updateVersion.js.map +1 -1
  82. package/dist/upsert.d.ts +6 -0
  83. package/dist/upsert.d.ts.map +1 -1
  84. package/dist/upsert.js +57 -41
  85. package/dist/upsert.js.map +1 -1
  86. package/dist/utilities/backfillUniqueGuards.d.ts +42 -0
  87. package/dist/utilities/backfillUniqueGuards.d.ts.map +1 -0
  88. package/dist/utilities/backfillUniqueGuards.js +39 -0
  89. package/dist/utilities/backfillUniqueGuards.js.map +1 -0
  90. package/dist/utilities/buildFilterExpression.d.ts +29 -14
  91. package/dist/utilities/buildFilterExpression.d.ts.map +1 -1
  92. package/dist/utilities/buildFilterExpression.js +55 -14
  93. package/dist/utilities/buildFilterExpression.js.map +1 -1
  94. package/dist/utilities/castWhere.d.ts +84 -0
  95. package/dist/utilities/castWhere.d.ts.map +1 -0
  96. package/dist/utilities/castWhere.js +254 -0
  97. package/dist/utilities/castWhere.js.map +1 -0
  98. package/dist/utilities/findFirst.d.ts +3 -2
  99. package/dist/utilities/findFirst.d.ts.map +1 -1
  100. package/dist/utilities/findFirst.js +14 -46
  101. package/dist/utilities/findFirst.js.map +1 -1
  102. package/dist/utilities/geo.d.ts +36 -0
  103. package/dist/utilities/geo.d.ts.map +1 -0
  104. package/dist/utilities/geo.js +108 -0
  105. package/dist/utilities/geo.js.map +1 -0
  106. package/dist/utilities/mapWithConcurrency.d.ts +12 -0
  107. package/dist/utilities/mapWithConcurrency.d.ts.map +1 -0
  108. package/dist/utilities/mapWithConcurrency.js +32 -0
  109. package/dist/utilities/mapWithConcurrency.js.map +1 -0
  110. package/dist/utilities/matchesWhere.d.ts +25 -5
  111. package/dist/utilities/matchesWhere.d.ts.map +1 -1
  112. package/dist/utilities/matchesWhere.js +150 -38
  113. package/dist/utilities/matchesWhere.js.map +1 -1
  114. package/dist/utilities/operators.d.ts +22 -12
  115. package/dist/utilities/operators.d.ts.map +1 -1
  116. package/dist/utilities/operators.js +36 -31
  117. package/dist/utilities/operators.js.map +1 -1
  118. package/dist/utilities/optimistic.d.ts +29 -0
  119. package/dist/utilities/optimistic.d.ts.map +1 -0
  120. package/dist/utilities/optimistic.js +58 -0
  121. package/dist/utilities/optimistic.js.map +1 -0
  122. package/dist/utilities/prepareWhere.d.ts +63 -0
  123. package/dist/utilities/prepareWhere.d.ts.map +1 -0
  124. package/dist/utilities/prepareWhere.js +198 -0
  125. package/dist/utilities/prepareWhere.js.map +1 -0
  126. package/dist/utilities/queryCount.d.ts +6 -3
  127. package/dist/utilities/queryCount.d.ts.map +1 -1
  128. package/dist/utilities/queryCount.js +16 -45
  129. package/dist/utilities/queryCount.js.map +1 -1
  130. package/dist/utilities/queryMatching.d.ts +8 -7
  131. package/dist/utilities/queryMatching.d.ts.map +1 -1
  132. package/dist/utilities/queryMatching.js +17 -54
  133. package/dist/utilities/queryMatching.js.map +1 -1
  134. package/dist/utilities/queryPartition.d.ts +26 -0
  135. package/dist/utilities/queryPartition.d.ts.map +1 -0
  136. package/dist/utilities/queryPartition.js +55 -0
  137. package/dist/utilities/queryPartition.js.map +1 -0
  138. package/dist/utilities/resolveJoins.d.ts +37 -0
  139. package/dist/utilities/resolveJoins.d.ts.map +1 -0
  140. package/dist/utilities/resolveJoins.js +195 -0
  141. package/dist/utilities/resolveJoins.js.map +1 -0
  142. package/dist/utilities/stripInternalKeys.d.ts +17 -3
  143. package/dist/utilities/stripInternalKeys.d.ts.map +1 -1
  144. package/dist/utilities/stripInternalKeys.js +25 -4
  145. package/dist/utilities/stripInternalKeys.js.map +1 -1
  146. package/dist/utilities/uniqueGuards.d.ts +39 -0
  147. package/dist/utilities/uniqueGuards.d.ts.map +1 -0
  148. package/dist/utilities/uniqueGuards.js +106 -0
  149. package/dist/utilities/uniqueGuards.js.map +1 -0
  150. package/dist/utilities/updateVersionRow.d.ts +19 -0
  151. package/dist/utilities/updateVersionRow.d.ts.map +1 -0
  152. package/dist/utilities/updateVersionRow.js +40 -0
  153. package/dist/utilities/updateVersionRow.js.map +1 -0
  154. package/dist/utilities/writeErrors.d.ts +11 -0
  155. package/dist/utilities/writeErrors.d.ts.map +1 -0
  156. package/dist/utilities/writeErrors.js +27 -0
  157. package/dist/utilities/writeErrors.js.map +1 -0
  158. package/dist/utilities/writeItems.d.ts +124 -0
  159. package/dist/utilities/writeItems.d.ts.map +1 -0
  160. package/dist/utilities/writeItems.js +383 -0
  161. package/dist/utilities/writeItems.js.map +1 -0
  162. package/package.json +21 -14
@@ -0,0 +1,42 @@
1
+ import type { Payload } from 'payload';
2
+ /**
3
+ * One-shot pass that creates unique-field guard rows for documents written
4
+ * before the adapter enforced `unique: true`.
5
+ *
6
+ * Guards are created as documents are written, so a document that hasn't
7
+ * been touched since upgrading holds its unique values without a guard — a
8
+ * new document could still take the same value. Running this once closes
9
+ * that gap: it walks every collection that declares unique fields and claims
10
+ * a guard for each value each document holds.
11
+ *
12
+ * Safe to re-run; claiming a guard the document already owns is a no-op.
13
+ *
14
+ * Existing duplicates can't both be guarded. The first document reached
15
+ * keeps the value and the others are listed under `conflicts` — fix those by
16
+ * hand (change or clear the duplicated value) and run the pass again.
17
+ *
18
+ * @example
19
+ * import { getPayload } from 'payload'
20
+ * import config from './payload.config'
21
+ * import { backfillUniqueGuards } from '@aih-software/payload-ddb'
22
+ *
23
+ * const payload = await getPayload({ config })
24
+ * const report = await backfillUniqueGuards(payload)
25
+ * console.log(JSON.stringify(report, null, 2))
26
+ * await payload.destroy()
27
+ */
28
+ export interface UniqueBackfillReport {
29
+ collections: Record<string, {
30
+ /** Values another document already holds a guard for. */
31
+ conflicts: {
32
+ heldBy: string;
33
+ id: unknown;
34
+ path: string;
35
+ }[];
36
+ /** Guards this pass created or confirmed. */
37
+ guarded: number;
38
+ scanned: number;
39
+ }>;
40
+ }
41
+ export declare function backfillUniqueGuards(payload: Payload): Promise<UniqueBackfillReport>;
42
+ //# sourceMappingURL=backfillUniqueGuards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backfillUniqueGuards.d.ts","sourceRoot":"","sources":["../../src/utilities/backfillUniqueGuards.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,SAAS,CAAA;AAStC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,MAAM,CACjB,MAAM,EACN;QACE,yDAAyD;QACzD,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,CAAC;YAAC,EAAE,EAAE,OAAO,CAAC;YAAC,IAAI,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;QAC1D,6CAA6C;QAC7C,OAAO,EAAE,MAAM,CAAA;QACf,OAAO,EAAE,MAAM,CAAA;KAChB,CACF,CAAA;CACF;AAED,wBAAsB,oBAAoB,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,oBAAoB,CAAC,CA+B1F"}
@@ -0,0 +1,39 @@
1
+ import { mapWithConcurrency } from './mapWithConcurrency.js';
2
+ import { queryMatching } from './queryMatching.js';
3
+ import { hasUniqueFields, uniqueGuardsFor } from './uniqueGuards.js';
4
+ import { claimGuard, WRITE_CONCURRENCY } from './writeItems.js';
5
+ export async function backfillUniqueGuards(payload) {
6
+ const adapter = payload.db;
7
+ if (!adapter.docClient) {
8
+ throw new Error('payload-ddb: backfillUniqueGuards requires a connected adapter.');
9
+ }
10
+ const report = {
11
+ collections: {}
12
+ };
13
+ for (const slug of Object.keys(adapter.payload?.collections ?? {})){
14
+ if (!hasUniqueFields(adapter, slug)) continue;
15
+ const partition = adapter.resolvePartition(slug);
16
+ const entry = {
17
+ conflicts: [],
18
+ guarded: 0,
19
+ scanned: 0
20
+ };
21
+ report.collections[slug] = entry;
22
+ const rows = await queryMatching(adapter, partition, undefined);
23
+ await mapWithConcurrency(rows, WRITE_CONCURRENCY, async (row)=>{
24
+ entry.scanned++;
25
+ for (const guard of uniqueGuardsFor(adapter, slug, partition, row)){
26
+ const heldBy = await claimGuard(adapter, guard, String(row['id']));
27
+ if (heldBy === null) entry.guarded++;
28
+ else entry.conflicts.push({
29
+ heldBy,
30
+ id: row['id'],
31
+ path: guard.path
32
+ });
33
+ }
34
+ });
35
+ }
36
+ return report;
37
+ }
38
+
39
+ //# sourceMappingURL=backfillUniqueGuards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/backfillUniqueGuards.ts"],"sourcesContent":["import type { Payload } from 'payload'\n\nimport type { DynamoAdapter } from '../types.js'\n\nimport { mapWithConcurrency } from './mapWithConcurrency.js'\nimport { queryMatching } from './queryMatching.js'\nimport { hasUniqueFields, uniqueGuardsFor } from './uniqueGuards.js'\nimport { claimGuard, WRITE_CONCURRENCY } from './writeItems.js'\n\n/**\n * One-shot pass that creates unique-field guard rows for documents written\n * before the adapter enforced `unique: true`.\n *\n * Guards are created as documents are written, so a document that hasn't\n * been touched since upgrading holds its unique values without a guard — a\n * new document could still take the same value. Running this once closes\n * that gap: it walks every collection that declares unique fields and claims\n * a guard for each value each document holds.\n *\n * Safe to re-run; claiming a guard the document already owns is a no-op.\n *\n * Existing duplicates can't both be guarded. The first document reached\n * keeps the value and the others are listed under `conflicts` — fix those by\n * hand (change or clear the duplicated value) and run the pass again.\n *\n * @example\n * import { getPayload } from 'payload'\n * import config from './payload.config'\n * import { backfillUniqueGuards } from '@aih-software/payload-ddb'\n *\n * const payload = await getPayload({ config })\n * const report = await backfillUniqueGuards(payload)\n * console.log(JSON.stringify(report, null, 2))\n * await payload.destroy()\n */\nexport interface UniqueBackfillReport {\n collections: Record<\n string,\n {\n /** Values another document already holds a guard for. */\n conflicts: { heldBy: string; id: unknown; path: string }[]\n /** Guards this pass created or confirmed. */\n guarded: number\n scanned: number\n }\n >\n}\n\nexport async function backfillUniqueGuards(payload: Payload): Promise<UniqueBackfillReport> {\n const adapter = payload.db as DynamoAdapter\n if (!adapter.docClient) {\n throw new Error('payload-ddb: backfillUniqueGuards requires a connected adapter.')\n }\n\n const report: UniqueBackfillReport = { collections: {} }\n\n for (const slug of Object.keys(adapter.payload?.collections ?? {})) {\n if (!hasUniqueFields(adapter, slug)) continue\n\n const partition = adapter.resolvePartition(slug)\n const entry: UniqueBackfillReport['collections'][string] = {\n conflicts: [],\n guarded: 0,\n scanned: 0,\n }\n report.collections[slug] = entry\n\n const rows = await queryMatching(adapter, partition, undefined)\n await mapWithConcurrency(rows, WRITE_CONCURRENCY, async (row) => {\n entry.scanned++\n for (const guard of uniqueGuardsFor(adapter, slug, partition, row)) {\n const heldBy = await claimGuard(adapter, guard, String(row['id']))\n if (heldBy === null) entry.guarded++\n else entry.conflicts.push({ heldBy, id: row['id'], path: guard.path })\n }\n })\n }\n\n return report\n}\n"],"names":["mapWithConcurrency","queryMatching","hasUniqueFields","uniqueGuardsFor","claimGuard","WRITE_CONCURRENCY","backfillUniqueGuards","payload","adapter","db","docClient","Error","report","collections","slug","Object","keys","partition","resolvePartition","entry","conflicts","guarded","scanned","rows","undefined","row","guard","heldBy","String","push","id","path"],"mappings":"AAIA,SAASA,kBAAkB,QAAQ,0BAAyB;AAC5D,SAASC,aAAa,QAAQ,qBAAoB;AAClD,SAASC,eAAe,EAAEC,eAAe,QAAQ,oBAAmB;AACpE,SAASC,UAAU,EAAEC,iBAAiB,QAAQ,kBAAiB;AAyC/D,OAAO,eAAeC,qBAAqBC,OAAgB;IACzD,MAAMC,UAAUD,QAAQE,EAAE;IAC1B,IAAI,CAACD,QAAQE,SAAS,EAAE;QACtB,MAAM,IAAIC,MAAM;IAClB;IAEA,MAAMC,SAA+B;QAAEC,aAAa,CAAC;IAAE;IAEvD,KAAK,MAAMC,QAAQC,OAAOC,IAAI,CAACR,QAAQD,OAAO,EAAEM,eAAe,CAAC,GAAI;QAClE,IAAI,CAACX,gBAAgBM,SAASM,OAAO;QAErC,MAAMG,YAAYT,QAAQU,gBAAgB,CAACJ;QAC3C,MAAMK,QAAqD;YACzDC,WAAW,EAAE;YACbC,SAAS;YACTC,SAAS;QACX;QACAV,OAAOC,WAAW,CAACC,KAAK,GAAGK;QAE3B,MAAMI,OAAO,MAAMtB,cAAcO,SAASS,WAAWO;QACrD,MAAMxB,mBAAmBuB,MAAMlB,mBAAmB,OAAOoB;YACvDN,MAAMG,OAAO;YACb,KAAK,MAAMI,SAASvB,gBAAgBK,SAASM,MAAMG,WAAWQ,KAAM;gBAClE,MAAME,SAAS,MAAMvB,WAAWI,SAASkB,OAAOE,OAAOH,GAAG,CAAC,KAAK;gBAChE,IAAIE,WAAW,MAAMR,MAAME,OAAO;qBAC7BF,MAAMC,SAAS,CAACS,IAAI,CAAC;oBAAEF;oBAAQG,IAAIL,GAAG,CAAC,KAAK;oBAAEM,MAAML,MAAMK,IAAI;gBAAC;YACtE;QACF;IACF;IAEA,OAAOnB;AACT"}
@@ -3,16 +3,23 @@ import type { Where } from 'payload';
3
3
  * Compile a Payload `Where` into a DynamoDB `FilterExpression` plus its
4
4
  * companion `ExpressionAttributeNames` / `ExpressionAttributeValues` maps.
5
5
  *
6
- * Return contract:
7
- * - `undefined` — no filter to apply (empty/missing `where`, or only no-op
8
- * clauses like `and: []`). Callers should query without a filter.
9
- * - `null` — the predicate is structurally always-false (e.g. an empty
10
- * `id: { in: [] }`). Callers should short-circuit and return zero rows
11
- * without ever hitting DynamoDB. Payload uses empty `in` as a "match
12
- * nothing" sentinel (typically from access control resolving to no IDs);
13
- * surfacing it as a filter would mean throwing or constructing an
14
- * invalid `IN ()` clause.
15
- * - `FilterExpression` — actual predicate to push down.
6
+ * The result is a *prefilter*: it never rejects a row the `where` accepts,
7
+ * but it may let through rows the `where` rejects. `exact` says which:
8
+ *
9
+ * - `exact: true` — the filter (or its absence) is the whole `where`; rows
10
+ * DynamoDB returns need no further checking.
11
+ * - `exact: false` — some clause couldn't be expressed (a JS-only operator
12
+ * such as `like`, or a path `castWhere` marked as running through an
13
+ * array). That clause is left out, so callers must re-check every
14
+ * returned row with `matchesWhere`. Everything that *could* be expressed
15
+ * still narrows the read server-side.
16
+ *
17
+ * `never: true` means the predicate is structurally always-false (e.g. an
18
+ * empty `id: { in: [] }`). Callers should short-circuit and return zero rows
19
+ * without ever hitting DynamoDB. Payload uses empty `in` as a "match
20
+ * nothing" sentinel (typically from access control resolving to no IDs);
21
+ * surfacing it as a filter would mean throwing or constructing an invalid
22
+ * `IN ()` clause.
16
23
  *
17
24
  * Throws on any operator outside `operators.ts`'s supported set, identical
18
25
  * to `matchesWhere` — coverage is enforced from one source so the in-JS
@@ -27,9 +34,12 @@ import type { Where } from 'payload';
27
34
  * keeps the expression readable.
28
35
  * - Dotted paths in `where` keys are split on `.` so DynamoDB document-path
29
36
  * syntax (`#a.#b`) is produced for nested fields.
30
- * - `in: []` propagates a NEVER sentinel through the AND/OR algebra:
31
- * AND with NEVER → NEVER (whole result `null`); OR with NEVER drops the
32
- * NEVER branch; an OR whose every branch is NEVER itself becomes NEVER.
37
+ * - A clause compiles to an expression string, to `''` (always true — no
38
+ * constraint), or to the NEVER sentinel (always false). AND drops `''`
39
+ * and collapses to NEVER if any part is NEVER; OR drops NEVER branches
40
+ * and collapses to `''` if any branch is `''`. An inexpressible clause
41
+ * compiles to `''` and clears `exact`, which keeps the result a superset
42
+ * under both AND and OR.
33
43
  * - `not_in: []` is treated as no constraint (the complement of an empty
34
44
  * set is everything, so omitting it from the expression matches semantics).
35
45
  */
@@ -38,5 +48,10 @@ export type FilterExpression = {
38
48
  names: Record<string, string>;
39
49
  values: Record<string, unknown>;
40
50
  };
41
- export declare function buildFilterExpression(where: undefined | Where): FilterExpression | null | undefined;
51
+ export type CompiledFilter = {
52
+ exact: boolean;
53
+ filter?: FilterExpression;
54
+ never: boolean;
55
+ };
56
+ export declare function buildFilterExpression(where: undefined | Where): CompiledFilter;
42
57
  //# sourceMappingURL=buildFilterExpression.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildFilterExpression.d.ts","sourceRoot":"","sources":["../../src/utilities/buildFilterExpression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAKpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,CAAA;AAaD,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,GAAG,KAAK,GACvB,gBAAgB,GAAG,IAAI,GAAG,SAAS,CAoBrC"}
1
+ {"version":3,"file":"buildFilterExpression.d.ts","sourceRoot":"","sources":["../../src/utilities/buildFilterExpression.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAUpC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,gBAAgB,CAAA;IACzB,KAAK,EAAE,OAAO,CAAA;CACf,CAAA;AAcD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,GAAG,cAAc,CAyB9E"}
@@ -1,9 +1,13 @@
1
1
  import { normalizeForDynamo } from './normalizeForDynamo.js';
2
- import { SUPPORTED_OPERATORS, unsupportedOperatorError } from './operators.js';
2
+ import { JS_ONLY_MARKER, JS_ONLY_OPERATORS, SUPPORTED_OPERATORS, unsupportedOperatorError } from './operators.js';
3
3
  const NEVER = Symbol('payload-ddb-filter-never');
4
4
  export function buildFilterExpression(where) {
5
- if (!where) return undefined;
5
+ if (!where) return {
6
+ exact: true,
7
+ never: false
8
+ };
6
9
  const ctx = {
10
+ exact: true,
7
11
  nameByPlaceholder: {},
8
12
  nameCounter: 0,
9
13
  placeholderBySegment: new Map(),
@@ -11,14 +15,38 @@ export function buildFilterExpression(where) {
11
15
  values: {}
12
16
  };
13
17
  const expression = compile(where, ctx);
14
- if (expression === NEVER) return null;
15
- if (!expression) return undefined;
18
+ if (expression === NEVER) return {
19
+ exact: true,
20
+ never: true
21
+ };
22
+ if (!expression) return {
23
+ exact: ctx.exact,
24
+ never: false
25
+ };
16
26
  return {
17
- expression,
18
- names: ctx.nameByPlaceholder,
19
- values: ctx.values
27
+ exact: ctx.exact,
28
+ filter: {
29
+ expression,
30
+ names: onlyReferenced(ctx.nameByPlaceholder, expression, /#n\d+/g),
31
+ values: onlyReferenced(ctx.values, expression, /:v\d+/g)
32
+ },
33
+ never: false
20
34
  };
21
35
  }
36
+ /**
37
+ * DynamoDB rejects a request that declares an expression name or value the
38
+ * expression never uses. Placeholders get registered as clauses compile, but
39
+ * a clause can still end up contributing nothing (a skipped JS-only
40
+ * operator, `not_in: []`, a branch dropped from an OR), so keep only what
41
+ * the final expression references.
42
+ */ function onlyReferenced(registered, expression, pattern) {
43
+ const used = new Set(expression.match(pattern) ?? []);
44
+ const out = {};
45
+ for (const [placeholder, value] of Object.entries(registered)){
46
+ if (used.has(placeholder)) out[placeholder] = value;
47
+ }
48
+ return out;
49
+ }
22
50
  function compile(where, ctx) {
23
51
  const parts = [];
24
52
  for (const [key, raw] of Object.entries(where)){
@@ -35,12 +63,20 @@ function compile(where, ctx) {
35
63
  continue;
36
64
  }
37
65
  if (!raw || typeof raw !== 'object') continue;
38
- const path = pathRef(ctx, key);
39
66
  const operators = raw;
67
+ // `castWhere` marks clauses whose path can't be a DynamoDB document path
68
+ // (it runs through an array). Leave the whole clause to `matchesWhere`.
69
+ const pathIsJsOnly = JS_ONLY_MARKER in operators;
70
+ const path = pathIsJsOnly ? '' : pathRef(ctx, key);
40
71
  for (const [operator, expected] of Object.entries(operators)){
72
+ if (operator === JS_ONLY_MARKER) continue;
41
73
  if (!SUPPORTED_OPERATORS.has(operator)) {
42
74
  throw unsupportedOperatorError(operator, key);
43
75
  }
76
+ if (pathIsJsOnly || JS_ONLY_OPERATORS.has(operator)) {
77
+ ctx.exact = false;
78
+ continue;
79
+ }
44
80
  const expr = operatorToExpression(operator, path, expected, ctx);
45
81
  if (expr === NEVER) return NEVER;
46
82
  if (expr) parts.push(expr);
@@ -64,6 +100,9 @@ function compileGroup(raw, ctx, joiner) {
64
100
  neverCount++;
65
101
  continue;
66
102
  }
103
+ // An unconstrained branch makes the whole OR unconstrained. (Dropping it
104
+ // and keeping the others would narrow the result.)
105
+ if (!expr && joiner === 'OR') return '';
67
106
  if (expr) subs.push(expr);
68
107
  }
69
108
  // OR over branches that all collapsed to NEVER is NEVER itself.
@@ -109,12 +148,14 @@ function operatorToExpression(operator, path, expected, ctx) {
109
148
  case 'less_than_equal':
110
149
  return `${path} <= ${valueRef(ctx, expected)}`;
111
150
  case 'exists':
112
- return expected ? `attribute_exists(${path})` : `attribute_not_exists(${path})`;
113
- case 'like':
114
- // JS-only: `whereHasJsOnlyOperator` should have steered the helpers
115
- // away from compiling this `where` server-side. Returning '' is a
116
- // belt-and-braces no-op in case a caller compiles directly.
117
- return '';
151
+ {
152
+ // "Exists" means "holds a non-null value", same as `matchesWhere`.
153
+ // Payload writes `null` when a field is cleared and DynamoDB stores
154
+ // that as a real attribute of type NULL, so `attribute_exists` alone
155
+ // would report a cleared field as present.
156
+ const nullType = valueRef(ctx, 'NULL');
157
+ return expected ? `(attribute_exists(${path}) AND NOT attribute_type(${path}, ${nullType}))` : `(attribute_not_exists(${path}) OR attribute_type(${path}, ${nullType}))`;
158
+ }
118
159
  case 'in':
119
160
  {
120
161
  if (!Array.isArray(expected)) return '';
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utilities/buildFilterExpression.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nimport { normalizeForDynamo } from './normalizeForDynamo.js'\nimport { SUPPORTED_OPERATORS, unsupportedOperatorError } from './operators.js'\n\n/**\n * Compile a Payload `Where` into a DynamoDB `FilterExpression` plus its\n * companion `ExpressionAttributeNames` / `ExpressionAttributeValues` maps.\n *\n * Return contract:\n * - `undefined` — no filter to apply (empty/missing `where`, or only no-op\n * clauses like `and: []`). Callers should query without a filter.\n * - `null` — the predicate is structurally always-false (e.g. an empty\n * `id: { in: [] }`). Callers should short-circuit and return zero rows\n * without ever hitting DynamoDB. Payload uses empty `in` as a \"match\n * nothing\" sentinel (typically from access control resolving to no IDs);\n * surfacing it as a filter would mean throwing or constructing an\n * invalid `IN ()` clause.\n * - `FilterExpression` — actual predicate to push down.\n *\n * Throws on any operator outside `operators.ts`'s supported set, identical\n * to `matchesWhere` — coverage is enforced from one source so the in-JS\n * path and the pushdown path can never disagree.\n *\n * Implementation notes:\n * - Every field path segment becomes an `#nN` placeholder, sidestepping\n * DynamoDB's reserved-word list entirely. Segments are deduped per-call so\n * repeated fields produce one `#name` entry rather than many.\n * - Every value becomes a `:vN` placeholder. We don't dedupe values because\n * array members and repeated literals are cheap and the placeholder count\n * keeps the expression readable.\n * - Dotted paths in `where` keys are split on `.` so DynamoDB document-path\n * syntax (`#a.#b`) is produced for nested fields.\n * - `in: []` propagates a NEVER sentinel through the AND/OR algebra:\n * AND with NEVER → NEVER (whole result `null`); OR with NEVER drops the\n * NEVER branch; an OR whose every branch is NEVER itself becomes NEVER.\n * - `not_in: []` is treated as no constraint (the complement of an empty\n * set is everything, so omitting it from the expression matches semantics).\n */\nexport type FilterExpression = {\n expression: string\n names: Record<string, string>\n values: Record<string, unknown>\n}\n\nconst NEVER = Symbol('payload-ddb-filter-never')\ntype Compiled = string | typeof NEVER\n\ntype Ctx = {\n nameByPlaceholder: Record<string, string>\n nameCounter: number\n placeholderBySegment: Map<string, string>\n valueCounter: number\n values: Record<string, unknown>\n}\n\nexport function buildFilterExpression(\n where: undefined | Where,\n): FilterExpression | null | undefined {\n if (!where) return undefined\n\n const ctx: Ctx = {\n nameByPlaceholder: {},\n nameCounter: 0,\n placeholderBySegment: new Map(),\n valueCounter: 0,\n values: {},\n }\n\n const expression = compile(where, ctx)\n if (expression === NEVER) return null\n if (!expression) return undefined\n\n return {\n expression,\n names: ctx.nameByPlaceholder,\n values: ctx.values,\n }\n}\n\nfunction compile(where: Where, ctx: Ctx): Compiled {\n const parts: string[] = []\n\n for (const [key, raw] of Object.entries(where)) {\n if (key === 'and') {\n const inner = compileGroup(raw, ctx, 'AND')\n if (inner === NEVER) return NEVER\n if (inner) parts.push(inner)\n continue\n }\n if (key === 'or') {\n const inner = compileGroup(raw, ctx, 'OR')\n if (inner === NEVER) return NEVER\n if (inner) parts.push(inner)\n continue\n }\n\n if (!raw || typeof raw !== 'object') continue\n\n const path = pathRef(ctx, 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 const expr = operatorToExpression(operator, path, expected, ctx)\n if (expr === NEVER) return NEVER\n if (expr) parts.push(expr)\n }\n }\n\n if (parts.length === 0) return ''\n if (parts.length === 1) return parts[0]!\n return parts.join(' AND ')\n}\n\nfunction compileGroup(raw: unknown, ctx: Ctx, joiner: 'AND' | 'OR'): Compiled {\n if (!Array.isArray(raw)) return ''\n const subs: string[] = []\n let neverCount = 0\n let consideredCount = 0\n for (const sub of raw) {\n if (!sub || typeof sub !== 'object') continue\n consideredCount++\n const expr = compile(sub as Where, ctx)\n if (expr === NEVER) {\n if (joiner === 'AND') return NEVER\n neverCount++\n continue\n }\n if (expr) subs.push(expr)\n }\n // OR over branches that all collapsed to NEVER is NEVER itself.\n if (joiner === 'OR' && consideredCount > 0 && neverCount === consideredCount) {\n return NEVER\n }\n if (subs.length === 0) return ''\n if (subs.length === 1) return subs[0]!\n return `(${subs.join(` ${joiner} `)})`\n}\n\nfunction pathRef(ctx: Ctx, path: string): string {\n return path\n .split('.')\n .map((segment) => segmentRef(ctx, segment))\n .join('.')\n}\n\nfunction segmentRef(ctx: Ctx, segment: string): string {\n const cached = ctx.placeholderBySegment.get(segment)\n if (cached) return cached\n const placeholder = `#n${ctx.nameCounter++}`\n ctx.nameByPlaceholder[placeholder] = segment\n ctx.placeholderBySegment.set(segment, placeholder)\n return placeholder\n}\n\nfunction valueRef(ctx: Ctx, value: unknown): string {\n const placeholder = `:v${ctx.valueCounter++}`\n // Normalize so Date literals in `where` compare against the ISO strings we\n // actually stored. Without this, `{ updatedAt: { greater_than: new Date() } }`\n // would push an empty map literal into the filter and DynamoDB would reject\n // the comparison.\n ctx.values[placeholder] = normalizeForDynamo(value)\n return placeholder\n}\n\nfunction operatorToExpression(\n operator: string,\n path: string,\n expected: unknown,\n ctx: Ctx,\n): Compiled {\n switch (operator) {\n case 'equals':\n return `${path} = ${valueRef(ctx, expected)}`\n case 'not_equals':\n return `${path} <> ${valueRef(ctx, expected)}`\n case 'greater_than':\n return `${path} > ${valueRef(ctx, expected)}`\n case 'greater_than_equal':\n return `${path} >= ${valueRef(ctx, expected)}`\n case 'less_than':\n return `${path} < ${valueRef(ctx, expected)}`\n case 'less_than_equal':\n return `${path} <= ${valueRef(ctx, expected)}`\n case 'exists':\n return expected ? `attribute_exists(${path})` : `attribute_not_exists(${path})`\n case 'like':\n // JS-only: `whereHasJsOnlyOperator` should have steered the helpers\n // away from compiling this `where` server-side. Returning '' is a\n // belt-and-braces no-op in case a caller compiles directly.\n return ''\n case 'in': {\n if (!Array.isArray(expected)) return ''\n if (expected.length === 0) return NEVER\n const refs = expected.map((v) => valueRef(ctx, v))\n return `${path} IN (${refs.join(', ')})`\n }\n case 'not_in': {\n if (!Array.isArray(expected) || expected.length === 0) return ''\n const refs = expected.map((v) => valueRef(ctx, v))\n return `NOT (${path} IN (${refs.join(', ')}))`\n }\n default:\n // Unreachable — gated by SUPPORTED_OPERATORS check above. Belt-and-braces.\n throw unsupportedOperatorError(operator, path)\n }\n}\n"],"names":["normalizeForDynamo","SUPPORTED_OPERATORS","unsupportedOperatorError","NEVER","Symbol","buildFilterExpression","where","undefined","ctx","nameByPlaceholder","nameCounter","placeholderBySegment","Map","valueCounter","values","expression","compile","names","parts","key","raw","Object","entries","inner","compileGroup","push","path","pathRef","operators","operator","expected","has","expr","operatorToExpression","length","join","joiner","Array","isArray","subs","neverCount","consideredCount","sub","split","map","segment","segmentRef","cached","get","placeholder","set","valueRef","value","refs","v"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utilities/buildFilterExpression.ts"],"sourcesContent":["import type { Where } from 'payload'\n\nimport { normalizeForDynamo } from './normalizeForDynamo.js'\nimport {\n JS_ONLY_MARKER,\n JS_ONLY_OPERATORS,\n SUPPORTED_OPERATORS,\n unsupportedOperatorError,\n} from './operators.js'\n\n/**\n * Compile a Payload `Where` into a DynamoDB `FilterExpression` plus its\n * companion `ExpressionAttributeNames` / `ExpressionAttributeValues` maps.\n *\n * The result is a *prefilter*: it never rejects a row the `where` accepts,\n * but it may let through rows the `where` rejects. `exact` says which:\n *\n * - `exact: true` — the filter (or its absence) is the whole `where`; rows\n * DynamoDB returns need no further checking.\n * - `exact: false` — some clause couldn't be expressed (a JS-only operator\n * such as `like`, or a path `castWhere` marked as running through an\n * array). That clause is left out, so callers must re-check every\n * returned row with `matchesWhere`. Everything that *could* be expressed\n * still narrows the read server-side.\n *\n * `never: true` means the predicate is structurally always-false (e.g. an\n * empty `id: { in: [] }`). Callers should short-circuit and return zero rows\n * without ever hitting DynamoDB. Payload uses empty `in` as a \"match\n * nothing\" sentinel (typically from access control resolving to no IDs);\n * surfacing it as a filter would mean throwing or constructing an invalid\n * `IN ()` clause.\n *\n * Throws on any operator outside `operators.ts`'s supported set, identical\n * to `matchesWhere` — coverage is enforced from one source so the in-JS\n * path and the pushdown path can never disagree.\n *\n * Implementation notes:\n * - Every field path segment becomes an `#nN` placeholder, sidestepping\n * DynamoDB's reserved-word list entirely. Segments are deduped per-call so\n * repeated fields produce one `#name` entry rather than many.\n * - Every value becomes a `:vN` placeholder. We don't dedupe values because\n * array members and repeated literals are cheap and the placeholder count\n * keeps the expression readable.\n * - Dotted paths in `where` keys are split on `.` so DynamoDB document-path\n * syntax (`#a.#b`) is produced for nested fields.\n * - A clause compiles to an expression string, to `''` (always true — no\n * constraint), or to the NEVER sentinel (always false). AND drops `''`\n * and collapses to NEVER if any part is NEVER; OR drops NEVER branches\n * and collapses to `''` if any branch is `''`. An inexpressible clause\n * compiles to `''` and clears `exact`, which keeps the result a superset\n * under both AND and OR.\n * - `not_in: []` is treated as no constraint (the complement of an empty\n * set is everything, so omitting it from the expression matches semantics).\n */\nexport type FilterExpression = {\n expression: string\n names: Record<string, string>\n values: Record<string, unknown>\n}\n\nexport type CompiledFilter = {\n exact: boolean\n filter?: FilterExpression\n never: boolean\n}\n\nconst NEVER = Symbol('payload-ddb-filter-never')\ntype Compiled = string | typeof NEVER\n\ntype Ctx = {\n exact: boolean\n nameByPlaceholder: Record<string, string>\n nameCounter: number\n placeholderBySegment: Map<string, string>\n valueCounter: number\n values: Record<string, unknown>\n}\n\nexport function buildFilterExpression(where: undefined | Where): CompiledFilter {\n if (!where) return { exact: true, never: false }\n\n const ctx: Ctx = {\n exact: true,\n nameByPlaceholder: {},\n nameCounter: 0,\n placeholderBySegment: new Map(),\n valueCounter: 0,\n values: {},\n }\n\n const expression = compile(where, ctx)\n if (expression === NEVER) return { exact: true, never: true }\n if (!expression) return { exact: ctx.exact, never: false }\n\n return {\n exact: ctx.exact,\n filter: {\n expression,\n names: onlyReferenced(ctx.nameByPlaceholder, expression, /#n\\d+/g),\n values: onlyReferenced(ctx.values, expression, /:v\\d+/g),\n },\n never: false,\n }\n}\n\n/**\n * DynamoDB rejects a request that declares an expression name or value the\n * expression never uses. Placeholders get registered as clauses compile, but\n * a clause can still end up contributing nothing (a skipped JS-only\n * operator, `not_in: []`, a branch dropped from an OR), so keep only what\n * the final expression references.\n */\nfunction onlyReferenced<T>(\n registered: Record<string, T>,\n expression: string,\n pattern: RegExp,\n): Record<string, T> {\n const used = new Set(expression.match(pattern) ?? [])\n const out: Record<string, T> = {}\n for (const [placeholder, value] of Object.entries(registered)) {\n if (used.has(placeholder)) out[placeholder] = value\n }\n return out\n}\n\nfunction compile(where: Where, ctx: Ctx): Compiled {\n const parts: string[] = []\n\n for (const [key, raw] of Object.entries(where)) {\n if (key === 'and') {\n const inner = compileGroup(raw, ctx, 'AND')\n if (inner === NEVER) return NEVER\n if (inner) parts.push(inner)\n continue\n }\n if (key === 'or') {\n const inner = compileGroup(raw, ctx, 'OR')\n if (inner === NEVER) return NEVER\n if (inner) parts.push(inner)\n continue\n }\n\n if (!raw || typeof raw !== 'object') continue\n\n const operators = raw as Record<string, unknown>\n // `castWhere` marks clauses whose path can't be a DynamoDB document path\n // (it runs through an array). Leave the whole clause to `matchesWhere`.\n const pathIsJsOnly = JS_ONLY_MARKER in operators\n const path = pathIsJsOnly ? '' : pathRef(ctx, key)\n\n for (const [operator, expected] of Object.entries(operators)) {\n if (operator === JS_ONLY_MARKER) continue\n if (!SUPPORTED_OPERATORS.has(operator)) {\n throw unsupportedOperatorError(operator, key)\n }\n if (pathIsJsOnly || JS_ONLY_OPERATORS.has(operator)) {\n ctx.exact = false\n continue\n }\n const expr = operatorToExpression(operator, path, expected, ctx)\n if (expr === NEVER) return NEVER\n if (expr) parts.push(expr)\n }\n }\n\n if (parts.length === 0) return ''\n if (parts.length === 1) return parts[0]!\n return parts.join(' AND ')\n}\n\nfunction compileGroup(raw: unknown, ctx: Ctx, joiner: 'AND' | 'OR'): Compiled {\n if (!Array.isArray(raw)) return ''\n const subs: string[] = []\n let neverCount = 0\n let consideredCount = 0\n for (const sub of raw) {\n if (!sub || typeof sub !== 'object') continue\n consideredCount++\n const expr = compile(sub as Where, ctx)\n if (expr === NEVER) {\n if (joiner === 'AND') return NEVER\n neverCount++\n continue\n }\n // An unconstrained branch makes the whole OR unconstrained. (Dropping it\n // and keeping the others would narrow the result.)\n if (!expr && joiner === 'OR') return ''\n if (expr) subs.push(expr)\n }\n // OR over branches that all collapsed to NEVER is NEVER itself.\n if (joiner === 'OR' && consideredCount > 0 && neverCount === consideredCount) {\n return NEVER\n }\n if (subs.length === 0) return ''\n if (subs.length === 1) return subs[0]!\n return `(${subs.join(` ${joiner} `)})`\n}\n\nfunction pathRef(ctx: Ctx, path: string): string {\n return path\n .split('.')\n .map((segment) => segmentRef(ctx, segment))\n .join('.')\n}\n\nfunction segmentRef(ctx: Ctx, segment: string): string {\n const cached = ctx.placeholderBySegment.get(segment)\n if (cached) return cached\n const placeholder = `#n${ctx.nameCounter++}`\n ctx.nameByPlaceholder[placeholder] = segment\n ctx.placeholderBySegment.set(segment, placeholder)\n return placeholder\n}\n\nfunction valueRef(ctx: Ctx, value: unknown): string {\n const placeholder = `:v${ctx.valueCounter++}`\n // Normalize so Date literals in `where` compare against the ISO strings we\n // actually stored. Without this, `{ updatedAt: { greater_than: new Date() } }`\n // would push an empty map literal into the filter and DynamoDB would reject\n // the comparison.\n ctx.values[placeholder] = normalizeForDynamo(value)\n return placeholder\n}\n\nfunction operatorToExpression(\n operator: string,\n path: string,\n expected: unknown,\n ctx: Ctx,\n): Compiled {\n switch (operator) {\n case 'equals':\n return `${path} = ${valueRef(ctx, expected)}`\n case 'not_equals':\n return `${path} <> ${valueRef(ctx, expected)}`\n case 'greater_than':\n return `${path} > ${valueRef(ctx, expected)}`\n case 'greater_than_equal':\n return `${path} >= ${valueRef(ctx, expected)}`\n case 'less_than':\n return `${path} < ${valueRef(ctx, expected)}`\n case 'less_than_equal':\n return `${path} <= ${valueRef(ctx, expected)}`\n case 'exists': {\n // \"Exists\" means \"holds a non-null value\", same as `matchesWhere`.\n // Payload writes `null` when a field is cleared and DynamoDB stores\n // that as a real attribute of type NULL, so `attribute_exists` alone\n // would report a cleared field as present.\n const nullType = valueRef(ctx, 'NULL')\n return expected\n ? `(attribute_exists(${path}) AND NOT attribute_type(${path}, ${nullType}))`\n : `(attribute_not_exists(${path}) OR attribute_type(${path}, ${nullType}))`\n }\n case 'in': {\n if (!Array.isArray(expected)) return ''\n if (expected.length === 0) return NEVER\n const refs = expected.map((v) => valueRef(ctx, v))\n return `${path} IN (${refs.join(', ')})`\n }\n case 'not_in': {\n if (!Array.isArray(expected) || expected.length === 0) return ''\n const refs = expected.map((v) => valueRef(ctx, v))\n return `NOT (${path} IN (${refs.join(', ')}))`\n }\n default:\n // Unreachable — gated by SUPPORTED_OPERATORS check above. Belt-and-braces.\n throw unsupportedOperatorError(operator, path)\n }\n}\n"],"names":["normalizeForDynamo","JS_ONLY_MARKER","JS_ONLY_OPERATORS","SUPPORTED_OPERATORS","unsupportedOperatorError","NEVER","Symbol","buildFilterExpression","where","exact","never","ctx","nameByPlaceholder","nameCounter","placeholderBySegment","Map","valueCounter","values","expression","compile","filter","names","onlyReferenced","registered","pattern","used","Set","match","out","placeholder","value","Object","entries","has","parts","key","raw","inner","compileGroup","push","operators","pathIsJsOnly","path","pathRef","operator","expected","expr","operatorToExpression","length","join","joiner","Array","isArray","subs","neverCount","consideredCount","sub","split","map","segment","segmentRef","cached","get","set","valueRef","nullType","refs","v"],"mappings":"AAEA,SAASA,kBAAkB,QAAQ,0BAAyB;AAC5D,SACEC,cAAc,EACdC,iBAAiB,EACjBC,mBAAmB,EACnBC,wBAAwB,QACnB,iBAAgB;AA0DvB,MAAMC,QAAQC,OAAO;AAYrB,OAAO,SAASC,sBAAsBC,KAAwB;IAC5D,IAAI,CAACA,OAAO,OAAO;QAAEC,OAAO;QAAMC,OAAO;IAAM;IAE/C,MAAMC,MAAW;QACfF,OAAO;QACPG,mBAAmB,CAAC;QACpBC,aAAa;QACbC,sBAAsB,IAAIC;QAC1BC,cAAc;QACdC,QAAQ,CAAC;IACX;IAEA,MAAMC,aAAaC,QAAQX,OAAOG;IAClC,IAAIO,eAAeb,OAAO,OAAO;QAAEI,OAAO;QAAMC,OAAO;IAAK;IAC5D,IAAI,CAACQ,YAAY,OAAO;QAAET,OAAOE,IAAIF,KAAK;QAAEC,OAAO;IAAM;IAEzD,OAAO;QACLD,OAAOE,IAAIF,KAAK;QAChBW,QAAQ;YACNF;YACAG,OAAOC,eAAeX,IAAIC,iBAAiB,EAAEM,YAAY;YACzDD,QAAQK,eAAeX,IAAIM,MAAM,EAAEC,YAAY;QACjD;QACAR,OAAO;IACT;AACF;AAEA;;;;;;CAMC,GACD,SAASY,eACPC,UAA6B,EAC7BL,UAAkB,EAClBM,OAAe;IAEf,MAAMC,OAAO,IAAIC,IAAIR,WAAWS,KAAK,CAACH,YAAY,EAAE;IACpD,MAAMI,MAAyB,CAAC;IAChC,KAAK,MAAM,CAACC,aAAaC,MAAM,IAAIC,OAAOC,OAAO,CAACT,YAAa;QAC7D,IAAIE,KAAKQ,GAAG,CAACJ,cAAcD,GAAG,CAACC,YAAY,GAAGC;IAChD;IACA,OAAOF;AACT;AAEA,SAAST,QAAQX,KAAY,EAAEG,GAAQ;IACrC,MAAMuB,QAAkB,EAAE;IAE1B,KAAK,MAAM,CAACC,KAAKC,IAAI,IAAIL,OAAOC,OAAO,CAACxB,OAAQ;QAC9C,IAAI2B,QAAQ,OAAO;YACjB,MAAME,QAAQC,aAAaF,KAAKzB,KAAK;YACrC,IAAI0B,UAAUhC,OAAO,OAAOA;YAC5B,IAAIgC,OAAOH,MAAMK,IAAI,CAACF;YACtB;QACF;QACA,IAAIF,QAAQ,MAAM;YAChB,MAAME,QAAQC,aAAaF,KAAKzB,KAAK;YACrC,IAAI0B,UAAUhC,OAAO,OAAOA;YAC5B,IAAIgC,OAAOH,MAAMK,IAAI,CAACF;YACtB;QACF;QAEA,IAAI,CAACD,OAAO,OAAOA,QAAQ,UAAU;QAErC,MAAMI,YAAYJ;QAClB,yEAAyE;QACzE,wEAAwE;QACxE,MAAMK,eAAexC,kBAAkBuC;QACvC,MAAME,OAAOD,eAAe,KAAKE,QAAQhC,KAAKwB;QAE9C,KAAK,MAAM,CAACS,UAAUC,SAAS,IAAId,OAAOC,OAAO,CAACQ,WAAY;YAC5D,IAAII,aAAa3C,gBAAgB;YACjC,IAAI,CAACE,oBAAoB8B,GAAG,CAACW,WAAW;gBACtC,MAAMxC,yBAAyBwC,UAAUT;YAC3C;YACA,IAAIM,gBAAgBvC,kBAAkB+B,GAAG,CAACW,WAAW;gBACnDjC,IAAIF,KAAK,GAAG;gBACZ;YACF;YACA,MAAMqC,OAAOC,qBAAqBH,UAAUF,MAAMG,UAAUlC;YAC5D,IAAImC,SAASzC,OAAO,OAAOA;YAC3B,IAAIyC,MAAMZ,MAAMK,IAAI,CAACO;QACvB;IACF;IAEA,IAAIZ,MAAMc,MAAM,KAAK,GAAG,OAAO;IAC/B,IAAId,MAAMc,MAAM,KAAK,GAAG,OAAOd,KAAK,CAAC,EAAE;IACvC,OAAOA,MAAMe,IAAI,CAAC;AACpB;AAEA,SAASX,aAAaF,GAAY,EAAEzB,GAAQ,EAAEuC,MAAoB;IAChE,IAAI,CAACC,MAAMC,OAAO,CAAChB,MAAM,OAAO;IAChC,MAAMiB,OAAiB,EAAE;IACzB,IAAIC,aAAa;IACjB,IAAIC,kBAAkB;IACtB,KAAK,MAAMC,OAAOpB,IAAK;QACrB,IAAI,CAACoB,OAAO,OAAOA,QAAQ,UAAU;QACrCD;QACA,MAAMT,OAAO3B,QAAQqC,KAAc7C;QACnC,IAAImC,SAASzC,OAAO;YAClB,IAAI6C,WAAW,OAAO,OAAO7C;YAC7BiD;YACA;QACF;QACA,yEAAyE;QACzE,mDAAmD;QACnD,IAAI,CAACR,QAAQI,WAAW,MAAM,OAAO;QACrC,IAAIJ,MAAMO,KAAKd,IAAI,CAACO;IACtB;IACA,gEAAgE;IAChE,IAAII,WAAW,QAAQK,kBAAkB,KAAKD,eAAeC,iBAAiB;QAC5E,OAAOlD;IACT;IACA,IAAIgD,KAAKL,MAAM,KAAK,GAAG,OAAO;IAC9B,IAAIK,KAAKL,MAAM,KAAK,GAAG,OAAOK,IAAI,CAAC,EAAE;IACrC,OAAO,CAAC,CAAC,EAAEA,KAAKJ,IAAI,CAAC,CAAC,CAAC,EAAEC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;AACxC;AAEA,SAASP,QAAQhC,GAAQ,EAAE+B,IAAY;IACrC,OAAOA,KACJe,KAAK,CAAC,KACNC,GAAG,CAAC,CAACC,UAAYC,WAAWjD,KAAKgD,UACjCV,IAAI,CAAC;AACV;AAEA,SAASW,WAAWjD,GAAQ,EAAEgD,OAAe;IAC3C,MAAME,SAASlD,IAAIG,oBAAoB,CAACgD,GAAG,CAACH;IAC5C,IAAIE,QAAQ,OAAOA;IACnB,MAAMhC,cAAc,CAAC,EAAE,EAAElB,IAAIE,WAAW,IAAI;IAC5CF,IAAIC,iBAAiB,CAACiB,YAAY,GAAG8B;IACrChD,IAAIG,oBAAoB,CAACiD,GAAG,CAACJ,SAAS9B;IACtC,OAAOA;AACT;AAEA,SAASmC,SAASrD,GAAQ,EAAEmB,KAAc;IACxC,MAAMD,cAAc,CAAC,EAAE,EAAElB,IAAIK,YAAY,IAAI;IAC7C,2EAA2E;IAC3E,+EAA+E;IAC/E,4EAA4E;IAC5E,kBAAkB;IAClBL,IAAIM,MAAM,CAACY,YAAY,GAAG7B,mBAAmB8B;IAC7C,OAAOD;AACT;AAEA,SAASkB,qBACPH,QAAgB,EAChBF,IAAY,EACZG,QAAiB,EACjBlC,GAAQ;IAER,OAAQiC;QACN,KAAK;YACH,OAAO,GAAGF,KAAK,GAAG,EAAEsB,SAASrD,KAAKkC,WAAW;QAC/C,KAAK;YACH,OAAO,GAAGH,KAAK,IAAI,EAAEsB,SAASrD,KAAKkC,WAAW;QAChD,KAAK;YACH,OAAO,GAAGH,KAAK,GAAG,EAAEsB,SAASrD,KAAKkC,WAAW;QAC/C,KAAK;YACH,OAAO,GAAGH,KAAK,IAAI,EAAEsB,SAASrD,KAAKkC,WAAW;QAChD,KAAK;YACH,OAAO,GAAGH,KAAK,GAAG,EAAEsB,SAASrD,KAAKkC,WAAW;QAC/C,KAAK;YACH,OAAO,GAAGH,KAAK,IAAI,EAAEsB,SAASrD,KAAKkC,WAAW;QAChD,KAAK;YAAU;gBACb,mEAAmE;gBACnE,oEAAoE;gBACpE,qEAAqE;gBACrE,2CAA2C;gBAC3C,MAAMoB,WAAWD,SAASrD,KAAK;gBAC/B,OAAOkC,WACH,CAAC,kBAAkB,EAAEH,KAAK,yBAAyB,EAAEA,KAAK,EAAE,EAAEuB,SAAS,EAAE,CAAC,GAC1E,CAAC,sBAAsB,EAAEvB,KAAK,oBAAoB,EAAEA,KAAK,EAAE,EAAEuB,SAAS,EAAE,CAAC;YAC/E;QACA,KAAK;YAAM;gBACT,IAAI,CAACd,MAAMC,OAAO,CAACP,WAAW,OAAO;gBACrC,IAAIA,SAASG,MAAM,KAAK,GAAG,OAAO3C;gBAClC,MAAM6D,OAAOrB,SAASa,GAAG,CAAC,CAACS,IAAMH,SAASrD,KAAKwD;gBAC/C,OAAO,GAAGzB,KAAK,KAAK,EAAEwB,KAAKjB,IAAI,CAAC,MAAM,CAAC,CAAC;YAC1C;QACA,KAAK;YAAU;gBACb,IAAI,CAACE,MAAMC,OAAO,CAACP,aAAaA,SAASG,MAAM,KAAK,GAAG,OAAO;gBAC9D,MAAMkB,OAAOrB,SAASa,GAAG,CAAC,CAACS,IAAMH,SAASrD,KAAKwD;gBAC/C,OAAO,CAAC,KAAK,EAAEzB,KAAK,KAAK,EAAEwB,KAAKjB,IAAI,CAAC,MAAM,EAAE,CAAC;YAChD;QACA;YACE,2EAA2E;YAC3E,MAAM7C,yBAAyBwC,UAAUF;IAC7C;AACF"}
@@ -0,0 +1,84 @@
1
+ import type { FlattenedField, Where } from 'payload';
2
+ /**
3
+ * Rewrite a Payload `Where` into the terms the stored rows are in, before
4
+ * either evaluator (`buildFilterExpression` or `matchesWhere`) sees it. Three
5
+ * things happen per clause, all driven by the field the path points at:
6
+ *
7
+ * **Operands are cast** to the type the field stores. They routinely arrive
8
+ * as strings: the REST API parses `?where[price][greater_than]=10` with
9
+ * `qs`, which yields `"10"`, and the admin list filters go the same way.
10
+ * Mongoose and SQL drivers quietly coerce those against their schema.
11
+ * DynamoDB has no schema and compares strictly by type — `"10"` never equals
12
+ * or orders against the number `10` — so without this such a filter matches
13
+ * nothing and reports no error. The rules follow Payload's Mongo and
14
+ * Postgres adapters (`sanitizeQueryValue`), minus their database quirks:
15
+ *
16
+ * - `checkbox`: `'true'` / `'false'` (any case) → boolean.
17
+ * - `number`: numeric strings → number.
18
+ * - `date`: parseable strings, epoch-ms numbers and `Date`s → the full ISO
19
+ * string we store. `'2026-01-01'` must become `'2026-01-01T00:00:00.000Z'`
20
+ * or it would compare as a mere prefix of the stored value.
21
+ * - `relationship` / `upload`: `'null'` → `null`; ids → number when the
22
+ * related collection has numeric ids, including inside a polymorphic
23
+ * `{ relationTo, value }` operand.
24
+ * - `id`: → number when this collection has numeric ids.
25
+ * - `in` / `not_in` / `all`: a comma-delimited string → array, each member
26
+ * cast.
27
+ * - `exists`: operand → boolean (`'false'` is a truthy string otherwise),
28
+ * and for fields that can hold text, an empty string counts as absent —
29
+ * that's what the admin UI leaves behind when a text input is cleared.
30
+ * - `contains` on a `hasMany` select or a relationship means "has this
31
+ * value", so it becomes `equals` / `in`.
32
+ *
33
+ * **Localized paths get their locale key.** A localized field is stored as
34
+ * `{ en: …, es: … }`, so `title` becomes `title.en` for the request's locale
35
+ * (unless the caller already wrote a locale into the path).
36
+ *
37
+ * **Paths DynamoDB can't address are marked** with `JS_ONLY_MARKER`: any that
38
+ * run through an `array` / `blocks` field or a `hasMany` value, or into a
39
+ * field whose inner shape the schema doesn't describe (`json`, rich text).
40
+ * The pushdown skips those clauses; `matchesWhere` evaluates them with
41
+ * any-element semantics.
42
+ *
43
+ * Anything it can't confidently cast is left exactly as given: an unknown
44
+ * path, a path that continues past a leaf field, an unparseable value.
45
+ *
46
+ * Returns a new `Where`; the caller's object is never mutated.
47
+ */
48
+ export type IdType = 'number' | 'text';
49
+ export type Localization = {
50
+ /** Locale to address when a path doesn't name one. */
51
+ locale: string;
52
+ localeCodes: string[];
53
+ };
54
+ export type CastContext = {
55
+ /** Flattened fields of the entity the `where` targets. */
56
+ fields: FlattenedField[];
57
+ /** Type of the entity's own `id` when it isn't declared as a field. */
58
+ idType: IdType;
59
+ /** Id type of another collection, for relationship operands. */
60
+ idTypeOf: (collectionSlug: string) => IdType;
61
+ /** Set only when the Payload config has localization enabled. */
62
+ localization?: Localization;
63
+ };
64
+ export declare function castWhere(where: undefined | Where, ctx: CastContext): undefined | Where;
65
+ export type ResolvedPath = {
66
+ /** The field the path ends on, when the schema describes it. */
67
+ field: FlattenedField | null;
68
+ /** True when DynamoDB can't address the path as one value. */
69
+ multi: boolean;
70
+ /** The path as stored: locale keys inserted after localized segments. */
71
+ path: string;
72
+ };
73
+ /**
74
+ * Walk a dotted path through groups, named tabs, arrays and blocks to the
75
+ * field it names, noting on the way whether it crosses anything list-shaped
76
+ * and where locale keys belong.
77
+ *
78
+ * `field` is `null` when the path leaves the declared schema — an unknown
79
+ * name, or a path that continues past a field with no sub-fields (a
80
+ * polymorphic relationship's `.value`, a key inside `json`), where guessing
81
+ * a type would be wrong.
82
+ */
83
+ export declare function resolvePath(fields: FlattenedField[], path: string, localization?: Localization): ResolvedPath;
84
+ //# sourceMappingURL=castWhere.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"castWhere.d.ts","sourceRoot":"","sources":["../../src/utilities/castWhere.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAOpD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAEH,MAAM,MAAM,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAA;AAEtC,MAAM,MAAM,YAAY,GAAG;IACzB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,0DAA0D;IAC1D,MAAM,EAAE,cAAc,EAAE,CAAA;IACxB,uEAAuE;IACvE,MAAM,EAAE,MAAM,CAAA;IACd,gEAAgE;IAChE,QAAQ,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,MAAM,CAAA;IAC5C,iEAAiE;IACjE,YAAY,CAAC,EAAE,YAAY,CAAA;CAC5B,CAAA;AAaD,wBAAgB,SAAS,CAAC,KAAK,EAAE,SAAS,GAAG,KAAK,EAAE,GAAG,EAAE,WAAW,GAAG,SAAS,GAAG,KAAK,CAoFvF;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,gEAAgE;IAChE,KAAK,EAAE,cAAc,GAAG,IAAI,CAAA;IAC5B,8DAA8D;IAC9D,KAAK,EAAE,OAAO,CAAA;IACd,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,cAAc,EAAE,EACxB,IAAI,EAAE,MAAM,EACZ,YAAY,CAAC,EAAE,YAAY,GAC1B,YAAY,CAwDd"}