mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2026-09-03 05:10:16 +03:00
Improve type safety of tool definitions (#6895)
# Description of Changes Followup work requested in review of #6867. Currently, there is nothing enforcing that the endpoint chosen in the tool config is the correct mapping for `toApiParams`, so theoretically it's possible for a tool to be set up to call an endpoint with the wrong API params for it. There's also nothing currently enforcing that `toApiParams` and `fromApiParams` are compatible with each other (using the same types). This PR changes it so that instead of creating the config object directly, tools create it via a generic function, which enforces that all of the relevant mappings are using compatible types.
This commit is contained in:
+3
-4
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -73,15 +73,14 @@ export const buildAddPageNumbersFormData = (
|
||||
): FormData =>
|
||||
objectToFormData(addPageNumbersToApiParams(parameters), { fileInput: file });
|
||||
|
||||
export const addPageNumbersOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const addPageNumbersOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildAddPageNumbersFormData,
|
||||
toApiParams: addPageNumbersToApiParams,
|
||||
fromApiParams: addPageNumbersFromApiParams,
|
||||
operationType: "addPageNumbers",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useAddPageNumbersOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -87,15 +87,14 @@ export const buildAddStampFormData = (
|
||||
: { fileInput: file },
|
||||
);
|
||||
|
||||
export const addStampOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const addStampOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildAddStampFormData,
|
||||
toApiParams: addStampToApiParams,
|
||||
fromApiParams: addStampFromApiParams,
|
||||
operationType: "addStamp",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useAddStampOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -48,16 +47,14 @@ const buildFormData = (
|
||||
});
|
||||
|
||||
// Operation configuration for automation
|
||||
export const addAttachmentsOperationConfig: ToolOperationConfig<AddAttachmentsParameters> =
|
||||
{
|
||||
toolType: ToolType.singleFile,
|
||||
buildFormData,
|
||||
toApiParams: addAttachmentsToApiParams,
|
||||
fromApiParams: addAttachmentsFromApiParams,
|
||||
operationType: "addAttachments",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters: DEFAULT_ADD_ATTACHMENTS_PARAMETERS,
|
||||
};
|
||||
export const addAttachmentsOperationConfig = defineSingleFileTool({
|
||||
buildFormData,
|
||||
toApiParams: addAttachmentsToApiParams,
|
||||
fromApiParams: addAttachmentsFromApiParams,
|
||||
operationType: "addAttachments",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters: DEFAULT_ADD_ATTACHMENTS_PARAMETERS,
|
||||
});
|
||||
|
||||
export const useAddAttachmentsOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -85,15 +85,14 @@ const fullDefaultParameters: AddPasswordFullParameters = {
|
||||
};
|
||||
|
||||
// Static configuration object
|
||||
export const addPasswordOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const addPasswordOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildAddPasswordFormData,
|
||||
toApiParams: addPasswordToApiParams,
|
||||
fromApiParams: addPasswordFromApiParams,
|
||||
operationType: "addPassword",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters: fullDefaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useAddPasswordOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -87,15 +87,14 @@ export const buildAddWatermarkFormData = (
|
||||
);
|
||||
|
||||
// Static configuration object
|
||||
export const addWatermarkOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const addWatermarkOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildAddWatermarkFormData,
|
||||
toApiParams: addWatermarkToApiParams,
|
||||
fromApiParams: addWatermarkFromApiParams,
|
||||
operationType: "watermark",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useAddWatermarkOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
defineCustomTool,
|
||||
useToolOperation,
|
||||
CustomProcessorResult,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
@@ -195,14 +195,11 @@ async function processPdfClientSide(
|
||||
};
|
||||
}
|
||||
|
||||
export const adjustContrastOperationConfig = {
|
||||
toolType: ToolType.custom,
|
||||
export const adjustContrastOperationConfig = defineCustomTool({
|
||||
customProcessor: processPdfClientSide,
|
||||
operationType: "adjustContrast",
|
||||
defaultParameters,
|
||||
settingsComponentPath:
|
||||
"components/tools/adjustContrast/AdjustContrastSingleStepSettings",
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useAdjustContrastOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import { createStandardErrorHandler } from "@app/utils/toolErrorHandler";
|
||||
import {
|
||||
@@ -21,15 +21,14 @@ export {
|
||||
adjustPageScaleFromApiParams,
|
||||
};
|
||||
|
||||
export const adjustPageScaleOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const adjustPageScaleOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildAdjustPageScaleFormData,
|
||||
toApiParams: adjustPageScaleToApiParams,
|
||||
fromApiParams: adjustPageScaleFromApiParams,
|
||||
operationType: "scalePages",
|
||||
endpoint: ADJUST_PAGE_SCALE_ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useAdjustPageScaleOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -44,8 +44,7 @@ export const buildAutoRenameFormData = (
|
||||
objectToFormData(autoRenameToApiParams(parameters), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const autoRenameOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const autoRenameOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildAutoRenameFormData,
|
||||
toApiParams: autoRenameToApiParams,
|
||||
fromApiParams: autoRenameFromApiParams,
|
||||
@@ -53,7 +52,7 @@ export const autoRenameOperationConfig = {
|
||||
endpoint: ENDPOINT,
|
||||
preserveBackendFilename: true, // Use filename from backend response headers
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useAutoRenameOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {
|
||||
ToolType,
|
||||
defineCustomTool,
|
||||
useToolOperation,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import { useCallback } from "react";
|
||||
@@ -55,10 +55,11 @@ export function useAutomateOperation() {
|
||||
[toolRegistry],
|
||||
);
|
||||
|
||||
return useToolOperation<AutomateParameters>({
|
||||
toolType: ToolType.custom,
|
||||
operationType: "automate",
|
||||
customProcessor,
|
||||
consumesAllInputs: true,
|
||||
});
|
||||
return useToolOperation<AutomateParameters>(
|
||||
defineCustomTool({
|
||||
operationType: "automate",
|
||||
customProcessor,
|
||||
consumesAllInputs: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -59,15 +59,14 @@ export const buildBookletImpositionFormData = (
|
||||
});
|
||||
|
||||
// Static configuration object
|
||||
export const bookletImpositionOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const bookletImpositionOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildBookletImpositionFormData,
|
||||
toApiParams: bookletImpositionToApiParams,
|
||||
fromApiParams: bookletImpositionFromApiParams,
|
||||
operationType: "bookletImposition",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useBookletImpositionOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -136,16 +136,14 @@ export const buildCertSignFormData = (
|
||||
});
|
||||
|
||||
// Static configuration object
|
||||
export const certSignOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const certSignOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildCertSignFormData,
|
||||
toApiParams: certSignToApiParams,
|
||||
fromApiParams: certSignFromApiParams,
|
||||
operationType: "certSign",
|
||||
endpoint: ENDPOINT,
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useCertSignOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import { createStandardErrorHandler } from "@app/utils/toolErrorHandler";
|
||||
import {
|
||||
@@ -75,13 +75,12 @@ export const buildChangeMetadataFormData = (
|
||||
};
|
||||
|
||||
// Static configuration object
|
||||
export const changeMetadataOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const changeMetadataOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildChangeMetadataFormData,
|
||||
operationType: "changeMetadata",
|
||||
endpoint: "/api/v1/misc/update-metadata",
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useChangeMetadataOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -71,15 +71,14 @@ export const buildChangePermissionsFormData = (
|
||||
});
|
||||
|
||||
// Static configuration object
|
||||
export const changePermissionsOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const changePermissionsOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildChangePermissionsFormData,
|
||||
toApiParams: changePermissionsToApiParams,
|
||||
fromApiParams: changePermissionsFromApiParams,
|
||||
operationType: "changePermissions",
|
||||
endpoint: ENDPOINT, // Change Permissions is a fake endpoint for the Add Password tool
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useChangePermissionsOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -90,15 +90,14 @@ export const buildCompressFormData = (
|
||||
objectToFormData(compressToApiParams(parameters), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const compressOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const compressOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildCompressFormData,
|
||||
toApiParams: compressToApiParams,
|
||||
fromApiParams: compressFromApiParams,
|
||||
operationType: "compress",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useCompressOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
import { createFileFromApiResponse } from "@app/utils/fileResponseUtils";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineCustomTool,
|
||||
CustomProcessorResult,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
@@ -300,8 +300,7 @@ export const convertProcessor = async (
|
||||
};
|
||||
|
||||
// Static configuration object
|
||||
export const convertOperationConfig = {
|
||||
toolType: ToolType.custom,
|
||||
export const convertOperationConfig = defineCustomTool({
|
||||
customProcessor: convertProcessor, // Can't use callback version here
|
||||
operationType: "convert",
|
||||
defaultParameters,
|
||||
@@ -311,7 +310,7 @@ export const convertOperationConfig = {
|
||||
params.toExtension === "pdfx" ? "pdfa" : params.toExtension;
|
||||
return getEndpointUrl(params.fromExtension, actualToExtension) ?? undefined;
|
||||
},
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useConvertOperation = (parameters?: ConvertParameters) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -59,15 +59,14 @@ export const buildCropFormData = (
|
||||
objectToFormData(cropToApiParams(parameters), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const cropOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const cropOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildCropFormData,
|
||||
toApiParams: cropToApiParams,
|
||||
fromApiParams: cropFromApiParams,
|
||||
operationType: "crop",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useCropOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+8
-11
@@ -1,7 +1,6 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
type ToolOperationConfig,
|
||||
defineSingleFileTool,
|
||||
useToolOperation,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
@@ -64,15 +63,13 @@ const buildFormData = (
|
||||
fileInput: file,
|
||||
});
|
||||
|
||||
export const editTableOfContentsOperationConfig: ToolOperationConfig<EditTableOfContentsParameters> =
|
||||
{
|
||||
toolType: ToolType.singleFile,
|
||||
operationType: "editTableOfContents",
|
||||
endpoint: ENDPOINT,
|
||||
buildFormData,
|
||||
toApiParams: editTableOfContentsToApiParams,
|
||||
fromApiParams: editTableOfContentsFromApiParams,
|
||||
};
|
||||
export const editTableOfContentsOperationConfig = defineSingleFileTool({
|
||||
operationType: "editTableOfContents",
|
||||
endpoint: ENDPOINT,
|
||||
buildFormData,
|
||||
toApiParams: editTableOfContentsToApiParams,
|
||||
fromApiParams: editTableOfContentsFromApiParams,
|
||||
});
|
||||
|
||||
export const useEditTableOfContentsOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -41,15 +41,14 @@ export const buildExtractImagesFormData = (
|
||||
objectToFormData(extractImagesToApiParams(parameters), { fileInput: file });
|
||||
|
||||
// Static configuration object (without response handler - will be added in hook)
|
||||
export const extractImagesOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const extractImagesOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildExtractImagesFormData,
|
||||
toApiParams: extractImagesToApiParams,
|
||||
fromApiParams: extractImagesFromApiParams,
|
||||
operationType: "extractImages",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useExtractImagesOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import apiClient from "@app/services/apiClient";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
defineCustomTool,
|
||||
useToolOperation,
|
||||
CustomProcessorResult,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
@@ -33,8 +33,7 @@ async function resolveSelectionToCsv(
|
||||
}
|
||||
}
|
||||
|
||||
export const extractPagesOperationConfig = {
|
||||
toolType: ToolType.custom,
|
||||
export const extractPagesOperationConfig = defineCustomTool({
|
||||
operationType: "extractPages",
|
||||
customProcessor: async (
|
||||
parameters: ExtractPagesParameters,
|
||||
@@ -71,7 +70,7 @@ export const extractPagesOperationConfig = {
|
||||
};
|
||||
},
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useExtractPagesOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -59,16 +59,14 @@ export const buildFlattenFormData = (
|
||||
objectToFormData(flattenToApiParams(parameters), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const flattenOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const flattenOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildFlattenFormData,
|
||||
toApiParams: flattenToApiParams,
|
||||
fromApiParams: flattenFromApiParams,
|
||||
operationType: "flatten",
|
||||
endpoint: ENDPOINT,
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useFlattenOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
ToolType,
|
||||
defineMultiFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -56,8 +55,7 @@ const buildFormData = (
|
||||
};
|
||||
|
||||
// Operation configuration for automation
|
||||
export const mergeOperationConfig: ToolOperationConfig<MergeParameters> = {
|
||||
toolType: ToolType.multiFile,
|
||||
export const mergeOperationConfig = defineMultiFileTool({
|
||||
buildFormData,
|
||||
toApiParams: mergeToApiParams,
|
||||
fromApiParams: mergeFromApiParams,
|
||||
@@ -65,7 +63,7 @@ export const mergeOperationConfig: ToolOperationConfig<MergeParameters> = {
|
||||
endpoint: ENDPOINT,
|
||||
filePrefix: "merged_",
|
||||
defaultParameters,
|
||||
};
|
||||
});
|
||||
|
||||
export const useMergeOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -151,15 +151,14 @@ export const ocrResponseHandler = async (
|
||||
};
|
||||
|
||||
// Static configuration object (without t function dependencies)
|
||||
export const ocrOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const ocrOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildOCRFormData,
|
||||
toApiParams: ocrToApiParams,
|
||||
fromApiParams: ocrFromApiParams,
|
||||
operationType: "ocr",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useOCROperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
type ToolOperationConfig,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -60,14 +60,13 @@ const buildFormData = (
|
||||
});
|
||||
|
||||
export const overlayPdfsOperationConfig: ToolOperationConfig<OverlayPdfsParameters> =
|
||||
{
|
||||
toolType: ToolType.singleFile,
|
||||
defineSingleFileTool({
|
||||
buildFormData,
|
||||
toApiParams: overlayPdfsToApiParams,
|
||||
fromApiParams: overlayPdfsFromApiParams,
|
||||
operationType: "overlayPdfs",
|
||||
endpoint: ENDPOINT,
|
||||
};
|
||||
});
|
||||
|
||||
export const useOverlayPdfsOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -67,15 +67,14 @@ export const buildPageLayoutFormData = (
|
||||
): FormData =>
|
||||
objectToFormData(pageLayoutToApiParams(parameters), { fileInput: file });
|
||||
|
||||
export const pageLayoutOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const pageLayoutOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildPageLayoutFormData,
|
||||
toApiParams: pageLayoutToApiParams,
|
||||
fromApiParams: pageLayoutFromApiParams,
|
||||
operationType: "pageLayout",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const usePageLayoutOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -64,8 +64,7 @@ export const buildRedactFormData = (
|
||||
};
|
||||
|
||||
// Static configuration object
|
||||
export const redactOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const redactOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRedactFormData,
|
||||
toApiParams: redactToApiParams,
|
||||
fromApiParams: redactFromApiParams,
|
||||
@@ -73,7 +72,7 @@ export const redactOperationConfig = {
|
||||
endpoint: (parameters: RedactParameters) =>
|
||||
parameters.mode === "automatic" ? AUTO_ENDPOINT : null,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useRedactOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineCustomTool,
|
||||
CustomProcessorResult,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import { createStandardErrorHandler } from "@app/utils/toolErrorHandler";
|
||||
@@ -78,12 +78,11 @@ const removeAnnotationsProcessor = async (
|
||||
};
|
||||
|
||||
// Static configuration object
|
||||
export const removeAnnotationsOperationConfig = {
|
||||
toolType: ToolType.custom,
|
||||
export const removeAnnotationsOperationConfig = defineCustomTool({
|
||||
operationType: "removeAnnotations",
|
||||
customProcessor: removeAnnotationsProcessor,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useRemoveAnnotationsOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -41,15 +40,14 @@ export const buildRemoveBlanksFormData = (
|
||||
): FormData =>
|
||||
objectToFormData(removeBlanksToApiParams(parameters), { fileInput: file });
|
||||
|
||||
export const removeBlanksOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const removeBlanksOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRemoveBlanksFormData,
|
||||
toApiParams: removeBlanksToApiParams,
|
||||
fromApiParams: removeBlanksFromApiParams,
|
||||
operationType: "removeBlanks",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const satisfies ToolOperationConfig<RemoveBlanksParameters>;
|
||||
});
|
||||
|
||||
export const useRemoveBlanksOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+3
-4
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
fileOnlyMapping,
|
||||
@@ -25,15 +25,14 @@ export const buildRemoveCertificateSignFormData = (
|
||||
): FormData => objectToFormData(toApiParams(), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const removeCertificateSignOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const removeCertificateSignOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRemoveCertificateSignFormData,
|
||||
toApiParams,
|
||||
fromApiParams,
|
||||
operationType: "removeCertSign",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useRemoveCertificateSignOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
fileOnlyMapping,
|
||||
@@ -22,15 +21,13 @@ export const buildRemoveImageFormData = (
|
||||
file: File,
|
||||
): FormData => objectToFormData(toApiParams(), { fileInput: file });
|
||||
|
||||
export const removeImageOperationConfig: ToolOperationConfig<RemoveImageParameters> =
|
||||
{
|
||||
toolType: ToolType.singleFile,
|
||||
buildFormData: buildRemoveImageFormData,
|
||||
toApiParams,
|
||||
fromApiParams,
|
||||
operationType: "removeImage",
|
||||
endpoint: ENDPOINT,
|
||||
};
|
||||
export const removeImageOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRemoveImageFormData,
|
||||
toApiParams,
|
||||
fromApiParams,
|
||||
operationType: "removeImage",
|
||||
endpoint: ENDPOINT,
|
||||
});
|
||||
|
||||
export const useRemoveImageOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -42,15 +41,14 @@ export const buildRemovePagesFormData = (
|
||||
): FormData =>
|
||||
objectToFormData(removePagesToApiParams(parameters), { fileInput: file });
|
||||
|
||||
export const removePagesOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const removePagesOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRemovePagesFormData,
|
||||
toApiParams: removePagesToApiParams,
|
||||
fromApiParams: removePagesFromApiParams,
|
||||
operationType: "removePages",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const satisfies ToolOperationConfig<RemovePagesParameters>;
|
||||
});
|
||||
|
||||
export const useRemovePagesOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import { createStandardErrorHandler } from "@app/utils/toolErrorHandler";
|
||||
import {
|
||||
@@ -19,15 +19,14 @@ import {
|
||||
export { buildRemovePasswordFormData };
|
||||
|
||||
// Static configuration object
|
||||
export const removePasswordOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const removePasswordOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRemovePasswordFormData,
|
||||
toApiParams: removePasswordToApiParams,
|
||||
fromApiParams: removePasswordFromApiParams,
|
||||
operationType: "removePassword",
|
||||
endpoint: REMOVE_PASSWORD_ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useRemovePasswordOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+8
-11
@@ -1,7 +1,6 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolOperationConfig,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
useToolOperation,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
@@ -54,15 +53,13 @@ const buildFormData = (
|
||||
fileInput: file,
|
||||
});
|
||||
|
||||
export const reorganizePagesOperationConfig: ToolOperationConfig<ReorganizePagesParameters> =
|
||||
{
|
||||
toolType: ToolType.singleFile,
|
||||
buildFormData,
|
||||
toApiParams: reorganizePagesToApiParams,
|
||||
fromApiParams: reorganizePagesFromApiParams,
|
||||
operationType: "reorganizePages",
|
||||
endpoint: ENDPOINT,
|
||||
};
|
||||
export const reorganizePagesOperationConfig = defineSingleFileTool({
|
||||
buildFormData,
|
||||
toApiParams: reorganizePagesToApiParams,
|
||||
fromApiParams: reorganizePagesFromApiParams,
|
||||
operationType: "reorganizePages",
|
||||
endpoint: ENDPOINT,
|
||||
});
|
||||
|
||||
export const useReorganizePagesOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
fileOnlyMapping,
|
||||
@@ -25,15 +25,14 @@ export const buildRepairFormData = (
|
||||
): FormData => objectToFormData(toApiParams(), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const repairOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const repairOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRepairFormData,
|
||||
toApiParams,
|
||||
fromApiParams,
|
||||
operationType: "repair",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useRepairOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -62,16 +62,14 @@ export const buildReplaceColorFormData = (
|
||||
): FormData =>
|
||||
objectToFormData(replaceColorToApiParams(parameters), { fileInput: file });
|
||||
|
||||
export const replaceColorOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const replaceColorOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildReplaceColorFormData,
|
||||
toApiParams: replaceColorToApiParams,
|
||||
fromApiParams: replaceColorFromApiParams,
|
||||
operationType: "replaceColor",
|
||||
endpoint: ENDPOINT,
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useReplaceColorOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -43,15 +43,14 @@ export const buildRotateFormData = (
|
||||
objectToFormData(rotateToApiParams(parameters), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const rotateOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const rotateOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildRotateFormData,
|
||||
toApiParams: rotateToApiParams,
|
||||
fromApiParams: rotateFromApiParams,
|
||||
operationType: "rotate",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useRotateOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -55,16 +55,14 @@ export const buildSanitizeFormData = (
|
||||
objectToFormData(sanitizeToApiParams(parameters), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const sanitizeOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const sanitizeOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildSanitizeFormData,
|
||||
toApiParams: sanitizeToApiParams,
|
||||
fromApiParams: sanitizeFromApiParams,
|
||||
operationType: "sanitize",
|
||||
endpoint: ENDPOINT,
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useSanitizeOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+3
-4
@@ -1,9 +1,9 @@
|
||||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -54,15 +54,14 @@ export const buildScannerImageSplitFormData = (
|
||||
});
|
||||
|
||||
// Static configuration object
|
||||
export const scannerImageSplitOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const scannerImageSplitOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildScannerImageSplitFormData,
|
||||
toApiParams: scannerImageSplitToApiParams,
|
||||
fromApiParams: scannerImageSplitFromApiParams,
|
||||
operationType: "scannerImageSplit",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useScannerImageSplitOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -117,7 +117,7 @@ describe("migrated tool mappers (sweep)", () => {
|
||||
|
||||
describe("redact mappers", () => {
|
||||
test("toApiParams builds the auto-redact body from UI parameters", () => {
|
||||
const api = redactOperationConfig.toApiParams({
|
||||
const api = redactOperationConfig.toApiParams!({
|
||||
mode: "automatic",
|
||||
wordsToRedact: ["foo", "bar"],
|
||||
useRegex: true,
|
||||
@@ -138,7 +138,7 @@ describe("redact mappers", () => {
|
||||
});
|
||||
|
||||
test("round-trips through fromApiParams", () => {
|
||||
const api = redactOperationConfig.toApiParams({
|
||||
const api = redactOperationConfig.toApiParams!({
|
||||
mode: "automatic",
|
||||
wordsToRedact: ["secret"],
|
||||
useRegex: false,
|
||||
@@ -147,7 +147,7 @@ describe("redact mappers", () => {
|
||||
customPadding: 0.1,
|
||||
convertPDFToImage: true,
|
||||
});
|
||||
const roundTripped = redactOperationConfig.toApiParams({
|
||||
const roundTripped = redactOperationConfig.toApiParams!({
|
||||
mode: "automatic",
|
||||
wordsToRedact: [],
|
||||
useRegex: false,
|
||||
@@ -155,7 +155,7 @@ describe("redact mappers", () => {
|
||||
redactColor: "#000000",
|
||||
customPadding: 0,
|
||||
convertPDFToImage: false,
|
||||
...redactOperationConfig.fromApiParams(api),
|
||||
...redactOperationConfig.fromApiParams!(api),
|
||||
});
|
||||
|
||||
expect(roundTripped).toEqual(api);
|
||||
|
||||
@@ -3,7 +3,7 @@ import { StirlingFile } from "@app/types/fileContext";
|
||||
import type { ResponseHandler } from "@app/utils/toolResponseProcessor";
|
||||
import { ToolId } from "@app/types/toolId";
|
||||
import type { ProcessingProgress } from "@app/hooks/tools/shared/useToolState";
|
||||
import type { ToolApiRequest, ToolEndpoint } from "@app/types/toolApiTypes";
|
||||
import type { ToolApiParams, ToolEndpoint } from "@app/types/toolApiTypes";
|
||||
|
||||
export type { ProcessingProgress, ResponseHandler };
|
||||
|
||||
@@ -53,7 +53,7 @@ export interface CustomProcessorResult {
|
||||
* 2. Multi-file tools: toolType: multiFile, single API call with all files
|
||||
* 3. Complex tools: toolType: custom, customProcessor handles all processing logic
|
||||
*/
|
||||
interface BaseToolOperationConfig<TParams> {
|
||||
interface BaseToolOperationConfig<TParams, TEndpoint extends ToolEndpoint> {
|
||||
/** Operation identifier for tracking and logging */
|
||||
operationType: ToolId;
|
||||
|
||||
@@ -82,15 +82,16 @@ interface BaseToolOperationConfig<TParams> {
|
||||
/**
|
||||
* Typed frontend params -> backend request model. When a tool provides this,
|
||||
* it is the spec-checked source of truth for the request body and its
|
||||
* buildFormData is derived from it via objectToFormData.
|
||||
* buildFormData is derived from it via objectToFormData. Bound to the tool's
|
||||
* endpoint, so a spec rename of that endpoint's model breaks the build here.
|
||||
*/
|
||||
toApiParams?(params: TParams): ToolApiRequest;
|
||||
toApiParams?(params: TParams): ToolApiParams[TEndpoint];
|
||||
|
||||
/**
|
||||
* Backend request model -> partial frontend params, so a stored API call
|
||||
* can be re-hydrated into this tool's settings UI.
|
||||
*/
|
||||
fromApiParams?(apiParams: ToolApiRequest): Partial<TParams>;
|
||||
fromApiParams?(apiParams: ToolApiParams[TEndpoint]): Partial<TParams>;
|
||||
|
||||
/**
|
||||
* For custom tools: if true, success implies all input files were successfully processed.
|
||||
@@ -102,7 +103,8 @@ interface BaseToolOperationConfig<TParams> {
|
||||
|
||||
export interface SingleFileToolOperationConfig<
|
||||
TParams,
|
||||
> extends BaseToolOperationConfig<TParams> {
|
||||
TEndpoint extends ToolEndpoint = ToolEndpoint,
|
||||
> extends BaseToolOperationConfig<TParams, TEndpoint> {
|
||||
/** This tool processes one file at a time. */
|
||||
toolType: ToolType.singleFile;
|
||||
|
||||
@@ -110,18 +112,18 @@ export interface SingleFileToolOperationConfig<
|
||||
buildFormData: (params: TParams, file: File) => FormData;
|
||||
|
||||
/**
|
||||
* API endpoint for the operation. Can be static or a function for dynamic routing.
|
||||
* API endpoint for the operation, or a function for dynamic routing. `null`
|
||||
* when the operation has no backend endpoint (see {@link ToolOperationEndpoint}).
|
||||
*/
|
||||
endpoint:
|
||||
| ToolOperationEndpoint
|
||||
| ((params: TParams) => ToolOperationEndpoint);
|
||||
endpoint: TEndpoint | null | ((params: TParams) => TEndpoint | null);
|
||||
|
||||
customProcessor?: undefined;
|
||||
}
|
||||
|
||||
export interface MultiFileToolOperationConfig<
|
||||
TParams,
|
||||
> extends BaseToolOperationConfig<TParams> {
|
||||
TEndpoint extends ToolEndpoint = ToolEndpoint,
|
||||
> extends BaseToolOperationConfig<TParams, TEndpoint> {
|
||||
/** This tool processes multiple files at once. */
|
||||
toolType: ToolType.multiFile;
|
||||
|
||||
@@ -132,18 +134,17 @@ export interface MultiFileToolOperationConfig<
|
||||
buildFormData: (params: TParams, files: File[]) => FormData;
|
||||
|
||||
/**
|
||||
* API endpoint for the operation. Can be static or a function for dynamic routing.
|
||||
* API endpoint for the operation, or a function for dynamic routing. `null`
|
||||
* when the operation has no backend endpoint (see {@link ToolOperationEndpoint}).
|
||||
*/
|
||||
endpoint:
|
||||
| ToolOperationEndpoint
|
||||
| ((params: TParams) => ToolOperationEndpoint);
|
||||
endpoint: TEndpoint | null | ((params: TParams) => TEndpoint | null);
|
||||
|
||||
customProcessor?: undefined;
|
||||
}
|
||||
|
||||
export interface CustomToolOperationConfig<
|
||||
TParams,
|
||||
> extends BaseToolOperationConfig<TParams> {
|
||||
> extends BaseToolOperationConfig<TParams, ToolEndpoint> {
|
||||
/** This tool has custom behaviour. */
|
||||
toolType: ToolType.custom;
|
||||
|
||||
@@ -171,11 +172,50 @@ export interface CustomToolOperationConfig<
|
||||
) => Promise<CustomProcessorResult>;
|
||||
}
|
||||
|
||||
export type ToolOperationConfig<TParams = void> =
|
||||
| SingleFileToolOperationConfig<TParams>
|
||||
| MultiFileToolOperationConfig<TParams>
|
||||
export type ToolOperationConfig<
|
||||
TParams = void,
|
||||
TEndpoint extends ToolEndpoint = ToolEndpoint,
|
||||
> =
|
||||
| SingleFileToolOperationConfig<TParams, TEndpoint>
|
||||
| MultiFileToolOperationConfig<TParams, TEndpoint>
|
||||
| CustomToolOperationConfig<TParams>;
|
||||
|
||||
/**
|
||||
* Define a single-file tool's operation config. Infers the endpoint literal from
|
||||
* `endpoint` and binds toApiParams/fromApiParams to that endpoint's request
|
||||
* model, so a mapper cannot silently drift from the generated spec.
|
||||
*/
|
||||
export function defineSingleFileTool<
|
||||
TParams,
|
||||
const TEndpoint extends ToolEndpoint,
|
||||
>(
|
||||
config: Omit<SingleFileToolOperationConfig<TParams, TEndpoint>, "toolType">,
|
||||
): SingleFileToolOperationConfig<TParams, TEndpoint> {
|
||||
return { ...config, toolType: ToolType.singleFile };
|
||||
}
|
||||
|
||||
/** Multi-file counterpart of {@link defineSingleFileTool}. */
|
||||
export function defineMultiFileTool<
|
||||
TParams,
|
||||
const TEndpoint extends ToolEndpoint,
|
||||
>(
|
||||
config: Omit<MultiFileToolOperationConfig<TParams, TEndpoint>, "toolType">,
|
||||
): MultiFileToolOperationConfig<TParams, TEndpoint> {
|
||||
return { ...config, toolType: ToolType.multiFile };
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom-processor counterpart of {@link defineSingleFileTool}, for tools whose
|
||||
* customProcessor owns the API calls and file handling. Rejects fields that
|
||||
* belong to the file-based patterns (e.g. buildFormData) and any property not on
|
||||
* the config, so a stray or stale field is a build error rather than dead weight.
|
||||
*/
|
||||
export function defineCustomTool<TParams>(
|
||||
config: Omit<CustomToolOperationConfig<TParams>, "toolType">,
|
||||
): CustomToolOperationConfig<TParams> {
|
||||
return { ...config, toolType: ToolType.custom };
|
||||
}
|
||||
|
||||
/**
|
||||
* One generic source-of-truth for the props every automation settings component
|
||||
* accepts: the tool's parameters plus a typed change handler.
|
||||
|
||||
@@ -40,6 +40,9 @@ import {
|
||||
} from "@app/hooks/tools/shared/toolOperationHelpers";
|
||||
import {
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
defineMultiFileTool,
|
||||
defineCustomTool,
|
||||
ToolOperationConfig,
|
||||
ToolOperationHook,
|
||||
CustomProcessorResult,
|
||||
@@ -50,7 +53,12 @@ import {
|
||||
ResponseHandler,
|
||||
} from "@app/hooks/tools/shared/toolOperationTypes";
|
||||
|
||||
export { ToolType };
|
||||
export {
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
defineMultiFileTool,
|
||||
defineCustomTool,
|
||||
};
|
||||
export type {
|
||||
ToolOperationConfig,
|
||||
ToolOperationHook,
|
||||
@@ -69,10 +77,10 @@ export { createStandardErrorHandler } from "@app/utils/toolErrorHandler";
|
||||
* Shared hook for tool operations providing consistent error handling, progress tracking,
|
||||
* and FileContext integration. Eliminates boilerplate while maintaining flexibility.
|
||||
*
|
||||
* Supports three tool patterns:
|
||||
* 1. Single-file tools: Set multiFileEndpoint: false, processes files individually
|
||||
* 2. Multi-file tools: Set multiFileEndpoint: true, single API call with all files
|
||||
* 3. Complex tools: Provide customProcessor for full control over processing logic
|
||||
* Supports three tool patterns, selected by the config's toolType:
|
||||
* 1. Single-file tools (ToolType.singleFile): processes files individually
|
||||
* 2. Multi-file tools (ToolType.multiFile): single API call with all files
|
||||
* 3. Complex tools (ToolType.custom): customProcessor takes full control
|
||||
*
|
||||
* @param config - Tool operation configuration
|
||||
* @returns Hook interface with state and execution methods
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
useToolOperation,
|
||||
ToolOperationHook,
|
||||
ToolType,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
SignParameters,
|
||||
@@ -50,8 +50,7 @@ export const buildSignFormData = (
|
||||
};
|
||||
|
||||
// Static configuration object
|
||||
export const signOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const signOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildSignFormData,
|
||||
operationType: "sign",
|
||||
// Signing is applied client-side in the viewer (see createStampTool ->
|
||||
@@ -60,7 +59,7 @@ export const signOperationConfig = {
|
||||
endpoint: null,
|
||||
filePrefix: "signed_",
|
||||
defaultParameters: DEFAULT_PARAMETERS,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useSignOperation = (): ToolOperationHook<SignParameters> => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
fileOnlyMapping,
|
||||
@@ -26,15 +26,14 @@ export const buildSingleLargePageFormData = (
|
||||
): FormData => objectToFormData(toApiParams(), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const singleLargePageOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const singleLargePageOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildSingleLargePageFormData,
|
||||
toApiParams,
|
||||
fromApiParams,
|
||||
operationType: "pdfToSinglePage",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useSingleLargePageOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
ToolOperationConfig,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -33,7 +33,8 @@ const SPLIT_ENDPOINTS = {
|
||||
[SPLIT_METHODS.BY_POSTER]: "/api/v1/general/split-for-poster-print",
|
||||
} as const satisfies Record<SplitMethod, ToolEndpoint>;
|
||||
|
||||
type SplitApiParams = ToolApiParams[(typeof SPLIT_ENDPOINTS)[SplitMethod]];
|
||||
type SplitEndpoint = (typeof SPLIT_ENDPOINTS)[SplitMethod];
|
||||
type SplitApiParams = ToolApiParams[SplitEndpoint];
|
||||
type SectionsApiParams =
|
||||
ToolApiParams[(typeof SPLIT_ENDPOINTS)[typeof SPLIT_METHODS.BY_SECTIONS]];
|
||||
type PosterApiParams =
|
||||
@@ -165,20 +166,19 @@ export const buildSplitFormData = (
|
||||
): FormData =>
|
||||
objectToFormData(splitToApiParams(parameters), { fileInput: file });
|
||||
|
||||
export const getSplitEndpoint = (parameters: SplitParameters): ToolEndpoint =>
|
||||
export const getSplitEndpoint = (parameters: SplitParameters): SplitEndpoint =>
|
||||
// Default to BY_PAGES when no method is selected yet.
|
||||
SPLIT_ENDPOINTS[parameters.method ?? SPLIT_METHODS.BY_PAGES];
|
||||
|
||||
// Static configuration object
|
||||
export const splitOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const splitOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildSplitFormData,
|
||||
toApiParams: splitToApiParams,
|
||||
fromApiParams: splitFromApiParams,
|
||||
operationType: "split",
|
||||
endpoint: getSplitEndpoint,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useSplitOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
objectToFormData,
|
||||
@@ -35,16 +35,14 @@ export const buildTimestampPdfFormData = (
|
||||
): FormData =>
|
||||
objectToFormData(timestampPdfToApiParams(parameters), { fileInput: file });
|
||||
|
||||
export const timestampPdfOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const timestampPdfOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildTimestampPdfFormData,
|
||||
toApiParams: timestampPdfToApiParams,
|
||||
fromApiParams: timestampPdfFromApiParams,
|
||||
operationType: "timestampPdf",
|
||||
endpoint: ENDPOINT,
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useTimestampPdfOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ToolType,
|
||||
useToolOperation,
|
||||
defineSingleFileTool,
|
||||
} from "@app/hooks/tools/shared/useToolOperation";
|
||||
import {
|
||||
fileOnlyMapping,
|
||||
@@ -26,15 +26,14 @@ export const buildUnlockPdfFormsFormData = (
|
||||
): FormData => objectToFormData(toApiParams(), { fileInput: file });
|
||||
|
||||
// Static configuration object
|
||||
export const unlockPdfFormsOperationConfig = {
|
||||
toolType: ToolType.singleFile,
|
||||
export const unlockPdfFormsOperationConfig = defineSingleFileTool({
|
||||
buildFormData: buildUnlockPdfFormsFormData,
|
||||
toApiParams,
|
||||
fromApiParams,
|
||||
operationType: "unlockPDFForms",
|
||||
endpoint: ENDPOINT,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
});
|
||||
|
||||
export const useUnlockPdfFormsOperation = () => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
+8
-9
@@ -1,7 +1,6 @@
|
||||
import apiClient from "@app/services/apiClient";
|
||||
import {
|
||||
ToolType,
|
||||
CustomToolOperationConfig,
|
||||
defineCustomTool,
|
||||
CustomProcessorResult,
|
||||
} from "@app/hooks/tools/shared/toolOperationTypes";
|
||||
import {
|
||||
@@ -104,10 +103,10 @@ const processPdfCommentAgent = async (
|
||||
return { files: [resultFile] };
|
||||
};
|
||||
|
||||
export const pdfCommentAgentOperationConfig = {
|
||||
toolType: ToolType.custom,
|
||||
operationType: "pdfCommentAgent",
|
||||
endpoint: PDF_COMMENT_AGENT_ENDPOINT,
|
||||
customProcessor: processPdfCommentAgent,
|
||||
defaultParameters,
|
||||
} as const satisfies CustomToolOperationConfig<PdfCommentAgentParameters>;
|
||||
export const pdfCommentAgentOperationConfig =
|
||||
defineCustomTool<PdfCommentAgentParameters>({
|
||||
operationType: "pdfCommentAgent",
|
||||
endpoint: PDF_COMMENT_AGENT_ENDPOINT,
|
||||
customProcessor: processPdfCommentAgent,
|
||||
defaultParameters,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user