Improve script error dialogs #304

- Include the script's directory path #304.
- Exclude Windows-specific instructions on non-Windows OS.
- Standardize language across dialogs for consistency.

Other supporting changes:

- Add script diagnostics data collection from main process.
- Document script file storage and execution tamper protection in
  SECURITY.md.
- Remove redundant comment in `NodeReadbackFileWriter`.
- Centralize error display for uniformity and simplicity.
- Simpify `WindowVariablesValidator` to omit checks when not on the
  renderer process.
- Improve and centralize Electron environment detection.
- Use more emphatic language (don't worry) in error messages.
This commit is contained in:
undergroundwires
2024-01-17 23:59:05 +01:00
parent f03fc24098
commit 6ada8d425c
34 changed files with 1182 additions and 450 deletions

View File

@@ -3,17 +3,22 @@ import { CodeRunner } from '@/application/CodeRunner/CodeRunner';
import { Dialog } from '@/presentation/common/Dialog';
import { ElectronDialog } from '@/infrastructure/Dialog/Electron/ElectronDialog';
import { IpcChannel } from '@/presentation/electron/shared/IpcBridging/IpcChannel';
import { ScriptEnvironmentDiagnosticsCollector } from '@/infrastructure/ScriptDiagnostics/ScriptEnvironmentDiagnosticsCollector';
import { ScriptDiagnosticsCollector } from '@/application/ScriptDiagnostics/ScriptDiagnosticsCollector';
import { registerIpcChannel } from '../shared/IpcBridging/IpcProxy';
import { ChannelDefinitionKey, IpcChannelDefinitions } from '../shared/IpcBridging/IpcChannelDefinitions';
export function registerAllIpcChannels(
registrar: IpcChannelRegistrar = registerIpcChannel,
createCodeRunner: CodeRunnerFactory = () => new ScriptFileCodeRunner(),
createDialog: DialogFactory = () => new ElectronDialog(),
registrar: IpcChannelRegistrar = registerIpcChannel,
createScriptDiagnosticsCollector
: ScriptDiagnosticsCollectorFactory = () => new ScriptEnvironmentDiagnosticsCollector(),
) {
const ipcInstanceCreators: IpcChannelRegistrars = {
CodeRunner: () => createCodeRunner(),
Dialog: () => createDialog(),
ScriptDiagnosticsCollector: () => createScriptDiagnosticsCollector(),
};
Object.entries(ipcInstanceCreators).forEach(([name, instanceFactory]) => {
try {
@@ -26,9 +31,11 @@ export function registerAllIpcChannels(
});
}
export type IpcChannelRegistrar = typeof registerIpcChannel;
export type CodeRunnerFactory = () => CodeRunner;
export type DialogFactory = () => Dialog;
export type IpcChannelRegistrar = typeof registerIpcChannel;
export type ScriptDiagnosticsCollectorFactory = () => ScriptDiagnosticsCollector;
type RegistrationChannel<T extends ChannelDefinitionKey> = (typeof IpcChannelDefinitions)[T];
type ExtractChannelServiceType<T> = T extends IpcChannel<infer U> ? U : never;

View File

@@ -7,10 +7,10 @@ import { IpcChannelDefinitions } from '../../shared/IpcBridging/IpcChannelDefini
import { createSecureFacade } from './SecureFacadeCreator';
export function provideWindowVariables(
createLogger: LoggerFactory = () => createElectronLogger(),
convertToOs = convertPlatformToOs,
createApiFacade: ApiFacadeFactory = createSecureFacade,
ipcConsumerCreator: IpcConsumerProxyCreator = createIpcConsumerProxy,
convertToOs = convertPlatformToOs,
createLogger: LoggerFactory = () => createElectronLogger(),
): WindowVariables {
// Enforces mandatory variable availability at compile time
const variables: RequiredWindowVariables = {
@@ -19,6 +19,9 @@ export function provideWindowVariables(
os: convertToOs(process.platform),
codeRunner: ipcConsumerCreator(IpcChannelDefinitions.CodeRunner),
dialog: ipcConsumerCreator(IpcChannelDefinitions.Dialog),
scriptDiagnosticsCollector: ipcConsumerCreator(
IpcChannelDefinitions.ScriptDiagnosticsCollector,
),
};
return variables;
}
@@ -26,8 +29,8 @@ export function provideWindowVariables(
type RequiredWindowVariables = PartiallyRequired<WindowVariables, 'os' /* | 'anotherOptionalKey'.. */>;
type PartiallyRequired<T, K extends keyof T> = Required<Omit<T, K>> & Pick<T, K>;
export type LoggerFactory = () => Logger;
export type ApiFacadeFactory = typeof createSecureFacade;
export type IpcConsumerProxyCreator = typeof createIpcConsumerProxy;
export type LoggerFactory = () => Logger;

View File

@@ -1,11 +1,13 @@
import { FunctionKeys } from '@/TypeHelpers';
import { CodeRunner } from '@/application/CodeRunner/CodeRunner';
import { Dialog } from '@/presentation/common/Dialog';
import { ScriptDiagnosticsCollector } from '@/application/ScriptDiagnostics/ScriptDiagnosticsCollector';
import { IpcChannel } from './IpcChannel';
export const IpcChannelDefinitions = {
CodeRunner: defineElectronIpcChannel<CodeRunner>('code-run', ['runCode']),
Dialog: defineElectronIpcChannel<Dialog>('dialogs', ['showError', 'saveFile']),
ScriptDiagnosticsCollector: defineElectronIpcChannel<ScriptDiagnosticsCollector>('script-diagnostics-collector', ['collectDiagnosticInformation']),
} as const;
export type ChannelDefinitionKey = keyof typeof IpcChannelDefinitions;