kitcn 0.33.5 → 0.33.7
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 +31 -0
- package/dist/aggregate/index.d.ts +1 -1
- package/dist/auth/client/index.d.ts +74 -1
- package/dist/auth/client/index.js +238 -32
- package/dist/auth/generated/index.d.ts +1 -1
- package/dist/auth/index.d.ts +19 -19
- package/dist/auth/start/index.js +134 -2
- package/dist/crpc/index.d.ts +2 -2
- package/dist/crpc/index.js +2 -2
- package/dist/{generated-contract-disabled-PdNGvNYP.d.ts → generated-contract-disabled-QZXEzUnd.d.ts} +26 -26
- package/dist/orm/index.d.ts +1 -1
- package/dist/orm/migrations/index.d.ts +1 -1
- package/dist/{query-options-DGG93--5.js → query-options-YkFvau71.js} +6 -2
- package/dist/react/index.d.ts +12 -3
- package/dist/react/index.js +157 -140
- package/dist/rsc/index.d.ts +12 -2
- package/dist/rsc/index.js +1 -1
- package/dist/solid/index.js +4 -1
- package/dist/{auth-store-x3_wvhTR.js → token-gate-CEzbXArg.js} +178 -1
- package/dist/{types-C7JkpfeY.d.ts → types-Bb0kwteA.d.ts} +5 -1
- package/dist/{where-clause-compiler-qH6-NCM9.d.ts → where-clause-compiler-DAqhUREe.d.ts} +67 -67
- package/package.json +1 -1
- package/skills/kitcn/references/features/auth.md +35 -2
- package/skills/kitcn/references/features/react.md +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# kitcn
|
|
2
2
|
|
|
3
|
+
## 0.33.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#475](https://github.com/udecode/kitcn/pull/475) [`9e513f8`](https://github.com/udecode/kitcn/commit/9e513f85d4742efa041840c45989dfd3e96c883e) Thanks [@EfficiencyCorp](https://github.com/EfficiencyCorp)! - ## Patches
|
|
8
|
+
|
|
9
|
+
- Fix `onTokenIdentityChange` letting a token of another user or session into
|
|
10
|
+
the auth store, Convex, cRPC HTTP headers or the TanStack Start loader: the
|
|
11
|
+
SSR token, restored sessions, sign-in tokens, JWTs without `exp` and
|
|
12
|
+
concurrent first tokens are all held to the page's identity and every
|
|
13
|
+
mounted provider's current `tokenIdentityBaseline` getter, including changes
|
|
14
|
+
made by `onTokenIdentityAdmitted` before publication.
|
|
15
|
+
- Fix an identity guard trip staying local to one provider: every mounted
|
|
16
|
+
provider now hands out no token and publishes unauthenticated, each guarded
|
|
17
|
+
one closes its client and calls `onTokenIdentityChange` once, including a
|
|
18
|
+
guard enabled after the trip, and sign-in
|
|
19
|
+
mutations fail with `TOKEN_IDENTITY_CHANGED` until the page reloads.
|
|
20
|
+
- Fix the `optimisticAuth` window reopening after Convex refused a token: it
|
|
21
|
+
now ends at the Convex client's first auth result.
|
|
22
|
+
|
|
23
|
+
## 0.33.6
|
|
24
|
+
|
|
25
|
+
### Patch Changes
|
|
26
|
+
|
|
27
|
+
- [#473](https://github.com/udecode/kitcn/pull/473) [`16e0bb4`](https://github.com/udecode/kitcn/commit/16e0bb46d66a4d7166d577793fcb47f8161efa5d) Thanks [@EfficiencyCorp](https://github.com/EfficiencyCorp)! - - Add opt-in optimistic auth and document identity admission to
|
|
28
|
+
`ConvexAuthProvider`, with the same guarded token source for Convex and cRPC
|
|
29
|
+
HTTP requests.
|
|
30
|
+
- Add Convex-native `optimisticUpdate` support to cRPC `mutationOptions`.
|
|
31
|
+
- Reuse the Convex client's logger for server HTTP clients so construction is
|
|
32
|
+
deterministic during prerendering.
|
|
33
|
+
|
|
3
34
|
## 0.33.5
|
|
4
35
|
|
|
5
36
|
### Patch Changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Vn as ConvexTextBuilderInitial, Zt as ConvexTableWithColumns } from "../capabilities-Bxzoofl4.js";
|
|
2
|
-
import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-
|
|
2
|
+
import { C as ConvexNumberBuilderInitial, E as ConvexIdBuilderInitial, N as ConvexCustomBuilderInitial } from "../where-clause-compiler-DAqhUREe.js";
|
|
3
3
|
import * as convex_values0 from "convex/values";
|
|
4
4
|
import { GenericId, Infer, Value } from "convex/values";
|
|
5
5
|
import { DocumentByName, GenericDataModel, GenericDatabaseReader, GenericDatabaseWriter, TableNamesInDataModel } from "convex/server";
|
|
@@ -398,6 +398,75 @@ type ConvexAuthProviderProps = {
|
|
|
398
398
|
queryName: string;
|
|
399
399
|
}) => void; /** Custom function to detect UNAUTHORIZED errors. Default checks code property. */
|
|
400
400
|
isUnauthorized?: (error: unknown) => boolean;
|
|
401
|
+
/**
|
|
402
|
+
* Run auth-bound queries as soon as an unexpired JWT is held instead of
|
|
403
|
+
* after Convex confirms it, until the Convex client reports its first auth
|
|
404
|
+
* result (confirmed or refused); after that the gate follows Convex's
|
|
405
|
+
* confirmed state for the client's lifetime, remounts included. Convex
|
|
406
|
+
* sends them after Authenticate on the same socket and evaluates none of
|
|
407
|
+
* them if the token is refused; a refused token sets `isAuthenticated` back
|
|
408
|
+
* to false, which resets auth-bound queries. With the identity guard, only
|
|
409
|
+
* a token the guard admits opens it. No effect over a client the TanStack
|
|
410
|
+
* Start loader already authenticated. Every provider over one Convex client
|
|
411
|
+
* must use the same setting: mixing optimistic and non-optimistic providers
|
|
412
|
+
* over one client is unsupported, because results the client reports
|
|
413
|
+
* before an optimistic provider mounts are not seen. Default `false`.
|
|
414
|
+
*/
|
|
415
|
+
optimisticAuth?: boolean;
|
|
416
|
+
/**
|
|
417
|
+
* Fix the identity (JWT `sub` and `sessionId`) the document speaks for: the
|
|
418
|
+
* one it holds when it mounts (`initialToken`), or, with none, the first
|
|
419
|
+
* token that carries one. Guarantees: (1) a token of another user or
|
|
420
|
+
* session (or a JWT without an identity once one is established) is never
|
|
421
|
+
* cached, published, or handed to Convex, cRPC HTTP or the Start loader;
|
|
422
|
+
* (2) when a trip happens (this or another provider, or the Start loader,
|
|
423
|
+
* refuses one), this provider stops handing out tokens (null), publishes
|
|
424
|
+
* unauthenticated, calls `client.close()` (Convex's close semantics govern
|
|
425
|
+
* its queued work) and then this, once, where the app should reload the
|
|
426
|
+
* page. A provider that mounts (or shows again) on a page that is already
|
|
427
|
+
* tripped starts tripped and does the same, once, so a trip that happened
|
|
428
|
+
* while no guarded provider was mounted still reaches the app.
|
|
429
|
+
* The trip is page-wide in the browser (never on the server); sign-in
|
|
430
|
+
* mutations fail with `AuthMutationError` code `TOKEN_IDENTITY_CHANGED`
|
|
431
|
+
* until the reload. It governs the token kitcn supplies, not an
|
|
432
|
+
* `Authorization` header the app sets itself. Same-session refreshes pass
|
|
433
|
+
* through. Off when not set: a page that never enables it is unchanged.
|
|
434
|
+
* Once a guarded provider establishes the page identity, it persists until
|
|
435
|
+
* the reload, binding every provider (with or without this) and the Start
|
|
436
|
+
* loader, even after that provider unmounts. Two kitcn versions or
|
|
437
|
+
* revisions on one page (dev HMR across revisions included) are
|
|
438
|
+
* unsupported: they share no page identity until the reload.
|
|
439
|
+
*/
|
|
440
|
+
onTokenIdentityChange?: () => void;
|
|
441
|
+
/**
|
|
442
|
+
* The identity (`sub|sessionId`, as the guard decodes it from a JWT) the
|
|
443
|
+
* document already speaks for when this provider mounts, for an app that
|
|
444
|
+
* mounts the provider more than once in one document (for example one per
|
|
445
|
+
* route group, over a shared Convex client). The guard starts from it
|
|
446
|
+
* instead of from `initialToken` or the first token obtained, so a remount
|
|
447
|
+
* without a token still refuses another user's or session's token.
|
|
448
|
+
*
|
|
449
|
+
* A fixed value is read on the first render only. A getter is read at
|
|
450
|
+
* every admission, cached tokens included, and while this provider is
|
|
451
|
+
* mounted it binds every provider's admissions. A token must match all of:
|
|
452
|
+
* the page identity (the first one a provider knew), this provider's
|
|
453
|
+
* baseline and admitted identity, and every mounted getter's current
|
|
454
|
+
* answer; any mismatch trips the page. So a provider kept mounted but
|
|
455
|
+
* hidden (React `<Activity>`) cannot resume under an identity the document
|
|
456
|
+
* has since moved away from. A getter returning null does not constrain.
|
|
457
|
+
* Needs `onTokenIdentityChange`.
|
|
458
|
+
*/
|
|
459
|
+
tokenIdentityBaseline?: string | null | (() => string | null);
|
|
460
|
+
/**
|
|
461
|
+
* Called synchronously with every token the identity guard admits, before
|
|
462
|
+
* Convex or HTTP headers receive it: a fresh token when it is admitted and
|
|
463
|
+
* cached, a cached token each time it is handed out. The document can claim
|
|
464
|
+
* the identity at that moment (for example the first token a document
|
|
465
|
+
* without one obtains). Never called for a refused token. Read from a ref,
|
|
466
|
+
* so passing a new function does not re-run effects. Needs
|
|
467
|
+
* `onTokenIdentityChange`.
|
|
468
|
+
*/
|
|
469
|
+
onTokenIdentityAdmitted?: (token: string) => void;
|
|
401
470
|
};
|
|
402
471
|
/**
|
|
403
472
|
* Unified auth provider for Convex + Better Auth.
|
|
@@ -414,7 +483,11 @@ declare function ConvexAuthProvider({
|
|
|
414
483
|
initialToken,
|
|
415
484
|
onMutationUnauthorized,
|
|
416
485
|
onQueryUnauthorized,
|
|
417
|
-
isUnauthorized
|
|
486
|
+
isUnauthorized,
|
|
487
|
+
optimisticAuth,
|
|
488
|
+
onTokenIdentityChange,
|
|
489
|
+
onTokenIdentityAdmitted,
|
|
490
|
+
tokenIdentityBaseline
|
|
418
491
|
}: ConvexAuthProviderProps): react_jsx_runtime0.JSX.Element;
|
|
419
492
|
//#endregion
|
|
420
493
|
export { type AuthClient, type AuthClientWithPlugins, ConvexAuthProvider, type ConvexAuthProviderClient, ConvexAuthProviderProps, ConvexAuthProviderQueryClient, type PluginsWithCrossDomain, type PluginsWithoutCrossDomain, convexClient };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import {
|
|
2
|
+
import { C as FetchAccessTokenContext, D as isSessionSyncGraceActive, H as readAuthSessionFallbackData, K as CRPCClientError, M as useAuthValue, S as ConvexProviderWithAuth$1, U as readAuthSessionFallbackToken, V as clearAuthSessionFallback, W as writeAuthSessionFallbackData, _ as AUTH_SESSION_SYNC_GRACE_MS, a as admitToken, c as identityGuardInPlay, d as isTokenAdmissible, f as joinPage, g as tripDocument, h as subscribeDocumentTrip, i as publishToken, j as useAuthStore, l as isDocumentTripped, m as pageRegistry, n as publishAuthState, o as attachStoreGuard, p as mountGuard, q as defaultIsUnauthorized, s as decodeTokenSubjectSessionIdentity, t as admitStoreToken, u as isJwt, v as AuthProvider, z as decodeJwtExp } from "../../token-gate-CEzbXArg.js";
|
|
3
3
|
import { useConvexAuth } from "convex/react";
|
|
4
|
-
import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
4
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
5
5
|
import { jsx } from "react/jsx-runtime";
|
|
6
6
|
|
|
7
7
|
//#region src/auth/internal/convex-client.ts
|
|
@@ -12,6 +12,39 @@ const convexClient = () => {
|
|
|
12
12
|
};
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/auth-client/client-settlement.ts
|
|
17
|
+
function watchClientSettlement(client) {
|
|
18
|
+
const registry = pageRegistry();
|
|
19
|
+
if (!registry || registry.watchedClients.has(client)) return;
|
|
20
|
+
registry.watchedClients.add(client);
|
|
21
|
+
const setAuth = client.setAuth.bind(client);
|
|
22
|
+
client.setAuth = (fetchToken, onChange, onRefreshChange) => setAuth(fetchToken, (isAuthenticated) => {
|
|
23
|
+
settleClient(client);
|
|
24
|
+
onChange?.(isAuthenticated);
|
|
25
|
+
}, onRefreshChange);
|
|
26
|
+
}
|
|
27
|
+
function settleClient(client) {
|
|
28
|
+
const registry = pageRegistry();
|
|
29
|
+
if (!registry || registry.settledClients.has(client)) return;
|
|
30
|
+
registry.settledClients.add(client);
|
|
31
|
+
for (const listener of [...registry.settlementListeners.get(client) ?? []]) listener();
|
|
32
|
+
}
|
|
33
|
+
const isClientSettled = (client) => pageRegistry()?.settledClients.has(client) ?? false;
|
|
34
|
+
const subscribeClientSettlement = (client, listener) => {
|
|
35
|
+
const registry = pageRegistry();
|
|
36
|
+
if (!registry) return () => {};
|
|
37
|
+
let clientListeners = registry.settlementListeners.get(client);
|
|
38
|
+
if (!clientListeners) {
|
|
39
|
+
clientListeners = /* @__PURE__ */ new Set();
|
|
40
|
+
registry.settlementListeners.set(client, clientListeners);
|
|
41
|
+
}
|
|
42
|
+
clientListeners.add(listener);
|
|
43
|
+
return () => {
|
|
44
|
+
clientListeners.delete(listener);
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
15
48
|
//#endregion
|
|
16
49
|
//#region src/auth-client/convex-auth-provider.tsx
|
|
17
50
|
const defaultMutationHandler = () => {
|
|
@@ -134,13 +167,61 @@ const clearSessionAtom = (authClient) => {
|
|
|
134
167
|
* Structure: AuthProvider wraps ConvexAuthProviderInner so that
|
|
135
168
|
* useAuthStore() is available when creating fetchAccessToken.
|
|
136
169
|
*/
|
|
137
|
-
function ConvexAuthProvider({ children, client, authClient, convexQueryClient, initialToken, onMutationUnauthorized, onQueryUnauthorized, isUnauthorized }) {
|
|
170
|
+
function ConvexAuthProvider({ children, client, authClient, convexQueryClient, initialToken, onMutationUnauthorized, onQueryUnauthorized, isUnauthorized, optimisticAuth = false, onTokenIdentityChange, onTokenIdentityAdmitted, tokenIdentityBaseline }) {
|
|
138
171
|
useOTTHandler(authClient);
|
|
172
|
+
useMemo(() => {
|
|
173
|
+
if (optimisticAuth) watchClientSettlement(client);
|
|
174
|
+
}, [client, optimisticAuth]);
|
|
175
|
+
const baselineRef = useRef(tokenIdentityBaseline);
|
|
176
|
+
baselineRef.current = tokenIdentityBaseline;
|
|
177
|
+
const onAdmittedRef = useRef(onTokenIdentityAdmitted);
|
|
178
|
+
onAdmittedRef.current = onTokenIdentityAdmitted;
|
|
179
|
+
const [{ guard, inheritedTrip, refusedInitialToken }] = useState(() => {
|
|
180
|
+
const guarded = onTokenIdentityChange !== void 0;
|
|
181
|
+
const getter = typeof tokenIdentityBaseline === "function";
|
|
182
|
+
const fixed = getter ? null : tokenIdentityBaseline ?? null;
|
|
183
|
+
const created = {
|
|
184
|
+
baseline: () => {
|
|
185
|
+
if (!created.guarded) return null;
|
|
186
|
+
return created.hasGetter ? resolveTokenIdentityBaseline(baselineRef.current) : fixed;
|
|
187
|
+
},
|
|
188
|
+
guarded,
|
|
189
|
+
hasGetter: guarded && getter,
|
|
190
|
+
heldToken: () => null,
|
|
191
|
+
identity: guarded ? fixed : null,
|
|
192
|
+
onAdmitted: (token) => onAdmittedRef.current?.(token),
|
|
193
|
+
tripped: isDocumentTripped()
|
|
194
|
+
};
|
|
195
|
+
const refused = !created.tripped && !!initialToken && !isTokenAdmissible(initialToken, created, "hold");
|
|
196
|
+
if (refused) created.tripped = true;
|
|
197
|
+
else if (guarded) created.identity ??= decodeTokenSubjectSessionIdentity(initialToken ?? null);
|
|
198
|
+
return {
|
|
199
|
+
guard: created,
|
|
200
|
+
inheritedTrip: isDocumentTripped(),
|
|
201
|
+
refusedInitialToken: refused
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
useLayoutEffect(() => {
|
|
205
|
+
const guarded = onTokenIdentityChange !== void 0;
|
|
206
|
+
guard.hasGetter = guarded && typeof tokenIdentityBaseline === "function";
|
|
207
|
+
if (guarded === guard.guarded) return;
|
|
208
|
+
guard.guarded = guarded;
|
|
209
|
+
if (!guarded) return;
|
|
210
|
+
guard.identity ??= (guard.hasGetter ? null : guard.baseline()) ?? decodeTokenSubjectSessionIdentity(guard.heldToken());
|
|
211
|
+
joinPage(guard);
|
|
212
|
+
});
|
|
139
213
|
return /* @__PURE__ */ jsx(AuthProvider, {
|
|
140
|
-
initialValues: useMemo(() =>
|
|
214
|
+
initialValues: useMemo(() => refusedInitialToken || inheritedTrip ? {
|
|
215
|
+
expiresAt: null,
|
|
216
|
+
token: null
|
|
217
|
+
} : {
|
|
141
218
|
expiresAt: initialToken ? decodeJwtExp(initialToken) : null,
|
|
142
219
|
token: initialToken ?? null
|
|
143
|
-
}
|
|
220
|
+
}, [
|
|
221
|
+
initialToken,
|
|
222
|
+
inheritedTrip,
|
|
223
|
+
refusedInitialToken
|
|
224
|
+
]),
|
|
144
225
|
isUnauthorized: isUnauthorized ?? defaultIsUnauthorized,
|
|
145
226
|
onMutationUnauthorized: onMutationUnauthorized ?? defaultMutationHandler,
|
|
146
227
|
onQueryUnauthorized: onQueryUnauthorized ?? (() => {}),
|
|
@@ -148,6 +229,11 @@ function ConvexAuthProvider({ children, client, authClient, convexQueryClient, i
|
|
|
148
229
|
authClient,
|
|
149
230
|
client,
|
|
150
231
|
convexQueryClient,
|
|
232
|
+
guard,
|
|
233
|
+
inheritedTrip,
|
|
234
|
+
onTokenIdentityChange,
|
|
235
|
+
optimisticAuth,
|
|
236
|
+
refusedInitialToken,
|
|
151
237
|
children
|
|
152
238
|
})
|
|
153
239
|
});
|
|
@@ -156,7 +242,7 @@ function ConvexAuthProvider({ children, client, authClient, convexQueryClient, i
|
|
|
156
242
|
* Inner provider that has access to AuthStore via useAuthStore().
|
|
157
243
|
* Creates fetchAccessToken and passes it through context (no race condition).
|
|
158
244
|
*/
|
|
159
|
-
function ConvexAuthProviderInner({ children, client, authClient, convexQueryClient }) {
|
|
245
|
+
function ConvexAuthProviderInner({ children, client, authClient, convexQueryClient, guard, inheritedTrip, optimisticAuth, onTokenIdentityChange, refusedInitialToken }) {
|
|
160
246
|
const authStore = useAuthStore();
|
|
161
247
|
convexQueryClient?.updateAuthStore(authStore);
|
|
162
248
|
const { data: session, isPending } = authClient.useSession();
|
|
@@ -167,13 +253,14 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
167
253
|
const isMountedRef = useRef(false);
|
|
168
254
|
sessionRef.current = session;
|
|
169
255
|
isPendingRef.current = isPending;
|
|
256
|
+
const isOpaqueUnderGuard = useCallback((token) => identityGuardInPlay(guard) && !isJwt(token), [guard]);
|
|
170
257
|
const getCachedJwt = useCallback((minTimeRemainingMs = 0) => {
|
|
171
258
|
const cachedToken = authStore.get("token");
|
|
172
|
-
if (!cachedToken) return null;
|
|
259
|
+
if (!cachedToken || isOpaqueUnderGuard(cachedToken)) return null;
|
|
173
260
|
const expiresAt = decodeJwtExp(cachedToken);
|
|
174
261
|
if (expiresAt === null || expiresAt <= Date.now() + minTimeRemainingMs) return null;
|
|
175
262
|
return cachedToken;
|
|
176
|
-
}, [authStore]);
|
|
263
|
+
}, [authStore, isOpaqueUnderGuard]);
|
|
177
264
|
useEffect(() => {
|
|
178
265
|
if (hasActiveSessionData(session)) {
|
|
179
266
|
authStore.set("sessionSyncGraceUntil", null);
|
|
@@ -190,6 +277,62 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
190
277
|
isPending,
|
|
191
278
|
authStore
|
|
192
279
|
]);
|
|
280
|
+
const onTokenIdentityChangeRef = useRef(onTokenIdentityChange);
|
|
281
|
+
onTokenIdentityChangeRef.current = onTokenIdentityChange;
|
|
282
|
+
attachStoreGuard(authStore.store, guard);
|
|
283
|
+
const [guardTripped, setGuardTripped] = useState(refusedInitialToken || inheritedTrip);
|
|
284
|
+
const quarantinedRef = useRef(false);
|
|
285
|
+
const tripNotifiedRef = useRef(false);
|
|
286
|
+
const announcedTokenRef = useRef(null);
|
|
287
|
+
const quarantine = useCallback(() => {
|
|
288
|
+
guard.tripped = true;
|
|
289
|
+
authStore.set("token", null);
|
|
290
|
+
authStore.set("expiresAt", null);
|
|
291
|
+
authStore.set("sessionSyncGraceUntil", null);
|
|
292
|
+
authStore.set("isAuthenticated", false);
|
|
293
|
+
authStore.set("isLoading", false);
|
|
294
|
+
setGuardTripped(true);
|
|
295
|
+
}, [authStore, guard]);
|
|
296
|
+
const tripGuard = useCallback(() => {
|
|
297
|
+
guard.tripped = true;
|
|
298
|
+
if (!quarantinedRef.current) {
|
|
299
|
+
quarantinedRef.current = true;
|
|
300
|
+
quarantine();
|
|
301
|
+
}
|
|
302
|
+
tripDocument();
|
|
303
|
+
if (tripNotifiedRef.current || !onTokenIdentityChangeRef.current) return;
|
|
304
|
+
tripNotifiedRef.current = true;
|
|
305
|
+
try {
|
|
306
|
+
Promise.resolve(client.close()).catch(() => {});
|
|
307
|
+
} catch {}
|
|
308
|
+
try {
|
|
309
|
+
onTokenIdentityChangeRef.current?.();
|
|
310
|
+
} catch (error) {
|
|
311
|
+
console.error("[ConvexAuthProvider] onTokenIdentityChange threw", error);
|
|
312
|
+
}
|
|
313
|
+
}, [
|
|
314
|
+
client,
|
|
315
|
+
guard,
|
|
316
|
+
quarantine
|
|
317
|
+
]);
|
|
318
|
+
useLayoutEffect(() => {
|
|
319
|
+
guard.heldToken = () => authStore.get("token");
|
|
320
|
+
const unsubscribe = subscribeDocumentTrip(tripGuard);
|
|
321
|
+
const unmount = mountGuard(guard);
|
|
322
|
+
if (refusedInitialToken || isDocumentTripped()) tripGuard();
|
|
323
|
+
return () => {
|
|
324
|
+
unmount();
|
|
325
|
+
unsubscribe();
|
|
326
|
+
};
|
|
327
|
+
}, [
|
|
328
|
+
authStore,
|
|
329
|
+
guard,
|
|
330
|
+
refusedInitialToken,
|
|
331
|
+
tripGuard
|
|
332
|
+
]);
|
|
333
|
+
useLayoutEffect(() => {
|
|
334
|
+
if (onTokenIdentityChange && isDocumentTripped()) tripGuard();
|
|
335
|
+
}, [onTokenIdentityChange, tripGuard]);
|
|
193
336
|
useEffect(() => {
|
|
194
337
|
isMountedRef.current = true;
|
|
195
338
|
return () => {
|
|
@@ -203,9 +346,11 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
203
346
|
restoredTokenRef.current = persistedToken;
|
|
204
347
|
const persistedSessionData = readAuthSessionFallbackData();
|
|
205
348
|
const graceUntil = Date.now() + AUTH_SESSION_SYNC_GRACE_MS;
|
|
206
|
-
authStore
|
|
207
|
-
|
|
208
|
-
|
|
349
|
+
if (!publishToken(authStore, persistedToken, {
|
|
350
|
+
announce: true,
|
|
351
|
+
sessionSyncGraceUntil: graceUntil,
|
|
352
|
+
use: "restore"
|
|
353
|
+
})) return;
|
|
209
354
|
if (persistedSessionData) syncSessionAtom(authClient, persistedSessionData);
|
|
210
355
|
const ownsToken = () => authStore.get("token") === persistedToken;
|
|
211
356
|
const shouldStop = () => !isMountedRef.current || !ownsToken();
|
|
@@ -244,17 +389,18 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
244
389
|
if (pendingTokenRef.current) return pendingTokenRef.current;
|
|
245
390
|
const cachedToken = authStore.get("token");
|
|
246
391
|
const fetchOptions = { throw: false };
|
|
247
|
-
if (cachedToken && decodeJwtExp(cachedToken) === null) {
|
|
392
|
+
if (cachedToken && (decodeJwtExp(cachedToken) === null || isOpaqueUnderGuard(cachedToken))) {
|
|
248
393
|
fetchOptions.credentials = "omit";
|
|
249
394
|
fetchOptions.headers = { Authorization: `Bearer ${cachedToken}` };
|
|
250
395
|
}
|
|
251
396
|
pendingTokenRef.current = authClient.convex.token({ fetchOptions }).then((result) => {
|
|
252
|
-
const jwt = result
|
|
397
|
+
const jwt = result?.data?.token || null;
|
|
253
398
|
if (jwt) {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
399
|
+
if (!publishToken(authStore, jwt, {
|
|
400
|
+
announce: true,
|
|
401
|
+
sessionSyncGraceUntil: null
|
|
402
|
+
})) return null;
|
|
403
|
+
announcedTokenRef.current = jwt;
|
|
258
404
|
return jwt;
|
|
259
405
|
}
|
|
260
406
|
const cachedJwt = getCachedJwt();
|
|
@@ -304,11 +450,7 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
304
450
|
return fetchFreshToken();
|
|
305
451
|
}
|
|
306
452
|
const freshToken = await fetchFreshTokenForced();
|
|
307
|
-
if (!freshToken && cachedJwt) {
|
|
308
|
-
authStore.set("token", cachedJwt);
|
|
309
|
-
authStore.set("expiresAt", decodeJwtExp(cachedJwt));
|
|
310
|
-
return cachedJwt;
|
|
311
|
-
}
|
|
453
|
+
if (!freshToken && cachedJwt) return publishToken(authStore, cachedJwt, { announce: false }) ? cachedJwt : null;
|
|
312
454
|
return freshToken;
|
|
313
455
|
}
|
|
314
456
|
authStore.set("token", null);
|
|
@@ -319,15 +461,28 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
319
461
|
const cachedToken = authStore.get("token");
|
|
320
462
|
const expiresAt = authStore.get("expiresAt");
|
|
321
463
|
const timeRemaining = expiresAt ? expiresAt - Date.now() : 0;
|
|
322
|
-
if (!forceRefreshToken && cachedToken && expiresAt && timeRemaining >= 6e4) return cachedToken;
|
|
464
|
+
if (!forceRefreshToken && cachedToken && expiresAt && timeRemaining >= 6e4 && !isOpaqueUnderGuard(cachedToken)) return cachedToken;
|
|
323
465
|
if (!forceRefreshToken && pendingTokenRef.current) return pendingTokenRef.current;
|
|
324
466
|
if (forceRefreshToken) return fetchFreshTokenForced();
|
|
325
467
|
return fetchFreshToken();
|
|
326
468
|
}, [
|
|
327
469
|
authStore,
|
|
328
470
|
authClient,
|
|
329
|
-
getCachedJwt
|
|
471
|
+
getCachedJwt,
|
|
472
|
+
isOpaqueUnderGuard
|
|
330
473
|
]);
|
|
474
|
+
const guardedFetchAccessToken = useCallback(async (args = {}) => {
|
|
475
|
+
if (isDocumentTripped()) return null;
|
|
476
|
+
const token = await fetchAccessToken(args);
|
|
477
|
+
if (!token) return null;
|
|
478
|
+
const announced = announcedTokenRef.current === token;
|
|
479
|
+
if (announced) announcedTokenRef.current = null;
|
|
480
|
+
return admitToken(token, {
|
|
481
|
+
announce: !announced,
|
|
482
|
+
guard,
|
|
483
|
+
use: "handout"
|
|
484
|
+
}) ? token : null;
|
|
485
|
+
}, [fetchAccessToken, guard]);
|
|
331
486
|
const useAuth = useCallback(function useConvexAuthHook() {
|
|
332
487
|
const token = authStore.get("token");
|
|
333
488
|
const hasSession = hasActiveSessionData(sessionRef.current);
|
|
@@ -335,15 +490,20 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
335
490
|
return {
|
|
336
491
|
isLoading: isPendingRef.current && !token,
|
|
337
492
|
isAuthenticated: sessionMissing ? false : hasSession || token !== null,
|
|
338
|
-
fetchAccessToken
|
|
493
|
+
fetchAccessToken: guardedFetchAccessToken
|
|
339
494
|
};
|
|
340
|
-
}, [
|
|
495
|
+
}, [guardedFetchAccessToken, authStore]);
|
|
341
496
|
return /* @__PURE__ */ jsx(FetchAccessTokenContext.Provider, {
|
|
342
|
-
value:
|
|
497
|
+
value: guardedFetchAccessToken,
|
|
343
498
|
children: /* @__PURE__ */ jsx(ConvexProviderWithAuth$1, {
|
|
344
499
|
client,
|
|
345
500
|
useAuth,
|
|
346
|
-
children: /* @__PURE__ */ jsx(AuthStateSync, {
|
|
501
|
+
children: /* @__PURE__ */ jsx(AuthStateSync, {
|
|
502
|
+
client,
|
|
503
|
+
guardTripped,
|
|
504
|
+
optimisticAuth,
|
|
505
|
+
children
|
|
506
|
+
})
|
|
347
507
|
})
|
|
348
508
|
});
|
|
349
509
|
}
|
|
@@ -359,22 +519,68 @@ function ConvexAuthProviderInner({ children, client, authClient, convexQueryClie
|
|
|
359
519
|
* 5. Without defensive check, we'd sync { isLoading: false, isAuthenticated: false }
|
|
360
520
|
* 6. Queries would throw UNAUTHORIZED before token is validated
|
|
361
521
|
*/
|
|
362
|
-
function AuthStateSync({ children }) {
|
|
522
|
+
function AuthStateSync({ children, client, guardTripped, optimisticAuth = false }) {
|
|
363
523
|
const { isLoading: convexIsLoading, isAuthenticated } = useConvexAuth();
|
|
364
524
|
const authStore = useAuthStore();
|
|
365
525
|
const token = useAuthValue("token");
|
|
526
|
+
const subscribe = useCallback((listener) => subscribeClientSettlement(client, listener), [client]);
|
|
527
|
+
const getSettled = useCallback(() => isClientSettled(client), [client]);
|
|
528
|
+
const settled = useSyncExternalStore(subscribe, getSettled, getSettled);
|
|
366
529
|
useEffect(() => {
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
530
|
+
publishAuthState(authStore, resolveAuthGate({
|
|
531
|
+
admitHeldToken: (held) => admitStoreToken(authStore, held, { use: "hold" }),
|
|
532
|
+
convexIsLoading,
|
|
533
|
+
guardTripped: guardTripped || isDocumentTripped(),
|
|
534
|
+
isAuthenticated,
|
|
535
|
+
optimisticWindow: optimisticAuth && !settled && !isClientSettled(client),
|
|
536
|
+
token: authStore.get("token")
|
|
537
|
+
}));
|
|
370
538
|
}, [
|
|
539
|
+
client,
|
|
371
540
|
convexIsLoading,
|
|
541
|
+
guardTripped,
|
|
372
542
|
isAuthenticated,
|
|
543
|
+
optimisticAuth,
|
|
544
|
+
settled,
|
|
373
545
|
token,
|
|
374
546
|
authStore
|
|
375
547
|
]);
|
|
376
548
|
return children;
|
|
377
549
|
}
|
|
550
|
+
function resolveTokenIdentityBaseline(baseline) {
|
|
551
|
+
return typeof baseline === "function" ? baseline() : baseline ?? null;
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* The auth state published to the store, which is what every query gate reads.
|
|
555
|
+
*
|
|
556
|
+
* Without `optimisticAuth` this is the confirmed state: queries wait until
|
|
557
|
+
* Convex has confirmed the token. With it, until the Convex client reports its
|
|
558
|
+
* first auth result, a held, unexpired JWT counts as authenticated while
|
|
559
|
+
* Convex confirms it, so auth-bound queries subscribe at once; Convex sends
|
|
560
|
+
* them after Authenticate on the same socket, and a refused token closes the
|
|
561
|
+
* socket before any of them is evaluated. When Convex refuses the token,
|
|
562
|
+
* `isAuthenticated` falls back to false, which resets auth-bound queries (see
|
|
563
|
+
* `CRPCProviderInner`), and the window is over: no token reopens it.
|
|
564
|
+
*/
|
|
565
|
+
function resolveAuthGate({ admitHeldToken, convexIsLoading, guardTripped, isAuthenticated, optimisticWindow, token }) {
|
|
566
|
+
if (guardTripped || token !== null && !admitHeldToken(token)) return {
|
|
567
|
+
isAuthenticated: false,
|
|
568
|
+
isLoading: false
|
|
569
|
+
};
|
|
570
|
+
if (optimisticWindow && convexIsLoading && token !== null && isOptimisticToken(token)) return {
|
|
571
|
+
isAuthenticated: true,
|
|
572
|
+
isLoading: false
|
|
573
|
+
};
|
|
574
|
+
return {
|
|
575
|
+
isAuthenticated,
|
|
576
|
+
isLoading: convexIsLoading || !!token && !isAuthenticated
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
function isOptimisticToken(token) {
|
|
580
|
+
if (!isJwt(token)) return false;
|
|
581
|
+
const expiresAt = decodeJwtExp(token);
|
|
582
|
+
return expiresAt !== null && expiresAt > Date.now();
|
|
583
|
+
}
|
|
378
584
|
/**
|
|
379
585
|
* Handles cross-domain one-time token (OTT) verification.
|
|
380
586
|
*/
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, b as GenericAuthBeforeResult, i as getGeneratedAuthDisabledReason, n as GeneratedAuthDisabledReasonKind, r as createDisabledAuthRuntime, t as AuthRuntime, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../../generated-contract-disabled-
|
|
1
|
+
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, b as GenericAuthBeforeResult, i as getGeneratedAuthDisabledReason, n as GeneratedAuthDisabledReasonKind, r as createDisabledAuthRuntime, t as AuthRuntime, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../../generated-contract-disabled-QZXEzUnd.js";
|
|
2
2
|
export { type AuthRuntime, BetterAuthOptionsWithoutDatabase, type GeneratedAuthDisabledReasonKind, GenericAuthBeforeResult, GenericAuthDefinition, GenericAuthTriggerChange, GenericAuthTriggerHandlers, GenericAuthTriggers, createDisabledAuthRuntime, defineAuth, getGeneratedAuthDisabledReason };
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { a as QueryCtxWithPreferredOrmQueryTable, n as LookupByIdResultByCtx, t as DocByCtx } from "../query-context-BkNjkDCk.js";
|
|
2
2
|
import { t as GetAuth } from "../types-ex0-J-SC.js";
|
|
3
3
|
import { t as GenericCtx } from "../context-utils-Cbv4r0AA.js";
|
|
4
|
-
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, _ as updateManyHandler, a as AuthFunctions, b as GenericAuthBeforeResult, c as consumeOneHandler, d as createHandler, f as deleteManyHandler, g as incrementOneHandler, h as findOneHandler, i as getGeneratedAuthDisabledReason, l as countHandler, m as findManyHandler, n as GeneratedAuthDisabledReasonKind, o as Triggers, p as deleteOneHandler, r as createDisabledAuthRuntime, s as createClient, t as AuthRuntime, u as createApi, v as updateOneHandler, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../generated-contract-disabled-
|
|
4
|
+
import { C as GenericAuthTriggerHandlers, S as GenericAuthTriggerChange, T as defineAuth, _ as updateManyHandler, a as AuthFunctions, b as GenericAuthBeforeResult, c as consumeOneHandler, d as createHandler, f as deleteManyHandler, g as incrementOneHandler, h as findOneHandler, i as getGeneratedAuthDisabledReason, l as countHandler, m as findManyHandler, n as GeneratedAuthDisabledReasonKind, o as Triggers, p as deleteOneHandler, r as createDisabledAuthRuntime, s as createClient, t as AuthRuntime, u as createApi, v as updateOneHandler, w as GenericAuthTriggers, x as GenericAuthDefinition, y as BetterAuthOptionsWithoutDatabase } from "../generated-contract-disabled-QZXEzUnd.js";
|
|
5
5
|
import * as convex_values0 from "convex/values";
|
|
6
6
|
import { Infer } from "convex/values";
|
|
7
7
|
import { AuthConfig, DocumentByName, GenericDataModel, GenericMutationCtx, GenericQueryCtx, GenericSchema, PaginationOptions, PaginationResult, SchemaDefinition, TableNamesInDataModel } from "convex/server";
|
|
@@ -109,31 +109,31 @@ type AdapterPaginationOptions = PaginationOptions & {
|
|
|
109
109
|
};
|
|
110
110
|
declare const adapterWhereValidator: convex_values0.VObject<{
|
|
111
111
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
112
|
+
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
112
113
|
connector?: "AND" | "OR" | undefined;
|
|
113
|
-
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
114
|
-
field: string;
|
|
115
114
|
value: string | number | boolean | string[] | number[] | null;
|
|
115
|
+
field: string;
|
|
116
116
|
}, {
|
|
117
117
|
connector: convex_values0.VUnion<"AND" | "OR" | undefined, [convex_values0.VLiteral<"AND", "required">, convex_values0.VLiteral<"OR", "required">], "optional", never>;
|
|
118
118
|
field: convex_values0.VString<string, "required">;
|
|
119
119
|
mode: convex_values0.VUnion<"sensitive" | "insensitive" | undefined, [convex_values0.VLiteral<"sensitive", "required">, convex_values0.VLiteral<"insensitive", "required">], "optional", never>;
|
|
120
|
-
operator: convex_values0.VUnion<"
|
|
120
|
+
operator: convex_values0.VUnion<"eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined, [convex_values0.VLiteral<"lt", "required">, convex_values0.VLiteral<"lte", "required">, convex_values0.VLiteral<"gt", "required">, convex_values0.VLiteral<"gte", "required">, convex_values0.VLiteral<"eq", "required">, convex_values0.VLiteral<"in", "required">, convex_values0.VLiteral<"not_in", "required">, convex_values0.VLiteral<"ne", "required">, convex_values0.VLiteral<"contains", "required">, convex_values0.VLiteral<"starts_with", "required">, convex_values0.VLiteral<"ends_with", "required">], "optional", never>;
|
|
121
121
|
value: convex_values0.VUnion<string | number | boolean | string[] | number[] | null, [convex_values0.VString<string, "required">, convex_values0.VFloat64<number, "required">, convex_values0.VBoolean<boolean, "required">, convex_values0.VArray<string[], convex_values0.VString<string, "required">, "required">, convex_values0.VArray<number[], convex_values0.VFloat64<number, "required">, "required">, convex_values0.VNull<null, "required">], "required", never>;
|
|
122
|
-
}, "required", "mode" | "
|
|
122
|
+
}, "required", "mode" | "operator" | "value" | "field" | "connector">;
|
|
123
123
|
declare const adapterArgsValidator: convex_values0.VObject<{
|
|
124
|
+
limit?: number | undefined;
|
|
124
125
|
where?: {
|
|
125
126
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
127
|
+
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
126
128
|
connector?: "AND" | "OR" | undefined;
|
|
127
|
-
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
128
|
-
field: string;
|
|
129
129
|
value: string | number | boolean | string[] | number[] | null;
|
|
130
|
+
field: string;
|
|
130
131
|
}[] | undefined;
|
|
131
|
-
limit?: number | undefined;
|
|
132
|
-
offset?: number | undefined;
|
|
133
132
|
select?: string[] | undefined;
|
|
133
|
+
offset?: number | undefined;
|
|
134
134
|
sortBy?: {
|
|
135
|
-
field: string;
|
|
136
135
|
direction: "asc" | "desc";
|
|
136
|
+
field: string;
|
|
137
137
|
} | undefined;
|
|
138
138
|
model: string;
|
|
139
139
|
}, {
|
|
@@ -142,32 +142,32 @@ declare const adapterArgsValidator: convex_values0.VObject<{
|
|
|
142
142
|
offset: convex_values0.VFloat64<number | undefined, "optional">;
|
|
143
143
|
select: convex_values0.VArray<string[] | undefined, convex_values0.VString<string, "required">, "optional">;
|
|
144
144
|
sortBy: convex_values0.VObject<{
|
|
145
|
-
field: string;
|
|
146
145
|
direction: "asc" | "desc";
|
|
146
|
+
field: string;
|
|
147
147
|
} | undefined, {
|
|
148
148
|
direction: convex_values0.VUnion<"asc" | "desc", [convex_values0.VLiteral<"asc", "required">, convex_values0.VLiteral<"desc", "required">], "required", never>;
|
|
149
149
|
field: convex_values0.VString<string, "required">;
|
|
150
|
-
}, "optional", "
|
|
150
|
+
}, "optional", "direction" | "field">;
|
|
151
151
|
where: convex_values0.VArray<{
|
|
152
152
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
153
|
+
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
153
154
|
connector?: "AND" | "OR" | undefined;
|
|
154
|
-
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
155
|
-
field: string;
|
|
156
155
|
value: string | number | boolean | string[] | number[] | null;
|
|
156
|
+
field: string;
|
|
157
157
|
}[] | undefined, convex_values0.VObject<{
|
|
158
158
|
mode?: "sensitive" | "insensitive" | undefined;
|
|
159
|
+
operator?: "eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
159
160
|
connector?: "AND" | "OR" | undefined;
|
|
160
|
-
operator?: "lt" | "lte" | "gt" | "gte" | "eq" | "in" | "not_in" | "ne" | "contains" | "starts_with" | "ends_with" | undefined;
|
|
161
|
-
field: string;
|
|
162
161
|
value: string | number | boolean | string[] | number[] | null;
|
|
162
|
+
field: string;
|
|
163
163
|
}, {
|
|
164
164
|
connector: convex_values0.VUnion<"AND" | "OR" | undefined, [convex_values0.VLiteral<"AND", "required">, convex_values0.VLiteral<"OR", "required">], "optional", never>;
|
|
165
165
|
field: convex_values0.VString<string, "required">;
|
|
166
166
|
mode: convex_values0.VUnion<"sensitive" | "insensitive" | undefined, [convex_values0.VLiteral<"sensitive", "required">, convex_values0.VLiteral<"insensitive", "required">], "optional", never>;
|
|
167
|
-
operator: convex_values0.VUnion<"
|
|
167
|
+
operator: convex_values0.VUnion<"eq" | "ne" | "lt" | "lte" | "gt" | "gte" | "in" | "not_in" | "contains" | "starts_with" | "ends_with" | undefined, [convex_values0.VLiteral<"lt", "required">, convex_values0.VLiteral<"lte", "required">, convex_values0.VLiteral<"gt", "required">, convex_values0.VLiteral<"gte", "required">, convex_values0.VLiteral<"eq", "required">, convex_values0.VLiteral<"in", "required">, convex_values0.VLiteral<"not_in", "required">, convex_values0.VLiteral<"ne", "required">, convex_values0.VLiteral<"contains", "required">, convex_values0.VLiteral<"starts_with", "required">, convex_values0.VLiteral<"ends_with", "required">], "optional", never>;
|
|
168
168
|
value: convex_values0.VUnion<string | number | boolean | string[] | number[] | null, [convex_values0.VString<string, "required">, convex_values0.VFloat64<number, "required">, convex_values0.VBoolean<boolean, "required">, convex_values0.VArray<string[], convex_values0.VString<string, "required">, "required">, convex_values0.VArray<number[], convex_values0.VFloat64<number, "required">, "required">, convex_values0.VNull<null, "required">], "required", never>;
|
|
169
|
-
}, "required", "mode" | "
|
|
170
|
-
}, "required", "
|
|
169
|
+
}, "required", "mode" | "operator" | "value" | "field" | "connector">, "optional">;
|
|
170
|
+
}, "required", "limit" | "where" | "model" | "select" | "offset" | "sortBy" | "sortBy.direction" | "sortBy.field">;
|
|
171
171
|
declare const hasUniqueFields: (betterAuthSchema: BetterAuthDBSchema, model: string, input: Record<string, any>) => boolean;
|
|
172
172
|
declare const checkUniqueFields: <Schema extends SchemaDefinition<any, any>>(ctx: GenericQueryCtx<GenericDataModel>, schema: Schema, betterAuthSchema: BetterAuthDBSchema, table: string, input: Record<string, any>, doc?: Record<string, any>) => Promise<void>;
|
|
173
173
|
declare const selectFields: <T extends TableNamesInDataModel<GenericDataModel>, D extends DocumentByName<GenericDataModel, T>>(doc: D | null, select?: string[]) => D | null;
|