@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.
- package/AGENT_GUIDE.md +424 -0
- package/CHANGELOG.md +448 -0
- package/LICENSE.txt +82 -0
- package/README.md +204 -0
- package/TASK.md +249 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.js +683 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/args.d.ts +13 -0
- package/dist/commands/args.js +207 -0
- package/dist/commands/args.js.map +1 -0
- package/dist/commands/build.d.ts +11 -0
- package/dist/commands/build.js +209 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/connect.d.ts +8 -0
- package/dist/commands/connect.js +55 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/describe.d.ts +6 -0
- package/dist/commands/describe.js +229 -0
- package/dist/commands/describe.js.map +1 -0
- package/dist/commands/meta.d.ts +9 -0
- package/dist/commands/meta.js +140 -0
- package/dist/commands/meta.js.map +1 -0
- package/dist/commands/navigate.d.ts +14 -0
- package/dist/commands/navigate.js +105 -0
- package/dist/commands/navigate.js.map +1 -0
- package/dist/commands/query-helpers.d.ts +80 -0
- package/dist/commands/query-helpers.js +865 -0
- package/dist/commands/query-helpers.js.map +1 -0
- package/dist/commands/query.d.ts +26 -0
- package/dist/commands/query.js +901 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/commands/review.d.ts +18 -0
- package/dist/commands/review.js +533 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/session-mgmt.d.ts +25 -0
- package/dist/commands/session-mgmt.js +206 -0
- package/dist/commands/session-mgmt.js.map +1 -0
- package/dist/commands/type.d.ts +6 -0
- package/dist/commands/type.js +342 -0
- package/dist/commands/type.js.map +1 -0
- package/dist/commands/validate-input.d.ts +6 -0
- package/dist/commands/validate-input.js +32 -0
- package/dist/commands/validate-input.js.map +1 -0
- package/dist/intent/build-aggregate.d.ts +33 -0
- package/dist/intent/build-aggregate.js +134 -0
- package/dist/intent/build-aggregate.js.map +1 -0
- package/dist/intent/build-create.d.ts +14 -0
- package/dist/intent/build-create.js +16 -0
- package/dist/intent/build-create.js.map +1 -0
- package/dist/intent/build-delete.d.ts +30 -0
- package/dist/intent/build-delete.js +53 -0
- package/dist/intent/build-delete.js.map +1 -0
- package/dist/intent/build-detail.d.ts +32 -0
- package/dist/intent/build-detail.js +80 -0
- package/dist/intent/build-detail.js.map +1 -0
- package/dist/intent/build-discover.d.ts +30 -0
- package/dist/intent/build-discover.js +149 -0
- package/dist/intent/build-discover.js.map +1 -0
- package/dist/intent/build-list.d.ts +28 -0
- package/dist/intent/build-list.js +75 -0
- package/dist/intent/build-list.js.map +1 -0
- package/dist/intent/build-mutation.d.ts +23 -0
- package/dist/intent/build-mutation.js +54 -0
- package/dist/intent/build-mutation.js.map +1 -0
- package/dist/intent/build-output.d.ts +27 -0
- package/dist/intent/build-output.js +60 -0
- package/dist/intent/build-output.js.map +1 -0
- package/dist/intent/build-raw.d.ts +23 -0
- package/dist/intent/build-raw.js +54 -0
- package/dist/intent/build-raw.js.map +1 -0
- package/dist/intent/build-update.d.ts +14 -0
- package/dist/intent/build-update.js +16 -0
- package/dist/intent/build-update.js.map +1 -0
- package/dist/intent/get-schema-with-priming.d.ts +26 -0
- package/dist/intent/get-schema-with-priming.js +32 -0
- package/dist/intent/get-schema-with-priming.js.map +1 -0
- package/dist/intent/select-child-relationship.d.ts +19 -0
- package/dist/intent/select-child-relationship.js +38 -0
- package/dist/intent/select-child-relationship.js.map +1 -0
- package/dist/intent/types.d.ts +159 -0
- package/dist/intent/types.js +21 -0
- package/dist/intent/types.js.map +1 -0
- package/dist/lib/apply-command.d.ts +15 -0
- package/dist/lib/apply-command.js +238 -0
- package/dist/lib/apply-command.js.map +1 -0
- package/dist/lib/auth.d.ts +38 -0
- package/dist/lib/auth.js +113 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/codegen.d.ts +32 -0
- package/dist/lib/codegen.js +700 -0
- package/dist/lib/codegen.js.map +1 -0
- package/dist/lib/command-registry.d.ts +59 -0
- package/dist/lib/command-registry.js +366 -0
- package/dist/lib/command-registry.js.map +1 -0
- package/dist/lib/formatter.d.ts +76 -0
- package/dist/lib/formatter.js +419 -0
- package/dist/lib/formatter.js.map +1 -0
- package/dist/lib/fs-utils.d.ts +23 -0
- package/dist/lib/fs-utils.js +46 -0
- package/dist/lib/fs-utils.js.map +1 -0
- package/dist/lib/graphql-name.d.ts +27 -0
- package/dist/lib/graphql-name.js +32 -0
- package/dist/lib/graphql-name.js.map +1 -0
- package/dist/lib/interactive.d.ts +6 -0
- package/dist/lib/interactive.js +562 -0
- package/dist/lib/interactive.js.map +1 -0
- package/dist/lib/introspect.d.ts +40 -0
- package/dist/lib/introspect.js +280 -0
- package/dist/lib/introspect.js.map +1 -0
- package/dist/lib/object-info.d.ts +79 -0
- package/dist/lib/object-info.js +313 -0
- package/dist/lib/object-info.js.map +1 -0
- package/dist/lib/path-selection.d.ts +50 -0
- package/dist/lib/path-selection.js +146 -0
- package/dist/lib/path-selection.js.map +1 -0
- package/dist/lib/prime-schema.d.ts +59 -0
- package/dist/lib/prime-schema.js +158 -0
- package/dist/lib/prime-schema.js.map +1 -0
- package/dist/lib/query-builder.d.ts +10 -0
- package/dist/lib/query-builder.js +168 -0
- package/dist/lib/query-builder.js.map +1 -0
- package/dist/lib/query-commands.d.ts +19 -0
- package/dist/lib/query-commands.js +262 -0
- package/dist/lib/query-commands.js.map +1 -0
- package/dist/lib/session.d.ts +205 -0
- package/dist/lib/session.js +1075 -0
- package/dist/lib/session.js.map +1 -0
- package/dist/lib/tokenize.d.ts +12 -0
- package/dist/lib/tokenize.js +48 -0
- package/dist/lib/tokenize.js.map +1 -0
- package/dist/lib/uiapi.d.ts +109 -0
- package/dist/lib/uiapi.js +159 -0
- package/dist/lib/uiapi.js.map +1 -0
- package/dist/lib/validator.d.ts +27 -0
- package/dist/lib/validator.js +100 -0
- package/dist/lib/validator.js.map +1 -0
- package/dist/lib/variable-promotion.d.ts +69 -0
- package/dist/lib/variable-promotion.js +95 -0
- package/dist/lib/variable-promotion.js.map +1 -0
- package/dist/lib/walker.d.ts +147 -0
- package/dist/lib/walker.js +723 -0
- package/dist/lib/walker.js.map +1 -0
- package/dist/mcp/server.d.ts +7 -0
- package/dist/mcp/server.js +34 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/stdio.d.ts +7 -0
- package/dist/mcp/stdio.js +25 -0
- package/dist/mcp/stdio.js.map +1 -0
- package/dist/mcp/tools/echo.d.ts +7 -0
- package/dist/mcp/tools/echo.js +17 -0
- package/dist/mcp/tools/echo.js.map +1 -0
- package/dist/mcp/tools/sf-gql-aggregate.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-aggregate.js +75 -0
- package/dist/mcp/tools/sf-gql-aggregate.js.map +1 -0
- package/dist/mcp/tools/sf-gql-create.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-create.js +35 -0
- package/dist/mcp/tools/sf-gql-create.js.map +1 -0
- package/dist/mcp/tools/sf-gql-delete.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-delete.js +31 -0
- package/dist/mcp/tools/sf-gql-delete.js.map +1 -0
- package/dist/mcp/tools/sf-gql-detail.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-detail.js +58 -0
- package/dist/mcp/tools/sf-gql-detail.js.map +1 -0
- package/dist/mcp/tools/sf-gql-discover.d.ts +9 -0
- package/dist/mcp/tools/sf-gql-discover.js +51 -0
- package/dist/mcp/tools/sf-gql-discover.js.map +1 -0
- package/dist/mcp/tools/sf-gql-list.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-list.js +53 -0
- package/dist/mcp/tools/sf-gql-list.js.map +1 -0
- package/dist/mcp/tools/sf-gql-raw.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-raw.js +39 -0
- package/dist/mcp/tools/sf-gql-raw.js.map +1 -0
- package/dist/mcp/tools/sf-gql-update.d.ts +11 -0
- package/dist/mcp/tools/sf-gql-update.js +35 -0
- package/dist/mcp/tools/sf-gql-update.js.map +1 -0
- package/dist/mcp/tools/shared/zod-schemas.d.ts +49 -0
- package/dist/mcp/tools/shared/zod-schemas.js +46 -0
- package/dist/mcp/tools/shared/zod-schemas.js.map +1 -0
- package/package.json +36 -0
- package/ralph-loop.sh +120 -0
- package/scripts/smoke-orderby.sh +190 -0
- package/src/__tests__/helpers/prime-deps.ts +46 -0
- package/src/__tests__/helpers/schema.ts +73 -0
- package/src/__tests__/helpers/stdout.ts +33 -0
- package/src/__tests__/setup.ts +19 -0
- package/src/cli.ts +764 -0
- package/src/commands/__tests__/query.spec.ts +137 -0
- package/src/commands/args.ts +306 -0
- package/src/commands/build.ts +288 -0
- package/src/commands/connect.ts +60 -0
- package/src/commands/describe.ts +246 -0
- package/src/commands/meta.ts +171 -0
- package/src/commands/navigate.ts +134 -0
- package/src/commands/query-helpers.ts +1202 -0
- package/src/commands/query.ts +1085 -0
- package/src/commands/review.ts +670 -0
- package/src/commands/session-mgmt.ts +256 -0
- package/src/commands/type.ts +437 -0
- package/src/commands/validate-input.ts +38 -0
- package/src/intent/__tests__/build-aggregate.spec.ts +931 -0
- package/src/intent/__tests__/build-create-validation.spec.ts +135 -0
- package/src/intent/__tests__/build-delete.spec.ts +121 -0
- package/src/intent/__tests__/build-detail.spec.ts +333 -0
- package/src/intent/__tests__/build-discover.spec.ts +432 -0
- package/src/intent/__tests__/build-list.spec.ts +284 -0
- package/src/intent/__tests__/build-mutation.spec.ts +108 -0
- package/src/intent/__tests__/build-output.spec.ts +55 -0
- package/src/intent/__tests__/build-raw.spec.ts +153 -0
- package/src/intent/__tests__/build-update-validation.spec.ts +134 -0
- package/src/intent/build-aggregate.ts +182 -0
- package/src/intent/build-create.ts +19 -0
- package/src/intent/build-delete.ts +62 -0
- package/src/intent/build-detail.ts +95 -0
- package/src/intent/build-discover.ts +199 -0
- package/src/intent/build-list.ts +91 -0
- package/src/intent/build-mutation.ts +75 -0
- package/src/intent/build-output.ts +72 -0
- package/src/intent/build-raw.ts +61 -0
- package/src/intent/build-update.ts +19 -0
- package/src/intent/get-schema-with-priming.ts +43 -0
- package/src/intent/select-child-relationship.ts +48 -0
- package/src/intent/types.ts +181 -0
- package/src/lib/__tests__/apply-command.parity.spec.ts +97 -0
- package/src/lib/__tests__/apply-command.spec.ts +171 -0
- package/src/lib/__tests__/auth.spec.ts +292 -0
- package/src/lib/__tests__/formatter.spec.ts +86 -0
- package/src/lib/__tests__/graphql-name.spec.ts +64 -0
- package/src/lib/__tests__/introspect.spec.ts +399 -0
- package/src/lib/__tests__/object-info.spec.ts +124 -0
- package/src/lib/__tests__/path-selection.spec.ts +219 -0
- package/src/lib/__tests__/prime-schema.spec.ts +269 -0
- package/src/lib/__tests__/query-builder.spec.ts +95 -0
- package/src/lib/__tests__/query-commands.spec.ts +74 -0
- package/src/lib/__tests__/session.spec.ts +292 -0
- package/src/lib/__tests__/tokenize.spec.ts +33 -0
- package/src/lib/__tests__/uiapi.spec.ts +192 -0
- package/src/lib/__tests__/variable-promotion.spec.ts +211 -0
- package/src/lib/__tests__/walker.spec.ts +250 -0
- package/src/lib/apply-command.ts +286 -0
- package/src/lib/auth.ts +157 -0
- package/src/lib/codegen.ts +899 -0
- package/src/lib/command-registry.ts +434 -0
- package/src/lib/formatter.ts +587 -0
- package/src/lib/fs-utils.ts +46 -0
- package/src/lib/graphql-name.ts +35 -0
- package/src/lib/interactive.ts +634 -0
- package/src/lib/introspect.ts +320 -0
- package/src/lib/object-info.ts +409 -0
- package/src/lib/path-selection.ts +162 -0
- package/src/lib/prime-schema.ts +195 -0
- package/src/lib/query-builder.ts +193 -0
- package/src/lib/query-commands.ts +290 -0
- package/src/lib/session.ts +1304 -0
- package/src/lib/tokenize.ts +43 -0
- package/src/lib/uiapi.ts +176 -0
- package/src/lib/validator.ts +143 -0
- package/src/lib/variable-promotion.ts +145 -0
- package/src/lib/walker.ts +975 -0
- package/src/mcp/__tests__/server.spec.ts +155 -0
- package/src/mcp/server.ts +38 -0
- package/src/mcp/stdio.ts +29 -0
- package/src/mcp/tools/__tests__/sf-gql-aggregate.spec.ts +173 -0
- package/src/mcp/tools/__tests__/sf-gql-create.spec.ts +235 -0
- package/src/mcp/tools/__tests__/sf-gql-delete.spec.ts +194 -0
- package/src/mcp/tools/__tests__/sf-gql-detail.spec.ts +246 -0
- package/src/mcp/tools/__tests__/sf-gql-discover.spec.ts +320 -0
- package/src/mcp/tools/__tests__/sf-gql-list.spec.ts +128 -0
- package/src/mcp/tools/__tests__/sf-gql-raw.spec.ts +141 -0
- package/src/mcp/tools/__tests__/sf-gql-update.spec.ts +207 -0
- package/src/mcp/tools/echo.ts +24 -0
- package/src/mcp/tools/sf-gql-aggregate.ts +102 -0
- package/src/mcp/tools/sf-gql-create.ts +55 -0
- package/src/mcp/tools/sf-gql-delete.ts +49 -0
- package/src/mcp/tools/sf-gql-detail.ts +85 -0
- package/src/mcp/tools/sf-gql-discover.ts +67 -0
- package/src/mcp/tools/sf-gql-list.ts +73 -0
- package/src/mcp/tools/sf-gql-raw.ts +56 -0
- package/src/mcp/tools/sf-gql-update.ts +55 -0
- package/src/mcp/tools/shared/zod-schemas.ts +55 -0
- package/tsconfig.json +18 -0
- package/vitest.config.ts +14 -0
|
@@ -0,0 +1,280 @@
|
|
|
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
|
+
import crypto from "crypto";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import path from "path";
|
|
10
|
+
import { Org } from "@salesforce/core";
|
|
11
|
+
import { atomicWriteJson, graphitiHome } from "./fs-utils.js";
|
|
12
|
+
// Re-export for backward compatibility with existing graphiti consumers.
|
|
13
|
+
export { graphitiHome };
|
|
14
|
+
/** API version used for display-only endpoints where no live connection is available
|
|
15
|
+
* to query the org's actual version (live requests use connection.getApiVersion()). */
|
|
16
|
+
export const DEFAULT_API_VERSION = "67.0";
|
|
17
|
+
async function getConnection(auth) {
|
|
18
|
+
const org = await Org.create({ aliasOrUsername: auth.alias });
|
|
19
|
+
return org.getConnection();
|
|
20
|
+
}
|
|
21
|
+
/** Resolves the directory holding cached introspection JSON files. */
|
|
22
|
+
export function schemaDir() {
|
|
23
|
+
return path.join(graphitiHome(), "schemas");
|
|
24
|
+
}
|
|
25
|
+
const INTROSPECTION_QUERY = `
|
|
26
|
+
query IntrospectionQuery {
|
|
27
|
+
__schema {
|
|
28
|
+
queryType { name }
|
|
29
|
+
mutationType { name }
|
|
30
|
+
types {
|
|
31
|
+
kind
|
|
32
|
+
name
|
|
33
|
+
description
|
|
34
|
+
fields(includeDeprecated: false) {
|
|
35
|
+
name
|
|
36
|
+
description
|
|
37
|
+
args {
|
|
38
|
+
name
|
|
39
|
+
description
|
|
40
|
+
type { ...TypeRef }
|
|
41
|
+
defaultValue
|
|
42
|
+
}
|
|
43
|
+
type { ...TypeRef }
|
|
44
|
+
}
|
|
45
|
+
inputFields {
|
|
46
|
+
name
|
|
47
|
+
description
|
|
48
|
+
type { ...TypeRef }
|
|
49
|
+
defaultValue
|
|
50
|
+
}
|
|
51
|
+
interfaces { ...TypeRef }
|
|
52
|
+
enumValues(includeDeprecated: false) {
|
|
53
|
+
name
|
|
54
|
+
description
|
|
55
|
+
}
|
|
56
|
+
possibleTypes { ...TypeRef }
|
|
57
|
+
}
|
|
58
|
+
directives {
|
|
59
|
+
name
|
|
60
|
+
description
|
|
61
|
+
locations
|
|
62
|
+
args {
|
|
63
|
+
name
|
|
64
|
+
description
|
|
65
|
+
type { ...TypeRef }
|
|
66
|
+
defaultValue
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
fragment TypeRef on __Type {
|
|
73
|
+
kind
|
|
74
|
+
name
|
|
75
|
+
ofType {
|
|
76
|
+
kind
|
|
77
|
+
name
|
|
78
|
+
ofType {
|
|
79
|
+
kind
|
|
80
|
+
name
|
|
81
|
+
ofType {
|
|
82
|
+
kind
|
|
83
|
+
name
|
|
84
|
+
ofType {
|
|
85
|
+
kind
|
|
86
|
+
name
|
|
87
|
+
ofType {
|
|
88
|
+
kind
|
|
89
|
+
name
|
|
90
|
+
ofType {
|
|
91
|
+
kind
|
|
92
|
+
name
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}`;
|
|
100
|
+
// ── DLM/SSOT stripping ───────────────────────────────────────────────────────
|
|
101
|
+
const DLM_SSOT_RE = /__dlm/i;
|
|
102
|
+
function getNamedTypeNameFromRef(typeRef) {
|
|
103
|
+
if (!typeRef)
|
|
104
|
+
return null;
|
|
105
|
+
if (typeRef.name)
|
|
106
|
+
return typeRef.name;
|
|
107
|
+
return getNamedTypeNameFromRef(typeRef.ofType);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Strips Data Cloud (DLM/SSOT) types and their field references from a raw
|
|
111
|
+
* introspection result. Matches type names containing `__dlm` (case-insensitive)
|
|
112
|
+
* or starting with `ssot__`. Returns the mutated result and a count of removed types.
|
|
113
|
+
*/
|
|
114
|
+
export function stripDataCloudTypes(raw) {
|
|
115
|
+
const schema = raw?.data?.__schema ?? raw?.__schema;
|
|
116
|
+
if (!schema?.types)
|
|
117
|
+
return { result: raw, removedCount: 0 };
|
|
118
|
+
const removedNames = new Set();
|
|
119
|
+
for (const type of schema.types) {
|
|
120
|
+
if (type.name && (DLM_SSOT_RE.test(type.name) || type.name.startsWith("ssot__"))) {
|
|
121
|
+
removedNames.add(type.name);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (removedNames.size === 0)
|
|
125
|
+
return { result: raw, removedCount: 0 };
|
|
126
|
+
const isRemoved = (ref) => {
|
|
127
|
+
const name = getNamedTypeNameFromRef(ref);
|
|
128
|
+
return name != null && removedNames.has(name);
|
|
129
|
+
};
|
|
130
|
+
schema.types = schema.types.filter((t) => !removedNames.has(t.name));
|
|
131
|
+
for (const type of schema.types) {
|
|
132
|
+
if (type.fields) {
|
|
133
|
+
type.fields = type.fields.filter((f) => !isRemoved(f.type));
|
|
134
|
+
}
|
|
135
|
+
if (type.inputFields) {
|
|
136
|
+
type.inputFields = type.inputFields.filter((f) => !isRemoved(f.type));
|
|
137
|
+
}
|
|
138
|
+
if (type.possibleTypes) {
|
|
139
|
+
type.possibleTypes = type.possibleTypes.filter((t) => !t.name || !removedNames.has(t.name));
|
|
140
|
+
}
|
|
141
|
+
if (type.interfaces) {
|
|
142
|
+
type.interfaces = type.interfaces.filter((t) => !t.name || !removedNames.has(t.name));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return { result: raw, removedCount: removedNames.size };
|
|
146
|
+
}
|
|
147
|
+
function _schemaPath(alias) {
|
|
148
|
+
return path.join(schemaDir(), `${alias}.json`);
|
|
149
|
+
}
|
|
150
|
+
export function normalizeInstanceUrl(instanceUrl) {
|
|
151
|
+
return instanceUrl.replace(/\/+$/, "").toLowerCase();
|
|
152
|
+
}
|
|
153
|
+
export function schemaCacheKeyForInstanceUrl(instanceUrl) {
|
|
154
|
+
const normalized = normalizeInstanceUrl(instanceUrl);
|
|
155
|
+
return crypto.createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
156
|
+
}
|
|
157
|
+
function schemaPathForInstanceUrl(instanceUrl) {
|
|
158
|
+
const cacheKey = schemaCacheKeyForInstanceUrl(instanceUrl);
|
|
159
|
+
return path.join(schemaDir(), `${cacheKey}.json`);
|
|
160
|
+
}
|
|
161
|
+
export function schemaExists(instanceUrl) {
|
|
162
|
+
return fs.existsSync(schemaPathForInstanceUrl(normalizeInstanceUrl(instanceUrl)));
|
|
163
|
+
}
|
|
164
|
+
export function getSchemaMetadata(instanceUrl) {
|
|
165
|
+
const normalized = normalizeInstanceUrl(instanceUrl);
|
|
166
|
+
const fp = schemaPathForInstanceUrl(normalized);
|
|
167
|
+
if (!fs.existsSync(fp))
|
|
168
|
+
return null;
|
|
169
|
+
try {
|
|
170
|
+
const raw = JSON.parse(fs.readFileSync(fp, "utf-8"));
|
|
171
|
+
const types = (raw?.data?.__schema?.types ?? raw?.__schema?.types ?? []);
|
|
172
|
+
const stat = fs.statSync(fp);
|
|
173
|
+
return {
|
|
174
|
+
cacheKey: schemaCacheKeyForInstanceUrl(normalized),
|
|
175
|
+
instanceUrl: normalized,
|
|
176
|
+
typeCount: types.filter((t) => !t.name.startsWith("__")).length,
|
|
177
|
+
downloadedAt: stat.mtime.toISOString(),
|
|
178
|
+
filePath: fp,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export function listCachedSchemas() {
|
|
186
|
+
const dir = schemaDir();
|
|
187
|
+
if (!fs.existsSync(dir))
|
|
188
|
+
return [];
|
|
189
|
+
return fs
|
|
190
|
+
.readdirSync(dir)
|
|
191
|
+
.filter((f) => f.endsWith(".json"))
|
|
192
|
+
.map((fileName) => {
|
|
193
|
+
try {
|
|
194
|
+
const fp = path.join(dir, fileName);
|
|
195
|
+
const raw = JSON.parse(fs.readFileSync(fp, "utf-8"));
|
|
196
|
+
const types = (raw?.data?.__schema?.types ?? raw?.__schema?.types ?? []);
|
|
197
|
+
const stat = fs.statSync(fp);
|
|
198
|
+
const instanceUrl = raw?.__graphiti?.instanceUrl;
|
|
199
|
+
if (typeof instanceUrl !== "string")
|
|
200
|
+
return null;
|
|
201
|
+
return {
|
|
202
|
+
cacheKey: fileName.replace(".json", ""),
|
|
203
|
+
instanceUrl,
|
|
204
|
+
typeCount: types.filter((t) => !t.name.startsWith("__")).length,
|
|
205
|
+
downloadedAt: stat.mtime.toISOString(),
|
|
206
|
+
filePath: fp,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
.filter((m) => m !== null);
|
|
214
|
+
}
|
|
215
|
+
export async function downloadSchema(auth) {
|
|
216
|
+
const connection = await getConnection(auth);
|
|
217
|
+
const url = `${auth.instanceUrl}/services/data/v${connection.getApiVersion()}/graphql`;
|
|
218
|
+
const rawResult = (await connection.request({
|
|
219
|
+
method: "POST",
|
|
220
|
+
url,
|
|
221
|
+
body: JSON.stringify({ query: INTROSPECTION_QUERY }),
|
|
222
|
+
headers: { "Content-Type": "application/json", "X-Chatter-Entity-Encoding": "false" },
|
|
223
|
+
}));
|
|
224
|
+
if (rawResult?.errors?.length) {
|
|
225
|
+
const messages = rawResult.errors
|
|
226
|
+
.map((e) => e.message ?? JSON.stringify(e))
|
|
227
|
+
.join("\n ");
|
|
228
|
+
throw new Error(`Introspection query returned errors:\n ${messages}`);
|
|
229
|
+
}
|
|
230
|
+
const rawSchema = rawResult?.data?.__schema ?? rawResult?.__schema;
|
|
231
|
+
if (!rawSchema) {
|
|
232
|
+
throw new Error("Introspection query did not return a __schema field");
|
|
233
|
+
}
|
|
234
|
+
const { result, removedCount } = stripDataCloudTypes(rawResult);
|
|
235
|
+
const schema = result?.data?.__schema ?? result?.__schema;
|
|
236
|
+
const instanceUrl = normalizeInstanceUrl(auth.instanceUrl);
|
|
237
|
+
const filePath = schemaPathForInstanceUrl(instanceUrl);
|
|
238
|
+
const payload = {
|
|
239
|
+
...result,
|
|
240
|
+
__graphiti: {
|
|
241
|
+
instanceUrl,
|
|
242
|
+
alias: auth.alias,
|
|
243
|
+
cachedAt: new Date().toISOString(),
|
|
244
|
+
cacheKey: schemaCacheKeyForInstanceUrl(instanceUrl),
|
|
245
|
+
strippedDataCloudTypes: removedCount,
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
atomicWriteJson(filePath, payload);
|
|
249
|
+
const typeCount = schema.types.filter((t) => !t.name.startsWith("__")).length;
|
|
250
|
+
return {
|
|
251
|
+
cacheKey: schemaCacheKeyForInstanceUrl(instanceUrl),
|
|
252
|
+
instanceUrl,
|
|
253
|
+
typeCount,
|
|
254
|
+
downloadedAt: new Date().toISOString(),
|
|
255
|
+
filePath,
|
|
256
|
+
strippedDataCloudTypes: removedCount || undefined,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
export function loadIntrospectionResult(instanceUrl) {
|
|
260
|
+
const fp = schemaPathForInstanceUrl(normalizeInstanceUrl(instanceUrl));
|
|
261
|
+
if (!fs.existsSync(fp)) {
|
|
262
|
+
throw new Error(`No cached schema for "${instanceUrl}". Run \`graphiti connect <org>\` first.`);
|
|
263
|
+
}
|
|
264
|
+
return JSON.parse(fs.readFileSync(fp, "utf-8"));
|
|
265
|
+
}
|
|
266
|
+
/** Returns the absolute path to the cached introspection JSON file. */
|
|
267
|
+
export function getSchemaFilePath(instanceUrl) {
|
|
268
|
+
return schemaPathForInstanceUrl(normalizeInstanceUrl(instanceUrl));
|
|
269
|
+
}
|
|
270
|
+
export async function executeGraphQL(auth, query, variables) {
|
|
271
|
+
const connection = await getConnection(auth);
|
|
272
|
+
const url = `${auth.instanceUrl}/services/data/v${connection.getApiVersion()}/graphql`;
|
|
273
|
+
return connection.request({
|
|
274
|
+
method: "POST",
|
|
275
|
+
url,
|
|
276
|
+
body: JSON.stringify({ query, variables }),
|
|
277
|
+
headers: { "Content-Type": "application/json", "X-Chatter-Entity-Encoding": "false" },
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=introspect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"introspect.js","sourceRoot":"","sources":["../../src/lib/introspect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2026, Salesforce, Inc.,
|
|
3
|
+
* All rights reserved.
|
|
4
|
+
* For full license text, see the LICENSE.txt file
|
|
5
|
+
*/
|
|
6
|
+
import type { OrgAuth } from "./auth.js";
|
|
7
|
+
export interface PicklistValue {
|
|
8
|
+
value: string | null;
|
|
9
|
+
label: string | null;
|
|
10
|
+
}
|
|
11
|
+
export interface PicklistFieldInfo {
|
|
12
|
+
apiName: string;
|
|
13
|
+
label: string;
|
|
14
|
+
required: boolean;
|
|
15
|
+
values: PicklistValue[];
|
|
16
|
+
}
|
|
17
|
+
export interface FieldMetadata {
|
|
18
|
+
apiName: string;
|
|
19
|
+
label: string | null;
|
|
20
|
+
dataType: string | null;
|
|
21
|
+
required: boolean;
|
|
22
|
+
createable: boolean;
|
|
23
|
+
updateable: boolean;
|
|
24
|
+
calculated: boolean;
|
|
25
|
+
custom: boolean;
|
|
26
|
+
filterable: boolean;
|
|
27
|
+
sortable: boolean;
|
|
28
|
+
nameField: boolean;
|
|
29
|
+
reference: boolean;
|
|
30
|
+
relationshipName: string | null;
|
|
31
|
+
compound: boolean;
|
|
32
|
+
compoundFieldName: string | null;
|
|
33
|
+
defaultedOnCreate: boolean;
|
|
34
|
+
extraTypeInfo: string | null;
|
|
35
|
+
inlineHelpText: string | null;
|
|
36
|
+
precision: number;
|
|
37
|
+
scale: number;
|
|
38
|
+
referenceToInfos: {
|
|
39
|
+
apiName: string;
|
|
40
|
+
nameFields: string[];
|
|
41
|
+
}[];
|
|
42
|
+
controllerName: string | null;
|
|
43
|
+
controllingFields: string[];
|
|
44
|
+
}
|
|
45
|
+
export interface ObjectInfoResult {
|
|
46
|
+
apiName: string;
|
|
47
|
+
label: string | null;
|
|
48
|
+
labelPlural: string | null;
|
|
49
|
+
createable: boolean;
|
|
50
|
+
deletable: boolean;
|
|
51
|
+
updateable: boolean;
|
|
52
|
+
queryable: boolean;
|
|
53
|
+
searchable: boolean;
|
|
54
|
+
custom: boolean;
|
|
55
|
+
keyPrefix: string | null;
|
|
56
|
+
nameFields: string[];
|
|
57
|
+
defaultRecordTypeId: string | null;
|
|
58
|
+
fields: FieldMetadata[];
|
|
59
|
+
childRelationships: {
|
|
60
|
+
childObjectApiName: string;
|
|
61
|
+
fieldName: string | null;
|
|
62
|
+
relationshipName: string | null;
|
|
63
|
+
}[];
|
|
64
|
+
recordTypeInfos: {
|
|
65
|
+
recordTypeId: string;
|
|
66
|
+
name: string | null;
|
|
67
|
+
available: boolean;
|
|
68
|
+
master: boolean;
|
|
69
|
+
defaultRecordTypeMapping: boolean;
|
|
70
|
+
}[];
|
|
71
|
+
picklists: PicklistFieldInfo[];
|
|
72
|
+
fetchedAt: string;
|
|
73
|
+
}
|
|
74
|
+
export declare function getObjectInfo(auth: OrgAuth, orgAlias: string, sObjectName: string, refresh?: boolean): Promise<ObjectInfoResult>;
|
|
75
|
+
export declare function getCachedObjectInfo(orgAlias: string, sObjectName: string): ObjectInfoResult | null;
|
|
76
|
+
export declare function setCachedObjectInfo(orgAlias: string, sObjectName: string, info: ObjectInfoResult): void;
|
|
77
|
+
export declare function getRequiredCreateFields(info: ObjectInfoResult): FieldMetadata[];
|
|
78
|
+
export declare function getPicklistValues(info: ObjectInfoResult, fieldName: string): PicklistValue[] | null;
|
|
79
|
+
export declare function clearObjectInfoCache(orgAlias?: string): void;
|
|
@@ -0,0 +1,313 @@
|
|
|
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
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import { graphitiHome } from "./fs-utils.js";
|
|
10
|
+
import { executeGraphQL } from "./introspect.js";
|
|
11
|
+
// Re-read GRAPHITI_HOME on each call so tests redirecting via env vars
|
|
12
|
+
// see the current value, not a frozen import-time snapshot.
|
|
13
|
+
function cacheDir() {
|
|
14
|
+
return path.join(graphitiHome(), "cache", "objectInfos");
|
|
15
|
+
}
|
|
16
|
+
const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour
|
|
17
|
+
const MASTER_RECORD_TYPE_ID = "012000000000000AAA";
|
|
18
|
+
const OBJECT_INFO_QUERY = `
|
|
19
|
+
query ObjectInfoQuery($inputs: [ObjectInfoInput!]) {
|
|
20
|
+
uiapi {
|
|
21
|
+
objectInfos(objectInfoInputs: $inputs) {
|
|
22
|
+
ApiName
|
|
23
|
+
label
|
|
24
|
+
labelPlural
|
|
25
|
+
createable
|
|
26
|
+
deletable
|
|
27
|
+
updateable
|
|
28
|
+
queryable
|
|
29
|
+
searchable
|
|
30
|
+
custom
|
|
31
|
+
keyPrefix
|
|
32
|
+
nameFields
|
|
33
|
+
defaultRecordTypeId
|
|
34
|
+
recordTypeInfos {
|
|
35
|
+
recordTypeId
|
|
36
|
+
name
|
|
37
|
+
available
|
|
38
|
+
master
|
|
39
|
+
defaultRecordTypeMapping
|
|
40
|
+
}
|
|
41
|
+
childRelationships {
|
|
42
|
+
childObjectApiName
|
|
43
|
+
fieldName
|
|
44
|
+
relationshipName
|
|
45
|
+
}
|
|
46
|
+
fields {
|
|
47
|
+
ApiName
|
|
48
|
+
label
|
|
49
|
+
dataType
|
|
50
|
+
required
|
|
51
|
+
createable
|
|
52
|
+
updateable
|
|
53
|
+
calculated
|
|
54
|
+
custom
|
|
55
|
+
filterable
|
|
56
|
+
sortable
|
|
57
|
+
nameField
|
|
58
|
+
reference
|
|
59
|
+
relationshipName
|
|
60
|
+
compound
|
|
61
|
+
compoundFieldName
|
|
62
|
+
defaultedOnCreate
|
|
63
|
+
extraTypeInfo
|
|
64
|
+
inlineHelpText
|
|
65
|
+
precision
|
|
66
|
+
scale
|
|
67
|
+
controllerName
|
|
68
|
+
controllingFields
|
|
69
|
+
referenceToInfos {
|
|
70
|
+
ApiName
|
|
71
|
+
nameFields
|
|
72
|
+
}
|
|
73
|
+
... on PicklistField {
|
|
74
|
+
picklistValuesByRecordTypeIDs {
|
|
75
|
+
recordTypeID
|
|
76
|
+
picklistValues {
|
|
77
|
+
value
|
|
78
|
+
label
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}`;
|
|
86
|
+
// Process-lifetime in-memory cache. Unbounded by design: graphiti runs as
|
|
87
|
+
// short-lived CLI invocations and per-MCP-session servers, so the working
|
|
88
|
+
// set is naturally capped by the SObjects an agent walks during one session.
|
|
89
|
+
const memoryCache = new Map();
|
|
90
|
+
function cacheKey(orgAlias, sObjectName) {
|
|
91
|
+
return `${orgAlias}:${sObjectName}`;
|
|
92
|
+
}
|
|
93
|
+
// Defense in depth: callers (the MCP boundary) already validate API-name
|
|
94
|
+
// charsets, but cache paths are also reachable from CLI/library callers, so
|
|
95
|
+
// re-validate here before joining and assert the resolved path stays inside
|
|
96
|
+
// the cache root.
|
|
97
|
+
const ORG_ALIAS_PATH_RE = /^[A-Za-z0-9_-]{1,80}$/;
|
|
98
|
+
const SOBJECT_NAME_PATH_RE = /^[A-Za-z][A-Za-z0-9_]{0,79}$/;
|
|
99
|
+
function cacheFilePath(orgAlias, sObjectName) {
|
|
100
|
+
if (!ORG_ALIAS_PATH_RE.test(orgAlias)) {
|
|
101
|
+
throw new Error(`Invalid org alias for cache path: "${orgAlias}"`);
|
|
102
|
+
}
|
|
103
|
+
if (!SOBJECT_NAME_PATH_RE.test(sObjectName)) {
|
|
104
|
+
throw new Error(`Invalid SObject name for cache path: "${sObjectName}"`);
|
|
105
|
+
}
|
|
106
|
+
const rootResolved = path.resolve(cacheDir());
|
|
107
|
+
const resolved = path.resolve(rootResolved, orgAlias, `${sObjectName}.json`);
|
|
108
|
+
if (!resolved.startsWith(rootResolved + path.sep)) {
|
|
109
|
+
throw new Error("Refusing to access cache path outside cache root");
|
|
110
|
+
}
|
|
111
|
+
return resolved;
|
|
112
|
+
}
|
|
113
|
+
function readDiskCache(orgAlias, sObjectName) {
|
|
114
|
+
let fp;
|
|
115
|
+
try {
|
|
116
|
+
fp = cacheFilePath(orgAlias, sObjectName);
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
if (!fs.existsSync(fp))
|
|
122
|
+
return null;
|
|
123
|
+
try {
|
|
124
|
+
const raw = JSON.parse(fs.readFileSync(fp, "utf-8"));
|
|
125
|
+
const age = Date.now() - new Date(raw.fetchedAt).getTime();
|
|
126
|
+
// Reject NaN (corrupt or missing fetchedAt) so a hand-edited cache
|
|
127
|
+
// can't read as fresh forever.
|
|
128
|
+
if (Number.isNaN(age) || age > CACHE_TTL_MS)
|
|
129
|
+
return null;
|
|
130
|
+
return raw;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function writeDiskCache(orgAlias, sObjectName, data) {
|
|
137
|
+
let filePath;
|
|
138
|
+
try {
|
|
139
|
+
filePath = cacheFilePath(orgAlias, sObjectName);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return; // invalid inputs — refuse to write
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
146
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
// Non-critical
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
function parseObjectInfoResponse(raw) {
|
|
153
|
+
const fields = (raw.fields ?? []).map((f) => ({
|
|
154
|
+
apiName: f.ApiName,
|
|
155
|
+
label: f.label ?? null,
|
|
156
|
+
dataType: f.dataType ?? null,
|
|
157
|
+
required: f.required ?? false,
|
|
158
|
+
createable: f.createable ?? false,
|
|
159
|
+
updateable: f.updateable ?? false,
|
|
160
|
+
calculated: f.calculated ?? false,
|
|
161
|
+
custom: f.custom ?? false,
|
|
162
|
+
filterable: f.filterable ?? false,
|
|
163
|
+
sortable: f.sortable ?? false,
|
|
164
|
+
nameField: f.nameField ?? false,
|
|
165
|
+
reference: f.reference ?? false,
|
|
166
|
+
relationshipName: f.relationshipName ?? null,
|
|
167
|
+
compound: f.compound ?? false,
|
|
168
|
+
compoundFieldName: f.compoundFieldName ?? null,
|
|
169
|
+
defaultedOnCreate: f.defaultedOnCreate ?? false,
|
|
170
|
+
extraTypeInfo: f.extraTypeInfo ?? null,
|
|
171
|
+
inlineHelpText: f.inlineHelpText ?? null,
|
|
172
|
+
precision: f.precision ?? 0,
|
|
173
|
+
scale: f.scale ?? 0,
|
|
174
|
+
referenceToInfos: (f.referenceToInfos ?? []).map((r) => ({
|
|
175
|
+
apiName: r.ApiName,
|
|
176
|
+
nameFields: r.nameFields ?? [],
|
|
177
|
+
})),
|
|
178
|
+
controllerName: f.controllerName ?? null,
|
|
179
|
+
controllingFields: f.controllingFields ?? [],
|
|
180
|
+
}));
|
|
181
|
+
const picklists = (raw.fields ?? [])
|
|
182
|
+
.filter((f) => f.picklistValuesByRecordTypeIDs)
|
|
183
|
+
.map((f) => {
|
|
184
|
+
const allValues = (f.picklistValuesByRecordTypeIDs ?? []).flatMap((rt) => rt.picklistValues ?? []);
|
|
185
|
+
// Drop entries with no value rather than recording `value: null`.
|
|
186
|
+
// Keeps the on-disk cache shape identical to pre-change behavior so
|
|
187
|
+
// consumers that don't null-guard (commands/type.ts, commands/review.ts)
|
|
188
|
+
// can't read explicit nulls back out after a rollback.
|
|
189
|
+
return {
|
|
190
|
+
apiName: f.ApiName,
|
|
191
|
+
label: f.label ?? f.ApiName,
|
|
192
|
+
required: f.required ?? false,
|
|
193
|
+
values: allValues
|
|
194
|
+
.filter((v) => v?.value != null)
|
|
195
|
+
.map((v) => ({ value: v.value, label: v.label ?? null })),
|
|
196
|
+
};
|
|
197
|
+
});
|
|
198
|
+
return {
|
|
199
|
+
apiName: raw.ApiName,
|
|
200
|
+
label: raw.label ?? null,
|
|
201
|
+
labelPlural: raw.labelPlural ?? null,
|
|
202
|
+
createable: raw.createable ?? false,
|
|
203
|
+
deletable: raw.deletable ?? false,
|
|
204
|
+
updateable: raw.updateable ?? false,
|
|
205
|
+
queryable: raw.queryable ?? false,
|
|
206
|
+
searchable: raw.searchable ?? false,
|
|
207
|
+
custom: raw.custom ?? false,
|
|
208
|
+
keyPrefix: raw.keyPrefix ?? null,
|
|
209
|
+
nameFields: raw.nameFields ?? [],
|
|
210
|
+
defaultRecordTypeId: raw.defaultRecordTypeId ?? null,
|
|
211
|
+
fields,
|
|
212
|
+
childRelationships: (raw.childRelationships ?? []).map((cr) => ({
|
|
213
|
+
childObjectApiName: cr.childObjectApiName,
|
|
214
|
+
fieldName: cr.fieldName ?? null,
|
|
215
|
+
relationshipName: cr.relationshipName ?? null,
|
|
216
|
+
})),
|
|
217
|
+
recordTypeInfos: (raw.recordTypeInfos ?? []).map((rt) => ({
|
|
218
|
+
recordTypeId: rt.recordTypeId,
|
|
219
|
+
name: rt.name ?? null,
|
|
220
|
+
available: rt.available ?? false,
|
|
221
|
+
master: rt.master ?? false,
|
|
222
|
+
defaultRecordTypeMapping: rt.defaultRecordTypeMapping ?? false,
|
|
223
|
+
})),
|
|
224
|
+
picklists,
|
|
225
|
+
fetchedAt: new Date().toISOString(),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
export async function getObjectInfo(auth, orgAlias, sObjectName, refresh = false) {
|
|
229
|
+
const key = cacheKey(orgAlias, sObjectName);
|
|
230
|
+
if (!refresh) {
|
|
231
|
+
const mem = memoryCache.get(key);
|
|
232
|
+
if (mem) {
|
|
233
|
+
const age = Date.now() - new Date(mem.fetchedAt).getTime();
|
|
234
|
+
if (age <= CACHE_TTL_MS)
|
|
235
|
+
return mem;
|
|
236
|
+
}
|
|
237
|
+
const disk = readDiskCache(orgAlias, sObjectName);
|
|
238
|
+
if (disk) {
|
|
239
|
+
memoryCache.set(key, disk);
|
|
240
|
+
return disk;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
// Master record type is the union of all picklist values for the field;
|
|
244
|
+
// non-master record types only restrict (hide) values, never add them. So
|
|
245
|
+
// a single fetch with [MASTER_RECORD_TYPE_ID] returns the complete set.
|
|
246
|
+
const result = await executeGraphQL(auth, OBJECT_INFO_QUERY, {
|
|
247
|
+
inputs: [{ apiName: sObjectName, recordTypeIDs: [MASTER_RECORD_TYPE_ID] }],
|
|
248
|
+
});
|
|
249
|
+
const infos = result?.data?.uiapi?.objectInfos;
|
|
250
|
+
if (!infos || infos.length === 0) {
|
|
251
|
+
throw new Error(`No objectInfo returned for "${sObjectName}". Check that the object exists and is accessible.`);
|
|
252
|
+
}
|
|
253
|
+
const parsed = parseObjectInfoResponse(infos[0]);
|
|
254
|
+
memoryCache.set(key, parsed);
|
|
255
|
+
writeDiskCache(orgAlias, sObjectName, parsed);
|
|
256
|
+
return parsed;
|
|
257
|
+
}
|
|
258
|
+
export function getCachedObjectInfo(orgAlias, sObjectName) {
|
|
259
|
+
const key = cacheKey(orgAlias, sObjectName);
|
|
260
|
+
const mem = memoryCache.get(key);
|
|
261
|
+
if (mem)
|
|
262
|
+
return mem;
|
|
263
|
+
const disk = readDiskCache(orgAlias, sObjectName);
|
|
264
|
+
if (disk) {
|
|
265
|
+
memoryCache.set(key, disk);
|
|
266
|
+
return disk;
|
|
267
|
+
}
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
// Seed the in-memory ObjectInfo cache directly. Lets callers prime codegen
|
|
271
|
+
// without going through the live UIAPI fetch in `getObjectInfo`. Used by
|
|
272
|
+
// tests today; the planned MCP intent-layer prewarm helper (W-22694063)
|
|
273
|
+
// will reuse the same write path after a successful network fetch.
|
|
274
|
+
export function setCachedObjectInfo(orgAlias, sObjectName, info) {
|
|
275
|
+
memoryCache.set(cacheKey(orgAlias, sObjectName), info);
|
|
276
|
+
}
|
|
277
|
+
export function getRequiredCreateFields(info) {
|
|
278
|
+
return info.fields.filter((f) => f.required && f.createable && !f.defaultedOnCreate);
|
|
279
|
+
}
|
|
280
|
+
export function getPicklistValues(info, fieldName) {
|
|
281
|
+
const picklist = info.picklists.find((p) => p.apiName === fieldName);
|
|
282
|
+
return picklist?.values ?? null;
|
|
283
|
+
}
|
|
284
|
+
export function clearObjectInfoCache(orgAlias) {
|
|
285
|
+
if (orgAlias) {
|
|
286
|
+
for (const [key] of memoryCache) {
|
|
287
|
+
if (key.startsWith(`${orgAlias}:`))
|
|
288
|
+
memoryCache.delete(key);
|
|
289
|
+
}
|
|
290
|
+
if (!ORG_ALIAS_PATH_RE.test(orgAlias))
|
|
291
|
+
return;
|
|
292
|
+
const root = path.resolve(cacheDir());
|
|
293
|
+
const dir = path.resolve(root, orgAlias);
|
|
294
|
+
if (!dir.startsWith(root + path.sep))
|
|
295
|
+
return;
|
|
296
|
+
try {
|
|
297
|
+
fs.rmSync(dir, { recursive: true });
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
/* ok */
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
memoryCache.clear();
|
|
305
|
+
try {
|
|
306
|
+
fs.rmSync(cacheDir(), { recursive: true });
|
|
307
|
+
}
|
|
308
|
+
catch {
|
|
309
|
+
/* ok */
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=object-info.js.map
|