@devp0nt/error0 1.0.0-next.55 → 1.0.0-next.57

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.
@@ -1,35 +0,0 @@
1
- import { Error0 } from '../index.js'
2
-
3
- export const statusPlugin = <TStatuses extends Record<string, number> = Record<never, number>>({
4
- isPublic = false,
5
- statuses,
6
- strict = false,
7
- }: { isPublic?: boolean; statuses?: TStatuses; strict?: boolean } = {}) => {
8
- const statusValues = statuses ? Object.values(statuses) : undefined
9
- const isStatusValue = (value: unknown): value is number =>
10
- typeof value === 'number' && (!statusValues || !strict || statusValues.includes(value))
11
- const normalizeStatusValue = (value: unknown): number | undefined => {
12
- return isStatusValue(value) ? value : undefined
13
- }
14
- const convertStatusValue = (value: number | string): number | undefined => {
15
- if (typeof value === 'number') {
16
- return normalizeStatusValue(value)
17
- }
18
- if (statuses && value in statuses) {
19
- return statuses[value as keyof TStatuses]
20
- }
21
- return undefined
22
- }
23
-
24
- return Error0.plugin().prop('status', {
25
- init: (status: number | Extract<keyof TStatuses, string>) => convertStatusValue(status),
26
- resolve: ({ flow }) => flow.find(Boolean),
27
- serialize: ({ resolved, isPublic: _isPublic }) => {
28
- if (!isPublic && _isPublic) {
29
- return undefined
30
- }
31
- return resolved
32
- },
33
- deserialize: ({ value }) => (typeof value === 'number' ? value : undefined),
34
- })
35
- }
@@ -1,72 +0,0 @@
1
- import { describe, expect, expectTypeOf, it } from 'bun:test'
2
- import { Error0 } from '../index.js'
3
- import { tagsPlugin } from './tags.js'
4
-
5
- describe('tagsPlugin', () => {
6
- const tags = ['api', 'db', 'retry'] as const
7
-
8
- it('merges and deduplicates tags across causes', () => {
9
- const AppError = Error0.use(tagsPlugin())
10
- const root = new AppError('root', { tags: ['db', 'retry'] })
11
- const leaf = new AppError('leaf', { tags: ['api', 'retry'], cause: root })
12
- expect(leaf.tags).toEqual(['api', 'retry', 'db'])
13
- expectTypeOf(leaf.tags).toEqualTypeOf<string[] | undefined>()
14
- expectTypeOf(leaf.own?.tags).toEqualTypeOf<string[] | undefined>()
15
- expectTypeOf(leaf.flow('tags')).toEqualTypeOf<Array<string[] | undefined>>()
16
- })
17
-
18
- it('serializes tags only for private output', () => {
19
- const AppError = Error0.use(tagsPlugin())
20
- const error = new AppError('test', { tags: ['internal'] })
21
- expect(AppError.serialize(error, false).tags).toEqual(['internal'])
22
- expect('tags' in AppError.serialize(error, true)).toBe(false)
23
- })
24
-
25
- it('supports hasTag for single, some, and every checks', () => {
26
- const AppError = Error0.use(tagsPlugin())
27
- const root = new AppError('root', { tags: ['db', 'retry'] })
28
- const leaf = new AppError('leaf', { tags: ['api'], cause: root })
29
-
30
- expect(AppError.hasTag(leaf, 'db')).toBe(true)
31
- expect(leaf.hasTag('db')).toBe(true)
32
- expect(leaf.hasTag('unknown')).toBe(false)
33
- expect(leaf.hasTag(['api', 'db'], 'some')).toBe(true)
34
- expect(leaf.hasTag(['api', 'db'], 'every')).toBe(true)
35
- expect(leaf.hasTag(['api', 'unknown'], 'every')).toBe(false)
36
- })
37
-
38
- it('filters tags not in the allow-list when strict is true', () => {
39
- const AppError = Error0.use(tagsPlugin({ tags: [...tags], strict: true }))
40
- const recreated = AppError.from({
41
- name: 'Error0',
42
- message: 'test',
43
- tags: ['db', 'invalid', 123],
44
- })
45
- expect(recreated.tags).toEqual(['db'])
46
- })
47
-
48
- it('keeps all string tags when strict is false', () => {
49
- const AppError = Error0.use(tagsPlugin({ tags: [...tags], strict: false }))
50
- const recreated = AppError.from({
51
- name: 'Error0',
52
- message: 'test',
53
- tags: ['db', 'custom', 123],
54
- })
55
-
56
- expect(recreated.tags).toEqual(['db', 'custom'])
57
- })
58
-
59
- it('enforces typed hasTag inputs when allow-list is provided', () => {
60
- const AppError = Error0.use(tagsPlugin({ tags }))
61
- const error = new AppError('test', { tags: ['api', 'db'] })
62
-
63
- expectTypeOf(error.hasTag('api')).toEqualTypeOf<boolean>()
64
- expectTypeOf(error.hasTag(['api', 'db'], 'every')).toEqualTypeOf<boolean>()
65
- expectTypeOf(error.hasTag(['api', 'retry'], 'some')).toEqualTypeOf<boolean>()
66
-
67
- // @ts-expect-error - unknown tag is not part of allow-list
68
- error.hasTag('custom')
69
- // @ts-expect-error - unsupported policy
70
- error.hasTag(['api', 'db'], 'all')
71
- })
72
- })
@@ -1,63 +0,0 @@
1
- import { Error0 } from '../index.js'
2
-
3
- export const tagsPlugin = <TTag extends string>({
4
- isPublic = false,
5
- tags,
6
- strict = true,
7
- }: { isPublic?: boolean; tags?: TTag[] | readonly TTag[]; strict?: boolean } = {}) => {
8
- // function hasTag(error: Error0, tag: TTag): boolean
9
- // function hasTag(error: Error0, tag: TTag[], policy: 'every' | 'some'): boolean
10
- // function hasTag(error: Error0, tag: TTag | TTag[], policy?: 'every' | 'some'): boolean {
11
- // const tags = (error as any).tags as string[] | undefined
12
- // if (!tags) {
13
- // return false
14
- // }
15
- // if (Array.isArray(tag)) {
16
- // if (policy === 'every') {
17
- // return tag.every((item) => tags.includes(item))
18
- // }
19
- // return tag.some((item) => tags.includes(item))
20
- // }
21
- // return tags.includes(tag)
22
- // }
23
- const isTag = (value: unknown): value is TTag =>
24
- typeof value === 'string' && (!tags || !strict || tags.includes(value as TTag))
25
- return Error0.plugin()
26
- .prop('tags', {
27
- init: (input: string[]) => input,
28
- resolve: ({ flow }) => {
29
- const merged: string[] = []
30
- for (const value of flow) {
31
- if (Array.isArray(value)) {
32
- merged.push(...value)
33
- }
34
- }
35
- return merged.length > 0 ? Array.from(new Set(merged)) : undefined
36
- },
37
- serialize: ({ resolved, isPublic: _isPublic }) => {
38
- if (!isPublic && _isPublic) {
39
- return undefined
40
- }
41
- return resolved
42
- },
43
- deserialize: ({ value }) => {
44
- if (!Array.isArray(value)) {
45
- return undefined
46
- }
47
- return value.filter((item) => isTag(item))
48
- },
49
- })
50
- .method('hasTag', (error, tag: TTag | TTag[], policy: 'every' | 'some' = 'every'): boolean => {
51
- const tags = error.tags
52
- if (!tags) {
53
- return false
54
- }
55
- if (Array.isArray(tag)) {
56
- if (policy === 'every') {
57
- return tag.every((item) => tags.includes(item))
58
- }
59
- return tag.some((item) => tags.includes(item))
60
- }
61
- return tags.includes(tag)
62
- })
63
- }