@payloadcms/figma 0.0.1-alpha.68 → 0.0.1-alpha.69
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/db-content-api/index.d.ts +15 -1
- package/dist/db-content-api/index.js +20 -0
- package/dist/oauth/components/LoginButton/iframeAutoLogin.d.ts +27 -8
- package/dist/oauth/components/LoginButton/iframeAutoLogin.js +32 -15
- package/dist/oauth/components/LoginButton/index.js +10 -2
- package/dist/plugin/build-config.js +7 -0
- package/dist/storage-content-api/client-uploads/ClientUploadHandler.js +1 -0
- package/dist/storage-content-api/handleUpload.js +5 -5
- package/dist/storage-content-api/staticHandler.js +24 -0
- package/dist/storage-content-api/utilities/index.d.ts +1 -0
- package/dist/storage-content-api/utilities/index.js +1 -0
- package/dist/storage-content-api/utilities/parseClientUploadContext.d.ts +19 -0
- package/dist/storage-content-api/utilities/parseClientUploadContext.js +30 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { BaseDatabaseAdapter, DatabaseAdapterObj } from 'payload';
|
|
1
|
+
import type { BaseDatabaseAdapter, CreateArgs, DatabaseAdapterObj } from 'payload';
|
|
2
2
|
import createClient from 'openapi-fetch';
|
|
3
3
|
import type { paths } from './generated/content-api-types.js';
|
|
4
4
|
import { type AuthMode } from './utilities/auth.js';
|
|
@@ -7,8 +7,21 @@ type ContentAPIOptions = {
|
|
|
7
7
|
auth: AuthMode;
|
|
8
8
|
contentSystemId: string;
|
|
9
9
|
environmentName?: string;
|
|
10
|
+
notifyLocalJobQueue: boolean;
|
|
10
11
|
url: string;
|
|
11
12
|
};
|
|
13
|
+
/**
|
|
14
|
+
* Heads-up logged locally whenever a job is queued. Queued jobs are executed by Figma's deployed runner, so
|
|
15
|
+
* if a deployed environment exists its runner can pick up anything in the queue. The developer
|
|
16
|
+
* needs to know that a job they just created locally may run on their deployed app instead of their machine.
|
|
17
|
+
*/
|
|
18
|
+
export declare const LOCAL_JOB_QUEUE_NOTICE: string;
|
|
19
|
+
/**
|
|
20
|
+
* Logs {@link LOCAL_JOB_QUEUE_NOTICE} for every job queued locally: if a deployed
|
|
21
|
+
* environment exists, its runner can pick up anything in the queue. No-op when
|
|
22
|
+
* `notifyLocalJobQueue` is false (i.e. not a local runtime).
|
|
23
|
+
*/
|
|
24
|
+
export declare function maybeNotifyLocalJobQueue(adapter: ContentAPIAdapter, args: CreateArgs): void;
|
|
12
25
|
export type ContentAPIAdapter = {
|
|
13
26
|
auth: AuthMode;
|
|
14
27
|
clearDatabase: () => Promise<void>;
|
|
@@ -16,6 +29,7 @@ export type ContentAPIAdapter = {
|
|
|
16
29
|
contentSystemId: string;
|
|
17
30
|
environmentName?: string;
|
|
18
31
|
idType: 'uuid';
|
|
32
|
+
notifyLocalJobQueue: boolean;
|
|
19
33
|
url: string;
|
|
20
34
|
} & BaseDatabaseAdapter;
|
|
21
35
|
export declare const contentAPIAdapter: (opts: ContentAPIOptions) => DatabaseAdapterObj;
|
|
@@ -14,6 +14,24 @@ import { addFallbackLocale } from './utilities/locale/index.js';
|
|
|
14
14
|
import { buildMeta } from './utilities/meta/buildMeta.js';
|
|
15
15
|
import { normalizeLocaleInWhere } from './utilities/meta/normalizeLocaleInWhere.js';
|
|
16
16
|
import { convertPayloadWhereToContentAPI } from './utilities/where.js';
|
|
17
|
+
/**
|
|
18
|
+
* Heads-up logged locally whenever a job is queued. Queued jobs are executed by Figma's deployed runner, so
|
|
19
|
+
* if a deployed environment exists its runner can pick up anything in the queue. The developer
|
|
20
|
+
* needs to know that a job they just created locally may run on their deployed app instead of their machine.
|
|
21
|
+
*/ export const LOCAL_JOB_QUEUE_NOTICE = "A job was queued locally. Queued jobs may be run by Figma's deployed runner, not your local " + 'dev server. If this app has a deployed environment, your deployed app may pick this ' + 'job up and run it against deployed code. If this app has no deployed ' + 'environment, the job will not run automatically. To test job logic against your local ' + 'code, call the function your task runs directly instead of queueing a job. ';
|
|
22
|
+
/**
|
|
23
|
+
* Logs {@link LOCAL_JOB_QUEUE_NOTICE} for every job queued locally: if a deployed
|
|
24
|
+
* environment exists, its runner can pick up anything in the queue. No-op when
|
|
25
|
+
* `notifyLocalJobQueue` is false (i.e. not a local runtime).
|
|
26
|
+
*/ export function maybeNotifyLocalJobQueue(adapter, args) {
|
|
27
|
+
if (!adapter.notifyLocalJobQueue) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (args.collection !== 'payload-jobs') {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
adapter.payload.logger.warn(LOCAL_JOB_QUEUE_NOTICE);
|
|
34
|
+
}
|
|
17
35
|
async function syncCollections() {
|
|
18
36
|
let existingIds;
|
|
19
37
|
try {
|
|
@@ -544,6 +562,7 @@ async function create(args) {
|
|
|
544
562
|
if (!response) {
|
|
545
563
|
throw new Error('No response from create');
|
|
546
564
|
}
|
|
565
|
+
maybeNotifyLocalJobQueue(this, args);
|
|
547
566
|
return unwrapDocument({
|
|
548
567
|
collectionSlug: args.collection,
|
|
549
568
|
doc: response.result,
|
|
@@ -878,6 +897,7 @@ export const contentAPIAdapter = (opts)=>({
|
|
|
878
897
|
findVersions: findVersions,
|
|
879
898
|
idType: 'uuid',
|
|
880
899
|
init,
|
|
900
|
+
notifyLocalJobQueue: opts.notifyLocalJobQueue,
|
|
881
901
|
packageName: '@payloadcms/db-content-api',
|
|
882
902
|
payload,
|
|
883
903
|
queryDrafts: queryDrafts,
|
|
@@ -9,15 +9,30 @@ export interface CmsOAuthCodeMessage {
|
|
|
9
9
|
* value the iframe sent on its outbound `mint-cms-oauth-code` request.
|
|
10
10
|
*/
|
|
11
11
|
export declare const parseCmsOAuthCodeMessage: (data: unknown) => CmsOAuthCodeMessage | null;
|
|
12
|
+
export interface ParentOriginSignals {
|
|
13
|
+
/**
|
|
14
|
+
* `ancestorOrigins[0]`: immediate embedder origin. `undefined` where the
|
|
15
|
+
* browser lacks `Location.ancestorOrigins`.
|
|
16
|
+
*/
|
|
17
|
+
ancestorOrigin: null | string | undefined;
|
|
18
|
+
/** `window.location.origin` — this iframe's own origin. */
|
|
19
|
+
ownOrigin: null | string | undefined;
|
|
20
|
+
/** `document.referrer`. */
|
|
21
|
+
referrer: null | string | undefined;
|
|
22
|
+
}
|
|
12
23
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* `
|
|
18
|
-
*
|
|
24
|
+
* Resolve the exact origin for `window.parent.postMessage`, or `null` if we
|
|
25
|
+
* can't establish a trusted embedder (caller must then NOT post). We never
|
|
26
|
+
* target `'*'`: the payload embeds PKCE material in `state`.
|
|
27
|
+
*
|
|
28
|
+
* 1. `ancestorOrigin` present (browser-set, unspoofable): trust iff Figma,
|
|
29
|
+
* else `null` — no fall-through to weaker signals.
|
|
30
|
+
* 2. No `ancestorOrigins`: fall back to `referrer`, but only if
|
|
31
|
+
* it's a Figma origin and not our own. (Payload's `/admin` -> `/admin/login`
|
|
32
|
+
* 307 leaves `referrer` as our own origin, never the embedder.)
|
|
33
|
+
* 3. Otherwise `null`.
|
|
19
34
|
*/
|
|
20
|
-
export declare const
|
|
35
|
+
export declare const resolveParentTargetOrigin: ({ ancestorOrigin, ownOrigin, referrer, }: ParentOriginSignals) => null | string;
|
|
21
36
|
export interface IframeAutoLoginDeps {
|
|
22
37
|
doFetch: typeof fetch;
|
|
23
38
|
/**
|
|
@@ -36,7 +51,11 @@ export interface IframeAutoLoginDeps {
|
|
|
36
51
|
* the host is expected to apply its own timeout.
|
|
37
52
|
*/
|
|
38
53
|
onFailed?: () => void;
|
|
39
|
-
|
|
54
|
+
/**
|
|
55
|
+
* Exact origin for `postMessage`, from `resolveParentTargetOrigin`. `null`
|
|
56
|
+
* means no trusted embedder: don't post, trigger `onFailed`. Never `'*'`.
|
|
57
|
+
*/
|
|
58
|
+
parentTargetOrigin: null | string;
|
|
40
59
|
postMessageToParent: (message: unknown, targetOrigin: string) => void;
|
|
41
60
|
win: Pick<Window, 'addEventListener' | 'removeEventListener'>;
|
|
42
61
|
}
|
|
@@ -24,22 +24,31 @@ import { isFigmaOrigin } from '../../utilities/figmaHostnames.js';
|
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* `
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
27
|
+
* Resolve the exact origin for `window.parent.postMessage`, or `null` if we
|
|
28
|
+
* can't establish a trusted embedder (caller must then NOT post). We never
|
|
29
|
+
* target `'*'`: the payload embeds PKCE material in `state`.
|
|
30
|
+
*
|
|
31
|
+
* 1. `ancestorOrigin` present (browser-set, unspoofable): trust iff Figma,
|
|
32
|
+
* else `null` — no fall-through to weaker signals.
|
|
33
|
+
* 2. No `ancestorOrigins`: fall back to `referrer`, but only if
|
|
34
|
+
* it's a Figma origin and not our own. (Payload's `/admin` -> `/admin/login`
|
|
35
|
+
* 307 leaves `referrer` as our own origin, never the embedder.)
|
|
36
|
+
* 3. Otherwise `null`.
|
|
37
|
+
*/ export const resolveParentTargetOrigin = ({ ancestorOrigin, ownOrigin, referrer })=>{
|
|
38
|
+
if (ancestorOrigin != null) {
|
|
39
|
+
return isFigmaOrigin(ancestorOrigin) ? ancestorOrigin : null;
|
|
36
40
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
if (referrer) {
|
|
42
|
+
try {
|
|
43
|
+
const candidate = new URL(referrer).origin;
|
|
44
|
+
if (candidate !== ownOrigin && isFigmaOrigin(candidate)) {
|
|
45
|
+
return candidate;
|
|
46
|
+
}
|
|
47
|
+
} catch {
|
|
48
|
+
// unparseable referrer — fall through
|
|
49
|
+
}
|
|
42
50
|
}
|
|
51
|
+
return null;
|
|
43
52
|
};
|
|
44
53
|
/**
|
|
45
54
|
* Wire up the iframe auto-login flow:
|
|
@@ -122,6 +131,14 @@ import { isFigmaOrigin } from '../../utilities/figmaHostnames.js';
|
|
|
122
131
|
if (abortController.signal.aborted) {
|
|
123
132
|
return;
|
|
124
133
|
}
|
|
134
|
+
// No trusted embedder origin: refuse to post (don't broadcast PKCE-bearing
|
|
135
|
+
// `state` to '*'). See `resolveParentTargetOrigin`.
|
|
136
|
+
if (deps.parentTargetOrigin === null) {
|
|
137
|
+
// eslint-disable-next-line no-console -- surface auto-login failures to dev tools
|
|
138
|
+
console.warn('[payload iframe auto-login] could not determine a trusted parent origin — falling back to manual login');
|
|
139
|
+
deps.onFailed?.();
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
125
142
|
flow = {
|
|
126
143
|
redirectUri: redirect,
|
|
127
144
|
state
|
|
@@ -134,7 +151,7 @@ import { isFigmaOrigin } from '../../utilities/figmaHostnames.js';
|
|
|
134
151
|
redirect_uri: redirect,
|
|
135
152
|
scope,
|
|
136
153
|
state
|
|
137
|
-
},
|
|
154
|
+
}, deps.parentTargetOrigin);
|
|
138
155
|
} catch (err) {
|
|
139
156
|
if (abortController.signal.aborted) {
|
|
140
157
|
return;
|
|
@@ -3,7 +3,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useConfig } from '@payloadcms/ui';
|
|
4
4
|
import { useSearchParams } from 'next/navigation.js';
|
|
5
5
|
import React, { useEffect, useState } from 'react';
|
|
6
|
-
import { setupIframeAutoLogin } from './iframeAutoLogin.js';
|
|
6
|
+
import { resolveParentTargetOrigin, setupIframeAutoLogin } from './iframeAutoLogin.js';
|
|
7
7
|
import './index.scss';
|
|
8
8
|
const baseClass = 'oauth-login';
|
|
9
9
|
// Max time we wait for the parent (figma.com) to respond with a code after
|
|
@@ -82,6 +82,10 @@ export const DefaultLoginButton = ({ disabled, endpointSlug })=>{
|
|
|
82
82
|
console.warn(`[payload iframe auto-login] parent did not reply with cms-oauth-code within ${AUTO_LOGIN_PARENT_REPLY_TIMEOUT_MS}ms — falling back to manual login`);
|
|
83
83
|
setAutoLoginFailed(true);
|
|
84
84
|
}, AUTO_LOGIN_PARENT_REPLY_TIMEOUT_MS);
|
|
85
|
+
// ancestorOrigins[0] is the immediate embedder's origin (browser-set,
|
|
86
|
+
// unspoofable) where supported; undefined on browsers that don't implement
|
|
87
|
+
// the non-standard Location.ancestorOrigins (older Firefox, pre-148).
|
|
88
|
+
const ancestorOrigins = window.location.ancestorOrigins;
|
|
85
89
|
const cleanup = setupIframeAutoLogin({
|
|
86
90
|
doFetch: window.fetch.bind(window),
|
|
87
91
|
metaUrl: `${serverURL}${api}/${userSlug}/${endpointSlug}/meta?serverURL=${window.location.origin}`,
|
|
@@ -89,7 +93,11 @@ export const DefaultLoginButton = ({ disabled, endpointSlug })=>{
|
|
|
89
93
|
window.location.href = url;
|
|
90
94
|
},
|
|
91
95
|
onFailed: ()=>setAutoLoginFailed(true),
|
|
92
|
-
|
|
96
|
+
parentTargetOrigin: resolveParentTargetOrigin({
|
|
97
|
+
ancestorOrigin: ancestorOrigins?.[0],
|
|
98
|
+
ownOrigin: window.location.origin,
|
|
99
|
+
referrer: document.referrer
|
|
100
|
+
}),
|
|
93
101
|
postMessageToParent: (msg, target)=>window.parent.postMessage(msg, target),
|
|
94
102
|
win: window
|
|
95
103
|
});
|
|
@@ -213,6 +213,10 @@ export async function buildFigmaConfig(config) {
|
|
|
213
213
|
}
|
|
214
214
|
const url = process.env.FIGMA_CONTENT_API_URL || envConfig.contentApiUrl;
|
|
215
215
|
const isProduction = process.env.NODE_ENV === 'production';
|
|
216
|
+
// The deployed Lambda runs all queued jobs; locally there is no runner, so the db adapter logs
|
|
217
|
+
// a heads-up whenever a job is queued. Deployed environments set DISABLE_LOCAL_JOB_QUEUE_NOTIFICATIONS=true
|
|
218
|
+
// (injected by the control plane); end-users can also set it locally to silence the notice.
|
|
219
|
+
const notifyLocalJobQueue = process.env.DISABLE_LOCAL_JOB_QUEUE_NOTIFICATIONS !== 'true';
|
|
216
220
|
const usesTokenStoreAuth = !process.env.FIGMA_CONTENT_API_ACCESS_KEY && process.env.FIGMA_DEV_JWT !== 'true';
|
|
217
221
|
if (usesTokenStoreAuth) {
|
|
218
222
|
logMissingCliAuth(getTokenStore());
|
|
@@ -229,6 +233,7 @@ export async function buildFigmaConfig(config) {
|
|
|
229
233
|
},
|
|
230
234
|
contentSystemId,
|
|
231
235
|
environmentName: process.env.FIGMA_ENVIRONMENT_NAME,
|
|
236
|
+
notifyLocalJobQueue,
|
|
232
237
|
url
|
|
233
238
|
});
|
|
234
239
|
} else if (process.env.FIGMA_DEV_JWT === 'true') {
|
|
@@ -238,6 +243,7 @@ export async function buildFigmaConfig(config) {
|
|
|
238
243
|
},
|
|
239
244
|
contentSystemId,
|
|
240
245
|
environmentName: process.env.FIGMA_ENVIRONMENT_NAME,
|
|
246
|
+
notifyLocalJobQueue,
|
|
241
247
|
url
|
|
242
248
|
});
|
|
243
249
|
} else {
|
|
@@ -248,6 +254,7 @@ export async function buildFigmaConfig(config) {
|
|
|
248
254
|
},
|
|
249
255
|
contentSystemId,
|
|
250
256
|
environmentName: process.env.FIGMA_ENVIRONMENT_NAME,
|
|
257
|
+
notifyLocalJobQueue,
|
|
251
258
|
url
|
|
252
259
|
});
|
|
253
260
|
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { parseClientUploadContext } from './utilities/index.js';
|
|
3
4
|
const PART_SIZE = 64 * 1024 * 1024 // 64MB per part
|
|
4
5
|
;
|
|
5
6
|
const MAX_FILE_SIZE_BEFORE_MULTIPART = 100 * 1024 * 1024 // 100MB
|
|
6
7
|
;
|
|
7
8
|
export const getHandleUpload = ({ client, collection, prefix = '' })=>{
|
|
8
9
|
return async ({ clientUploadContext, data, file })=>{
|
|
9
|
-
// Skip if client already uploaded THIS SPECIFIC file
|
|
10
|
-
//
|
|
11
|
-
|
|
12
|
-
const
|
|
13
|
-
const isOriginalClientUploaded = clientUploadContext && typeof clientUploadContext === 'object' && 'clientUploaded' in clientUploadContext && clientUploadContext.clientUploaded && file.filename === clientUploadedFilename;
|
|
10
|
+
// Skip if the client already uploaded THIS SPECIFIC file. Resized versions
|
|
11
|
+
// (different filename) should still be uploaded.
|
|
12
|
+
const context = parseClientUploadContext(clientUploadContext);
|
|
13
|
+
const isOriginalClientUploaded = context.clientUploaded === true && file.filename === context.filename;
|
|
14
14
|
if (isOriginalClientUploaded) {
|
|
15
15
|
return data;
|
|
16
16
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { getFilePrefix } from '@payloadcms/plugin-cloud-storage/utilities';
|
|
2
2
|
import path from 'path';
|
|
3
|
+
import { isImage } from 'payload/shared';
|
|
4
|
+
import { parseClientUploadContext } from './utilities/index.js';
|
|
3
5
|
export const getHandler = ({ client, collection })=>{
|
|
4
6
|
return async (req, { params: { clientUploadContext, filename } })=>{
|
|
5
7
|
try {
|
|
@@ -23,6 +25,28 @@ export const getHandler = ({ client, collection })=>{
|
|
|
23
25
|
status: 500
|
|
24
26
|
});
|
|
25
27
|
}
|
|
28
|
+
// On a client-upload fetch-back, core reads this response body into req.file.data to
|
|
29
|
+
// size the image; a 302's empty body crashes getImageSize. Return real bytes for
|
|
30
|
+
// images (and unknown mimetypes, to be safe), but keep the redirect otherwise so we
|
|
31
|
+
// don't download large non-images server-side.
|
|
32
|
+
if (clientUploadContext) {
|
|
33
|
+
const { mimeType } = parseClientUploadContext(clientUploadContext);
|
|
34
|
+
if (!mimeType || isImage(mimeType)) {
|
|
35
|
+
const fileResponse = await fetch(data.url);
|
|
36
|
+
if (!fileResponse.ok) {
|
|
37
|
+
req.payload.logger.error(`Failed to fetch file for client-upload buffer fetch-back: ${fileResponse.status}`);
|
|
38
|
+
return new Response('Internal Server Error', {
|
|
39
|
+
status: 500
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
const contentType = fileResponse.headers.get('Content-Type') ?? mimeType ?? 'application/octet-stream';
|
|
43
|
+
return new Response(await fileResponse.arrayBuffer(), {
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': contentType
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
26
50
|
return Response.redirect(data.url, 302);
|
|
27
51
|
} catch (err) {
|
|
28
52
|
req.payload.logger.error({
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of the `clientUploadContext` produced by this adapter's client upload
|
|
3
|
+
* handler. The plugin's `StaticHandler`/`HandleUpload` types expose it as `unknown`
|
|
4
|
+
* (it is deserialized from a JSON form field and is adapter-specific), so it must be
|
|
5
|
+
* narrowed at runtime before use.
|
|
6
|
+
*/
|
|
7
|
+
export type ContentApiClientUploadContext = {
|
|
8
|
+
clientUploaded?: boolean;
|
|
9
|
+
filename?: string;
|
|
10
|
+
mimeType?: string;
|
|
11
|
+
prefix?: string;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Safely narrows an `unknown` clientUploadContext into the adapter's known shape,
|
|
15
|
+
* keeping only properties that are actually present with the expected type. Returns
|
|
16
|
+
* an empty object for any non-object input.
|
|
17
|
+
*/
|
|
18
|
+
export declare const parseClientUploadContext: (context: unknown) => ContentApiClientUploadContext;
|
|
19
|
+
//# sourceMappingURL=parseClientUploadContext.d.ts.map
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape of the `clientUploadContext` produced by this adapter's client upload
|
|
3
|
+
* handler. The plugin's `StaticHandler`/`HandleUpload` types expose it as `unknown`
|
|
4
|
+
* (it is deserialized from a JSON form field and is adapter-specific), so it must be
|
|
5
|
+
* narrowed at runtime before use.
|
|
6
|
+
*/ /**
|
|
7
|
+
* Safely narrows an `unknown` clientUploadContext into the adapter's known shape,
|
|
8
|
+
* keeping only properties that are actually present with the expected type. Returns
|
|
9
|
+
* an empty object for any non-object input.
|
|
10
|
+
*/ export const parseClientUploadContext = (context)=>{
|
|
11
|
+
if (!context || typeof context !== 'object') {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
const result = {};
|
|
15
|
+
if ('clientUploaded' in context && typeof context.clientUploaded === 'boolean') {
|
|
16
|
+
result.clientUploaded = context.clientUploaded;
|
|
17
|
+
}
|
|
18
|
+
if ('filename' in context && typeof context.filename === 'string') {
|
|
19
|
+
result.filename = context.filename;
|
|
20
|
+
}
|
|
21
|
+
if ('mimeType' in context && typeof context.mimeType === 'string') {
|
|
22
|
+
result.mimeType = context.mimeType;
|
|
23
|
+
}
|
|
24
|
+
if ('prefix' in context && typeof context.prefix === 'string') {
|
|
25
|
+
result.prefix = context.prefix;
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//# sourceMappingURL=parseClientUploadContext.js.map
|