@effect-app/vue 4.0.0-beta.19 → 4.0.0-beta.190

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.
Files changed (103) hide show
  1. package/CHANGELOG.md +1383 -0
  2. package/dist/commander.d.ts +620 -0
  3. package/dist/commander.d.ts.map +1 -0
  4. package/dist/commander.js +1056 -0
  5. package/dist/confirm.d.ts +19 -0
  6. package/dist/confirm.d.ts.map +1 -0
  7. package/dist/confirm.js +24 -0
  8. package/dist/errorReporter.d.ts +4 -4
  9. package/dist/errorReporter.d.ts.map +1 -1
  10. package/dist/errorReporter.js +12 -18
  11. package/dist/form.d.ts +13 -4
  12. package/dist/form.d.ts.map +1 -1
  13. package/dist/form.js +41 -12
  14. package/dist/index.d.ts +1 -1
  15. package/dist/intl.d.ts +15 -0
  16. package/dist/intl.d.ts.map +1 -0
  17. package/dist/intl.js +9 -0
  18. package/dist/lib.d.ts +6 -8
  19. package/dist/lib.d.ts.map +1 -1
  20. package/dist/lib.js +34 -7
  21. package/dist/makeClient.d.ts +191 -292
  22. package/dist/makeClient.d.ts.map +1 -1
  23. package/dist/makeClient.js +217 -369
  24. package/dist/makeContext.d.ts +1 -1
  25. package/dist/makeContext.d.ts.map +1 -1
  26. package/dist/makeIntl.d.ts +1 -1
  27. package/dist/makeIntl.d.ts.map +1 -1
  28. package/dist/makeUseCommand.d.ts +8 -0
  29. package/dist/makeUseCommand.d.ts.map +1 -0
  30. package/dist/makeUseCommand.js +13 -0
  31. package/dist/mutate.d.ts +56 -25
  32. package/dist/mutate.d.ts.map +1 -1
  33. package/dist/mutate.js +132 -33
  34. package/dist/query.d.ts +24 -16
  35. package/dist/query.d.ts.map +1 -1
  36. package/dist/query.js +119 -37
  37. package/dist/routeParams.d.ts +1 -1
  38. package/dist/runtime.d.ts +5 -2
  39. package/dist/runtime.d.ts.map +1 -1
  40. package/dist/runtime.js +27 -17
  41. package/dist/toast.d.ts +46 -0
  42. package/dist/toast.d.ts.map +1 -0
  43. package/dist/toast.js +32 -0
  44. package/dist/withToast.d.ts +26 -0
  45. package/dist/withToast.d.ts.map +1 -0
  46. package/dist/withToast.js +54 -0
  47. package/eslint.config.mjs +2 -2
  48. package/examples/streamMutation.ts +70 -0
  49. package/package.json +48 -48
  50. package/src/commander.ts +3378 -0
  51. package/src/{experimental/confirm.ts → confirm.ts} +10 -14
  52. package/src/errorReporter.ts +62 -74
  53. package/src/form.ts +55 -16
  54. package/src/intl.ts +12 -0
  55. package/src/lib.ts +46 -13
  56. package/src/makeClient.ts +623 -1043
  57. package/src/{experimental/makeUseCommand.ts → makeUseCommand.ts} +6 -4
  58. package/src/mutate.ts +273 -72
  59. package/src/query.ts +181 -68
  60. package/src/runtime.ts +39 -18
  61. package/src/{experimental/toast.ts → toast.ts} +11 -25
  62. package/src/{experimental/withToast.ts → withToast.ts} +28 -10
  63. package/test/Mutation.test.ts +105 -11
  64. package/test/dist/form.test.d.ts.map +1 -1
  65. package/test/dist/lib.test.d.ts.map +1 -0
  66. package/test/dist/streamFinal.test.d.ts.map +1 -0
  67. package/test/dist/streamFn.test.d.ts.map +1 -0
  68. package/test/dist/stubs.d.ts +3289 -114
  69. package/test/dist/stubs.d.ts.map +1 -1
  70. package/test/dist/stubs.js +152 -25
  71. package/test/form-validation-errors.test.ts +23 -19
  72. package/test/form.test.ts +20 -2
  73. package/test/lib.test.ts +240 -0
  74. package/test/makeClient.test.ts +286 -38
  75. package/test/streamFinal.test.ts +63 -0
  76. package/test/streamFn.test.ts +436 -0
  77. package/test/stubs.ts +192 -42
  78. package/tsconfig.examples.json +20 -0
  79. package/tsconfig.json +0 -1
  80. package/tsconfig.json.bak +5 -2
  81. package/tsconfig.src.json +34 -34
  82. package/tsconfig.test.json +2 -2
  83. package/vitest.config.ts +5 -5
  84. package/dist/experimental/commander.d.ts +0 -359
  85. package/dist/experimental/commander.d.ts.map +0 -1
  86. package/dist/experimental/commander.js +0 -557
  87. package/dist/experimental/confirm.d.ts +0 -19
  88. package/dist/experimental/confirm.d.ts.map +0 -1
  89. package/dist/experimental/confirm.js +0 -28
  90. package/dist/experimental/intl.d.ts +0 -16
  91. package/dist/experimental/intl.d.ts.map +0 -1
  92. package/dist/experimental/intl.js +0 -5
  93. package/dist/experimental/makeUseCommand.d.ts +0 -8
  94. package/dist/experimental/makeUseCommand.d.ts.map +0 -1
  95. package/dist/experimental/makeUseCommand.js +0 -13
  96. package/dist/experimental/toast.d.ts +0 -47
  97. package/dist/experimental/toast.d.ts.map +0 -1
  98. package/dist/experimental/toast.js +0 -41
  99. package/dist/experimental/withToast.d.ts +0 -25
  100. package/dist/experimental/withToast.d.ts.map +0 -1
  101. package/dist/experimental/withToast.js +0 -45
  102. package/src/experimental/commander.ts +0 -1835
  103. package/src/experimental/intl.ts +0 -9
package/test/stubs.ts CHANGED
@@ -1,18 +1,20 @@
1
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
2
2
  import { type MessageFormatElement } from "@formatjs/icu-messageformat-parser"
3
3
  import * as Intl from "@formatjs/intl"
4
+ import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query"
4
5
  import { Effect, Layer, ManagedRuntime, Option, S } from "effect-app"
5
6
  import { ApiClientFactory, makeRpcClient } from "effect-app/client"
6
7
  import { RpcContextMap } from "effect-app/rpc"
8
+ import * as Exit from "effect/Exit"
7
9
  import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"
8
- import { ref } from "vue"
9
- import { Commander } from "../src/experimental/commander.js"
10
- import { I18n } from "../src/experimental/intl.js"
11
- import { makeUseCommand } from "../src/experimental/makeUseCommand.js"
12
- import * as Toast from "../src/experimental/toast.js"
13
- import { WithToast } from "../src/experimental/withToast.js"
14
- import { LegacyMutation, makeClient } from "../src/makeClient.js"
10
+ import { createApp, ref } from "vue"
11
+ import { Commander } from "../src/commander.js"
12
+ import { I18n } from "../src/intl.js"
13
+ import { makeClient } from "../src/makeClient.js"
15
14
  import { type MakeIntlReturn } from "../src/makeIntl.js"
15
+ import { makeUseCommand } from "../src/makeUseCommand.js"
16
+ import * as Toast from "../src/toast.js"
17
+ import { WithToast } from "../src/withToast.js"
16
18
 
17
19
  const fakeToastLayer = (toasts: any[] = []) =>
18
20
  Layer.effect(
@@ -26,33 +28,34 @@ const fakeToastLayer = (toasts: any[] = []) =>
26
28
  toasts.splice(idx, 1)
27
29
  }
28
30
  }
29
- const fakeToast = (message: string, options?: Toast.ToastOpts) => {
30
- const id = options?.id ?? Math.random().toString(36).substring(2, 15)
31
- console.log(`Toast [${id}]: ${message}`, options)
31
+ const fakeToast =
32
+ (type: "error" | "warning" | "success" | "info") => (message: string, options?: Toast.ToastOpts) => {
33
+ const id = options?.id ?? Math.random().toString(36).substring(2, 15)
34
+ console.log(`Toast [${type}][${id}]: ${message}`, options)
32
35
 
33
- options = { ...options, id }
34
- const idx = toasts.findIndex((_) => _.id === id)
35
- if (idx > -1) {
36
- const toast = toasts[idx]
37
- clearTimeout(toast.timeoutId)
38
- Object.assign(toast, { message, options })
39
- toast.timeoutId = setTimeout(() => {
40
- toasts.splice(idx, 1)
41
- }, options?.timeout ?? 3000)
42
- } else {
43
- const toast: any = { id, message, options }
44
- toast.timeoutId = setTimeout(() => {
45
- toasts.splice(idx, 1)
46
- }, options?.timeout ?? 3000)
47
- toasts.push(toast)
36
+ options = { ...options, id }
37
+ const idx = toasts.findIndex((_) => _.id === id)
38
+ if (idx > -1) {
39
+ const toast = toasts[idx]
40
+ clearTimeout(toast.timeoutId)
41
+ Object.assign(toast, { type, message, options })
42
+ toast.timeoutId = setTimeout(() => {
43
+ toasts.splice(idx, 1)
44
+ }, options?.timeout ?? 3000)
45
+ } else {
46
+ const toast: any = { id, type, message, options }
47
+ toast.timeoutId = setTimeout(() => {
48
+ toasts.splice(idx, 1)
49
+ }, options?.timeout ?? 3000)
50
+ toasts.push(toast)
51
+ }
52
+ return id
48
53
  }
49
- return id
50
- }
51
54
  return Toast.Toast.of(Toast.wrap({
52
- error: fakeToast,
53
- warning: fakeToast,
54
- success: fakeToast,
55
- info: fakeToast,
55
+ error: fakeToast("error"),
56
+ warning: fakeToast("warning"),
57
+ success: fakeToast("success"),
58
+ info: fakeToast("info"),
56
59
  dismiss
57
60
  })) as any
58
61
  })
@@ -92,20 +95,145 @@ export const useExperimental = (
92
95
  }
93
96
 
94
97
  export class RequestContextMap extends RpcContextMap.makeMap({}) {}
95
- export const { TaggedRequest: Req } = makeRpcClient(RequestContextMap)
96
- export class GetSomething2 extends Req<GetSomething2>()("GetSomething2", {
98
+ export const { TaggedRequestFor } = makeRpcClient(RequestContextMap)
99
+
100
+ export const SomethingReq = TaggedRequestFor("Something")
101
+ const SomethingQuery = SomethingReq.Query
102
+ const SomethingCommand = SomethingReq.Command
103
+ const SomethingStream = SomethingReq.Stream
104
+
105
+ class SomethingGetSomething2 extends SomethingQuery<SomethingGetSomething2>()("GetSomething2", {
106
+ id: S.String
107
+ }, { success: S.FiniteFromString }) {}
108
+
109
+ class SomethingGetSomething3 extends SomethingQuery<SomethingGetSomething3>()("GetSomething3", {
110
+ id: S.NullOr(S.String).withDefault
111
+ }, { success: S.FiniteFromString }) {}
112
+
113
+ class SomethingGetSomething4
114
+ extends SomethingQuery<SomethingGetSomething4>()("GetSomething4", {}, { success: S.FiniteFromString })
115
+ {}
116
+
117
+ class SomethingGetSomething2WithDependencies
118
+ extends SomethingQuery<SomethingGetSomething2WithDependencies>()("GetSomething2", {
119
+ id: S.String
120
+ }, {
121
+ // this is intentilally fake, to simulate a codec that requires a dependency
122
+ success: S.FiniteFromString as S.Codec<number, string, "dep-a">,
123
+ error: S.String
124
+ })
125
+ {}
126
+
127
+ type SomethingInvalidationResources = {
128
+ GetSomething2: typeof SomethingGetSomething2
129
+ GetSomething2WithDependencies: typeof SomethingGetSomething2WithDependencies
130
+ GetSomething3: typeof SomethingGetSomething3
131
+ }
132
+
133
+ // command stubs covering the input-shape matrix
134
+ class SomethingDoNoProps extends SomethingCommand<SomethingDoNoProps>()("DoNoProps", {}) {}
135
+
136
+ class SomethingDoOptionalOnly extends SomethingCommand<SomethingDoOptionalOnly>()("DoOptionalOnly", {
137
+ name: S.optional(S.String)
138
+ }) {}
139
+
140
+ class SomethingDoRequiredOnly extends SomethingCommand<SomethingDoRequiredOnly>()("DoRequiredOnly", {
97
141
  id: S.String
98
- }, { success: S.NumberFromString }) {}
142
+ }) {}
99
143
 
100
- export class GetSomething2WithDependencies extends Req<GetSomething2WithDependencies>()("GetSomething2", {
144
+ class SomethingDoMixed extends SomethingCommand<SomethingDoMixed>()("DoMixed", {
145
+ id: S.String,
146
+ name: S.optional(S.String)
147
+ }) {}
148
+
149
+ class SomethingDoSomething extends SomethingCommand<
150
+ SomethingDoSomething,
151
+ { Something: SomethingInvalidationResources }
152
+ >()("DoSomething", {
101
153
  id: S.String
102
154
  }, {
103
- // this is intentilally fake, to simulate a codec that requires a dependency
104
- success: S.NumberFromString as S.Codec<number, string, "dep-a">,
105
- error: S.String
155
+ success: S.FiniteFromString
156
+ }, (queryKey, { Something }, input, output) => {
157
+ return [
158
+ { filters: { queryKey } },
159
+ {
160
+ filters: {
161
+ queryKey: [
162
+ Something["GetSomething2"].id,
163
+ input.id,
164
+ Exit.isSuccess(output) ? output.value.toString() : "failed"
165
+ ]
166
+ }
167
+ }
168
+ ]
106
169
  }) {}
107
170
 
108
- export const Something = { GetSomething2, GetSomething2WithDependencies, meta: { moduleName: "Something" as const } }
171
+ // success schema has encoded shape { a: string | null } — used to test projection constraints
172
+ class SomethingGetStructNullable extends SomethingQuery<SomethingGetStructNullable>()("GetStructNullable", {}, {
173
+ success: S.Struct({ a: S.NullOr(S.String) })
174
+ }) {}
175
+
176
+ /** Stream event: intermediate progress update. */
177
+ export class OperationProgress extends S.TaggedClass<OperationProgress>()("OperationProgress", {
178
+ completed: S.NonNegativeInt,
179
+ total: S.NonNegativeInt
180
+ }) {}
181
+
182
+ /** Stream event: final completion result. */
183
+ export class ExportComplete extends S.TaggedClass<ExportComplete>()("ExportComplete", {
184
+ fileUrl: S.NonEmptyString
185
+ }) {}
186
+
187
+ /** Stream with no `final` schema — execute resolves with `void`. */
188
+ class SomethingStreamWithoutFinal extends SomethingStream<SomethingStreamWithoutFinal>()("StreamWithoutFinal", {
189
+ id: S.String
190
+ }, {
191
+ success: S.Union([OperationProgress, ExportComplete])
192
+ }) {}
193
+
194
+ /** Stream with a `final` schema — execute resolves with `ExportComplete`. */
195
+ class SomethingStreamWithFinal extends SomethingStream<SomethingStreamWithFinal>()("StreamWithFinal", {
196
+ id: S.String
197
+ }, {
198
+ success: S.Union([OperationProgress, ExportComplete]),
199
+ final: ExportComplete
200
+ }) {}
201
+
202
+ export const Something = {
203
+ GetSomething2: SomethingGetSomething2,
204
+ GetSomething2WithDependencies: SomethingGetSomething2WithDependencies,
205
+ GetSomething3: SomethingGetSomething3,
206
+ GetSomething4: SomethingGetSomething4,
207
+ DoNoProps: SomethingDoNoProps,
208
+ DoOptionalOnly: SomethingDoOptionalOnly,
209
+ DoRequiredOnly: SomethingDoRequiredOnly,
210
+ DoMixed: SomethingDoMixed,
211
+ DoSomething: SomethingDoSomething,
212
+ GetStructNullable: SomethingGetStructNullable,
213
+ StreamWithoutFinal: SomethingStreamWithoutFinal,
214
+ StreamWithFinal: SomethingStreamWithFinal
215
+ }
216
+
217
+ export const SomethingElseReq = TaggedRequestFor("SomethingElse")
218
+ const SomethingElseQuery = SomethingElseReq.Query
219
+
220
+ class SomethingElseGetSomething2 extends SomethingElseQuery<SomethingElseGetSomething2>()("GetSomething2", {
221
+ id: S.String
222
+ }, { success: S.FiniteFromString }) {}
223
+
224
+ class SomethingElseGetSomething2WithDependencies
225
+ extends SomethingElseQuery<SomethingElseGetSomething2WithDependencies>()("GetSomething2", {
226
+ id: S.String
227
+ }, {
228
+ success: S.FiniteFromString as S.Codec<number, string, "dep-a">,
229
+ error: S.String
230
+ })
231
+ {}
232
+
233
+ export const SomethingElse = {
234
+ GetSomething2: SomethingElseGetSomething2,
235
+ GetSomething2WithDependencies: SomethingElseGetSomething2WithDependencies
236
+ }
109
237
 
110
238
  export const useClient = (
111
239
  options?: { messages?: Record<string, string> | Record<string, MessageFormatElement[]>; toasts: any[] }
@@ -117,9 +245,31 @@ export const useClient = (
117
245
  const api = ApiClientFactory.layer({ url: "bogus", headers: Option.none() }).pipe(
118
246
  Layer.provide(FetchHttpClient.layer)
119
247
  )
120
- const lm = LegacyMutation.Default.pipe(Layer.provide([FakeIntlLayer, FakeToastLayer]))
121
- const layers = Layer.mergeAll(CommanderLayer, WithToastLayer, FakeToastLayer, FakeIntlLayer, api, lm)
248
+ const layers = Layer.mergeAll(CommanderLayer, WithToastLayer, FakeToastLayer, FakeIntlLayer, api)
122
249
 
123
250
  const clientFor_ = ApiClientFactory.makeFor(Layer.empty)
124
- return makeClient(() => ManagedRuntime.make(layers), clientFor_, Layer.empty)
251
+ const rawClient = makeClient(() => ManagedRuntime.make(layers), clientFor_, Layer.empty)
252
+
253
+ // Provide a Vue injection context so that composition-API hooks (e.g. useQueryClient)
254
+ // called during client initialisation work outside a component setup() function.
255
+ const vueApp = createApp({})
256
+ const testQueryClientConfig = { defaultOptions: { queries: { retry: false }, mutations: { retry: false } } }
257
+ vueApp.use(VueQueryPlugin, { queryClient: new QueryClient(testQueryClientConfig) })
258
+
259
+ const origClientFor = rawClient.clientFor
260
+ const clientFor: typeof origClientFor = function(m, ...args) {
261
+ const proxy = origClientFor(m, ...args)
262
+ // Warm up lazy mutation-hook initialisation inside the Vue injection context.
263
+ // After the first property access, useMutation() is cached and subsequent
264
+ // accesses outside the context succeed.
265
+ const firstPropertyName = Object.keys(m)[0]
266
+ if (firstPropertyName !== undefined) {
267
+ vueApp.runWithContext(() => {
268
+ void (proxy as Record<string, unknown>)[firstPropertyName]
269
+ })
270
+ }
271
+ return proxy
272
+ }
273
+
274
+ return { ...rawClient, clientFor }
125
275
  }
@@ -0,0 +1,20 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "include": [
4
+ "examples"
5
+ ],
6
+ "references": [
7
+ {
8
+ "path": "./tsconfig.src.json"
9
+ }
10
+ ],
11
+ "compilerOptions": {
12
+ "lib": [
13
+ "esnext",
14
+ "DOM"
15
+ ],
16
+ "tsBuildInfoFile": ".tsbuildinfo/examples.tsbuildinfo",
17
+ "rootDir": "examples",
18
+ "moduleResolution": "Node16"
19
+ }
20
+ }
package/tsconfig.json CHANGED
@@ -32,7 +32,6 @@
32
32
  "outDir": "build/dist",
33
33
  "resolveJsonModule": true,
34
34
  "moduleResolution": "Node16",
35
- "downlevelIteration": true,
36
35
  "noErrorTruncation": true,
37
36
  "forceConsistentCasingInFileNames": true,
38
37
  "types": [
package/tsconfig.json.bak CHANGED
@@ -7,6 +7,9 @@
7
7
  },
8
8
  {
9
9
  "path": "./tsconfig.test.json"
10
+ },
11
+ {
12
+ "path": "./tsconfig.examples.json"
10
13
  }
11
- ],
12
- }
14
+ ]
15
+ }
package/tsconfig.src.json CHANGED
@@ -1,36 +1,36 @@
1
1
  {
2
- "extends": "../../tsconfig.base.json",
3
- "compilerOptions": {
4
- "lib": [
5
- "esnext",
6
- "DOM"
7
- ],
8
- "tsBuildInfoFile": "./dist/.tsbuildinfo",
9
- "esModuleInterop": true,
10
- "rootDir": "./src",
11
- // keep in here, cause madge can't detect it from extended tsconfig
12
- "moduleResolution": "Node16",
13
- "outDir": "./dist"
14
- },
15
- "include": [
16
- "./src/**/*.ts"
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "lib": [
5
+ "esnext",
6
+ "DOM"
17
7
  ],
18
- "exclude": [
19
- "./dist",
20
- "*.test.ts",
21
- "node_modules",
22
- "build",
23
- "lib",
24
- "dist",
25
- "**/*.d.ts.map",
26
- ".git",
27
- ".data",
28
- "**/.*",
29
- "**/*.tmp"
30
- ],
31
- "references": [
32
- {
33
- "path": "../effect-app"
34
- }
35
- ]
36
- }
8
+ "tsBuildInfoFile": "./dist/.tsbuildinfo",
9
+ "esModuleInterop": true,
10
+ "rootDir": "./src",
11
+ // keep in here, cause madge can't detect it from extended tsconfig
12
+ "moduleResolution": "Node16",
13
+ "outDir": "./dist"
14
+ },
15
+ "include": [
16
+ "./src/**/*.ts"
17
+ ],
18
+ "exclude": [
19
+ "./dist",
20
+ "*.test.ts",
21
+ "node_modules",
22
+ "build",
23
+ "lib",
24
+ "dist",
25
+ "**/*.d.ts.map",
26
+ ".git",
27
+ ".data",
28
+ "**/.*",
29
+ "**/*.tmp"
30
+ ],
31
+ "references": [
32
+ {
33
+ "path": "../effect-app"
34
+ }
35
+ ]
36
+ }
@@ -28,6 +28,6 @@
28
28
  "references": [
29
29
  {
30
30
  "path": "./tsconfig.src.json"
31
- },
31
+ }
32
32
  ]
33
- }
33
+ }
package/vitest.config.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  /// <reference types="vitest" />
2
+ import vue from "@vitejs/plugin-vue"
2
3
  import { defineConfig } from "vitest/config"
3
- import vue from '@vitejs/plugin-vue'
4
4
  import makeConfig from "../../vite.config.base"
5
5
 
6
6
  export default defineConfig({
7
7
  ...makeConfig(__dirname),
8
8
  plugins: [vue()],
9
9
  test: {
10
- environment: 'jsdom',
11
- include: ['src/**/*.test.{ts,tsx}', '**/test/**/*.test.{ts,tsx}', '**/__tests__/**/*.test.{ts,tsx}'],
12
- exclude: ['node_modules/**', 'dist/**'],
10
+ environment: "jsdom",
11
+ include: ["src/**/*.test.{ts,tsx}", "**/test/**/*.test.{ts,tsx}", "**/__tests__/**/*.test.{ts,tsx}"],
12
+ exclude: ["node_modules/**", "dist/**"],
13
13
  globals: true
14
14
  },
15
15
  optimizeDeps: {
16
- exclude: ['**/__tests__/**', '**/test/**', '**/*.test.*']
16
+ exclude: ["**/__tests__/**", "**/test/**", "**/*.test.*"]
17
17
  }
18
18
  })