@effect-app/vue 4.0.0-beta.99 → 4.0.0
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/CHANGELOG.md +2701 -0
- package/dist/atomQuery.d.ts +120 -0
- package/dist/atomQuery.d.ts.map +1 -0
- package/dist/atomQuery.js +434 -0
- package/dist/commander.d.ts +318 -43
- package/dist/commander.d.ts.map +1 -1
- package/dist/commander.js +593 -81
- package/dist/confirm.d.ts +5 -3
- package/dist/confirm.d.ts.map +1 -1
- package/dist/confirm.js +12 -14
- package/dist/dependencyMetadata.d.ts +18 -0
- package/dist/dependencyMetadata.d.ts.map +1 -0
- package/dist/dependencyMetadata.js +67 -0
- package/dist/errorReporter.d.ts +6 -4
- package/dist/errorReporter.d.ts.map +1 -1
- package/dist/errorReporter.js +14 -19
- package/dist/form.d.ts +5 -5
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +16 -10
- package/dist/internal/tanstackQuery.d.ts +9 -0
- package/dist/internal/tanstackQuery.d.ts.map +1 -0
- package/dist/internal/tanstackQuery.js +204 -0
- package/dist/intl.d.ts +4 -4
- package/dist/intl.d.ts.map +1 -1
- package/dist/intl.js +2 -2
- package/dist/lib.d.ts +8 -10
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +37 -12
- package/dist/liveQueryInvalidation.d.ts +16 -0
- package/dist/liveQueryInvalidation.d.ts.map +1 -0
- package/dist/liveQueryInvalidation.js +115 -0
- package/dist/makeClient.d.ts +232 -109
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +416 -84
- package/dist/makeContext.d.ts.map +1 -1
- package/dist/makeIntl.d.ts.map +1 -1
- package/dist/makeUseCommand.d.ts +3 -2
- package/dist/makeUseCommand.d.ts.map +1 -1
- package/dist/makeUseCommand.js +2 -2
- package/dist/mutate.d.ts +110 -41
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +236 -59
- package/dist/query.d.ts +161 -44
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +382 -113
- package/dist/queryLifetime.d.ts +25 -0
- package/dist/queryLifetime.d.ts.map +1 -0
- package/dist/queryLifetime.js +54 -0
- package/dist/routeParams.d.ts +4 -4
- package/dist/routeParams.d.ts.map +1 -1
- package/dist/routeParams.js +4 -3
- package/dist/runtime.d.ts +1 -17
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -38
- package/dist/suspense.d.ts +19 -0
- package/dist/suspense.d.ts.map +1 -0
- package/dist/suspense.js +38 -0
- package/dist/toast.d.ts +1 -45
- package/dist/toast.d.ts.map +1 -1
- package/dist/toast.js +2 -32
- package/dist/withToast.d.ts +1 -24
- package/dist/withToast.d.ts.map +1 -1
- package/dist/withToast.js +2 -45
- package/docs/atom-query-api-redesign.md +207 -0
- package/docs/mutation-command-atoms.md +40 -0
- package/docs/query-key-invalidation.md +107 -0
- package/examples/streamMutation.ts +72 -0
- package/package.json +35 -94
- package/src/atomQuery.ts +600 -0
- package/src/commander.ts +1064 -116
- package/src/confirm.ts +12 -14
- package/src/dependencyMetadata.ts +84 -0
- package/src/errorReporter.ts +65 -75
- package/src/form.ts +22 -15
- package/src/index.ts +7 -7
- package/src/internal/tanstackQuery.ts +308 -0
- package/src/intl.ts +2 -2
- package/src/lib.ts +51 -23
- package/src/liveQueryInvalidation.ts +138 -0
- package/src/makeClient.ts +972 -263
- package/src/makeIntl.ts +2 -2
- package/src/makeUseCommand.ts +8 -5
- package/src/mutate.ts +407 -154
- package/src/query.ts +758 -242
- package/src/queryLifetime.ts +74 -0
- package/src/routeParams.ts +7 -7
- package/src/runtime.ts +1 -54
- package/src/suspense.ts +43 -0
- package/src/toast.ts +1 -52
- package/src/withToast.ts +1 -99
- package/test/Mutation.test.ts +104 -17
- package/test/dependencyInvalidation.test.ts +444 -0
- package/test/dist/dependencyInvalidation.test.d.ts.map +1 -0
- package/test/dist/form.test.d.ts.map +1 -1
- package/test/dist/inactiveInvalidation.test.d.ts.map +1 -0
- package/test/dist/interrupt-refetch-repro.test.d.ts.map +1 -0
- package/test/dist/lib.test.d.ts.map +1 -0
- package/test/dist/liveQueryInvalidation.test.d.ts.map +1 -0
- package/test/dist/query-span.test.d.ts.map +1 -0
- package/test/dist/queryOptions.test.d.ts.map +1 -0
- package/test/dist/streamFinal.test.d.ts.map +1 -0
- package/test/dist/streamFn.test.d.ts.map +1 -0
- package/test/dist/stubs.d.ts +3681 -256
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +166 -25
- package/test/dist/suspense-regression.test.d.ts.map +1 -0
- package/test/dist/suspense.test.d.ts.map +1 -0
- package/test/form-validation-errors.test.ts +2 -1
- package/test/form.test.ts +2 -1
- package/test/inactiveInvalidation.test.ts +172 -0
- package/test/interrupt-refetch-repro.test.ts +625 -0
- package/test/lib.test.ts +240 -0
- package/test/liveQueryInvalidation.test.ts +108 -0
- package/test/makeClient.test.ts +414 -32
- package/test/query-span.test.ts +92 -0
- package/test/queryOptions.test.ts +88 -0
- package/test/streamFinal.test.ts +64 -0
- package/test/streamFn.test.ts +457 -0
- package/test/stubs.ts +194 -36
- package/test/suspense-regression.test.ts +168 -0
- package/test/suspense.test.ts +160 -0
- package/tsconfig.examples.json +20 -0
- package/tsconfig.json +9 -1
- package/tsconfig.src.json +34 -34
- package/tsconfig.test.json +2 -2
- package/vitest.config.ts +5 -5
- package/eslint.config.mjs +0 -24
- package/tsconfig.json.bak +0 -12
package/src/confirm.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import * as Context from "effect-app/Context"
|
|
2
|
+
import * as Effect from "effect-app/Effect"
|
|
3
|
+
import * as Layer from "effect-app/Layer"
|
|
4
|
+
import { I18n } from "./intl.ts"
|
|
3
5
|
|
|
4
6
|
// @effect-diagnostics-next-line missingEffectServiceDependency:off
|
|
5
7
|
export class Confirm extends Context.Service<Confirm>()("Confirm", {
|
|
@@ -21,16 +23,12 @@ export class Confirm extends Context.Service<Confirm>()("Confirm", {
|
|
|
21
23
|
static readonly DefaultWithoutDependencies = Layer.effect(this, this.make)
|
|
22
24
|
static readonly Default = this.DefaultWithoutDependencies
|
|
23
25
|
|
|
24
|
-
static confirm(message?: string) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const c = yield* Confirm
|
|
33
|
-
return yield* c.confirmOrInterrupt(message)
|
|
34
|
-
})
|
|
35
|
-
}
|
|
26
|
+
static readonly confirm = Effect.fnUntraced(function*(message?: string) {
|
|
27
|
+
const c = yield* Confirm
|
|
28
|
+
return yield* c.confirm(message)
|
|
29
|
+
})
|
|
30
|
+
static readonly confirmOrInterrupt = Effect.fnUntraced(function*(message?: string) {
|
|
31
|
+
const c = yield* Confirm
|
|
32
|
+
return yield* c.confirmOrInterrupt(message)
|
|
33
|
+
})
|
|
36
34
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { DataDependencies } from "effect-app/client"
|
|
2
|
+
import * as Hash from "effect/Hash"
|
|
3
|
+
|
|
4
|
+
// Maps a live query's reactivity key (its full `[...queryKey, input]` key) to the
|
|
5
|
+
// data dependencies it read while fetching. Populated when the query handler resolves and
|
|
6
|
+
// cleared when the query atom is GC'd, so the set mirrors the live query atoms (mounted or
|
|
7
|
+
// cached-within-ttl) — the atom equivalent of the former tanstack query cache.
|
|
8
|
+
type Entry = { readonly key: ReadonlyArray<unknown>; readonly reads: DataDependencies.DataDependencies }
|
|
9
|
+
const readDependencies = new Map<number, Entry>()
|
|
10
|
+
export type QueryInvalidationMode = "await" | "soft"
|
|
11
|
+
type InvalidationModeEntry = { awaitSubscribers: number; softSubscribers: number }
|
|
12
|
+
const invalidationModes = new Map<number, InvalidationModeEntry>()
|
|
13
|
+
|
|
14
|
+
export const registerQueryInvalidationMode = (
|
|
15
|
+
key: ReadonlyArray<unknown>,
|
|
16
|
+
mode: QueryInvalidationMode
|
|
17
|
+
): () => void => {
|
|
18
|
+
const hash = Hash.hash(key)
|
|
19
|
+
const entry = invalidationModes.get(hash) ?? { awaitSubscribers: 0, softSubscribers: 0 }
|
|
20
|
+
if (mode === "soft") entry.softSubscribers++
|
|
21
|
+
else entry.awaitSubscribers++
|
|
22
|
+
invalidationModes.set(hash, entry)
|
|
23
|
+
let active = true
|
|
24
|
+
return () => {
|
|
25
|
+
if (!active) return
|
|
26
|
+
active = false
|
|
27
|
+
if (mode === "soft") entry.softSubscribers--
|
|
28
|
+
else entry.awaitSubscribers--
|
|
29
|
+
if (entry.awaitSubscribers === 0 && entry.softSubscribers === 0) invalidationModes.delete(hash)
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const getQueryInvalidationMode = (key: ReadonlyArray<unknown>): QueryInvalidationMode => {
|
|
34
|
+
const entry = invalidationModes.get(Hash.hash(key))
|
|
35
|
+
return entry !== undefined && entry.awaitSubscribers === 0 && entry.softSubscribers > 0 ? "soft" : "await"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const setQueryReadDependencies = (
|
|
39
|
+
key: ReadonlyArray<unknown>,
|
|
40
|
+
reads: DataDependencies.DataDependencies
|
|
41
|
+
) => {
|
|
42
|
+
const h = Hash.hash(key)
|
|
43
|
+
if (!DataDependencies.isNonEmpty(reads)) readDependencies.delete(h)
|
|
44
|
+
else readDependencies.set(h, { key, reads })
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const clearQueryReadDependencies = (key: ReadonlyArray<unknown>) => {
|
|
48
|
+
readDependencies.delete(Hash.hash(key))
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const getQueryReadDependencies = (
|
|
52
|
+
key: ReadonlyArray<unknown>
|
|
53
|
+
): DataDependencies.DataDependencies => readDependencies.get(Hash.hash(key))?.reads ?? DataDependencies.empty()
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reactivity keys of every live query whose recorded read-dependencies intersect this
|
|
57
|
+
* mutation's `writeDependencies`. Returned keys are passed to `invalidateAndAwait`, refreshing
|
|
58
|
+
* exactly those queries.
|
|
59
|
+
*/
|
|
60
|
+
export const getDerivedInvalidationKeys = (
|
|
61
|
+
writeDependencies: DataDependencies.DataDependencies
|
|
62
|
+
): ReadonlyArray<ReadonlyArray<unknown>> => {
|
|
63
|
+
if (!DataDependencies.isNonEmpty(writeDependencies)) return []
|
|
64
|
+
const keys: Array<ReadonlyArray<unknown>> = []
|
|
65
|
+
for (const { key, reads } of readDependencies.values()) {
|
|
66
|
+
if (DataDependencies.intersects(reads, writeDependencies)) keys.push(key)
|
|
67
|
+
}
|
|
68
|
+
return keys
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const partitionInvalidationKeys = (
|
|
72
|
+
keys: ReadonlyArray<ReadonlyArray<unknown>>
|
|
73
|
+
): {
|
|
74
|
+
readonly awaitKeys: ReadonlyArray<ReadonlyArray<unknown>>
|
|
75
|
+
readonly softKeys: ReadonlyArray<ReadonlyArray<unknown>>
|
|
76
|
+
} => {
|
|
77
|
+
const awaitKeys: Array<ReadonlyArray<unknown>> = []
|
|
78
|
+
const softKeys: Array<ReadonlyArray<unknown>> = []
|
|
79
|
+
for (const key of keys) {
|
|
80
|
+
if (getQueryInvalidationMode(key) === "soft") softKeys.push(key)
|
|
81
|
+
else awaitKeys.push(key)
|
|
82
|
+
}
|
|
83
|
+
return { awaitKeys, softKeys }
|
|
84
|
+
}
|
package/src/errorReporter.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
|
3
3
|
import * as Sentry from "@sentry/browser"
|
|
4
|
-
import { Cause, Effect, type LogLevel } from "effect-app"
|
|
5
4
|
import { CauseException, tryToJson, tryToReport } from "effect-app/client/errors"
|
|
5
|
+
import * as Effect from "effect-app/Effect"
|
|
6
6
|
import { dropUndefined, LogLevelToSentry } from "effect-app/utils"
|
|
7
|
+
import * as Cause from "effect/Cause"
|
|
8
|
+
import type * as LogLevel from "effect/LogLevel"
|
|
7
9
|
|
|
8
10
|
export const tryCauseException = <E>(cause: Cause.Cause<E>, name: string): CauseException<E> => {
|
|
9
11
|
try {
|
|
@@ -13,45 +15,41 @@ export const tryCauseException = <E>(cause: Cause.Cause<E>, name: string): Cause
|
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
export function reportError(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
yield* Effect.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
|
|
28
|
-
return Cause.squash(cause)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const error = tryCauseException(cause, name)
|
|
32
|
-
yield* reportSentry(error, extras, LogLevelToSentry(level))
|
|
33
|
-
yield* Effect
|
|
34
|
-
.logWithLevel(level)("Reporting error", cause)
|
|
35
|
-
.pipe(
|
|
36
|
-
Effect.annotateLogs(dropUndefined({
|
|
37
|
-
extras,
|
|
38
|
-
error: tryToReport(error),
|
|
39
|
-
cause: tryToJson(cause),
|
|
40
|
-
__error_name__: name
|
|
41
|
-
})),
|
|
42
|
-
Effect.catchCause((cause) => Effect.logWarning("Failed to log error", cause)),
|
|
43
|
-
Effect.catchCause(() => Effect.logFatal("Failed to log error cause"))
|
|
44
|
-
)
|
|
18
|
+
export function reportError(name: string) {
|
|
19
|
+
return Effect.fnUntraced(
|
|
20
|
+
function*(
|
|
21
|
+
cause: Cause.Cause<unknown>,
|
|
22
|
+
extras?: Record<string, unknown>,
|
|
23
|
+
level: LogLevel.Severity = "Error"
|
|
24
|
+
) {
|
|
25
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
26
|
+
yield* Effect.logDebug("Interrupted").pipe(Effect.annotateLogs("extras", JSON.stringify(extras ?? {})))
|
|
27
|
+
return Cause.squash(cause)
|
|
28
|
+
}
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
30
|
+
const error = tryCauseException(cause, name)
|
|
31
|
+
yield* reportSentry(error, extras, LogLevelToSentry(level))
|
|
32
|
+
yield* Effect
|
|
33
|
+
.logWithLevel(level)("Reporting error", cause)
|
|
34
|
+
.pipe(
|
|
35
|
+
Effect.annotateLogs(dropUndefined({
|
|
36
|
+
extras,
|
|
37
|
+
error: tryToReport(error),
|
|
38
|
+
cause: tryToJson(cause),
|
|
39
|
+
__error_name__: name
|
|
40
|
+
})),
|
|
41
|
+
Effect.catchCause((cause) => Effect.logWarning("Failed to log error", cause)),
|
|
42
|
+
Effect.catchCause(() => Effect.logFatal("Failed to log error cause"))
|
|
53
43
|
)
|
|
54
|
-
|
|
44
|
+
|
|
45
|
+
return error
|
|
46
|
+
},
|
|
47
|
+
(effect) =>
|
|
48
|
+
Effect.tapCause(effect, (cause) =>
|
|
49
|
+
Effect.logError("Failed to report error", cause).pipe(
|
|
50
|
+
Effect.tapCause(() => Effect.logFatal("Failed to log error cause"))
|
|
51
|
+
))
|
|
52
|
+
)
|
|
55
53
|
}
|
|
56
54
|
|
|
57
55
|
function reportSentry(
|
|
@@ -63,39 +61,33 @@ function reportSentry(
|
|
|
63
61
|
const scope = new Sentry.Scope()
|
|
64
62
|
scope.setLevel(level)
|
|
65
63
|
if (extras) scope.setContext("extras", extras)
|
|
66
|
-
scope.setContext("error", tryToReport(error)
|
|
67
|
-
scope.setContext("cause", tryToJson(error.originalCause)
|
|
64
|
+
scope.setContext("error", tryToReport(error))
|
|
65
|
+
scope.setContext("cause", tryToJson(error.originalCause))
|
|
68
66
|
Sentry.captureException(error, scope)
|
|
69
67
|
})
|
|
70
68
|
}
|
|
71
69
|
|
|
72
|
-
export function logError<E>(
|
|
73
|
-
|
|
74
|
-
) {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
Effect.tapCause((cause) =>
|
|
94
|
-
Effect.logError("Failed to log error", cause).pipe(
|
|
95
|
-
Effect.tapCause(() => Effect.logFatal("Failed to log error cause"))
|
|
96
|
-
)
|
|
97
|
-
)
|
|
98
|
-
)
|
|
70
|
+
export function logError<E>(name: string) {
|
|
71
|
+
return Effect.fnUntraced(
|
|
72
|
+
function*(cause: Cause.Cause<E>, extras?: Record<string, unknown>) {
|
|
73
|
+
if (Cause.hasInterruptsOnly(cause)) {
|
|
74
|
+
yield* Effect.logDebug("Interrupted").pipe(Effect.annotateLogs(dropUndefined({ extras })))
|
|
75
|
+
return
|
|
76
|
+
}
|
|
77
|
+
yield* Effect
|
|
78
|
+
.logWarning("Logging error", cause)
|
|
79
|
+
.pipe(Effect.annotateLogs(dropUndefined({
|
|
80
|
+
extras,
|
|
81
|
+
cause: tryToJson(cause),
|
|
82
|
+
__error_name__: name
|
|
83
|
+
})))
|
|
84
|
+
},
|
|
85
|
+
(effect) =>
|
|
86
|
+
Effect.tapCause(effect, (cause) =>
|
|
87
|
+
Effect.logError("Failed to log error", cause).pipe(
|
|
88
|
+
Effect.tapCause(() => Effect.logFatal("Failed to log error cause"))
|
|
89
|
+
))
|
|
90
|
+
)
|
|
99
91
|
}
|
|
100
92
|
|
|
101
93
|
export function captureException(error: unknown, extras?: Record<string, unknown>) {
|
|
@@ -105,12 +97,10 @@ export function captureException(error: unknown, extras?: Record<string, unknown
|
|
|
105
97
|
console.error(error, extras)
|
|
106
98
|
}
|
|
107
99
|
|
|
108
|
-
export
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
Sentry.captureMessage(message, scope)
|
|
100
|
+
export const reportMessage = Effect.fnUntraced(function*(message: string, extras?: Record<string, unknown>) {
|
|
101
|
+
const scope = new Sentry.Scope()
|
|
102
|
+
if (extras) scope.setContext("extras", extras)
|
|
103
|
+
Sentry.captureMessage(message, scope)
|
|
113
104
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
105
|
+
console.warn(message, extras)
|
|
106
|
+
})
|
package/src/form.ts
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { createIntl, type IntlFormatters } from "@formatjs/intl"
|
|
2
|
-
|
|
3
|
-
import
|
|
2
|
+
|
|
3
|
+
import * as Option from "effect-app/Option"
|
|
4
|
+
import * as S from "effect-app/Schema"
|
|
4
5
|
import type { Unbranded } from "effect-app/Schema/brand"
|
|
5
6
|
import type { IsUnion } from "effect-app/utils"
|
|
7
|
+
import * as Cause from "effect/Cause"
|
|
8
|
+
import * as Exit from "effect/Exit"
|
|
9
|
+
import { pipe } from "effect/Function"
|
|
6
10
|
import { capitalize, ref } from "vue"
|
|
7
11
|
|
|
8
12
|
// type GetSchemaFromProp<T> = T extends Field<infer S, any, any, any> ? S
|
|
@@ -23,7 +27,7 @@ function getObjectsAST(ast: S.AST.AST): S.AST.Objects | null {
|
|
|
23
27
|
}
|
|
24
28
|
|
|
25
29
|
/** @deprecated Use OmegaForm instead */
|
|
26
|
-
export function convertIn(v: string | null, type?: "text" | "float" | "int") {
|
|
30
|
+
export function convertIn(v: number | string | null, type?: "text" | "float" | "int") {
|
|
27
31
|
return v === null ? "" : type === "text" ? v : `${v}`
|
|
28
32
|
}
|
|
29
33
|
|
|
@@ -76,7 +80,8 @@ export interface DiscriminatedUnionFieldInfo<T> {
|
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
export type NestedFieldInfoKey<Key> = [Key] extends [Record<PropertyKey, any>]
|
|
79
|
-
? Unbranded<Key> extends
|
|
83
|
+
? Unbranded<Key> extends (string | number | boolean | bigint | symbol) ? FieldInfo<Key>
|
|
84
|
+
: Unbranded<Key> extends Record<PropertyKey, any> ? NestedFieldInfo<Key>
|
|
80
85
|
: FieldInfo<Key>
|
|
81
86
|
: FieldInfo<Key>
|
|
82
87
|
|
|
@@ -262,12 +267,12 @@ function buildFieldInfo(
|
|
|
262
267
|
const propertyKey = property.name
|
|
263
268
|
const schema = S.make<S.Codec<unknown>>(property.type)
|
|
264
269
|
const metadata = getMetadataFromSchema(property.type)
|
|
265
|
-
const parse = S.decodeUnknownExit(schema
|
|
270
|
+
const parse = S.decodeUnknownExit(schema)
|
|
266
271
|
|
|
267
272
|
const nullableOrUndefined = S.AST.isUnion(property.type)
|
|
268
273
|
&& (property.type.types.includes(S.Null.ast) || property.type.types.some((_) => _._tag === "Undefined"))
|
|
269
274
|
const realSelf = nullableOrUndefined && S.AST.isUnion(property.type)
|
|
270
|
-
? property.type.types.
|
|
275
|
+
? property.type.types.find((_) => _ !== S.Null.ast && _._tag !== "Undefined")!
|
|
271
276
|
: property.type
|
|
272
277
|
const id = S.AST.resolveIdentifier(property.type)
|
|
273
278
|
const id2 = S.AST.resolveIdentifier(realSelf)
|
|
@@ -284,20 +289,22 @@ function buildFieldInfo(
|
|
|
284
289
|
return custom(err, e, v)
|
|
285
290
|
}
|
|
286
291
|
|
|
287
|
-
// parse specific error types for better translation support
|
|
288
|
-
|
|
292
|
+
// parse specific error types for better translation support.
|
|
293
|
+
// beta.107 formatters often omit ", actual/got <value>" from the issue string;
|
|
294
|
+
// fall back to the rejected input `v` when the regex has no capture.
|
|
295
|
+
const integerMatch = err.match(/Expected.*integer(?:.*(?:actual|got)\s+([^)\s]+))?/i)
|
|
289
296
|
if (integerMatch) {
|
|
290
297
|
return translate.value(
|
|
291
298
|
{ defaultMessage: "Expected an integer, actual {actualValue}", id: "validation.integer.expected" },
|
|
292
|
-
{ actualValue: integerMatch[1] }
|
|
299
|
+
{ actualValue: integerMatch[1] ?? String(v) }
|
|
293
300
|
)
|
|
294
301
|
}
|
|
295
302
|
|
|
296
|
-
const numberMatch = err.match(/Expected.*number
|
|
303
|
+
const numberMatch = err.match(/Expected.*number(?:.*(?:actual|got)\s+([^)\s]+))?/i)
|
|
297
304
|
if (numberMatch) {
|
|
298
305
|
return translate.value(
|
|
299
306
|
{ defaultMessage: "Expected a number, actual {actualValue}", id: "validation.number.expected" },
|
|
300
|
-
{ actualValue: numberMatch[1] }
|
|
307
|
+
{ actualValue: numberMatch[1] ?? String(v) }
|
|
301
308
|
)
|
|
302
309
|
}
|
|
303
310
|
|
|
@@ -384,7 +391,7 @@ function buildFieldInfo(
|
|
|
384
391
|
: metadata.type === "float" || metadata.type === "int"
|
|
385
392
|
? numberRules
|
|
386
393
|
: []) as UnknownRule[],
|
|
387
|
-
parseRule
|
|
394
|
+
parseRule
|
|
388
395
|
]
|
|
389
396
|
|
|
390
397
|
const info = {
|
|
@@ -451,20 +458,20 @@ export function getMetadataFromSchema(
|
|
|
451
458
|
|
|
452
459
|
const nullable = S.AST.isUnion(ast) && ast.types.includes(S.Null.ast)
|
|
453
460
|
const realSelf = nullable && S.AST.isUnion(ast)
|
|
454
|
-
? ast.types.
|
|
461
|
+
? ast.types.find((_) => _ !== S.Null.ast)!
|
|
455
462
|
: ast
|
|
456
463
|
|
|
457
464
|
let jschema: any
|
|
458
465
|
try {
|
|
459
466
|
const doc = S.toJsonSchemaDocument(S.make<S.Codec<unknown>>(realSelf))
|
|
460
467
|
jschema = doc.schema as any
|
|
461
|
-
const defs = doc.definitions
|
|
468
|
+
const defs = doc.definitions
|
|
462
469
|
// resolve $ref against definitions
|
|
463
470
|
while (jschema["$ref"] && jschema["$ref"].startsWith("#/$defs/")) {
|
|
464
471
|
const { $ref: _, ...rest } = jschema
|
|
465
472
|
jschema = { ...defs[jschema["$ref"].replace("#/$defs/", "")], ...rest }
|
|
466
473
|
}
|
|
467
|
-
} catch
|
|
474
|
+
} catch {
|
|
468
475
|
jschema = {}
|
|
469
476
|
}
|
|
470
477
|
// or we need to add these info directly in the refinement like the minimum
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
export * from "./lib.
|
|
1
|
+
export * from "./lib.ts"
|
|
2
2
|
|
|
3
|
-
export * from "./makeClient.
|
|
4
|
-
export * from "./makeContext.
|
|
5
|
-
export * from "./makeIntl.
|
|
6
|
-
export * from "./mutate.
|
|
7
|
-
export * from "./query.
|
|
8
|
-
export * from "./runtime.
|
|
3
|
+
export * from "./makeClient.ts"
|
|
4
|
+
export * from "./makeContext.ts"
|
|
5
|
+
export * from "./makeIntl.ts"
|
|
6
|
+
export * from "./mutate.ts"
|
|
7
|
+
export * from "./query.ts"
|
|
8
|
+
export * from "./runtime.ts"
|