@wener/common 2.0.2 → 2.0.3
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/lib/ai/qwen3vl/index.js +2 -0
- package/lib/ai/qwen3vl/index.js.map +1 -0
- package/lib/ai/qwen3vl/utils.js +31 -0
- package/lib/ai/qwen3vl/utils.js.map +1 -0
- package/lib/ai/vision/DocLayoutElementTypeSchema.js +28 -0
- package/lib/ai/vision/DocLayoutElementTypeSchema.js.map +1 -0
- package/lib/ai/vision/ImageAnnotationSchema.js +50 -0
- package/lib/ai/vision/ImageAnnotationSchema.js.map +1 -0
- package/lib/ai/vision/index.js +3 -0
- package/lib/ai/vision/index.js.map +1 -0
- package/lib/ai/vision/resolveImageAnnotation.js +105 -0
- package/lib/ai/vision/resolveImageAnnotation.js.map +1 -0
- package/lib/cn/ChineseResidentIdNo.js +21 -14
- package/lib/cn/ChineseResidentIdNo.js.map +1 -0
- package/lib/cn/ChineseResidentIdNo.test.js +1 -1
- package/lib/cn/DivisionCode.js +30 -25
- package/lib/cn/DivisionCode.js.map +1 -0
- package/lib/cn/DivisionCode.test.js +1 -1
- package/lib/cn/Mod11.js +38 -81
- package/lib/cn/Mod11.js.map +1 -0
- package/lib/cn/Mod31.js +41 -90
- package/lib/cn/Mod31.js.map +1 -0
- package/lib/cn/UnifiedSocialCreditCode.js +43 -34
- package/lib/cn/UnifiedSocialCreditCode.js.map +1 -0
- package/lib/cn/UnifiedSocialCreditCode.test.js +1 -1
- package/lib/cn/formatChineseAmount.js +77 -0
- package/lib/cn/formatChineseAmount.js.map +1 -0
- package/lib/cn/index.js +7 -1
- package/lib/cn/index.js.map +1 -0
- package/lib/cn/parseChineseNumber.js +94 -0
- package/lib/cn/parseChineseNumber.js.map +1 -0
- package/lib/cn/parseChineseNumber.test.js +278 -0
- package/lib/cn/pinyin/cartesianProduct.js +22 -0
- package/lib/cn/pinyin/cartesianProduct.js.map +1 -0
- package/lib/cn/pinyin/cartesianProduct.test.js +179 -0
- package/lib/cn/pinyin/data.json +23573 -0
- package/lib/cn/pinyin/loader.js +14 -0
- package/lib/cn/pinyin/loader.js.map +1 -0
- package/lib/cn/pinyin/preload.js +3 -0
- package/lib/cn/pinyin/preload.js.map +1 -0
- package/lib/cn/pinyin/toPinyin.test.js +167 -0
- package/lib/cn/pinyin/toPinyinPure.js +33 -0
- package/lib/cn/pinyin/toPinyinPure.js.map +1 -0
- package/lib/cn/pinyin/transform.js +14 -0
- package/lib/cn/pinyin/transform.js.map +1 -0
- package/lib/cn/types.d.js +2 -0
- package/lib/cn/types.d.js.map +1 -0
- package/lib/consola/createStandardConsolaReporter.js +6 -6
- package/lib/consola/createStandardConsolaReporter.js.map +1 -0
- package/lib/consola/formatLogObject.js +65 -145
- package/lib/consola/formatLogObject.js.map +1 -0
- package/lib/consola/formatLogObject.test.js +184 -0
- package/lib/consola/index.js +1 -0
- package/lib/consola/index.js.map +1 -0
- package/lib/data/formatSort.js +6 -5
- package/lib/data/formatSort.js.map +1 -0
- package/lib/data/index.js +1 -0
- package/lib/data/index.js.map +1 -0
- package/lib/data/maybeNumber.js +5 -7
- package/lib/data/maybeNumber.js.map +1 -0
- package/lib/data/parseSort.js +22 -28
- package/lib/data/parseSort.js.map +1 -0
- package/lib/data/resolvePagination.js +13 -17
- package/lib/data/resolvePagination.js.map +1 -0
- package/lib/data/types.d.js +2 -0
- package/lib/data/types.d.js.map +1 -0
- package/lib/dayjs/dayjs.js +21 -19
- package/lib/dayjs/dayjs.js.map +1 -0
- package/lib/dayjs/formatDuration.js +15 -14
- package/lib/dayjs/formatDuration.js.map +1 -0
- package/lib/dayjs/index.js +2 -0
- package/lib/dayjs/index.js.map +1 -0
- package/lib/dayjs/parseDuration.js +5 -8
- package/lib/dayjs/parseDuration.js.map +1 -0
- package/lib/dayjs/parseRelativeTime.js +90 -0
- package/lib/dayjs/parseRelativeTime.js.map +1 -0
- package/lib/dayjs/parseRelativeTime.test.js +247 -0
- package/lib/dayjs/resolveRelativeTime.js +158 -0
- package/lib/dayjs/resolveRelativeTime.js.map +1 -0
- package/lib/dayjs/resolveRelativeTime.test.js +310 -0
- package/lib/decimal/index.js +1 -0
- package/lib/decimal/index.js.map +1 -0
- package/lib/decimal/parseDecimal.js +3 -1
- package/lib/decimal/parseDecimal.js.map +1 -0
- package/lib/emittery/emitter.js +10 -0
- package/lib/emittery/emitter.js.map +1 -0
- package/lib/emittery/index.js +2 -0
- package/lib/emittery/index.js.map +1 -0
- package/lib/foundation/schema/SexType.js +5 -3
- package/lib/foundation/schema/SexType.js.map +1 -0
- package/lib/foundation/schema/index.js +1 -0
- package/lib/foundation/schema/index.js.map +1 -0
- package/lib/foundation/schema/parseSexType.js +1 -0
- package/lib/foundation/schema/parseSexType.js.map +1 -0
- package/lib/foundation/schema/types.js +4 -2
- package/lib/foundation/schema/types.js.map +1 -0
- package/lib/fs/FileSystemError.js +23 -0
- package/lib/fs/FileSystemError.js.map +1 -0
- package/lib/fs/IFileSystem.d.js +3 -0
- package/lib/fs/IFileSystem.d.js.map +1 -0
- package/lib/fs/MemoryFileSystem.test.js +188 -0
- package/lib/fs/createBrowserFileSystem.js +248 -0
- package/lib/fs/createBrowserFileSystem.js.map +1 -0
- package/lib/fs/createMemoryFileSystem.js +516 -0
- package/lib/fs/createMemoryFileSystem.js.map +1 -0
- package/lib/fs/createSandboxFileSystem.js +108 -0
- package/lib/fs/createSandboxFileSystem.js.map +1 -0
- package/lib/fs/createWebDavFileSystem.js +137 -0
- package/lib/fs/createWebDavFileSystem.js.map +1 -0
- package/lib/fs/findMimeType.js +17 -0
- package/lib/fs/findMimeType.js.map +1 -0
- package/lib/fs/index.js +8 -0
- package/lib/fs/index.js.map +1 -0
- package/lib/fs/orpc/FileSystemContract.js +93 -0
- package/lib/fs/orpc/FileSystemContract.js.map +1 -0
- package/lib/fs/orpc/createContractClientFileSystem.js +93 -0
- package/lib/fs/orpc/createContractClientFileSystem.js.map +1 -0
- package/lib/fs/orpc/index.js +3 -0
- package/lib/fs/orpc/index.js.map +1 -0
- package/lib/fs/orpc/server/createFileSystemContractImpl.js +63 -0
- package/lib/fs/orpc/server/createFileSystemContractImpl.js.map +1 -0
- package/lib/fs/orpc/server/index.js +2 -0
- package/lib/fs/orpc/server/index.js.map +1 -0
- package/lib/fs/s3/createS3MiniFileSystem.js +705 -0
- package/lib/fs/s3/createS3MiniFileSystem.js.map +1 -0
- package/lib/fs/s3/index.js +2 -0
- package/lib/fs/s3/index.js.map +1 -0
- package/lib/fs/s3/s3mini.test.js +584 -0
- package/lib/fs/scandir.js +59 -0
- package/lib/fs/scandir.js.map +1 -0
- package/lib/fs/server/createDatabaseFileSystem.js +750 -0
- package/lib/fs/server/createDatabaseFileSystem.js.map +1 -0
- package/lib/fs/server/createNodeFileSystem.js +401 -0
- package/lib/fs/server/createNodeFileSystem.js.map +1 -0
- package/lib/fs/server/dbfs.test.js +221 -0
- package/lib/fs/server/index.js +2 -0
- package/lib/fs/server/index.js.map +1 -0
- package/lib/fs/server/loadTestDatabase.js +127 -0
- package/lib/fs/server/loadTestDatabase.js.map +1 -0
- package/lib/fs/tests/runFileSystemTest.js +318 -0
- package/lib/fs/tests/runFileSystemTest.js.map +1 -0
- package/lib/fs/types.js +27 -0
- package/lib/fs/types.js.map +1 -0
- package/lib/fs/utils/getFileUrl.js +35 -0
- package/lib/fs/utils/getFileUrl.js.map +1 -0
- package/lib/fs/utils.js +22 -0
- package/lib/fs/utils.js.map +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -0
- package/lib/jsonschema/JsonSchema.js +146 -172
- package/lib/jsonschema/JsonSchema.js.map +1 -0
- package/lib/jsonschema/forEachJsonSchema.js +44 -0
- package/lib/jsonschema/forEachJsonSchema.js.map +1 -0
- package/lib/jsonschema/index.js +2 -0
- package/lib/jsonschema/index.js.map +1 -0
- package/lib/jsonschema/types.d.js +2 -0
- package/lib/jsonschema/types.d.js.map +1 -0
- package/lib/meta/defineFileType.js +20 -103
- package/lib/meta/defineFileType.js.map +1 -0
- package/lib/meta/defineInit.js +31 -250
- package/lib/meta/defineInit.js.map +1 -0
- package/lib/meta/defineMetadata.js +24 -140
- package/lib/meta/defineMetadata.js.map +1 -0
- package/lib/meta/index.js +1 -0
- package/lib/meta/index.js.map +1 -0
- package/lib/orpc/createOpenApiContractClient.js +27 -0
- package/lib/orpc/createOpenApiContractClient.js.map +1 -0
- package/lib/orpc/createRpcContractClient.js +34 -0
- package/lib/orpc/createRpcContractClient.js.map +1 -0
- package/lib/orpc/index.js +3 -0
- package/lib/orpc/index.js.map +1 -0
- package/lib/orpc/resolveLinkPlugins.js +28 -0
- package/lib/orpc/resolveLinkPlugins.js.map +1 -0
- package/lib/password/PHC.js +63 -87
- package/lib/password/PHC.js.map +1 -0
- package/lib/password/PHC.test.js +11 -3
- package/lib/password/Password.js +29 -294
- package/lib/password/Password.js.map +1 -0
- package/lib/password/Password.test.js +35 -22
- package/lib/password/createArgon2PasswordAlgorithm.js +35 -191
- package/lib/password/createArgon2PasswordAlgorithm.js.map +1 -0
- package/lib/password/createBase64PasswordAlgorithm.js +8 -141
- package/lib/password/createBase64PasswordAlgorithm.js.map +1 -0
- package/lib/password/createBcryptPasswordAlgorithm.js +13 -168
- package/lib/password/createBcryptPasswordAlgorithm.js.map +1 -0
- package/lib/password/createPBKDF2PasswordAlgorithm.js +46 -228
- package/lib/password/createPBKDF2PasswordAlgorithm.js.map +1 -0
- package/lib/password/createScryptPasswordAlgorithm.js +55 -211
- package/lib/password/createScryptPasswordAlgorithm.js.map +1 -0
- package/lib/password/index.js +1 -0
- package/lib/password/index.js.map +1 -0
- package/lib/password/server/index.js +1 -0
- package/lib/password/server/index.js.map +1 -0
- package/lib/resource/Identifiable.js +2 -0
- package/lib/resource/Identifiable.js.map +1 -0
- package/lib/resource/ListQuery.js +21 -93
- package/lib/resource/ListQuery.js.map +1 -0
- package/lib/resource/getTitleOfResource.js +3 -5
- package/lib/resource/getTitleOfResource.js.map +1 -0
- package/lib/resource/index.js +1 -0
- package/lib/resource/index.js.map +1 -0
- package/lib/resource/schema/AnyResourceSchema.js +2 -1
- package/lib/resource/schema/AnyResourceSchema.js.map +1 -0
- package/lib/resource/schema/BaseResourceSchema.js +2 -1
- package/lib/resource/schema/BaseResourceSchema.js.map +1 -0
- package/lib/resource/schema/ResourceActionType.js +6 -4
- package/lib/resource/schema/ResourceActionType.js.map +1 -0
- package/lib/resource/schema/ResourceStatus.js +5 -3
- package/lib/resource/schema/ResourceStatus.js.map +1 -0
- package/lib/resource/schema/ResourceType.js +5 -3
- package/lib/resource/schema/ResourceType.js.map +1 -0
- package/lib/resource/schema/index.js +1 -0
- package/lib/resource/schema/index.js.map +1 -0
- package/lib/resource/schema/types.js +16 -20
- package/lib/resource/schema/types.js.map +1 -0
- package/lib/s3/formatS3Url.js +65 -0
- package/lib/s3/formatS3Url.js.map +1 -0
- package/lib/s3/formatS3Url.test.js +262 -0
- package/lib/s3/index.js +3 -0
- package/lib/s3/index.js.map +1 -0
- package/lib/s3/parseS3Url.js +65 -0
- package/lib/s3/parseS3Url.js.map +1 -0
- package/lib/s3/parseS3Url.test.js +270 -0
- package/lib/schema/SchemaRegistry.js +38 -38
- package/lib/schema/SchemaRegistry.js.map +1 -0
- package/lib/schema/TypeSchema.d.js +2 -0
- package/lib/schema/TypeSchema.d.js.map +1 -0
- package/lib/schema/createSchemaData.js +26 -125
- package/lib/schema/createSchemaData.js.map +1 -0
- package/lib/schema/findJsonSchemaByPath.js +13 -36
- package/lib/schema/findJsonSchemaByPath.js.map +1 -0
- package/lib/schema/formatZodError.js +140 -0
- package/lib/schema/formatZodError.js.map +1 -0
- package/lib/schema/formatZodError.test.js +196 -0
- package/lib/schema/getSchemaCache.js +5 -5
- package/lib/schema/getSchemaCache.js.map +1 -0
- package/lib/schema/getSchemaOptions.js +8 -11
- package/lib/schema/getSchemaOptions.js.map +1 -0
- package/lib/schema/index.js +2 -1
- package/lib/schema/index.js.map +1 -0
- package/lib/schema/toJsonSchema.js +47 -290
- package/lib/schema/toJsonSchema.js.map +1 -0
- package/lib/schema/validate.js +33 -45
- package/lib/schema/validate.js.map +1 -0
- package/lib/tools/generateSchema.js +39 -197
- package/lib/tools/generateSchema.js.map +1 -0
- package/lib/tools/renderJsonSchemaToMarkdownDoc.js +55 -143
- package/lib/tools/renderJsonSchemaToMarkdownDoc.js.map +1 -0
- package/lib/utils/buildBaseUrl.js +13 -0
- package/lib/utils/buildBaseUrl.js.map +1 -0
- package/lib/utils/buildRedactorFormSchema.js +59 -0
- package/lib/utils/buildRedactorFormSchema.js.map +1 -0
- package/lib/utils/getEstimateProcessTime.js +12 -11
- package/lib/utils/getEstimateProcessTime.js.map +1 -0
- package/lib/utils/index.js +3 -0
- package/lib/utils/index.js.map +1 -0
- package/lib/utils/resolveFeatureOptions.js +12 -0
- package/lib/utils/resolveFeatureOptions.js.map +1 -0
- package/package.json +61 -13
- package/src/ai/qwen3vl/index.ts +1 -0
- package/src/ai/qwen3vl/utils.ts +36 -0
- package/src/ai/vision/DocLayoutElementTypeSchema.ts +30 -0
- package/src/ai/vision/ImageAnnotationSchema.ts +60 -0
- package/src/ai/vision/index.ts +2 -0
- package/src/ai/vision/resolveImageAnnotation.ts +135 -0
- package/src/cn/ChineseResidentIdNo.test.ts +1 -1
- package/src/cn/ChineseResidentIdNo.ts +8 -0
- package/src/cn/DivisionCode.test.ts +1 -1
- package/src/cn/DivisionCode.ts +8 -0
- package/src/cn/UnifiedSocialCreditCode.test.ts +1 -1
- package/src/cn/UnifiedSocialCreditCode.ts +15 -0
- package/src/cn/__snapshots__/UnifiedSocialCreditCode.test.ts.snap +23 -0
- package/src/cn/formatChineseAmount.ts +61 -0
- package/src/cn/index.ts +7 -1
- package/src/cn/parseChineseNumber.test.ts +159 -0
- package/src/cn/parseChineseNumber.ts +97 -0
- package/src/cn/pinyin/cartesianProduct.test.ts +64 -0
- package/src/cn/pinyin/cartesianProduct.ts +24 -0
- package/src/cn/pinyin/data.json +23573 -0
- package/src/cn/pinyin/loader.ts +12 -0
- package/src/cn/pinyin/preload.ts +3 -0
- package/src/cn/pinyin/toPinyin.test.ts +12 -0
- package/src/cn/pinyin/toPinyinPure.ts +43 -0
- package/src/cn/pinyin/transform.ts +12 -0
- package/src/consola/formatLogObject.test.ts +27 -0
- package/src/consola/formatLogObject.ts +34 -6
- package/src/dayjs/dayjs.ts +18 -18
- package/src/dayjs/index.ts +3 -1
- package/src/dayjs/parseRelativeTime.test.ts +185 -0
- package/src/dayjs/parseRelativeTime.ts +115 -0
- package/src/dayjs/resolveRelativeTime.test.ts +357 -0
- package/src/dayjs/resolveRelativeTime.ts +167 -0
- package/src/emittery/emitter.ts +9 -0
- package/src/emittery/index.ts +1 -0
- package/src/fs/FileSystemError.ts +26 -0
- package/src/fs/IFileSystem.d.ts +102 -0
- package/src/fs/MemoryFileSystem.test.ts +37 -0
- package/src/fs/createBrowserFileSystem.ts +291 -0
- package/src/fs/createMemoryFileSystem.ts +604 -0
- package/src/fs/createSandboxFileSystem.ts +136 -0
- package/src/fs/createWebDavFileSystem.ts +172 -0
- package/src/fs/findMimeType.ts +23 -0
- package/src/fs/index.ts +8 -0
- package/src/fs/orpc/FileSystemContract.ts +92 -0
- package/src/fs/orpc/createContractClientFileSystem.ts +115 -0
- package/src/fs/orpc/index.ts +2 -0
- package/src/fs/orpc/server/createFileSystemContractImpl.ts +64 -0
- package/src/fs/orpc/server/index.ts +1 -0
- package/src/fs/s3/createS3MiniFileSystem.ts +830 -0
- package/src/fs/s3/index.ts +1 -0
- package/src/fs/s3/s3mini.test.ts +264 -0
- package/src/fs/scandir.ts +75 -0
- package/src/fs/server/createDatabaseFileSystem.ts +668 -0
- package/src/fs/server/createNodeFileSystem.ts +499 -0
- package/src/fs/server/dbfs.test.ts +47 -0
- package/src/fs/server/index.ts +1 -0
- package/src/fs/server/loadTestDatabase.ts +131 -0
- package/src/fs/tests/runFileSystemTest.ts +288 -0
- package/src/fs/types.ts +29 -0
- package/src/fs/utils/getFileUrl.ts +44 -0
- package/src/fs/utils.ts +23 -0
- package/src/jsonschema/JsonSchema.ts +118 -110
- package/src/jsonschema/forEachJsonSchema.ts +50 -0
- package/src/jsonschema/index.ts +1 -0
- package/src/orpc/createOpenApiContractClient.ts +52 -0
- package/src/orpc/createRpcContractClient.ts +50 -0
- package/src/orpc/index.ts +2 -0
- package/src/orpc/resolveLinkPlugins.ts +29 -0
- package/src/password/PHC.ts +3 -3
- package/src/password/Password.test.ts +1 -1
- package/src/password/createPBKDF2PasswordAlgorithm.ts +2 -2
- package/src/resource/schema/AnyResourceSchema.ts +16 -2
- package/src/s3/formatS3Url.test.ts +254 -0
- package/src/s3/formatS3Url.ts +84 -0
- package/src/s3/index.ts +2 -0
- package/src/s3/parseS3Url.test.ts +258 -0
- package/src/s3/parseS3Url.ts +88 -0
- package/src/schema/SchemaRegistry.ts +35 -33
- package/src/schema/formatZodError.test.ts +196 -0
- package/src/schema/formatZodError.ts +151 -0
- package/src/schema/getSchemaOptions.ts +2 -2
- package/src/schema/index.ts +1 -1
- package/src/utils/buildBaseUrl.ts +12 -0
- package/src/utils/buildRedactorFormSchema.ts +85 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/resolveFeatureOptions.ts +14 -0
- package/src/cn/ChineseResidentIdNo.mod.ts +0 -7
- package/src/cn/DivisionCode.mod.ts +0 -7
- package/src/cn/UnifiedSocialCreditCode.mod.ts +0 -7
- package/src/cn/mod.ts +0 -3
- package/src/schema/SchemaRegistry.mod.ts +0 -1
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
|
|
3
|
+
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { resolveRelativeTime } from './resolveRelativeTime';
|
|
6
|
+
|
|
7
|
+
dayjs.extend(quarterOfYear);
|
|
8
|
+
dayjs.extend(weekOfYear);
|
|
9
|
+
|
|
10
|
+
describe('resolveRelativeTime', () => {
|
|
11
|
+
const fixedDate = new Date('2024-03-15T14:30:45.123Z');
|
|
12
|
+
|
|
13
|
+
describe('basic cases', () => {
|
|
14
|
+
it('should handle "now"', () => {
|
|
15
|
+
const result = resolveRelativeTime('now', fixedDate);
|
|
16
|
+
expect(result).toEqual(fixedDate);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should handle "now" without reference date', () => {
|
|
20
|
+
const result = resolveRelativeTime('now', undefined);
|
|
21
|
+
expect(result).toBeInstanceOf(Date);
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('subtraction operations', () => {
|
|
26
|
+
it('should subtract seconds', () => {
|
|
27
|
+
const result = resolveRelativeTime('now-30s', fixedDate);
|
|
28
|
+
const expected = dayjs(fixedDate).subtract(30, 'second').toDate();
|
|
29
|
+
expect(result).toEqual(expected);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should subtract minutes', () => {
|
|
33
|
+
const result = resolveRelativeTime('now-15m', fixedDate);
|
|
34
|
+
const expected = dayjs(fixedDate).subtract(15, 'minute').toDate();
|
|
35
|
+
expect(result).toEqual(expected);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('should subtract hours', () => {
|
|
39
|
+
const result = resolveRelativeTime('now-2h', fixedDate);
|
|
40
|
+
const expected = dayjs(fixedDate).subtract(2, 'hour').toDate();
|
|
41
|
+
expect(result).toEqual(expected);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('should subtract days', () => {
|
|
45
|
+
const result = resolveRelativeTime('now-7d', fixedDate);
|
|
46
|
+
const expected = dayjs(fixedDate).subtract(7, 'day').toDate();
|
|
47
|
+
expect(result).toEqual(expected);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('should subtract weeks', () => {
|
|
51
|
+
const result = resolveRelativeTime('now-2w', fixedDate);
|
|
52
|
+
const expected = dayjs(fixedDate).subtract(2, 'week').toDate();
|
|
53
|
+
expect(result).toEqual(expected);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('should subtract months', () => {
|
|
57
|
+
const result = resolveRelativeTime('now-1M', fixedDate);
|
|
58
|
+
const expected = dayjs(fixedDate).subtract(1, 'month').toDate();
|
|
59
|
+
expect(result).toEqual(expected);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should subtract quarters', () => {
|
|
63
|
+
const result = resolveRelativeTime('now-1Q', fixedDate);
|
|
64
|
+
const expected = dayjs(fixedDate).subtract(1, 'quarter').toDate();
|
|
65
|
+
expect(result).toEqual(expected);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should subtract years', () => {
|
|
69
|
+
const result = resolveRelativeTime('now-1y', fixedDate);
|
|
70
|
+
const expected = dayjs(fixedDate).subtract(1, 'year').toDate();
|
|
71
|
+
expect(result).toEqual(expected);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('addition operations', () => {
|
|
76
|
+
it('should add seconds', () => {
|
|
77
|
+
const result = resolveRelativeTime('now+30s', fixedDate);
|
|
78
|
+
const expected = dayjs(fixedDate).add(30, 'second').toDate();
|
|
79
|
+
expect(result).toEqual(expected);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should add minutes', () => {
|
|
83
|
+
const result = resolveRelativeTime('now+15m', fixedDate);
|
|
84
|
+
const expected = dayjs(fixedDate).add(15, 'minute').toDate();
|
|
85
|
+
expect(result).toEqual(expected);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should add hours', () => {
|
|
89
|
+
const result = resolveRelativeTime('now+3h', fixedDate);
|
|
90
|
+
const expected = dayjs(fixedDate).add(3, 'hour').toDate();
|
|
91
|
+
expect(result).toEqual(expected);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should add days', () => {
|
|
95
|
+
const result = resolveRelativeTime('now+5d', fixedDate);
|
|
96
|
+
const expected = dayjs(fixedDate).add(5, 'day').toDate();
|
|
97
|
+
expect(result).toEqual(expected);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should add weeks', () => {
|
|
101
|
+
const result = resolveRelativeTime('now+2w', fixedDate);
|
|
102
|
+
const expected = dayjs(fixedDate).add(2, 'week').toDate();
|
|
103
|
+
expect(result).toEqual(expected);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('should add months', () => {
|
|
107
|
+
const result = resolveRelativeTime('now+2M', fixedDate);
|
|
108
|
+
const expected = dayjs(fixedDate).add(2, 'month').toDate();
|
|
109
|
+
expect(result).toEqual(expected);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('should add quarters', () => {
|
|
113
|
+
const result = resolveRelativeTime('now+1Q', fixedDate);
|
|
114
|
+
const expected = dayjs(fixedDate).add(1, 'quarter').toDate();
|
|
115
|
+
expect(result).toEqual(expected);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should add years', () => {
|
|
119
|
+
const result = resolveRelativeTime('now+1y', fixedDate);
|
|
120
|
+
const expected = dayjs(fixedDate).add(1, 'year').toDate();
|
|
121
|
+
expect(result).toEqual(expected);
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('truncation operations', () => {
|
|
126
|
+
it('should truncate to start of second', () => {
|
|
127
|
+
const result = resolveRelativeTime('now/s', fixedDate);
|
|
128
|
+
const expected = dayjs(fixedDate).startOf('second').toDate();
|
|
129
|
+
expect(result).toEqual(expected);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should truncate to start of minute', () => {
|
|
133
|
+
const result = resolveRelativeTime('now/m', fixedDate);
|
|
134
|
+
const expected = dayjs(fixedDate).startOf('minute').toDate();
|
|
135
|
+
expect(result).toEqual(expected);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should truncate to start of hour', () => {
|
|
139
|
+
const result = resolveRelativeTime('now/h', fixedDate);
|
|
140
|
+
const expected = dayjs(fixedDate).startOf('hour').toDate();
|
|
141
|
+
expect(result).toEqual(expected);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('should truncate to start of day', () => {
|
|
145
|
+
const result = resolveRelativeTime('now/d', fixedDate);
|
|
146
|
+
const expected = dayjs(fixedDate).startOf('day').toDate();
|
|
147
|
+
expect(result).toEqual(expected);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('should truncate to start of week', () => {
|
|
151
|
+
const result = resolveRelativeTime('now/w', fixedDate);
|
|
152
|
+
const expected = dayjs(fixedDate).startOf('week').toDate();
|
|
153
|
+
expect(result).toEqual(expected);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it('should truncate to start of month', () => {
|
|
157
|
+
const result = resolveRelativeTime('now/M', fixedDate);
|
|
158
|
+
const expected = dayjs(fixedDate).startOf('month').toDate();
|
|
159
|
+
expect(result).toEqual(expected);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it('should truncate to start of quarter', () => {
|
|
163
|
+
const result = resolveRelativeTime('now/Q', fixedDate);
|
|
164
|
+
const expected = dayjs(fixedDate).startOf('quarter').toDate();
|
|
165
|
+
expect(result).toEqual(expected);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should truncate to start of year', () => {
|
|
169
|
+
const result = resolveRelativeTime('now/y', fixedDate);
|
|
170
|
+
const expected = dayjs(fixedDate).startOf('year').toDate();
|
|
171
|
+
expect(result).toEqual(expected);
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe('endOf truncation operations (backslash)', () => {
|
|
176
|
+
it('should truncate to end of second', () => {
|
|
177
|
+
const result = resolveRelativeTime('now\\s', fixedDate);
|
|
178
|
+
const expected = dayjs(fixedDate).endOf('second').toDate();
|
|
179
|
+
expect(result).toEqual(expected);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should truncate to end of minute', () => {
|
|
183
|
+
const result = resolveRelativeTime('now\\m', fixedDate);
|
|
184
|
+
const expected = dayjs(fixedDate).endOf('minute').toDate();
|
|
185
|
+
expect(result).toEqual(expected);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('should truncate to end of hour', () => {
|
|
189
|
+
const result = resolveRelativeTime('now\\h', fixedDate);
|
|
190
|
+
const expected = dayjs(fixedDate).endOf('hour').toDate();
|
|
191
|
+
expect(result).toEqual(expected);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('should truncate to end of day', () => {
|
|
195
|
+
const result = resolveRelativeTime('now\\d', fixedDate);
|
|
196
|
+
const expected = dayjs(fixedDate).endOf('day').toDate();
|
|
197
|
+
expect(result).toEqual(expected);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('should truncate to end of week', () => {
|
|
201
|
+
const result = resolveRelativeTime('now\\w', fixedDate);
|
|
202
|
+
const expected = dayjs(fixedDate).endOf('week').toDate();
|
|
203
|
+
expect(result).toEqual(expected);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('should truncate to end of month', () => {
|
|
207
|
+
const result = resolveRelativeTime('now\\M', fixedDate);
|
|
208
|
+
const expected = dayjs(fixedDate).endOf('month').toDate();
|
|
209
|
+
expect(result).toEqual(expected);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should truncate to end of quarter', () => {
|
|
213
|
+
const result = resolveRelativeTime('now\\Q', fixedDate);
|
|
214
|
+
const expected = dayjs(fixedDate).endOf('quarter').toDate();
|
|
215
|
+
expect(result).toEqual(expected);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('should truncate to end of year', () => {
|
|
219
|
+
const result = resolveRelativeTime('now\\y', fixedDate);
|
|
220
|
+
const expected = dayjs(fixedDate).endOf('year').toDate();
|
|
221
|
+
expect(result).toEqual(expected);
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('complex combinations', () => {
|
|
226
|
+
it('should handle "now-1M/M" (start of previous month)', () => {
|
|
227
|
+
const result = resolveRelativeTime('now-1M/M', fixedDate);
|
|
228
|
+
const expected = dayjs(fixedDate).subtract(1, 'month').startOf('month').toDate();
|
|
229
|
+
expect(result).toEqual(expected);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('should handle "now/d+1d-1s" (end of current day)', () => {
|
|
233
|
+
const result = resolveRelativeTime('now/d+1d-1s', fixedDate);
|
|
234
|
+
const expected = dayjs(fixedDate).startOf('day').add(1, 'day').subtract(1, 'second').toDate();
|
|
235
|
+
expect(result).toEqual(expected);
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
it('should handle multiple operations "now-1w+2d-3h"', () => {
|
|
239
|
+
const result = resolveRelativeTime('now-1w+2d-3h', fixedDate);
|
|
240
|
+
const expected = dayjs(fixedDate).subtract(1, 'week').add(2, 'day').subtract(3, 'hour').toDate();
|
|
241
|
+
expect(result).toEqual(expected);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should handle "now-1M\\M" (end of previous month)', () => {
|
|
245
|
+
const result = resolveRelativeTime('now-1M\\M', fixedDate);
|
|
246
|
+
const expected = dayjs(fixedDate).subtract(1, 'month').endOf('month').toDate();
|
|
247
|
+
expect(result).toEqual(expected);
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should handle "now\\d" vs "now/d+1d-1s" (both end of current day)', () => {
|
|
251
|
+
const resultBackslash = resolveRelativeTime('now\\d', fixedDate);
|
|
252
|
+
const resultAlternative = resolveRelativeTime('now/d+1d-1s', fixedDate);
|
|
253
|
+
const expectedBackslash = dayjs(fixedDate).endOf('day').toDate();
|
|
254
|
+
const expectedAlternative = dayjs(fixedDate).startOf('day').add(1, 'day').subtract(1, 'second').toDate();
|
|
255
|
+
|
|
256
|
+
expect(resultBackslash).toEqual(expectedBackslash);
|
|
257
|
+
expect(resultAlternative).toEqual(expectedAlternative);
|
|
258
|
+
// Note: endOf gives millisecond precision (.999), while alternative gives second precision (.000)
|
|
259
|
+
// Both represent end of day, but endOf is more precise
|
|
260
|
+
expect(resultBackslash.getTime()).toBeGreaterThan(resultAlternative.getTime());
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('should handle "now-1y\\y" (end of previous year)', () => {
|
|
264
|
+
const result = resolveRelativeTime('now-1y\\y', fixedDate);
|
|
265
|
+
const expected = dayjs(fixedDate).subtract(1, 'year').endOf('year').toDate();
|
|
266
|
+
expect(result).toEqual(expected);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('should handle "now/y+6M" (middle of current year)', () => {
|
|
270
|
+
const result = resolveRelativeTime('now/y+6M', fixedDate);
|
|
271
|
+
const expected = dayjs(fixedDate).startOf('year').add(6, 'month').toDate();
|
|
272
|
+
expect(result).toEqual(expected);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe('error cases', () => {
|
|
277
|
+
it('should throw error for invalid format', () => {
|
|
278
|
+
expect(() => resolveRelativeTime('invalid', fixedDate)).toThrow('Invalid');
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should throw error for non-now start', () => {
|
|
282
|
+
expect(() => resolveRelativeTime('then-1h', fixedDate)).toThrow('Invalid');
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
it('should throw error for invalid time unit', () => {
|
|
286
|
+
expect(() => resolveRelativeTime('now-1x', fixedDate)).toThrow('Invalid time operand');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('should throw error for invalid operator', () => {
|
|
290
|
+
expect(() => resolveRelativeTime('now*1h', fixedDate)).toThrow('Invalid relative time format');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('should throw error for invalid truncation unit', () => {
|
|
294
|
+
expect(() => resolveRelativeTime('now/z', fixedDate)).toThrow();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it('should throw error for incomplete operation', () => {
|
|
298
|
+
expect(() => resolveRelativeTime('now-', fixedDate)).toThrow('Invalid relative time format');
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
describe('Grafana examples from comments', () => {
|
|
303
|
+
it('should handle "now-24h" (last 24 hours)', () => {
|
|
304
|
+
const result = resolveRelativeTime('now-24h', fixedDate);
|
|
305
|
+
const expected = dayjs(fixedDate).subtract(24, 'hour').toDate();
|
|
306
|
+
expect(result).toEqual(expected);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('should handle "now/M" (start of current month)', () => {
|
|
310
|
+
const result = resolveRelativeTime('now/M', fixedDate);
|
|
311
|
+
const expected = dayjs(fixedDate).startOf('month').toDate();
|
|
312
|
+
expect(result).toEqual(expected);
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
it('should handle "now-7d" (last 7 days)', () => {
|
|
316
|
+
const result = resolveRelativeTime('now-7d', fixedDate);
|
|
317
|
+
const expected = dayjs(fixedDate).subtract(7, 'day').toDate();
|
|
318
|
+
expect(result).toEqual(expected);
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('should handle "now/y" (start of current year)', () => {
|
|
322
|
+
const result = resolveRelativeTime('now/y', fixedDate);
|
|
323
|
+
const expected = dayjs(fixedDate).startOf('year').toDate();
|
|
324
|
+
expect(result).toEqual(expected);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should handle "now/w" (start of current week)', () => {
|
|
328
|
+
const result = resolveRelativeTime('now/w', fixedDate);
|
|
329
|
+
const expected = dayjs(fixedDate).startOf('week').toDate();
|
|
330
|
+
expect(result).toEqual(expected);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('should handle "now\\d" (end of current day)', () => {
|
|
334
|
+
const result = resolveRelativeTime('now\\d', fixedDate);
|
|
335
|
+
const expected = dayjs(fixedDate).endOf('day').toDate();
|
|
336
|
+
expect(result).toEqual(expected);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it('should handle "now\\M" (end of current month)', () => {
|
|
340
|
+
const result = resolveRelativeTime('now\\M', fixedDate);
|
|
341
|
+
const expected = dayjs(fixedDate).endOf('month').toDate();
|
|
342
|
+
expect(result).toEqual(expected);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('should handle "now\\w" (end of current week)', () => {
|
|
346
|
+
const result = resolveRelativeTime('now\\w', fixedDate);
|
|
347
|
+
const expected = dayjs(fixedDate).endOf('week').toDate();
|
|
348
|
+
expect(result).toEqual(expected);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should handle "now\\y" (end of current year)', () => {
|
|
352
|
+
const result = resolveRelativeTime('now\\y', fixedDate);
|
|
353
|
+
const expected = dayjs(fixedDate).endOf('year').toDate();
|
|
354
|
+
expect(result).toEqual(expected);
|
|
355
|
+
});
|
|
356
|
+
});
|
|
357
|
+
});
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
import duration from 'dayjs/plugin/duration';
|
|
3
|
+
import quarterOfYear from 'dayjs/plugin/quarterOfYear';
|
|
4
|
+
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
|
5
|
+
|
|
6
|
+
dayjs.extend(duration);
|
|
7
|
+
dayjs.extend(quarterOfYear);
|
|
8
|
+
dayjs.extend(weekOfYear);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resolves a Grafana-style relative time string and returns the corresponding Date.
|
|
12
|
+
*
|
|
13
|
+
* @param s - The relative time string to parse (must start with "now")
|
|
14
|
+
* @param now - Optional reference date. If undefined, uses current time
|
|
15
|
+
* @returns The calculated Date based on the relative time expression
|
|
16
|
+
*
|
|
17
|
+
* @throws {Error} When the format is invalid or contains unsupported operators/units
|
|
18
|
+
*
|
|
19
|
+
* ## Syntax
|
|
20
|
+
*
|
|
21
|
+
* All expressions must start with `now` followed by optional operations:
|
|
22
|
+
*
|
|
23
|
+
* ### Operators
|
|
24
|
+
* - `-` - Subtraction (e.g., `now-1h` for one hour ago)
|
|
25
|
+
* - `+` - Addition (e.g., `now+1d` for one day from now)
|
|
26
|
+
* - `/` - Truncation to beginning of time unit (e.g., `now/d` for start of current day)
|
|
27
|
+
* - `\` - Truncation to end of time unit (e.g., `now\d` for end of current day)
|
|
28
|
+
*
|
|
29
|
+
* ### Time Units
|
|
30
|
+
* - `s` - seconds
|
|
31
|
+
* - `m` - minutes
|
|
32
|
+
* - `h` - hours
|
|
33
|
+
* - `d` - days
|
|
34
|
+
* - `w` - weeks
|
|
35
|
+
* - `M` - months
|
|
36
|
+
* - `Q` - quarters
|
|
37
|
+
* - `y` - years
|
|
38
|
+
*
|
|
39
|
+
* ### Operations
|
|
40
|
+
* Operations are processed sequentially from left to right:
|
|
41
|
+
* - **Addition/Subtraction**: `now±{count}{unit}` (e.g., `now-24h`, `now+1d`)
|
|
42
|
+
* - **Truncation to start**: `now/{unit}` (e.g., `now/d`, `now/M`)
|
|
43
|
+
* - **Truncation to end**: `now\{unit}` (e.g., `now\d`, `now\M`)
|
|
44
|
+
* - **Complex**: Multiple operations can be chained (e.g., `now-1M/M`, `now\d`, `now-1y\y`)
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* // Basic usage
|
|
49
|
+
* resolveRelativeTime('now') // Current time
|
|
50
|
+
* resolveRelativeTime('now-1h') // One hour ago
|
|
51
|
+
* resolveRelativeTime('now+1d') // One day from now
|
|
52
|
+
* resolveRelativeTime('now/d') // Start of current day
|
|
53
|
+
* resolveRelativeTime('now\\d') // End of current day
|
|
54
|
+
*
|
|
55
|
+
* // Common patterns
|
|
56
|
+
* resolveRelativeTime('now-24h') // Last 24 hours
|
|
57
|
+
* resolveRelativeTime('now-7d') // Last 7 days
|
|
58
|
+
* resolveRelativeTime('now/M') // Start of current month
|
|
59
|
+
* resolveRelativeTime('now\\M') // End of current month
|
|
60
|
+
* resolveRelativeTime('now-1M/M') // Start of previous month
|
|
61
|
+
* resolveRelativeTime('now-1M\\M') // End of previous month
|
|
62
|
+
* resolveRelativeTime('now/w') // Start of current week
|
|
63
|
+
* resolveRelativeTime('now\\w') // End of current week
|
|
64
|
+
* resolveRelativeTime('now/y') // Start of current year
|
|
65
|
+
* resolveRelativeTime('now\\y') // End of current year
|
|
66
|
+
*
|
|
67
|
+
* // Complex expressions
|
|
68
|
+
* resolveRelativeTime('now/d+1d-1s') // End of current day (alternative method)
|
|
69
|
+
* resolveRelativeTime('now\\d') // End of current day (direct method)
|
|
70
|
+
* resolveRelativeTime('now-1w+2d-3h') // 1 week ago, plus 2 days, minus 3 hours
|
|
71
|
+
*
|
|
72
|
+
* // With reference date
|
|
73
|
+
* const refDate = new Date('2024-03-15T14:30:45.123Z');
|
|
74
|
+
* resolveRelativeTime('now-1h', refDate); // One hour before reference date
|
|
75
|
+
* resolveRelativeTime('now\\d', refDate); // End of reference day
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function resolveRelativeTime(s: string | Date | number, now?: Date | undefined): Date {
|
|
79
|
+
{
|
|
80
|
+
let out: Date | undefined;
|
|
81
|
+
if (!s) {
|
|
82
|
+
} else if (s instanceof Date) {
|
|
83
|
+
out = s;
|
|
84
|
+
} else if (typeof s === 'number') {
|
|
85
|
+
out = new Date(s);
|
|
86
|
+
} else if (!s.startsWith('now')) {
|
|
87
|
+
out = new Date(s);
|
|
88
|
+
}
|
|
89
|
+
if (out) {
|
|
90
|
+
if (isNaN(out.getTime())) {
|
|
91
|
+
throw new Error(`Invalid date: ${s}`);
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof s !== 'string' || !s.startsWith('now')) {
|
|
98
|
+
throw new Error(`Invalid relative time format: ${s}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let current = dayjs(now);
|
|
102
|
+
const remaining = s.slice(3); // Remove 'now'
|
|
103
|
+
|
|
104
|
+
if (!remaining) {
|
|
105
|
+
return current.toDate();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Split by operators while keeping them in the result
|
|
109
|
+
const parts = remaining.split(/([+\-/\\])/).filter((part) => part !== '');
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < parts.length; i += 2) {
|
|
112
|
+
const operator = parts[i];
|
|
113
|
+
const operand = parts[i + 1];
|
|
114
|
+
|
|
115
|
+
if (!operator || !operand) {
|
|
116
|
+
throw new Error(`Invalid relative time format: ${s}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (operator === '/') {
|
|
120
|
+
// Truncation to start operation
|
|
121
|
+
checkDurationUnit(operand);
|
|
122
|
+
current = current.startOf(operand);
|
|
123
|
+
} else if (operator === '\\') {
|
|
124
|
+
// Truncation to end operation
|
|
125
|
+
checkDurationUnit(operand);
|
|
126
|
+
current = current.endOf(operand);
|
|
127
|
+
} else {
|
|
128
|
+
// Addition or subtraction operation
|
|
129
|
+
const match = operand.match(/^(\d+)([smhdwMQy])$/);
|
|
130
|
+
if (!match) {
|
|
131
|
+
throw new Error(`Invalid time operand: ${operand}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const [, amountStr, unit] = match;
|
|
135
|
+
checkDurationUnit(unit);
|
|
136
|
+
const amount = parseInt(amountStr, 10);
|
|
137
|
+
|
|
138
|
+
if (operator === '+') {
|
|
139
|
+
current = current.add(amount, unit as 'w');
|
|
140
|
+
} else if (operator === '-') {
|
|
141
|
+
current = current.subtract(amount, unit as 'Q');
|
|
142
|
+
} else {
|
|
143
|
+
throw new Error(`Invalid operator: ${operator}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return current.toDate();
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Array of all valid time units */
|
|
152
|
+
export const RelativeDurationUnits = ['ms', 's', 'm', 'h', 'd', 'w', 'M', 'Q', 'y'] as const;
|
|
153
|
+
|
|
154
|
+
/** Valid time units for relative time expressions */
|
|
155
|
+
export type RelativeDurationUnit = (typeof RelativeDurationUnits)[number];
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Type guard to validate and assert that a string is a valid time unit.
|
|
159
|
+
*
|
|
160
|
+
* @param u - The string to check
|
|
161
|
+
* @throws {Error} When the unit is not supported
|
|
162
|
+
*/
|
|
163
|
+
export function checkDurationUnit(u: string): asserts u is RelativeDurationUnit {
|
|
164
|
+
if (!RelativeDurationUnits.includes(u as RelativeDurationUnit)) {
|
|
165
|
+
throw new Error(`Invalid unit: ${u}`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { createEmitter } from './emitter';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export const FileSystemErrorCode = {
|
|
2
|
+
ENOENT: 'ENOENT',
|
|
3
|
+
ENOTDIR: 'ENOTDIR',
|
|
4
|
+
EEXIST: 'EEXIST',
|
|
5
|
+
EISDIR: 'EISDIR',
|
|
6
|
+
ENOTEMPTY: 'ENOTEMPTY',
|
|
7
|
+
EACCES: 'EACCES',
|
|
8
|
+
EPERM: 'EPERM',
|
|
9
|
+
EINVAL: 'EINVAL',
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export type FileSystemErrorCode = (typeof FileSystemErrorCode)[keyof typeof FileSystemErrorCode];
|
|
13
|
+
|
|
14
|
+
export class FileSystemError extends Error {
|
|
15
|
+
constructor(
|
|
16
|
+
message: string,
|
|
17
|
+
public readonly code: FileSystemErrorCode | string,
|
|
18
|
+
) {
|
|
19
|
+
super(message);
|
|
20
|
+
this.name = 'FileSystemError';
|
|
21
|
+
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
|
22
|
+
if (Error.captureStackTrace) {
|
|
23
|
+
Error.captureStackTrace(this, FileSystemError);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { FileKind, FileUrlOptions } from './types';
|
|
2
|
+
|
|
3
|
+
// Base operation options
|
|
4
|
+
type OperationOptions = {
|
|
5
|
+
signal?: AbortSignal;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// Directory operations
|
|
9
|
+
export type ReaddirOptions = OperationOptions & {
|
|
10
|
+
glob?: string;
|
|
11
|
+
recursive?: boolean;
|
|
12
|
+
depth?: number;
|
|
13
|
+
kind?: FileKind;
|
|
14
|
+
hidden?: boolean;
|
|
15
|
+
};
|
|
16
|
+
export type MkdirOptions = OperationOptions & {
|
|
17
|
+
recursive?: boolean;
|
|
18
|
+
};
|
|
19
|
+
// File operations
|
|
20
|
+
export type ReadFileOptions = OperationOptions & {
|
|
21
|
+
encoding?: 'text' | 'binary';
|
|
22
|
+
onDownloadProgress?: (e: { loaded: number; total: number }) => void;
|
|
23
|
+
};
|
|
24
|
+
export type WriteFileOptions = OperationOptions & {
|
|
25
|
+
overwrite?: boolean;
|
|
26
|
+
onUploadProgress?: (e: { loaded: number; total: number }) => void;
|
|
27
|
+
};
|
|
28
|
+
export type RenameOptions = OperationOptions & {
|
|
29
|
+
overwrite?: boolean;
|
|
30
|
+
};
|
|
31
|
+
export type RmOptions = OperationOptions & {
|
|
32
|
+
recursive?: boolean;
|
|
33
|
+
force?: boolean;
|
|
34
|
+
};
|
|
35
|
+
export type CopyOptions = OperationOptions & {
|
|
36
|
+
overwrite?: boolean;
|
|
37
|
+
shallow?: boolean;
|
|
38
|
+
};
|
|
39
|
+
export type CreateReadStreamOptions = OperationOptions & {
|
|
40
|
+
range?: { start: number; end?: number };
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
};
|
|
43
|
+
export type CreateWriteStreamOptions = OperationOptions & {
|
|
44
|
+
overwrite?: boolean;
|
|
45
|
+
};
|
|
46
|
+
export type StatOptions = OperationOptions & {};
|
|
47
|
+
|
|
48
|
+
type WritableData = string | ArrayBuffer | ArrayBufferView | ReadableStream;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Universal file system interface (browser & server compatible)
|
|
52
|
+
*/
|
|
53
|
+
export type IFileSystem = {
|
|
54
|
+
readdir(dir: string, options?: ReaddirOptions): Promise<IFileStat[]>;
|
|
55
|
+
stat(entry: string, options?: StatOptions): Promise<IFileStat>;
|
|
56
|
+
mkdir(path: string, options?: MkdirOptions): Promise<void>;
|
|
57
|
+
readFile(path: string, options?: ReadFileOptions & { encoding: 'text' }): Promise<string>;
|
|
58
|
+
readFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;
|
|
59
|
+
writeFile(path: string, data: WritableData, options?: WriteFileOptions): Promise<void>;
|
|
60
|
+
rm(path: string, options?: RmOptions): Promise<void>;
|
|
61
|
+
rename(oldPath: string, newPath: string, options?: RenameOptions): Promise<void>;
|
|
62
|
+
exists(path: string): Promise<boolean>;
|
|
63
|
+
copy(src: string, dest: string, options?: CopyOptions): Promise<void>;
|
|
64
|
+
|
|
65
|
+
getUrl?(path: IFileStat | string, options?: FileUrlOptions): string | undefined;
|
|
66
|
+
|
|
67
|
+
createReadableStream?(path: string, options?: CreateReadStreamOptions): ReadableStream;
|
|
68
|
+
createWritableStream?(path: string, options?: CreateWriteStreamOptions): WritableStream;
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Server/Node.js specific file system interface with stream support
|
|
73
|
+
*/
|
|
74
|
+
export type IServerFileSystem = IFileSystem & {
|
|
75
|
+
createReadStream(path: string, options?: CreateReadStreamOptions): import('node:stream').Readable;
|
|
76
|
+
createWriteStream(path: string, options?: CreateWriteStreamOptions): import('node:stream').Writable;
|
|
77
|
+
writeFile(
|
|
78
|
+
path: string,
|
|
79
|
+
data: WritableData | Buffer | import('node:stream').Readable,
|
|
80
|
+
options?: WriteFileOptions,
|
|
81
|
+
): Promise<void>;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export type IFileStat = {
|
|
85
|
+
/**
|
|
86
|
+
* parent path
|
|
87
|
+
*/
|
|
88
|
+
directory: string;
|
|
89
|
+
/**
|
|
90
|
+
* full path
|
|
91
|
+
*/
|
|
92
|
+
path: string;
|
|
93
|
+
/**
|
|
94
|
+
* basename
|
|
95
|
+
*/
|
|
96
|
+
name: string;
|
|
97
|
+
kind: 'directory' | 'file';
|
|
98
|
+
mtime: number;
|
|
99
|
+
meta: Record<string, any>;
|
|
100
|
+
size: number;
|
|
101
|
+
};
|
|
102
|
+
|