zodvex 0.7.4 → 0.7.5
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/README.md +1 -1
- package/dist/client/index.js +69 -9
- package/dist/client/index.js.map +1 -1
- package/dist/internal/actionCtx.d.ts +18 -2
- package/dist/internal/actionCtx.d.ts.map +1 -1
- package/dist/internal/init.d.ts.map +1 -1
- package/dist/mini/client/index.js +69 -9
- package/dist/mini/client/index.js.map +1 -1
- package/dist/mini/react/index.js +11 -0
- package/dist/mini/react/index.js.map +1 -1
- package/dist/mini/server/index.js +56 -20
- package/dist/mini/server/index.js.map +1 -1
- package/dist/public/client/zodvexClient.d.ts +38 -2
- package/dist/public/client/zodvexClient.d.ts.map +1 -1
- package/dist/public/react/zodvexReactClient.d.ts +10 -0
- package/dist/public/react/zodvexReactClient.d.ts.map +1 -1
- package/dist/react/index.js +11 -0
- package/dist/react/index.js.map +1 -1
- package/dist/server/index.js +56 -20
- package/dist/server/index.js.map +1 -1
- package/package.json +1 -1
- package/src/internal/actionCtx.ts +73 -18
- package/src/internal/init.ts +35 -6
- package/src/public/client/zodvexClient.ts +116 -11
- package/src/public/react/zodvexReactClient.ts +17 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zodvex",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"description": "Codec-first Zod v4 integration for Convex -- type-safe validation, encoding, DB wrapping, and codegen.",
|
|
5
5
|
"keywords": ["zod", "convex", "validators", "codec", "mapping", "schema", "validation"],
|
|
6
6
|
"homepage": "https://github.com/panzacoder/zodvex#readme",
|
|
@@ -1,14 +1,83 @@
|
|
|
1
|
-
import type { GenericActionCtx, GenericDataModel } from 'convex/server'
|
|
2
|
-
import type { BoundaryHelpersOptions } from './boundaryHelpers'
|
|
1
|
+
import type { GenericActionCtx, GenericDataModel, Scheduler } from 'convex/server'
|
|
2
|
+
import type { BoundaryHelpers, BoundaryHelpersOptions } from './boundaryHelpers'
|
|
3
3
|
import { createBoundaryHelpers } from './boundaryHelpers'
|
|
4
4
|
import type { AnyRegistry } from './types'
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Wraps
|
|
7
|
+
* Wraps a `runQuery`/`runMutation` function so it encodes codec args
|
|
8
|
+
* (runtime -> wire) before the call and decodes the result (wire -> runtime)
|
|
9
|
+
* after. Functions not in the registry pass through unchanged.
|
|
10
|
+
*/
|
|
11
|
+
function wrapRun(fn: (ref: any, ...rest: any[]) => Promise<any>, codec: BoundaryHelpers) {
|
|
12
|
+
return async (ref: any, ...restArgs: any[]) => {
|
|
13
|
+
const wireArgs = codec.encodeArgs(ref, restArgs[0])
|
|
14
|
+
const wireResult = await fn(ref, wireArgs)
|
|
15
|
+
return codec.decodeResult(ref, wireResult)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wraps a {@link Scheduler} so `runAfter`/`runAt` encode codec args to wire
|
|
21
|
+
* before scheduling.
|
|
22
|
+
*
|
|
23
|
+
* A scheduled function crosses the Convex boundary exactly like `runMutation`:
|
|
24
|
+
* the caller holds *decoded* (runtime) values, but Convex serializes the args
|
|
25
|
+
* against the target's *wire* validator. A non-serializable runtime value (e.g.
|
|
26
|
+
* a Symbol-valued codec field) cannot cross at all, and branded/`SensitiveField`
|
|
27
|
+
* codecs are the wrong shape — so the args must be encoded first.
|
|
28
|
+
*
|
|
29
|
+
* Return values are NOT decoded: the scheduler returns the scheduled-function
|
|
30
|
+
* id, not the target's result. Other members (e.g. `cancel`) pass through.
|
|
31
|
+
*/
|
|
32
|
+
function wrapScheduler(scheduler: Scheduler, codec: BoundaryHelpers): Scheduler {
|
|
33
|
+
const wrapped: any = { ...scheduler }
|
|
34
|
+
wrapped.runAfter = (delayMs: number, ref: any, ...restArgs: any[]) => {
|
|
35
|
+
const wireArgs = codec.encodeArgs(ref, restArgs[0])
|
|
36
|
+
return (scheduler.runAfter as any)(delayMs, ref, ...(restArgs.length ? [wireArgs] : []))
|
|
37
|
+
}
|
|
38
|
+
wrapped.runAt = (timestamp: number | Date, ref: any, ...restArgs: any[]) => {
|
|
39
|
+
const wireArgs = codec.encodeArgs(ref, restArgs[0])
|
|
40
|
+
return (scheduler.runAt as any)(timestamp, ref, ...(restArgs.length ? [wireArgs] : []))
|
|
41
|
+
}
|
|
42
|
+
return wrapped as Scheduler
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Builds ctx overrides that auto-encode codec args at outbound call sites:
|
|
47
|
+
* - `runQuery` / `runMutation`: encode args, decode result.
|
|
48
|
+
* - `scheduler.runAfter` / `scheduler.runAt`: encode args.
|
|
49
|
+
*
|
|
50
|
+
* Only the members present on the given ctx are included, so this serves both
|
|
51
|
+
* action ctx (run* + scheduler) and mutation ctx (scheduler only).
|
|
52
|
+
*
|
|
53
|
+
* @internal Used by initZodvex when the `registry` option is provided.
|
|
54
|
+
*/
|
|
55
|
+
export function createCodecCallOverrides(
|
|
56
|
+
registry: AnyRegistry,
|
|
57
|
+
ctx: { runQuery?: unknown; runMutation?: unknown; scheduler?: Scheduler },
|
|
58
|
+
options?: BoundaryHelpersOptions
|
|
59
|
+
): Record<string, unknown> {
|
|
60
|
+
const codec = createBoundaryHelpers(registry, options)
|
|
61
|
+
const overrides: Record<string, unknown> = {}
|
|
62
|
+
if (typeof ctx.runQuery === 'function') {
|
|
63
|
+
overrides.runQuery = wrapRun(ctx.runQuery as any, codec)
|
|
64
|
+
}
|
|
65
|
+
if (typeof ctx.runMutation === 'function') {
|
|
66
|
+
overrides.runMutation = wrapRun(ctx.runMutation as any, codec)
|
|
67
|
+
}
|
|
68
|
+
if (ctx.scheduler) {
|
|
69
|
+
overrides.scheduler = wrapScheduler(ctx.scheduler, codec)
|
|
70
|
+
}
|
|
71
|
+
return overrides
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Wraps an action context's runQuery/runMutation and scheduler with automatic
|
|
8
76
|
* codec transforms via the zodvex registry.
|
|
9
77
|
*
|
|
10
78
|
* - Args are encoded (runtime -> wire) before calling the inner function
|
|
11
79
|
* - Results are decoded (wire -> runtime) before returning to the handler
|
|
80
|
+
* (runQuery/runMutation only — the scheduler returns a scheduled-function id)
|
|
12
81
|
* - Functions not in the registry pass through unchanged
|
|
13
82
|
*
|
|
14
83
|
* @internal Used by initZodvex when registry option is provided.
|
|
@@ -18,19 +87,5 @@ export function createZodvexActionCtx<DM extends GenericDataModel>(
|
|
|
18
87
|
ctx: GenericActionCtx<DM>,
|
|
19
88
|
options?: BoundaryHelpersOptions
|
|
20
89
|
): GenericActionCtx<DM> {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
...ctx,
|
|
25
|
-
runQuery: async (ref: any, ...restArgs: any[]) => {
|
|
26
|
-
const wireArgs = codec.encodeArgs(ref, restArgs[0])
|
|
27
|
-
const wireResult = await ctx.runQuery(ref, wireArgs)
|
|
28
|
-
return codec.decodeResult(ref, wireResult)
|
|
29
|
-
},
|
|
30
|
-
runMutation: async (ref: any, ...restArgs: any[]) => {
|
|
31
|
-
const wireArgs = codec.encodeArgs(ref, restArgs[0])
|
|
32
|
-
const wireResult = await ctx.runMutation(ref, wireArgs)
|
|
33
|
-
return codec.decodeResult(ref, wireResult)
|
|
34
|
-
}
|
|
35
|
-
} as GenericActionCtx<DM>
|
|
90
|
+
return { ...ctx, ...createCodecCallOverrides(registry, ctx, options) } as GenericActionCtx<DM>
|
|
36
91
|
}
|
package/src/internal/init.ts
CHANGED
|
@@ -10,7 +10,7 @@ import type {
|
|
|
10
10
|
} from 'convex/server'
|
|
11
11
|
import { NoOp } from 'convex-helpers/server/customFunctions'
|
|
12
12
|
import type { z } from 'zod'
|
|
13
|
-
import {
|
|
13
|
+
import { createCodecCallOverrides } from './actionCtx'
|
|
14
14
|
import type { CustomBuilder } from './custom'
|
|
15
15
|
import { zCustomAction, zCustomMutation, zCustomQuery } from './custom'
|
|
16
16
|
import { createZodvexCustomization } from './customization'
|
|
@@ -285,10 +285,11 @@ export function initZodvex(
|
|
|
285
285
|
const noOp = createNoOpCustomization()
|
|
286
286
|
const wrap = options?.wrapDb !== false
|
|
287
287
|
|
|
288
|
-
const
|
|
288
|
+
const registryThunk = options?.registry
|
|
289
|
+
const actionCust = createActionCustomization(registryThunk, noOp)
|
|
289
290
|
const customizations = {
|
|
290
291
|
query: wrap ? codec.query : noOp,
|
|
291
|
-
mutation: wrap ? codec.mutation : noOp,
|
|
292
|
+
mutation: createMutationCustomization(wrap ? codec.mutation : noOp, registryThunk),
|
|
292
293
|
action: actionCust
|
|
293
294
|
}
|
|
294
295
|
|
|
@@ -309,10 +310,38 @@ function createActionCustomization(
|
|
|
309
310
|
|
|
310
311
|
return {
|
|
311
312
|
args: {} as Record<string, never>,
|
|
312
|
-
input: async (ctx: any) => {
|
|
313
|
-
|
|
313
|
+
input: async (ctx: any) => ({
|
|
314
|
+
// Auto-encode codec args at outbound call sites: runQuery/runMutation
|
|
315
|
+
// (encode args, decode result) and scheduler.runAfter/runAt (encode args).
|
|
316
|
+
ctx: createCodecCallOverrides(registryThunk(), ctx),
|
|
317
|
+
args: {}
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Composes the codec DB customization with outbound codec-arg encoding for the
|
|
324
|
+
* mutation builders. Mutations expose `ctx.scheduler` (runAfter/runAt), so when
|
|
325
|
+
* a registry is provided those calls auto-encode decoded codec args to wire —
|
|
326
|
+
* symmetric with the inbound decode the receiving function already performs.
|
|
327
|
+
*
|
|
328
|
+
* Without a registry, the DB customization is returned unchanged.
|
|
329
|
+
*/
|
|
330
|
+
function createMutationCustomization(
|
|
331
|
+
dbCust: InternalCustomization,
|
|
332
|
+
registryThunk: (() => AnyRegistry) | undefined
|
|
333
|
+
): InternalCustomization {
|
|
334
|
+
if (!registryThunk) {
|
|
335
|
+
return dbCust
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
args: {} as Record<string, never>,
|
|
340
|
+
input: async (ctx: any, _args: any, extra?: any) => {
|
|
341
|
+
const dbResult = await dbCust.input(ctx, {}, extra)
|
|
342
|
+
const callOverrides = createCodecCallOverrides(registryThunk(), ctx)
|
|
314
343
|
return {
|
|
315
|
-
ctx: {
|
|
344
|
+
ctx: { ...dbResult.ctx, ...callOverrides },
|
|
316
345
|
args: {}
|
|
317
346
|
}
|
|
318
347
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AuthTokenFetcher } from 'convex/browser'
|
|
1
|
+
import type { AuthTokenFetcher, ConnectionState, MutationOptions } from 'convex/browser'
|
|
2
2
|
import { ConvexClient } from 'convex/browser'
|
|
3
3
|
import type { FunctionArgs, FunctionReference, FunctionReturnType } from 'convex/server'
|
|
4
4
|
import type { BoundaryHelpersOptions } from '../../internal/boundaryHelpers'
|
|
@@ -21,6 +21,7 @@ export class ZodvexClient<R extends AnyRegistry = AnyRegistry> {
|
|
|
21
21
|
private innerClient?: ConvexClient
|
|
22
22
|
private url?: string
|
|
23
23
|
private pendingAuthFetcher?: AuthTokenFetcher
|
|
24
|
+
private pendingAuthOnChange?: (isAuthenticated: boolean) => void
|
|
24
25
|
|
|
25
26
|
constructor(registry: R, options: ZodvexClientOptions) {
|
|
26
27
|
this.codec = createBoundaryHelpers(registry, { onDecodeError: options.onDecodeError })
|
|
@@ -42,7 +43,7 @@ export class ZodvexClient<R extends AnyRegistry = AnyRegistry> {
|
|
|
42
43
|
|
|
43
44
|
const client = new ConvexClient(this.getUrl())
|
|
44
45
|
if (this.pendingAuthFetcher) {
|
|
45
|
-
client.setAuth(this.pendingAuthFetcher)
|
|
46
|
+
client.setAuth(this.pendingAuthFetcher, this.pendingAuthOnChange)
|
|
46
47
|
}
|
|
47
48
|
this.innerClient = client
|
|
48
49
|
return client
|
|
@@ -65,11 +66,33 @@ export class ZodvexClient<R extends AnyRegistry = AnyRegistry> {
|
|
|
65
66
|
|
|
66
67
|
async mutate<M extends FunctionReference<'mutation', any, any, any>>(
|
|
67
68
|
ref: M,
|
|
68
|
-
args: M['_args']
|
|
69
|
+
args: M['_args'],
|
|
70
|
+
options?: MutationOptions
|
|
69
71
|
): Promise<M['_returnType']> {
|
|
70
72
|
const wireResult = await this.getConvex().mutation(
|
|
71
73
|
ref,
|
|
72
|
-
this.codec.encodeArgs(ref, args) as FunctionArgs<M
|
|
74
|
+
this.codec.encodeArgs(ref, args) as FunctionArgs<M>,
|
|
75
|
+
options
|
|
76
|
+
)
|
|
77
|
+
return this.codec.decodeResult(ref, wireResult)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Alias for {@link mutate} — matches `ConvexClient.mutation` / `ZodvexReactClient.mutation`. */
|
|
81
|
+
mutation<M extends FunctionReference<'mutation', any, any, any>>(
|
|
82
|
+
ref: M,
|
|
83
|
+
args: M['_args'],
|
|
84
|
+
options?: MutationOptions
|
|
85
|
+
): Promise<M['_returnType']> {
|
|
86
|
+
return this.mutate(ref, args, options)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async action<A extends FunctionReference<'action', any, any, any>>(
|
|
90
|
+
ref: A,
|
|
91
|
+
args: A['_args']
|
|
92
|
+
): Promise<A['_returnType']> {
|
|
93
|
+
const wireResult = await this.getConvex().action(
|
|
94
|
+
ref,
|
|
95
|
+
this.codec.encodeArgs(ref, args) as FunctionArgs<A>
|
|
73
96
|
)
|
|
74
97
|
return this.codec.decodeResult(ref, wireResult)
|
|
75
98
|
}
|
|
@@ -77,22 +100,104 @@ export class ZodvexClient<R extends AnyRegistry = AnyRegistry> {
|
|
|
77
100
|
subscribe<Q extends FunctionReference<'query', any, any, any>>(
|
|
78
101
|
ref: Q,
|
|
79
102
|
args: Q['_args'],
|
|
80
|
-
callback: (result: Q['_returnType']) => void
|
|
103
|
+
callback: (result: Q['_returnType']) => void,
|
|
104
|
+
onError?: (e: Error) => void
|
|
105
|
+
): () => void {
|
|
106
|
+
const wireArgs = this.codec.encodeArgs(ref, args) as FunctionArgs<Q>
|
|
107
|
+
return this.getConvex().onUpdate(
|
|
108
|
+
ref,
|
|
109
|
+
wireArgs,
|
|
110
|
+
(wireResult: FunctionReturnType<Q>) => {
|
|
111
|
+
callback(this.codec.decodeResult(ref, wireResult))
|
|
112
|
+
},
|
|
113
|
+
onError
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Alias for {@link subscribe} — matches `ConvexClient.onUpdate`. */
|
|
118
|
+
onUpdate<Q extends FunctionReference<'query', any, any, any>>(
|
|
119
|
+
ref: Q,
|
|
120
|
+
args: Q['_args'],
|
|
121
|
+
callback: (result: Q['_returnType']) => void,
|
|
122
|
+
onError?: (e: Error) => void
|
|
81
123
|
): () => void {
|
|
124
|
+
return this.subscribe(ref, args, callback, onError)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Experimental paginated subscription. Encodes args to wire and decodes each
|
|
129
|
+
* page item through the registry, mirroring {@link subscribe}.
|
|
130
|
+
*/
|
|
131
|
+
onPaginatedUpdate_experimental<Q extends FunctionReference<'query', any, any, any>>(
|
|
132
|
+
ref: Q,
|
|
133
|
+
args: Q['_args'],
|
|
134
|
+
options: { initialNumItems: number },
|
|
135
|
+
callback: (result: {
|
|
136
|
+
page: Q['_returnType'][]
|
|
137
|
+
isDone: boolean
|
|
138
|
+
continueCursor: string
|
|
139
|
+
[key: string]: unknown
|
|
140
|
+
}) => void,
|
|
141
|
+
onError?: (e: Error) => void
|
|
142
|
+
): ReturnType<ConvexClient['onPaginatedUpdate_experimental']> {
|
|
82
143
|
const wireArgs = this.codec.encodeArgs(ref, args) as FunctionArgs<Q>
|
|
83
|
-
return this.getConvex().
|
|
84
|
-
|
|
85
|
-
|
|
144
|
+
return this.getConvex().onPaginatedUpdate_experimental(
|
|
145
|
+
ref,
|
|
146
|
+
wireArgs,
|
|
147
|
+
options,
|
|
148
|
+
(wireResult: any) => {
|
|
149
|
+
callback({
|
|
150
|
+
...wireResult,
|
|
151
|
+
page: wireResult.page.map((item: any) => this.codec.decodeResult(ref, item))
|
|
152
|
+
})
|
|
153
|
+
},
|
|
154
|
+
onError
|
|
155
|
+
) as ReturnType<ConvexClient['onPaginatedUpdate_experimental']>
|
|
86
156
|
}
|
|
87
157
|
|
|
88
|
-
|
|
89
|
-
|
|
158
|
+
/**
|
|
159
|
+
* Set the auth token. Accepts either a raw token string (convenience) or a
|
|
160
|
+
* Convex `AuthTokenFetcher` plus optional `onChange` callback (parity with
|
|
161
|
+
* `ConvexClient.setAuth`).
|
|
162
|
+
*/
|
|
163
|
+
setAuth(token: string | null): void
|
|
164
|
+
setAuth(fetchToken: AuthTokenFetcher, onChange?: (isAuthenticated: boolean) => void): void
|
|
165
|
+
setAuth(
|
|
166
|
+
tokenOrFetcher: string | null | AuthTokenFetcher,
|
|
167
|
+
onChange?: (isAuthenticated: boolean) => void
|
|
168
|
+
): void {
|
|
169
|
+
const fetcher: AuthTokenFetcher =
|
|
170
|
+
typeof tokenOrFetcher === 'function' ? tokenOrFetcher : async () => tokenOrFetcher
|
|
90
171
|
this.pendingAuthFetcher = fetcher
|
|
172
|
+
this.pendingAuthOnChange = onChange
|
|
91
173
|
if (this.innerClient) {
|
|
92
|
-
this.innerClient.setAuth(fetcher)
|
|
174
|
+
this.innerClient.setAuth(fetcher, onChange)
|
|
93
175
|
}
|
|
94
176
|
}
|
|
95
177
|
|
|
178
|
+
/** Returns the current auth token and its decoded claims, if authenticated. */
|
|
179
|
+
getAuth(): { token: string; decoded: Record<string, any> } | undefined {
|
|
180
|
+
return this.getConvex().getAuth()
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** Whether this client has been closed. False before the inner client is created. */
|
|
184
|
+
get closed(): boolean {
|
|
185
|
+
return this.innerClient?.closed ?? false
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Whether this client is disabled. False before the inner client is created. */
|
|
189
|
+
get disabled(): boolean {
|
|
190
|
+
return this.innerClient?.disabled ?? false
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
connectionState(): ConnectionState {
|
|
194
|
+
return this.getConvex().connectionState()
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
subscribeToConnectionState(cb: (state: ConnectionState) => void): () => void {
|
|
198
|
+
return this.getConvex().subscribeToConnectionState(cb)
|
|
199
|
+
}
|
|
200
|
+
|
|
96
201
|
async close() {
|
|
97
202
|
await this.getConvex().close()
|
|
98
203
|
}
|
|
@@ -84,6 +84,19 @@ export class ZodvexReactClient<R extends AnyRegistry = AnyRegistry> {
|
|
|
84
84
|
return this.codec.decodeResult(ref, wireResult)
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Indicates likely future interest in a query subscription. Encodes args to
|
|
89
|
+
* wire before delegating, mirroring {@link watchQuery}.
|
|
90
|
+
*/
|
|
91
|
+
prewarmQuery<Q extends FunctionReference<'query', any, any, any>>(queryOptions: {
|
|
92
|
+
query: Q
|
|
93
|
+
args: Q['_args']
|
|
94
|
+
extendSubscriptionFor?: number
|
|
95
|
+
}): void {
|
|
96
|
+
const wireArgs = this.codec.encodeArgs(queryOptions.query, queryOptions.args) as FunctionArgs<Q>
|
|
97
|
+
this.getConvex().prewarmQuery({ ...queryOptions, args: wireArgs })
|
|
98
|
+
}
|
|
99
|
+
|
|
87
100
|
watchQuery<Q extends FunctionReference<'query', any, any, any>>(
|
|
88
101
|
ref: Q,
|
|
89
102
|
args: Q['_args'],
|
|
@@ -139,6 +152,10 @@ export class ZodvexReactClient<R extends AnyRegistry = AnyRegistry> {
|
|
|
139
152
|
return this.getUrl()
|
|
140
153
|
}
|
|
141
154
|
|
|
155
|
+
get logger(): ConvexReactClient['logger'] {
|
|
156
|
+
return this.getConvex().logger
|
|
157
|
+
}
|
|
158
|
+
|
|
142
159
|
connectionState() {
|
|
143
160
|
return this.getConvex().connectionState()
|
|
144
161
|
}
|