@commitspark/graphql-api 1.0.0 → 2.0.0-beta.2

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.
Files changed (119) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +87 -4
  3. package/dist/cjs/client.d.ts +4 -2
  4. package/dist/cjs/client.js +4 -2
  5. package/dist/cjs/client.js.map +1 -1
  6. package/dist/cjs/graphql/errors.d.ts +3 -2
  7. package/dist/cjs/graphql/errors.js.map +1 -1
  8. package/dist/cjs/graphql/input-type-generator.js +2 -1
  9. package/dist/cjs/graphql/input-type-generator.js.map +1 -1
  10. package/dist/cjs/graphql/queries-mutations-generator.d.ts +8 -1
  11. package/dist/cjs/graphql/queries-mutations-generator.js +19 -0
  12. package/dist/cjs/graphql/queries-mutations-generator.js.map +1 -1
  13. package/dist/cjs/graphql/resolvers/field-resolvers/field-default-value-resolver.js +2 -1
  14. package/dist/cjs/graphql/resolvers/field-resolvers/field-default-value-resolver.js.map +1 -1
  15. package/dist/cjs/graphql/resolvers/query-mutation-resolvers/query-search-resolver.d.ts +2 -0
  16. package/dist/cjs/graphql/resolvers/query-mutation-resolvers/query-search-resolver.js +74 -0
  17. package/dist/cjs/graphql/resolvers/query-mutation-resolvers/query-search-resolver.js.map +1 -0
  18. package/dist/cjs/graphql/resolvers/types.d.ts +6 -0
  19. package/dist/cjs/graphql/schema-analyzer.js +2 -1
  20. package/dist/cjs/graphql/schema-analyzer.js.map +1 -1
  21. package/dist/cjs/graphql/schema-generator.js +7 -1
  22. package/dist/cjs/graphql/schema-generator.js.map +1 -1
  23. package/dist/cjs/graphql/schema-root-type-generator.d.ts +2 -2
  24. package/dist/cjs/graphql/schema-root-type-generator.js +2 -1
  25. package/dist/cjs/graphql/schema-root-type-generator.js.map +1 -1
  26. package/dist/cjs/graphql/schema-utils/directive-util.d.ts +4 -0
  27. package/dist/cjs/graphql/schema-utils/directive-util.js +10 -0
  28. package/dist/cjs/graphql/schema-utils/directive-util.js.map +1 -0
  29. package/dist/cjs/graphql/schema-utils/entry-reference-util.js +3 -1
  30. package/dist/cjs/graphql/schema-utils/entry-reference-util.js.map +1 -1
  31. package/dist/cjs/graphql/schema-utils/entry-type-util.d.ts +1 -2
  32. package/dist/cjs/graphql/schema-utils/entry-type-util.js +2 -6
  33. package/dist/cjs/graphql/schema-utils/entry-type-util.js.map +1 -1
  34. package/dist/cjs/graphql/schema-utils/searchable-field-util.d.ts +4 -0
  35. package/dist/cjs/graphql/schema-utils/searchable-field-util.js +79 -0
  36. package/dist/cjs/graphql/schema-utils/searchable-field-util.js.map +1 -0
  37. package/dist/cjs/graphql/schema-validator.js +21 -3
  38. package/dist/cjs/graphql/schema-validator.js.map +1 -1
  39. package/dist/cjs/index.d.ts +9 -2
  40. package/dist/cjs/index.js +4 -3
  41. package/dist/cjs/index.js.map +1 -1
  42. package/dist/cjs/persistence/cache.js +76 -27
  43. package/dist/cjs/persistence/cache.js.map +1 -1
  44. package/dist/esm/client.js +4 -2
  45. package/dist/esm/client.js.map +1 -1
  46. package/dist/esm/graphql/errors.js.map +1 -1
  47. package/dist/esm/graphql/input-type-generator.js +3 -2
  48. package/dist/esm/graphql/input-type-generator.js.map +1 -1
  49. package/dist/esm/graphql/queries-mutations-generator.js +18 -0
  50. package/dist/esm/graphql/queries-mutations-generator.js.map +1 -1
  51. package/dist/esm/graphql/resolvers/field-resolvers/field-default-value-resolver.js +3 -2
  52. package/dist/esm/graphql/resolvers/field-resolvers/field-default-value-resolver.js.map +1 -1
  53. package/dist/esm/graphql/resolvers/query-mutation-resolvers/query-search-resolver.js +70 -0
  54. package/dist/esm/graphql/resolvers/query-mutation-resolvers/query-search-resolver.js.map +1 -0
  55. package/dist/esm/graphql/schema-analyzer.js +2 -1
  56. package/dist/esm/graphql/schema-analyzer.js.map +1 -1
  57. package/dist/esm/graphql/schema-generator.js +8 -2
  58. package/dist/esm/graphql/schema-generator.js.map +1 -1
  59. package/dist/esm/graphql/schema-root-type-generator.js +2 -1
  60. package/dist/esm/graphql/schema-root-type-generator.js.map +1 -1
  61. package/dist/esm/graphql/schema-utils/directive-util.js +6 -0
  62. package/dist/esm/graphql/schema-utils/directive-util.js.map +1 -0
  63. package/dist/esm/graphql/schema-utils/entry-reference-util.js +4 -2
  64. package/dist/esm/graphql/schema-utils/entry-reference-util.js.map +1 -1
  65. package/dist/esm/graphql/schema-utils/entry-type-util.js +2 -5
  66. package/dist/esm/graphql/schema-utils/entry-type-util.js.map +1 -1
  67. package/dist/esm/graphql/schema-utils/searchable-field-util.js +76 -0
  68. package/dist/esm/graphql/schema-utils/searchable-field-util.js.map +1 -0
  69. package/dist/esm/graphql/schema-validator.js +22 -4
  70. package/dist/esm/graphql/schema-validator.js.map +1 -1
  71. package/dist/esm/index.js +4 -3
  72. package/dist/esm/index.js.map +1 -1
  73. package/dist/esm/persistence/cache.js +76 -27
  74. package/dist/esm/persistence/cache.js.map +1 -1
  75. package/dist/types/client.d.ts +4 -2
  76. package/dist/types/client.d.ts.map +1 -1
  77. package/dist/types/graphql/errors.d.ts +3 -2
  78. package/dist/types/graphql/errors.d.ts.map +1 -1
  79. package/dist/types/graphql/input-type-generator.d.ts.map +1 -1
  80. package/dist/types/graphql/queries-mutations-generator.d.ts +8 -1
  81. package/dist/types/graphql/queries-mutations-generator.d.ts.map +1 -1
  82. package/dist/types/graphql/resolvers/field-resolvers/field-default-value-resolver.d.ts.map +1 -1
  83. package/dist/types/graphql/resolvers/query-mutation-resolvers/query-search-resolver.d.ts +3 -0
  84. package/dist/types/graphql/resolvers/query-mutation-resolvers/query-search-resolver.d.ts.map +1 -0
  85. package/dist/types/graphql/resolvers/types.d.ts +6 -0
  86. package/dist/types/graphql/resolvers/types.d.ts.map +1 -1
  87. package/dist/types/graphql/schema-analyzer.d.ts.map +1 -1
  88. package/dist/types/graphql/schema-generator.d.ts.map +1 -1
  89. package/dist/types/graphql/schema-root-type-generator.d.ts +2 -2
  90. package/dist/types/graphql/schema-root-type-generator.d.ts.map +1 -1
  91. package/dist/types/graphql/schema-utils/directive-util.d.ts +5 -0
  92. package/dist/types/graphql/schema-utils/directive-util.d.ts.map +1 -0
  93. package/dist/types/graphql/schema-utils/entry-reference-util.d.ts.map +1 -1
  94. package/dist/types/graphql/schema-utils/entry-type-util.d.ts +1 -2
  95. package/dist/types/graphql/schema-utils/entry-type-util.d.ts.map +1 -1
  96. package/dist/types/graphql/schema-utils/searchable-field-util.d.ts +5 -0
  97. package/dist/types/graphql/schema-utils/searchable-field-util.d.ts.map +1 -0
  98. package/dist/types/graphql/schema-validator.d.ts.map +1 -1
  99. package/dist/types/index.d.ts +9 -2
  100. package/dist/types/index.d.ts.map +1 -1
  101. package/dist/types/persistence/cache.d.ts.map +1 -1
  102. package/package.json +3 -2
  103. package/src/client.ts +6 -0
  104. package/src/graphql/errors.ts +3 -2
  105. package/src/graphql/input-type-generator.ts +5 -4
  106. package/src/graphql/queries-mutations-generator.ts +31 -1
  107. package/src/graphql/resolvers/field-resolvers/field-default-value-resolver.ts +5 -4
  108. package/src/graphql/resolvers/query-mutation-resolvers/query-search-resolver.ts +110 -0
  109. package/src/graphql/resolvers/types.ts +8 -0
  110. package/src/graphql/schema-analyzer.ts +6 -1
  111. package/src/graphql/schema-generator.ts +13 -1
  112. package/src/graphql/schema-root-type-generator.ts +3 -0
  113. package/src/graphql/schema-utils/directive-util.ts +15 -0
  114. package/src/graphql/schema-utils/entry-reference-util.ts +6 -2
  115. package/src/graphql/schema-utils/entry-type-util.ts +2 -11
  116. package/src/graphql/schema-utils/searchable-field-util.ts +130 -0
  117. package/src/graphql/schema-validator.ts +37 -7
  118. package/src/index.ts +25 -4
  119. package/src/persistence/cache.ts +115 -28
@@ -3,7 +3,11 @@ import { ApolloContext } from '../client.ts'
3
3
  import { createError, ErrorCode } from '../graphql/errors.ts'
4
4
 
5
5
  type Ref = string
6
- type EntriesCache = Map<Ref, EntriesRecord>
6
+ type ContentHash = string
7
+ type EntryContent = Omit<Entry, 'id'>
8
+ type EntriesCache = Map<Ref, CommitCacheRecord>
9
+ // entry content shared across all cached commits, so that unchanged entries are neither fetched nor stored twice
10
+ type ContentCache = Map<ContentHash, EntryContent>
7
11
  type SchemaCache = Map<Ref, string>
8
12
  type InflightEntries = Map<Ref, Promise<EntriesRecord>>
9
13
 
@@ -12,6 +16,11 @@ interface EntriesRecord {
12
16
  byType: Map<string, Entry[]>
13
17
  }
14
18
 
19
+ interface CommitCacheRecord {
20
+ entriesRecord: EntriesRecord
21
+ contentHashes: ContentHash[]
22
+ }
23
+
15
24
  export interface RepositoryCacheHandler {
16
25
  getEntriesRecord: (
17
26
  context: ApolloContext,
@@ -25,6 +34,7 @@ const MAX_CACHE_ENTRIES = 50
25
34
  // intentionally not async so that the same in-flight promise is returned to all callers
26
35
  const getEntriesRecordByRef = (
27
36
  entriesCache: EntriesCache,
37
+ contentCache: ContentCache,
28
38
  inflightEntries: InflightEntries,
29
39
  cacheSize: number,
30
40
  context: ApolloContext,
@@ -36,7 +46,7 @@ const getEntriesRecordByRef = (
36
46
  entriesCache.delete(ref)
37
47
  entriesCache.set(ref, cacheRecord)
38
48
 
39
- return Promise.resolve(cacheRecord)
49
+ return Promise.resolve(cacheRecord.entriesRecord)
40
50
  }
41
51
 
42
52
  const inflightPromise = inflightEntries.get(ref)
@@ -46,6 +56,7 @@ const getEntriesRecordByRef = (
46
56
 
47
57
  const fetchPromise = fetchAndCacheEntries(
48
58
  entriesCache,
59
+ contentCache,
49
60
  cacheSize,
50
61
  context,
51
62
  ref,
@@ -58,34 +69,72 @@ const getEntriesRecordByRef = (
58
69
 
59
70
  const fetchAndCacheEntries = async (
60
71
  entriesCache: EntriesCache,
72
+ contentCache: ContentCache,
61
73
  cacheSize: number,
62
74
  context: ApolloContext,
63
75
  ref: string,
64
76
  ): Promise<EntriesRecord> => {
65
- let allEntries: Entry[]
66
- try {
67
- allEntries = await context.gitAdapter.getEntries(ref)
68
- } catch (err) {
69
- if (err instanceof GitAdapterError) {
70
- throw createError(err.message, err.code, {})
77
+ const entryHashes = await callGitAdapter(() =>
78
+ context.gitAdapter.getEntryHashes(ref),
79
+ )
80
+
81
+ // hold on to known content now, as it may be evicted from the content cache while missing content is fetched
82
+ const contentByHash = new Map<ContentHash, EntryContent>()
83
+ const idsToFetchByHash = new Map<ContentHash, string>()
84
+ for (const { id, hash } of entryHashes) {
85
+ const content = contentCache.get(hash)
86
+ if (content !== undefined) {
87
+ contentByHash.set(hash, content)
88
+ } else if (!idsToFetchByHash.has(hash)) {
89
+ // entries with equal hash have equal content, so fetching one of them is sufficient
90
+ idsToFetchByHash.set(hash, id)
91
+ }
92
+ }
93
+
94
+ if (idsToFetchByHash.size > 0) {
95
+ const fetchedEntries = await callGitAdapter(() =>
96
+ context.gitAdapter.getEntriesByIds(ref, [...idsToFetchByHash.values()]),
97
+ )
98
+ const fetchedEntriesById = new Map(
99
+ fetchedEntries.map((entry) => [entry.id, entry]),
100
+ )
101
+ for (const [hash, id] of idsToFetchByHash) {
102
+ const fetchedEntry = fetchedEntriesById.get(id)
103
+ if (fetchedEntry === undefined) {
104
+ throw createError(
105
+ `Git adapter did not return requested entry "${id}".`,
106
+ ErrorCode.INTERNAL_ERROR,
107
+ {},
108
+ )
109
+ }
110
+ contentByHash.set(hash, createEntryContent(fetchedEntry))
71
111
  }
72
- const message = err instanceof Error ? err.message : String(err)
73
- throw createError(message, ErrorCode.INTERNAL_ERROR, {})
74
112
  }
75
113
 
76
- const entriesById = new Map(allEntries.map((entry) => [entry.id, entry]))
114
+ const entriesById = new Map<string, Entry>()
77
115
  const entriesByType = new Map<string, Entry[]>()
78
- for (const entry of allEntries) {
116
+ for (const { id, hash } of entryHashes) {
117
+ const entry: Entry = {
118
+ id: id,
119
+ ...(contentByHash.get(hash) as EntryContent),
120
+ }
121
+ entriesById.set(id, entry)
79
122
  const existingEntriesOfType = entriesByType.get(entry.metadata.type) || []
80
123
  existingEntriesOfType.push(entry)
81
124
  entriesByType.set(entry.metadata.type, existingEntriesOfType)
82
125
  }
83
126
 
84
- const newCacheRecord = {
85
- byType: entriesByType,
86
- byId: entriesById,
127
+ const newCacheRecord: CommitCacheRecord = {
128
+ entriesRecord: {
129
+ byType: entriesByType,
130
+ byId: entriesById,
131
+ },
132
+ contentHashes: [...contentByHash.keys()],
87
133
  }
88
134
 
135
+ for (const [hash, content] of contentByHash) {
136
+ contentCache.set(hash, content)
137
+ }
89
138
  entriesCache.set(ref, newCacheRecord)
90
139
 
91
140
  if (entriesCache.size > cacheSize) {
@@ -93,10 +142,48 @@ const fetchAndCacheEntries = async (
93
142
  const oldestKey = entriesCache.keys().next().value as Ref | undefined
94
143
  if (oldestKey !== undefined) {
95
144
  entriesCache.delete(oldestKey)
145
+ removeUnreferencedContent(entriesCache, contentCache)
96
146
  }
97
147
  }
98
148
 
99
- return newCacheRecord
149
+ return newCacheRecord.entriesRecord
150
+ }
151
+
152
+ const createEntryContent = (entry: Entry): EntryContent => {
153
+ const content: EntryContent = { metadata: entry.metadata }
154
+ if (entry.data !== undefined) {
155
+ content.data = entry.data
156
+ }
157
+ return content
158
+ }
159
+
160
+ const removeUnreferencedContent = (
161
+ entriesCache: EntriesCache,
162
+ contentCache: ContentCache,
163
+ ): void => {
164
+ const referencedHashes = new Set<ContentHash>()
165
+ for (const cacheRecord of entriesCache.values()) {
166
+ for (const hash of cacheRecord.contentHashes) {
167
+ referencedHashes.add(hash)
168
+ }
169
+ }
170
+ for (const hash of contentCache.keys()) {
171
+ if (!referencedHashes.has(hash)) {
172
+ contentCache.delete(hash)
173
+ }
174
+ }
175
+ }
176
+
177
+ const callGitAdapter = async <T>(operation: () => Promise<T>): Promise<T> => {
178
+ try {
179
+ return await operation()
180
+ } catch (err) {
181
+ if (err instanceof GitAdapterError) {
182
+ throw createError(err.message, err.code, {})
183
+ }
184
+ const message = err instanceof Error ? err.message : String(err)
185
+ throw createError(message, ErrorCode.INTERNAL_ERROR, {})
186
+ }
100
187
  }
101
188
 
102
189
  const getSchemaStringByRef = async (
@@ -114,16 +201,9 @@ const getSchemaStringByRef = async (
114
201
  return schemaCacheRecord
115
202
  }
116
203
 
117
- let schemaString
118
- try {
119
- schemaString = await context.gitAdapter.getSchema(ref)
120
- } catch (err) {
121
- if (err instanceof GitAdapterError) {
122
- throw createError(err.message, err.code, {})
123
- }
124
- const message = err instanceof Error ? err.message : String(err)
125
- throw createError(message, ErrorCode.INTERNAL_ERROR, {})
126
- }
204
+ const schemaString = await callGitAdapter(() =>
205
+ context.gitAdapter.getSchema(ref),
206
+ )
127
207
 
128
208
  schemaCache.set(ref, schemaString)
129
209
 
@@ -141,7 +221,8 @@ const getSchemaStringByRef = async (
141
221
  export const createCacheHandler = (
142
222
  cacheSize: number = MAX_CACHE_ENTRIES,
143
223
  ): RepositoryCacheHandler => {
144
- const entriesCache: EntriesCache = new Map<Ref, EntriesRecord>()
224
+ const entriesCache: EntriesCache = new Map<Ref, CommitCacheRecord>()
225
+ const contentCache: ContentCache = new Map<ContentHash, EntryContent>()
145
226
  const schemaCache: SchemaCache = new Map<Ref, string>()
146
227
  const inflightEntries: InflightEntries = new Map<
147
228
  Ref,
@@ -149,7 +230,13 @@ export const createCacheHandler = (
149
230
  >()
150
231
  return {
151
232
  getEntriesRecord: (...args) =>
152
- getEntriesRecordByRef(entriesCache, inflightEntries, cacheSize, ...args),
233
+ getEntriesRecordByRef(
234
+ entriesCache,
235
+ contentCache,
236
+ inflightEntries,
237
+ cacheSize,
238
+ ...args,
239
+ ),
153
240
  getSchema: (...args) =>
154
241
  getSchemaStringByRef(schemaCache, cacheSize, ...args),
155
242
  }