@effect-app/vue 4.0.0-beta.32 → 4.0.0-beta.320
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 +2397 -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 +634 -0
- package/dist/commander.d.ts.map +1 -0
- package/dist/commander.js +1070 -0
- package/dist/{experimental/confirm.d.ts → confirm.d.ts} +6 -4
- package/dist/confirm.d.ts.map +1 -0
- package/dist/confirm.js +26 -0
- 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 +14 -5
- package/dist/form.d.ts.map +1 -1
- package/dist/form.js +51 -16
- 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 +15 -0
- package/dist/intl.d.ts.map +1 -0
- package/dist/intl.js +9 -0
- package/dist/lib.d.ts +7 -9
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +35 -10
- 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 +236 -336
- package/dist/makeClient.d.ts.map +1 -1
- package/dist/makeClient.js +417 -408
- package/dist/makeContext.d.ts.map +1 -1
- package/dist/makeIntl.d.ts.map +1 -1
- package/dist/{experimental/makeUseCommand.d.ts → makeUseCommand.d.ts} +4 -3
- package/dist/makeUseCommand.d.ts.map +1 -0
- package/dist/makeUseCommand.js +13 -0
- package/dist/mutate.d.ts +109 -40
- package/dist/mutate.d.ts.map +1 -1
- package/dist/mutate.js +235 -58
- package/dist/query.d.ts +158 -45
- package/dist/query.d.ts.map +1 -1
- package/dist/query.js +380 -122
- 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 -14
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +2 -26
- 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 +2 -0
- package/dist/toast.d.ts.map +1 -0
- package/dist/toast.js +2 -0
- package/dist/withToast.d.ts +2 -0
- package/dist/withToast.d.ts.map +1 -0
- package/dist/withToast.js +2 -0
- 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 +36 -95
- package/src/atomQuery.ts +600 -0
- package/src/commander.ts +3413 -0
- package/src/{experimental/confirm.ts → confirm.ts} +13 -15
- package/src/dependencyMetadata.ts +84 -0
- package/src/errorReporter.ts +65 -75
- package/src/form.ts +66 -21
- package/src/index.ts +7 -7
- package/src/internal/tanstackQuery.ts +308 -0
- package/src/intl.ts +12 -0
- package/src/lib.ts +49 -21
- package/src/liveQueryInvalidation.ts +138 -0
- package/src/makeClient.ts +971 -1159
- package/src/makeIntl.ts +2 -2
- package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +9 -6
- package/src/mutate.ts +406 -153
- package/src/query.ts +763 -260
- package/src/queryLifetime.ts +74 -0
- package/src/routeParams.ts +7 -7
- package/src/runtime.ts +1 -31
- package/src/suspense.ts +43 -0
- package/src/toast.ts +1 -0
- package/src/withToast.ts +1 -0
- package/test/Mutation.test.ts +181 -24
- 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 +3753 -122
- package/test/dist/stubs.d.ts.map +1 -1
- package/test/dist/stubs.js +192 -32
- 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 +25 -20
- package/test/form.test.ts +22 -3
- 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 +437 -38
- 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 +228 -43
- 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 -2
- package/tsconfig.src.json +34 -34
- package/tsconfig.test.json +2 -2
- package/vitest.config.ts +5 -5
- package/dist/experimental/commander.d.ts +0 -359
- package/dist/experimental/commander.d.ts.map +0 -1
- package/dist/experimental/commander.js +0 -557
- package/dist/experimental/confirm.d.ts.map +0 -1
- package/dist/experimental/confirm.js +0 -28
- package/dist/experimental/intl.d.ts +0 -16
- package/dist/experimental/intl.d.ts.map +0 -1
- package/dist/experimental/intl.js +0 -5
- package/dist/experimental/makeUseCommand.d.ts.map +0 -1
- package/dist/experimental/makeUseCommand.js +0 -13
- package/dist/experimental/toast.d.ts +0 -47
- package/dist/experimental/toast.d.ts.map +0 -1
- package/dist/experimental/toast.js +0 -41
- package/dist/experimental/withToast.d.ts +0 -25
- package/dist/experimental/withToast.d.ts.map +0 -1
- package/dist/experimental/withToast.js +0 -45
- package/eslint.config.mjs +0 -24
- package/src/experimental/commander.ts +0 -1835
- package/src/experimental/intl.ts +0 -9
- package/src/experimental/toast.ts +0 -66
- package/src/experimental/withToast.ts +0 -99
- package/tsconfig.json.bak +0 -12
|
@@ -1,8 +1,10 @@
|
|
|
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
|
-
export class Confirm extends
|
|
7
|
+
export class Confirm extends Context.Service<Confirm>()("Confirm", {
|
|
6
8
|
make: Effect.gen(function*() {
|
|
7
9
|
const { intl } = yield* I18n
|
|
8
10
|
|
|
@@ -21,16 +23,12 @@ export class Confirm extends ServiceMap.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
|
|
@@ -22,7 +26,8 @@ function getObjectsAST(ast: S.AST.AST): S.AST.Objects | null {
|
|
|
22
26
|
return null
|
|
23
27
|
}
|
|
24
28
|
|
|
25
|
-
|
|
29
|
+
/** @deprecated Use OmegaForm instead */
|
|
30
|
+
export function convertIn(v: number | string | null, type?: "text" | "float" | "int") {
|
|
26
31
|
return v === null ? "" : type === "text" ? v : `${v}`
|
|
27
32
|
}
|
|
28
33
|
|
|
@@ -30,8 +35,10 @@ export function convertIn(v: string | null, type?: "text" | "float" | "int") {
|
|
|
30
35
|
* Makes sure our international number format is converted to js int/float format.
|
|
31
36
|
* Right now assumes . for thousands and , for decimal.
|
|
32
37
|
*/
|
|
38
|
+
/** @deprecated Use OmegaForm instead */
|
|
33
39
|
export const prepareNumberForLocale = (v: string) => v.replace(/\./g, "").replace(/,/g, ".")
|
|
34
40
|
|
|
41
|
+
/** @deprecated Use OmegaForm instead */
|
|
35
42
|
export function convertOutInt(v: string, type?: "text" | "float" | "int") {
|
|
36
43
|
v = v == null ? v : v.trim()
|
|
37
44
|
const c = v === ""
|
|
@@ -49,6 +56,7 @@ export function convertOutInt(v: string, type?: "text" | "float" | "int") {
|
|
|
49
56
|
return c
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
/** @deprecated Use OmegaForm instead */
|
|
52
60
|
export function convertOut(v: string, set: (v: {} | null) => void, type?: "text" | "float" | "int") {
|
|
53
61
|
return set(convertOutInt(v, type))
|
|
54
62
|
}
|
|
@@ -72,7 +80,8 @@ export interface DiscriminatedUnionFieldInfo<T> {
|
|
|
72
80
|
}
|
|
73
81
|
|
|
74
82
|
export type NestedFieldInfoKey<Key> = [Key] extends [Record<PropertyKey, any>]
|
|
75
|
-
? Unbranded<Key> extends
|
|
83
|
+
? Unbranded<Key> extends (string | number | boolean | bigint | symbol) ? FieldInfo<Key>
|
|
84
|
+
: Unbranded<Key> extends Record<PropertyKey, any> ? NestedFieldInfo<Key>
|
|
76
85
|
: FieldInfo<Key>
|
|
77
86
|
: FieldInfo<Key>
|
|
78
87
|
|
|
@@ -143,10 +152,18 @@ function handlePropertySignature(
|
|
|
143
152
|
const typeLiteral = getObjectsAST(ps.type)
|
|
144
153
|
|
|
145
154
|
const tagPropertySignature = typeLiteral?.propertySignatures.find((_) => _.name === "_tag")
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
// unwrap single-element Union to Literal (S.Struct({ _tag: S.Literal("x") }) wraps as Union([Literal("x")]))
|
|
156
|
+
const tagType = tagPropertySignature
|
|
157
|
+
? S.AST.isUnion(tagPropertySignature.type)
|
|
158
|
+
&& tagPropertySignature.type.types.length === 1
|
|
159
|
+
&& S.AST.isLiteral(tagPropertySignature.type.types[0]!)
|
|
160
|
+
? tagPropertySignature.type.types[0]
|
|
161
|
+
: tagPropertySignature.type
|
|
162
|
+
: undefined
|
|
163
|
+
const tagLiteral = tagType
|
|
164
|
+
&& S.AST.isLiteral(tagType)
|
|
165
|
+
&& typeof tagType.literal === "string"
|
|
166
|
+
? tagType.literal
|
|
150
167
|
: void 0
|
|
151
168
|
|
|
152
169
|
const toRet = handlePropertySignature(ps)
|
|
@@ -189,6 +206,7 @@ function handlePropertySignature(
|
|
|
189
206
|
}
|
|
190
207
|
}
|
|
191
208
|
|
|
209
|
+
/** @deprecated Use OmegaForm instead */
|
|
192
210
|
export function buildFieldInfoFromFields<
|
|
193
211
|
From extends Record<PropertyKey, any>,
|
|
194
212
|
To extends Record<PropertyKey, any>
|
|
@@ -198,6 +216,7 @@ export function buildFieldInfoFromFields<
|
|
|
198
216
|
return buildFieldInfoFromFieldsRoot(schema).fields
|
|
199
217
|
}
|
|
200
218
|
|
|
219
|
+
/** @deprecated Use OmegaForm instead */
|
|
201
220
|
export function buildFieldInfoFromFieldsRoot<
|
|
202
221
|
From extends Record<PropertyKey, any>,
|
|
203
222
|
To extends Record<PropertyKey, any>,
|
|
@@ -235,7 +254,9 @@ abstract class PhantomTypeParameter<
|
|
|
235
254
|
|
|
236
255
|
const defaultIntl = createIntl({ locale: "en" })
|
|
237
256
|
|
|
257
|
+
/** @deprecated Use OmegaForm instead */
|
|
238
258
|
export const translate = ref<IntlFormatters["formatMessage"]>(defaultIntl.formatMessage)
|
|
259
|
+
/** @deprecated Use OmegaForm instead */
|
|
239
260
|
export const customSchemaErrors = ref<Map<S.AST.AST | string, (message: string, e: unknown, v: unknown) => string>>(
|
|
240
261
|
new Map()
|
|
241
262
|
)
|
|
@@ -246,12 +267,12 @@ function buildFieldInfo(
|
|
|
246
267
|
const propertyKey = property.name
|
|
247
268
|
const schema = S.make<S.Codec<unknown>>(property.type)
|
|
248
269
|
const metadata = getMetadataFromSchema(property.type)
|
|
249
|
-
const parse = S.decodeUnknownExit(schema
|
|
270
|
+
const parse = S.decodeUnknownExit(schema)
|
|
250
271
|
|
|
251
272
|
const nullableOrUndefined = S.AST.isUnion(property.type)
|
|
252
273
|
&& (property.type.types.includes(S.Null.ast) || property.type.types.some((_) => _._tag === "Undefined"))
|
|
253
274
|
const realSelf = nullableOrUndefined && S.AST.isUnion(property.type)
|
|
254
|
-
? property.type.types.
|
|
275
|
+
? property.type.types.find((_) => _ !== S.Null.ast && _._tag !== "Undefined")!
|
|
255
276
|
: property.type
|
|
256
277
|
const id = S.AST.resolveIdentifier(property.type)
|
|
257
278
|
const id2 = S.AST.resolveIdentifier(realSelf)
|
|
@@ -268,20 +289,22 @@ function buildFieldInfo(
|
|
|
268
289
|
return custom(err, e, v)
|
|
269
290
|
}
|
|
270
291
|
|
|
271
|
-
// parse specific error types for better translation support
|
|
272
|
-
|
|
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)
|
|
273
296
|
if (integerMatch) {
|
|
274
297
|
return translate.value(
|
|
275
298
|
{ defaultMessage: "Expected an integer, actual {actualValue}", id: "validation.integer.expected" },
|
|
276
|
-
{ actualValue: integerMatch[1] }
|
|
299
|
+
{ actualValue: integerMatch[1] ?? String(v) }
|
|
277
300
|
)
|
|
278
301
|
}
|
|
279
302
|
|
|
280
|
-
const numberMatch = err.match(/Expected.*number
|
|
303
|
+
const numberMatch = err.match(/Expected.*number(?:.*(?:actual|got)\s+([^)\s]+))?/i)
|
|
281
304
|
if (numberMatch) {
|
|
282
305
|
return translate.value(
|
|
283
306
|
{ defaultMessage: "Expected a number, actual {actualValue}", id: "validation.number.expected" },
|
|
284
|
-
{ actualValue: numberMatch[1] }
|
|
307
|
+
{ actualValue: numberMatch[1] ?? String(v) }
|
|
285
308
|
)
|
|
286
309
|
}
|
|
287
310
|
|
|
@@ -368,7 +391,7 @@ function buildFieldInfo(
|
|
|
368
391
|
: metadata.type === "float" || metadata.type === "int"
|
|
369
392
|
? numberRules
|
|
370
393
|
: []) as UnknownRule[],
|
|
371
|
-
parseRule
|
|
394
|
+
parseRule
|
|
372
395
|
]
|
|
373
396
|
|
|
374
397
|
const info = {
|
|
@@ -398,6 +421,7 @@ function buildFieldInfo(
|
|
|
398
421
|
return info as any
|
|
399
422
|
}
|
|
400
423
|
|
|
424
|
+
/** @deprecated Use OmegaForm instead */
|
|
401
425
|
export function getMetadataFromSchema(
|
|
402
426
|
ast: S.AST.AST
|
|
403
427
|
): {
|
|
@@ -411,22 +435,43 @@ export function getMetadataFromSchema(
|
|
|
411
435
|
required: boolean
|
|
412
436
|
description?: string
|
|
413
437
|
} {
|
|
438
|
+
const findJsonSchemaType = (
|
|
439
|
+
schema: any,
|
|
440
|
+
target: "number" | "integer"
|
|
441
|
+
): boolean => {
|
|
442
|
+
if (!schema || typeof schema !== "object") {
|
|
443
|
+
return false
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if (schema.type === target) {
|
|
447
|
+
return true
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
if (Array.isArray(schema.type) && schema.type.includes(target)) {
|
|
451
|
+
return true
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return ["anyOf", "oneOf", "allOf"].some((key) =>
|
|
455
|
+
Array.isArray(schema[key]) && schema[key].some((member: any) => findJsonSchemaType(member, target))
|
|
456
|
+
)
|
|
457
|
+
}
|
|
458
|
+
|
|
414
459
|
const nullable = S.AST.isUnion(ast) && ast.types.includes(S.Null.ast)
|
|
415
460
|
const realSelf = nullable && S.AST.isUnion(ast)
|
|
416
|
-
? ast.types.
|
|
461
|
+
? ast.types.find((_) => _ !== S.Null.ast)!
|
|
417
462
|
: ast
|
|
418
463
|
|
|
419
464
|
let jschema: any
|
|
420
465
|
try {
|
|
421
466
|
const doc = S.toJsonSchemaDocument(S.make<S.Codec<unknown>>(realSelf))
|
|
422
467
|
jschema = doc.schema as any
|
|
423
|
-
const defs = doc.definitions
|
|
468
|
+
const defs = doc.definitions
|
|
424
469
|
// resolve $ref against definitions
|
|
425
470
|
while (jschema["$ref"] && jschema["$ref"].startsWith("#/$defs/")) {
|
|
426
471
|
const { $ref: _, ...rest } = jschema
|
|
427
472
|
jschema = { ...defs[jschema["$ref"].replace("#/$defs/", "")], ...rest }
|
|
428
473
|
}
|
|
429
|
-
} catch
|
|
474
|
+
} catch {
|
|
430
475
|
jschema = {}
|
|
431
476
|
}
|
|
432
477
|
// or we need to add these info directly in the refinement like the minimum
|
|
@@ -440,8 +485,8 @@ export function getMetadataFromSchema(
|
|
|
440
485
|
// "title": "Int"
|
|
441
486
|
// }
|
|
442
487
|
// }
|
|
443
|
-
const
|
|
444
|
-
const
|
|
488
|
+
const isInt = findJsonSchemaType(jschema, "integer")
|
|
489
|
+
const isNumber = isInt || findJsonSchemaType(jschema, "number")
|
|
445
490
|
return {
|
|
446
491
|
type: isInt ? "int" as const : isNumber ? "float" as const : "text" as const,
|
|
447
492
|
minimum: jschema.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"
|