@lupinum/better-convex-nuxt 0.8.0-beta.40
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/LICENSE +21 -0
- package/LICENSES/Apache-2.0.txt +201 -0
- package/README.md +151 -0
- package/THIRD_PARTY_NOTICES.md +38 -0
- package/dist/module.d.mts +55 -0
- package/dist/module.json +12 -0
- package/dist/module.mjs +309 -0
- package/dist/runtime/auth/auth-identity.d.ts +31 -0
- package/dist/runtime/auth/auth-identity.js +21 -0
- package/dist/runtime/auth/better-auth-browser-adapter.d.ts +33 -0
- package/dist/runtime/auth/better-auth-browser-adapter.js +215 -0
- package/dist/runtime/auth/client-engine-types.d.ts +4 -0
- package/dist/runtime/auth/client-engine-types.js +0 -0
- package/dist/runtime/auth/integrated-client.d.ts +25 -0
- package/dist/runtime/auth/integrated-client.js +132 -0
- package/dist/runtime/auth/operation-tracker.d.ts +7 -0
- package/dist/runtime/auth/operation-tracker.js +14 -0
- package/dist/runtime/auth/session-synchronization.d.ts +23 -0
- package/dist/runtime/auth/session-synchronization.js +156 -0
- package/dist/runtime/auth/token-fetcher.d.ts +66 -0
- package/dist/runtime/auth/token-fetcher.js +131 -0
- package/dist/runtime/auth/validate-auth-client-definition.d.ts +12 -0
- package/dist/runtime/auth/validate-auth-client-definition.js +47 -0
- package/dist/runtime/auth-client/convex-client-plugin.d.ts +31 -0
- package/dist/runtime/auth-client/convex-client-plugin.js +14 -0
- package/dist/runtime/auth-client/definition-types.d.ts +6 -0
- package/dist/runtime/auth-client/definition-types.js +0 -0
- package/dist/runtime/auth-client/empty-definition.d.ts +2 -0
- package/dist/runtime/auth-client/empty-definition.js +2 -0
- package/dist/runtime/auth-client/index.d.ts +71 -0
- package/dist/runtime/auth-client/index.js +3 -0
- package/dist/runtime/cli/auth-schema.d.ts +2 -0
- package/dist/runtime/cli/auth-schema.js +169 -0
- package/dist/runtime/cli/convex.d.ts +11 -0
- package/dist/runtime/cli/convex.js +274 -0
- package/dist/runtime/composables/useConvex.d.ts +4 -0
- package/dist/runtime/composables/useConvex.js +9 -0
- package/dist/runtime/composables/useConvexAction.d.ts +4 -0
- package/dist/runtime/composables/useConvexAction.js +18 -0
- package/dist/runtime/composables/useConvexAttachment.d.ts +9 -0
- package/dist/runtime/composables/useConvexAttachment.js +14 -0
- package/dist/runtime/composables/useConvexAuth.d.ts +10 -0
- package/dist/runtime/composables/useConvexAuth.js +37 -0
- package/dist/runtime/composables/useConvexConfig.d.ts +12 -0
- package/dist/runtime/composables/useConvexConfig.js +7 -0
- package/dist/runtime/composables/useConvexConnectionState.d.ts +33 -0
- package/dist/runtime/composables/useConvexConnectionState.js +24 -0
- package/dist/runtime/composables/useConvexFileUpload.d.ts +169 -0
- package/dist/runtime/composables/useConvexFileUpload.js +198 -0
- package/dist/runtime/composables/useConvexMutation.d.ts +13 -0
- package/dist/runtime/composables/useConvexMutation.js +24 -0
- package/dist/runtime/composables/useConvexPaginatedQuery.d.ts +13 -0
- package/dist/runtime/composables/useConvexPaginatedQuery.js +256 -0
- package/dist/runtime/composables/useConvexQuery.d.ts +19 -0
- package/dist/runtime/composables/useConvexQuery.js +308 -0
- package/dist/runtime/convex-auth/adapter/create-adapter.d.ts +33 -0
- package/dist/runtime/convex-auth/adapter/create-adapter.js +308 -0
- package/dist/runtime/convex-auth/adapter/define-functions.d.ts +160 -0
- package/dist/runtime/convex-auth/adapter/define-functions.js +497 -0
- package/dist/runtime/convex-auth/adapter/generate-schema.d.ts +16 -0
- package/dist/runtime/convex-auth/adapter/generate-schema.js +332 -0
- package/dist/runtime/convex-auth/adapter/metadata.d.ts +44 -0
- package/dist/runtime/convex-auth/adapter/metadata.js +91 -0
- package/dist/runtime/convex-auth/adapter/query.d.ts +32 -0
- package/dist/runtime/convex-auth/adapter/query.js +275 -0
- package/dist/runtime/convex-auth/adapter/relationships.d.ts +20 -0
- package/dist/runtime/convex-auth/adapter/relationships.js +128 -0
- package/dist/runtime/convex-auth/component/_generated/api.js +5 -0
- package/dist/runtime/convex-auth/component/_generated/component.d.ts +116 -0
- package/dist/runtime/convex-auth/component/_generated/component.js +0 -0
- package/dist/runtime/convex-auth/component/_generated/dataModel.js +0 -0
- package/dist/runtime/convex-auth/component/_generated/server.js +16 -0
- package/dist/runtime/convex-auth/component/adapter.d.ts +142 -0
- package/dist/runtime/convex-auth/component/adapter.js +16 -0
- package/dist/runtime/convex-auth/component/convex.config.d.ts +2 -0
- package/dist/runtime/convex-auth/component/convex.config.js +3 -0
- package/dist/runtime/convex-auth/component/schema.d.ts +799 -0
- package/dist/runtime/convex-auth/component/schema.js +180 -0
- package/dist/runtime/convex-auth/component/schemaMetadata.d.ts +2094 -0
- package/dist/runtime/convex-auth/component/schemaMetadata.js +2161 -0
- package/dist/runtime/convex-auth/context.d.ts +5 -0
- package/dist/runtime/convex-auth/context.js +8 -0
- package/dist/runtime/convex-auth/create-auth-component.d.ts +41 -0
- package/dist/runtime/convex-auth/create-auth-component.js +157 -0
- package/dist/runtime/convex-auth/index.d.ts +11 -0
- package/dist/runtime/convex-auth/index.js +7 -0
- package/dist/runtime/convex-auth/internal-session.d.ts +2 -0
- package/dist/runtime/convex-auth/internal-session.js +1 -0
- package/dist/runtime/convex-auth/jwks-rotation.d.ts +58 -0
- package/dist/runtime/convex-auth/jwks-rotation.js +163 -0
- package/dist/runtime/convex-auth/oauth-live-access.d.ts +17 -0
- package/dist/runtime/convex-auth/oauth-live-access.js +78 -0
- package/dist/runtime/convex-auth/oauth-provider-compat.d.ts +10 -0
- package/dist/runtime/convex-auth/oauth-provider-compat.js +170 -0
- package/dist/runtime/convex-auth/oauth-resource.d.ts +39 -0
- package/dist/runtime/convex-auth/oauth-resource.js +118 -0
- package/dist/runtime/convex-auth/oauth-security.d.ts +82 -0
- package/dist/runtime/convex-auth/oauth-security.js +454 -0
- package/dist/runtime/convex-auth/origin.d.ts +4 -0
- package/dist/runtime/convex-auth/origin.js +9 -0
- package/dist/runtime/convex-auth/plugin.d.ts +18 -0
- package/dist/runtime/convex-auth/plugin.js +605 -0
- package/dist/runtime/convex-auth/provider.d.ts +2 -0
- package/dist/runtime/convex-auth/provider.js +11 -0
- package/dist/runtime/convex-auth/test.d.ts +410 -0
- package/dist/runtime/convex-auth/test.js +9 -0
- package/dist/runtime/convex-auth/types.d.ts +18 -0
- package/dist/runtime/convex-auth/types.js +0 -0
- package/dist/runtime/convex-auth/user-projection.d.ts +106 -0
- package/dist/runtime/convex-auth/user-projection.js +128 -0
- package/dist/runtime/devtools/auth-proxy-registry.d.ts +4 -0
- package/dist/runtime/devtools/auth-proxy-registry.js +47 -0
- package/dist/runtime/devtools/bridge-setup.d.ts +21 -0
- package/dist/runtime/devtools/bridge-setup.js +165 -0
- package/dist/runtime/devtools/path-utils.d.ts +2 -0
- package/dist/runtime/devtools/path-utils.js +12 -0
- package/dist/runtime/devtools/server.d.ts +2 -0
- package/dist/runtime/devtools/server.js +108 -0
- package/dist/runtime/devtools/setup-client.d.ts +16 -0
- package/dist/runtime/devtools/setup-client.js +43 -0
- package/dist/runtime/devtools/sink.d.ts +18 -0
- package/dist/runtime/devtools/sink.js +110 -0
- package/dist/runtime/devtools/transport.d.ts +16 -0
- package/dist/runtime/devtools/transport.js +149 -0
- package/dist/runtime/devtools/types.d.ts +167 -0
- package/dist/runtime/devtools/types.js +1 -0
- package/dist/runtime/devtools/ui/dist/200.html +1 -0
- package/dist/runtime/devtools/ui/dist/404.html +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/C4MASuCh.js +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/DJHkqtZq.js +3 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/DWwrCltl.js +2 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/DZ_UiYoH.js +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/_m9ujb-i.js +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/builds/latest.json +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/builds/meta/better-convex-nuxt-devtools.json +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/entry.DpFDKYZ5.css +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/error-404.DYUJo5xt.css +1 -0
- package/dist/runtime/devtools/ui/dist/_nuxt/error-500.DQdoJJXg.css +1 -0
- package/dist/runtime/devtools/ui/dist/convex-logo.svg +5 -0
- package/dist/runtime/devtools/ui/dist/index.html +1 -0
- package/dist/runtime/errors/index.d.ts +7 -0
- package/dist/runtime/errors/index.js +5 -0
- package/dist/runtime/middleware/convex-auth.global.d.ts +2 -0
- package/dist/runtime/middleware/convex-auth.global.js +31 -0
- package/dist/runtime/plugin.auth.client.d.ts +3 -0
- package/dist/runtime/plugin.auth.client.js +217 -0
- package/dist/runtime/plugin.client.d.ts +3 -0
- package/dist/runtime/plugin.client.js +43 -0
- package/dist/runtime/plugin.server.d.ts +12 -0
- package/dist/runtime/plugin.server.js +98 -0
- package/dist/runtime/plugins/convex-call-error-payload.d.ts +14 -0
- package/dist/runtime/plugins/convex-call-error-payload.js +12 -0
- package/dist/runtime/runtime-context.d.ts +26 -0
- package/dist/runtime/runtime-context.js +48 -0
- package/dist/runtime/server/api/auth/[...].d.ts +10 -0
- package/dist/runtime/server/api/auth/[...].js +509 -0
- package/dist/runtime/server/api/auth/authorization-server-metadata.d.ts +2 -0
- package/dist/runtime/server/api/auth/authorization-server-metadata.js +6 -0
- package/dist/runtime/server/api/auth/body-size.d.ts +15 -0
- package/dist/runtime/server/api/auth/body-size.js +179 -0
- package/dist/runtime/server/api/auth/headers.d.ts +6 -0
- package/dist/runtime/server/api/auth/headers.js +98 -0
- package/dist/runtime/server/api/auth/jwks.d.ts +2 -0
- package/dist/runtime/server/api/auth/jwks.js +6 -0
- package/dist/runtime/server/api/auth/security.d.ts +14 -0
- package/dist/runtime/server/api/auth/security.js +85 -0
- package/dist/runtime/server/api/release-fingerprint.d.ts +2 -0
- package/dist/runtime/server/api/release-fingerprint.js +36 -0
- package/dist/runtime/server/index.d.ts +1 -0
- package/dist/runtime/server/index.js +3 -0
- package/dist/runtime/server/plugins/runtime-config.d.ts +7 -0
- package/dist/runtime/server/plugins/runtime-config.js +7 -0
- package/dist/runtime/server/utils/auth-snapshot.d.ts +26 -0
- package/dist/runtime/server/utils/auth-snapshot.js +165 -0
- package/dist/runtime/server/utils/http.d.ts +16 -0
- package/dist/runtime/server/utils/http.js +133 -0
- package/dist/runtime/server/utils/server-convex-caller.d.ts +31 -0
- package/dist/runtime/server/utils/server-convex-caller.js +151 -0
- package/dist/runtime/server/utils/server-convex-options.d.ts +83 -0
- package/dist/runtime/server/utils/server-convex-options.js +69 -0
- package/dist/runtime/server/utils/signed-client-ip.d.ts +6 -0
- package/dist/runtime/server/utils/signed-client-ip.js +29 -0
- package/dist/runtime/server/utils/ssr-auth-headers.d.ts +26 -0
- package/dist/runtime/server/utils/ssr-auth-headers.js +49 -0
- package/dist/runtime/server/utils/token-exchange.d.ts +27 -0
- package/dist/runtime/server/utils/token-exchange.js +96 -0
- package/dist/runtime/shared/auth-cookie.d.ts +4 -0
- package/dist/runtime/shared/auth-cookie.js +25 -0
- package/dist/runtime/shared/auth-origin.d.ts +15 -0
- package/dist/runtime/shared/auth-origin.js +42 -0
- package/dist/runtime/shared/bounded-stream.d.ts +2 -0
- package/dist/runtime/shared/bounded-stream.js +50 -0
- package/dist/runtime/shared/client-ip.d.ts +21 -0
- package/dist/runtime/shared/client-ip.js +123 -0
- package/dist/runtime/shared/release-fingerprint.d.ts +5 -0
- package/dist/runtime/shared/release-fingerprint.js +17 -0
- package/dist/runtime/types.d.ts +19 -0
- package/dist/runtime/utils/auth-config.d.ts +24 -0
- package/dist/runtime/utils/auth-config.js +45 -0
- package/dist/runtime/utils/auth-contract.d.ts +22 -0
- package/dist/runtime/utils/auth-contract.js +0 -0
- package/dist/runtime/utils/auth-errors.d.ts +5 -0
- package/dist/runtime/utils/auth-errors.js +22 -0
- package/dist/runtime/utils/auth-identity-state.d.ts +4 -0
- package/dist/runtime/utils/auth-identity-state.js +8 -0
- package/dist/runtime/utils/auth-pending-state.d.ts +13 -0
- package/dist/runtime/utils/auth-pending-state.js +4 -0
- package/dist/runtime/utils/auth-pending.d.ts +10 -0
- package/dist/runtime/utils/auth-pending.js +30 -0
- package/dist/runtime/utils/auth-route-protection.d.ts +16 -0
- package/dist/runtime/utils/auth-route-protection.js +58 -0
- package/dist/runtime/utils/auth-status.d.ts +47 -0
- package/dist/runtime/utils/auth-status.js +8 -0
- package/dist/runtime/utils/bounded-convex-fetch.d.ts +18 -0
- package/dist/runtime/utils/bounded-convex-fetch.js +152 -0
- package/dist/runtime/utils/callable-devtools.d.ts +17 -0
- package/dist/runtime/utils/callable-devtools.js +49 -0
- package/dist/runtime/utils/callable-observer.d.ts +2 -0
- package/dist/runtime/utils/callable-observer.js +1 -0
- package/dist/runtime/utils/composable-scope.d.ts +2 -0
- package/dist/runtime/utils/composable-scope.js +8 -0
- package/dist/runtime/utils/config-defaults.d.ts +8 -0
- package/dist/runtime/utils/config-defaults.js +8 -0
- package/dist/runtime/utils/convex-cache.d.ts +68 -0
- package/dist/runtime/utils/convex-cache.js +47 -0
- package/dist/runtime/utils/convex-config.d.ts +16 -0
- package/dist/runtime/utils/convex-config.js +50 -0
- package/dist/runtime/utils/convex-shared.d.ts +67 -0
- package/dist/runtime/utils/convex-shared.js +134 -0
- package/dist/runtime/utils/identity-changed-error.d.ts +4 -0
- package/dist/runtime/utils/identity-changed-error.js +12 -0
- package/dist/runtime/utils/identity-key.d.ts +19 -0
- package/dist/runtime/utils/identity-key.js +10 -0
- package/dist/runtime/utils/integrated-auth-client.d.ts +5 -0
- package/dist/runtime/utils/integrated-auth-client.js +0 -0
- package/dist/runtime/utils/logger.d.ts +42 -0
- package/dist/runtime/utils/logger.js +190 -0
- package/dist/runtime/utils/mime-type.d.ts +33 -0
- package/dist/runtime/utils/mime-type.js +10 -0
- package/dist/runtime/utils/query-execution-gate.d.ts +47 -0
- package/dist/runtime/utils/query-execution-gate.js +63 -0
- package/dist/runtime/utils/query-execution.d.ts +8 -0
- package/dist/runtime/utils/query-execution.js +19 -0
- package/dist/runtime/utils/query-foundation.d.ts +20 -0
- package/dist/runtime/utils/query-foundation.js +39 -0
- package/dist/runtime/utils/query-state.d.ts +12 -0
- package/dist/runtime/utils/query-state.js +12 -0
- package/dist/runtime/utils/reactive-args.d.ts +5 -0
- package/dist/runtime/utils/reactive-args.js +42 -0
- package/dist/runtime/utils/runtime-config-normalize.d.ts +22 -0
- package/dist/runtime/utils/runtime-config-normalize.js +28 -0
- package/dist/runtime/utils/runtime-config.d.ts +4 -0
- package/dist/runtime/utils/runtime-config.js +8 -0
- package/dist/runtime/utils/sanitize-diagnostic.d.ts +6 -0
- package/dist/runtime/utils/sanitize-diagnostic.js +81 -0
- package/dist/runtime/utils/shared-helpers.d.ts +46 -0
- package/dist/runtime/utils/shared-helpers.js +139 -0
- package/dist/runtime/utils/site-url.d.ts +4 -0
- package/dist/runtime/utils/site-url.js +15 -0
- package/dist/runtime/utils/ssr-pagination-state.d.ts +3 -0
- package/dist/runtime/utils/ssr-pagination-state.js +3 -0
- package/dist/runtime/utils/types.d.ts +29 -0
- package/dist/runtime/utils/types.js +0 -0
- package/dist/runtime/utils/upload-core.d.ts +16 -0
- package/dist/runtime/utils/upload-core.js +116 -0
- package/dist/types.d.mts +21 -0
- package/package.json +253 -0
- package/security/upstream-convex-better-auth.json +477 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { hash } from "ohash";
|
|
2
|
+
const functionNameSymbol = Symbol.for("functionName");
|
|
3
|
+
const MAX_LOCAL_JWT_LENGTH = 65536;
|
|
4
|
+
const MAX_DISPLAY_STRING_LENGTH = 4096;
|
|
5
|
+
export const TOKEN_EXPIRY_SAFETY_BUFFER_MS = 3e4;
|
|
6
|
+
function isPlainRecord(value) {
|
|
7
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
8
|
+
const prototype = Object.getPrototypeOf(value);
|
|
9
|
+
return prototype === Object.prototype || prototype === null;
|
|
10
|
+
}
|
|
11
|
+
function defineSafeProperty(target, key, value) {
|
|
12
|
+
Object.defineProperty(target, key, {
|
|
13
|
+
value,
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: true,
|
|
16
|
+
writable: true
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function readDisplayString(value) {
|
|
20
|
+
if (value === null) return null;
|
|
21
|
+
return typeof value === "string" && value.length <= MAX_DISPLAY_STRING_LENGTH ? value : void 0;
|
|
22
|
+
}
|
|
23
|
+
function copyNormalizedDisplayFields(target, input) {
|
|
24
|
+
for (const key of ["name", "email", "image", "createdAt", "updatedAt"]) {
|
|
25
|
+
const value = readDisplayString(input[key]);
|
|
26
|
+
if (value !== void 0) defineSafeProperty(target, key, value);
|
|
27
|
+
}
|
|
28
|
+
if (typeof input.emailVerified === "boolean") {
|
|
29
|
+
defineSafeProperty(target, "emailVerified", input.emailVerified);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function base64UrlDecode(str) {
|
|
33
|
+
if (str.length % 4 === 1) throw new Error("Invalid base64url length");
|
|
34
|
+
let base64 = str.replace(/-/g, "+").replace(/_/g, "/");
|
|
35
|
+
const padding = base64.length % 4;
|
|
36
|
+
if (padding > 0) {
|
|
37
|
+
base64 += "=".repeat(4 - padding);
|
|
38
|
+
}
|
|
39
|
+
let bytes;
|
|
40
|
+
if (typeof Buffer !== "undefined") {
|
|
41
|
+
bytes = Buffer.from(base64, "base64");
|
|
42
|
+
} else {
|
|
43
|
+
const binaryString = atob(base64);
|
|
44
|
+
bytes = new Uint8Array(binaryString.length);
|
|
45
|
+
for (let i = 0; i < binaryString.length; i++) {
|
|
46
|
+
bytes[i] = binaryString.charCodeAt(i);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
let canonical;
|
|
50
|
+
if (typeof Buffer !== "undefined") {
|
|
51
|
+
canonical = Buffer.from(bytes).toString("base64url");
|
|
52
|
+
} else {
|
|
53
|
+
let binaryString = "";
|
|
54
|
+
for (const byte of bytes) binaryString += String.fromCharCode(byte);
|
|
55
|
+
canonical = btoa(binaryString).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
56
|
+
}
|
|
57
|
+
if (canonical !== str) throw new Error("Non-canonical base64url encoding");
|
|
58
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
59
|
+
}
|
|
60
|
+
function isBase64UrlSegment(value) {
|
|
61
|
+
return value.length > 0 && /^[\w-]+$/.test(value);
|
|
62
|
+
}
|
|
63
|
+
export function decodeJwtPayload(token) {
|
|
64
|
+
try {
|
|
65
|
+
if (token.length === 0 || token.length > MAX_LOCAL_JWT_LENGTH) return null;
|
|
66
|
+
const parts = token.split(".");
|
|
67
|
+
if (parts.length !== 3) return null;
|
|
68
|
+
const [header, payload, signature] = parts;
|
|
69
|
+
if (!header || !payload || !signature || !isBase64UrlSegment(header) || !isBase64UrlSegment(payload) || !isBase64UrlSegment(signature)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const decoded = base64UrlDecode(payload);
|
|
73
|
+
const parsed = JSON.parse(decoded);
|
|
74
|
+
return isPlainRecord(parsed) ? parsed : null;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function getJwtTimeUntilExpiryMs(token, nowMs = Date.now()) {
|
|
80
|
+
const payload = decodeJwtPayload(token);
|
|
81
|
+
if (!payload) return null;
|
|
82
|
+
const exp = payload.exp;
|
|
83
|
+
if (typeof exp !== "number" || !Number.isFinite(exp)) return null;
|
|
84
|
+
const expiryMs = exp * 1e3;
|
|
85
|
+
if (!Number.isFinite(expiryMs)) return null;
|
|
86
|
+
return expiryMs - nowMs;
|
|
87
|
+
}
|
|
88
|
+
export function isJwtUsable(token, nowMs = Date.now()) {
|
|
89
|
+
if (!token) return false;
|
|
90
|
+
const timeUntilExpiry = getJwtTimeUntilExpiryMs(token, nowMs);
|
|
91
|
+
return timeUntilExpiry !== null && timeUntilExpiry > TOKEN_EXPIRY_SAFETY_BUFFER_MS;
|
|
92
|
+
}
|
|
93
|
+
export function decodeUserFromJwt(token) {
|
|
94
|
+
const payload = decodeJwtPayload(token);
|
|
95
|
+
if (!payload) return null;
|
|
96
|
+
const subject = payload.sub;
|
|
97
|
+
if (typeof subject !== "string" || subject.length === 0 || subject.length > MAX_DISPLAY_STRING_LENGTH) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
const user = { id: subject };
|
|
101
|
+
copyNormalizedDisplayFields(user, payload);
|
|
102
|
+
return user;
|
|
103
|
+
}
|
|
104
|
+
export function computeQueryStatus(isSkipped, hasError, isPending, hasData) {
|
|
105
|
+
if (isSkipped) return "idle";
|
|
106
|
+
if (hasError) return "error";
|
|
107
|
+
if (isPending && !hasData) return "pending";
|
|
108
|
+
return "success";
|
|
109
|
+
}
|
|
110
|
+
export function getFunctionName(fn) {
|
|
111
|
+
if (!fn) return "unknown";
|
|
112
|
+
try {
|
|
113
|
+
const value = fn;
|
|
114
|
+
if (typeof value === "string") return value;
|
|
115
|
+
if (typeof value !== "object" && typeof value !== "function" || value === null) {
|
|
116
|
+
return "unknown";
|
|
117
|
+
}
|
|
118
|
+
const functionRef = value;
|
|
119
|
+
const symbolName = functionRef[functionNameSymbol];
|
|
120
|
+
if (typeof symbolName === "string") return symbolName;
|
|
121
|
+
if (typeof functionRef._path === "string") return functionRef._path;
|
|
122
|
+
if (typeof functionRef.functionPath === "string") return functionRef.functionPath;
|
|
123
|
+
return "unknown";
|
|
124
|
+
} catch {
|
|
125
|
+
return "unknown";
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export function hashArgs(args) {
|
|
129
|
+
return hash(args ?? {});
|
|
130
|
+
}
|
|
131
|
+
export function createConvexQueryKey(query, args, namespace = "convex") {
|
|
132
|
+
const fnName = getFunctionName(query);
|
|
133
|
+
return `${namespace}:${fnName}:${hashArgs(args)}`;
|
|
134
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ConvexCallError } from '../errors/index.js';
|
|
2
|
+
export declare const IDENTITY_CHANGED: "IDENTITY_CHANGED";
|
|
3
|
+
export declare function createIdentityChangedError(operation?: string): ConvexCallError;
|
|
4
|
+
export declare function isIdentityChangedError(error: unknown): boolean;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { ConvexCallError } from "../errors/index.js";
|
|
2
|
+
export const IDENTITY_CHANGED = "IDENTITY_CHANGED";
|
|
3
|
+
export function createIdentityChangedError(operation) {
|
|
4
|
+
return new ConvexCallError({
|
|
5
|
+
kind: "authentication",
|
|
6
|
+
code: IDENTITY_CHANGED,
|
|
7
|
+
message: operation ? `Convex ${operation} rejected: the auth identity changed before it settled (${IDENTITY_CHANGED}).` : `Convex operation rejected: the auth identity changed (${IDENTITY_CHANGED}).`
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
export function isIdentityChangedError(error) {
|
|
11
|
+
return typeof error === "object" && error !== null && error.code === IDENTITY_CHANGED;
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ConvexUser } from './types.js';
|
|
2
|
+
export type ConvexIdentityKey = 'anonymous' | `user:${string}`;
|
|
3
|
+
export declare function isAuthenticatedIdentityKey(key: ConvexIdentityKey | null): key is `user:${string}`;
|
|
4
|
+
/**
|
|
5
|
+
* The single stable-user-ID extraction function.
|
|
6
|
+
*
|
|
7
|
+
* `'anonymous'` covers every unauthenticated identity; `user:${string}` uses the
|
|
8
|
+
* Better Auth `user.id` at this Nuxt adapter boundary. It is never a JWT, token
|
|
9
|
+
* hash, role, or permission.
|
|
10
|
+
*
|
|
11
|
+
* Use this everywhere an identity-varying holder is keyed: SSR snapshots, client
|
|
12
|
+
* auth, cache keys, payload keys, subscription keys, and identity generation.
|
|
13
|
+
* There is deliberately exactly one implementation .
|
|
14
|
+
*
|
|
15
|
+
* @throws TypeError when a user is present but has no non-empty string `id`.
|
|
16
|
+
* A token without a resolved user id is not a settled identity and must keep
|
|
17
|
+
* auth-gated queries waiting; it must never produce `user:undefined`.
|
|
18
|
+
*/
|
|
19
|
+
export declare function getConvexIdentityKey(user: ConvexUser | null): ConvexIdentityKey;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function isAuthenticatedIdentityKey(key) {
|
|
2
|
+
return typeof key === "string" && key.startsWith("user:");
|
|
3
|
+
}
|
|
4
|
+
export function getConvexIdentityKey(user) {
|
|
5
|
+
if (!user) return "anonymous";
|
|
6
|
+
if (typeof user.id !== "string" || user.id.length === 0) {
|
|
7
|
+
throw new TypeError("Authenticated Convex user is missing a stable Better Auth user id");
|
|
8
|
+
}
|
|
9
|
+
return `user:${user.id}`;
|
|
10
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic logger for better-convex-nuxt
|
|
3
|
+
*
|
|
4
|
+
* Levels:
|
|
5
|
+
* - false: No logs (production default)
|
|
6
|
+
* - 'info': Auth and upload events
|
|
7
|
+
* - 'debug': Auth/uploads plus timing and debug messages
|
|
8
|
+
*
|
|
9
|
+
* Output:
|
|
10
|
+
* - Server: ANSI-styled single-line logs for easy grep/scanning
|
|
11
|
+
* - Browser: CSS badges + collapsed groups for clean dev tools
|
|
12
|
+
*/
|
|
13
|
+
export type LogLevel = false | 'info' | 'debug';
|
|
14
|
+
export interface AuthEvent {
|
|
15
|
+
phase: string;
|
|
16
|
+
outcome: 'success' | 'error' | 'skip' | 'miss';
|
|
17
|
+
details?: Record<string, unknown>;
|
|
18
|
+
error?: unknown;
|
|
19
|
+
}
|
|
20
|
+
export interface UploadEvent {
|
|
21
|
+
name: string;
|
|
22
|
+
event: 'success' | 'error';
|
|
23
|
+
filename?: string;
|
|
24
|
+
size?: number;
|
|
25
|
+
duration?: number;
|
|
26
|
+
error?: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface Logger {
|
|
29
|
+
auth(event: AuthEvent): void;
|
|
30
|
+
upload(event: UploadEvent): void;
|
|
31
|
+
debug(message: string, data?: unknown): void;
|
|
32
|
+
time(label: string): () => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Create a logger with the specified level.
|
|
36
|
+
* Automatically selects ANSI (server) or CSS (browser) styling.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createLogger(level: LogLevel): Logger;
|
|
39
|
+
/**
|
|
40
|
+
* Get log level from runtime config.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getLogLevel(config: unknown): LogLevel;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { sanitizeDiagnosticValue } from "./sanitize-diagnostic.js";
|
|
2
|
+
const noopLogger = Object.freeze({
|
|
3
|
+
auth: () => {
|
|
4
|
+
},
|
|
5
|
+
upload: () => {
|
|
6
|
+
},
|
|
7
|
+
debug: () => {
|
|
8
|
+
},
|
|
9
|
+
time: () => () => {
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
const ANSI = {
|
|
13
|
+
reset: "\x1B[0m",
|
|
14
|
+
dim: "\x1B[2m",
|
|
15
|
+
cyan: "\x1B[36m",
|
|
16
|
+
green: "\x1B[32m",
|
|
17
|
+
yellow: "\x1B[33m",
|
|
18
|
+
red: "\x1B[31m"
|
|
19
|
+
};
|
|
20
|
+
const ICONS = {
|
|
21
|
+
success: "\u2714",
|
|
22
|
+
error: "\u2716",
|
|
23
|
+
warning: "\u26A0",
|
|
24
|
+
upload: "\u{1F4E4}"
|
|
25
|
+
};
|
|
26
|
+
const CSS = {
|
|
27
|
+
badge: "background: #6366f1; color: white; padding: 2px 6px; border-radius: 3px; font-weight: bold;",
|
|
28
|
+
success: "color: #22c55e; font-weight: bold;",
|
|
29
|
+
error: "color: #ef4444; font-weight: bold;",
|
|
30
|
+
warning: "color: #f59e0b; font-weight: bold;",
|
|
31
|
+
dim: "color: #9ca3af;",
|
|
32
|
+
name: "color: #3b82f6; font-weight: bold;"
|
|
33
|
+
};
|
|
34
|
+
function formatBytes(bytes) {
|
|
35
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
36
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`;
|
|
37
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
|
|
38
|
+
}
|
|
39
|
+
function formatDuration(ms) {
|
|
40
|
+
return `${ms}ms`;
|
|
41
|
+
}
|
|
42
|
+
function formatDetails(details) {
|
|
43
|
+
return Object.entries(details).map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`).join(" ");
|
|
44
|
+
}
|
|
45
|
+
function createServerLogger(level) {
|
|
46
|
+
const isDebug = level === "debug";
|
|
47
|
+
const PREFIX = `${ANSI.cyan}[convex]${ANSI.reset}`;
|
|
48
|
+
return {
|
|
49
|
+
auth(event) {
|
|
50
|
+
const icon = event.outcome === "success" ? `${ANSI.green}${ICONS.success}${ANSI.reset}` : event.outcome === "error" ? `${ANSI.red}${ICONS.error}${ANSI.reset}` : `${ANSI.yellow}${ICONS.warning}${ANSI.reset}`;
|
|
51
|
+
let msg = `${PREFIX} ${icon} Auth:${event.phase}`;
|
|
52
|
+
if (event.details) {
|
|
53
|
+
msg += ` ${ANSI.dim}${formatDetails(event.details)}${ANSI.reset}`;
|
|
54
|
+
}
|
|
55
|
+
if (event.error) {
|
|
56
|
+
console.error(msg, "[Omitted]");
|
|
57
|
+
} else {
|
|
58
|
+
console.log(msg);
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
upload(event) {
|
|
62
|
+
const icon = event.event === "error" ? `${ANSI.red}${ICONS.error}${ANSI.reset}` : `${ANSI.green}${ICONS.upload}${ANSI.reset}`;
|
|
63
|
+
let msg = `${PREFIX} ${icon} ${event.name}`;
|
|
64
|
+
if (event.event === "success") {
|
|
65
|
+
msg += ` ${ANSI.green}success${ANSI.reset}`;
|
|
66
|
+
if (event.filename) {
|
|
67
|
+
msg += ` ${ANSI.dim}${event.filename}${ANSI.reset}`;
|
|
68
|
+
}
|
|
69
|
+
if (event.size !== void 0) {
|
|
70
|
+
msg += ` ${ANSI.dim}(${formatBytes(event.size)})${ANSI.reset}`;
|
|
71
|
+
}
|
|
72
|
+
if (event.duration !== void 0) {
|
|
73
|
+
msg += ` ${ANSI.dim}${formatDuration(event.duration)}${ANSI.reset}`;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
msg += ` ${ANSI.red}error${ANSI.reset}`;
|
|
77
|
+
if (event.error) msg += ` ${ANSI.red}[Omitted]${ANSI.reset}`;
|
|
78
|
+
}
|
|
79
|
+
console.log(msg);
|
|
80
|
+
},
|
|
81
|
+
debug(message, data) {
|
|
82
|
+
if (!isDebug) return;
|
|
83
|
+
void data;
|
|
84
|
+
console.log(`${PREFIX} ${ANSI.dim}[debug]${ANSI.reset} ${message}`);
|
|
85
|
+
},
|
|
86
|
+
time(label) {
|
|
87
|
+
if (!isDebug) return () => {
|
|
88
|
+
};
|
|
89
|
+
const start = performance.now();
|
|
90
|
+
return () => {
|
|
91
|
+
const elapsed = Math.round(performance.now() - start);
|
|
92
|
+
console.log(`${PREFIX} ${ANSI.dim}[time]${ANSI.reset} ${label}: ${elapsed}ms`);
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function createBrowserLogger(level) {
|
|
98
|
+
const isDebug = level === "debug";
|
|
99
|
+
return {
|
|
100
|
+
auth(event) {
|
|
101
|
+
const icon = event.outcome === "success" ? ICONS.success : event.outcome === "error" ? ICONS.error : ICONS.warning;
|
|
102
|
+
const iconStyle = event.outcome === "success" ? CSS.success : event.outcome === "error" ? CSS.error : CSS.warning;
|
|
103
|
+
const hasDetails = event.details && Object.keys(event.details).length > 0;
|
|
104
|
+
if (hasDetails || event.error) {
|
|
105
|
+
console.groupCollapsed(
|
|
106
|
+
`%cConvex%c Auth | %c${icon} ${event.phase}`,
|
|
107
|
+
CSS.badge,
|
|
108
|
+
"",
|
|
109
|
+
iconStyle
|
|
110
|
+
);
|
|
111
|
+
if (event.details) console.log(event.details);
|
|
112
|
+
if (event.error) console.error("[Omitted]");
|
|
113
|
+
console.groupEnd();
|
|
114
|
+
} else {
|
|
115
|
+
console.log(`%cConvex%c Auth | %c${icon} ${event.phase}`, CSS.badge, "", iconStyle);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
upload(event) {
|
|
119
|
+
const icon = event.event === "error" ? ICONS.error : ICONS.upload;
|
|
120
|
+
let label = `%cConvex%c ${icon} %c${event.name}`;
|
|
121
|
+
const styles = [CSS.badge, "", CSS.name];
|
|
122
|
+
if (event.event === "success") {
|
|
123
|
+
if (event.filename) {
|
|
124
|
+
label += `%c ${event.filename}`;
|
|
125
|
+
styles.push(CSS.dim);
|
|
126
|
+
}
|
|
127
|
+
if (event.size !== void 0) {
|
|
128
|
+
label += `%c (${formatBytes(event.size)})`;
|
|
129
|
+
styles.push(CSS.dim);
|
|
130
|
+
}
|
|
131
|
+
if (event.duration !== void 0) {
|
|
132
|
+
label += `%c ${formatDuration(event.duration)}`;
|
|
133
|
+
styles.push(CSS.dim);
|
|
134
|
+
}
|
|
135
|
+
} else {
|
|
136
|
+
label += `%c Failed`;
|
|
137
|
+
styles.push(CSS.error);
|
|
138
|
+
}
|
|
139
|
+
if (event.error) {
|
|
140
|
+
console.groupCollapsed(label, ...styles);
|
|
141
|
+
console.error("Error:", "[Omitted]");
|
|
142
|
+
console.groupEnd();
|
|
143
|
+
} else {
|
|
144
|
+
console.log(label, ...styles);
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
debug(message, data) {
|
|
148
|
+
if (!isDebug) return;
|
|
149
|
+
void data;
|
|
150
|
+
console.log(`%cConvex%c [debug] ${message}`, CSS.badge, CSS.dim);
|
|
151
|
+
},
|
|
152
|
+
time(label) {
|
|
153
|
+
if (!isDebug) return () => {
|
|
154
|
+
};
|
|
155
|
+
const start = performance.now();
|
|
156
|
+
return () => {
|
|
157
|
+
const elapsed = Math.round(performance.now() - start);
|
|
158
|
+
console.log(`%cConvex%c [time] ${label}: ${elapsed}ms`, CSS.badge, CSS.dim);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
export function createLogger(level) {
|
|
164
|
+
if (!level) return noopLogger;
|
|
165
|
+
const sink = !isBrowserRuntime() ? createServerLogger(level) : createBrowserLogger(level);
|
|
166
|
+
const sanitizeEvent = (event) => ({
|
|
167
|
+
...event,
|
|
168
|
+
...event.phase === void 0 ? {} : { phase: String(sanitizeDiagnosticValue(event.phase)) },
|
|
169
|
+
...event.name === void 0 ? {} : { name: String(sanitizeDiagnosticValue(event.name)) },
|
|
170
|
+
...event.filename === void 0 ? {} : { filename: String(sanitizeDiagnosticValue(event.filename)) },
|
|
171
|
+
...event.details === void 0 ? {} : { details: sanitizeDiagnosticValue(event.details) },
|
|
172
|
+
// Error objects/messages often contain transport data and credentials.
|
|
173
|
+
// Callers must put a stable, reviewed code in `details` instead.
|
|
174
|
+
...event.error === void 0 ? {} : { error: "[Omitted]" }
|
|
175
|
+
});
|
|
176
|
+
const safeLogger = {
|
|
177
|
+
auth: (event) => sink.auth(sanitizeEvent(event)),
|
|
178
|
+
upload: (event) => sink.upload(sanitizeEvent(event)),
|
|
179
|
+
debug: (message, data) => sink.debug(String(sanitizeDiagnosticValue(message)), data),
|
|
180
|
+
time: (label) => sink.time(String(sanitizeDiagnosticValue(label)))
|
|
181
|
+
};
|
|
182
|
+
return Object.freeze(safeLogger);
|
|
183
|
+
}
|
|
184
|
+
function isBrowserRuntime() {
|
|
185
|
+
return typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
186
|
+
}
|
|
187
|
+
export function getLogLevel(config) {
|
|
188
|
+
const logging = config && typeof config === "object" && "logging" in config ? config.logging : void 0;
|
|
189
|
+
return logging ?? false;
|
|
190
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MIME type matching utilities for file validation.
|
|
3
|
+
* Used by useConvexFileUpload for client-side file type validation.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Check if a file type matches an allowed type pattern.
|
|
7
|
+
* Supports exact matches and wildcards (e.g., 'image/*').
|
|
8
|
+
*
|
|
9
|
+
* @param fileType - The MIME type of the file (e.g., 'image/jpeg')
|
|
10
|
+
* @param pattern - The pattern to match against (e.g., 'image/*' or 'image/jpeg')
|
|
11
|
+
* @returns true if the file type matches the pattern
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* matchesMimeType('image/jpeg', 'image/jpeg') // true (exact match)
|
|
15
|
+
* matchesMimeType('image/jpeg', 'image/*') // true (wildcard)
|
|
16
|
+
* matchesMimeType('image/jpeg', 'video/*') // false
|
|
17
|
+
* matchesMimeType('video/mp4', 'video/*') // true
|
|
18
|
+
*/
|
|
19
|
+
export declare function matchesMimeType(fileType: string, pattern: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Check if a file type is allowed by any of the allowed type patterns.
|
|
22
|
+
*
|
|
23
|
+
* @param fileType - The MIME type of the file (e.g., 'image/jpeg')
|
|
24
|
+
* @param allowedTypes - Array of allowed patterns (supports wildcards)
|
|
25
|
+
* @returns true if the file type matches any of the allowed patterns
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* isFileTypeAllowed('image/jpeg', ['image/jpeg', 'image/png']) // true
|
|
29
|
+
* isFileTypeAllowed('image/gif', ['image/*']) // true
|
|
30
|
+
* isFileTypeAllowed('video/mp4', ['image/*']) // false
|
|
31
|
+
* isFileTypeAllowed('application/pdf', ['image/*', 'application/pdf']) // true
|
|
32
|
+
*/
|
|
33
|
+
export declare function isFileTypeAllowed(fileType: string, allowedTypes: readonly string[]): boolean;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function matchesMimeType(fileType, pattern) {
|
|
2
|
+
if (pattern.endsWith("/*")) {
|
|
3
|
+
const category = pattern.slice(0, -2);
|
|
4
|
+
return fileType.startsWith(`${category}/`);
|
|
5
|
+
}
|
|
6
|
+
return fileType === pattern;
|
|
7
|
+
}
|
|
8
|
+
export function isFileTypeAllowed(fileType, allowedTypes) {
|
|
9
|
+
return allowedTypes.some((pattern) => matchesMimeType(fileType, pattern));
|
|
10
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { ConvexAuthMode, ConvexQueryAuthStatus } from './auth-status.js';
|
|
2
|
+
import { type ConvexIdentityKey } from './identity-key.js';
|
|
3
|
+
/**
|
|
4
|
+
* Canonical query execution gate ("Required execution-gate behavior").
|
|
5
|
+
*
|
|
6
|
+
* The gate is driven by the canonical auth status and the stable identity key
|
|
7
|
+
* published by the frozen {@link ClientIdentityPort} adapter — never by raw engine
|
|
8
|
+
* state and never by `isPending`. Background auth work must not idle an already
|
|
9
|
+
* usable identity, so `isPending` is deliberately absent from the input.
|
|
10
|
+
*
|
|
11
|
+
* Decision order (each step returns immediately):
|
|
12
|
+
* 1. Explicit `'skip'` resolves idle.
|
|
13
|
+
* 2. `none` executes without waiting and uses the `anonymous` cache dimension.
|
|
14
|
+
* 3. `disabled`: `required` resolves idle; `optional` executes anonymously
|
|
15
|
+
* without waiting.
|
|
16
|
+
* 4. `loading`: `required` and `optional` wait for initial auth settlement.
|
|
17
|
+
* 5. `error`: `required` and `optional` surface the auth error without a
|
|
18
|
+
* network request. They do not silently downgrade to anonymous.
|
|
19
|
+
* 6. `anonymous`: `required` resolves idle; `optional` executes anonymously.
|
|
20
|
+
* 7. `authenticated`: `required` and `optional` require a non-null matching
|
|
21
|
+
* `user:<id>` key and execute with that identity.
|
|
22
|
+
*/
|
|
23
|
+
export interface QueryExecutionGateInput {
|
|
24
|
+
authStatus: ConvexQueryAuthStatus;
|
|
25
|
+
authMode: ConvexAuthMode;
|
|
26
|
+
identityKey: ConvexIdentityKey | null;
|
|
27
|
+
skipped: boolean;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The terminal query execution decision:
|
|
31
|
+
* - `execute` — the caller may issue its configured network request;
|
|
32
|
+
* - `idle` — resolve idle with no request and no error;
|
|
33
|
+
* - `wait` — wait for initial auth settlement, then re-evaluate;
|
|
34
|
+
* - `error` — surface the settled auth error with no request.
|
|
35
|
+
*/
|
|
36
|
+
export type QueryExecutionOutcome = 'execute' | 'idle' | 'wait' | 'error';
|
|
37
|
+
export interface QueryExecutionGate {
|
|
38
|
+
outcome: QueryExecutionOutcome;
|
|
39
|
+
/** The identity dimension for the cache / payload / subscription key. */
|
|
40
|
+
cacheIdentity: ConvexIdentityKey;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Pure gate. No side effects, no reactivity, no client access — it maps the
|
|
44
|
+
* canonical status + mode + identity key to a terminal decision so the same
|
|
45
|
+
* matrix is trivially unit-testable across all status/mode combinations.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createQueryExecutionGate(input: QueryExecutionGateInput): QueryExecutionGate;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { isAuthenticatedIdentityKey } from "./identity-key.js";
|
|
2
|
+
const IDLE = {
|
|
3
|
+
outcome: "idle"
|
|
4
|
+
};
|
|
5
|
+
export function createQueryExecutionGate(input) {
|
|
6
|
+
const { authStatus, authMode, identityKey, skipped } = input;
|
|
7
|
+
if (skipped) {
|
|
8
|
+
return {
|
|
9
|
+
...IDLE,
|
|
10
|
+
cacheIdentity: identityDimension(authMode, identityKey)
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
if (authMode === "none") {
|
|
14
|
+
return {
|
|
15
|
+
outcome: "execute",
|
|
16
|
+
cacheIdentity: "anonymous"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (authStatus === "disabled") {
|
|
20
|
+
if (authMode === "required") {
|
|
21
|
+
return { ...IDLE, cacheIdentity: "anonymous" };
|
|
22
|
+
}
|
|
23
|
+
return executeAnonymously();
|
|
24
|
+
}
|
|
25
|
+
if (authStatus === "loading") {
|
|
26
|
+
return {
|
|
27
|
+
outcome: "wait",
|
|
28
|
+
cacheIdentity: identityDimension(authMode, identityKey)
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
if (authStatus === "error") {
|
|
32
|
+
return {
|
|
33
|
+
outcome: "error",
|
|
34
|
+
cacheIdentity: identityDimension(authMode, identityKey)
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (authStatus === "anonymous") {
|
|
38
|
+
if (authMode === "required") {
|
|
39
|
+
return { ...IDLE, cacheIdentity: "anonymous" };
|
|
40
|
+
}
|
|
41
|
+
return executeAnonymously();
|
|
42
|
+
}
|
|
43
|
+
if (!isAuthenticatedIdentityKey(identityKey)) {
|
|
44
|
+
return {
|
|
45
|
+
outcome: "wait",
|
|
46
|
+
cacheIdentity: "anonymous"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
outcome: "execute",
|
|
51
|
+
cacheIdentity: identityKey
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function executeAnonymously() {
|
|
55
|
+
return {
|
|
56
|
+
outcome: "execute",
|
|
57
|
+
cacheIdentity: "anonymous"
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function identityDimension(authMode, identityKey) {
|
|
61
|
+
if (authMode === "none") return "anonymous";
|
|
62
|
+
return isAuthenticatedIdentityKey(identityKey) ? identityKey : "anonymous";
|
|
63
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Execute one request-scoped SSR query through Convex's official HTTP client.
|
|
3
|
+
* The client owns Convex value encoding, response decoding, and structured
|
|
4
|
+
* application-error reconstruction. The custom fetch owns only request bounds.
|
|
5
|
+
*
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
export declare function executeQueryHttp<T>(convexUrl: string, functionPath: string, args: Record<string, unknown>, authToken?: string, signal?: AbortSignal): Promise<T>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ConvexHttpClient } from "convex/browser";
|
|
2
|
+
import { makeFunctionReference } from "convex/server";
|
|
3
|
+
import { normalizeConvexError } from "../errors/index.js";
|
|
4
|
+
import { createBoundedConvexFetch } from "./bounded-convex-fetch.js";
|
|
5
|
+
export async function executeQueryHttp(convexUrl, functionPath, args, authToken, signal) {
|
|
6
|
+
const client = new ConvexHttpClient(convexUrl, {
|
|
7
|
+
fetch: createBoundedConvexFetch({ signal }),
|
|
8
|
+
logger: false
|
|
9
|
+
});
|
|
10
|
+
if (authToken) client.setAuth(authToken);
|
|
11
|
+
try {
|
|
12
|
+
return await client.query(
|
|
13
|
+
makeFunctionReference(functionPath),
|
|
14
|
+
args
|
|
15
|
+
);
|
|
16
|
+
} catch (error) {
|
|
17
|
+
throw normalizeConvexError(error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ComputedRef } from 'vue';
|
|
2
|
+
import { ConvexCallError } from '../errors/index.js';
|
|
3
|
+
import { type ConvexQueryAuthStatus } from './auth-status.js';
|
|
4
|
+
import type { ConvexIdentityKey } from './identity-key.js';
|
|
5
|
+
/**
|
|
6
|
+
* Reactive canonical auth-identity inputs for query gating and isolation
|
|
7
|
+
* tagging (architecture invariant). This is the single place query composables read auth
|
|
8
|
+
* state; they never touch the auth engine directly.
|
|
9
|
+
*
|
|
10
|
+
* Derived from the SSR-seeded reactive state (`convex:pending` / `convex:identity` /
|
|
11
|
+
* `convex:authError`) so it is correct on both server and client, plus the
|
|
12
|
+
* Query isolation generations are owned by the attached Vue runtime; this Nuxt
|
|
13
|
+
* context only derives the server/client execution gate.
|
|
14
|
+
*/
|
|
15
|
+
export interface ConvexQueryAuthContext {
|
|
16
|
+
readonly status: ComputedRef<ConvexQueryAuthStatus>;
|
|
17
|
+
readonly identityKey: ComputedRef<ConvexIdentityKey | null>;
|
|
18
|
+
readonly error: ComputedRef<ConvexCallError | null>;
|
|
19
|
+
}
|
|
20
|
+
export declare function createConvexQueryAuthContext(): ConvexQueryAuthContext;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { computed } from "vue";
|
|
2
|
+
import { useState } from "#imports";
|
|
3
|
+
import { identityKeyOf } from "../auth/auth-identity.js";
|
|
4
|
+
import { ConvexCallError } from "../errors/index.js";
|
|
5
|
+
import { useConvexIdentityState } from "./auth-identity-state.js";
|
|
6
|
+
import { useConvexAuthPendingState } from "./auth-pending-state.js";
|
|
7
|
+
import { deriveConvexAuthStatus } from "./auth-status.js";
|
|
8
|
+
import { getConvexRuntimeConfig } from "./runtime-config.js";
|
|
9
|
+
export function createConvexQueryAuthContext() {
|
|
10
|
+
const authEnabled = getConvexRuntimeConfig().auth !== false;
|
|
11
|
+
const identity = useConvexIdentityState();
|
|
12
|
+
const pending = useConvexAuthPendingState();
|
|
13
|
+
const authError = useState("convex:authError", () => null);
|
|
14
|
+
const identityKey = computed(
|
|
15
|
+
() => authEnabled ? identityKeyOf(identity.value) : null
|
|
16
|
+
);
|
|
17
|
+
const error = computed(() => {
|
|
18
|
+
if (!authEnabled) return null;
|
|
19
|
+
return authError.value ? new ConvexCallError({
|
|
20
|
+
kind: "authentication",
|
|
21
|
+
message: authError.value
|
|
22
|
+
}) : null;
|
|
23
|
+
});
|
|
24
|
+
const status = computed(() => {
|
|
25
|
+
if (!authEnabled) return "disabled";
|
|
26
|
+
if (pending.value) return "loading";
|
|
27
|
+
return deriveConvexAuthStatus({
|
|
28
|
+
authEnabled: true,
|
|
29
|
+
settled: true,
|
|
30
|
+
identityKey: identityKey.value,
|
|
31
|
+
error: error.value
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
status,
|
|
36
|
+
identityKey,
|
|
37
|
+
error
|
|
38
|
+
};
|
|
39
|
+
}
|