fix: align bound query keys with invalidation prefixes
This commit is contained in:
@@ -1,6 +1,90 @@
|
||||
export const QUERY_REGISTRY: Readonly<Record<string, readonly unknown[]>> =
|
||||
Object.freeze({});
|
||||
|
||||
export const QUERY_KEY_SCHEMA_VERSION = 2 as const;
|
||||
export const QUERY_NAMESPACE_ID_MAX_BYTES = 80 as const;
|
||||
|
||||
export type QueryNamespaceIdentity = Readonly<{
|
||||
namespaceId: string;
|
||||
namespaceVersion: number;
|
||||
}>;
|
||||
|
||||
function hasControlCharacter(value: string): boolean {
|
||||
for (const character of value) {
|
||||
const codePoint = character.codePointAt(0) ?? 0;
|
||||
if (
|
||||
codePoint <= 0x1f ||
|
||||
(codePoint >= 0x7f && codePoint <= 0x9f)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function assertQueryNamespaceIdentity(
|
||||
namespace: QueryNamespaceIdentity,
|
||||
): void {
|
||||
if (
|
||||
!namespace ||
|
||||
typeof namespace !== "object" ||
|
||||
typeof namespace.namespaceId !== "string" ||
|
||||
namespace.namespaceId.length === 0 ||
|
||||
hasControlCharacter(namespace.namespaceId) ||
|
||||
new TextEncoder().encode(namespace.namespaceId).byteLength >
|
||||
QUERY_NAMESPACE_ID_MAX_BYTES ||
|
||||
!Number.isSafeInteger(namespace.namespaceVersion) ||
|
||||
namespace.namespaceVersion < 1
|
||||
) {
|
||||
throw new TypeError("Query namespace identity is invalid.");
|
||||
}
|
||||
}
|
||||
|
||||
export function defineQueryNamespaceIdentity(
|
||||
namespaceId: string,
|
||||
namespaceVersion: number,
|
||||
): QueryNamespaceIdentity {
|
||||
const namespace = { namespaceId, namespaceVersion };
|
||||
assertQueryNamespaceIdentity(namespace);
|
||||
return Object.freeze(namespace);
|
||||
}
|
||||
|
||||
export function queryNamespaceIdentityKey(
|
||||
namespace: QueryNamespaceIdentity,
|
||||
): string {
|
||||
assertQueryNamespaceIdentity(namespace);
|
||||
return JSON.stringify([namespace.namespaceId, namespace.namespaceVersion]);
|
||||
}
|
||||
|
||||
export function createQueryInvalidationPrefix(
|
||||
namespace: QueryNamespaceIdentity,
|
||||
) {
|
||||
assertQueryNamespaceIdentity(namespace);
|
||||
return Object.freeze([
|
||||
"query",
|
||||
QUERY_KEY_SCHEMA_VERSION,
|
||||
namespace.namespaceId,
|
||||
namespace.namespaceVersion,
|
||||
] as const);
|
||||
}
|
||||
|
||||
export function createBoundQueryKey(
|
||||
namespace: QueryNamespaceIdentity,
|
||||
scopeFingerprint: string,
|
||||
definitionVersion: number,
|
||||
identityToken: string,
|
||||
) {
|
||||
if (!Number.isSafeInteger(definitionVersion) || definitionVersion < 1) {
|
||||
throw new TypeError("Query definition version is invalid.");
|
||||
}
|
||||
return Object.freeze([
|
||||
...createQueryInvalidationPrefix(namespace),
|
||||
scopeFingerprint,
|
||||
definitionVersion,
|
||||
identityToken,
|
||||
] as const);
|
||||
}
|
||||
|
||||
export type CanonicalValue =
|
||||
| null
|
||||
| boolean
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import type { Result } from "../application/result.ts";
|
||||
import type { QueryInvalidationTopic } from "./query-invalidation.ts";
|
||||
import type { RuntimeIdentityBinding } from "./query-keys.ts";
|
||||
import {
|
||||
createBoundQueryKey,
|
||||
defineQueryNamespaceIdentity,
|
||||
type RuntimeIdentityBinding,
|
||||
} from "./query-keys.ts";
|
||||
import type { CacheScopeSnapshot } from "./server-state-scope.ts";
|
||||
|
||||
/**
|
||||
@@ -176,20 +180,21 @@ export function bindQuery<Input, Value>(
|
||||
`Query definition requires measureResult: ${definition.definitionId}`,
|
||||
);
|
||||
}
|
||||
const namespace = defineQueryNamespaceIdentity(
|
||||
definition.namespace,
|
||||
definition.namespaceVersion,
|
||||
);
|
||||
const identity = scope.identities.intern(input);
|
||||
return Object.freeze({
|
||||
definitionId: definition.definitionId,
|
||||
// §10.7. Opaque runtime identity only. No raw account/resource ID, URL,
|
||||
// filter object, document or cursor ever enters a query key.
|
||||
queryKey: Object.freeze([
|
||||
"query",
|
||||
1,
|
||||
queryKey: createBoundQueryKey(
|
||||
namespace,
|
||||
scope.fingerprint,
|
||||
definition.namespace,
|
||||
definition.namespaceVersion,
|
||||
definition.definitionVersion,
|
||||
identity.token,
|
||||
]),
|
||||
),
|
||||
profile: getServerStateProfile(definition.profileId),
|
||||
identity,
|
||||
scope,
|
||||
|
||||
Reference in New Issue
Block a user