Skip to content

Commit

Permalink
✨ (core) [DSDK-335]: Limit the usage of as keyword for type asserti…
Browse files Browse the repository at this point in the history
…on (#96)
  • Loading branch information
jiyuzhuang authored Jun 12, 2024
2 parents 722c654 + a397fca commit d57b00c
Show file tree
Hide file tree
Showing 19 changed files with 288 additions and 200 deletions.
2 changes: 1 addition & 1 deletion apps/sample/src/components/ApduView/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ export const ApduView: React.FC = () => {
try {
rawApduResponse = await sdk.sendApdu({
// eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion
sessionId: selectedSessionId!,
sessionId: selectedSessionId ?? "",
apdu: getRawApdu(values),
});
setApduResponse(rawApduResponse);
Expand Down
2 changes: 1 addition & 1 deletion apps/sample/src/components/Sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const Sidebar: React.FC = () => {
.getVersion()
.then((v) => setVersion(v))
.catch((error: unknown) => {
console.error(error as Error);
console.error(new Error(String(error)));
setVersion("");
});
}, [sdk]);
Expand Down
2 changes: 1 addition & 1 deletion apps/sample/src/hooks/useApduForm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ export function useApduForm() {
.map((char) => Number(`0x${char}`))
.filter((nbr) => !Number.isNaN(nbr)),
],
[] as number[],
Array<number>(),
),
),
[],
Expand Down
9 changes: 8 additions & 1 deletion packages/config/eslint/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const project = resolve(process.cwd(), "tsconfig.json");
/** @type {import("eslint").Linter.Config} */
module.exports = {
extends: ["eslint:recommended", "prettier", "turbo"],
plugins: ["simple-import-sort"],
plugins: ["no-type-assertion", "simple-import-sort"],
globals: {
React: true,
JSX: true,
Expand Down Expand Up @@ -82,6 +82,13 @@ module.exports = {
"error",
{ argsIgnorePattern: "^_" },
],
"no-type-assertion/no-type-assertion": "error",
},
},
{
files: ["**/*.test.ts", "**/*.stub.ts"],
rules: {
"no-type-assertion/no-type-assertion": "off",
},
},
],
Expand Down
1 change: 1 addition & 0 deletions packages/config/eslint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"@vercel/style-guide": "^6.0.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-turbo": "^1.13.2",
"eslint-plugin-no-type-assertion": "^1.3.0",
"eslint-plugin-simple-import-sort": "^12.0.0"
}
}
2 changes: 1 addition & 1 deletion packages/core/src/api/Error.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export interface SdkError {
readonly _tag: string;
originalError?: Error;
originalError?: unknown;
// [could] message?: string;
}
6 changes: 2 additions & 4 deletions packages/core/src/api/apdu/utils/ApduParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,14 @@ export class ApduParser {
extractFieldTLVEncoded(): TaggedField | undefined {
if (this._outOfRange(2)) return;

// extract the tag field
const tag = this.extract8BitUint();
const value = this.extractFieldLVEncoded();

// if the field is inconsistent then roll back to the initial point
if (!value) {
if (!tag || !value) {
this._index--;
return;
}
return { tag, value } as TaggedField;
return { tag, value };
}

/**
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/api/command/os/GetAppAndVersionCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ export class GetAppAndVersionCommand
const version = parser.encodeToString(parser.extractFieldLVEncoded());

if (parser.getUnparsedRemainingLength() === 0) {
return { name, version } as GetAppAndVersionResponse;
return { name, version };
}

const flags = parser.extractFieldLVEncoded();
return { name, version, flags } as GetAppAndVersionResponse;
return { name, version, flags };
}
}
23 changes: 14 additions & 9 deletions packages/core/src/internal/config/data/LocalConfigDataSource.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { injectable } from "inversify";
import { Either } from "purify-ts";

import { Config } from "@internal/config/model/Config";
import { Config, isConfig } from "@internal/config/model/Config";
import {
JSONParseError,
LocalConfigFailure,
Expand All @@ -18,21 +18,26 @@ const version = {

export const stubFsReadFile = () => JSON.stringify(version);

@injectable()
/**
*
* class FileLocalConfigDataSource
* This is a local data source that reads the config from a local file.
*
* The data source for retrieving local configuration.
*/
@injectable()
export class FileLocalConfigDataSource implements LocalConfigDataSource {
/**
* Retrieves the local configuration.
* @returns An `Either` containing either a `LocalConfigFailure` or a `Config` object.
*/
getConfig(): Either<LocalConfigFailure, Config> {
return Either.encase(() => stubFsReadFile())
.mapLeft((error) => new ReadFileError(error))
.chain((str) => {
return Either.encase(() => JSON.parse(str) as Config).mapLeft(
(error) => new JSONParseError(error),
);
return Either.encase(() => {
const config: unknown = JSON.parse(str);
if (isConfig(config)) {
return config;
}
throw new Error("Invalid config file");
}).mapLeft((error) => new JSONParseError(error));
});
}
}
16 changes: 16 additions & 0 deletions packages/core/src/internal/config/model/Config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,19 @@ export type Config = {
version: string;
name: string;
};

/**
* Checks if the provided object is a valid Config object.
* @param obj - The object to be checked.
* @returns A boolean indicating whether the object is a Config object.
*/
export function isConfig(obj: unknown): obj is Config {
return (
typeof obj === "object" &&
obj !== null &&
"version" in obj &&
"name" in obj &&
typeof obj.version === "string" &&
typeof obj.name === "string"
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export class StaticDeviceModelDataSource implements DeviceModelDataSource {
): InternalDeviceModel[] {
return this.getAllDeviceModels().filter((deviceModel) => {
return Object.entries(params).every(([key, value]) => {
// eslint-disable-next-line no-type-assertion/no-type-assertion
return deviceModel[key as keyof InternalDeviceModel] === value;
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,15 @@ export class DeviceSessionRefresher {
filter((parsedResponse) => parsedResponse !== null),
)
.subscribe((parsedResponse: GetAppAndVersionResponse | null) => {
// batteryStatus and firmwareVersion are not available in the polling response.
// This should never happen and it should be abled to handle in next version of TypeScript.
if (parsedResponse === null) {
return;
}
// `batteryStatus` and `firmwareVersion` are not available in the polling response.
updateStateFn({
sessionStateType: DeviceSessionStateType.ReadyWithoutSecureChannel,
deviceStatus: this._deviceStatus,
currentApp: parsedResponse!.name,
currentApp: parsedResponse.name,
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,10 @@ export class DefaultApduReceiverService implements ApduReceiverService {
data: { frame: value.getRawData() },
});
this._pendingFrames.push(value);

const dataSize = this._pendingFrames[0]!.getHeader().getDataLength();
if (!this._pendingFrames[0]) {
return Nothing;
}
const dataSize = this._pendingFrames[0].getHeader().getDataLength();
return this.getCompleteFrame(dataSize);
});
}
Expand Down
74 changes: 39 additions & 35 deletions packages/core/src/internal/usb/model/Errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,63 @@ export type PromptDeviceAccessError =

export type ConnectError = UnknownDeviceError | OpeningConnectionError;

export class DeviceNotRecognizedError implements SdkError {
readonly _tag = "DeviceNotRecognizedError";
originalError?: Error;
constructor(readonly err?: Error) {
this.originalError = err;
class GeneralSdkError implements SdkError {
_tag = "GeneralSdkError";
originalError?: unknown;
constructor(err?: unknown) {
if (err instanceof Error) {
this.originalError = err;
} else {
this.originalError = new Error(String(err));
}
}
}

export class NoAccessibleDeviceError implements SdkError {
readonly _tag = "NoAccessibleDeviceError";
originalError?: Error;
constructor(readonly err?: Error) {
this.originalError = err;
export class DeviceNotRecognizedError extends GeneralSdkError {
override readonly _tag = "DeviceNotRecognizedError";
constructor(readonly err?: unknown) {
super(err);
}
}

export class OpeningConnectionError implements SdkError {
readonly _tag = "ConnectionOpeningError";
originalError?: Error;
constructor(readonly err?: Error) {
this.originalError = err;
export class NoAccessibleDeviceError extends GeneralSdkError {
override readonly _tag = "NoAccessibleDeviceError";
constructor(readonly err?: unknown) {
super(err);
}
}

export class UnknownDeviceError implements SdkError {
readonly _tag = "UnknownDeviceError";
originalError?: Error;
constructor(readonly err?: Error) {
this.originalError = err;
export class OpeningConnectionError extends GeneralSdkError {
override readonly _tag = "ConnectionOpeningError";
constructor(readonly err?: unknown) {
super(err);
}
}

export class UsbHidTransportNotSupportedError implements SdkError {
readonly _tag = "UsbHidTransportNotSupportedError";
originalError?: Error;
constructor(readonly err?: Error) {
this.originalError = err;
export class UnknownDeviceError extends GeneralSdkError {
override readonly _tag = "UnknownDeviceError";
constructor(readonly err?: unknown) {
super(err);
}
}

export class SendApduConcurrencyError implements SdkError {
readonly _tag = "SendApduConcurrencyError";
originalError?: Error;
constructor(readonly err?: Error) {
this.originalError = err;
export class UsbHidTransportNotSupportedError extends GeneralSdkError {
override readonly _tag = "UsbHidTransportNotSupportedError";
constructor(readonly err?: unknown) {
super(err);
}
}

export class DisconnectError implements SdkError {
readonly _tag = "DisconnectError";
originalError?: Error;
export class SendApduConcurrencyError extends GeneralSdkError {
override readonly _tag = "SendApduConcurrencyError";
constructor(readonly err?: unknown) {
super(err);
}
}

constructor(readonly err?: Error) {
this.originalError = err;
export class DisconnectError extends GeneralSdkError {
override readonly _tag = "DisconnectError";
constructor(readonly err?: unknown) {
super(err);
}
}
4 changes: 2 additions & 2 deletions packages/core/src/internal/usb/model/HIDDevice.stub.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const oninputreport = jest.fn();
const oninputreport = jest.fn().mockResolvedValue(void 0);
export const hidDeviceStubBuilder = (
props: Partial<HIDDevice> = {},
): HIDDevice => ({
Expand All @@ -10,7 +10,7 @@ export const hidDeviceStubBuilder = (
open: jest.fn(),
oninputreport,
close: jest.fn(),
sendReport: jest.fn(async () => oninputreport()),
sendReport: jest.fn().mockImplementation(() => oninputreport()),
sendFeatureReport: jest.fn(),
forget: jest.fn(),
receiveFeatureReport: jest.fn(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe("UsbHidDeviceConnection", () => {
expect(cDevice).toStrictEqual(device);
});

it("should send APDU through hid report", async () => {
it("should send APDU through hid report", () => {
// given
const connection = new UsbHidDeviceConnection(
{ device, apduSender, apduReceiver },
Expand All @@ -50,7 +50,7 @@ describe("UsbHidDeviceConnection", () => {
expect(device.sendReport).toHaveBeenCalled();
});

it("should receive APDU through hid report", async () => {
it("should receive APDU through hid report", () => {
// given
device.sendReport = jest.fn(() =>
Promise.resolve(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ describe("WebUsbHidTransport", () => {
const connect = await transport.connect(connectParams);

expect(connect).toStrictEqual(
Left(new UnknownDeviceError(new Error("Unknown device fake"))),
Left(new UnknownDeviceError("Unknown device fake")),
);
});

Expand All @@ -254,7 +254,7 @@ describe("WebUsbHidTransport", () => {
const connect = await transport.connect(device);

expect(connect).toStrictEqual(
Left(new UnknownDeviceError(new Error("Unknown device fake"))),
Left(new UnknownDeviceError("Unknown device fake")),
);
});

Expand Down Expand Up @@ -376,11 +376,7 @@ describe("WebUsbHidTransport", () => {
});

expect(disconnect).toStrictEqual(
Left(
new UnknownDeviceError(
new Error(`Unknown device ${connectedDevice.id}`),
),
),
Left(new UnknownDeviceError(`Unknown device ${connectedDevice.id}`)),
);
});

Expand Down
Loading

0 comments on commit d57b00c

Please sign in to comment.