Skip to content

Commit 1abad53

Browse files
committed
feat(package/gqty): Expose aliasGenerator option and default alias generators
1 parent f858e19 commit 1abad53

File tree

22 files changed

+1629
-2486
lines changed

22 files changed

+1629
-2486
lines changed

.changeset/slow-buses-march.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gqty': minor
3+
---
4+
5+
Expose aliasGenerator option and default alias generators

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ public-hoist-pattern[]=rollup*
99
public-hoist-pattern[]=@rollup*
1010
prefer-workspace-packages=true
1111
stream=true
12+
@jsr:registry=https://npm.jsr.io

examples/solid/src/gqty/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33
*/
44

55
import { createSolidClient } from '@gqty/solid';
6-
import { Cache, GQtyError, createClient, type QueryFetcher } from 'gqty';
6+
import {
7+
Cache,
8+
GQtyError,
9+
createClient,
10+
createDebugAliasHasher,
11+
type QueryFetcher,
12+
} from 'gqty';
713
import {
814
generatedSchema,
915
scalarsEnumsHash,
@@ -62,6 +68,7 @@ const cache = new Cache(
6268
);
6369

6470
export const client = createClient<GeneratedSchema>({
71+
aliasGenerator: createDebugAliasHasher(6),
6572
schema: generatedSchema,
6673
scalars: scalarsEnumsHash,
6774
cache,

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,5 @@
4949
"engines": {
5050
"pnpm": "^8.10.0"
5151
},
52-
"packageManager": "[email protected].6+sha256.01c01eeb990e379b31ef19c03e9d06a14afa5250b82e81303f88721c99ff2e6f"
52+
"packageManager": "[email protected].9+sha512.499434c9d8fdd1a2794ebf4552b3b25c0a633abcee5bb15e7b5de90f32f47b513aca98cd5cfd001c31f0db454bc3804edccd578501e4ca293a6816166bbd9f81"
5353
}

packages/gqty/src/Accessor/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ export function createSchemaAccessor<TSchema extends BaseGeneratedSchema>(
3838
return;
3939

4040
const selection =
41-
selectionCache.get(key) ??
42-
Selection.createRoot(key, { aliasLength: context.aliasLength });
41+
selectionCache.get(key) ?? Selection.createRoot(key);
4342

4443
selectionCache.set(key, selection);
4544

packages/gqty/src/Accessor/resolve.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
type GeneratedSchemaObject,
99
type Type,
1010
} from '../Schema';
11-
import type { Selection } from '../Selection';
11+
import type { Selection, SelectionInput } from '../Selection';
1212
import { isPlainObject } from '../Utils';
1313
import type { Meta } from './meta';
1414
import { $meta } from './meta';
@@ -253,15 +253,32 @@ const objectProxyHandler: ProxyHandler<GeneratedSchemaObject> = {
253253

254254
const { __args, __type } = targetType;
255255
if (__args) {
256-
return (args?: Record<string, unknown>) =>
257-
resolve(
256+
return (args?: Record<string, unknown>) => {
257+
const alias = meta.context.aliasGenerator?.(
258+
meta.selection.ancestry.map((s) => s.key.toString()).concat(key),
259+
args
260+
);
261+
const input: SelectionInput = {};
262+
263+
if (args) {
264+
for (const key in args) {
265+
input[key] = {
266+
alias: alias?.input[key],
267+
type: __args[key],
268+
value: args[key],
269+
};
270+
}
271+
}
272+
273+
return resolve(
258274
proxy,
259275
meta.selection.getChild(
260276
key,
261-
args ? { input: { types: __args!, values: args } } : {}
277+
args ? { alias: alias?.field, input } : {}
262278
),
263279
__type
264280
);
281+
};
265282
}
266283

267284
return resolve(proxy, meta.selection.getChild(key), __type);
@@ -468,7 +485,7 @@ const selectIdentityFields = (
468485

469486
// Always __typename except inside interfaces and unions
470487
if (parent?.key !== '$on') {
471-
accessor.__typename;
488+
Reflect.get(accessor, '__typename');
472489
}
473490

474491
const keys = getIdentityFields(meta);
@@ -479,7 +496,7 @@ const selectIdentityFields = (
479496
// Already selected at the common root of this interface/union.
480497
if (isUnion && parent?.parent?.children.has(key)) continue;
481498

482-
accessor[key];
499+
Reflect.get(accessor, key);
483500
}
484501
};
485502

@@ -506,7 +523,7 @@ const arrayProxyHandler: ProxyHandler<CacheObject[]> = {
506523
throw new GQtyError(`Cache data must be an array.`);
507524
}
508525

509-
if (key === 'length') proxy[0];
526+
if (key === 'length') Reflect.get(proxy, 0);
510527

511528
const numKey = +key;
512529
if (!isNaN(numKey) && numKey < data.length) {

packages/gqty/src/Client/alias.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { hash } from '../Utils/hash';
2+
3+
export type AliasGenerator = (
4+
/**
5+
* The chain of selection keys from query root, a unique alias is generated
6+
* based on they provided keys and arguments.
7+
*
8+
* @example ["query", "foo", "bar"]
9+
*/
10+
keys: string[],
11+
12+
/**
13+
* GraphQL arguments related to the current selection, a unique alias is
14+
* generated based on they provided keys and arguments.
15+
*/
16+
args?: Record<string, unknown>
17+
) => SelectionAlias;
18+
19+
export type SelectionAlias = {
20+
/**
21+
* Alias of the current selection field.
22+
*/
23+
field: string;
24+
25+
/**
26+
* Variable name to aliases mapping.
27+
*/
28+
input: Record<string, string | undefined>;
29+
};
30+
31+
export const createAliasHasher =
32+
(maxLength = Infinity): AliasGenerator =>
33+
(keys, args) => {
34+
const field = hash({ key: keys.at(-1), ...args }).slice(0, maxLength);
35+
const input: Record<string, string> = {};
36+
37+
if (args) {
38+
for (const key in args) {
39+
input[key] = hash(`${field}_${key}`).slice(0, maxLength);
40+
}
41+
}
42+
43+
return {
44+
field,
45+
input,
46+
};
47+
};
48+
49+
export const createDebugAliasHasher =
50+
(maxLength = Infinity): AliasGenerator =>
51+
(keys, args) => {
52+
const field = keys
53+
.concat(hash({ key: keys.at(-1), args }).slice(0, maxLength))
54+
.join('_');
55+
const input: Record<string, string> = {};
56+
57+
if (args) {
58+
for (const key in args) {
59+
input[key] = keys.concat(key, field).join('_');
60+
}
61+
}
62+
63+
return {
64+
field,
65+
input,
66+
};
67+
};

packages/gqty/src/Client/compat/selection.ts

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,13 @@ export class LegacySelection {
6565
const isInterfaceUnionSelection = key === '$on';
6666

6767
this.cachePath = isInterfaceUnionSelection
68-
? prevSelection?.cachePath ?? []
68+
? (prevSelection?.cachePath ?? [])
6969
: prevSelection
70-
? [...prevSelection.cachePath, pathKey]
71-
: [pathKey];
70+
? [...prevSelection.cachePath, pathKey]
71+
: [pathKey];
7272

7373
this.pathString = isInterfaceUnionSelection
74-
? prevSelection?.pathString ?? ''
74+
? (prevSelection?.pathString ?? '')
7575
: `${prevSelection?.pathString.concat('.') ?? ''}${pathKey}`;
7676

7777
const prevSelectionsList = prevSelection?.selectionsList ?? [];
@@ -129,21 +129,32 @@ export const convertSelection = (
129129
selectionId = 0,
130130
operationName?: string
131131
): LegacySelection => {
132+
const args: Record<string, unknown> = {};
133+
const argTypes: Record<string, string> = {};
134+
135+
if (selection.input) {
136+
for (const key in selection.input) {
137+
const { type, value } = selection.input[key];
138+
args[key] = value;
139+
argTypes[key] = type;
140+
}
141+
}
142+
132143
return new LegacySelection({
133144
id: ++selectionId,
134145
key: selection.key,
135146
// translate the whole selection chain upwards
136147
prevSelection: selection.parent
137148
? convertSelection(selection.parent, selectionId, operationName)
138149
: undefined,
139-
args: selection.input?.values,
140-
argTypes: selection.input?.types,
150+
args,
151+
argTypes,
141152
type:
142153
selection.root.key === 'query'
143154
? LegacySelectionType.Query
144155
: selection.root.key === 'mutation'
145-
? LegacySelectionType.Mutation
146-
: LegacySelectionType.Subscription,
156+
? LegacySelectionType.Mutation
157+
: LegacySelectionType.Subscription,
147158
operationName,
148159
alias: selection.alias,
149160
unions: selection.isUnion ? [selection.key.toString()] : undefined,

packages/gqty/src/Client/context.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type { Disposable } from '../Disposable';
33
import type { Resetable } from '../Resetable';
44
import type { ScalarsEnumsHash, Schema } from '../Schema';
55
import type { Selectable } from '../Selectable';
6+
import type { AliasGenerator } from './alias';
67

78
export type SchemaContext<
89
T extends Record<string, unknown> = Record<string, unknown>,
@@ -11,7 +12,7 @@ export type SchemaContext<
1112
Resetable &
1213
Selectable & {
1314
cache: Cache;
14-
readonly aliasLength?: number;
15+
readonly aliasGenerator?: AliasGenerator;
1516
readonly cacheOptions?: CacheGetOptions;
1617
readonly depthLimit: number;
1718
readonly scalars: ScalarsEnumsHash;
@@ -26,7 +27,7 @@ export type SchemaContext<
2627
};
2728

2829
export type CreateContextOptions = {
29-
aliasLength?: number;
30+
aliasGenerator?: AliasGenerator;
3031
cache: Cache;
3132
depthLimit: number;
3233
cachePolicy: RequestCache;
@@ -36,7 +37,7 @@ export type CreateContextOptions = {
3637
};
3738

3839
export const createContext = ({
39-
aliasLength,
40+
aliasGenerator,
4041
cache,
4142
cachePolicy,
4243
depthLimit,
@@ -48,7 +49,7 @@ export const createContext = ({
4849
const selectSubscriptions = new Set<Selectable['select']>();
4950

5051
return {
51-
aliasLength,
52+
aliasGenerator,
5253
cache:
5354
cachePolicy === 'no-cache' ||
5455
cachePolicy === 'no-store' ||

packages/gqty/src/Client/index.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
ScalarsEnumsHash,
1111
Schema,
1212
} from '../Schema';
13+
import { createAliasHasher, type AliasGenerator } from './alias';
1314
import {
1415
createLegacyClient,
1516
type LegacyClient,
@@ -21,9 +22,7 @@ import { createContext } from './context';
2122
import { createDebugger, type Debugger } from './debugger';
2223
import { createResolvers, type Resolvers } from './resolvers';
2324

24-
export { $meta } from '../Accessor';
25-
export { getFields, prepass, selectFields } from '../Helpers';
26-
export * as useMetaStateHack from '../Helpers/useMetaStateHack';
25+
export * from './alias';
2726
export type {
2827
LegacyClientOptions as LegacyFetchers,
2928
LegacyHydrateCache,
@@ -107,9 +106,17 @@ export type ClientOptions = {
107106
* when collisions occur, specify Infinity here to use the full hash.
108107
*
109108
* @default 6
109+
* @deprecated Use `aliasGenerator` instead.
110110
*/
111111
aliasLength?: number;
112112

113+
/**
114+
* Alias generator function, useful for debugging and logging.
115+
*
116+
* This option takes precedence over `aliasLength`.
117+
*/
118+
aliasGenerator?: AliasGenerator;
119+
113120
/**
114121
* Milliseconds to wait before pending queries are batched up for fetching.
115122
*/
@@ -164,6 +171,7 @@ export const createClient = <
164171
_ObjectTypes extends SchemaObjects<TSchema> = never,
165172
>({
166173
aliasLength = 6,
174+
aliasGenerator = createAliasHasher(aliasLength),
167175
batchWindow,
168176
// This default cache on a required option is for legacy clients, which does
169177
// not provide a `cache` option.
@@ -206,7 +214,7 @@ export const createClient = <
206214

207215
// Global scope for accessing the cache via `schema` property.
208216
const clientContext = createContext({
209-
aliasLength,
217+
aliasGenerator,
210218
cache,
211219
depthLimit: __depthLimit,
212220
cachePolicy: fetchPolicy,
@@ -216,7 +224,7 @@ export const createClient = <
216224
});
217225

218226
const resolvers = createResolvers<TSchema>({
219-
aliasLength,
227+
aliasGenerator,
220228
batchWindow,
221229
scalars,
222230
schema,

0 commit comments

Comments
 (0)