Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions packages/snaps-rpc-methods/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@ module.exports = deepmerge(baseConfig, {
],
coverageThreshold: {
global: {
branches: 94.98,
functions: 98.64,
lines: 98.76,
statements: 98.45,
branches: 94.93,
functions: 98.63,
lines: 98.75,
statements: 98.44,
},
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,12 @@ import {
type ScheduleBackgroundEventResult,
} from '@metamask/snaps-sdk';
import type { CronjobRpcRequest, InferMatching } from '@metamask/snaps-utils';
import { CronjobRpcRequestStruct } from '@metamask/snaps-utils';
import {
StructError,
create,
object,
refine,
string,
} from '@metamask/superstruct';
CronjobRpcRequestStruct,
Iso8601DateStruct,
Iso8601DurationStruct,
} from '@metamask/snaps-utils';
import { StructError, create, object } from '@metamask/superstruct';
import {
assert,
hasProperty,
Expand Down Expand Up @@ -56,31 +54,13 @@ export const scheduleBackgroundEventHandler: PermittedHandlerExport<
hookNames,
};

const offsetRegex = /Z|([+-]\d{2}:?\d{2})$/u;

const ScheduleBackgroundEventParametersWithDateStruct = object({
date: refine(string(), 'date', (val) => {
const date = DateTime.fromISO(val);
if (date.isValid) {
// Luxon doesn't have a reliable way to check if timezone info was not provided
if (!offsetRegex.test(val)) {
return 'ISO 8601 date must have timezone information';
}
return true;
}
return 'Not a valid ISO 8601 date';
}),
date: Iso8601DateStruct,
request: CronjobRpcRequestStruct,
});

const ScheduleBackgroundEventParametersWithDurationStruct = object({
duration: refine(string(), 'duration', (val) => {
const duration = Duration.fromISO(val);
if (!duration.isValid) {
return 'Not a valid ISO 8601 duration';
}
return true;
}),
duration: Iso8601DurationStruct,
request: CronjobRpcRequestStruct,
});

Expand Down
8 changes: 4 additions & 4 deletions packages/snaps-utils/coverage.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"branches": 99.74,
"functions": 98.93,
"lines": 99.61,
"statements": 96.95
"branches": 99.75,
"functions": 98.94,
"lines": 99.62,
"statements": 96.99
}
2 changes: 2 additions & 0 deletions packages/snaps-utils/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
"fast-deep-equal": "^3.1.3",
"fast-json-stable-stringify": "^2.1.0",
"fast-xml-parser": "^4.4.1",
"luxon": "^3.5.0",
"marked": "^12.0.1",
"rfdc": "^1.3.0",
"semver": "^7.5.4",
Expand All @@ -111,6 +112,7 @@
"@swc/jest": "^0.2.26",
"@ts-bridge/cli": "^0.6.1",
"@types/jest": "^27.5.1",
"@types/luxon": "^3",
"@types/mocha": "^10.0.1",
"@types/node": "18.14.2",
"@types/semver": "^7.5.0",
Expand Down
1 change: 1 addition & 0 deletions packages/snaps-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export * from './platform-version';
export * from './snaps';
export * from './strings';
export * from './structs';
export * from './time';
export * from './types';
export * from './ui';
export * from './url';
Expand Down
54 changes: 54 additions & 0 deletions packages/snaps-utils/src/time.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { create, is } from '@metamask/superstruct';
import { DateTime } from 'luxon';

import { Iso8601DateStruct, Iso8601DurationStruct } from './time';

describe('Iso8601dateStruct', () => {
it('should return true for a valid ISO 8601 date', () => {
const value = DateTime.now().toISO();
expect(is(value, Iso8601DateStruct)).toBe(true);
});

it('should return false for an invalid ISO 8601 date', () => {
const value = 'Mon Mar 31 2025';
expect(is(value, Iso8601DateStruct)).toBe(false);
});

it('should return false for an ISO 8601 date without timezone information', () => {
const value = '2025-03-31T12:00:00';
expect(is(value, Iso8601DateStruct)).toBe(false);
});

it('should return an error message for invalid ISO 8601 date', () => {
const value = 'Mon Mar 31 2025';
expect(() => create(value, Iso8601DateStruct)).toThrow(
'Not a valid ISO 8601 date',
);
});

it('should return an error message for ISO 8601 date without timezone information', () => {
const value = '2025-03-31T12:00:00';
expect(() => create(value, Iso8601DateStruct)).toThrow(
'ISO 8601 date must have timezone information',
);
});
});

describe('Iso8601DurationStruct', () => {
it('should return true for a valid ISO 8601 duration', () => {
const value = 'P3Y6M4DT12H30M5S';
expect(is(value, Iso8601DurationStruct)).toBe(true);
});

it('should return false for an invalid ISO 8601 duration', () => {
const value = 'Millisecond';
expect(is(value, Iso8601DurationStruct)).toBe(false);
});

it('should return an error message for invalid ISO 8601 duration', () => {
const value = '1Millisecond';
expect(() => create(value, Iso8601DurationStruct)).toThrow(
'Not a valid ISO 8601 duration',
);
});
});
40 changes: 40 additions & 0 deletions packages/snaps-utils/src/time.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { refine, string } from '@metamask/superstruct';
import { DateTime, Duration } from 'luxon';

/**
* Refines a string as an ISO 8601 duration.
*/
export const Iso8601DurationStruct = refine(
string(),
'ISO 8601 duration',
(value) => {
const parsedDuration = Duration.fromISO(value);
if (!parsedDuration.isValid) {
return 'Not a valid ISO 8601 duration';
}
return true;
},
);

/**
* Regex to match the offset part of an ISO 8601 date.
*/
const offsetRegex = /Z|([+-]\d{2}:?\d{2})$/u;

/**
* Refines a string as an ISO 8601 date.
*/
export const Iso8601DateStruct = refine(string(), 'ISO 8601 date', (value) => {
const parsedDate = DateTime.fromISO(value);

if (!parsedDate.isValid) {
return 'Not a valid ISO 8601 date';
}

if (!offsetRegex.test(value)) {
// Luxon doesn't have a reliable way to check if timezone info was not provided
return 'ISO 8601 date must have timezone information';
}

return true;
});
2 changes: 2 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -6049,6 +6049,7 @@ __metadata:
"@swc/jest": "npm:^0.2.26"
"@ts-bridge/cli": "npm:^0.6.1"
"@types/jest": "npm:^27.5.1"
"@types/luxon": "npm:^3"
"@types/mocha": "npm:^10.0.1"
"@types/node": "npm:18.14.2"
"@types/semver": "npm:^7.5.0"
Expand All @@ -6075,6 +6076,7 @@ __metadata:
istanbul-reports: "npm:^3.1.5"
jest: "npm:^29.0.2"
jest-silent-reporter: "npm:^0.6.0"
luxon: "npm:^3.5.0"
marked: "npm:^12.0.1"
memfs: "npm:^3.4.13"
prettier: "npm:^3.3.3"
Expand Down
Loading