@ruiapp/rapid-core 0.1.82 → 0.1.83
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/index.js +14 -5
- package/package.json +1 -1
- package/rollup.config.js +16 -16
- package/src/core/actionHandler.ts +22 -22
- package/src/core/eventManager.ts +20 -20
- package/src/core/facility.ts +7 -7
- package/src/core/http/formDataParser.ts +89 -89
- package/src/core/pluginManager.ts +175 -175
- package/src/core/providers/runtimeProvider.ts +5 -5
- package/src/core/request.ts +86 -86
- package/src/core/response.ts +76 -76
- package/src/core/routeContext.ts +43 -43
- package/src/core/routesBuilder.ts +88 -88
- package/src/dataAccess/dataAccessor.ts +137 -137
- package/src/dataAccess/entityManager.ts +14 -5
- package/src/deno-std/datetime/to_imf.ts +32 -32
- package/src/deno-std/encoding/base64.ts +141 -141
- package/src/facilities/log/LogFacility.ts +35 -35
- package/src/helpers/entityHelpers.ts +76 -76
- package/src/plugins/auth/actionHandlers/changePassword.ts +54 -54
- package/src/plugins/auth/actionHandlers/createSession.ts +63 -63
- package/src/plugins/auth/actionHandlers/deleteSession.ts +18 -18
- package/src/plugins/auth/actionHandlers/getMyProfile.ts +35 -35
- package/src/plugins/auth/actionHandlers/index.ts +8 -8
- package/src/plugins/auth/actionHandlers/resetPassword.ts +38 -38
- package/src/plugins/auth/models/AccessToken.ts +56 -56
- package/src/plugins/auth/models/index.ts +3 -3
- package/src/plugins/auth/routes/changePassword.ts +15 -15
- package/src/plugins/auth/routes/getMyProfile.ts +15 -15
- package/src/plugins/auth/routes/index.ts +7 -7
- package/src/plugins/auth/routes/resetPassword.ts +15 -15
- package/src/plugins/auth/routes/signin.ts +15 -15
- package/src/plugins/auth/routes/signout.ts +15 -15
- package/src/plugins/cronJob/CronJobPluginTypes.ts +49 -49
- package/src/plugins/cronJob/actionHandlers/index.ts +4 -4
- package/src/plugins/cronJob/actionHandlers/runCronJob.ts +29 -29
- package/src/plugins/cronJob/routes/index.ts +3 -3
- package/src/plugins/cronJob/routes/runCronJob.ts +15 -15
- package/src/plugins/dataManage/actionHandlers/addEntityRelations.ts +20 -20
- package/src/plugins/dataManage/actionHandlers/countCollectionEntities.ts +15 -15
- package/src/plugins/dataManage/actionHandlers/createCollectionEntitiesBatch.ts +42 -42
- package/src/plugins/dataManage/actionHandlers/createCollectionEntity.ts +24 -24
- package/src/plugins/dataManage/actionHandlers/findCollectionEntities.ts +26 -26
- package/src/plugins/dataManage/actionHandlers/findCollectionEntityById.ts +21 -21
- package/src/plugins/dataManage/actionHandlers/queryDatabase.ts +22 -22
- package/src/plugins/dataManage/actionHandlers/removeEntityRelations.ts +20 -20
- package/src/plugins/dataManage/actionHandlers/updateCollectionEntityById.ts +35 -35
- package/src/plugins/fileManage/actionHandlers/downloadDocument.ts +36 -36
- package/src/plugins/fileManage/actionHandlers/uploadFile.ts +33 -33
- package/src/plugins/fileManage/routes/downloadDocument.ts +15 -15
- package/src/plugins/fileManage/routes/downloadFile.ts +15 -15
- package/src/plugins/fileManage/routes/index.ts +5 -5
- package/src/plugins/fileManage/routes/uploadFile.ts +15 -15
- package/src/plugins/metaManage/actionHandlers/getMetaModelDetail.ts +10 -10
- package/src/plugins/metaManage/actionHandlers/listMetaModels.ts +9 -9
- package/src/plugins/metaManage/actionHandlers/listMetaRoutes.ts +9 -9
- package/src/plugins/routeManage/actionHandlers/httpProxy.ts +13 -13
- package/src/plugins/sequence/SequenceService.ts +81 -81
- package/src/plugins/sequence/actionHandlers/generateSn.ts +32 -32
- package/src/plugins/sequence/actionHandlers/index.ts +4 -4
- package/src/plugins/sequence/models/SequenceAutoIncrementRecord.ts +49 -49
- package/src/plugins/sequence/models/SequenceRule.ts +42 -42
- package/src/plugins/sequence/models/index.ts +4 -4
- package/src/plugins/sequence/routes/generateSn.ts +15 -15
- package/src/plugins/sequence/routes/index.ts +3 -3
- package/src/plugins/sequence/segment-utility.ts +11 -11
- package/src/plugins/sequence/segments/index.ts +9 -9
- package/src/plugins/serverOperation/ServerOperationPlugin.ts +91 -91
- package/src/plugins/serverOperation/ServerOperationPluginTypes.ts +15 -15
- package/src/plugins/serverOperation/actionHandlers/index.ts +4 -4
- package/src/plugins/setting/SettingService.ts +213 -213
- package/src/plugins/setting/actionHandlers/getSystemSettingValues.ts +30 -30
- package/src/plugins/setting/actionHandlers/getUserSettingValues.ts +38 -38
- package/src/plugins/setting/actionHandlers/index.ts +6 -6
- package/src/plugins/setting/actionHandlers/setSystemSettingValues.ts +30 -30
- package/src/plugins/setting/models/SystemSettingGroupSetting.ts +57 -57
- package/src/plugins/setting/models/SystemSettingItem.ts +42 -42
- package/src/plugins/setting/models/SystemSettingItemSetting.ts +73 -73
- package/src/plugins/setting/models/UserSettingGroupSetting.ts +57 -57
- package/src/plugins/setting/models/UserSettingItem.ts +49 -49
- package/src/plugins/setting/models/UserSettingItemSetting.ts +73 -73
- package/src/plugins/setting/models/index.ts +8 -8
- package/src/plugins/setting/routes/getSystemSettingValues.ts +15 -15
- package/src/plugins/setting/routes/getUserSettingValues.ts +15 -15
- package/src/plugins/setting/routes/index.ts +5 -5
- package/src/plugins/setting/routes/setSystemSettingValues.ts +15 -15
- package/src/plugins/stateMachine/actionHandlers/index.ts +4 -4
- package/src/plugins/stateMachine/actionHandlers/sendStateMachineEvent.ts +51 -51
- package/src/plugins/stateMachine/models/StateMachine.ts +42 -42
- package/src/plugins/stateMachine/models/index.ts +3 -3
- package/src/plugins/stateMachine/routes/index.ts +3 -3
- package/src/plugins/stateMachine/routes/sendStateMachineEvent.ts +15 -15
- package/src/polyfill.ts +5 -5
- package/src/proxy/mod.ts +38 -38
- package/src/utilities/accessControlUtility.ts +33 -33
- package/src/utilities/fsUtility.ts +61 -61
- package/src/utilities/httpUtility.ts +19 -19
- package/src/utilities/jwtUtility.ts +26 -26
- package/src/utilities/timeUtility.ts +9 -9
- package/tsconfig.json +19 -19
package/dist/index.js
CHANGED
|
@@ -2262,6 +2262,9 @@ function convertEntityOrderByToRowOrderBy(server, model, baseModel, orderByList)
|
|
|
2262
2262
|
}
|
|
2263
2263
|
if (relationField) {
|
|
2264
2264
|
const relationProperty = getEntityPropertyByCode(server, model, relationField);
|
|
2265
|
+
if (!relationProperty) {
|
|
2266
|
+
throw new Error(`Property '${relationProperty}' was not found in ${model.namespace}.${model.singularCode}`);
|
|
2267
|
+
}
|
|
2265
2268
|
if (!isRelationProperty(relationProperty)) {
|
|
2266
2269
|
throw new Error("orderBy[].relation must be a one-relation property.");
|
|
2267
2270
|
}
|
|
@@ -2318,7 +2321,12 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2318
2321
|
let relationOptions = options.relations || {};
|
|
2319
2322
|
let relationPropertyCodes = Object.keys(relationOptions) || [];
|
|
2320
2323
|
if (!options.properties || !options.properties.length) {
|
|
2321
|
-
propertiesToSelect = getEntityPropertiesIncludingBase(server, model).filter((property) =>
|
|
2324
|
+
propertiesToSelect = getEntityPropertiesIncludingBase(server, model).filter((property) => {
|
|
2325
|
+
if (!property) {
|
|
2326
|
+
throw new Error(`Property '${property}' was not found in ${model.namespace}.${model.singularCode}`);
|
|
2327
|
+
}
|
|
2328
|
+
return !options.keepNonPropertyFields || isRelationProperty(property) || relationPropertyCodes.includes(property.code);
|
|
2329
|
+
});
|
|
2322
2330
|
}
|
|
2323
2331
|
else {
|
|
2324
2332
|
propertiesToSelect = getEntityPropertiesIncludingBase(server, model).filter((property) => options.properties.includes(property.code) || relationPropertyCodes.includes(property.code));
|
|
@@ -2326,6 +2334,9 @@ async function findEntities(server, dataAccessor, options) {
|
|
|
2326
2334
|
const columnsToSelect = [];
|
|
2327
2335
|
const relationPropertiesToSelect = [];
|
|
2328
2336
|
lodash.forEach(propertiesToSelect, (property) => {
|
|
2337
|
+
if (!property) {
|
|
2338
|
+
throw new Error(`Property '${property}' was not found in ${model.namespace}.${model.singularCode}`);
|
|
2339
|
+
}
|
|
2329
2340
|
if (isRelationProperty(property)) {
|
|
2330
2341
|
relationPropertiesToSelect.push(property);
|
|
2331
2342
|
if (property.relation === "one" && !property.linkTableName) {
|
|
@@ -2864,8 +2875,7 @@ async function createEntity(server, dataAccessor, options, plugin) {
|
|
|
2864
2875
|
lodash.keys(entity).forEach((propertyCode) => {
|
|
2865
2876
|
const property = getEntityPropertyByCode(server, model, propertyCode);
|
|
2866
2877
|
if (!property) {
|
|
2867
|
-
|
|
2868
|
-
return;
|
|
2878
|
+
throw new Error(`Property '${property}' was not found in ${model.namespace}.${model.singularCode}`);
|
|
2869
2879
|
}
|
|
2870
2880
|
if (isRelationProperty(property)) {
|
|
2871
2881
|
if (property.relation === "many") {
|
|
@@ -3097,8 +3107,7 @@ async function updateEntityById(server, dataAccessor, options, plugin) {
|
|
|
3097
3107
|
lodash.keys(changes).forEach((propertyCode) => {
|
|
3098
3108
|
const property = getEntityPropertyByCode(server, model, propertyCode);
|
|
3099
3109
|
if (!property) {
|
|
3100
|
-
|
|
3101
|
-
return;
|
|
3110
|
+
throw new Error(`Property '${property}' was not found in ${model.namespace}.${model.singularCode}`);
|
|
3102
3111
|
}
|
|
3103
3112
|
if (isRelationProperty(property)) {
|
|
3104
3113
|
if (property.relation === "many") {
|
package/package.json
CHANGED
package/rollup.config.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import typescript from "rollup-plugin-typescript2";
|
|
2
|
-
import tscAlias from "rollup-plugin-tsc-alias";
|
|
3
|
-
|
|
4
|
-
export default {
|
|
5
|
-
input: ["src/index.ts"],
|
|
6
|
-
output: [
|
|
7
|
-
{
|
|
8
|
-
dir: "dist",
|
|
9
|
-
entryFileNames: "[name].js",
|
|
10
|
-
format: "cjs",
|
|
11
|
-
exports: "named",
|
|
12
|
-
},
|
|
13
|
-
],
|
|
14
|
-
plugins: [typescript(), tscAlias()],
|
|
15
|
-
external: [],
|
|
16
|
-
};
|
|
1
|
+
import typescript from "rollup-plugin-typescript2";
|
|
2
|
+
import tscAlias from "rollup-plugin-tsc-alias";
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
input: ["src/index.ts"],
|
|
6
|
+
output: [
|
|
7
|
+
{
|
|
8
|
+
dir: "dist",
|
|
9
|
+
entryFileNames: "[name].js",
|
|
10
|
+
format: "cjs",
|
|
11
|
+
exports: "named",
|
|
12
|
+
},
|
|
13
|
+
],
|
|
14
|
+
plugins: [typescript(), tscAlias()],
|
|
15
|
+
external: [],
|
|
16
|
+
};
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { RpdApplicationConfig } from "~/types";
|
|
2
|
-
import { IRpdServer, RapidPlugin } from "./server";
|
|
3
|
-
import { Next, RouteContext } from "./routeContext";
|
|
4
|
-
import { Logger } from "~/facilities/log/LogFacility";
|
|
5
|
-
|
|
6
|
-
export interface ActionHandlerContext {
|
|
7
|
-
logger: Logger;
|
|
8
|
-
routerContext: RouteContext;
|
|
9
|
-
next: Next;
|
|
10
|
-
server: IRpdServer;
|
|
11
|
-
applicationConfig: RpdApplicationConfig;
|
|
12
|
-
input?: any;
|
|
13
|
-
output?: any;
|
|
14
|
-
status?: Response["status"];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export type ActionHandler = (ctx: ActionHandlerContext, options: any) => void | Promise<void>;
|
|
18
|
-
|
|
19
|
-
export interface IPluginActionHandler {
|
|
20
|
-
code: string;
|
|
21
|
-
handler: (plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) => void | Promise<void>;
|
|
22
|
-
}
|
|
1
|
+
import { RpdApplicationConfig } from "~/types";
|
|
2
|
+
import { IRpdServer, RapidPlugin } from "./server";
|
|
3
|
+
import { Next, RouteContext } from "./routeContext";
|
|
4
|
+
import { Logger } from "~/facilities/log/LogFacility";
|
|
5
|
+
|
|
6
|
+
export interface ActionHandlerContext {
|
|
7
|
+
logger: Logger;
|
|
8
|
+
routerContext: RouteContext;
|
|
9
|
+
next: Next;
|
|
10
|
+
server: IRpdServer;
|
|
11
|
+
applicationConfig: RpdApplicationConfig;
|
|
12
|
+
input?: any;
|
|
13
|
+
output?: any;
|
|
14
|
+
status?: Response["status"];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type ActionHandler = (ctx: ActionHandlerContext, options: any) => void | Promise<void>;
|
|
18
|
+
|
|
19
|
+
export interface IPluginActionHandler {
|
|
20
|
+
code: string;
|
|
21
|
+
handler: (plugin: RapidPlugin, ctx: ActionHandlerContext, options: any) => void | Promise<void>;
|
|
22
|
+
}
|
package/src/core/eventManager.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { EventEmitter } from "events";
|
|
2
|
-
|
|
3
|
-
export default class EventManager<EventTypes extends Record<string, any[]>> {
|
|
4
|
-
#eventEmitter: EventEmitter;
|
|
5
|
-
|
|
6
|
-
constructor() {
|
|
7
|
-
this.#eventEmitter = new EventEmitter();
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
on<K extends keyof EventTypes>(eventName: K, listener: (...args: EventTypes[K]) => void) {
|
|
11
|
-
this.#eventEmitter.on(eventName as string, listener);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
async emit<K extends keyof EventTypes>(eventName: K, ...args: EventTypes[K]) {
|
|
15
|
-
const listeners = this.#eventEmitter.listeners(eventName as string);
|
|
16
|
-
for (const listener of listeners) {
|
|
17
|
-
await listener(...args);
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
1
|
+
import { EventEmitter } from "events";
|
|
2
|
+
|
|
3
|
+
export default class EventManager<EventTypes extends Record<string, any[]>> {
|
|
4
|
+
#eventEmitter: EventEmitter;
|
|
5
|
+
|
|
6
|
+
constructor() {
|
|
7
|
+
this.#eventEmitter = new EventEmitter();
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
on<K extends keyof EventTypes>(eventName: K, listener: (...args: EventTypes[K]) => void) {
|
|
11
|
+
this.#eventEmitter.on(eventName as string, listener);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async emit<K extends keyof EventTypes>(eventName: K, ...args: EventTypes[K]) {
|
|
15
|
+
const listeners = this.#eventEmitter.listeners(eventName as string);
|
|
16
|
+
for (const listener of listeners) {
|
|
17
|
+
await listener(...args);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/core/facility.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { IRpdServer } from "./server";
|
|
2
|
-
|
|
3
|
-
export interface FacilityFactory {
|
|
4
|
-
name: string;
|
|
5
|
-
|
|
6
|
-
createFacility: (server: IRpdServer, options?: any) => Promise<any>;
|
|
7
|
-
}
|
|
1
|
+
import { IRpdServer } from "./server";
|
|
2
|
+
|
|
3
|
+
export interface FacilityFactory {
|
|
4
|
+
name: string;
|
|
5
|
+
|
|
6
|
+
createFacility: (server: IRpdServer, options?: any) => Promise<any>;
|
|
7
|
+
}
|
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import type { RapidRequest } from "../request";
|
|
2
|
-
|
|
3
|
-
export type BodyData = Record<string, string | File | (string | File)[]>;
|
|
4
|
-
export type ParseBodyOptions = {
|
|
5
|
-
/**
|
|
6
|
-
* Parse all fields with multiple values should be parsed as an array.
|
|
7
|
-
* @default false
|
|
8
|
-
* @example
|
|
9
|
-
* ```ts
|
|
10
|
-
* const data = new FormData()
|
|
11
|
-
* data.append('file', 'aaa')
|
|
12
|
-
* data.append('file', 'bbb')
|
|
13
|
-
* data.append('message', 'hello')
|
|
14
|
-
* ```
|
|
15
|
-
*
|
|
16
|
-
* If `all` is `false`:
|
|
17
|
-
* parseBody should return `{ file: 'bbb', message: 'hello' }`
|
|
18
|
-
*
|
|
19
|
-
* If `all` is `true`:
|
|
20
|
-
* parseBody should return `{ file: ['aaa', 'bbb'], message: 'hello' }`
|
|
21
|
-
*/
|
|
22
|
-
all?: boolean;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export const parseFormDataBody = async <T extends BodyData = BodyData>(request: Request, options: ParseBodyOptions = { all: false }): Promise<T> => {
|
|
26
|
-
const contentType = request.headers.get("Content-Type");
|
|
27
|
-
|
|
28
|
-
if (isFormDataContent(contentType)) {
|
|
29
|
-
return parseFormData<T>(request, options);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return {} as T;
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
function isFormDataContent(contentType: string | null): boolean {
|
|
36
|
-
if (contentType === null) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async function parseFormData<T extends BodyData = BodyData>(request: Request, options: ParseBodyOptions): Promise<T> {
|
|
44
|
-
const formData = await (request as Request).formData();
|
|
45
|
-
|
|
46
|
-
if (formData) {
|
|
47
|
-
return convertFormDataToBodyData<T>(formData, options);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return {} as T;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function convertFormDataToBodyData<T extends BodyData = BodyData>(formData: FormData, options: ParseBodyOptions): T {
|
|
54
|
-
const form: BodyData = {};
|
|
55
|
-
|
|
56
|
-
formData.forEach((value, key) => {
|
|
57
|
-
const shouldParseAllValues = options.all || key.endsWith("[]");
|
|
58
|
-
|
|
59
|
-
if (!shouldParseAllValues) {
|
|
60
|
-
form[key] = value;
|
|
61
|
-
} else {
|
|
62
|
-
handleParsingAllValues(form, key, value);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
return form as T;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const handleParsingAllValues = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
|
70
|
-
if (form[key] && isArrayField(form[key])) {
|
|
71
|
-
appendToExistingArray(form[key] as (string | File)[], value);
|
|
72
|
-
} else if (form[key]) {
|
|
73
|
-
convertToNewArray(form, key, value);
|
|
74
|
-
} else {
|
|
75
|
-
form[key] = value;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
|
|
79
|
-
function isArrayField(field: unknown): field is (string | File)[] {
|
|
80
|
-
return Array.isArray(field);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const appendToExistingArray = (arr: (string | File)[], value: FormDataEntryValue): void => {
|
|
84
|
-
arr.push(value);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const convertToNewArray = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
|
88
|
-
form[key] = [form[key] as string | File, value];
|
|
89
|
-
};
|
|
1
|
+
import type { RapidRequest } from "../request";
|
|
2
|
+
|
|
3
|
+
export type BodyData = Record<string, string | File | (string | File)[]>;
|
|
4
|
+
export type ParseBodyOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Parse all fields with multiple values should be parsed as an array.
|
|
7
|
+
* @default false
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const data = new FormData()
|
|
11
|
+
* data.append('file', 'aaa')
|
|
12
|
+
* data.append('file', 'bbb')
|
|
13
|
+
* data.append('message', 'hello')
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* If `all` is `false`:
|
|
17
|
+
* parseBody should return `{ file: 'bbb', message: 'hello' }`
|
|
18
|
+
*
|
|
19
|
+
* If `all` is `true`:
|
|
20
|
+
* parseBody should return `{ file: ['aaa', 'bbb'], message: 'hello' }`
|
|
21
|
+
*/
|
|
22
|
+
all?: boolean;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export const parseFormDataBody = async <T extends BodyData = BodyData>(request: Request, options: ParseBodyOptions = { all: false }): Promise<T> => {
|
|
26
|
+
const contentType = request.headers.get("Content-Type");
|
|
27
|
+
|
|
28
|
+
if (isFormDataContent(contentType)) {
|
|
29
|
+
return parseFormData<T>(request, options);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {} as T;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function isFormDataContent(contentType: string | null): boolean {
|
|
36
|
+
if (contentType === null) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function parseFormData<T extends BodyData = BodyData>(request: Request, options: ParseBodyOptions): Promise<T> {
|
|
44
|
+
const formData = await (request as Request).formData();
|
|
45
|
+
|
|
46
|
+
if (formData) {
|
|
47
|
+
return convertFormDataToBodyData<T>(formData, options);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {} as T;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function convertFormDataToBodyData<T extends BodyData = BodyData>(formData: FormData, options: ParseBodyOptions): T {
|
|
54
|
+
const form: BodyData = {};
|
|
55
|
+
|
|
56
|
+
formData.forEach((value, key) => {
|
|
57
|
+
const shouldParseAllValues = options.all || key.endsWith("[]");
|
|
58
|
+
|
|
59
|
+
if (!shouldParseAllValues) {
|
|
60
|
+
form[key] = value;
|
|
61
|
+
} else {
|
|
62
|
+
handleParsingAllValues(form, key, value);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return form as T;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const handleParsingAllValues = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
|
70
|
+
if (form[key] && isArrayField(form[key])) {
|
|
71
|
+
appendToExistingArray(form[key] as (string | File)[], value);
|
|
72
|
+
} else if (form[key]) {
|
|
73
|
+
convertToNewArray(form, key, value);
|
|
74
|
+
} else {
|
|
75
|
+
form[key] = value;
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
function isArrayField(field: unknown): field is (string | File)[] {
|
|
80
|
+
return Array.isArray(field);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const appendToExistingArray = (arr: (string | File)[], value: FormDataEntryValue): void => {
|
|
84
|
+
arr.push(value);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const convertToNewArray = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
|
88
|
+
form[key] = [form[key] as string | File, value];
|
|
89
|
+
};
|