@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,88 @@
1
+ import { PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
2
+ import { pickConfiguredFields, pickConfiguredVersionRow } from './pickConfiguredFields.js';
3
+ import { ROW_RESERVED_KEYS } from './resolveSchema.js';
4
+ import { stripInternalKeys } from './stripInternalKeys.js';
5
+ export async function scrubUnknownFields(payload) {
6
+ const adapter = payload.db;
7
+ if (!adapter.docClient) {
8
+ throw new Error('payload-ddb: scrubUnknownFields requires a connected adapter.');
9
+ }
10
+ const report = {
11
+ collections: {},
12
+ collectionVersions: {},
13
+ globals: {},
14
+ globalVersions: {}
15
+ };
16
+ const collections = adapter.payload?.collections ?? {};
17
+ for (const [slug, collection] of Object.entries(collections)){
18
+ const fields = collection.config.fields;
19
+ report.collections[slug] = await scrubPartition(adapter, adapter.resolvePartition(slug), (row)=>pickConfiguredFields(row, fields, ROW_RESERVED_KEYS));
20
+ if (collection.config.versions) {
21
+ report.collectionVersions[slug] = await scrubPartition(adapter, adapter.resolveVersionsPartition(slug), (row)=>pickConfiguredVersionRow(row, fields));
22
+ }
23
+ }
24
+ const globals = adapter.payload?.config?.globals ?? [];
25
+ for (const global of globals){
26
+ const fields = global.fields;
27
+ report.globals[global.slug] = await scrubPartition(adapter, adapter.resolvePartition(global.slug), (row)=>pickConfiguredFields(row, fields, ROW_RESERVED_KEYS));
28
+ if (global.versions) {
29
+ report.globalVersions[global.slug] = await scrubPartition(adapter, adapter.resolveVersionsPartition(global.slug), (row)=>pickConfiguredVersionRow(row, fields));
30
+ }
31
+ }
32
+ return report;
33
+ }
34
+ async function scrubPartition(adapter, partition, project) {
35
+ let scanned = 0;
36
+ let modified = 0;
37
+ let exclusiveStartKey;
38
+ do {
39
+ const result = await adapter.docClient.send(new QueryCommand({
40
+ TableName: adapter.tableName,
41
+ KeyConditionExpression: 'pk = :pk',
42
+ ExpressionAttributeValues: {
43
+ ':pk': partition
44
+ },
45
+ ExclusiveStartKey: exclusiveStartKey
46
+ }));
47
+ for (const item of result.Items ?? []){
48
+ scanned++;
49
+ const sansInternal = stripInternalKeys(item);
50
+ const projected = project(sansInternal);
51
+ if (stableStringify(sansInternal) !== stableStringify(projected)) {
52
+ // Re-attach pk/sk explicitly. They aren't part of the projection's
53
+ // domain (those keys are adapter-internal partition coordinates),
54
+ // and their values must come from the original item to keep the
55
+ // row addressable at the same key.
56
+ await adapter.docClient.send(new PutCommand({
57
+ TableName: adapter.tableName,
58
+ Item: {
59
+ ...projected,
60
+ pk: item['pk'],
61
+ sk: item['sk']
62
+ }
63
+ }));
64
+ modified++;
65
+ }
66
+ }
67
+ exclusiveStartKey = result.LastEvaluatedKey;
68
+ }while (exclusiveStartKey)
69
+ return {
70
+ scanned,
71
+ modified
72
+ };
73
+ }
74
+ /**
75
+ * Sorted-key JSON serialization for stable deep equality. The projection
76
+ * drops keys at any depth (nested groups, array items, blocks), so a
77
+ * top-level key compare wouldn't detect leaks buried inside a `meta` or
78
+ * `tags` field. Sort keys before stringifying so insertion-order
79
+ * differences between the original row and the freshly-built projection
80
+ * don't register as a false-positive diff.
81
+ */ function stableStringify(value) {
82
+ if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null';
83
+ if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
84
+ const keys = Object.keys(value).sort();
85
+ return `{${keys.map((k)=>`${JSON.stringify(k)}:${stableStringify(value[k])}`).join(',')}}`;
86
+ }
87
+
88
+ //# sourceMappingURL=scrubUnknownFields.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/scrubUnknownFields.ts"],"sourcesContent":["import type { Payload, SanitizedGlobalConfig } from 'payload'\n\nimport { PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb'\n\nimport type { DynamoAdapter } from '../types.js'\n\nimport {\n pickConfiguredFields,\n pickConfiguredVersionRow,\n} from './pickConfiguredFields.js'\nimport { ROW_RESERVED_KEYS } from './resolveSchema.js'\nimport { stripInternalKeys } from './stripInternalKeys.js'\n\n/**\n * One-shot cleanup pass for rows that were written before write-time\n * projection landed. Walks every collection, global, and versions partition\n * the adapter knows about, applies the same projection that today's writes\n * use, and re-`Put`s rows that changed. Rows whose contents are already\n * clean stay untouched.\n *\n * Intended to be called once after upgrading; not on every boot. The\n * adapter's regular write paths now project on every mutation, so once a\n * row has been written under the new code it stays clean. This helper\n * exists for the static legacy data that hasn't been touched since the bug\n * was active.\n *\n * @example\n * import { getPayload } from 'payload'\n * import config from './payload.config'\n * import { scrubUnknownFields } from '@aih-software/payload-ddb'\n *\n * const payload = await getPayload({ config })\n * const report = await scrubUnknownFields(payload)\n * console.log(report)\n * await payload.destroy()\n */\nexport interface ScrubReport {\n collections: Record<string, { scanned: number; modified: number }>\n collectionVersions: Record<string, { scanned: number; modified: number }>\n globals: Record<string, { scanned: number; modified: number }>\n globalVersions: Record<string, { scanned: number; modified: number }>\n}\n\nexport async function scrubUnknownFields(payload: Payload): Promise<ScrubReport> {\n const adapter = payload.db as DynamoAdapter\n if (!adapter.docClient) {\n throw new Error('payload-ddb: scrubUnknownFields requires a connected adapter.')\n }\n\n const report: ScrubReport = {\n collections: {},\n collectionVersions: {},\n globals: {},\n globalVersions: {},\n }\n\n const collections = adapter.payload?.collections ?? {}\n for (const [slug, collection] of Object.entries(collections)) {\n const fields = collection.config.fields\n report.collections[slug] = await scrubPartition(\n adapter,\n adapter.resolvePartition(slug),\n (row) => pickConfiguredFields(row, fields, ROW_RESERVED_KEYS),\n )\n\n if (collection.config.versions) {\n report.collectionVersions[slug] = await scrubPartition(\n adapter,\n adapter.resolveVersionsPartition(slug),\n (row) => pickConfiguredVersionRow(row, fields),\n )\n }\n }\n\n const globals: SanitizedGlobalConfig[] = adapter.payload?.config?.globals ?? []\n for (const global of globals) {\n const fields = global.fields\n report.globals[global.slug] = await scrubPartition(\n adapter,\n adapter.resolvePartition(global.slug),\n (row) => pickConfiguredFields(row, fields, ROW_RESERVED_KEYS),\n )\n\n if (global.versions) {\n report.globalVersions[global.slug] = await scrubPartition(\n adapter,\n adapter.resolveVersionsPartition(global.slug),\n (row) => pickConfiguredVersionRow(row, fields),\n )\n }\n }\n\n return report\n}\n\nasync function scrubPartition(\n adapter: DynamoAdapter,\n partition: string,\n project: (row: Record<string, unknown>) => Record<string, unknown>,\n): Promise<{ scanned: number; modified: number }> {\n let scanned = 0\n let modified = 0\n let exclusiveStartKey: Record<string, unknown> | undefined\n\n do {\n const result = await adapter.docClient!.send(\n new QueryCommand({\n TableName: adapter.tableName,\n KeyConditionExpression: 'pk = :pk',\n ExpressionAttributeValues: { ':pk': partition },\n ExclusiveStartKey: exclusiveStartKey,\n }),\n )\n\n for (const item of result.Items ?? []) {\n scanned++\n const sansInternal = stripInternalKeys(item)\n const projected = project(sansInternal)\n if (stableStringify(sansInternal) !== stableStringify(projected)) {\n // Re-attach pk/sk explicitly. They aren't part of the projection's\n // domain (those keys are adapter-internal partition coordinates),\n // and their values must come from the original item to keep the\n // row addressable at the same key.\n await adapter.docClient!.send(\n new PutCommand({\n TableName: adapter.tableName,\n Item: { ...projected, pk: item['pk'], sk: item['sk'] },\n }),\n )\n modified++\n }\n }\n\n exclusiveStartKey = result.LastEvaluatedKey\n } while (exclusiveStartKey)\n\n return { scanned, modified }\n}\n\n/**\n * Sorted-key JSON serialization for stable deep equality. The projection\n * drops keys at any depth (nested groups, array items, blocks), so a\n * top-level key compare wouldn't detect leaks buried inside a `meta` or\n * `tags` field. Sort keys before stringifying so insertion-order\n * differences between the original row and the freshly-built projection\n * don't register as a false-positive diff.\n */\nfunction stableStringify(value: unknown): string {\n if (value === null || typeof value !== 'object') return JSON.stringify(value) ?? 'null'\n if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`\n const keys = Object.keys(value as Record<string, unknown>).sort()\n return `{${keys\n .map((k) => `${JSON.stringify(k)}:${stableStringify((value as Record<string, unknown>)[k])}`)\n .join(',')}}`\n}\n"],"names":["PutCommand","QueryCommand","pickConfiguredFields","pickConfiguredVersionRow","ROW_RESERVED_KEYS","stripInternalKeys","scrubUnknownFields","payload","adapter","db","docClient","Error","report","collections","collectionVersions","globals","globalVersions","slug","collection","Object","entries","fields","config","scrubPartition","resolvePartition","row","versions","resolveVersionsPartition","global","partition","project","scanned","modified","exclusiveStartKey","result","send","TableName","tableName","KeyConditionExpression","ExpressionAttributeValues","ExclusiveStartKey","item","Items","sansInternal","projected","stableStringify","Item","pk","sk","LastEvaluatedKey","value","JSON","stringify","Array","isArray","map","join","keys","sort","k"],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Drop the adapter's internal `pk`/`sk` attributes from a row before it
3
+ * crosses the boundary back into Payload land. Callers see clean documents
4
+ * keyed by the user-facing `id`; the composite-key plumbing stays internal.
5
+ */
6
+ export declare function stripInternalKeys(item: Record<string, unknown>): Record<string, unknown>;
7
+ //# sourceMappingURL=stripInternalKeys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stripInternalKeys.d.ts","sourceRoot":"","sources":["../../src/utilities/stripInternalKeys.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAGxF"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Drop the adapter's internal `pk`/`sk` attributes from a row before it
3
+ * crosses the boundary back into Payload land. Callers see clean documents
4
+ * keyed by the user-facing `id`; the composite-key plumbing stays internal.
5
+ */ export function stripInternalKeys(item) {
6
+ const { pk: _pk, sk: _sk, ...rest } = item;
7
+ return rest;
8
+ }
9
+
10
+ //# sourceMappingURL=stripInternalKeys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/stripInternalKeys.ts"],"sourcesContent":["/**\n * Drop the adapter's internal `pk`/`sk` attributes from a row before it\n * crosses the boundary back into Payload land. Callers see clean documents\n * keyed by the user-facing `id`; the composite-key plumbing stays internal.\n */\nexport function stripInternalKeys(item: Record<string, unknown>): Record<string, unknown> {\n const { pk: _pk, sk: _sk, ...rest } = item\n return rest\n}\n"],"names":["stripInternalKeys","item","pk","_pk","sk","_sk","rest"],"mappings":"AAAA;;;;CAIC,GACD,OAAO,SAASA,kBAAkBC,IAA6B;IAC7D,MAAM,EAAEC,IAAIC,GAAG,EAAEC,IAAIC,GAAG,EAAE,GAAGC,MAAM,GAAGL;IACtC,OAAOK;AACT"}
@@ -0,0 +1,11 @@
1
+ import type { Where } from 'payload';
2
+ /**
3
+ * Detect the "lookup by id equality" fast path. Returns the id when the
4
+ * `where` clause is *exactly* `{ id: { equals: <value> } }` or a single-clause
5
+ * `and` wrapping the same. Returns `null` for anything else — including
6
+ * additional sibling constraints, since DynamoDB `GetItem` cannot enforce them
7
+ * server-side. The caller should fall back to `Scan` + in-memory filtering in
8
+ * that case.
9
+ */
10
+ export declare function whereToId(where: Where | undefined): null | number | string;
11
+ //# sourceMappingURL=whereToId.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"whereToId.d.ts","sourceRoot":"","sources":["../../src/utilities/whereToId.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAA;AAEpC;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAkC1E"}
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Detect the "lookup by id equality" fast path. Returns the id when the
3
+ * `where` clause is *exactly* `{ id: { equals: <value> } }` or a single-clause
4
+ * `and` wrapping the same. Returns `null` for anything else — including
5
+ * additional sibling constraints, since DynamoDB `GetItem` cannot enforce them
6
+ * server-side. The caller should fall back to `Scan` + in-memory filtering in
7
+ * that case.
8
+ */ export function whereToId(where) {
9
+ if (!where) {
10
+ return null;
11
+ }
12
+ const keys = Object.keys(where);
13
+ if (keys.length === 1 && keys[0] === 'and') {
14
+ const conditions = where.and;
15
+ if (Array.isArray(conditions) && conditions.length === 1 && conditions[0]) {
16
+ return whereToId(conditions[0]);
17
+ }
18
+ return null;
19
+ }
20
+ if (keys.length !== 1 || keys[0] !== 'id') {
21
+ return null;
22
+ }
23
+ const clause = where['id'];
24
+ if (!clause || typeof clause !== 'object') {
25
+ return null;
26
+ }
27
+ const operatorKeys = Object.keys(clause);
28
+ if (operatorKeys.length !== 1 || operatorKeys[0] !== 'equals') {
29
+ return null;
30
+ }
31
+ const value = clause.equals;
32
+ if (typeof value === 'string' || typeof value === 'number') {
33
+ return value;
34
+ }
35
+ return null;
36
+ }
37
+
38
+ //# sourceMappingURL=whereToId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utilities/whereToId.ts"],"sourcesContent":["import type { Where } from 'payload'\n\n/**\n * Detect the \"lookup by id equality\" fast path. Returns the id when the\n * `where` clause is *exactly* `{ id: { equals: <value> } }` or a single-clause\n * `and` wrapping the same. Returns `null` for anything else — including\n * additional sibling constraints, since DynamoDB `GetItem` cannot enforce them\n * server-side. The caller should fall back to `Scan` + in-memory filtering in\n * that case.\n */\nexport function whereToId(where: Where | undefined): null | number | string {\n if (!where) {\n return null\n }\n\n const keys = Object.keys(where)\n\n if (keys.length === 1 && keys[0] === 'and') {\n const conditions = where.and\n if (Array.isArray(conditions) && conditions.length === 1 && conditions[0]) {\n return whereToId(conditions[0])\n }\n return null\n }\n\n if (keys.length !== 1 || keys[0] !== 'id') {\n return null\n }\n\n const clause = where['id']\n if (!clause || typeof clause !== 'object') {\n return null\n }\n\n const operatorKeys = Object.keys(clause)\n if (operatorKeys.length !== 1 || operatorKeys[0] !== 'equals') {\n return null\n }\n\n const value = (clause as { equals: unknown }).equals\n if (typeof value === 'string' || typeof value === 'number') {\n return value\n }\n return null\n}\n"],"names":["whereToId","where","keys","Object","length","conditions","and","Array","isArray","clause","operatorKeys","value","equals"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,SAASA,UAAUC,KAAwB;IAChD,IAAI,CAACA,OAAO;QACV,OAAO;IACT;IAEA,MAAMC,OAAOC,OAAOD,IAAI,CAACD;IAEzB,IAAIC,KAAKE,MAAM,KAAK,KAAKF,IAAI,CAAC,EAAE,KAAK,OAAO;QAC1C,MAAMG,aAAaJ,MAAMK,GAAG;QAC5B,IAAIC,MAAMC,OAAO,CAACH,eAAeA,WAAWD,MAAM,KAAK,KAAKC,UAAU,CAAC,EAAE,EAAE;YACzE,OAAOL,UAAUK,UAAU,CAAC,EAAE;QAChC;QACA,OAAO;IACT;IAEA,IAAIH,KAAKE,MAAM,KAAK,KAAKF,IAAI,CAAC,EAAE,KAAK,MAAM;QACzC,OAAO;IACT;IAEA,MAAMO,SAASR,KAAK,CAAC,KAAK;IAC1B,IAAI,CAACQ,UAAU,OAAOA,WAAW,UAAU;QACzC,OAAO;IACT;IAEA,MAAMC,eAAeP,OAAOD,IAAI,CAACO;IACjC,IAAIC,aAAaN,MAAM,KAAK,KAAKM,YAAY,CAAC,EAAE,KAAK,UAAU;QAC7D,OAAO;IACT;IAEA,MAAMC,QAAQ,AAACF,OAA+BG,MAAM;IACpD,IAAI,OAAOD,UAAU,YAAY,OAAOA,UAAU,UAAU;QAC1D,OAAOA;IACT;IACA,OAAO;AACT"}
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "@open-aih/payload-ddb",
3
+ "version": "3.1.0",
4
+ "description": "An unofficial database provider for PayloadCMS, utilizing DynamoDb",
5
+ "keywords": [
6
+ "payloadcms",
7
+ "ddb",
8
+ "dynamo",
9
+ "dynamodb",
10
+ "database",
11
+ "provider"
12
+ ],
13
+ "publishConfig": {
14
+ "registry": "https://registry.npmjs.org",
15
+ "access": "public"
16
+ },
17
+ "homepage": "https://github.com/AIH-Software/payload-ddb#readme",
18
+ "bugs": {
19
+ "url": "https://github.com/AIH-Software/payload-ddb/issues"
20
+ },
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/AIH-Software/payload-ddb.git"
24
+ },
25
+ "license": "MIT",
26
+ "author": "Dillon Byron",
27
+ "type": "module",
28
+ "main": "./dist/index.js",
29
+ "types": "./dist/index.d.ts",
30
+ "exports": {
31
+ ".": {
32
+ "import": "./dist/index.js",
33
+ "types": "./dist/index.d.ts"
34
+ }
35
+ },
36
+ "files": [
37
+ "dist"
38
+ ],
39
+ "peerDependencies": {
40
+ "payload": ">=3.0.0"
41
+ },
42
+ "dependencies": {
43
+ "@aws-sdk/client-dynamodb": "^3.1038.0",
44
+ "@aws-sdk/lib-dynamodb": "^3.1038.0"
45
+ },
46
+ "devDependencies": {
47
+ "@changesets/cli": "^2.27.0",
48
+ "@swc/cli": "^0.8.1",
49
+ "@swc/core": "^1.15.32",
50
+ "@types/node": "^25.6.0",
51
+ "payload": "^3.84.1",
52
+ "typescript": "^6.0.3",
53
+ "vitest": "^3.0.0"
54
+ },
55
+ "scripts": {
56
+ "build": "swc src -d dist --strip-leading-paths && tsc --emitDeclarationOnly",
57
+ "docker:start": "docker compose up -d dynamodb-local",
58
+ "docker:stop": "docker compose down",
59
+ "docker:logs": "docker compose logs -f dynamodb-local",
60
+ "test": "vitest run",
61
+ "test:watch": "vitest",
62
+ "typecheck:test": "tsc -p test/tsconfig.json",
63
+ "changeset": "changeset"
64
+ }
65
+ }