payload 3.79.0-internal.2874f3f → 3.79.0-internal.de5df42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config/orderable/index.d.ts +2 -2
- package/dist/config/orderable/index.d.ts.map +1 -1
- package/dist/config/orderable/index.js +34 -60
- package/dist/config/orderable/index.js.map +1 -1
- package/dist/index.bundled.d.ts +1 -0
- package/dist/uploads/checkFileAccess.d.ts +2 -1
- package/dist/uploads/checkFileAccess.d.ts.map +1 -1
- package/dist/uploads/checkFileAccess.js +8 -1
- package/dist/uploads/checkFileAccess.js.map +1 -1
- package/dist/uploads/checkFileAccess.spec.js +109 -0
- package/dist/uploads/checkFileAccess.spec.js.map +1 -0
- package/dist/uploads/endpoints/getFile.d.ts.map +1 -1
- package/dist/uploads/endpoints/getFile.js +4 -1
- package/dist/uploads/endpoints/getFile.js.map +1 -1
- package/dist/uploads/types.d.ts +1 -0
- package/dist/uploads/types.d.ts.map +1 -1
- package/dist/uploads/types.js.map +1 -1
- package/package.json +2 -2
- package/dist/config/orderable/utils/buildJoinScopeWhere.d.ts +0 -10
- package/dist/config/orderable/utils/buildJoinScopeWhere.d.ts.map +0 -1
- package/dist/config/orderable/utils/buildJoinScopeWhere.js +0 -43
- package/dist/config/orderable/utils/buildJoinScopeWhere.js.map +0 -1
- package/dist/config/orderable/utils/getJoinScopeContext.d.ts +0 -16
- package/dist/config/orderable/utils/getJoinScopeContext.d.ts.map +0 -1
- package/dist/config/orderable/utils/getJoinScopeContext.js +0 -41
- package/dist/config/orderable/utils/getJoinScopeContext.js.map +0 -1
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.d.ts +0 -12
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.d.ts.map +0 -1
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.js +0 -18
- package/dist/config/orderable/utils/getJoinScopeWhereFromDocData.js.map +0 -1
- package/dist/config/orderable/utils/getValueAtPath.d.ts +0 -5
- package/dist/config/orderable/utils/getValueAtPath.d.ts.map +0 -1
- package/dist/config/orderable/utils/getValueAtPath.js +0 -18
- package/dist/config/orderable/utils/getValueAtPath.js.map +0 -1
- package/dist/config/orderable/utils/resolvePendingTargetKey.d.ts +0 -13
- package/dist/config/orderable/utils/resolvePendingTargetKey.d.ts.map +0 -1
- package/dist/config/orderable/utils/resolvePendingTargetKey.js +0 -23
- package/dist/config/orderable/utils/resolvePendingTargetKey.js.map +0 -1
|
@@ -9,7 +9,7 @@ import type { SanitizedConfig } from '../types.js';
|
|
|
9
9
|
* Also, if collection.defaultSort or joinField.defaultSort is not set, it will be set to the orderable field.
|
|
10
10
|
*/
|
|
11
11
|
export declare const setupOrderable: (config: SanitizedConfig) => void;
|
|
12
|
-
export declare const addOrderableFieldsAndHook: (collection: CollectionConfig, orderableFieldNames: string[]
|
|
12
|
+
export declare const addOrderableFieldsAndHook: (collection: CollectionConfig, orderableFieldNames: string[]) => void;
|
|
13
13
|
/**
|
|
14
14
|
* The body of the reorder endpoint.
|
|
15
15
|
* @internal
|
|
@@ -24,5 +24,5 @@ export type OrderableEndpointBody = {
|
|
|
24
24
|
key: string;
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
|
-
export declare const addOrderableEndpoint: (config: SanitizedConfig
|
|
27
|
+
export declare const addOrderableEndpoint: (config: SanitizedConfig) => void;
|
|
28
28
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/orderable/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AAE3F,OAAO,KAAK,EAA4B,eAAe,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/config/orderable/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAoB,gBAAgB,EAAE,MAAM,mCAAmC,CAAA;AAE3F,OAAO,KAAK,EAA4B,eAAe,EAAE,MAAM,aAAa,CAAA;AAU5E;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,WAAY,eAAe,SAuDrD,CAAA;AAED,eAAO,MAAM,yBAAyB,eACxB,gBAAgB,uBACP,MAAM,EAAE,SAgE9B,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,YAAY,EAAE,SAAS,GAAG,MAAM,CAAA;IAChC,kBAAkB,EAAE,MAAM,CAAA;IAC1B,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAA;QACV,GAAG,EAAE,MAAM,CAAA;KACZ,CAAA;CACF,CAAA;AAED,eAAO,MAAM,oBAAoB,WAAY,eAAe,SAkL3D,CAAA"}
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
import { status as httpStatus } from 'http-status';
|
|
2
2
|
import { executeAccess } from '../../auth/executeAccess.js';
|
|
3
3
|
import { APIError } from '../../errors/index.js';
|
|
4
|
-
import { combineWhereConstraints } from '../../utilities/combineWhereConstraints.js';
|
|
5
4
|
import { commitTransaction } from '../../utilities/commitTransaction.js';
|
|
6
5
|
import { initTransaction } from '../../utilities/initTransaction.js';
|
|
7
6
|
import { killTransaction } from '../../utilities/killTransaction.js';
|
|
8
7
|
import { traverseFields } from '../../utilities/traverseFields.js';
|
|
9
8
|
import { generateKeyBetween, generateNKeysBetween } from './fractional-indexing.js';
|
|
10
|
-
import { getJoinScopeContext } from './utils/getJoinScopeContext.js';
|
|
11
|
-
import { getJoinScopeWhereFromDocData } from './utils/getJoinScopeWhereFromDocData.js';
|
|
12
|
-
import { resolvePendingTargetKey } from './utils/resolvePendingTargetKey.js';
|
|
13
9
|
/**
|
|
14
10
|
* This function creates:
|
|
15
11
|
* - N fields per collection, named `_order` or `_<collection>_<joinField>_order`
|
|
@@ -19,7 +15,6 @@ import { resolvePendingTargetKey } from './utils/resolvePendingTargetKey.js';
|
|
|
19
15
|
* Also, if collection.defaultSort or joinField.defaultSort is not set, it will be set to the orderable field.
|
|
20
16
|
*/ export const setupOrderable = (config)=>{
|
|
21
17
|
const fieldsToAdd = new Map();
|
|
22
|
-
const joinFieldPathsByCollection = new Map();
|
|
23
18
|
config.collections.forEach((collection)=>{
|
|
24
19
|
if (collection.orderable) {
|
|
25
20
|
const currentFields = fieldsToAdd.get(collection) || [];
|
|
@@ -52,27 +47,23 @@ import { resolvePendingTargetKey } from './utils/resolvePendingTargetKey.js';
|
|
|
52
47
|
const currentFields = fieldsToAdd.get(relationshipCollection) || [];
|
|
53
48
|
// @ts-expect-error ref is untyped
|
|
54
49
|
const prefix = parentRef?.prefix ? `${parentRef.prefix}_` : '';
|
|
55
|
-
const joinOrderableFieldName = `_${field.collection}_${prefix}${field.name}_order`;
|
|
56
50
|
fieldsToAdd.set(relationshipCollection, [
|
|
57
51
|
...currentFields,
|
|
58
|
-
|
|
52
|
+
`_${field.collection}_${prefix}${field.name}_order`
|
|
59
53
|
]);
|
|
60
|
-
const currentJoinFieldPaths = joinFieldPathsByCollection.get(relationshipCollection.slug) || new Map();
|
|
61
|
-
currentJoinFieldPaths.set(joinOrderableFieldName, field.on);
|
|
62
|
-
joinFieldPathsByCollection.set(relationshipCollection.slug, currentJoinFieldPaths);
|
|
63
54
|
}
|
|
64
55
|
},
|
|
65
56
|
fields: collection.fields
|
|
66
57
|
});
|
|
67
58
|
});
|
|
68
59
|
Array.from(fieldsToAdd.entries()).forEach(([collection, orderableFields])=>{
|
|
69
|
-
addOrderableFieldsAndHook(collection, orderableFields
|
|
60
|
+
addOrderableFieldsAndHook(collection, orderableFields);
|
|
70
61
|
});
|
|
71
62
|
if (fieldsToAdd.size > 0) {
|
|
72
|
-
addOrderableEndpoint(config
|
|
63
|
+
addOrderableEndpoint(config);
|
|
73
64
|
}
|
|
74
65
|
};
|
|
75
|
-
export const addOrderableFieldsAndHook = (collection, orderableFieldNames
|
|
66
|
+
export const addOrderableFieldsAndHook = (collection, orderableFieldNames)=>{
|
|
76
67
|
// 1. Add field
|
|
77
68
|
orderableFieldNames.forEach((orderableFieldName)=>{
|
|
78
69
|
const orderField = {
|
|
@@ -108,13 +99,6 @@ export const addOrderableFieldsAndHook = (collection, orderableFieldNames, joinF
|
|
|
108
99
|
const orderBeforeChangeHook = async ({ data, originalDoc, req })=>{
|
|
109
100
|
for (const orderableFieldName of orderableFieldNames){
|
|
110
101
|
if (!data[orderableFieldName] && !originalDoc?.[orderableFieldName]) {
|
|
111
|
-
const joinScopeWhere = getJoinScopeWhereFromDocData({
|
|
112
|
-
collectionSlug: collection.slug,
|
|
113
|
-
data,
|
|
114
|
-
joinFieldPathsByCollection,
|
|
115
|
-
orderableFieldName,
|
|
116
|
-
originalDoc
|
|
117
|
-
});
|
|
118
102
|
const lastDoc = await req.payload.find({
|
|
119
103
|
collection: collection.slug,
|
|
120
104
|
depth: 0,
|
|
@@ -125,14 +109,11 @@ export const addOrderableFieldsAndHook = (collection, orderableFieldNames, joinF
|
|
|
125
109
|
[orderableFieldName]: true
|
|
126
110
|
},
|
|
127
111
|
sort: `-${orderableFieldName}`,
|
|
128
|
-
where:
|
|
129
|
-
{
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
},
|
|
134
|
-
joinScopeWhere ?? undefined
|
|
135
|
-
])
|
|
112
|
+
where: {
|
|
113
|
+
[orderableFieldName]: {
|
|
114
|
+
exists: true
|
|
115
|
+
}
|
|
116
|
+
}
|
|
136
117
|
});
|
|
137
118
|
const lastOrderValue = lastDoc.docs[0]?.[orderableFieldName] || null;
|
|
138
119
|
data[orderableFieldName] = generateKeyBetween(lastOrderValue, null);
|
|
@@ -142,7 +123,7 @@ export const addOrderableFieldsAndHook = (collection, orderableFieldNames, joinF
|
|
|
142
123
|
};
|
|
143
124
|
collection.hooks.beforeChange.push(orderBeforeChangeHook);
|
|
144
125
|
};
|
|
145
|
-
export const addOrderableEndpoint = (config
|
|
126
|
+
export const addOrderableEndpoint = (config)=>{
|
|
146
127
|
// 3. Add endpoint
|
|
147
128
|
const reorderHandler = async (req)=>{
|
|
148
129
|
const body = await req.json?.();
|
|
@@ -188,13 +169,6 @@ export const addOrderableEndpoint = (config, joinFieldPathsByCollection)=>{
|
|
|
188
169
|
status: 400
|
|
189
170
|
});
|
|
190
171
|
}
|
|
191
|
-
const { joinScopeWhere, targetDoc } = await getJoinScopeContext({
|
|
192
|
-
collectionSlug: collection.slug,
|
|
193
|
-
joinFieldPathsByCollection,
|
|
194
|
-
orderableFieldName,
|
|
195
|
-
req,
|
|
196
|
-
target
|
|
197
|
-
});
|
|
198
172
|
// Prevent reordering if user doesn't have editing permissions
|
|
199
173
|
if (collection.access?.update) {
|
|
200
174
|
await executeAccess({
|
|
@@ -222,14 +196,11 @@ export const addOrderableEndpoint = (config, joinFieldPathsByCollection)=>{
|
|
|
222
196
|
select: {
|
|
223
197
|
[orderableFieldName]: true
|
|
224
198
|
},
|
|
225
|
-
where:
|
|
226
|
-
{
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
},
|
|
231
|
-
joinScopeWhere ?? undefined
|
|
232
|
-
])
|
|
199
|
+
where: {
|
|
200
|
+
[orderableFieldName]: {
|
|
201
|
+
exists: false
|
|
202
|
+
}
|
|
203
|
+
}
|
|
233
204
|
});
|
|
234
205
|
await initTransaction(req);
|
|
235
206
|
// We cannot update all documents in a single operation with `payload.update`,
|
|
@@ -273,14 +244,20 @@ export const addOrderableEndpoint = (config, joinFieldPathsByCollection)=>{
|
|
|
273
244
|
});
|
|
274
245
|
}
|
|
275
246
|
const targetId = target.id;
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
247
|
+
let targetKey = target.key;
|
|
248
|
+
// If targetKey = pending, we need to find its current key.
|
|
249
|
+
// This can only happen if the user reorders rows quickly with a slow connection.
|
|
250
|
+
if (targetKey === 'pending') {
|
|
251
|
+
const beforeDoc = await req.payload.findByID({
|
|
252
|
+
id: targetId,
|
|
253
|
+
collection: collection.slug,
|
|
254
|
+
depth: 0,
|
|
255
|
+
select: {
|
|
256
|
+
[orderableFieldName]: true
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
targetKey = beforeDoc?.[orderableFieldName] || null;
|
|
260
|
+
}
|
|
284
261
|
// The reason the endpoint does not receive this docId as an argument is that there
|
|
285
262
|
// are situations where the user may not see or know what the next or previous one is. For
|
|
286
263
|
// example, access control restrictions, if docBefore is the last one on the page, etc.
|
|
@@ -293,14 +270,11 @@ export const addOrderableEndpoint = (config, joinFieldPathsByCollection)=>{
|
|
|
293
270
|
[orderableFieldName]: true
|
|
294
271
|
},
|
|
295
272
|
sort: newKeyWillBe === 'greater' ? orderableFieldName : `-${orderableFieldName}`,
|
|
296
|
-
where:
|
|
297
|
-
{
|
|
298
|
-
[
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
},
|
|
302
|
-
joinScopeWhere ?? undefined
|
|
303
|
-
])
|
|
273
|
+
where: {
|
|
274
|
+
[orderableFieldName]: {
|
|
275
|
+
[newKeyWillBe === 'greater' ? 'greater_than' : 'less_than']: targetKey
|
|
276
|
+
}
|
|
277
|
+
}
|
|
304
278
|
});
|
|
305
279
|
const adjacentDocKey = adjacentDoc.docs?.[0]?.[orderableFieldName] || null;
|
|
306
280
|
// Currently N (= docsToMove.length) is always 1. Maybe in the future we will
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/config/orderable/index.ts"],"sourcesContent":["import { status as httpStatus } from 'http-status'\n\nimport type { BeforeChangeHook, CollectionConfig } from '../../collections/config/types.js'\nimport type { Field } from '../../fields/config/types.js'\nimport type { Endpoint, PayloadHandler, SanitizedConfig } from '../types.js'\n\nimport { executeAccess } from '../../auth/executeAccess.js'\nimport { APIError } from '../../errors/index.js'\nimport { combineWhereConstraints } from '../../utilities/combineWhereConstraints.js'\nimport { commitTransaction } from '../../utilities/commitTransaction.js'\nimport { initTransaction } from '../../utilities/initTransaction.js'\nimport { killTransaction } from '../../utilities/killTransaction.js'\nimport { traverseFields } from '../../utilities/traverseFields.js'\nimport { generateKeyBetween, generateNKeysBetween } from './fractional-indexing.js'\nimport { getJoinScopeContext } from './utils/getJoinScopeContext.js'\nimport { getJoinScopeWhereFromDocData } from './utils/getJoinScopeWhereFromDocData.js'\nimport { resolvePendingTargetKey } from './utils/resolvePendingTargetKey.js'\n\n/**\n * This function creates:\n * - N fields per collection, named `_order` or `_<collection>_<joinField>_order`\n * - 1 hook per collection\n * - 1 endpoint per app\n *\n * Also, if collection.defaultSort or joinField.defaultSort is not set, it will be set to the orderable field.\n */\nexport const setupOrderable = (config: SanitizedConfig) => {\n const fieldsToAdd = new Map<CollectionConfig, string[]>()\n const joinFieldPathsByCollection = new Map<string, Map<string, string>>()\n\n config.collections.forEach((collection) => {\n if (collection.orderable) {\n const currentFields = fieldsToAdd.get(collection) || []\n fieldsToAdd.set(collection, [...currentFields, '_order'])\n collection.defaultSort = collection.defaultSort ?? '_order'\n }\n\n traverseFields({\n callback: ({ field, parentRef, ref }) => {\n if (field.type === 'array' || field.type === 'blocks') {\n return false\n }\n if (field.type === 'group' || field.type === 'tab') {\n // @ts-expect-error ref is untyped\n const parentPrefix = parentRef?.prefix ? `${parentRef.prefix}_` : ''\n // @ts-expect-error ref is untyped\n ref.prefix = `${parentPrefix}${field.name}`\n }\n if (field.type === 'join' && field.orderable === true) {\n if (Array.isArray(field.collection)) {\n throw new APIError(\n 'Orderable joins must target a single collection',\n httpStatus.BAD_REQUEST,\n {},\n true,\n )\n }\n const relationshipCollection = config.collections.find((c) => c.slug === field.collection)\n if (!relationshipCollection) {\n return false\n }\n field.defaultSort = field.defaultSort ?? `_${field.collection}_${field.name}_order`\n const currentFields = fieldsToAdd.get(relationshipCollection) || []\n // @ts-expect-error ref is untyped\n const prefix = parentRef?.prefix ? `${parentRef.prefix}_` : ''\n const joinOrderableFieldName = `_${field.collection}_${prefix}${field.name}_order`\n fieldsToAdd.set(relationshipCollection, [...currentFields, joinOrderableFieldName])\n\n const currentJoinFieldPaths =\n joinFieldPathsByCollection.get(relationshipCollection.slug) || new Map<string, string>()\n currentJoinFieldPaths.set(joinOrderableFieldName, field.on)\n joinFieldPathsByCollection.set(relationshipCollection.slug, currentJoinFieldPaths)\n }\n },\n fields: collection.fields,\n })\n })\n\n Array.from(fieldsToAdd.entries()).forEach(([collection, orderableFields]) => {\n addOrderableFieldsAndHook(collection, orderableFields, joinFieldPathsByCollection)\n })\n\n if (fieldsToAdd.size > 0) {\n addOrderableEndpoint(config, joinFieldPathsByCollection)\n }\n}\n\nexport const addOrderableFieldsAndHook = (\n collection: CollectionConfig,\n orderableFieldNames: string[],\n joinFieldPathsByCollection?: Map<string, Map<string, string>>,\n) => {\n // 1. Add field\n orderableFieldNames.forEach((orderableFieldName) => {\n const orderField: Field = {\n name: orderableFieldName,\n type: 'text',\n admin: {\n disableBulkEdit: true,\n disabled: true,\n disableGroupBy: true,\n disableListColumn: true,\n disableListFilter: true,\n hidden: true,\n readOnly: true,\n },\n hooks: {\n beforeDuplicate: [\n ({ siblingData }) => {\n delete siblingData[orderableFieldName]\n },\n ],\n },\n index: true,\n }\n\n collection.fields.unshift(orderField)\n })\n\n // 2. Add hook\n if (!collection.hooks) {\n collection.hooks = {}\n }\n if (!collection.hooks.beforeChange) {\n collection.hooks.beforeChange = []\n }\n\n const orderBeforeChangeHook: BeforeChangeHook = async ({ data, originalDoc, req }) => {\n for (const orderableFieldName of orderableFieldNames) {\n if (!data[orderableFieldName] && !originalDoc?.[orderableFieldName]) {\n const joinScopeWhere = getJoinScopeWhereFromDocData({\n collectionSlug: collection.slug,\n data,\n joinFieldPathsByCollection,\n orderableFieldName,\n originalDoc,\n })\n\n const lastDoc = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 1,\n pagination: false,\n req,\n select: { [orderableFieldName]: true },\n sort: `-${orderableFieldName}`,\n where: combineWhereConstraints([\n {\n [orderableFieldName]: {\n exists: true,\n },\n },\n joinScopeWhere ?? undefined,\n ]),\n })\n\n const lastOrderValue = lastDoc.docs[0]?.[orderableFieldName] || null\n data[orderableFieldName] = generateKeyBetween(lastOrderValue, null)\n }\n }\n\n return data\n }\n\n collection.hooks.beforeChange.push(orderBeforeChangeHook)\n}\n\n/**\n * The body of the reorder endpoint.\n * @internal\n */\nexport type OrderableEndpointBody = {\n collectionSlug: string\n docsToMove: string[]\n newKeyWillBe: 'greater' | 'less'\n orderableFieldName: string\n target: {\n id: string\n key: string\n }\n}\n\nexport const addOrderableEndpoint = (\n config: SanitizedConfig,\n joinFieldPathsByCollection: Map<string, Map<string, string>>,\n) => {\n // 3. Add endpoint\n const reorderHandler: PayloadHandler = async (req) => {\n const body = (await req.json?.()) as OrderableEndpointBody\n\n const { collectionSlug, docsToMove, newKeyWillBe, orderableFieldName, target } = body\n\n if (!Array.isArray(docsToMove) || docsToMove.length === 0) {\n return new Response(JSON.stringify({ error: 'docsToMove must be a non-empty array' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n if (newKeyWillBe !== 'greater' && newKeyWillBe !== 'less') {\n return new Response(JSON.stringify({ error: 'newKeyWillBe must be \"greater\" or \"less\"' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n const collection = config.collections.find((c) => c.slug === collectionSlug)\n if (!collection) {\n return new Response(JSON.stringify({ error: `Collection ${collectionSlug} not found` }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n if (typeof orderableFieldName !== 'string') {\n return new Response(JSON.stringify({ error: 'orderableFieldName must be a string' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n\n const { joinScopeWhere, targetDoc } = await getJoinScopeContext({\n collectionSlug: collection.slug,\n joinFieldPathsByCollection,\n orderableFieldName,\n req,\n target,\n })\n\n // Prevent reordering if user doesn't have editing permissions\n if (collection.access?.update) {\n await executeAccess(\n {\n // Currently only one doc can be moved at a time. We should review this if we want to allow\n // multiple docs to be moved at once in the future.\n id: docsToMove[0],\n data: {},\n req,\n },\n collection.access.update,\n )\n }\n /**\n * If there is no target.key, we can assume the user enabled `orderable`\n * on a collection with existing documents, and that this is the first\n * time they tried to reorder them. Therefore, we perform a one-time\n * migration by setting the key value for all documents. We do this\n * instead of enforcing `required` and `unique` at the database schema\n * level, so that users don't have to run a migration when they enable\n * `orderable` on a collection with existing documents.\n */\n if (!target.key) {\n const { docs } = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 0,\n req,\n select: { [orderableFieldName]: true },\n where: combineWhereConstraints([\n {\n [orderableFieldName]: {\n exists: false,\n },\n },\n joinScopeWhere ?? undefined,\n ]),\n })\n await initTransaction(req)\n // We cannot update all documents in a single operation with `payload.update`,\n // because they would all end up with the same order key (`a0`).\n try {\n for (const doc of docs) {\n await req.payload.update({\n id: doc.id,\n collection: collection.slug,\n data: {\n // no data needed since the order hooks will handle this\n },\n depth: 0,\n req,\n })\n await commitTransaction(req)\n }\n } catch (e) {\n await killTransaction(req)\n if (e instanceof Error) {\n throw new APIError(e.message, httpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n return new Response(JSON.stringify({ message: 'initial migration', success: true }), {\n headers: { 'Content-Type': 'application/json' },\n status: 200,\n })\n }\n\n if (\n typeof target !== 'object' ||\n typeof target.id === 'undefined' ||\n typeof target.key !== 'string'\n ) {\n return new Response(JSON.stringify({ error: 'target must be an object with id' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n\n const targetId = target.id\n const targetKey = await resolvePendingTargetKey({\n collectionSlug: collection.slug,\n orderableFieldName,\n req,\n targetDoc,\n targetID: targetId,\n targetKey: target.key,\n })\n\n // The reason the endpoint does not receive this docId as an argument is that there\n // are situations where the user may not see or know what the next or previous one is. For\n // example, access control restrictions, if docBefore is the last one on the page, etc.\n const adjacentDoc = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 1,\n pagination: false,\n select: { [orderableFieldName]: true },\n sort: newKeyWillBe === 'greater' ? orderableFieldName : `-${orderableFieldName}`,\n where: combineWhereConstraints([\n {\n [orderableFieldName]: {\n [newKeyWillBe === 'greater' ? 'greater_than' : 'less_than']: targetKey,\n },\n },\n joinScopeWhere ?? undefined,\n ]),\n })\n const adjacentDocKey = adjacentDoc.docs?.[0]?.[orderableFieldName] || null\n\n // Currently N (= docsToMove.length) is always 1. Maybe in the future we will\n // allow dragging and reordering multiple documents at once via the UI.\n const orderValues =\n newKeyWillBe === 'greater'\n ? generateNKeysBetween(targetKey, adjacentDocKey, docsToMove.length)\n : generateNKeysBetween(adjacentDocKey, targetKey, docsToMove.length)\n\n // Update each document with its new order value\n for (const [index, id] of docsToMove.entries()) {\n await req.payload.update({\n id,\n collection: collection.slug,\n data: {\n [orderableFieldName]: orderValues[index],\n },\n depth: 0,\n req,\n })\n }\n\n return new Response(JSON.stringify({ orderValues, success: true }), {\n headers: { 'Content-Type': 'application/json' },\n status: 200,\n })\n }\n\n const reorderEndpoint: Endpoint = {\n handler: reorderHandler,\n method: 'post',\n path: '/reorder',\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n config.endpoints.push(reorderEndpoint)\n}\n"],"names":["status","httpStatus","executeAccess","APIError","combineWhereConstraints","commitTransaction","initTransaction","killTransaction","traverseFields","generateKeyBetween","generateNKeysBetween","getJoinScopeContext","getJoinScopeWhereFromDocData","resolvePendingTargetKey","setupOrderable","config","fieldsToAdd","Map","joinFieldPathsByCollection","collections","forEach","collection","orderable","currentFields","get","set","defaultSort","callback","field","parentRef","ref","type","parentPrefix","prefix","name","Array","isArray","BAD_REQUEST","relationshipCollection","find","c","slug","joinOrderableFieldName","currentJoinFieldPaths","on","fields","from","entries","orderableFields","addOrderableFieldsAndHook","size","addOrderableEndpoint","orderableFieldNames","orderableFieldName","orderField","admin","disableBulkEdit","disabled","disableGroupBy","disableListColumn","disableListFilter","hidden","readOnly","hooks","beforeDuplicate","siblingData","index","unshift","beforeChange","orderBeforeChangeHook","data","originalDoc","req","joinScopeWhere","collectionSlug","lastDoc","payload","depth","limit","pagination","select","sort","where","exists","undefined","lastOrderValue","docs","push","reorderHandler","body","json","docsToMove","newKeyWillBe","target","length","Response","JSON","stringify","error","headers","targetDoc","access","update","id","key","doc","e","Error","message","INTERNAL_SERVER_ERROR","success","targetId","targetKey","targetID","adjacentDoc","adjacentDocKey","orderValues","reorderEndpoint","handler","method","path","endpoints"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../src/config/orderable/index.ts"],"sourcesContent":["import { status as httpStatus } from 'http-status'\n\nimport type { BeforeChangeHook, CollectionConfig } from '../../collections/config/types.js'\nimport type { Field } from '../../fields/config/types.js'\nimport type { Endpoint, PayloadHandler, SanitizedConfig } from '../types.js'\n\nimport { executeAccess } from '../../auth/executeAccess.js'\nimport { APIError } from '../../errors/index.js'\nimport { commitTransaction } from '../../utilities/commitTransaction.js'\nimport { initTransaction } from '../../utilities/initTransaction.js'\nimport { killTransaction } from '../../utilities/killTransaction.js'\nimport { traverseFields } from '../../utilities/traverseFields.js'\nimport { generateKeyBetween, generateNKeysBetween } from './fractional-indexing.js'\n\n/**\n * This function creates:\n * - N fields per collection, named `_order` or `_<collection>_<joinField>_order`\n * - 1 hook per collection\n * - 1 endpoint per app\n *\n * Also, if collection.defaultSort or joinField.defaultSort is not set, it will be set to the orderable field.\n */\nexport const setupOrderable = (config: SanitizedConfig) => {\n const fieldsToAdd = new Map<CollectionConfig, string[]>()\n\n config.collections.forEach((collection) => {\n if (collection.orderable) {\n const currentFields = fieldsToAdd.get(collection) || []\n fieldsToAdd.set(collection, [...currentFields, '_order'])\n collection.defaultSort = collection.defaultSort ?? '_order'\n }\n\n traverseFields({\n callback: ({ field, parentRef, ref }) => {\n if (field.type === 'array' || field.type === 'blocks') {\n return false\n }\n if (field.type === 'group' || field.type === 'tab') {\n // @ts-expect-error ref is untyped\n const parentPrefix = parentRef?.prefix ? `${parentRef.prefix}_` : ''\n // @ts-expect-error ref is untyped\n ref.prefix = `${parentPrefix}${field.name}`\n }\n if (field.type === 'join' && field.orderable === true) {\n if (Array.isArray(field.collection)) {\n throw new APIError(\n 'Orderable joins must target a single collection',\n httpStatus.BAD_REQUEST,\n {},\n true,\n )\n }\n const relationshipCollection = config.collections.find((c) => c.slug === field.collection)\n if (!relationshipCollection) {\n return false\n }\n field.defaultSort = field.defaultSort ?? `_${field.collection}_${field.name}_order`\n const currentFields = fieldsToAdd.get(relationshipCollection) || []\n // @ts-expect-error ref is untyped\n const prefix = parentRef?.prefix ? `${parentRef.prefix}_` : ''\n fieldsToAdd.set(relationshipCollection, [\n ...currentFields,\n `_${field.collection}_${prefix}${field.name}_order`,\n ])\n }\n },\n fields: collection.fields,\n })\n })\n\n Array.from(fieldsToAdd.entries()).forEach(([collection, orderableFields]) => {\n addOrderableFieldsAndHook(collection, orderableFields)\n })\n\n if (fieldsToAdd.size > 0) {\n addOrderableEndpoint(config)\n }\n}\n\nexport const addOrderableFieldsAndHook = (\n collection: CollectionConfig,\n orderableFieldNames: string[],\n) => {\n // 1. Add field\n orderableFieldNames.forEach((orderableFieldName) => {\n const orderField: Field = {\n name: orderableFieldName,\n type: 'text',\n admin: {\n disableBulkEdit: true,\n disabled: true,\n disableGroupBy: true,\n disableListColumn: true,\n disableListFilter: true,\n hidden: true,\n readOnly: true,\n },\n hooks: {\n beforeDuplicate: [\n ({ siblingData }) => {\n delete siblingData[orderableFieldName]\n },\n ],\n },\n index: true,\n }\n\n collection.fields.unshift(orderField)\n })\n\n // 2. Add hook\n if (!collection.hooks) {\n collection.hooks = {}\n }\n if (!collection.hooks.beforeChange) {\n collection.hooks.beforeChange = []\n }\n\n const orderBeforeChangeHook: BeforeChangeHook = async ({ data, originalDoc, req }) => {\n for (const orderableFieldName of orderableFieldNames) {\n if (!data[orderableFieldName] && !originalDoc?.[orderableFieldName]) {\n const lastDoc = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 1,\n pagination: false,\n req,\n select: { [orderableFieldName]: true },\n sort: `-${orderableFieldName}`,\n where: {\n [orderableFieldName]: {\n exists: true,\n },\n },\n })\n\n const lastOrderValue = lastDoc.docs[0]?.[orderableFieldName] || null\n data[orderableFieldName] = generateKeyBetween(lastOrderValue, null)\n }\n }\n\n return data\n }\n\n collection.hooks.beforeChange.push(orderBeforeChangeHook)\n}\n\n/**\n * The body of the reorder endpoint.\n * @internal\n */\nexport type OrderableEndpointBody = {\n collectionSlug: string\n docsToMove: string[]\n newKeyWillBe: 'greater' | 'less'\n orderableFieldName: string\n target: {\n id: string\n key: string\n }\n}\n\nexport const addOrderableEndpoint = (config: SanitizedConfig) => {\n // 3. Add endpoint\n const reorderHandler: PayloadHandler = async (req) => {\n const body = (await req.json?.()) as OrderableEndpointBody\n\n const { collectionSlug, docsToMove, newKeyWillBe, orderableFieldName, target } = body\n\n if (!Array.isArray(docsToMove) || docsToMove.length === 0) {\n return new Response(JSON.stringify({ error: 'docsToMove must be a non-empty array' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n if (newKeyWillBe !== 'greater' && newKeyWillBe !== 'less') {\n return new Response(JSON.stringify({ error: 'newKeyWillBe must be \"greater\" or \"less\"' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n const collection = config.collections.find((c) => c.slug === collectionSlug)\n if (!collection) {\n return new Response(JSON.stringify({ error: `Collection ${collectionSlug} not found` }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n if (typeof orderableFieldName !== 'string') {\n return new Response(JSON.stringify({ error: 'orderableFieldName must be a string' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n\n // Prevent reordering if user doesn't have editing permissions\n if (collection.access?.update) {\n await executeAccess(\n {\n // Currently only one doc can be moved at a time. We should review this if we want to allow\n // multiple docs to be moved at once in the future.\n id: docsToMove[0],\n data: {},\n req,\n },\n collection.access.update,\n )\n }\n /**\n * If there is no target.key, we can assume the user enabled `orderable`\n * on a collection with existing documents, and that this is the first\n * time they tried to reorder them. Therefore, we perform a one-time\n * migration by setting the key value for all documents. We do this\n * instead of enforcing `required` and `unique` at the database schema\n * level, so that users don't have to run a migration when they enable\n * `orderable` on a collection with existing documents.\n */\n if (!target.key) {\n const { docs } = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 0,\n req,\n select: { [orderableFieldName]: true },\n where: {\n [orderableFieldName]: {\n exists: false,\n },\n },\n })\n await initTransaction(req)\n // We cannot update all documents in a single operation with `payload.update`,\n // because they would all end up with the same order key (`a0`).\n try {\n for (const doc of docs) {\n await req.payload.update({\n id: doc.id,\n collection: collection.slug,\n data: {\n // no data needed since the order hooks will handle this\n },\n depth: 0,\n req,\n })\n await commitTransaction(req)\n }\n } catch (e) {\n await killTransaction(req)\n if (e instanceof Error) {\n throw new APIError(e.message, httpStatus.INTERNAL_SERVER_ERROR)\n }\n }\n\n return new Response(JSON.stringify({ message: 'initial migration', success: true }), {\n headers: { 'Content-Type': 'application/json' },\n status: 200,\n })\n }\n\n if (\n typeof target !== 'object' ||\n typeof target.id === 'undefined' ||\n typeof target.key !== 'string'\n ) {\n return new Response(JSON.stringify({ error: 'target must be an object with id' }), {\n headers: { 'Content-Type': 'application/json' },\n status: 400,\n })\n }\n\n const targetId = target.id\n let targetKey = target.key\n\n // If targetKey = pending, we need to find its current key.\n // This can only happen if the user reorders rows quickly with a slow connection.\n if (targetKey === 'pending') {\n const beforeDoc = await req.payload.findByID({\n id: targetId,\n collection: collection.slug,\n depth: 0,\n select: { [orderableFieldName]: true },\n })\n targetKey = beforeDoc?.[orderableFieldName] || null\n }\n\n // The reason the endpoint does not receive this docId as an argument is that there\n // are situations where the user may not see or know what the next or previous one is. For\n // example, access control restrictions, if docBefore is the last one on the page, etc.\n const adjacentDoc = await req.payload.find({\n collection: collection.slug,\n depth: 0,\n limit: 1,\n pagination: false,\n select: { [orderableFieldName]: true },\n sort: newKeyWillBe === 'greater' ? orderableFieldName : `-${orderableFieldName}`,\n where: {\n [orderableFieldName]: {\n [newKeyWillBe === 'greater' ? 'greater_than' : 'less_than']: targetKey,\n },\n },\n })\n const adjacentDocKey = adjacentDoc.docs?.[0]?.[orderableFieldName] || null\n\n // Currently N (= docsToMove.length) is always 1. Maybe in the future we will\n // allow dragging and reordering multiple documents at once via the UI.\n const orderValues =\n newKeyWillBe === 'greater'\n ? generateNKeysBetween(targetKey, adjacentDocKey, docsToMove.length)\n : generateNKeysBetween(adjacentDocKey, targetKey, docsToMove.length)\n\n // Update each document with its new order value\n for (const [index, id] of docsToMove.entries()) {\n await req.payload.update({\n id,\n collection: collection.slug,\n data: {\n [orderableFieldName]: orderValues[index],\n },\n depth: 0,\n req,\n })\n }\n\n return new Response(JSON.stringify({ orderValues, success: true }), {\n headers: { 'Content-Type': 'application/json' },\n status: 200,\n })\n }\n\n const reorderEndpoint: Endpoint = {\n handler: reorderHandler,\n method: 'post',\n path: '/reorder',\n }\n\n if (!config.endpoints) {\n config.endpoints = []\n }\n\n config.endpoints.push(reorderEndpoint)\n}\n"],"names":["status","httpStatus","executeAccess","APIError","commitTransaction","initTransaction","killTransaction","traverseFields","generateKeyBetween","generateNKeysBetween","setupOrderable","config","fieldsToAdd","Map","collections","forEach","collection","orderable","currentFields","get","set","defaultSort","callback","field","parentRef","ref","type","parentPrefix","prefix","name","Array","isArray","BAD_REQUEST","relationshipCollection","find","c","slug","fields","from","entries","orderableFields","addOrderableFieldsAndHook","size","addOrderableEndpoint","orderableFieldNames","orderableFieldName","orderField","admin","disableBulkEdit","disabled","disableGroupBy","disableListColumn","disableListFilter","hidden","readOnly","hooks","beforeDuplicate","siblingData","index","unshift","beforeChange","orderBeforeChangeHook","data","originalDoc","req","lastDoc","payload","depth","limit","pagination","select","sort","where","exists","lastOrderValue","docs","push","reorderHandler","body","json","collectionSlug","docsToMove","newKeyWillBe","target","length","Response","JSON","stringify","error","headers","access","update","id","key","doc","e","Error","message","INTERNAL_SERVER_ERROR","success","targetId","targetKey","beforeDoc","findByID","adjacentDoc","adjacentDocKey","orderValues","reorderEndpoint","handler","method","path","endpoints"],"mappings":"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"}
|
package/dist/index.bundled.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { Collection, TypeWithID } from '../collections/config/types.js';
|
|
2
2
|
import type { PayloadRequest } from '../types/index.js';
|
|
3
|
-
export declare const checkFileAccess: ({ collection, filename, req, }: {
|
|
3
|
+
export declare const checkFileAccess: ({ collection, filename, prefix, req, }: {
|
|
4
4
|
collection: Collection;
|
|
5
5
|
filename: string;
|
|
6
|
+
prefix?: string;
|
|
6
7
|
req: PayloadRequest;
|
|
7
8
|
}) => Promise<TypeWithID | undefined>;
|
|
8
9
|
//# sourceMappingURL=checkFileAccess.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkFileAccess.d.ts","sourceRoot":"","sources":["../../src/uploads/checkFileAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,mBAAmB,CAAA;AAK9D,eAAO,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"checkFileAccess.d.ts","sourceRoot":"","sources":["../../src/uploads/checkFileAccess.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAA;AAC5E,OAAO,KAAK,EAAE,cAAc,EAAS,MAAM,mBAAmB,CAAA;AAK9D,eAAO,MAAM,eAAe,2CAKzB;IACD,UAAU,EAAE,UAAU,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,GAAG,EAAE,cAAc,CAAA;CACpB,KAAG,OAAO,CAAC,UAAU,GAAG,SAAS,CAuDjC,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { executeAccess } from '../auth/executeAccess.js';
|
|
2
2
|
import { Forbidden } from '../errors/Forbidden.js';
|
|
3
|
-
export const checkFileAccess = async ({ collection, filename, req })=>{
|
|
3
|
+
export const checkFileAccess = async ({ collection, filename, prefix, req })=>{
|
|
4
4
|
if (filename.includes('../') || filename.includes('..\\')) {
|
|
5
5
|
throw new Forbidden(req.t);
|
|
6
6
|
}
|
|
@@ -36,6 +36,13 @@ export const checkFileAccess = async ({ collection, filename, req })=>{
|
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
|
+
if (typeof prefix === 'string') {
|
|
40
|
+
queryToBuild.and.push({
|
|
41
|
+
prefix: {
|
|
42
|
+
equals: prefix
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
}
|
|
39
46
|
const doc = await req.payload.db.findOne({
|
|
40
47
|
collection: config.slug,
|
|
41
48
|
req,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/uploads/checkFileAccess.ts"],"sourcesContent":["import type { Collection, TypeWithID } from '../collections/config/types.js'\nimport type { PayloadRequest, Where } from '../types/index.js'\n\nimport { executeAccess } from '../auth/executeAccess.js'\nimport { Forbidden } from '../errors/Forbidden.js'\n\nexport const checkFileAccess = async ({\n collection,\n filename,\n req,\n}: {\n collection: Collection\n filename: string\n req: PayloadRequest\n}): Promise<TypeWithID | undefined> => {\n if (filename.includes('../') || filename.includes('..\\\\')) {\n throw new Forbidden(req.t)\n }\n const { config } = collection\n\n const accessResult = await executeAccess(\n { data: { filename }, isReadingStaticFile: true, req },\n config.access.read,\n )\n\n if (typeof accessResult === 'object') {\n const queryToBuild: Where = {\n and: [\n {\n or: [\n {\n filename: {\n equals: filename,\n },\n },\n ],\n },\n accessResult,\n ],\n }\n\n if (config.upload.imageSizes) {\n config.upload.imageSizes.forEach(({ name }) => {\n queryToBuild.and?.[0]?.or?.push({\n [`sizes.${name}.filename`]: {\n equals: filename,\n },\n })\n })\n }\n\n const doc = await req.payload.db.findOne({\n collection: config.slug,\n req,\n where: queryToBuild,\n })\n\n if (!doc) {\n throw new Forbidden(req.t)\n }\n\n return doc\n }\n}\n"],"names":["executeAccess","Forbidden","checkFileAccess","collection","filename","req","includes","t","config","accessResult","data","isReadingStaticFile","access","read","queryToBuild","and","or","equals","upload","imageSizes","forEach","name","push","doc","payload","db","findOne","slug","where"],"mappings":"AAGA,SAASA,aAAa,QAAQ,2BAA0B;AACxD,SAASC,SAAS,QAAQ,yBAAwB;AAElD,OAAO,MAAMC,kBAAkB,OAAO,EACpCC,UAAU,EACVC,QAAQ,EACRC,GAAG,
|
|
1
|
+
{"version":3,"sources":["../../src/uploads/checkFileAccess.ts"],"sourcesContent":["import type { Collection, TypeWithID } from '../collections/config/types.js'\nimport type { PayloadRequest, Where } from '../types/index.js'\n\nimport { executeAccess } from '../auth/executeAccess.js'\nimport { Forbidden } from '../errors/Forbidden.js'\n\nexport const checkFileAccess = async ({\n collection,\n filename,\n prefix,\n req,\n}: {\n collection: Collection\n filename: string\n prefix?: string\n req: PayloadRequest\n}): Promise<TypeWithID | undefined> => {\n if (filename.includes('../') || filename.includes('..\\\\')) {\n throw new Forbidden(req.t)\n }\n const { config } = collection\n\n const accessResult = await executeAccess(\n { data: { filename }, isReadingStaticFile: true, req },\n config.access.read,\n )\n\n if (typeof accessResult === 'object') {\n const queryToBuild: Where = {\n and: [\n {\n or: [\n {\n filename: {\n equals: filename,\n },\n },\n ],\n },\n accessResult,\n ],\n }\n\n if (config.upload.imageSizes) {\n config.upload.imageSizes.forEach(({ name }) => {\n queryToBuild.and?.[0]?.or?.push({\n [`sizes.${name}.filename`]: {\n equals: filename,\n },\n })\n })\n }\n\n if (typeof prefix === 'string') {\n queryToBuild.and!.push({\n prefix: { equals: prefix },\n })\n }\n\n const doc = await req.payload.db.findOne({\n collection: config.slug,\n req,\n where: queryToBuild,\n })\n\n if (!doc) {\n throw new Forbidden(req.t)\n }\n\n return doc\n }\n}\n"],"names":["executeAccess","Forbidden","checkFileAccess","collection","filename","prefix","req","includes","t","config","accessResult","data","isReadingStaticFile","access","read","queryToBuild","and","or","equals","upload","imageSizes","forEach","name","push","doc","payload","db","findOne","slug","where"],"mappings":"AAGA,SAASA,aAAa,QAAQ,2BAA0B;AACxD,SAASC,SAAS,QAAQ,yBAAwB;AAElD,OAAO,MAAMC,kBAAkB,OAAO,EACpCC,UAAU,EACVC,QAAQ,EACRC,MAAM,EACNC,GAAG,EAMJ;IACC,IAAIF,SAASG,QAAQ,CAAC,UAAUH,SAASG,QAAQ,CAAC,SAAS;QACzD,MAAM,IAAIN,UAAUK,IAAIE,CAAC;IAC3B;IACA,MAAM,EAAEC,MAAM,EAAE,GAAGN;IAEnB,MAAMO,eAAe,MAAMV,cACzB;QAAEW,MAAM;YAAEP;QAAS;QAAGQ,qBAAqB;QAAMN;IAAI,GACrDG,OAAOI,MAAM,CAACC,IAAI;IAGpB,IAAI,OAAOJ,iBAAiB,UAAU;QACpC,MAAMK,eAAsB;YAC1BC,KAAK;gBACH;oBACEC,IAAI;wBACF;4BACEb,UAAU;gCACRc,QAAQd;4BACV;wBACF;qBACD;gBACH;gBACAM;aACD;QACH;QAEA,IAAID,OAAOU,MAAM,CAACC,UAAU,EAAE;YAC5BX,OAAOU,MAAM,CAACC,UAAU,CAACC,OAAO,CAAC,CAAC,EAAEC,IAAI,EAAE;gBACxCP,aAAaC,GAAG,EAAE,CAAC,EAAE,EAAEC,IAAIM,KAAK;oBAC9B,CAAC,CAAC,MAAM,EAAED,KAAK,SAAS,CAAC,CAAC,EAAE;wBAC1BJ,QAAQd;oBACV;gBACF;YACF;QACF;QAEA,IAAI,OAAOC,WAAW,UAAU;YAC9BU,aAAaC,GAAG,CAAEO,IAAI,CAAC;gBACrBlB,QAAQ;oBAAEa,QAAQb;gBAAO;YAC3B;QACF;QAEA,MAAMmB,MAAM,MAAMlB,IAAImB,OAAO,CAACC,EAAE,CAACC,OAAO,CAAC;YACvCxB,YAAYM,OAAOmB,IAAI;YACvBtB;YACAuB,OAAOd;QACT;QAEA,IAAI,CAACS,KAAK;YACR,MAAM,IAAIvB,UAAUK,IAAIE,CAAC;QAC3B;QAEA,OAAOgB;IACT;AACF,EAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
vi.mock('../auth/executeAccess.js', ()=>({
|
|
3
|
+
executeAccess: vi.fn()
|
|
4
|
+
}));
|
|
5
|
+
import { executeAccess } from '../auth/executeAccess.js';
|
|
6
|
+
import { checkFileAccess } from './checkFileAccess.js';
|
|
7
|
+
const makeFindOne = (result = {
|
|
8
|
+
id: '1',
|
|
9
|
+
filename: 'logo.png'
|
|
10
|
+
})=>vi.fn().mockResolvedValue(result);
|
|
11
|
+
const makeCollection = ()=>({
|
|
12
|
+
config: {
|
|
13
|
+
slug: 'test-media',
|
|
14
|
+
access: {
|
|
15
|
+
read: vi.fn()
|
|
16
|
+
},
|
|
17
|
+
upload: {}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
const makeReq = (findOne)=>({
|
|
21
|
+
t: vi.fn(),
|
|
22
|
+
payload: {
|
|
23
|
+
db: {
|
|
24
|
+
findOne
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
describe('checkFileAccess', ()=>{
|
|
29
|
+
beforeEach(()=>{
|
|
30
|
+
vi.mocked(executeAccess).mockResolvedValue({});
|
|
31
|
+
});
|
|
32
|
+
describe('prefix filtering', ()=>{
|
|
33
|
+
it('should add prefix clause to where query when prefix is provided', async ()=>{
|
|
34
|
+
const findOne = makeFindOne();
|
|
35
|
+
const req = makeReq(findOne);
|
|
36
|
+
const collection = makeCollection();
|
|
37
|
+
await checkFileAccess({
|
|
38
|
+
collection,
|
|
39
|
+
filename: 'logo.png',
|
|
40
|
+
prefix: 'abc123',
|
|
41
|
+
req
|
|
42
|
+
});
|
|
43
|
+
const whereArg = findOne.mock.calls[0]?.[0]?.where;
|
|
44
|
+
expect(whereArg?.and).toEqual(expect.arrayContaining([
|
|
45
|
+
{
|
|
46
|
+
prefix: {
|
|
47
|
+
equals: 'abc123'
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]));
|
|
51
|
+
});
|
|
52
|
+
it('should not add prefix clause to where query when prefix is omitted', async ()=>{
|
|
53
|
+
const findOne = makeFindOne();
|
|
54
|
+
const req = makeReq(findOne);
|
|
55
|
+
const collection = makeCollection();
|
|
56
|
+
await checkFileAccess({
|
|
57
|
+
collection,
|
|
58
|
+
filename: 'logo.png',
|
|
59
|
+
req
|
|
60
|
+
});
|
|
61
|
+
const whereArg = findOne.mock.calls[0]?.[0]?.where;
|
|
62
|
+
const hasPrefixCondition = whereArg?.and?.some((clause)=>'prefix' in clause);
|
|
63
|
+
expect(hasPrefixCondition).toBeFalsy();
|
|
64
|
+
});
|
|
65
|
+
it('should still include filename in where query when prefix is provided', async ()=>{
|
|
66
|
+
const findOne = makeFindOne();
|
|
67
|
+
const req = makeReq(findOne);
|
|
68
|
+
const collection = makeCollection();
|
|
69
|
+
await checkFileAccess({
|
|
70
|
+
collection,
|
|
71
|
+
filename: 'logo.png',
|
|
72
|
+
prefix: 'abc123',
|
|
73
|
+
req
|
|
74
|
+
});
|
|
75
|
+
const whereArg = findOne.mock.calls[0]?.[0]?.where;
|
|
76
|
+
const filenameCondition = whereArg?.and?.[0];
|
|
77
|
+
expect(filenameCondition?.or).toEqual(expect.arrayContaining([
|
|
78
|
+
{
|
|
79
|
+
filename: {
|
|
80
|
+
equals: 'logo.png'
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
]));
|
|
84
|
+
});
|
|
85
|
+
it('should throw when no doc matches the given prefix', async ()=>{
|
|
86
|
+
const findOne = makeFindOne(null);
|
|
87
|
+
const req = makeReq(findOne);
|
|
88
|
+
const collection = makeCollection();
|
|
89
|
+
await expect(checkFileAccess({
|
|
90
|
+
collection,
|
|
91
|
+
filename: 'logo.png',
|
|
92
|
+
prefix: 'nonexistent',
|
|
93
|
+
req
|
|
94
|
+
})).rejects.toThrow();
|
|
95
|
+
});
|
|
96
|
+
it('should throw when filename contains path traversal sequence', async ()=>{
|
|
97
|
+
const findOne = makeFindOne();
|
|
98
|
+
const req = makeReq(findOne);
|
|
99
|
+
const collection = makeCollection();
|
|
100
|
+
await expect(checkFileAccess({
|
|
101
|
+
collection,
|
|
102
|
+
filename: '../etc/passwd',
|
|
103
|
+
req
|
|
104
|
+
})).rejects.toThrow();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
//# sourceMappingURL=checkFileAccess.spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/uploads/checkFileAccess.spec.ts"],"sourcesContent":["import { beforeEach, describe, expect, it, vi } from 'vitest'\n\nimport type { Collection } from '../collections/config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\n\nvi.mock('../auth/executeAccess.js', () => ({\n executeAccess: vi.fn(),\n}))\n\nimport { executeAccess } from '../auth/executeAccess.js'\n\nimport { checkFileAccess } from './checkFileAccess.js'\n\nconst makeFindOne = (result: unknown = { id: '1', filename: 'logo.png' }) =>\n vi.fn().mockResolvedValue(result)\n\nconst makeCollection = (): Collection =>\n ({\n config: {\n slug: 'test-media',\n access: { read: vi.fn() },\n upload: {},\n },\n }) as unknown as Collection\n\nconst makeReq = (findOne: ReturnType<typeof vi.fn>): PayloadRequest =>\n ({\n t: vi.fn(),\n payload: {\n db: { findOne },\n },\n }) as unknown as PayloadRequest\n\ndescribe('checkFileAccess', () => {\n beforeEach(() => {\n vi.mocked(executeAccess).mockResolvedValue({})\n })\n\n describe('prefix filtering', () => {\n it('should add prefix clause to where query when prefix is provided', async () => {\n const findOne = makeFindOne()\n const req = makeReq(findOne)\n const collection = makeCollection()\n\n await checkFileAccess({ collection, filename: 'logo.png', prefix: 'abc123', req })\n\n const whereArg = findOne.mock.calls[0]?.[0]?.where\n expect(whereArg?.and).toEqual(expect.arrayContaining([{ prefix: { equals: 'abc123' } }]))\n })\n\n it('should not add prefix clause to where query when prefix is omitted', async () => {\n const findOne = makeFindOne()\n const req = makeReq(findOne)\n const collection = makeCollection()\n\n await checkFileAccess({ collection, filename: 'logo.png', req })\n\n const whereArg = findOne.mock.calls[0]?.[0]?.where\n const hasPrefixCondition = whereArg?.and?.some(\n (clause: Record<string, unknown>) => 'prefix' in clause,\n )\n expect(hasPrefixCondition).toBeFalsy()\n })\n\n it('should still include filename in where query when prefix is provided', async () => {\n const findOne = makeFindOne()\n const req = makeReq(findOne)\n const collection = makeCollection()\n\n await checkFileAccess({ collection, filename: 'logo.png', prefix: 'abc123', req })\n\n const whereArg = findOne.mock.calls[0]?.[0]?.where\n const filenameCondition = whereArg?.and?.[0]\n expect(filenameCondition?.or).toEqual(\n expect.arrayContaining([{ filename: { equals: 'logo.png' } }]),\n )\n })\n\n it('should throw when no doc matches the given prefix', async () => {\n const findOne = makeFindOne(null)\n const req = makeReq(findOne)\n const collection = makeCollection()\n\n await expect(\n checkFileAccess({ collection, filename: 'logo.png', prefix: 'nonexistent', req }),\n ).rejects.toThrow()\n })\n\n it('should throw when filename contains path traversal sequence', async () => {\n const findOne = makeFindOne()\n const req = makeReq(findOne)\n const collection = makeCollection()\n\n await expect(\n checkFileAccess({ collection, filename: '../etc/passwd', req }),\n ).rejects.toThrow()\n })\n })\n})\n"],"names":["beforeEach","describe","expect","it","vi","mock","executeAccess","fn","checkFileAccess","makeFindOne","result","id","filename","mockResolvedValue","makeCollection","config","slug","access","read","upload","makeReq","findOne","t","payload","db","mocked","req","collection","prefix","whereArg","calls","where","and","toEqual","arrayContaining","equals","hasPrefixCondition","some","clause","toBeFalsy","filenameCondition","or","rejects","toThrow"],"mappings":"AAAA,SAASA,UAAU,EAAEC,QAAQ,EAAEC,MAAM,EAAEC,EAAE,EAAEC,EAAE,QAAQ,SAAQ;AAK7DA,GAAGC,IAAI,CAAC,4BAA4B,IAAO,CAAA;QACzCC,eAAeF,GAAGG,EAAE;IACtB,CAAA;AAEA,SAASD,aAAa,QAAQ,2BAA0B;AAExD,SAASE,eAAe,QAAQ,uBAAsB;AAEtD,MAAMC,cAAc,CAACC,SAAkB;IAAEC,IAAI;IAAKC,UAAU;AAAW,CAAC,GACtER,GAAGG,EAAE,GAAGM,iBAAiB,CAACH;AAE5B,MAAMI,iBAAiB,IACpB,CAAA;QACCC,QAAQ;YACNC,MAAM;YACNC,QAAQ;gBAAEC,MAAMd,GAAGG,EAAE;YAAG;YACxBY,QAAQ,CAAC;QACX;IACF,CAAA;AAEF,MAAMC,UAAU,CAACC,UACd,CAAA;QACCC,GAAGlB,GAAGG,EAAE;QACRgB,SAAS;YACPC,IAAI;gBAAEH;YAAQ;QAChB;IACF,CAAA;AAEFpB,SAAS,mBAAmB;IAC1BD,WAAW;QACTI,GAAGqB,MAAM,CAACnB,eAAeO,iBAAiB,CAAC,CAAC;IAC9C;IAEAZ,SAAS,oBAAoB;QAC3BE,GAAG,mEAAmE;YACpE,MAAMkB,UAAUZ;YAChB,MAAMiB,MAAMN,QAAQC;YACpB,MAAMM,aAAab;YAEnB,MAAMN,gBAAgB;gBAAEmB;gBAAYf,UAAU;gBAAYgB,QAAQ;gBAAUF;YAAI;YAEhF,MAAMG,WAAWR,QAAQhB,IAAI,CAACyB,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAEC;YAC7C7B,OAAO2B,UAAUG,KAAKC,OAAO,CAAC/B,OAAOgC,eAAe,CAAC;gBAAC;oBAAEN,QAAQ;wBAAEO,QAAQ;oBAAS;gBAAE;aAAE;QACzF;QAEAhC,GAAG,sEAAsE;YACvE,MAAMkB,UAAUZ;YAChB,MAAMiB,MAAMN,QAAQC;YACpB,MAAMM,aAAab;YAEnB,MAAMN,gBAAgB;gBAAEmB;gBAAYf,UAAU;gBAAYc;YAAI;YAE9D,MAAMG,WAAWR,QAAQhB,IAAI,CAACyB,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAEC;YAC7C,MAAMK,qBAAqBP,UAAUG,KAAKK,KACxC,CAACC,SAAoC,YAAYA;YAEnDpC,OAAOkC,oBAAoBG,SAAS;QACtC;QAEApC,GAAG,wEAAwE;YACzE,MAAMkB,UAAUZ;YAChB,MAAMiB,MAAMN,QAAQC;YACpB,MAAMM,aAAab;YAEnB,MAAMN,gBAAgB;gBAAEmB;gBAAYf,UAAU;gBAAYgB,QAAQ;gBAAUF;YAAI;YAEhF,MAAMG,WAAWR,QAAQhB,IAAI,CAACyB,KAAK,CAAC,EAAE,EAAE,CAAC,EAAE,EAAEC;YAC7C,MAAMS,oBAAoBX,UAAUG,KAAK,CAAC,EAAE;YAC5C9B,OAAOsC,mBAAmBC,IAAIR,OAAO,CACnC/B,OAAOgC,eAAe,CAAC;gBAAC;oBAAEtB,UAAU;wBAAEuB,QAAQ;oBAAW;gBAAE;aAAE;QAEjE;QAEAhC,GAAG,qDAAqD;YACtD,MAAMkB,UAAUZ,YAAY;YAC5B,MAAMiB,MAAMN,QAAQC;YACpB,MAAMM,aAAab;YAEnB,MAAMZ,OACJM,gBAAgB;gBAAEmB;gBAAYf,UAAU;gBAAYgB,QAAQ;gBAAeF;YAAI,IAC/EgB,OAAO,CAACC,OAAO;QACnB;QAEAxC,GAAG,+DAA+D;YAChE,MAAMkB,UAAUZ;YAChB,MAAMiB,MAAMN,QAAQC;YACpB,MAAMM,aAAab;YAEnB,MAAMZ,OACJM,gBAAgB;gBAAEmB;gBAAYf,UAAU;gBAAiBc;YAAI,IAC7DgB,OAAO,CAACC,OAAO;QACnB;IACF;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../../../src/uploads/endpoints/getFile.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAU3D,eAAO,MAAM,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"getFile.d.ts","sourceRoot":"","sources":["../../../src/uploads/endpoints/getFile.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAU3D,eAAO,MAAM,cAAc,EAAE,cAqJ5B,CAAA"}
|
|
@@ -12,12 +12,14 @@ import { headersWithCors } from '../../utilities/headersWithCors.js';
|
|
|
12
12
|
export const getFileHandler = async (req)=>{
|
|
13
13
|
const collection = getRequestCollection(req);
|
|
14
14
|
const filename = req.routeParams?.filename;
|
|
15
|
+
const prefix = req.searchParams?.get('prefix') ?? undefined;
|
|
15
16
|
if (!collection.config.upload) {
|
|
16
17
|
throw new APIError(`This collection is not an upload collection: ${collection.config.slug}`, httpStatus.BAD_REQUEST);
|
|
17
18
|
}
|
|
18
19
|
const accessResult = await checkFileAccess({
|
|
19
20
|
collection,
|
|
20
21
|
filename,
|
|
22
|
+
prefix,
|
|
21
23
|
req
|
|
22
24
|
});
|
|
23
25
|
if (accessResult instanceof Response) {
|
|
@@ -32,7 +34,8 @@ export const getFileHandler = async (req)=>{
|
|
|
32
34
|
headers,
|
|
33
35
|
params: {
|
|
34
36
|
collection: collection.config.slug,
|
|
35
|
-
filename
|
|
37
|
+
filename,
|
|
38
|
+
prefix
|
|
36
39
|
}
|
|
37
40
|
});
|
|
38
41
|
if (customResponse && customResponse instanceof Response) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/uploads/endpoints/getFile.ts"],"sourcesContent":["import type { Stats } from 'fs'\n\nimport { fileTypeFromFile } from 'file-type'\nimport fsPromises from 'fs/promises'\nimport { status as httpStatus } from 'http-status'\nimport path from 'path'\n\nimport type { PayloadHandler } from '../../config/types.js'\n\nimport { APIError } from '../../errors/APIError.js'\nimport { checkFileAccess } from '../../uploads/checkFileAccess.js'\nimport { streamFile } from '../../uploads/fetchAPI-stream-file/index.js'\nimport { getFileTypeFallback } from '../../uploads/getFileTypeFallback.js'\nimport { parseRangeHeader } from '../../uploads/parseRangeHeader.js'\nimport { getRequestCollection } from '../../utilities/getRequestEntity.js'\nimport { headersWithCors } from '../../utilities/headersWithCors.js'\n\nexport const getFileHandler: PayloadHandler = async (req) => {\n const collection = getRequestCollection(req)\n\n const filename = req.routeParams?.filename as string\n\n if (!collection.config.upload) {\n throw new APIError(\n `This collection is not an upload collection: ${collection.config.slug}`,\n httpStatus.BAD_REQUEST,\n )\n }\n\n const accessResult = (await checkFileAccess({\n collection,\n filename,\n req,\n }))!\n\n if (accessResult instanceof Response) {\n return accessResult\n }\n\n if (collection.config.upload.handlers?.length) {\n let customResponse: null | Response | void = null\n const headers = new Headers()\n\n for (const handler of collection.config.upload.handlers) {\n customResponse = await handler(req, {\n doc: accessResult,\n headers,\n params: {\n collection: collection.config.slug,\n filename,\n },\n })\n if (customResponse && customResponse instanceof Response) {\n break\n }\n }\n\n if (customResponse instanceof Response) {\n return customResponse\n }\n }\n\n const fileDir = collection.config.upload?.staticDir || collection.config.slug\n const filePath = path.resolve(`${fileDir}/${filename}`)\n let stats: Stats\n\n try {\n stats = await fsPromises.stat(filePath)\n } catch (err) {\n if ((err as { code?: string }).code === 'ENOENT') {\n req.payload.logger.error(\n `File ${filename} for collection ${collection.config.slug} is missing on the disk. Expected path: ${filePath}`,\n )\n\n // Omit going to the routeError handler by returning response instead of\n // throwing an error to cut down log noise. The response still matches what you get with APIError to not leak details to the user.\n return Response.json(\n {\n errors: [\n {\n message: 'Something went wrong.',\n },\n ],\n },\n {\n headers: headersWithCors({\n headers: new Headers(),\n req,\n }),\n status: 500,\n },\n )\n }\n\n throw err\n }\n\n const fileTypeResult = (await fileTypeFromFile(filePath)) || getFileTypeFallback(filePath)\n let mimeType = fileTypeResult.mime\n\n if (filePath.endsWith('.svg') && fileTypeResult.mime === 'application/xml') {\n mimeType = 'image/svg+xml'\n }\n\n // Parse Range header for byte range requests\n const rangeHeader = req.headers.get('range')\n const rangeResult = parseRangeHeader({\n fileSize: stats.size,\n rangeHeader,\n })\n\n if (rangeResult.type === 'invalid') {\n let headers = new Headers()\n headers.set('Content-Range', `bytes */${stats.size}`)\n headers = collection.config.upload?.modifyResponseHeaders\n ? collection.config.upload.modifyResponseHeaders({ headers }) || headers\n : headers\n\n return new Response(null, {\n headers: headersWithCors({\n headers,\n req,\n }),\n status: httpStatus.REQUESTED_RANGE_NOT_SATISFIABLE,\n })\n }\n\n let headers = new Headers()\n headers.set('Content-Type', mimeType)\n headers.set('Accept-Ranges', 'bytes')\n\n if (mimeType === 'image/svg+xml') {\n headers.set('Content-Security-Policy', \"script-src 'none'\")\n }\n\n let data: ReadableStream\n let status: number\n const isPartial = rangeResult.type === 'partial'\n const range = rangeResult.range\n\n if (isPartial && range) {\n const contentLength = range.end - range.start + 1\n headers.set('Content-Length', String(contentLength))\n headers.set('Content-Range', `bytes ${range.start}-${range.end}/${stats.size}`)\n data = streamFile({ filePath, options: { end: range.end, start: range.start } })\n status = httpStatus.PARTIAL_CONTENT\n } else {\n headers.set('Content-Length', String(stats.size))\n data = streamFile({ filePath })\n status = httpStatus.OK\n }\n\n headers = collection.config.upload?.modifyResponseHeaders\n ? collection.config.upload.modifyResponseHeaders({ headers }) || headers\n : headers\n\n return new Response(data, {\n headers: headersWithCors({\n headers,\n req,\n }),\n status,\n })\n}\n"],"names":["fileTypeFromFile","fsPromises","status","httpStatus","path","APIError","checkFileAccess","streamFile","getFileTypeFallback","parseRangeHeader","getRequestCollection","headersWithCors","getFileHandler","req","collection","filename","routeParams","config","upload","slug","BAD_REQUEST","accessResult","Response","handlers","length","customResponse","headers","Headers","handler","doc","params","fileDir","staticDir","filePath","resolve","stats","stat","err","code","payload","logger","error","json","errors","message","fileTypeResult","mimeType","mime","endsWith","rangeHeader","
|
|
1
|
+
{"version":3,"sources":["../../../src/uploads/endpoints/getFile.ts"],"sourcesContent":["import type { Stats } from 'fs'\n\nimport { fileTypeFromFile } from 'file-type'\nimport fsPromises from 'fs/promises'\nimport { status as httpStatus } from 'http-status'\nimport path from 'path'\n\nimport type { PayloadHandler } from '../../config/types.js'\n\nimport { APIError } from '../../errors/APIError.js'\nimport { checkFileAccess } from '../../uploads/checkFileAccess.js'\nimport { streamFile } from '../../uploads/fetchAPI-stream-file/index.js'\nimport { getFileTypeFallback } from '../../uploads/getFileTypeFallback.js'\nimport { parseRangeHeader } from '../../uploads/parseRangeHeader.js'\nimport { getRequestCollection } from '../../utilities/getRequestEntity.js'\nimport { headersWithCors } from '../../utilities/headersWithCors.js'\n\nexport const getFileHandler: PayloadHandler = async (req) => {\n const collection = getRequestCollection(req)\n\n const filename = req.routeParams?.filename as string\n const prefix = req.searchParams?.get('prefix') ?? undefined\n\n if (!collection.config.upload) {\n throw new APIError(\n `This collection is not an upload collection: ${collection.config.slug}`,\n httpStatus.BAD_REQUEST,\n )\n }\n\n const accessResult = (await checkFileAccess({\n collection,\n filename,\n prefix,\n req,\n }))!\n\n if (accessResult instanceof Response) {\n return accessResult\n }\n\n if (collection.config.upload.handlers?.length) {\n let customResponse: null | Response | void = null\n const headers = new Headers()\n\n for (const handler of collection.config.upload.handlers) {\n customResponse = await handler(req, {\n doc: accessResult,\n headers,\n params: {\n collection: collection.config.slug,\n filename,\n prefix,\n },\n })\n if (customResponse && customResponse instanceof Response) {\n break\n }\n }\n\n if (customResponse instanceof Response) {\n return customResponse\n }\n }\n\n const fileDir = collection.config.upload?.staticDir || collection.config.slug\n const filePath = path.resolve(`${fileDir}/${filename}`)\n let stats: Stats\n\n try {\n stats = await fsPromises.stat(filePath)\n } catch (err) {\n if ((err as { code?: string }).code === 'ENOENT') {\n req.payload.logger.error(\n `File ${filename} for collection ${collection.config.slug} is missing on the disk. Expected path: ${filePath}`,\n )\n\n // Omit going to the routeError handler by returning response instead of\n // throwing an error to cut down log noise. The response still matches what you get with APIError to not leak details to the user.\n return Response.json(\n {\n errors: [\n {\n message: 'Something went wrong.',\n },\n ],\n },\n {\n headers: headersWithCors({\n headers: new Headers(),\n req,\n }),\n status: 500,\n },\n )\n }\n\n throw err\n }\n\n const fileTypeResult = (await fileTypeFromFile(filePath)) || getFileTypeFallback(filePath)\n let mimeType = fileTypeResult.mime\n\n if (filePath.endsWith('.svg') && fileTypeResult.mime === 'application/xml') {\n mimeType = 'image/svg+xml'\n }\n\n // Parse Range header for byte range requests\n const rangeHeader = req.headers.get('range')\n const rangeResult = parseRangeHeader({\n fileSize: stats.size,\n rangeHeader,\n })\n\n if (rangeResult.type === 'invalid') {\n let headers = new Headers()\n headers.set('Content-Range', `bytes */${stats.size}`)\n headers = collection.config.upload?.modifyResponseHeaders\n ? collection.config.upload.modifyResponseHeaders({ headers }) || headers\n : headers\n\n return new Response(null, {\n headers: headersWithCors({\n headers,\n req,\n }),\n status: httpStatus.REQUESTED_RANGE_NOT_SATISFIABLE,\n })\n }\n\n let headers = new Headers()\n headers.set('Content-Type', mimeType)\n headers.set('Accept-Ranges', 'bytes')\n\n if (mimeType === 'image/svg+xml') {\n headers.set('Content-Security-Policy', \"script-src 'none'\")\n }\n\n let data: ReadableStream\n let status: number\n const isPartial = rangeResult.type === 'partial'\n const range = rangeResult.range\n\n if (isPartial && range) {\n const contentLength = range.end - range.start + 1\n headers.set('Content-Length', String(contentLength))\n headers.set('Content-Range', `bytes ${range.start}-${range.end}/${stats.size}`)\n data = streamFile({ filePath, options: { end: range.end, start: range.start } })\n status = httpStatus.PARTIAL_CONTENT\n } else {\n headers.set('Content-Length', String(stats.size))\n data = streamFile({ filePath })\n status = httpStatus.OK\n }\n\n headers = collection.config.upload?.modifyResponseHeaders\n ? collection.config.upload.modifyResponseHeaders({ headers }) || headers\n : headers\n\n return new Response(data, {\n headers: headersWithCors({\n headers,\n req,\n }),\n status,\n })\n}\n"],"names":["fileTypeFromFile","fsPromises","status","httpStatus","path","APIError","checkFileAccess","streamFile","getFileTypeFallback","parseRangeHeader","getRequestCollection","headersWithCors","getFileHandler","req","collection","filename","routeParams","prefix","searchParams","get","undefined","config","upload","slug","BAD_REQUEST","accessResult","Response","handlers","length","customResponse","headers","Headers","handler","doc","params","fileDir","staticDir","filePath","resolve","stats","stat","err","code","payload","logger","error","json","errors","message","fileTypeResult","mimeType","mime","endsWith","rangeHeader","rangeResult","fileSize","size","type","set","modifyResponseHeaders","REQUESTED_RANGE_NOT_SATISFIABLE","data","isPartial","range","contentLength","end","start","String","options","PARTIAL_CONTENT","OK"],"mappings":"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"}
|
package/dist/uploads/types.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/uploads/types.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/uploads/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/uploads/types.ts"],"sourcesContent":["import type { ResizeOptions, Sharp, SharpOptions } from 'sharp'\n\nimport type { CollectionConfig, TypeWithID } from '../collections/config/types.js'\nimport type { PayloadComponent } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\nimport type { WithMetadata } from './optionallyAppendMetadata.js'\n\nexport type FileSize = {\n filename: null | string\n filesize: null | number\n height: null | number\n mimeType: null | string\n url?: null | string // TODO V4: make non-optional\n width: null | number\n}\n\n// TODO: deprecate in Payload v4.\n/**\n * FileSizeImproved is a more precise type, and will replace FileSize in Payload v4.\n * This type is for internal use only as it will be deprecated in the future.\n * @internal\n */\nexport type FileSizeImproved = {\n url: null | string\n} & FileSize\n\nexport type FileSizes = {\n [size: string]: FileSize\n}\n\nexport type FileData = {\n filename: string\n filesize: number\n focalX?: number\n focalY?: number\n height: number\n mimeType: string\n sizes: FileSizes\n tempFilePath?: string\n url?: string\n width: number\n}\n\nexport type ProbedImageSize = {\n height: number\n width: number\n}\n\n/**\n * Params sent to the sharp `toFormat()` function\n * @link https://sharp.pixelplumbing.com/api-output#toformat\n */\nexport type ImageUploadFormatOptions = {\n format: Parameters<Sharp['toFormat']>[0]\n options?: Parameters<Sharp['toFormat']>[1]\n}\n\n/**\n * Params sent to the sharp trim() function\n * @link https://sharp.pixelplumbing.com/api-resize#trim\n */\nexport type ImageUploadTrimOptions = Parameters<Sharp['trim']>[0]\n\nexport type GenerateImageName = (args: {\n extension: string\n height: number\n originalName: string\n sizeName: string\n width: number\n}) => string\n\nexport type ImageSize = {\n /**\n * Admin UI options that control how this image size appears in list views.\n *\n * NOTE: In Payload v4, these options (`disableGroupBy`, `disableListColumn` and `disableListFilter`)\n * should default to `true` so image size subfields are hidden from list columns\n * and filters by default, reducing noise in the admin UI.\n */\n admin?: {\n /**\n * If set to true, this image size will not be available\n * as a selectable groupBy option in the collection list view.\n * @default false\n */\n disableGroupBy?: boolean\n /**\n * If set to true, this image size will not be available\n * as a selectable column in the collection list view.\n * @default false\n */\n disableListColumn?: boolean\n /**\n * If set to true, this image size will not be available\n * as a filter option in the collection list view.\n * @default false\n */\n disableListFilter?: boolean\n }\n /**\n * @deprecated prefer position\n */\n crop?: string // comes from sharp package\n formatOptions?: ImageUploadFormatOptions\n /**\n * Generate a custom name for the file of this image size.\n */\n generateImageName?: GenerateImageName\n name: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * When an uploaded image is smaller than the defined image size, we have 3 options:\n *\n * `undefined | false | true`\n *\n * 1. `undefined` [default]: uploading images with smaller width AND height than the image size will return null\n * 2. `false`: always enlarge images to the image size\n * 3. `true`: if the image is smaller than the image size, return the original image\n */\n withoutEnlargement?: ResizeOptions['withoutEnlargement']\n} & Omit<ResizeOptions, 'withoutEnlargement'>\n\nexport type GetAdminThumbnail = (args: { doc: Record<string, unknown> }) => false | null | string\n\nexport type AllowList = Array<{\n hostname: string\n pathname?: string\n port?: string\n protocol?: 'http' | 'https'\n search?: string\n}>\n\nexport type FileAllowList = Array<{\n extensions: string[]\n mimeType: string\n}>\n\ntype Admin = {\n components?: {\n /**\n * The Controls component to extend the upload controls in the admin panel.\n */\n controls?: PayloadComponent[]\n }\n}\n\nexport type UploadConfig = {\n /**\n * The adapter name to use for uploads. Used for storage adapter telemetry.\n * @default undefined\n */\n adapter?: string\n /**\n * The admin configuration for the upload field.\n */\n admin?: Admin\n /**\n * Represents an admin thumbnail, which can be either a React component or a string.\n * - If a string, it should be one of the image size names.\n * - A function that generates a fully qualified URL for the thumbnail, receives the doc as the only argument.\n **/\n adminThumbnail?: GetAdminThumbnail | string\n /**\n * Allow restricted file types known to be problematic.\n * - If set to `true`, it will allow all file types.\n * - If set to `false`, it will not allow file types and extensions known to be problematic.\n * - This setting is overriden by the `mimeTypes` option.\n * @default false\n */\n allowRestrictedFileTypes?: boolean\n /**\n * Enables bulk upload of files from the list view.\n * @default true\n */\n bulkUpload?: boolean\n /**\n * Appends a cache tag to the image URL when fetching the thumbnail in the admin panel. It may be desirable to disable this when hosting via CDNs with strict parameters.\n *\n * @default true\n */\n cacheTags?: boolean\n /**\n * Sharp constructor options to be passed to the uploaded file.\n * @link https://sharp.pixelplumbing.com/api-constructor/#sharp\n */\n constructorOptions?: SharpOptions\n /**\n * Enables cropping of images.\n * @default true\n */\n crop?: boolean\n /**\n * Disable the ability to save files to disk.\n * @default false\n */\n disableLocalStorage?: boolean\n /**\n * Enable displaying preview of the uploaded file in Upload fields related to this Collection.\n * Can be locally overridden by `displayPreview` option in Upload field.\n * @default false\n */\n displayPreview?: boolean\n /**\n *\n * Accepts existing headers and returns the headers after filtering or modifying.\n * If using this option, you should handle the removal of any sensitive cookies\n * (like payload-prefixed cookies) to prevent leaking session information to external\n * services. By default, Payload automatically filters out payload-prefixed cookies\n * when this option is NOT defined.\n *\n * Useful for adding custom headers to fetch from external providers.\n * @default undefined\n */\n externalFileHeaderFilter?: (headers: Record<string, string>) => Record<string, string>\n /**\n * Field slugs to use for a compound index instead of the default filename index.\n */\n filenameCompoundIndex?: string[]\n /**\n * Require files to be uploaded when creating a document.\n * @default true\n */\n filesRequiredOnCreate?: boolean\n /**\n * Enables focal point positioning for image manipulation.\n * @default false\n */\n focalPoint?: boolean\n /**\n * Format options for the uploaded file. Formatting image sizes needs to be done within each formatOptions individually.\n */\n formatOptions?: ImageUploadFormatOptions\n /**\n * Custom handlers to run when a file is fetched.\n *\n * - If a handler returns a Response, the response will be sent to the client and no further handlers will be run.\n * - If a handler returns null, the next handler will be run.\n * - If no handlers return a response the file will be returned by default.\n *\n * @link https://sharp.pixelplumbing.com/api-output/#toformat\n * @default undefined\n */\n handlers?: ((\n req: PayloadRequest,\n args: {\n doc: TypeWithID\n headers?: Headers\n params: { clientUploadContext?: unknown; collection: string; filename: string }\n },\n ) => Promise<Response> | Promise<void> | Response | void)[]\n /**\n * Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation.\n */\n hideFileInputOnCreate?: boolean\n /**\n * Set to `true` to prevent the admin UI having a way to remove an existing file while editing.\n */\n hideRemoveFile?: boolean\n imageSizes?: ImageSize[]\n /**\n * Restrict mimeTypes in the file picker. Array of valid mime types or mimetype wildcards\n * @example ['image/*', 'application/pdf']\n * @default undefined\n */\n mimeTypes?: string[]\n /**\n * Ability to modify the response headers fetching a file.\n * @default undefined\n */\n modifyResponseHeaders?: ({ headers }: { headers: Headers }) => Headers | void\n /**\n * Controls the behavior of pasting/uploading files from URLs.\n * If set to `false`, fetching from remote URLs is disabled.\n * If an `allowList` is provided, server-side fetching will be enabled for specified URLs.\n *\n * @default true (client-side fetching enabled)\n */\n pasteURL?:\n | {\n allowList: AllowList\n }\n | false\n /**\n * Sharp resize options for the original image.\n * @link https://sharp.pixelplumbing.com/api-resize#resize\n * @default undefined\n */\n resizeOptions?: ResizeOptions\n /**\n * Skip safe fetch when using server-side fetching for external files from these URLs.\n * @default false\n */\n skipSafeFetch?: AllowList | boolean\n /**\n * The directory to serve static files from. Defaults to collection slug.\n * @default undefined\n */\n staticDir?: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * Optionally append metadata to the image during processing.\n *\n * Can be a boolean or a function.\n *\n * If true, metadata will be appended to the image.\n * If false, no metadata will be appended.\n * If a function, it will receive an object containing the metadata and should return a boolean indicating whether to append the metadata.\n * @default false\n */\n withMetadata?: WithMetadata\n}\nexport type checkFileRestrictionsParams = {\n collection: CollectionConfig\n file: File\n req: PayloadRequest\n}\n\nexport type SanitizedUploadConfig = {\n staticDir: UploadConfig['staticDir']\n} & UploadConfig\n\nexport type File = {\n /**\n * The buffer of the file.\n */\n data: Buffer\n /**\n * The mimetype of the file.\n */\n mimetype: string\n /**\n * The name of the file.\n */\n name: string\n /**\n * The size of the file in bytes.\n */\n size: number\n}\n\nexport type FileToSave = {\n /**\n * The buffer of the file.\n */\n buffer: Buffer\n /**\n * The path to save the file.\n */\n path: string\n}\n\ntype Crop = {\n height: number\n unit: '%' | 'px'\n width: number\n x: number\n y: number\n}\n\nexport type FocalPoint = {\n x: number\n y: number\n}\n\nexport type UploadEdits = {\n crop?: Crop\n focalPoint?: FocalPoint\n heightInPixels?: number\n widthInPixels?: number\n}\n"],"names":[],"mappings":"AA4WA,WAKC"}
|
|
1
|
+
{"version":3,"sources":["../../src/uploads/types.ts"],"sourcesContent":["import type { ResizeOptions, Sharp, SharpOptions } from 'sharp'\n\nimport type { CollectionConfig, TypeWithID } from '../collections/config/types.js'\nimport type { PayloadComponent } from '../config/types.js'\nimport type { PayloadRequest } from '../types/index.js'\nimport type { WithMetadata } from './optionallyAppendMetadata.js'\n\nexport type FileSize = {\n filename: null | string\n filesize: null | number\n height: null | number\n mimeType: null | string\n url?: null | string // TODO V4: make non-optional\n width: null | number\n}\n\n// TODO: deprecate in Payload v4.\n/**\n * FileSizeImproved is a more precise type, and will replace FileSize in Payload v4.\n * This type is for internal use only as it will be deprecated in the future.\n * @internal\n */\nexport type FileSizeImproved = {\n url: null | string\n} & FileSize\n\nexport type FileSizes = {\n [size: string]: FileSize\n}\n\nexport type FileData = {\n filename: string\n filesize: number\n focalX?: number\n focalY?: number\n height: number\n mimeType: string\n sizes: FileSizes\n tempFilePath?: string\n url?: string\n width: number\n}\n\nexport type ProbedImageSize = {\n height: number\n width: number\n}\n\n/**\n * Params sent to the sharp `toFormat()` function\n * @link https://sharp.pixelplumbing.com/api-output#toformat\n */\nexport type ImageUploadFormatOptions = {\n format: Parameters<Sharp['toFormat']>[0]\n options?: Parameters<Sharp['toFormat']>[1]\n}\n\n/**\n * Params sent to the sharp trim() function\n * @link https://sharp.pixelplumbing.com/api-resize#trim\n */\nexport type ImageUploadTrimOptions = Parameters<Sharp['trim']>[0]\n\nexport type GenerateImageName = (args: {\n extension: string\n height: number\n originalName: string\n sizeName: string\n width: number\n}) => string\n\nexport type ImageSize = {\n /**\n * Admin UI options that control how this image size appears in list views.\n *\n * NOTE: In Payload v4, these options (`disableGroupBy`, `disableListColumn` and `disableListFilter`)\n * should default to `true` so image size subfields are hidden from list columns\n * and filters by default, reducing noise in the admin UI.\n */\n admin?: {\n /**\n * If set to true, this image size will not be available\n * as a selectable groupBy option in the collection list view.\n * @default false\n */\n disableGroupBy?: boolean\n /**\n * If set to true, this image size will not be available\n * as a selectable column in the collection list view.\n * @default false\n */\n disableListColumn?: boolean\n /**\n * If set to true, this image size will not be available\n * as a filter option in the collection list view.\n * @default false\n */\n disableListFilter?: boolean\n }\n /**\n * @deprecated prefer position\n */\n crop?: string // comes from sharp package\n formatOptions?: ImageUploadFormatOptions\n /**\n * Generate a custom name for the file of this image size.\n */\n generateImageName?: GenerateImageName\n name: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * When an uploaded image is smaller than the defined image size, we have 3 options:\n *\n * `undefined | false | true`\n *\n * 1. `undefined` [default]: uploading images with smaller width AND height than the image size will return null\n * 2. `false`: always enlarge images to the image size\n * 3. `true`: if the image is smaller than the image size, return the original image\n */\n withoutEnlargement?: ResizeOptions['withoutEnlargement']\n} & Omit<ResizeOptions, 'withoutEnlargement'>\n\nexport type GetAdminThumbnail = (args: { doc: Record<string, unknown> }) => false | null | string\n\nexport type AllowList = Array<{\n hostname: string\n pathname?: string\n port?: string\n protocol?: 'http' | 'https'\n search?: string\n}>\n\nexport type FileAllowList = Array<{\n extensions: string[]\n mimeType: string\n}>\n\ntype Admin = {\n components?: {\n /**\n * The Controls component to extend the upload controls in the admin panel.\n */\n controls?: PayloadComponent[]\n }\n}\n\nexport type UploadConfig = {\n /**\n * The adapter name to use for uploads. Used for storage adapter telemetry.\n * @default undefined\n */\n adapter?: string\n /**\n * The admin configuration for the upload field.\n */\n admin?: Admin\n /**\n * Represents an admin thumbnail, which can be either a React component or a string.\n * - If a string, it should be one of the image size names.\n * - A function that generates a fully qualified URL for the thumbnail, receives the doc as the only argument.\n **/\n adminThumbnail?: GetAdminThumbnail | string\n /**\n * Allow restricted file types known to be problematic.\n * - If set to `true`, it will allow all file types.\n * - If set to `false`, it will not allow file types and extensions known to be problematic.\n * - This setting is overriden by the `mimeTypes` option.\n * @default false\n */\n allowRestrictedFileTypes?: boolean\n /**\n * Enables bulk upload of files from the list view.\n * @default true\n */\n bulkUpload?: boolean\n /**\n * Appends a cache tag to the image URL when fetching the thumbnail in the admin panel. It may be desirable to disable this when hosting via CDNs with strict parameters.\n *\n * @default true\n */\n cacheTags?: boolean\n /**\n * Sharp constructor options to be passed to the uploaded file.\n * @link https://sharp.pixelplumbing.com/api-constructor/#sharp\n */\n constructorOptions?: SharpOptions\n /**\n * Enables cropping of images.\n * @default true\n */\n crop?: boolean\n /**\n * Disable the ability to save files to disk.\n * @default false\n */\n disableLocalStorage?: boolean\n /**\n * Enable displaying preview of the uploaded file in Upload fields related to this Collection.\n * Can be locally overridden by `displayPreview` option in Upload field.\n * @default false\n */\n displayPreview?: boolean\n /**\n *\n * Accepts existing headers and returns the headers after filtering or modifying.\n * If using this option, you should handle the removal of any sensitive cookies\n * (like payload-prefixed cookies) to prevent leaking session information to external\n * services. By default, Payload automatically filters out payload-prefixed cookies\n * when this option is NOT defined.\n *\n * Useful for adding custom headers to fetch from external providers.\n * @default undefined\n */\n externalFileHeaderFilter?: (headers: Record<string, string>) => Record<string, string>\n /**\n * Field slugs to use for a compound index instead of the default filename index.\n */\n filenameCompoundIndex?: string[]\n /**\n * Require files to be uploaded when creating a document.\n * @default true\n */\n filesRequiredOnCreate?: boolean\n /**\n * Enables focal point positioning for image manipulation.\n * @default false\n */\n focalPoint?: boolean\n /**\n * Format options for the uploaded file. Formatting image sizes needs to be done within each formatOptions individually.\n */\n formatOptions?: ImageUploadFormatOptions\n /**\n * Custom handlers to run when a file is fetched.\n *\n * - If a handler returns a Response, the response will be sent to the client and no further handlers will be run.\n * - If a handler returns null, the next handler will be run.\n * - If no handlers return a response the file will be returned by default.\n *\n * @link https://sharp.pixelplumbing.com/api-output/#toformat\n * @default undefined\n */\n handlers?: ((\n req: PayloadRequest,\n args: {\n doc: TypeWithID\n headers?: Headers\n params: {\n clientUploadContext?: unknown\n collection: string\n filename: string\n prefix?: string\n }\n },\n ) => Promise<Response> | Promise<void> | Response | void)[]\n /**\n * Set to `true` to prevent the admin UI from showing file inputs during document creation, useful for programmatic file generation.\n */\n hideFileInputOnCreate?: boolean\n /**\n * Set to `true` to prevent the admin UI having a way to remove an existing file while editing.\n */\n hideRemoveFile?: boolean\n imageSizes?: ImageSize[]\n /**\n * Restrict mimeTypes in the file picker. Array of valid mime types or mimetype wildcards\n * @example ['image/*', 'application/pdf']\n * @default undefined\n */\n mimeTypes?: string[]\n /**\n * Ability to modify the response headers fetching a file.\n * @default undefined\n */\n modifyResponseHeaders?: ({ headers }: { headers: Headers }) => Headers | void\n /**\n * Controls the behavior of pasting/uploading files from URLs.\n * If set to `false`, fetching from remote URLs is disabled.\n * If an `allowList` is provided, server-side fetching will be enabled for specified URLs.\n *\n * @default true (client-side fetching enabled)\n */\n pasteURL?:\n | {\n allowList: AllowList\n }\n | false\n /**\n * Sharp resize options for the original image.\n * @link https://sharp.pixelplumbing.com/api-resize#resize\n * @default undefined\n */\n resizeOptions?: ResizeOptions\n /**\n * Skip safe fetch when using server-side fetching for external files from these URLs.\n * @default false\n */\n skipSafeFetch?: AllowList | boolean\n /**\n * The directory to serve static files from. Defaults to collection slug.\n * @default undefined\n */\n staticDir?: string\n trimOptions?: ImageUploadTrimOptions\n /**\n * Optionally append metadata to the image during processing.\n *\n * Can be a boolean or a function.\n *\n * If true, metadata will be appended to the image.\n * If false, no metadata will be appended.\n * If a function, it will receive an object containing the metadata and should return a boolean indicating whether to append the metadata.\n * @default false\n */\n withMetadata?: WithMetadata\n}\nexport type checkFileRestrictionsParams = {\n collection: CollectionConfig\n file: File\n req: PayloadRequest\n}\n\nexport type SanitizedUploadConfig = {\n staticDir: UploadConfig['staticDir']\n} & UploadConfig\n\nexport type File = {\n /**\n * The buffer of the file.\n */\n data: Buffer\n /**\n * The mimetype of the file.\n */\n mimetype: string\n /**\n * The name of the file.\n */\n name: string\n /**\n * The size of the file in bytes.\n */\n size: number\n}\n\nexport type FileToSave = {\n /**\n * The buffer of the file.\n */\n buffer: Buffer\n /**\n * The path to save the file.\n */\n path: string\n}\n\ntype Crop = {\n height: number\n unit: '%' | 'px'\n width: number\n x: number\n y: number\n}\n\nexport type FocalPoint = {\n x: number\n y: number\n}\n\nexport type UploadEdits = {\n crop?: Crop\n focalPoint?: FocalPoint\n heightInPixels?: number\n widthInPixels?: number\n}\n"],"names":[],"mappings":"AAiXA,WAKC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payload",
|
|
3
|
-
"version": "3.79.0-internal.
|
|
3
|
+
"version": "3.79.0-internal.de5df42",
|
|
4
4
|
"description": "Node, React, Headless CMS and Application Framework built on Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"admin panel",
|
|
@@ -115,7 +115,7 @@
|
|
|
115
115
|
"undici": "7.18.2",
|
|
116
116
|
"uuid": "10.0.0",
|
|
117
117
|
"ws": "^8.16.0",
|
|
118
|
-
"@payloadcms/translations": "3.79.0-internal.
|
|
118
|
+
"@payloadcms/translations": "3.79.0-internal.de5df42"
|
|
119
119
|
},
|
|
120
120
|
"devDependencies": {
|
|
121
121
|
"@hyrious/esbuild-plugin-commonjs": "0.2.6",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { Where } from '../../../types/index.js';
|
|
2
|
-
/**
|
|
3
|
-
* Builds a `where` fragment that scopes order operations to docs sharing the
|
|
4
|
-
* same join `on` field value.
|
|
5
|
-
*/
|
|
6
|
-
export declare function buildJoinScopeWhere(args: {
|
|
7
|
-
joinOnFieldPath: string;
|
|
8
|
-
scopeValue: unknown;
|
|
9
|
-
}): null | Where;
|
|
10
|
-
//# sourceMappingURL=buildJoinScopeWhere.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"buildJoinScopeWhere.d.ts","sourceRoot":"","sources":["../../../../src/config/orderable/utils/buildJoinScopeWhere.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,eAAe,EAAE,MAAM,CAAA;IACvB,UAAU,EAAE,OAAO,CAAA;CACpB,GAAG,IAAI,GAAG,KAAK,CAgDf"}
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds a `where` fragment that scopes order operations to docs sharing the
|
|
3
|
-
* same join `on` field value.
|
|
4
|
-
*/ export function buildJoinScopeWhere(args) {
|
|
5
|
-
const { joinOnFieldPath, scopeValue } = args;
|
|
6
|
-
if (typeof scopeValue === 'undefined') {
|
|
7
|
-
return null;
|
|
8
|
-
}
|
|
9
|
-
if (Array.isArray(scopeValue)) {
|
|
10
|
-
return buildJoinScopeWhere({
|
|
11
|
-
joinOnFieldPath,
|
|
12
|
-
scopeValue: scopeValue[0]
|
|
13
|
-
});
|
|
14
|
-
}
|
|
15
|
-
if (scopeValue && typeof scopeValue === 'object' && 'relationTo' in scopeValue && 'value' in scopeValue) {
|
|
16
|
-
const relation = scopeValue.relationTo;
|
|
17
|
-
const value = scopeValue.value;
|
|
18
|
-
if (typeof relation === 'undefined' || typeof value === 'undefined') {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
return {
|
|
22
|
-
and: [
|
|
23
|
-
{
|
|
24
|
-
[`${joinOnFieldPath}.relationTo`]: {
|
|
25
|
-
equals: relation
|
|
26
|
-
}
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
[`${joinOnFieldPath}.value`]: {
|
|
30
|
-
equals: value
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
]
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
return {
|
|
37
|
-
[joinOnFieldPath]: {
|
|
38
|
-
equals: scopeValue
|
|
39
|
-
}
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
//# sourceMappingURL=buildJoinScopeWhere.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/config/orderable/utils/buildJoinScopeWhere.ts"],"sourcesContent":["import type { Where } from '../../../types/index.js'\n\n/**\n * Builds a `where` fragment that scopes order operations to docs sharing the\n * same join `on` field value.\n */\nexport function buildJoinScopeWhere(args: {\n joinOnFieldPath: string\n scopeValue: unknown\n}): null | Where {\n const { joinOnFieldPath, scopeValue } = args\n\n if (typeof scopeValue === 'undefined') {\n return null\n }\n\n if (Array.isArray(scopeValue)) {\n return buildJoinScopeWhere({\n joinOnFieldPath,\n scopeValue: scopeValue[0],\n })\n }\n\n if (\n scopeValue &&\n typeof scopeValue === 'object' &&\n 'relationTo' in scopeValue &&\n 'value' in scopeValue\n ) {\n const relation = (scopeValue as { relationTo?: unknown }).relationTo\n const value = (scopeValue as { value?: unknown }).value\n\n if (typeof relation === 'undefined' || typeof value === 'undefined') {\n return null\n }\n\n return {\n and: [\n {\n [`${joinOnFieldPath}.relationTo`]: {\n equals: relation,\n },\n },\n {\n [`${joinOnFieldPath}.value`]: {\n equals: value,\n },\n },\n ],\n }\n }\n\n return {\n [joinOnFieldPath]: {\n equals: scopeValue,\n },\n }\n}\n"],"names":["buildJoinScopeWhere","args","joinOnFieldPath","scopeValue","Array","isArray","relation","relationTo","value","and","equals"],"mappings":"AAEA;;;CAGC,GACD,OAAO,SAASA,oBAAoBC,IAGnC;IACC,MAAM,EAAEC,eAAe,EAAEC,UAAU,EAAE,GAAGF;IAExC,IAAI,OAAOE,eAAe,aAAa;QACrC,OAAO;IACT;IAEA,IAAIC,MAAMC,OAAO,CAACF,aAAa;QAC7B,OAAOH,oBAAoB;YACzBE;YACAC,YAAYA,UAAU,CAAC,EAAE;QAC3B;IACF;IAEA,IACEA,cACA,OAAOA,eAAe,YACtB,gBAAgBA,cAChB,WAAWA,YACX;QACA,MAAMG,WAAW,AAACH,WAAwCI,UAAU;QACpE,MAAMC,QAAQ,AAACL,WAAmCK,KAAK;QAEvD,IAAI,OAAOF,aAAa,eAAe,OAAOE,UAAU,aAAa;YACnE,OAAO;QACT;QAEA,OAAO;YACLC,KAAK;gBACH;oBACE,CAAC,GAAGP,gBAAgB,WAAW,CAAC,CAAC,EAAE;wBACjCQ,QAAQJ;oBACV;gBACF;gBACA;oBACE,CAAC,GAAGJ,gBAAgB,MAAM,CAAC,CAAC,EAAE;wBAC5BQ,QAAQF;oBACV;gBACF;aACD;QACH;IACF;IAEA,OAAO;QACL,CAACN,gBAAgB,EAAE;YACjBQ,QAAQP;QACV;IACF;AACF"}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { PayloadHandler } from '../../types.js';
|
|
2
|
-
import { buildJoinScopeWhere } from './buildJoinScopeWhere.js';
|
|
3
|
-
/**
|
|
4
|
-
* Resolves join scope and target document context for reorder operations.
|
|
5
|
-
*/
|
|
6
|
-
export declare function getJoinScopeContext(args: {
|
|
7
|
-
collectionSlug: string;
|
|
8
|
-
joinFieldPathsByCollection: Map<string, Map<string, string>>;
|
|
9
|
-
orderableFieldName: string;
|
|
10
|
-
req: Parameters<PayloadHandler>[0];
|
|
11
|
-
target: unknown;
|
|
12
|
-
}): Promise<{
|
|
13
|
-
joinScopeWhere: ReturnType<typeof buildJoinScopeWhere>;
|
|
14
|
-
targetDoc: null | Record<string, unknown>;
|
|
15
|
-
}>;
|
|
16
|
-
//# sourceMappingURL=getJoinScopeContext.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getJoinScopeContext.d.ts","sourceRoot":"","sources":["../../../../src/config/orderable/utils/getJoinScopeContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAG9D;;GAEG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE;IAC9C,cAAc,EAAE,MAAM,CAAA;IACtB,0BAA0B,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC5D,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;IAClC,MAAM,EAAE,OAAO,CAAA;CAChB,GAAG,OAAO,CAAC;IACV,cAAc,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAA;IACtD,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC1C,CAAC,CA2CD"}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { buildJoinScopeWhere } from './buildJoinScopeWhere.js';
|
|
2
|
-
import { getValueAtPath } from './getValueAtPath.js';
|
|
3
|
-
/**
|
|
4
|
-
* Resolves join scope and target document context for reorder operations.
|
|
5
|
-
*/ export async function getJoinScopeContext(args) {
|
|
6
|
-
const { collectionSlug, joinFieldPathsByCollection, orderableFieldName, req, target } = args;
|
|
7
|
-
const joinOnFieldPath = joinFieldPathsByCollection.get(collectionSlug)?.get(orderableFieldName);
|
|
8
|
-
let targetDoc = null;
|
|
9
|
-
if (typeof target === 'object' && target && 'id' in target && (joinOnFieldPath || 'key' in target && target.key === 'pending')) {
|
|
10
|
-
const targetID = target.id;
|
|
11
|
-
if (typeof targetID === 'number' || typeof targetID === 'string') {
|
|
12
|
-
targetDoc = await req.payload.findByID({
|
|
13
|
-
id: targetID,
|
|
14
|
-
collection: collectionSlug,
|
|
15
|
-
depth: 0,
|
|
16
|
-
select: {
|
|
17
|
-
...joinOnFieldPath ? {
|
|
18
|
-
[joinOnFieldPath]: true
|
|
19
|
-
} : {},
|
|
20
|
-
[orderableFieldName]: true
|
|
21
|
-
}
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
if (!joinOnFieldPath) {
|
|
26
|
-
return {
|
|
27
|
-
joinScopeWhere: null,
|
|
28
|
-
targetDoc
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
const joinScopeValue = getValueAtPath(targetDoc, joinOnFieldPath);
|
|
32
|
-
return {
|
|
33
|
-
joinScopeWhere: buildJoinScopeWhere({
|
|
34
|
-
joinOnFieldPath,
|
|
35
|
-
scopeValue: joinScopeValue
|
|
36
|
-
}),
|
|
37
|
-
targetDoc
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
//# sourceMappingURL=getJoinScopeContext.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/config/orderable/utils/getJoinScopeContext.ts"],"sourcesContent":["import type { PayloadHandler } from '../../types.js'\n\nimport { buildJoinScopeWhere } from './buildJoinScopeWhere.js'\nimport { getValueAtPath } from './getValueAtPath.js'\n\n/**\n * Resolves join scope and target document context for reorder operations.\n */\nexport async function getJoinScopeContext(args: {\n collectionSlug: string\n joinFieldPathsByCollection: Map<string, Map<string, string>>\n orderableFieldName: string\n req: Parameters<PayloadHandler>[0]\n target: unknown\n}): Promise<{\n joinScopeWhere: ReturnType<typeof buildJoinScopeWhere>\n targetDoc: null | Record<string, unknown>\n}> {\n const { collectionSlug, joinFieldPathsByCollection, orderableFieldName, req, target } = args\n\n const joinOnFieldPath = joinFieldPathsByCollection.get(collectionSlug)?.get(orderableFieldName)\n let targetDoc: null | Record<string, unknown> = null\n\n if (\n typeof target === 'object' &&\n target &&\n 'id' in target &&\n (joinOnFieldPath || ('key' in target && target.key === 'pending'))\n ) {\n const targetID = (target as { id?: unknown }).id\n\n if (typeof targetID === 'number' || typeof targetID === 'string') {\n targetDoc = await req.payload.findByID({\n id: targetID,\n collection: collectionSlug,\n depth: 0,\n select: {\n ...(joinOnFieldPath ? { [joinOnFieldPath]: true } : {}),\n [orderableFieldName]: true,\n },\n })\n }\n }\n\n if (!joinOnFieldPath) {\n return {\n joinScopeWhere: null,\n targetDoc,\n }\n }\n\n const joinScopeValue = getValueAtPath(targetDoc, joinOnFieldPath)\n\n return {\n joinScopeWhere: buildJoinScopeWhere({\n joinOnFieldPath,\n scopeValue: joinScopeValue,\n }),\n targetDoc,\n }\n}\n"],"names":["buildJoinScopeWhere","getValueAtPath","getJoinScopeContext","args","collectionSlug","joinFieldPathsByCollection","orderableFieldName","req","target","joinOnFieldPath","get","targetDoc","key","targetID","id","payload","findByID","collection","depth","select","joinScopeWhere","joinScopeValue","scopeValue"],"mappings":"AAEA,SAASA,mBAAmB,QAAQ,2BAA0B;AAC9D,SAASC,cAAc,QAAQ,sBAAqB;AAEpD;;CAEC,GACD,OAAO,eAAeC,oBAAoBC,IAMzC;IAIC,MAAM,EAAEC,cAAc,EAAEC,0BAA0B,EAAEC,kBAAkB,EAAEC,GAAG,EAAEC,MAAM,EAAE,GAAGL;IAExF,MAAMM,kBAAkBJ,2BAA2BK,GAAG,CAACN,iBAAiBM,IAAIJ;IAC5E,IAAIK,YAA4C;IAEhD,IACE,OAAOH,WAAW,YAClBA,UACA,QAAQA,UACPC,CAAAA,mBAAoB,SAASD,UAAUA,OAAOI,GAAG,KAAK,SAAS,GAChE;QACA,MAAMC,WAAW,AAACL,OAA4BM,EAAE;QAEhD,IAAI,OAAOD,aAAa,YAAY,OAAOA,aAAa,UAAU;YAChEF,YAAY,MAAMJ,IAAIQ,OAAO,CAACC,QAAQ,CAAC;gBACrCF,IAAID;gBACJI,YAAYb;gBACZc,OAAO;gBACPC,QAAQ;oBACN,GAAIV,kBAAkB;wBAAE,CAACA,gBAAgB,EAAE;oBAAK,IAAI,CAAC,CAAC;oBACtD,CAACH,mBAAmB,EAAE;gBACxB;YACF;QACF;IACF;IAEA,IAAI,CAACG,iBAAiB;QACpB,OAAO;YACLW,gBAAgB;YAChBT;QACF;IACF;IAEA,MAAMU,iBAAiBpB,eAAeU,WAAWF;IAEjD,OAAO;QACLW,gBAAgBpB,oBAAoB;YAClCS;YACAa,YAAYD;QACd;QACAV;IACF;AACF"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { buildJoinScopeWhere } from './buildJoinScopeWhere.js';
|
|
2
|
-
/**
|
|
3
|
-
* Builds a join-scope filter for order key generation during beforeChange.
|
|
4
|
-
*/
|
|
5
|
-
export declare function getJoinScopeWhereFromDocData(args: {
|
|
6
|
-
collectionSlug: string;
|
|
7
|
-
data: Record<string, unknown>;
|
|
8
|
-
joinFieldPathsByCollection?: Map<string, Map<string, string>>;
|
|
9
|
-
orderableFieldName: string;
|
|
10
|
-
originalDoc?: Record<string, unknown>;
|
|
11
|
-
}): ReturnType<typeof buildJoinScopeWhere>;
|
|
12
|
-
//# sourceMappingURL=getJoinScopeWhereFromDocData.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getJoinScopeWhereFromDocData.d.ts","sourceRoot":"","sources":["../../../../src/config/orderable/utils/getJoinScopeWhereFromDocData.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAG9D;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE;IACjD,cAAc,EAAE,MAAM,CAAA;IACtB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC7B,0BAA0B,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAC7D,kBAAkB,EAAE,MAAM,CAAA;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACtC,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAgBzC"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { buildJoinScopeWhere } from './buildJoinScopeWhere.js';
|
|
2
|
-
import { getValueAtPath } from './getValueAtPath.js';
|
|
3
|
-
/**
|
|
4
|
-
* Builds a join-scope filter for order key generation during beforeChange.
|
|
5
|
-
*/ export function getJoinScopeWhereFromDocData(args) {
|
|
6
|
-
const { collectionSlug, data, joinFieldPathsByCollection, orderableFieldName, originalDoc } = args;
|
|
7
|
-
const joinOnFieldPath = joinFieldPathsByCollection?.get(collectionSlug)?.get(orderableFieldName);
|
|
8
|
-
if (!joinOnFieldPath) {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
const scopeValue = getValueAtPath(data, joinOnFieldPath) ?? getValueAtPath(originalDoc, joinOnFieldPath);
|
|
12
|
-
return buildJoinScopeWhere({
|
|
13
|
-
joinOnFieldPath,
|
|
14
|
-
scopeValue
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
//# sourceMappingURL=getJoinScopeWhereFromDocData.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/config/orderable/utils/getJoinScopeWhereFromDocData.ts"],"sourcesContent":["import { buildJoinScopeWhere } from './buildJoinScopeWhere.js'\nimport { getValueAtPath } from './getValueAtPath.js'\n\n/**\n * Builds a join-scope filter for order key generation during beforeChange.\n */\nexport function getJoinScopeWhereFromDocData(args: {\n collectionSlug: string\n data: Record<string, unknown>\n joinFieldPathsByCollection?: Map<string, Map<string, string>>\n orderableFieldName: string\n originalDoc?: Record<string, unknown>\n}): ReturnType<typeof buildJoinScopeWhere> {\n const { collectionSlug, data, joinFieldPathsByCollection, orderableFieldName, originalDoc } = args\n\n const joinOnFieldPath = joinFieldPathsByCollection?.get(collectionSlug)?.get(orderableFieldName)\n\n if (!joinOnFieldPath) {\n return null\n }\n\n const scopeValue =\n getValueAtPath(data, joinOnFieldPath) ?? getValueAtPath(originalDoc, joinOnFieldPath)\n\n return buildJoinScopeWhere({\n joinOnFieldPath,\n scopeValue,\n })\n}\n"],"names":["buildJoinScopeWhere","getValueAtPath","getJoinScopeWhereFromDocData","args","collectionSlug","data","joinFieldPathsByCollection","orderableFieldName","originalDoc","joinOnFieldPath","get","scopeValue"],"mappings":"AAAA,SAASA,mBAAmB,QAAQ,2BAA0B;AAC9D,SAASC,cAAc,QAAQ,sBAAqB;AAEpD;;CAEC,GACD,OAAO,SAASC,6BAA6BC,IAM5C;IACC,MAAM,EAAEC,cAAc,EAAEC,IAAI,EAAEC,0BAA0B,EAAEC,kBAAkB,EAAEC,WAAW,EAAE,GAAGL;IAE9F,MAAMM,kBAAkBH,4BAA4BI,IAAIN,iBAAiBM,IAAIH;IAE7E,IAAI,CAACE,iBAAiB;QACpB,OAAO;IACT;IAEA,MAAME,aACJV,eAAeI,MAAMI,oBAAoBR,eAAeO,aAAaC;IAEvE,OAAOT,oBAAoB;QACzBS;QACAE;IACF;AACF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"getValueAtPath.d.ts","sourceRoot":"","sources":["../../../../src/config/orderable/utils/getValueAtPath.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAiBnE"}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reads a nested value from an object using dot-notation path syntax.
|
|
3
|
-
*/ export function getValueAtPath(data, path) {
|
|
4
|
-
if (!data || typeof data !== 'object') {
|
|
5
|
-
return undefined;
|
|
6
|
-
}
|
|
7
|
-
const segments = path.split('.');
|
|
8
|
-
let currentValue = data;
|
|
9
|
-
for (const segment of segments){
|
|
10
|
-
if (!currentValue || typeof currentValue !== 'object') {
|
|
11
|
-
return undefined;
|
|
12
|
-
}
|
|
13
|
-
currentValue = currentValue[segment];
|
|
14
|
-
}
|
|
15
|
-
return currentValue;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
//# sourceMappingURL=getValueAtPath.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/config/orderable/utils/getValueAtPath.ts"],"sourcesContent":["/**\n * Reads a nested value from an object using dot-notation path syntax.\n */\nexport function getValueAtPath(data: unknown, path: string): unknown {\n if (!data || typeof data !== 'object') {\n return undefined\n }\n\n const segments = path.split('.')\n let currentValue: unknown = data\n\n for (const segment of segments) {\n if (!currentValue || typeof currentValue !== 'object') {\n return undefined\n }\n\n currentValue = (currentValue as Record<string, unknown>)[segment]\n }\n\n return currentValue\n}\n"],"names":["getValueAtPath","data","path","undefined","segments","split","currentValue","segment"],"mappings":"AAAA;;CAEC,GACD,OAAO,SAASA,eAAeC,IAAa,EAAEC,IAAY;IACxD,IAAI,CAACD,QAAQ,OAAOA,SAAS,UAAU;QACrC,OAAOE;IACT;IAEA,MAAMC,WAAWF,KAAKG,KAAK,CAAC;IAC5B,IAAIC,eAAwBL;IAE5B,KAAK,MAAMM,WAAWH,SAAU;QAC9B,IAAI,CAACE,gBAAgB,OAAOA,iBAAiB,UAAU;YACrD,OAAOH;QACT;QAEAG,eAAe,AAACA,YAAwC,CAACC,QAAQ;IACnE;IAEA,OAAOD;AACT"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { PayloadHandler } from '../../types.js';
|
|
2
|
-
/**
|
|
3
|
-
* Resolves the target key when the client sends the temporary `pending` marker.
|
|
4
|
-
*/
|
|
5
|
-
export declare function resolvePendingTargetKey(args: {
|
|
6
|
-
collectionSlug: string;
|
|
7
|
-
orderableFieldName: string;
|
|
8
|
-
req: Parameters<PayloadHandler>[0];
|
|
9
|
-
targetDoc: null | Record<string, unknown>;
|
|
10
|
-
targetID: string;
|
|
11
|
-
targetKey: string;
|
|
12
|
-
}): Promise<null | string>;
|
|
13
|
-
//# sourceMappingURL=resolvePendingTargetKey.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"resolvePendingTargetKey.d.ts","sourceRoot":"","sources":["../../../../src/config/orderable/utils/resolvePendingTargetKey.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAA;AAEpD;;GAEG;AACH,wBAAsB,uBAAuB,CAAC,IAAI,EAAE;IAClD,cAAc,EAAE,MAAM,CAAA;IACtB,kBAAkB,EAAE,MAAM,CAAA;IAC1B,GAAG,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAA;IAClC,SAAS,EAAE,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;CAClB,GAAG,OAAO,CAAC,IAAI,GAAG,MAAM,CAAC,CAoBzB"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolves the target key when the client sends the temporary `pending` marker.
|
|
3
|
-
*/ export async function resolvePendingTargetKey(args) {
|
|
4
|
-
const { collectionSlug, orderableFieldName, req, targetDoc, targetID, targetKey } = args;
|
|
5
|
-
if (targetKey !== 'pending') {
|
|
6
|
-
return targetKey;
|
|
7
|
-
}
|
|
8
|
-
const targetDocKey = targetDoc?.[orderableFieldName];
|
|
9
|
-
if (typeof targetDocKey === 'string') {
|
|
10
|
-
return targetDocKey;
|
|
11
|
-
}
|
|
12
|
-
const beforeDoc = await req.payload.findByID({
|
|
13
|
-
id: targetID,
|
|
14
|
-
collection: collectionSlug,
|
|
15
|
-
depth: 0,
|
|
16
|
-
select: {
|
|
17
|
-
[orderableFieldName]: true
|
|
18
|
-
}
|
|
19
|
-
});
|
|
20
|
-
return beforeDoc?.[orderableFieldName] || null;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
//# sourceMappingURL=resolvePendingTargetKey.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/config/orderable/utils/resolvePendingTargetKey.ts"],"sourcesContent":["import type { PayloadHandler } from '../../types.js'\n\n/**\n * Resolves the target key when the client sends the temporary `pending` marker.\n */\nexport async function resolvePendingTargetKey(args: {\n collectionSlug: string\n orderableFieldName: string\n req: Parameters<PayloadHandler>[0]\n targetDoc: null | Record<string, unknown>\n targetID: string\n targetKey: string\n}): Promise<null | string> {\n const { collectionSlug, orderableFieldName, req, targetDoc, targetID, targetKey } = args\n\n if (targetKey !== 'pending') {\n return targetKey\n }\n\n const targetDocKey = targetDoc?.[orderableFieldName]\n if (typeof targetDocKey === 'string') {\n return targetDocKey\n }\n\n const beforeDoc = await req.payload.findByID({\n id: targetID,\n collection: collectionSlug,\n depth: 0,\n select: { [orderableFieldName]: true },\n })\n\n return beforeDoc?.[orderableFieldName] || null\n}\n"],"names":["resolvePendingTargetKey","args","collectionSlug","orderableFieldName","req","targetDoc","targetID","targetKey","targetDocKey","beforeDoc","payload","findByID","id","collection","depth","select"],"mappings":"AAEA;;CAEC,GACD,OAAO,eAAeA,wBAAwBC,IAO7C;IACC,MAAM,EAAEC,cAAc,EAAEC,kBAAkB,EAAEC,GAAG,EAAEC,SAAS,EAAEC,QAAQ,EAAEC,SAAS,EAAE,GAAGN;IAEpF,IAAIM,cAAc,WAAW;QAC3B,OAAOA;IACT;IAEA,MAAMC,eAAeH,WAAW,CAACF,mBAAmB;IACpD,IAAI,OAAOK,iBAAiB,UAAU;QACpC,OAAOA;IACT;IAEA,MAAMC,YAAY,MAAML,IAAIM,OAAO,CAACC,QAAQ,CAAC;QAC3CC,IAAIN;QACJO,YAAYX;QACZY,OAAO;QACPC,QAAQ;YAAE,CAACZ,mBAAmB,EAAE;QAAK;IACvC;IAEA,OAAOM,WAAW,CAACN,mBAAmB,IAAI;AAC5C"}
|