houdini-core 2.0.0-go.0
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/package.json +24 -0
- package/postInstall.js +117 -0
- package/runtime/cache.ts +5 -0
- package/runtime/client.ts +181 -0
- package/runtime/config.ts +79 -0
- package/runtime/generated.ts +1 -0
- package/runtime/imports/config.ts +3 -0
- package/runtime/imports/pluginConfig.ts +5 -0
- package/runtime/index.ts +38 -0
- package/runtime/package.json +1 -0
- package/runtime/plugins/cache.ts +178 -0
- package/runtime/plugins/fetch.ts +337 -0
- package/runtime/plugins/fetchParams.ts +36 -0
- package/runtime/plugins/fragment.ts +80 -0
- package/runtime/plugins/index.ts +9 -0
- package/runtime/plugins/injectedPlugins.ts +9 -0
- package/runtime/plugins/mutation.ts +98 -0
- package/runtime/plugins/optimisticKeys.ts +455 -0
- package/runtime/plugins/query.ts +93 -0
- package/runtime/plugins/subscription.ts +153 -0
- package/runtime/plugins/throwOnError.ts +44 -0
- package/runtime/plugins/utils/documentPlugins.ts +54 -0
- package/runtime/plugins/utils/index.ts +1 -0
- package/runtime/public/cache.ts +121 -0
- package/runtime/public/index.ts +1 -0
- package/runtime/public/list.ts +164 -0
- package/runtime/public/record.ts +113 -0
- package/runtime/public/types.ts +166 -0
- package/runtime/server/index.ts +1 -0
- package/shim.cjs +64 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ClientPlugin, ClientPluginContext } from 'houdini/runtime/documentStore'
|
|
2
|
+
import type { ArtifactKinds, QueryResult } from 'houdini/runtime/types'
|
|
3
|
+
import { ArtifactKind } from 'houdini/runtime/types'
|
|
4
|
+
|
|
5
|
+
export type ThrowOnErrorOperations = 'all' | 'query' | 'mutation' | 'subscription'
|
|
6
|
+
|
|
7
|
+
export type ThrowOnErrorParams = {
|
|
8
|
+
operations: ThrowOnErrorOperations[]
|
|
9
|
+
error?: (
|
|
10
|
+
errors: NonNullable<QueryResult<any, any>['errors']>,
|
|
11
|
+
ctx: ClientPluginContext
|
|
12
|
+
) => unknown
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const throwOnError =
|
|
16
|
+
({ operations, error }: ThrowOnErrorParams): ClientPlugin =>
|
|
17
|
+
() => {
|
|
18
|
+
// build a map of artifact kinds we will throw on
|
|
19
|
+
const all = operations.includes('all')
|
|
20
|
+
const throwOnKind = (kind: ArtifactKinds) =>
|
|
21
|
+
all ||
|
|
22
|
+
{
|
|
23
|
+
[ArtifactKind.Query]: operations.includes('query'),
|
|
24
|
+
[ArtifactKind.Mutation]: operations.includes('mutation'),
|
|
25
|
+
[ArtifactKind.Fragment]: false,
|
|
26
|
+
[ArtifactKind.Subscription]: operations.includes('subscription'),
|
|
27
|
+
}[kind]
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
async end(ctx, { value, resolve }) {
|
|
31
|
+
// if we are supposed to throw and there are errors
|
|
32
|
+
if (value.errors && value.errors.length > 0 && throwOnKind(ctx.artifact.kind)) {
|
|
33
|
+
const result = await (error ?? defaultErrorFn)(value.errors, ctx)
|
|
34
|
+
throw result
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// we're not supposed to throw, move on
|
|
38
|
+
resolve(ctx)
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const defaultErrorFn: Required<ThrowOnErrorParams>['error'] = async (errors) =>
|
|
44
|
+
new Error(errors.map((error) => error.message).join('. ') + '.')
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { ArtifactKinds } from 'houdini/runtime'
|
|
2
|
+
import type {
|
|
3
|
+
ClientPlugin,
|
|
4
|
+
ClientPluginExitPhase,
|
|
5
|
+
ClientPluginEnterPhase,
|
|
6
|
+
ClientHooks,
|
|
7
|
+
} from 'houdini/runtime/documentStore'
|
|
8
|
+
|
|
9
|
+
export const documentPlugin = (kind: ArtifactKinds, source: () => ClientHooks): ClientPlugin => {
|
|
10
|
+
return () => {
|
|
11
|
+
// pull out the hooks we care about
|
|
12
|
+
const sourceHandlers = source()
|
|
13
|
+
|
|
14
|
+
const enterWrapper = (
|
|
15
|
+
handler?: ClientPluginEnterPhase
|
|
16
|
+
): ClientPluginEnterPhase | undefined => {
|
|
17
|
+
return !handler
|
|
18
|
+
? undefined
|
|
19
|
+
: (ctx, handlers) => {
|
|
20
|
+
if (ctx.artifact.kind !== kind) {
|
|
21
|
+
return handlers.next(ctx)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return handler(ctx, handlers)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const exitWrapper = (
|
|
28
|
+
handler?: ClientPluginExitPhase
|
|
29
|
+
): ClientPluginExitPhase | undefined => {
|
|
30
|
+
return !handler
|
|
31
|
+
? undefined
|
|
32
|
+
: (ctx, handlers) => {
|
|
33
|
+
if (ctx.artifact.kind !== kind) {
|
|
34
|
+
return handlers.resolve(ctx)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return handler(ctx, handlers)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// return the modified hooks
|
|
42
|
+
return {
|
|
43
|
+
start: enterWrapper(sourceHandlers.start),
|
|
44
|
+
network: enterWrapper(sourceHandlers.network),
|
|
45
|
+
beforeNetwork: enterWrapper(sourceHandlers.beforeNetwork),
|
|
46
|
+
afterNetwork: exitWrapper(sourceHandlers.afterNetwork),
|
|
47
|
+
end: exitWrapper(sourceHandlers.end),
|
|
48
|
+
catch: sourceHandlers.catch
|
|
49
|
+
? (ctx, handlers) => sourceHandlers.catch!(ctx, handlers)
|
|
50
|
+
: undefined,
|
|
51
|
+
cleanup: (...args) => sourceHandlers.cleanup?.(...args),
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './documentPlugins'
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { Cache as _Cache } from 'houdini/runtime/cache'
|
|
2
|
+
import { marshalInputs } from 'houdini/runtime/scalars'
|
|
3
|
+
import type { QueryArtifact } from 'houdini/runtime/types'
|
|
4
|
+
|
|
5
|
+
import { getCurrentConfig } from '../config'
|
|
6
|
+
import { ListCollection } from './list'
|
|
7
|
+
import { Record } from './record'
|
|
8
|
+
import type {
|
|
9
|
+
ArgType,
|
|
10
|
+
CacheTypeDef,
|
|
11
|
+
IDFields,
|
|
12
|
+
QueryInput,
|
|
13
|
+
QueryList,
|
|
14
|
+
QueryValue,
|
|
15
|
+
TypeFieldNames,
|
|
16
|
+
TypeNames,
|
|
17
|
+
ValidLists,
|
|
18
|
+
} from './types'
|
|
19
|
+
|
|
20
|
+
export class Cache<Def extends CacheTypeDef> {
|
|
21
|
+
_internal_unstable: _Cache
|
|
22
|
+
|
|
23
|
+
constructor(cache: _Cache) {
|
|
24
|
+
this._internal_unstable = cache
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// return the record proxy for the given type/id combo
|
|
28
|
+
get<T extends TypeNames<Def>>(type: T, data: IDFields<Def, T>): Record<Def, T> {
|
|
29
|
+
// compute the id for the record
|
|
30
|
+
let recordID = this._internal_unstable._internal_unstable.id(type, data)
|
|
31
|
+
if (!recordID) {
|
|
32
|
+
throw new Error('todo')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// return the proxy
|
|
36
|
+
return new Record({
|
|
37
|
+
cache: this,
|
|
38
|
+
type: type,
|
|
39
|
+
id: recordID,
|
|
40
|
+
idFields: data,
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
get config() {
|
|
45
|
+
return getCurrentConfig()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
list<Name extends ValidLists<Def>>(
|
|
49
|
+
name: Name,
|
|
50
|
+
{ parentID, allLists }: { parentID?: string; allLists?: boolean } = {}
|
|
51
|
+
): ListCollection<Def, Name> {
|
|
52
|
+
return new ListCollection<Def, Name>({
|
|
53
|
+
cache: this,
|
|
54
|
+
name,
|
|
55
|
+
parentID,
|
|
56
|
+
allLists,
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
read<_Query extends { artifact: QueryArtifact }>({
|
|
61
|
+
query,
|
|
62
|
+
variables,
|
|
63
|
+
}: {
|
|
64
|
+
query: _Query
|
|
65
|
+
variables?: QueryInput<QueryList<Def>, _Query>
|
|
66
|
+
}): {
|
|
67
|
+
data: QueryValue<QueryList<Def>, _Query> | null
|
|
68
|
+
partial: boolean
|
|
69
|
+
} {
|
|
70
|
+
// @ts-expect-error
|
|
71
|
+
return this._internal_unstable.read({
|
|
72
|
+
selection: query.artifact.selection,
|
|
73
|
+
variables,
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
write<_Query extends { artifact: QueryArtifact }>({
|
|
78
|
+
query,
|
|
79
|
+
variables,
|
|
80
|
+
data,
|
|
81
|
+
}: {
|
|
82
|
+
query: _Query
|
|
83
|
+
data: QueryValue<QueryList<Def>, _Query>
|
|
84
|
+
variables?: QueryInput<QueryList<Def>, _Query>
|
|
85
|
+
}) {
|
|
86
|
+
this._internal_unstable.write({
|
|
87
|
+
selection: query.artifact.selection,
|
|
88
|
+
// @ts-expect-error
|
|
89
|
+
data,
|
|
90
|
+
variables:
|
|
91
|
+
marshalInputs({
|
|
92
|
+
config: this.config,
|
|
93
|
+
artifact: query.artifact,
|
|
94
|
+
input: variables,
|
|
95
|
+
}) ?? {},
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
return
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Mark some elements of the cache stale.
|
|
103
|
+
*/
|
|
104
|
+
markStale<_Type extends TypeNames<Def>, _Field extends TypeFieldNames<Def, _Type>>(
|
|
105
|
+
type?: _Type,
|
|
106
|
+
options?: {
|
|
107
|
+
field?: _Field
|
|
108
|
+
when?: ArgType<Def, _Type, _Field>
|
|
109
|
+
}
|
|
110
|
+
): void {
|
|
111
|
+
return this._internal_unstable.markTypeStale(type ? { ...options, type } : undefined)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Reset the entire cache by clearing all records and lists
|
|
116
|
+
*/
|
|
117
|
+
|
|
118
|
+
reset(): void {
|
|
119
|
+
return this._internal_unstable.reset()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Cache } from './cache'
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import type { GraphQLObject, SubscriptionSelection } from 'houdini/runtime'
|
|
2
|
+
import type { ListCollection as _Collection } from 'houdini/runtime/cache/lists'
|
|
3
|
+
|
|
4
|
+
import type { Cache } from './cache'
|
|
5
|
+
import { Record } from './record'
|
|
6
|
+
import type { CacheTypeDef, ListType, ValidLists, ListFilters } from './types'
|
|
7
|
+
|
|
8
|
+
export class ListCollection<Def extends CacheTypeDef, ListName extends ValidLists<Def>> {
|
|
9
|
+
#parentID: string | undefined
|
|
10
|
+
#allLists: boolean | undefined
|
|
11
|
+
#when: ListFilters<Def, ListName> | undefined
|
|
12
|
+
#cache: Cache<Def>
|
|
13
|
+
#name: ValidLists<Def>
|
|
14
|
+
|
|
15
|
+
constructor({
|
|
16
|
+
parentID,
|
|
17
|
+
allLists,
|
|
18
|
+
when,
|
|
19
|
+
cache,
|
|
20
|
+
name,
|
|
21
|
+
}: {
|
|
22
|
+
name: ValidLists<Def>
|
|
23
|
+
parentID?: string
|
|
24
|
+
allLists?: boolean
|
|
25
|
+
when?: ListFilters<Def, ListName>
|
|
26
|
+
cache: Cache<Def>
|
|
27
|
+
}) {
|
|
28
|
+
this.#parentID = parentID
|
|
29
|
+
this.#allLists = allLists
|
|
30
|
+
this.#when = when
|
|
31
|
+
this.#cache = cache
|
|
32
|
+
this.#name = name
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
append(...records: ListType<Def, ListName>[]) {
|
|
36
|
+
if (!this.#collection) {
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const { selection, data } = this.#listOperationPayload(records)
|
|
41
|
+
for (const entry of data) {
|
|
42
|
+
if (entry) {
|
|
43
|
+
this.#collection.append({
|
|
44
|
+
selection,
|
|
45
|
+
data: entry,
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
prepend(...records: ListType<Def, ListName>[]) {
|
|
52
|
+
if (!this.#collection) {
|
|
53
|
+
return
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const { selection, data } = this.#listOperationPayload(records)
|
|
57
|
+
for (const entry of data) {
|
|
58
|
+
if (entry) {
|
|
59
|
+
this.#collection.prepend({
|
|
60
|
+
selection,
|
|
61
|
+
data: entry,
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
toggle(where: 'first' | 'last', ...records: ListType<Def, ListName>[]) {
|
|
68
|
+
if (!this.#collection) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const { selection, data } = this.#listOperationPayload(records)
|
|
73
|
+
for (const entry of data) {
|
|
74
|
+
if (entry) {
|
|
75
|
+
this.#collection.toggleElement({
|
|
76
|
+
selection,
|
|
77
|
+
data: entry,
|
|
78
|
+
where,
|
|
79
|
+
})
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
when(filter: ListFilters<Def, ListName>): ListCollection<Def, ListName> {
|
|
85
|
+
if (!this.#collection) {
|
|
86
|
+
return this
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return new ListCollection({
|
|
90
|
+
parentID: this.#parentID,
|
|
91
|
+
allLists: this.#allLists,
|
|
92
|
+
when: filter,
|
|
93
|
+
cache: this.#cache,
|
|
94
|
+
name: this.#name,
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
remove(...records: ListType<Def, ListName>[]) {
|
|
99
|
+
if (!this.#collection) {
|
|
100
|
+
return
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
for (const record of records) {
|
|
104
|
+
if (record) {
|
|
105
|
+
this.#collection.remove(record.idFields)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
*[Symbol.iterator]() {
|
|
111
|
+
for (const entry of this.#collection ?? []) {
|
|
112
|
+
yield entry
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
get #collection(): _Collection | null {
|
|
117
|
+
try {
|
|
118
|
+
const list = this.#cache._internal_unstable.list(
|
|
119
|
+
this.#name,
|
|
120
|
+
this.#parentID,
|
|
121
|
+
this.#allLists
|
|
122
|
+
)
|
|
123
|
+
if (this.#when) {
|
|
124
|
+
return list.when(this.#when)
|
|
125
|
+
}
|
|
126
|
+
return list
|
|
127
|
+
} catch {
|
|
128
|
+
return null
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
#listOperationPayload(records: ListType<Def, ListName>[]): {
|
|
133
|
+
selection: SubscriptionSelection
|
|
134
|
+
data: GraphQLObject[]
|
|
135
|
+
} {
|
|
136
|
+
// we need to build up the selection that describes the key
|
|
137
|
+
// for every type in the list
|
|
138
|
+
let selection: SubscriptionSelection = this.#collection!.selection
|
|
139
|
+
// if the list is a connection, we can't use this selection immediately
|
|
140
|
+
// we need to look for edges.node
|
|
141
|
+
const connectionSelection = selection.fields?.['edges']?.selection?.fields?.node.selection
|
|
142
|
+
if (connectionSelection) {
|
|
143
|
+
selection = connectionSelection
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// and the actual data for the record
|
|
147
|
+
const data: GraphQLObject[] = []
|
|
148
|
+
|
|
149
|
+
// loop over every record we are adding to build up the necessary structure
|
|
150
|
+
for (const record of records) {
|
|
151
|
+
if (!(record instanceof Record)) {
|
|
152
|
+
throw new Error('You must provide a Record to a list operation')
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// add the necessary information
|
|
156
|
+
data.push({ __typename: record.type, ...record.idFields })
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
selection,
|
|
161
|
+
data,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { marshalInputs } from 'houdini/runtime'
|
|
2
|
+
import { keyFieldsForType } from 'houdini/runtime'
|
|
3
|
+
import type { FragmentArtifact, GraphQLObject } from 'houdini/runtime'
|
|
4
|
+
import { rootID } from 'houdini/runtime/cache/stuff'
|
|
5
|
+
|
|
6
|
+
import type { Cache } from './cache'
|
|
7
|
+
import type {
|
|
8
|
+
ArgType,
|
|
9
|
+
CacheTypeDef,
|
|
10
|
+
FragmentList,
|
|
11
|
+
FragmentValue,
|
|
12
|
+
FragmentVariables,
|
|
13
|
+
TypeFieldNames,
|
|
14
|
+
ValidTypes,
|
|
15
|
+
} from './types'
|
|
16
|
+
|
|
17
|
+
export class Record<Def extends CacheTypeDef, Type extends ValidTypes<Def>> {
|
|
18
|
+
#id: string
|
|
19
|
+
#cache: Cache<Def>
|
|
20
|
+
|
|
21
|
+
type: string
|
|
22
|
+
idFields: {}
|
|
23
|
+
|
|
24
|
+
constructor({
|
|
25
|
+
cache,
|
|
26
|
+
type,
|
|
27
|
+
id,
|
|
28
|
+
idFields,
|
|
29
|
+
}: {
|
|
30
|
+
cache: Cache<Def>
|
|
31
|
+
type: string
|
|
32
|
+
idFields: {}
|
|
33
|
+
id: string
|
|
34
|
+
}) {
|
|
35
|
+
this.#cache = cache
|
|
36
|
+
this.#id = id
|
|
37
|
+
this.type = type
|
|
38
|
+
this.idFields = idFields
|
|
39
|
+
|
|
40
|
+
// make sure that we have all of the necessary fields for the id
|
|
41
|
+
if (id !== rootID) {
|
|
42
|
+
for (const key of keyFieldsForType(this.#cache.config, type)) {
|
|
43
|
+
if (!(key in idFields)) {
|
|
44
|
+
throw new Error('Missing key in idFields: ' + key)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
read<_Fragment extends { artifact: FragmentArtifact }>({
|
|
51
|
+
fragment,
|
|
52
|
+
variables,
|
|
53
|
+
}: {
|
|
54
|
+
fragment: _Fragment
|
|
55
|
+
variables?: FragmentVariables<FragmentList<Def, Type>, _Fragment>
|
|
56
|
+
}): { data: FragmentValue<FragmentList<Def, Type>, _Fragment> | null; partial: boolean } {
|
|
57
|
+
// @ts-expect-error
|
|
58
|
+
return this.#cache._internal_unstable.read({
|
|
59
|
+
selection: fragment.artifact.selection,
|
|
60
|
+
parent: this.#id,
|
|
61
|
+
variables:
|
|
62
|
+
marshalInputs({
|
|
63
|
+
config: this.#cache.config,
|
|
64
|
+
artifact: fragment.artifact,
|
|
65
|
+
input: variables,
|
|
66
|
+
}) ?? undefined,
|
|
67
|
+
})
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
write<_Fragment extends { artifact: FragmentArtifact }, _Variable>(args: {
|
|
71
|
+
fragment: _Fragment
|
|
72
|
+
data: FragmentValue<FragmentList<Def, Type>, _Fragment>
|
|
73
|
+
// TODO: figure out a way to make this required when _Variables has a value
|
|
74
|
+
// and optional when _Variables is never
|
|
75
|
+
variables?: FragmentVariables<FragmentList<Def, Type>, _Fragment>
|
|
76
|
+
forceStale?: boolean
|
|
77
|
+
}) {
|
|
78
|
+
// we have the data and the fragment, just pass them both to the cache
|
|
79
|
+
this.#cache._internal_unstable.write({
|
|
80
|
+
data: args.data as unknown as GraphQLObject,
|
|
81
|
+
selection: args.fragment.artifact.selection,
|
|
82
|
+
parent: this.#id,
|
|
83
|
+
variables:
|
|
84
|
+
marshalInputs({
|
|
85
|
+
config: this.#cache.config,
|
|
86
|
+
artifact: args.fragment.artifact,
|
|
87
|
+
input: args.variables,
|
|
88
|
+
}) ?? undefined,
|
|
89
|
+
forceStale: args.forceStale,
|
|
90
|
+
})
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
delete() {
|
|
94
|
+
this.#cache._internal_unstable.delete(this.#id)
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Mark some elements of the record stale in the cache.
|
|
99
|
+
* @param field
|
|
100
|
+
* @param when
|
|
101
|
+
*/
|
|
102
|
+
markStale<Field extends TypeFieldNames<Def, Type>>(
|
|
103
|
+
field?: Field,
|
|
104
|
+
{
|
|
105
|
+
when,
|
|
106
|
+
}: {
|
|
107
|
+
when?: ArgType<Def, Type, Field>
|
|
108
|
+
} = {}
|
|
109
|
+
): void {
|
|
110
|
+
// mark the record
|
|
111
|
+
this.#cache._internal_unstable.markRecordStale(this.#id, { field, when })
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import type { Record } from './record'
|
|
2
|
+
|
|
3
|
+
export type CacheTypeDef = {
|
|
4
|
+
types: {
|
|
5
|
+
[typeName: string]: {
|
|
6
|
+
idFields: {
|
|
7
|
+
[fieldName: string]: any
|
|
8
|
+
}
|
|
9
|
+
fields: {
|
|
10
|
+
[fieldName: string]: {
|
|
11
|
+
args: any
|
|
12
|
+
type: any
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
// the fragments we know about are passed as a list of pairs
|
|
16
|
+
// that map the tag return type to the data shape and input
|
|
17
|
+
fragments: [any, any, any][]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
lists: {
|
|
21
|
+
[listName: string]: {
|
|
22
|
+
types: any
|
|
23
|
+
filters: any
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// we need to map query tag values to their result
|
|
27
|
+
// to pass be able to pass queries to the read and write methods
|
|
28
|
+
// entries in the tuple are graphql tag, query shape, query input
|
|
29
|
+
queries: [any, any, any][]
|
|
30
|
+
|
|
31
|
+
// a union of valid runtime types (default scalars | config types)
|
|
32
|
+
scalars: any
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export type ValidTypes<Def extends CacheTypeDef> = keyof Def['types']
|
|
36
|
+
|
|
37
|
+
export type TypeFields<
|
|
38
|
+
Def extends CacheTypeDef,
|
|
39
|
+
Type extends keyof Def['types']
|
|
40
|
+
> = Def['types'][Type]['fields']
|
|
41
|
+
|
|
42
|
+
export type TypeFieldNames<
|
|
43
|
+
Def extends CacheTypeDef,
|
|
44
|
+
Type extends keyof Def['types']
|
|
45
|
+
// Extract is necessary because javascript allows numbers to be used as strings when indexing objects
|
|
46
|
+
// for more information: https://stackoverflow.com/questions/51808160/keyof-inferring-string-number-when-key-is-only-a-string
|
|
47
|
+
> = Extract<keyof TypeFields<Def, Type>, string>
|
|
48
|
+
|
|
49
|
+
export type TypeNames<Def extends CacheTypeDef> = Extract<
|
|
50
|
+
Exclude<ValidTypes<Def>, '__ROOT__'>,
|
|
51
|
+
string
|
|
52
|
+
>
|
|
53
|
+
|
|
54
|
+
export type FragmentList<
|
|
55
|
+
Def extends CacheTypeDef,
|
|
56
|
+
Type extends ValidTypes<Def>
|
|
57
|
+
> = Def['types'][Type]['fragments']
|
|
58
|
+
|
|
59
|
+
export type QueryList<Def extends CacheTypeDef> = Def['queries']
|
|
60
|
+
|
|
61
|
+
export type IDFields<
|
|
62
|
+
Def extends CacheTypeDef,
|
|
63
|
+
Type extends keyof Def['types']
|
|
64
|
+
> = Def['types'][Type]['idFields']
|
|
65
|
+
|
|
66
|
+
export type ProxyUnion<Def extends CacheTypeDef, U> = U extends null
|
|
67
|
+
? null
|
|
68
|
+
: U extends TypeNames<Def>
|
|
69
|
+
? Record<Def, U>
|
|
70
|
+
: never
|
|
71
|
+
|
|
72
|
+
export type FieldType<
|
|
73
|
+
Def extends CacheTypeDef,
|
|
74
|
+
Type extends keyof Def['types'],
|
|
75
|
+
Field extends keyof TypeFields<Def, Type>
|
|
76
|
+
> = TypeFields<Def, Type>[Field]['type']
|
|
77
|
+
|
|
78
|
+
export type ArgType<
|
|
79
|
+
Def extends CacheTypeDef,
|
|
80
|
+
Type extends keyof Def['types'],
|
|
81
|
+
Field extends keyof TypeFields<Def, Type>
|
|
82
|
+
> = TypeFields<Def, Type>[Field]['args']
|
|
83
|
+
|
|
84
|
+
export type ValidLists<Def extends CacheTypeDef> = Extract<keyof Def['lists'], string>
|
|
85
|
+
|
|
86
|
+
export type ListFilters<
|
|
87
|
+
Def extends CacheTypeDef,
|
|
88
|
+
ListName extends ValidLists<Def>
|
|
89
|
+
> = Def['lists'][ListName]['filters'] extends any
|
|
90
|
+
? {
|
|
91
|
+
must?: Def['lists'][ListName]['filters']
|
|
92
|
+
must_not?: Def['lists'][ListName]['filters']
|
|
93
|
+
}
|
|
94
|
+
: never
|
|
95
|
+
|
|
96
|
+
export type ListType<Def extends CacheTypeDef, Name extends ValidLists<Def>> = ProxyUnion<
|
|
97
|
+
Def,
|
|
98
|
+
Def['lists'][Name]['types']
|
|
99
|
+
>
|
|
100
|
+
|
|
101
|
+
// This same structure is repeated because when I dry'd the structure to
|
|
102
|
+
//
|
|
103
|
+
// type FragmentDefinition<_List, _Index, _Target>
|
|
104
|
+
// type FragmentVariables<_Def, _Type, _Target> = FragmentDefinition<FragmentList<...>>
|
|
105
|
+
//
|
|
106
|
+
// then typescript wasn't responding on every change... Having the duplication makes
|
|
107
|
+
// it very responsive.
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Note on the `_Key extends _Target && _Target extends _Key` check:
|
|
111
|
+
* Sometimes two queries might have a similar shape/inputs, e.g.:
|
|
112
|
+
*
|
|
113
|
+
* query Query1($userId: ID!) { | query Query2($userId: ID!) {
|
|
114
|
+
* user(id: $userId) { | user($id: $userId) {
|
|
115
|
+
* id | id
|
|
116
|
+
* name | name
|
|
117
|
+
* } | birthDate
|
|
118
|
+
* } | }
|
|
119
|
+
* | }
|
|
120
|
+
*
|
|
121
|
+
* To TypeScript, it would look like Query2's result would extend Query1's result.
|
|
122
|
+
* But if Query2 was listed in front of Query1 in the queries array above, `_Key extends _Target` will evaluate to true,
|
|
123
|
+
* causing it to return Query2's input/result types, while you were looking for Query1's input/result types.
|
|
124
|
+
* The additional `_Target extends _Key` ensures that the two objects have exactly the same shape, at least prompting the
|
|
125
|
+
* user for the correct fields.
|
|
126
|
+
*/
|
|
127
|
+
|
|
128
|
+
export type FragmentVariables<_List, _Target> = _List extends [infer Head, ...infer Rest]
|
|
129
|
+
? Head extends [infer _Key, infer _Value, infer _Input]
|
|
130
|
+
? _Key extends _Target
|
|
131
|
+
? _Target extends _Key
|
|
132
|
+
? _Input
|
|
133
|
+
: FragmentValue<Rest, _Target>
|
|
134
|
+
: FragmentValue<Rest, _Target>
|
|
135
|
+
: 'Encountered unknown fragment. Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
136
|
+
: 'Encountered unknown fragment. Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
137
|
+
|
|
138
|
+
export type FragmentValue<List, _Target> = List extends [infer Head, ...infer Rest]
|
|
139
|
+
? Head extends [infer _Key, infer _Value, infer _Input]
|
|
140
|
+
? _Key extends _Target
|
|
141
|
+
? _Target extends _Key
|
|
142
|
+
? _Value
|
|
143
|
+
: FragmentValue<Rest, _Target>
|
|
144
|
+
: FragmentValue<Rest, _Target>
|
|
145
|
+
: 'Encountered unknown fragment. Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
146
|
+
: 'Encountered unknown fragment. Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
147
|
+
|
|
148
|
+
export type QueryValue<List, _Target> = List extends [infer Head, ...infer Rest]
|
|
149
|
+
? Head extends [infer _Key, infer _Value, infer _Input]
|
|
150
|
+
? _Key extends _Target
|
|
151
|
+
? _Target extends _Key
|
|
152
|
+
? _Value
|
|
153
|
+
: QueryValue<Rest, _Target>
|
|
154
|
+
: QueryValue<Rest, _Target>
|
|
155
|
+
: 'Encountered unknown query.Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
156
|
+
: 'Encountered unknown query.Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
157
|
+
|
|
158
|
+
export type QueryInput<List, _Target> = List extends [infer Head, ...infer Rest]
|
|
159
|
+
? Head extends [infer _Key, infer _Value, infer _Input]
|
|
160
|
+
? _Key extends _Target
|
|
161
|
+
? _Target extends _Key
|
|
162
|
+
? _Input
|
|
163
|
+
: QueryInput<Rest, _Target>
|
|
164
|
+
: QueryInput<Rest, _Target>
|
|
165
|
+
: 'Encountered unknown query.Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
166
|
+
: 'Encountered unknown query.Please make sure your runtime is up to date (ie, `vite dev` or `vite build`).'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { Server } from 'houdini/router/server'
|