@salesforce/graphiti 10.10.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 (282) hide show
  1. package/AGENT_GUIDE.md +424 -0
  2. package/CHANGELOG.md +448 -0
  3. package/LICENSE.txt +82 -0
  4. package/README.md +204 -0
  5. package/TASK.md +249 -0
  6. package/dist/cli.d.ts +7 -0
  7. package/dist/cli.js +683 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/args.d.ts +13 -0
  10. package/dist/commands/args.js +207 -0
  11. package/dist/commands/args.js.map +1 -0
  12. package/dist/commands/build.d.ts +11 -0
  13. package/dist/commands/build.js +209 -0
  14. package/dist/commands/build.js.map +1 -0
  15. package/dist/commands/connect.d.ts +8 -0
  16. package/dist/commands/connect.js +55 -0
  17. package/dist/commands/connect.js.map +1 -0
  18. package/dist/commands/describe.d.ts +6 -0
  19. package/dist/commands/describe.js +229 -0
  20. package/dist/commands/describe.js.map +1 -0
  21. package/dist/commands/meta.d.ts +9 -0
  22. package/dist/commands/meta.js +140 -0
  23. package/dist/commands/meta.js.map +1 -0
  24. package/dist/commands/navigate.d.ts +14 -0
  25. package/dist/commands/navigate.js +105 -0
  26. package/dist/commands/navigate.js.map +1 -0
  27. package/dist/commands/query-helpers.d.ts +80 -0
  28. package/dist/commands/query-helpers.js +865 -0
  29. package/dist/commands/query-helpers.js.map +1 -0
  30. package/dist/commands/query.d.ts +26 -0
  31. package/dist/commands/query.js +901 -0
  32. package/dist/commands/query.js.map +1 -0
  33. package/dist/commands/review.d.ts +18 -0
  34. package/dist/commands/review.js +533 -0
  35. package/dist/commands/review.js.map +1 -0
  36. package/dist/commands/session-mgmt.d.ts +25 -0
  37. package/dist/commands/session-mgmt.js +206 -0
  38. package/dist/commands/session-mgmt.js.map +1 -0
  39. package/dist/commands/type.d.ts +6 -0
  40. package/dist/commands/type.js +342 -0
  41. package/dist/commands/type.js.map +1 -0
  42. package/dist/commands/validate-input.d.ts +6 -0
  43. package/dist/commands/validate-input.js +32 -0
  44. package/dist/commands/validate-input.js.map +1 -0
  45. package/dist/intent/build-aggregate.d.ts +33 -0
  46. package/dist/intent/build-aggregate.js +134 -0
  47. package/dist/intent/build-aggregate.js.map +1 -0
  48. package/dist/intent/build-create.d.ts +14 -0
  49. package/dist/intent/build-create.js +16 -0
  50. package/dist/intent/build-create.js.map +1 -0
  51. package/dist/intent/build-delete.d.ts +30 -0
  52. package/dist/intent/build-delete.js +53 -0
  53. package/dist/intent/build-delete.js.map +1 -0
  54. package/dist/intent/build-detail.d.ts +32 -0
  55. package/dist/intent/build-detail.js +80 -0
  56. package/dist/intent/build-detail.js.map +1 -0
  57. package/dist/intent/build-discover.d.ts +30 -0
  58. package/dist/intent/build-discover.js +149 -0
  59. package/dist/intent/build-discover.js.map +1 -0
  60. package/dist/intent/build-list.d.ts +28 -0
  61. package/dist/intent/build-list.js +75 -0
  62. package/dist/intent/build-list.js.map +1 -0
  63. package/dist/intent/build-mutation.d.ts +23 -0
  64. package/dist/intent/build-mutation.js +54 -0
  65. package/dist/intent/build-mutation.js.map +1 -0
  66. package/dist/intent/build-output.d.ts +27 -0
  67. package/dist/intent/build-output.js +60 -0
  68. package/dist/intent/build-output.js.map +1 -0
  69. package/dist/intent/build-raw.d.ts +23 -0
  70. package/dist/intent/build-raw.js +54 -0
  71. package/dist/intent/build-raw.js.map +1 -0
  72. package/dist/intent/build-update.d.ts +14 -0
  73. package/dist/intent/build-update.js +16 -0
  74. package/dist/intent/build-update.js.map +1 -0
  75. package/dist/intent/get-schema-with-priming.d.ts +26 -0
  76. package/dist/intent/get-schema-with-priming.js +32 -0
  77. package/dist/intent/get-schema-with-priming.js.map +1 -0
  78. package/dist/intent/select-child-relationship.d.ts +19 -0
  79. package/dist/intent/select-child-relationship.js +38 -0
  80. package/dist/intent/select-child-relationship.js.map +1 -0
  81. package/dist/intent/types.d.ts +159 -0
  82. package/dist/intent/types.js +21 -0
  83. package/dist/intent/types.js.map +1 -0
  84. package/dist/lib/apply-command.d.ts +15 -0
  85. package/dist/lib/apply-command.js +238 -0
  86. package/dist/lib/apply-command.js.map +1 -0
  87. package/dist/lib/auth.d.ts +38 -0
  88. package/dist/lib/auth.js +113 -0
  89. package/dist/lib/auth.js.map +1 -0
  90. package/dist/lib/codegen.d.ts +32 -0
  91. package/dist/lib/codegen.js +700 -0
  92. package/dist/lib/codegen.js.map +1 -0
  93. package/dist/lib/command-registry.d.ts +59 -0
  94. package/dist/lib/command-registry.js +366 -0
  95. package/dist/lib/command-registry.js.map +1 -0
  96. package/dist/lib/formatter.d.ts +76 -0
  97. package/dist/lib/formatter.js +419 -0
  98. package/dist/lib/formatter.js.map +1 -0
  99. package/dist/lib/fs-utils.d.ts +23 -0
  100. package/dist/lib/fs-utils.js +46 -0
  101. package/dist/lib/fs-utils.js.map +1 -0
  102. package/dist/lib/graphql-name.d.ts +27 -0
  103. package/dist/lib/graphql-name.js +32 -0
  104. package/dist/lib/graphql-name.js.map +1 -0
  105. package/dist/lib/interactive.d.ts +6 -0
  106. package/dist/lib/interactive.js +562 -0
  107. package/dist/lib/interactive.js.map +1 -0
  108. package/dist/lib/introspect.d.ts +40 -0
  109. package/dist/lib/introspect.js +280 -0
  110. package/dist/lib/introspect.js.map +1 -0
  111. package/dist/lib/object-info.d.ts +79 -0
  112. package/dist/lib/object-info.js +313 -0
  113. package/dist/lib/object-info.js.map +1 -0
  114. package/dist/lib/path-selection.d.ts +50 -0
  115. package/dist/lib/path-selection.js +146 -0
  116. package/dist/lib/path-selection.js.map +1 -0
  117. package/dist/lib/prime-schema.d.ts +59 -0
  118. package/dist/lib/prime-schema.js +158 -0
  119. package/dist/lib/prime-schema.js.map +1 -0
  120. package/dist/lib/query-builder.d.ts +10 -0
  121. package/dist/lib/query-builder.js +168 -0
  122. package/dist/lib/query-builder.js.map +1 -0
  123. package/dist/lib/query-commands.d.ts +19 -0
  124. package/dist/lib/query-commands.js +262 -0
  125. package/dist/lib/query-commands.js.map +1 -0
  126. package/dist/lib/session.d.ts +205 -0
  127. package/dist/lib/session.js +1075 -0
  128. package/dist/lib/session.js.map +1 -0
  129. package/dist/lib/tokenize.d.ts +12 -0
  130. package/dist/lib/tokenize.js +48 -0
  131. package/dist/lib/tokenize.js.map +1 -0
  132. package/dist/lib/uiapi.d.ts +109 -0
  133. package/dist/lib/uiapi.js +159 -0
  134. package/dist/lib/uiapi.js.map +1 -0
  135. package/dist/lib/validator.d.ts +27 -0
  136. package/dist/lib/validator.js +100 -0
  137. package/dist/lib/validator.js.map +1 -0
  138. package/dist/lib/variable-promotion.d.ts +69 -0
  139. package/dist/lib/variable-promotion.js +95 -0
  140. package/dist/lib/variable-promotion.js.map +1 -0
  141. package/dist/lib/walker.d.ts +147 -0
  142. package/dist/lib/walker.js +723 -0
  143. package/dist/lib/walker.js.map +1 -0
  144. package/dist/mcp/server.d.ts +7 -0
  145. package/dist/mcp/server.js +34 -0
  146. package/dist/mcp/server.js.map +1 -0
  147. package/dist/mcp/stdio.d.ts +7 -0
  148. package/dist/mcp/stdio.js +25 -0
  149. package/dist/mcp/stdio.js.map +1 -0
  150. package/dist/mcp/tools/echo.d.ts +7 -0
  151. package/dist/mcp/tools/echo.js +17 -0
  152. package/dist/mcp/tools/echo.js.map +1 -0
  153. package/dist/mcp/tools/sf-gql-aggregate.d.ts +11 -0
  154. package/dist/mcp/tools/sf-gql-aggregate.js +75 -0
  155. package/dist/mcp/tools/sf-gql-aggregate.js.map +1 -0
  156. package/dist/mcp/tools/sf-gql-create.d.ts +11 -0
  157. package/dist/mcp/tools/sf-gql-create.js +35 -0
  158. package/dist/mcp/tools/sf-gql-create.js.map +1 -0
  159. package/dist/mcp/tools/sf-gql-delete.d.ts +11 -0
  160. package/dist/mcp/tools/sf-gql-delete.js +31 -0
  161. package/dist/mcp/tools/sf-gql-delete.js.map +1 -0
  162. package/dist/mcp/tools/sf-gql-detail.d.ts +11 -0
  163. package/dist/mcp/tools/sf-gql-detail.js +58 -0
  164. package/dist/mcp/tools/sf-gql-detail.js.map +1 -0
  165. package/dist/mcp/tools/sf-gql-discover.d.ts +9 -0
  166. package/dist/mcp/tools/sf-gql-discover.js +51 -0
  167. package/dist/mcp/tools/sf-gql-discover.js.map +1 -0
  168. package/dist/mcp/tools/sf-gql-list.d.ts +11 -0
  169. package/dist/mcp/tools/sf-gql-list.js +53 -0
  170. package/dist/mcp/tools/sf-gql-list.js.map +1 -0
  171. package/dist/mcp/tools/sf-gql-raw.d.ts +11 -0
  172. package/dist/mcp/tools/sf-gql-raw.js +39 -0
  173. package/dist/mcp/tools/sf-gql-raw.js.map +1 -0
  174. package/dist/mcp/tools/sf-gql-update.d.ts +11 -0
  175. package/dist/mcp/tools/sf-gql-update.js +35 -0
  176. package/dist/mcp/tools/sf-gql-update.js.map +1 -0
  177. package/dist/mcp/tools/shared/zod-schemas.d.ts +49 -0
  178. package/dist/mcp/tools/shared/zod-schemas.js +46 -0
  179. package/dist/mcp/tools/shared/zod-schemas.js.map +1 -0
  180. package/package.json +36 -0
  181. package/ralph-loop.sh +120 -0
  182. package/scripts/smoke-orderby.sh +190 -0
  183. package/src/__tests__/helpers/prime-deps.ts +46 -0
  184. package/src/__tests__/helpers/schema.ts +73 -0
  185. package/src/__tests__/helpers/stdout.ts +33 -0
  186. package/src/__tests__/setup.ts +19 -0
  187. package/src/cli.ts +764 -0
  188. package/src/commands/__tests__/query.spec.ts +137 -0
  189. package/src/commands/args.ts +306 -0
  190. package/src/commands/build.ts +288 -0
  191. package/src/commands/connect.ts +60 -0
  192. package/src/commands/describe.ts +246 -0
  193. package/src/commands/meta.ts +171 -0
  194. package/src/commands/navigate.ts +134 -0
  195. package/src/commands/query-helpers.ts +1202 -0
  196. package/src/commands/query.ts +1085 -0
  197. package/src/commands/review.ts +670 -0
  198. package/src/commands/session-mgmt.ts +256 -0
  199. package/src/commands/type.ts +437 -0
  200. package/src/commands/validate-input.ts +38 -0
  201. package/src/intent/__tests__/build-aggregate.spec.ts +931 -0
  202. package/src/intent/__tests__/build-create-validation.spec.ts +135 -0
  203. package/src/intent/__tests__/build-delete.spec.ts +121 -0
  204. package/src/intent/__tests__/build-detail.spec.ts +333 -0
  205. package/src/intent/__tests__/build-discover.spec.ts +432 -0
  206. package/src/intent/__tests__/build-list.spec.ts +284 -0
  207. package/src/intent/__tests__/build-mutation.spec.ts +108 -0
  208. package/src/intent/__tests__/build-output.spec.ts +55 -0
  209. package/src/intent/__tests__/build-raw.spec.ts +153 -0
  210. package/src/intent/__tests__/build-update-validation.spec.ts +134 -0
  211. package/src/intent/build-aggregate.ts +182 -0
  212. package/src/intent/build-create.ts +19 -0
  213. package/src/intent/build-delete.ts +62 -0
  214. package/src/intent/build-detail.ts +95 -0
  215. package/src/intent/build-discover.ts +199 -0
  216. package/src/intent/build-list.ts +91 -0
  217. package/src/intent/build-mutation.ts +75 -0
  218. package/src/intent/build-output.ts +72 -0
  219. package/src/intent/build-raw.ts +61 -0
  220. package/src/intent/build-update.ts +19 -0
  221. package/src/intent/get-schema-with-priming.ts +43 -0
  222. package/src/intent/select-child-relationship.ts +48 -0
  223. package/src/intent/types.ts +181 -0
  224. package/src/lib/__tests__/apply-command.parity.spec.ts +97 -0
  225. package/src/lib/__tests__/apply-command.spec.ts +171 -0
  226. package/src/lib/__tests__/auth.spec.ts +292 -0
  227. package/src/lib/__tests__/formatter.spec.ts +86 -0
  228. package/src/lib/__tests__/graphql-name.spec.ts +64 -0
  229. package/src/lib/__tests__/introspect.spec.ts +399 -0
  230. package/src/lib/__tests__/object-info.spec.ts +124 -0
  231. package/src/lib/__tests__/path-selection.spec.ts +219 -0
  232. package/src/lib/__tests__/prime-schema.spec.ts +269 -0
  233. package/src/lib/__tests__/query-builder.spec.ts +95 -0
  234. package/src/lib/__tests__/query-commands.spec.ts +74 -0
  235. package/src/lib/__tests__/session.spec.ts +292 -0
  236. package/src/lib/__tests__/tokenize.spec.ts +33 -0
  237. package/src/lib/__tests__/uiapi.spec.ts +192 -0
  238. package/src/lib/__tests__/variable-promotion.spec.ts +211 -0
  239. package/src/lib/__tests__/walker.spec.ts +250 -0
  240. package/src/lib/apply-command.ts +286 -0
  241. package/src/lib/auth.ts +157 -0
  242. package/src/lib/codegen.ts +899 -0
  243. package/src/lib/command-registry.ts +434 -0
  244. package/src/lib/formatter.ts +587 -0
  245. package/src/lib/fs-utils.ts +46 -0
  246. package/src/lib/graphql-name.ts +35 -0
  247. package/src/lib/interactive.ts +634 -0
  248. package/src/lib/introspect.ts +320 -0
  249. package/src/lib/object-info.ts +409 -0
  250. package/src/lib/path-selection.ts +162 -0
  251. package/src/lib/prime-schema.ts +195 -0
  252. package/src/lib/query-builder.ts +193 -0
  253. package/src/lib/query-commands.ts +290 -0
  254. package/src/lib/session.ts +1304 -0
  255. package/src/lib/tokenize.ts +43 -0
  256. package/src/lib/uiapi.ts +176 -0
  257. package/src/lib/validator.ts +143 -0
  258. package/src/lib/variable-promotion.ts +145 -0
  259. package/src/lib/walker.ts +975 -0
  260. package/src/mcp/__tests__/server.spec.ts +155 -0
  261. package/src/mcp/server.ts +38 -0
  262. package/src/mcp/stdio.ts +29 -0
  263. package/src/mcp/tools/__tests__/sf-gql-aggregate.spec.ts +173 -0
  264. package/src/mcp/tools/__tests__/sf-gql-create.spec.ts +235 -0
  265. package/src/mcp/tools/__tests__/sf-gql-delete.spec.ts +194 -0
  266. package/src/mcp/tools/__tests__/sf-gql-detail.spec.ts +246 -0
  267. package/src/mcp/tools/__tests__/sf-gql-discover.spec.ts +320 -0
  268. package/src/mcp/tools/__tests__/sf-gql-list.spec.ts +128 -0
  269. package/src/mcp/tools/__tests__/sf-gql-raw.spec.ts +141 -0
  270. package/src/mcp/tools/__tests__/sf-gql-update.spec.ts +207 -0
  271. package/src/mcp/tools/echo.ts +24 -0
  272. package/src/mcp/tools/sf-gql-aggregate.ts +102 -0
  273. package/src/mcp/tools/sf-gql-create.ts +55 -0
  274. package/src/mcp/tools/sf-gql-delete.ts +49 -0
  275. package/src/mcp/tools/sf-gql-detail.ts +85 -0
  276. package/src/mcp/tools/sf-gql-discover.ts +67 -0
  277. package/src/mcp/tools/sf-gql-list.ts +73 -0
  278. package/src/mcp/tools/sf-gql-raw.ts +56 -0
  279. package/src/mcp/tools/sf-gql-update.ts +55 -0
  280. package/src/mcp/tools/shared/zod-schemas.ts +55 -0
  281. package/tsconfig.json +18 -0
  282. package/vitest.config.ts +14 -0
@@ -0,0 +1,320 @@
1
+ /**
2
+ * Copyright (c) 2026, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+ /* eslint-disable @typescript-eslint/no-explicit-any -- graphiti traverses untyped schema/introspection JSON; see follow-up to replace with `unknown` + narrowing */
7
+
8
+ import crypto from "crypto";
9
+ import fs from "fs";
10
+ import path from "path";
11
+ import { Org, type Connection } from "@salesforce/core";
12
+ import type { OrgAuth } from "./auth.js";
13
+ import { atomicWriteJson, graphitiHome } from "./fs-utils.js";
14
+
15
+ // Re-export for backward compatibility with existing graphiti consumers.
16
+ export { graphitiHome };
17
+
18
+ /** API version used for display-only endpoints where no live connection is available
19
+ * to query the org's actual version (live requests use connection.getApiVersion()). */
20
+ export const DEFAULT_API_VERSION = "67.0";
21
+
22
+ async function getConnection(auth: OrgAuth): Promise<Connection> {
23
+ const org = await Org.create({ aliasOrUsername: auth.alias });
24
+ return org.getConnection();
25
+ }
26
+
27
+ /** Resolves the directory holding cached introspection JSON files. */
28
+ export function schemaDir(): string {
29
+ return path.join(graphitiHome(), "schemas");
30
+ }
31
+
32
+ const INTROSPECTION_QUERY = `
33
+ query IntrospectionQuery {
34
+ __schema {
35
+ queryType { name }
36
+ mutationType { name }
37
+ types {
38
+ kind
39
+ name
40
+ description
41
+ fields(includeDeprecated: false) {
42
+ name
43
+ description
44
+ args {
45
+ name
46
+ description
47
+ type { ...TypeRef }
48
+ defaultValue
49
+ }
50
+ type { ...TypeRef }
51
+ }
52
+ inputFields {
53
+ name
54
+ description
55
+ type { ...TypeRef }
56
+ defaultValue
57
+ }
58
+ interfaces { ...TypeRef }
59
+ enumValues(includeDeprecated: false) {
60
+ name
61
+ description
62
+ }
63
+ possibleTypes { ...TypeRef }
64
+ }
65
+ directives {
66
+ name
67
+ description
68
+ locations
69
+ args {
70
+ name
71
+ description
72
+ type { ...TypeRef }
73
+ defaultValue
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ fragment TypeRef on __Type {
80
+ kind
81
+ name
82
+ ofType {
83
+ kind
84
+ name
85
+ ofType {
86
+ kind
87
+ name
88
+ ofType {
89
+ kind
90
+ name
91
+ ofType {
92
+ kind
93
+ name
94
+ ofType {
95
+ kind
96
+ name
97
+ ofType {
98
+ kind
99
+ name
100
+ }
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }`;
107
+
108
+ // ── DLM/SSOT stripping ───────────────────────────────────────────────────────
109
+
110
+ const DLM_SSOT_RE = /__dlm/i;
111
+
112
+ function getNamedTypeNameFromRef(typeRef: any): string | null {
113
+ if (!typeRef) return null;
114
+ if (typeRef.name) return typeRef.name;
115
+ return getNamedTypeNameFromRef(typeRef.ofType);
116
+ }
117
+
118
+ /**
119
+ * Strips Data Cloud (DLM/SSOT) types and their field references from a raw
120
+ * introspection result. Matches type names containing `__dlm` (case-insensitive)
121
+ * or starting with `ssot__`. Returns the mutated result and a count of removed types.
122
+ */
123
+ export function stripDataCloudTypes(raw: any): { result: any; removedCount: number } {
124
+ const schema = raw?.data?.__schema ?? raw?.__schema;
125
+ if (!schema?.types) return { result: raw, removedCount: 0 };
126
+
127
+ const removedNames = new Set<string>();
128
+ for (const type of schema.types) {
129
+ if (type.name && (DLM_SSOT_RE.test(type.name) || type.name.startsWith("ssot__"))) {
130
+ removedNames.add(type.name);
131
+ }
132
+ }
133
+
134
+ if (removedNames.size === 0) return { result: raw, removedCount: 0 };
135
+
136
+ const isRemoved = (ref: any) => {
137
+ const name = getNamedTypeNameFromRef(ref);
138
+ return name != null && removedNames.has(name);
139
+ };
140
+
141
+ schema.types = schema.types.filter((t: any) => !removedNames.has(t.name));
142
+
143
+ for (const type of schema.types) {
144
+ if (type.fields) {
145
+ type.fields = type.fields.filter((f: any) => !isRemoved(f.type));
146
+ }
147
+ if (type.inputFields) {
148
+ type.inputFields = type.inputFields.filter((f: any) => !isRemoved(f.type));
149
+ }
150
+ if (type.possibleTypes) {
151
+ type.possibleTypes = type.possibleTypes.filter(
152
+ (t: any) => !t.name || !removedNames.has(t.name),
153
+ );
154
+ }
155
+ if (type.interfaces) {
156
+ type.interfaces = type.interfaces.filter((t: any) => !t.name || !removedNames.has(t.name));
157
+ }
158
+ }
159
+
160
+ return { result: raw, removedCount: removedNames.size };
161
+ }
162
+
163
+ function _schemaPath(alias: string): string {
164
+ return path.join(schemaDir(), `${alias}.json`);
165
+ }
166
+
167
+ export function normalizeInstanceUrl(instanceUrl: string): string {
168
+ return instanceUrl.replace(/\/+$/, "").toLowerCase();
169
+ }
170
+
171
+ export function schemaCacheKeyForInstanceUrl(instanceUrl: string): string {
172
+ const normalized = normalizeInstanceUrl(instanceUrl);
173
+ return crypto.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
174
+ }
175
+
176
+ function schemaPathForInstanceUrl(instanceUrl: string): string {
177
+ const cacheKey = schemaCacheKeyForInstanceUrl(instanceUrl);
178
+ return path.join(schemaDir(), `${cacheKey}.json`);
179
+ }
180
+
181
+ export function schemaExists(instanceUrl: string): boolean {
182
+ return fs.existsSync(schemaPathForInstanceUrl(normalizeInstanceUrl(instanceUrl)));
183
+ }
184
+
185
+ export interface SchemaMetadata {
186
+ cacheKey: string;
187
+ instanceUrl: string;
188
+ typeCount: number;
189
+ downloadedAt: string;
190
+ filePath: string;
191
+ strippedDataCloudTypes?: number;
192
+ }
193
+
194
+ export function getSchemaMetadata(instanceUrl: string): SchemaMetadata | null {
195
+ const normalized = normalizeInstanceUrl(instanceUrl);
196
+ const fp = schemaPathForInstanceUrl(normalized);
197
+ if (!fs.existsSync(fp)) return null;
198
+ try {
199
+ const raw = JSON.parse(fs.readFileSync(fp, "utf-8"));
200
+ const types = (raw?.data?.__schema?.types ?? raw?.__schema?.types ?? []) as any[];
201
+ const stat = fs.statSync(fp);
202
+ return {
203
+ cacheKey: schemaCacheKeyForInstanceUrl(normalized),
204
+ instanceUrl: normalized,
205
+ typeCount: types.filter((t: any) => !t.name.startsWith("__")).length,
206
+ downloadedAt: stat.mtime.toISOString(),
207
+ filePath: fp,
208
+ };
209
+ } catch {
210
+ return null;
211
+ }
212
+ }
213
+
214
+ export function listCachedSchemas(): SchemaMetadata[] {
215
+ const dir = schemaDir();
216
+ if (!fs.existsSync(dir)) return [];
217
+ return fs
218
+ .readdirSync(dir)
219
+ .filter((f) => f.endsWith(".json"))
220
+ .map((fileName) => {
221
+ try {
222
+ const fp = path.join(dir, fileName);
223
+ const raw = JSON.parse(fs.readFileSync(fp, "utf-8"));
224
+ const types = (raw?.data?.__schema?.types ?? raw?.__schema?.types ?? []) as any[];
225
+ const stat = fs.statSync(fp);
226
+ const instanceUrl = raw?.__graphiti?.instanceUrl;
227
+ if (typeof instanceUrl !== "string") return null;
228
+ return {
229
+ cacheKey: fileName.replace(".json", ""),
230
+ instanceUrl,
231
+ typeCount: types.filter((t: any) => !t.name.startsWith("__")).length,
232
+ downloadedAt: stat.mtime.toISOString(),
233
+ filePath: fp,
234
+ };
235
+ } catch {
236
+ return null;
237
+ }
238
+ })
239
+ .filter((m): m is SchemaMetadata => m !== null);
240
+ }
241
+
242
+ export async function downloadSchema(auth: OrgAuth): Promise<SchemaMetadata> {
243
+ const connection = await getConnection(auth);
244
+ const url = `${auth.instanceUrl}/services/data/v${connection.getApiVersion()}/graphql`;
245
+
246
+ const rawResult = (await connection.request({
247
+ method: "POST",
248
+ url,
249
+ body: JSON.stringify({ query: INTROSPECTION_QUERY }),
250
+ headers: { "Content-Type": "application/json", "X-Chatter-Entity-Encoding": "false" },
251
+ })) as any;
252
+
253
+ if (rawResult?.errors?.length) {
254
+ const messages = (rawResult.errors as any[])
255
+ .map((e: any) => e.message ?? JSON.stringify(e))
256
+ .join("\n ");
257
+ throw new Error(`Introspection query returned errors:\n ${messages}`);
258
+ }
259
+
260
+ const rawSchema = rawResult?.data?.__schema ?? rawResult?.__schema;
261
+ if (!rawSchema) {
262
+ throw new Error("Introspection query did not return a __schema field");
263
+ }
264
+
265
+ const { result, removedCount } = stripDataCloudTypes(rawResult);
266
+ const schema = result?.data?.__schema ?? result?.__schema;
267
+
268
+ const instanceUrl = normalizeInstanceUrl(auth.instanceUrl);
269
+ const filePath = schemaPathForInstanceUrl(instanceUrl);
270
+ const payload = {
271
+ ...result,
272
+ __graphiti: {
273
+ instanceUrl,
274
+ alias: auth.alias,
275
+ cachedAt: new Date().toISOString(),
276
+ cacheKey: schemaCacheKeyForInstanceUrl(instanceUrl),
277
+ strippedDataCloudTypes: removedCount,
278
+ },
279
+ };
280
+ atomicWriteJson(filePath, payload);
281
+
282
+ const typeCount = (schema.types as any[]).filter((t: any) => !t.name.startsWith("__")).length;
283
+
284
+ return {
285
+ cacheKey: schemaCacheKeyForInstanceUrl(instanceUrl),
286
+ instanceUrl,
287
+ typeCount,
288
+ downloadedAt: new Date().toISOString(),
289
+ filePath,
290
+ strippedDataCloudTypes: removedCount || undefined,
291
+ };
292
+ }
293
+
294
+ export function loadIntrospectionResult(instanceUrl: string): any {
295
+ const fp = schemaPathForInstanceUrl(normalizeInstanceUrl(instanceUrl));
296
+ if (!fs.existsSync(fp)) {
297
+ throw new Error(`No cached schema for "${instanceUrl}". Run \`graphiti connect <org>\` first.`);
298
+ }
299
+ return JSON.parse(fs.readFileSync(fp, "utf-8"));
300
+ }
301
+
302
+ /** Returns the absolute path to the cached introspection JSON file. */
303
+ export function getSchemaFilePath(instanceUrl: string): string {
304
+ return schemaPathForInstanceUrl(normalizeInstanceUrl(instanceUrl));
305
+ }
306
+
307
+ export async function executeGraphQL(
308
+ auth: OrgAuth,
309
+ query: string,
310
+ variables?: Record<string, unknown>,
311
+ ): Promise<any> {
312
+ const connection = await getConnection(auth);
313
+ const url = `${auth.instanceUrl}/services/data/v${connection.getApiVersion()}/graphql`;
314
+ return connection.request({
315
+ method: "POST",
316
+ url,
317
+ body: JSON.stringify({ query, variables }),
318
+ headers: { "Content-Type": "application/json", "X-Chatter-Entity-Encoding": "false" },
319
+ });
320
+ }