@salesforce/platform-sdk 11.69.2 → 11.70.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/dist/core/data.d.ts +18 -0
- package/dist/core/data.d.ts.map +1 -1
- package/dist/data/gql.d.ts +2 -0
- package/dist/data/gql.d.ts.map +1 -1
- package/dist/data/index.d.ts +14 -2
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +1 -1
- package/dist/data/mosaic/index.d.ts +4 -1
- package/dist/data/mosaic/index.d.ts.map +1 -1
- package/docs/data/01-grounding-and-precedence.md +84 -0
- package/docs/data/02-caching-and-freshness.md +183 -0
- package/docs/data/03-reactive-handle-lifecycle.md +84 -0
- package/docs/data/04-surfaces-and-guards.md +146 -0
- package/docs/data/05-migration-from-sdk-data.md +126 -0
- package/package.json +9 -4
package/dist/core/data.d.ts
CHANGED
|
@@ -105,6 +105,8 @@ export type QuerySubscriber<T> = (snapshot: QuerySnapshot<T>) => void;
|
|
|
105
105
|
* @remarks
|
|
106
106
|
* On uncached surfaces (Mosaic, OpenAI), `subscribe` only emits in response
|
|
107
107
|
* to `refresh()` — there is no cache to push background updates.
|
|
108
|
+
*
|
|
109
|
+
* @see docs/data/03-reactive-handle-lifecycle.md
|
|
108
110
|
*/
|
|
109
111
|
export interface QueryResult<T> extends QuerySnapshot<T> {
|
|
110
112
|
subscribe(cb: QuerySubscriber<T>): Unsubscribe;
|
|
@@ -127,6 +129,8 @@ export interface QueryResult<T> extends QuerySnapshot<T> {
|
|
|
127
129
|
* `subscribe` / `refresh` (mutating a side effect on subscription is incoherent;
|
|
128
130
|
* re-running it on refresh is dangerous). Stale-after-mutation is handled by
|
|
129
131
|
* the caller holding query result references and calling `result.refresh()`.
|
|
132
|
+
*
|
|
133
|
+
* @see docs/data/03-reactive-handle-lifecycle.md
|
|
130
134
|
*/
|
|
131
135
|
export interface MutationResult<T> {
|
|
132
136
|
data: T | undefined;
|
|
@@ -140,6 +144,8 @@ export interface MutationResult<T> {
|
|
|
140
144
|
* Follows HTTP RFC 7234 `no-cache` semantics.
|
|
141
145
|
* - `"only-if-cached"` — return the cached entry without a network call.
|
|
142
146
|
* Returns an error if no entry exists for this query + variables.
|
|
147
|
+
*
|
|
148
|
+
* @see docs/data/02-caching-and-freshness.md
|
|
143
149
|
*/
|
|
144
150
|
export type CacheControlShorthand = "no-cache" | "only-if-cached";
|
|
145
151
|
/**
|
|
@@ -149,6 +155,8 @@ export type CacheControlShorthand = "no-cache" | "only-if-cached";
|
|
|
149
155
|
* - `maxAge: 0` — entry is written but immediately considered stale
|
|
150
156
|
* (always re-fetches, similar to `"no-cache"` but writes a 0s TTL).
|
|
151
157
|
* - Invalid values (NaN, Infinity, negative) fall back to the 300s default.
|
|
158
|
+
*
|
|
159
|
+
* @see docs/data/02-caching-and-freshness.md
|
|
152
160
|
*/
|
|
153
161
|
export interface CacheControlMaxAge {
|
|
154
162
|
type: "max-age";
|
|
@@ -169,10 +177,14 @@ export interface CacheControlMaxAge {
|
|
|
169
177
|
*
|
|
170
178
|
* When omitted, the default `max-age` strategy with a 300-second TTL applies.
|
|
171
179
|
* After the TTL expires, the next call fetches fresh data from the network.
|
|
180
|
+
*
|
|
181
|
+
* @see docs/data/02-caching-and-freshness.md
|
|
172
182
|
*/
|
|
173
183
|
export type CacheControl = CacheControlShorthand | CacheControlMaxAge;
|
|
174
184
|
/**
|
|
175
185
|
* Options bag for {@link DataSDKGraphQL.query}.
|
|
186
|
+
*
|
|
187
|
+
* @see docs/data/02-caching-and-freshness.md
|
|
176
188
|
*/
|
|
177
189
|
export interface QueryOptions<V = Record<string, unknown>> {
|
|
178
190
|
query: GraphQLRawDocument;
|
|
@@ -197,6 +209,8 @@ export interface QueryOptions<V = Record<string, unknown>> {
|
|
|
197
209
|
}
|
|
198
210
|
/**
|
|
199
211
|
* Options bag for {@link DataSDKGraphQL.mutate}.
|
|
212
|
+
*
|
|
213
|
+
* @see docs/data/02-caching-and-freshness.md
|
|
200
214
|
*/
|
|
201
215
|
export interface MutateOptions<V = Record<string, unknown>> {
|
|
202
216
|
mutation: GraphQLRawDocument;
|
|
@@ -214,6 +228,8 @@ export interface MutateOptions<V = Record<string, unknown>> {
|
|
|
214
228
|
* Queries are reactive (cached on surfaces that cache, subscribable on every
|
|
215
229
|
* surface); mutations are request/response. Each method's return shape
|
|
216
230
|
* matches its semantics — no fakes, no noops.
|
|
231
|
+
*
|
|
232
|
+
* @see docs/data/04-surfaces-and-guards.md
|
|
217
233
|
*/
|
|
218
234
|
export interface DataSDKGraphQL {
|
|
219
235
|
/**
|
|
@@ -302,6 +318,8 @@ export interface DataSDKGraphQL {
|
|
|
302
318
|
*
|
|
303
319
|
* May be a bare `{}` on unsupported surfaces — feature-detect `graphql` or
|
|
304
320
|
* `fetch` before use to avoid runtime errors.
|
|
321
|
+
*
|
|
322
|
+
* @see docs/data/04-surfaces-and-guards.md
|
|
305
323
|
*/
|
|
306
324
|
export interface DataSDK {
|
|
307
325
|
/** GraphQL namespace. Present when the surface supports data operations. */
|
package/dist/core/data.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/core/data.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC/C,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB;;;;;;;;;;;;;OAaG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACrC;AAED;;;;GAIG;AACH,MAAM,WAAW,eAAe,CAAC,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC;IACR,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,qBAAqB,GAAG,WAAW,CAAC;AAEhD;;;;GAIG;AACH,MAAM,WAAW,cAAc,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,CAAC,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,OAAO,CAAC,EAAE,qBAAqB,CAAC;CAChC;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC;AAExC;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC;IAC/B,IAAI,EAAE,CAAC,GAAG,SAAS,CAAC;IACpB,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;CACxB;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;AAEtE
|
|
1
|
+
{"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/core/data.ts"],"names":[],"mappings":"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"}
|
package/dist/data/gql.d.ts
CHANGED
package/dist/data/gql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gql.d.ts","sourceRoot":"","sources":["../../src/data/gql.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH
|
|
1
|
+
{"version":3,"file":"gql.d.ts","sourceRoot":"","sources":["../../src/data/gql.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAM/E"}
|
package/dist/data/index.d.ts
CHANGED
|
@@ -3,10 +3,15 @@ import { MosaicDataSDKOptions } from './mosaic';
|
|
|
3
3
|
/**
|
|
4
4
|
* A callback the host invokes for a given HTTP status (see {@link WebAppDataSDKOptions.onStatus}).
|
|
5
5
|
* May be sync or async.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/data/04-surfaces-and-guards.md
|
|
6
8
|
*/
|
|
7
9
|
export type StatusCallback = () => Promise<unknown> | void;
|
|
8
10
|
/**
|
|
9
|
-
* Options for
|
|
11
|
+
* Options for the WebApp surface — the browser-hosted, cached surface. Passed
|
|
12
|
+
* as `DataSDKOptions.webapp`.
|
|
13
|
+
*
|
|
14
|
+
* @see docs/data/04-surfaces-and-guards.md
|
|
10
15
|
*/
|
|
11
16
|
export interface WebAppDataSDKOptions {
|
|
12
17
|
/**
|
|
@@ -52,7 +57,10 @@ export declare const defineDataExtension: <const Name extends string, Api>(exten
|
|
|
52
57
|
*/
|
|
53
58
|
export type DataSDKWithExtensions<Extensions extends ExtensionsFor<DataSDK>> = ExtendedSDK<DataSDK, Extensions>;
|
|
54
59
|
/**
|
|
55
|
-
* Options for creating a DataSDK instance
|
|
60
|
+
* Options for creating a DataSDK instance — surface selection plus the
|
|
61
|
+
* per-surface option bags (`webapp`, `mosaic`) and any `plugins`.
|
|
62
|
+
*
|
|
63
|
+
* @see docs/data/04-surfaces-and-guards.md
|
|
56
64
|
*/
|
|
57
65
|
export interface DataSDKOptions extends SDKOptions {
|
|
58
66
|
/**
|
|
@@ -116,6 +124,8 @@ export interface DataSDKOptions extends SDKOptions {
|
|
|
116
124
|
* console.log(result.data);
|
|
117
125
|
* }
|
|
118
126
|
* ```
|
|
127
|
+
*
|
|
128
|
+
* @see docs/data/01-grounding-and-precedence.md
|
|
119
129
|
*/
|
|
120
130
|
export declare function createDataSDK<const Extensions extends ExtensionsFor<DataSDK> = []>(options?: Omit<DataSDKOptions, "extensions"> & {
|
|
121
131
|
extensions?: Extensions;
|
|
@@ -127,6 +137,8 @@ export type { CacheControl, DataSDK, DataSDKGraphQL, GraphQLError, GraphQLRawDoc
|
|
|
127
137
|
* `{ edges: [{ node }] }` connection. Resolves to `never` when `T` is not such a connection.
|
|
128
138
|
*
|
|
129
139
|
* @typeParam T - The connection type to unwrap.
|
|
140
|
+
*
|
|
141
|
+
* @see docs/data/04-surfaces-and-guards.md
|
|
130
142
|
*/
|
|
131
143
|
export type NodeOfConnection<T> = T extends {
|
|
132
144
|
edges?: (infer E)[] | null;
|
package/dist/data/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/data/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACX,OAAO,EACP,WAAW,EACX,SAAS,EACT,aAAa,EACb,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAKpE
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/data/index.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EACX,OAAO,EACP,WAAW,EACX,SAAS,EACT,aAAa,EACb,UAAU,EACV,oBAAoB,EACpB,eAAe,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAiB,KAAK,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAKpE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC,CAAC;IACnD;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,YAAY,EAAE,oBAAoB,EAAE,CAAC;AAErC;;;GAGG;AACH,MAAM,MAAM,gBAAgB,CAAC,IAAI,SAAS,MAAM,GAAG,MAAM,EAAE,GAAG,GAAG,OAAO,IAAI,SAAS,CACpF,OAAO,EACP,IAAI,EACJ,GAAG,CACH,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,mBAAmB,+IAAoC,CAAC;AAErE;;;;;;GAMG;AACH,MAAM,MAAM,qBAAqB,CAAC,UAAU,SAAS,aAAa,CAAC,OAAO,CAAC,IAAI,WAAW,CACzF,OAAO,EACP,UAAU,CACV,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,cAAe,SAAQ,UAAU;IACjD;;;OAGG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;;OAGG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,UAAU,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CACpC;AAqCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAiBH,wBAAgB,aAAa,CAAC,KAAK,CAAC,UAAU,SAAS,aAAa,CAAC,OAAO,CAAC,GAAG,EAAE,EACjF,OAAO,CAAC,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,GAAG;IAC9C,UAAU,CAAC,EAAE,UAAU,CAAC;CACxB,GAAG,oBAAoB,CAAC,UAAU,CAAC,GACnC,eAAe,CAAC,UAAU,CAAC,GAC1B,OAAO,CAAC,qBAAqB,CAAC,UAAU,CAAC,CAAC,CAAC;AAe9C,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAC5B,YAAY,EACX,YAAY,EACZ,OAAO,EACP,cAAc,EACd,YAAY,EACZ,kBAAkB,EAClB,cAAc,EACd,qBAAqB,EACrB,eAAe,EACf,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,eAAe,EACf,UAAU,EACV,WAAW,GACX,MAAM,SAAS,CAAC;AAEjB;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,CAAC,SAAS;IAC3C,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC;CAC3B,GAAG,IAAI,GACL,CAAC,SAAS;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAA;CAAE,GAAG,IAAI,GAClC,CAAC,GACD,KAAK,GACN,KAAK,CAAC"}
|
package/dist/data/index.js
CHANGED
|
@@ -279,7 +279,7 @@ const v = "Accept-Language", we = (t) => {
|
|
|
279
279
|
return l(t);
|
|
280
280
|
const [r, n] = t;
|
|
281
281
|
return (r instanceof Request && !n?.headers ? r.headers.has(v) : new Headers(n?.headers).has(v)) ? l(t) : l(b(v, e, t));
|
|
282
|
-
}, Se = "X-SFDC-Client-Name", ge = "X-SFDC-Client-Version", ve = "@salesforce/platform-sdk", Ee = "11.
|
|
282
|
+
}, Se = "X-SFDC-Client-Name", ge = "X-SFDC-Client-Version", ve = "@salesforce/platform-sdk", Ee = "11.70.0", Ce = (t) => {
|
|
283
283
|
let e = b(Se, ve, t);
|
|
284
284
|
return e = b(ge, Ee, e), l(e);
|
|
285
285
|
}, De = "X-CSRF-Token";
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { DataSDK, DataSDKGraphQL } from '../../core';
|
|
2
2
|
/**
|
|
3
|
-
* Options for
|
|
3
|
+
* Options for the Mosaic surface — the server-side (Node.js) surface. Auth is
|
|
4
|
+
* via access token and there is no cache. Passed as `DataSDKOptions.mosaic`.
|
|
5
|
+
*
|
|
6
|
+
* @see docs/data/04-surfaces-and-guards.md
|
|
4
7
|
*/
|
|
5
8
|
export interface MosaicDataSDKOptions {
|
|
6
9
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/data/mosaic/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAmC,MAAM,YAAY,CAAC;AAe3F
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/data/mosaic/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAmC,MAAM,YAAY,CAAC;AAe3F;;;;;GAKG;AACH,MAAM,WAAW,oBAAoB;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,OAAO;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAe;IAC3C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;gBAErB,OAAO,CAAC,EAAE,oBAAoB;YAa5B,iBAAiB;CAe/B"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!-- sdk-symbols
|
|
2
|
+
createDataSDK: dist/data/index.d.ts
|
|
3
|
+
gql: dist/data/gql.d.ts
|
|
4
|
+
DataSDK: dist/core/data.d.ts
|
|
5
|
+
-->
|
|
6
|
+
|
|
7
|
+
# Data access — grounding & precedence
|
|
8
|
+
|
|
9
|
+
This folder is the **authored** half of the `@salesforce/platform-sdk` data-access contract. It
|
|
10
|
+
ships **inside the installed package**, so it version-travels with the `.d.ts` you have on disk:
|
|
11
|
+
the guidance you read here always matches the SDK version you installed, never a newer or older
|
|
12
|
+
copy kept somewhere else.
|
|
13
|
+
|
|
14
|
+
It is deliberately **thin**. The installed declarations own the call contract; these pages carry
|
|
15
|
+
only the residue the types cannot express — strategy, negatives, lifecycle, and the migration
|
|
16
|
+
story.
|
|
17
|
+
|
|
18
|
+
## Two ways in — `.graphql` and `.fetch`
|
|
19
|
+
|
|
20
|
+
`createDataSDK()` returns two data-access members, both surface-optional (feature-detect before use
|
|
21
|
+
— see [04-surfaces-and-guards.md](./04-surfaces-and-guards.md)):
|
|
22
|
+
|
|
23
|
+
- **`.graphql` — the primary route.** UIAPI-backed reads and mutations, cached and reactive on the
|
|
24
|
+
WebApp surface. Steer here first: anything the GraphQL schema can express should go through
|
|
25
|
+
`.graphql`. Pages [02](./02-caching-and-freshness.md)–[03](./03-reactive-handle-lifecycle.md)
|
|
26
|
+
are entirely about this route.
|
|
27
|
+
- **`.fetch` — the escape hatch.** SDK-enriched authenticated HTTP (`typeof fetch`) for the
|
|
28
|
+
endpoints the GraphQL schema does not expose — REST (`/services/data/…`), Apex REST
|
|
29
|
+
(`/services/apexrest/…`), Connect, blob/file downloads. It carries the surface's auth, CSRF, and
|
|
30
|
+
header handling, and is **not** cached. On WebApp, `.graphql` is itself built on top of `.fetch`
|
|
31
|
+
— it is the primitive, not an afterthought.
|
|
32
|
+
|
|
33
|
+
**Which door:** default to `.graphql`; reach for `.fetch` only when there is no GraphQL
|
|
34
|
+
representation for what you need. These pages document the _mechanics_ of each — the `.fetch`
|
|
35
|
+
contract, per-surface behaviour, and examples live in
|
|
36
|
+
[04-surfaces-and-guards.md](./04-surfaces-and-guards.md). The authoritative
|
|
37
|
+
`.graphql`-vs-`.fetch` **routing** decision (which endpoints belong on which door) stays with the
|
|
38
|
+
`experience-ui-bundle-salesforce-data-access` skill, not here.
|
|
39
|
+
|
|
40
|
+
## Precedence — read in this order, and let the earlier rung win
|
|
41
|
+
|
|
42
|
+
1. **Tier-1 — the live GraphQL schema.** What fields/types actually exist for a `.graphql` query.
|
|
43
|
+
Not in this package. This rung grounds the `.graphql` door only — the `.fetch` door is grounded
|
|
44
|
+
by the contract of whatever REST/Apex endpoint you call, which this SDK does not own.
|
|
45
|
+
2. **Tier-2a — the installed `.d.ts`.** The authoritative call contract: exports, `query`/`mutate`
|
|
46
|
+
signatures, options bags, result shapes, `CacheControl`, `NodeOfConnection`. Start here:
|
|
47
|
+
- `dist/core/data.d.ts` — the contract types (`DataSDK`, `DataSDKGraphQL`, `QueryResult`,
|
|
48
|
+
`CacheControl`, …).
|
|
49
|
+
- `dist/data/index.d.ts` — the entry points (`createDataSDK`, `gql`) and the surface options.
|
|
50
|
+
3. **Tier-2b — these pages.** Only what the types can't carry. **If a page and the `.d.ts`
|
|
51
|
+
disagree, the `.d.ts` wins** — assume the prose has drifted and trust the declaration.
|
|
52
|
+
4. **Skill spine — fallback.** The `experience-ui-bundle-salesforce-data-access` skill covers
|
|
53
|
+
routing, guardrails, and the tier-1 gate. It points _here_ for the SDK-usage detail rather
|
|
54
|
+
than restating it.
|
|
55
|
+
|
|
56
|
+
## What lives where
|
|
57
|
+
|
|
58
|
+
| The installed `.d.ts` owns | These pages own |
|
|
59
|
+
| -------------------------------------------------------------- | --------------------------------------------------------------- |
|
|
60
|
+
| Signatures, options bags, result shapes, enums, default values | Strategy/decision tables, "do NOT reinvent it" negatives |
|
|
61
|
+
| `CacheControl` values & the default TTL | _When_ to reach for each cache policy, offline-first stance |
|
|
62
|
+
| The `QueryResult` shape | Living with the reactive handle in a framework lifecycle |
|
|
63
|
+
| `DataSDK.fetch` (`typeof fetch`) — the raw call shape | _When_ to reach for `.fetch` vs `.graphql`, and what it carries |
|
|
64
|
+
| The current call contract | The dead `@salesforce/sdk-data` → new-API migration story |
|
|
65
|
+
|
|
66
|
+
## Reading order
|
|
67
|
+
|
|
68
|
+
- [02-caching-and-freshness.md](./02-caching-and-freshness.md) — the WebApp cache, the two refresh
|
|
69
|
+
tools, offline-first.
|
|
70
|
+
- [03-reactive-handle-lifecycle.md](./03-reactive-handle-lifecycle.md) — living with
|
|
71
|
+
`QueryResult.subscribe`/`refresh`.
|
|
72
|
+
- [04-surfaces-and-guards.md](./04-surfaces-and-guards.md) — the two data-access members,
|
|
73
|
+
`sdk.graphql!` vs guard, reading `errors`, the `.fetch` escape hatch, per-surface options.
|
|
74
|
+
- [05-migration-from-sdk-data.md](./05-migration-from-sdk-data.md) — old callable API → new
|
|
75
|
+
namespace API.
|
|
76
|
+
|
|
77
|
+
## Deep-path `.d.ts` are for reading, not importing
|
|
78
|
+
|
|
79
|
+
A few pages cite declarations that live deep in the tree — e.g.
|
|
80
|
+
`dist/data/shared/graphql/cache/command.d.ts`. Those are **raw file paths you may open to read**,
|
|
81
|
+
**not import subpaths**. The only importable entries are the ones declared in the package's
|
|
82
|
+
`exports` (`@salesforce/platform-sdk`, `@salesforce/platform-sdk/core`,
|
|
83
|
+
`@salesforce/platform-sdk/data`, …). Never write
|
|
84
|
+
`import … from "@salesforce/platform-sdk/data/shared/graphql/cache/command"` — it will not resolve.
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
<!-- sdk-symbols
|
|
2
|
+
CacheControl: dist/core/data.d.ts
|
|
3
|
+
CacheControlMaxAge: dist/core/data.d.ts
|
|
4
|
+
CacheControlShorthand: dist/core/data.d.ts
|
|
5
|
+
QueryOptions: dist/core/data.d.ts
|
|
6
|
+
MutateOptions: dist/core/data.d.ts
|
|
7
|
+
DataSDKGraphQL: dist/core/data.d.ts
|
|
8
|
+
HttpGraphQLResourceCacheControlCommand: dist/data/shared/graphql/cache/command.d.ts
|
|
9
|
+
cacheBundleRegistry: dist/data/shared/graphql/cache/services.d.ts
|
|
10
|
+
buildCacheServices: dist/data/shared/graphql/cache/services.d.ts
|
|
11
|
+
-->
|
|
12
|
+
|
|
13
|
+
# Caching & freshness
|
|
14
|
+
|
|
15
|
+
> **Surface caveat up front:** everything about _caching_ here is the **WebApp surface**. On the
|
|
16
|
+
> uncached surfaces (Mosaic, OpenAI) there is no cache — see
|
|
17
|
+
> [Uncached surfaces](#uncached-surfaces-mosaic-openai) below and
|
|
18
|
+
> [04-surfaces-and-guards.md](./04-surfaces-and-guards.md). The `query`/`mutate` shape is identical
|
|
19
|
+
> on every surface, so call sites stay portable.
|
|
20
|
+
|
|
21
|
+
The `CacheControl`, `CacheControlMaxAge`, and `DataSDKGraphQL` declarations in `dist/core/data.d.ts`
|
|
22
|
+
own the supported values, the default TTL, and the hit/miss behaviour. Read them first; this page is
|
|
23
|
+
strategy and negatives only.
|
|
24
|
+
|
|
25
|
+
## Caching is ON by default (WebApp) — do NOT reinvent it
|
|
26
|
+
|
|
27
|
+
Every `sdk.graphql!.query()` on WebApp is cached automatically. There is **no opt-in flag, no
|
|
28
|
+
`createCachedClient` factory, and no `/cache` import subpath** — those do not exist in
|
|
29
|
+
`@salesforce/platform-sdk`. There is also **no need for React Query, SWR, `localStorage`, or any
|
|
30
|
+
hand-rolled memoization**. If you find yourself writing a cache, **stop** — it already exists.
|
|
31
|
+
|
|
32
|
+
The default TTL and the max-age override are defined on `CacheControl` / `CacheControlMaxAge` in the
|
|
33
|
+
`.d.ts`. Read the value there rather than hard-coding a number here — the declaration is the source
|
|
34
|
+
of truth for the default.
|
|
35
|
+
|
|
36
|
+
## What gets cached
|
|
37
|
+
|
|
38
|
+
Only **successful responses with a non-empty `data` object** are written:
|
|
39
|
+
|
|
40
|
+
- `data` is `null`, missing, or `{}` → **NOT cached**. An empty `data` usually means a transient
|
|
41
|
+
server condition; caching it would poison the entry for the full TTL.
|
|
42
|
+
- Response carries a non-empty `errors` array → surfaced as an error and **NOT cached**.
|
|
43
|
+
|
|
44
|
+
## Cache key
|
|
45
|
+
|
|
46
|
+
The key is `stableJSONStringify({ query, variables, operationName })`, plus a `headers` field when
|
|
47
|
+
the call passes per-request headers (see the `headers` bullet below). The derivation lives in
|
|
48
|
+
`HttpGraphQLResourceCacheControlCommand` (`dist/data/shared/graphql/cache/command.d.ts` — a raw path
|
|
49
|
+
to read, not an import). What matters at a call site:
|
|
50
|
+
|
|
51
|
+
- **`cacheControl` does NOT affect the key.** The same query + variables share **one** entry no
|
|
52
|
+
matter what policy each call passes. A `"no-cache"` call and a default call read/write the _same_
|
|
53
|
+
slot.
|
|
54
|
+
- The query is keyed by its **normalized, printed GraphQL document** — the WebApp path parses the
|
|
55
|
+
authored query into an AST and re-serializes it with `print()` before it reaches the cache key, so
|
|
56
|
+
whitespace-only differences between two calls do **not** produce separate entries. Two calls only
|
|
57
|
+
land in different entries if their parsed documents actually differ (selections, arguments,
|
|
58
|
+
fragments). Reuse the same `gql`-tagged constant anyway — it stays the single source of truth for
|
|
59
|
+
the operation, independent of cache-key mechanics.
|
|
60
|
+
- Variables are deep-cloned at call time, so mutating your variables object afterward does not
|
|
61
|
+
desync the key from the request body. Variables must be JSON-serializable (circular refs / BigInt
|
|
62
|
+
throw a typed error).
|
|
63
|
+
- **`headers` are part of the key** (see `QueryOptions.headers` in the `.d.ts`): two calls that
|
|
64
|
+
differ only in `headers` resolve to different entries, so a response scoped by an `Authorization`
|
|
65
|
+
header is never served to a caller that passed a different value.
|
|
66
|
+
|
|
67
|
+
## Shared across SDK instances by host + API version
|
|
68
|
+
|
|
69
|
+
Cache bundles are deduped in a module-level registry keyed by the resolved `baseUrl` **plus the
|
|
70
|
+
API-version path** (`cacheBundleRegistry` / `buildCacheServices` in
|
|
71
|
+
`dist/data/shared/graphql/cache/services.d.ts` — raw paths). **A query run through one
|
|
72
|
+
`createDataSDK()` instance is a cache hit on another instance only if both the host and the
|
|
73
|
+
`apiVersion` match.** Two instances targeting the same host but different `apiVersion` get fully
|
|
74
|
+
isolated cache bundles — version isolation matters because the per-entry cache key does not carry
|
|
75
|
+
the request URL, so sharing across versions would let one version read another's cached response.
|
|
76
|
+
The per-instance fetch pipeline (CSRF, `onStatus`) stays **isolated** — a cache _miss_ routes through
|
|
77
|
+
the calling SDK's own fetch. The registry is a plain module-level `Map`, so sharing only happens
|
|
78
|
+
**within the current JavaScript realm** — it does not persist across page reloads and is not shared
|
|
79
|
+
across browser tabs.
|
|
80
|
+
|
|
81
|
+
**Practical implication:** you do **not** need to hoist `createDataSDK()` into a singleton purely to
|
|
82
|
+
share cache. Calling it per-feature is fine as long as every instance resolves the same `baseUrl` +
|
|
83
|
+
`apiVersion`; the cache is shared underneath. (A singleton is still reasonable for other reasons.)
|
|
84
|
+
|
|
85
|
+
## The two refresh tools (keep them distinct)
|
|
86
|
+
|
|
87
|
+
There are **two unrelated mechanisms** for getting fresh data. Do not conflate `result.refresh()` (a
|
|
88
|
+
method on a live handle) with `cacheControl: "no-cache"` (a per-call option).
|
|
89
|
+
|
|
90
|
+
| | **Reactive refresh** | **Call-site cache control** |
|
|
91
|
+
| ------------------- | ---------------------------------------------------- | ----------------------------------------------------- |
|
|
92
|
+
| **API** | `result.subscribe(cb)` + `result.refresh()` | `cacheControl` on the query options bag |
|
|
93
|
+
| **Lifetime** | Long-lived handle; persists until you unsubscribe | One-shot, per call |
|
|
94
|
+
| **Pushes updates?** | Yes — `subscribe` fires on every subsequent snapshot | No — you read the returned value once |
|
|
95
|
+
| **PR** | #502 | #537 (W-22514759) |
|
|
96
|
+
| **Use when** | A mounted consumer should react / re-fetch on demand | "This specific read must bypass / only-use the cache" |
|
|
97
|
+
|
|
98
|
+
The reactive handle's lifecycle (subscribe/unsubscribe, framework teardown) is
|
|
99
|
+
[03-reactive-handle-lifecycle.md](./03-reactive-handle-lifecycle.md). This page covers `cacheControl`.
|
|
100
|
+
|
|
101
|
+
### `cacheControl` — the per-call policy
|
|
102
|
+
|
|
103
|
+
Values and mechanics are defined on `CacheControl` / `CacheControlShorthand` / `CacheControlMaxAge`
|
|
104
|
+
in the `.d.ts`. Two behaviours worth stating that the declaration does not:
|
|
105
|
+
|
|
106
|
+
- An **`only-if-cached` miss is not an exception.** The Promise resolves and the miss surfaces on
|
|
107
|
+
`result.errors`. This is offline-first: a miss is **expected, not an error** — when it genuinely is
|
|
108
|
+
a miss, render an empty state and **do not fall back to a network read** (a fallback defeats the
|
|
109
|
+
point of `only-if-cached`).
|
|
110
|
+
|
|
111
|
+
**A cache miss is discriminable; other failures are message-only.** `result.errors` is a flat
|
|
112
|
+
`GraphQLError[]` (`message`/`locations`/`path`/`extensions` — see `dist/core/data.d.ts`). A cache
|
|
113
|
+
miss on `only-if-cached` carries a typed discriminator — `extensions.code === "CACHE_MISS"` — so
|
|
114
|
+
you can identify it positively; every other SDK-synthesized failure sets no `code` and is told
|
|
115
|
+
apart by `message` text alone. Verified by executing every path (not just reading it — see
|
|
116
|
+
`error-shape-discriminators.spec.ts`):
|
|
117
|
+
|
|
118
|
+
| Failure | `result.errors?.[0]` |
|
|
119
|
+
| ----------------------------------------- | -------------------------------------------------------------------------------- |
|
|
120
|
+
| Cache miss (`only-if-cached`, cold cache) | `{ message: "No cached entry found …", extensions: { code: "CACHE_MISS" } }` |
|
|
121
|
+
| Wrong operation kind | `{ message: "…requires a GraphQL query, received <kind>." }` |
|
|
122
|
+
| Malformed GraphQL syntax | `{ message: "Syntax Error: …" }` (parser-generated) |
|
|
123
|
+
| Non-serializable variables | `{ message: "…must be JSON-serializable. …" }` |
|
|
124
|
+
| Network failure | `{ message: <the rejected fetch's Error.message, verbatim> }` |
|
|
125
|
+
| Server-returned GraphQL error | Whatever the server sent (`message`, optionally `path`/`locations`/`extensions`) |
|
|
126
|
+
|
|
127
|
+
`only-if-cached` never touches the network, so an errored result with no data is by construction a
|
|
128
|
+
cache miss — platform-sdk synthesizes the typed `CACHE_MISS` error (see `invokeResourceGraphQL`)
|
|
129
|
+
rather than leaking the underlying transport's opaque empty-message 504. Because the miss is
|
|
130
|
+
discriminable, you can branch on it positively even at a call site that can also fail for other
|
|
131
|
+
reasons: check `extensions.code === "CACHE_MISS"` first, then route anything else through your
|
|
132
|
+
normal application error path. It's pinned by `error-shape-discriminators.spec.ts` — if that test
|
|
133
|
+
ever fails, this table is stale.
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
const result = await sdk.graphql!.query({
|
|
137
|
+
query: GET_ACCOUNTS,
|
|
138
|
+
variables,
|
|
139
|
+
cacheControl: "only-if-cached",
|
|
140
|
+
});
|
|
141
|
+
if (result.errors?.[0]?.extensions?.code === "CACHE_MISS") {
|
|
142
|
+
// Cold cache. Do NOT fall back to the network; that defeats offline-first.
|
|
143
|
+
return renderEmptyState();
|
|
144
|
+
}
|
|
145
|
+
if (result.errors?.length) {
|
|
146
|
+
// Any other failure (syntax, variables, network, server) — not a cache miss.
|
|
147
|
+
return handleQueryError(result.errors);
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
- `cacheControl` does **not** affect the cache key (see above) — it only chooses the read/write
|
|
152
|
+
policy for that one call.
|
|
153
|
+
|
|
154
|
+
Mutations never touch the cache — `MutateOptions` has no `cacheControl`. To reflect a write, refresh
|
|
155
|
+
a held query (see [Refresh after a mutation](./03-reactive-handle-lifecycle.md#refresh-after-a-mutation)).
|
|
156
|
+
|
|
157
|
+
## Choosing a strategy
|
|
158
|
+
|
|
159
|
+
| Goal | Reach for |
|
|
160
|
+
| -------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- |
|
|
161
|
+
| Default reads, freshness within the default TTL is fine | Nothing — the default cache |
|
|
162
|
+
| Mounted consumer should reflect cache updates / re-fetch on demand | `subscribe` + `refresh` ([reactive handle lifecycle](./03-reactive-handle-lifecycle.md)) |
|
|
163
|
+
| Re-fetch a held query after a mutation | `result.refresh()` ([reactive handle lifecycle](./03-reactive-handle-lifecycle.md)) |
|
|
164
|
+
| One-off "this read must be fresh" (button, post-mutation one-shot) | `cacheControl: "no-cache"` |
|
|
165
|
+
| Offline-first — render only cached data, tolerate a miss as an empty state | `cacheControl: "only-if-cached"` |
|
|
166
|
+
| Data changes faster than the default TTL | `cacheControl: { type: "max-age", maxAge: N }` |
|
|
167
|
+
|
|
168
|
+
`no-cache` and `refresh()` both bypass the cache and write back; the difference is **`refresh()`
|
|
169
|
+
pushes to existing subscribers** and is a method on a live handle, while `no-cache` is a fresh
|
|
170
|
+
one-shot call with no subscribers.
|
|
171
|
+
|
|
172
|
+
## Uncached surfaces (Mosaic, OpenAI)
|
|
173
|
+
|
|
174
|
+
- **No cache exists.** Every `query()` is a network request.
|
|
175
|
+
- **`cacheControl` is silently ignored** — `"no-cache"`, `"only-if-cached"`, and `max-age` have no
|
|
176
|
+
effect. Notably `only-if-cached` will **not** raise `DataNotFoundError`, because there is no cache
|
|
177
|
+
layer to miss.
|
|
178
|
+
- **`subscribe` is real but only emits in response to `refresh()`** — there is no background cache to
|
|
179
|
+
push updates, so `refresh()` is the sole source of new snapshots and each one costs a network
|
|
180
|
+
request. See [03-reactive-handle-lifecycle.md](./03-reactive-handle-lifecycle.md).
|
|
181
|
+
- PR #502 flags the uncached `subscribe`/`refresh` edge semantics (re-emit-to-all, error fan-out,
|
|
182
|
+
ordering vs concurrent refresh) as a **known soft spot / follow-up**. Document conservatively; do
|
|
183
|
+
not over-promise behaviour there.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
<!-- sdk-symbols
|
|
2
|
+
QueryResult: dist/core/data.d.ts
|
|
3
|
+
QuerySnapshot: dist/core/data.d.ts
|
|
4
|
+
QuerySubscriber: dist/core/data.d.ts
|
|
5
|
+
Unsubscribe: dist/core/data.d.ts
|
|
6
|
+
MutationResult: dist/core/data.d.ts
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# Living with the reactive query handle
|
|
10
|
+
|
|
11
|
+
`sdk.graphql!.query()` resolves to a `QueryResult<T>` — a reactive handle, not a plain response.
|
|
12
|
+
Its shape and core behaviour are owned by `QueryResult` / `QuerySnapshot` / `QuerySubscriber` in
|
|
13
|
+
`dist/core/data.d.ts`. This page is the residue: the subscription behaviours the declaration doesn't
|
|
14
|
+
spell out, and how to live with the handle inside a UI framework's lifecycle.
|
|
15
|
+
|
|
16
|
+
## Two subscription behaviours to know
|
|
17
|
+
|
|
18
|
+
- Each `subscribe` is **independent** — unsubscribing one leaves the others live.
|
|
19
|
+
- `subscribe` does **not** fire on registration; it only pushes **later** snapshots. Set initial
|
|
20
|
+
state from the **awaited** snapshot (`result.data` / `result.errors`), not from the subscriber
|
|
21
|
+
callback.
|
|
22
|
+
- `result.refresh()` broadcasts the new snapshot to **all** current subscribers, not just the caller.
|
|
23
|
+
|
|
24
|
+
## The lifecycle
|
|
25
|
+
|
|
26
|
+
Read the initial snapshot, register a subscriber for later snapshots, and **always unsubscribe when
|
|
27
|
+
the consumer goes away** (component unmount, effect re-run, view teardown) so the subscription
|
|
28
|
+
doesn't leak:
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
const result = await sdk.graphql!.query<GetAccountsQuery>({ query: GET_ACCOUNTS, variables });
|
|
32
|
+
render(result.data, result.errors); // initial snapshot (subscribe won't re-deliver this)
|
|
33
|
+
|
|
34
|
+
const unsub = result.subscribe(({ data, errors }) => render(data, errors)); // live updates
|
|
35
|
+
await result.refresh(); // force re-fetch → pushes to subscribers
|
|
36
|
+
// later, when the consumer tears down:
|
|
37
|
+
unsub();
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Managing the subscription in a framework
|
|
41
|
+
|
|
42
|
+
Whatever reactive/lifecycle primitive your UI layer uses — a React effect, a Vue/Svelte lifecycle
|
|
43
|
+
hook, a web-component `connected`/`disconnectedCallback`, a store teardown — the same three
|
|
44
|
+
obligations hold:
|
|
45
|
+
|
|
46
|
+
- Kick off `query()` on mount/setup and store the resolved `result`, so you can call `refresh()` on
|
|
47
|
+
it later (e.g. behind a "Refresh" button).
|
|
48
|
+
- Push each `subscribe` snapshot into your reactive state so the view re-renders.
|
|
49
|
+
- Run `unsub()` in the teardown path, and guard against a late-resolving `query()` writing state
|
|
50
|
+
**after** teardown — track a `cancelled` flag. (Because `subscribe` does not fire on registration,
|
|
51
|
+
the awaited snapshot is the only place your initial state comes from.)
|
|
52
|
+
|
|
53
|
+
## Refresh after a mutation
|
|
54
|
+
|
|
55
|
+
Mutations resolve to a `MutationResult` — a one-shot `{ data, errors }` with **no**
|
|
56
|
+
`subscribe`/`refresh` (see `MutationResult` in the `.d.ts`; re-running a mutation on refresh would be
|
|
57
|
+
dangerous). A mutation **does not throw on a GraphQL-level failure** — exactly like a query, the
|
|
58
|
+
outcome surfaces on the resolved result (`result.errors`, with `result.data` left `undefined`). A
|
|
59
|
+
failed operation — or even a degenerate `{ data: undefined, errors: [] }` response from the server —
|
|
60
|
+
will **not** raise. So **guard on the result before treating the write as done**, and only refresh a
|
|
61
|
+
held query after a clean mutation:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
const result = await sdk.graphql!.mutate({ mutation: CREATE_ACCOUNT, variables: { input } });
|
|
65
|
+
if (result.errors?.length || !result.data) {
|
|
66
|
+
// Mutation didn't succeed. Surface it through your normal error path — do NOT refresh a
|
|
67
|
+
// write that didn't happen. (`!result.data` also catches the empty-errors degenerate case.)
|
|
68
|
+
return handleMutationFailure(result.errors);
|
|
69
|
+
}
|
|
70
|
+
await accountsResult.refresh(); // re-fetches, bypasses cache, pushes to subscribers
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## When to reach for this vs the default cache
|
|
74
|
+
|
|
75
|
+
_Whether_ to use `subscribe`/`refresh` at all — versus just relying on the default cache or a
|
|
76
|
+
one-shot `cacheControl` — is a strategy call. See the two-refresh-tools table and decision matrix in
|
|
77
|
+
[02-caching-and-freshness.md](./02-caching-and-freshness.md); this page is not duplicated there.
|
|
78
|
+
|
|
79
|
+
## Uncached surfaces
|
|
80
|
+
|
|
81
|
+
On Mosaic / OpenAI the handle exists but `subscribe` only emits in response to `refresh()` — there
|
|
82
|
+
is no background cache to push updates, and each `refresh()` costs a network request. See
|
|
83
|
+
[Uncached surfaces](./02-caching-and-freshness.md#uncached-surfaces-mosaic-openai) and
|
|
84
|
+
[04-surfaces-and-guards.md](./04-surfaces-and-guards.md).
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<!-- sdk-symbols
|
|
2
|
+
DataSDK: dist/core/data.d.ts
|
|
3
|
+
DataSDKGraphQL: dist/core/data.d.ts
|
|
4
|
+
DataSDKOptions: dist/data/index.d.ts
|
|
5
|
+
WebAppDataSDKOptions: dist/data/index.d.ts
|
|
6
|
+
StatusCallback: dist/data/index.d.ts
|
|
7
|
+
MosaicDataSDKOptions: dist/data/mosaic/index.d.ts
|
|
8
|
+
NodeOfConnection: dist/data/index.d.ts
|
|
9
|
+
-->
|
|
10
|
+
|
|
11
|
+
# Surfaces & guards
|
|
12
|
+
|
|
13
|
+
`createDataSDK()` returns a `DataSDK` with two data-access members — `graphql` (the primary route)
|
|
14
|
+
and `fetch` (the escape hatch; see [01-grounding-and-precedence.md](./01-grounding-and-precedence.md)
|
|
15
|
+
for which door to pick). Both are **optional**: a surface that doesn't support data operations
|
|
16
|
+
resolves to a bare `{}` rather than throwing (see `DataSDK` in `dist/core/data.d.ts`). This page is
|
|
17
|
+
the residue: how to guard that optionality, what `.fetch` is and how it differs from `.graphql`, how
|
|
18
|
+
to read `errors`, and the per-surface options.
|
|
19
|
+
|
|
20
|
+
## `sdk.graphql!` vs guard — a surface decision
|
|
21
|
+
|
|
22
|
+
Whether to assert `sdk.graphql` after `createDataSDK()` is a **surface** call, not a style call:
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// `!` form — WebApp-exclusive bundles only.
|
|
26
|
+
const result = await sdk.graphql!.query<...>({ query, variables });
|
|
27
|
+
|
|
28
|
+
// Portable guard form — safe on every surface. Use when the bundle is not WebApp-exclusive.
|
|
29
|
+
if (!sdk.graphql) {
|
|
30
|
+
// No data SDK on this surface — degrade gracefully (render empty, throw, or feature-flag off).
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const result = await sdk.graphql.query<...>({ query, variables });
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Use `!` **only** if the bundle ships to WebApp exclusively. A bare `sdk.graphql!` that later ships to
|
|
37
|
+
another surface throws `Cannot read properties of undefined` at runtime, and TypeScript **cannot**
|
|
38
|
+
catch it because `!` silences exactly that check. The same `!`-vs-guard decision applies to
|
|
39
|
+
`sdk.fetch!`.
|
|
40
|
+
|
|
41
|
+
## `.fetch` — the escape hatch
|
|
42
|
+
|
|
43
|
+
`sdk.fetch` is the SDK's authenticated HTTP primitive: the same call shape as the global `fetch`
|
|
44
|
+
(`typeof fetch` — a URL/`Request` plus optional `RequestInit`, resolving to a `Response`), enriched
|
|
45
|
+
per surface so you don't reassemble auth and transport concerns by hand. Reach for it when the
|
|
46
|
+
GraphQL schema doesn't expose what you need — REST (`/services/data/…`), Apex REST
|
|
47
|
+
(`/services/apexrest/…`), Connect, or blob/file downloads. Default to `.graphql`; `.fetch` is the
|
|
48
|
+
route for everything that has no GraphQL representation.
|
|
49
|
+
|
|
50
|
+
What it carries, per surface:
|
|
51
|
+
|
|
52
|
+
- **WebApp** — CSRF token handling with automatic retry on token rejection, the `Accept-Language`
|
|
53
|
+
and client-context headers the platform expects, and relative URLs resolved against the app's API
|
|
54
|
+
base path. Your `onStatus` callbacks fire on `.fetch` responses too, so a `401` handler
|
|
55
|
+
re-authenticates for `.fetch` and `.graphql` alike. On this surface `.graphql` is itself built on
|
|
56
|
+
top of `.fetch`.
|
|
57
|
+
- **Mosaic** — no `.fetch`. `MosaicDataSDK` only implements `graphql`; guard for `.fetch`'s absence
|
|
58
|
+
rather than assuming it.
|
|
59
|
+
- **OpenAI** — no `.fetch`. That surface exposes only `.graphql` via an MCP tool, so guard for
|
|
60
|
+
`.fetch`'s absence rather than assuming it.
|
|
61
|
+
|
|
62
|
+
Two things `.fetch` does **not** do, both by contrast with `.graphql`:
|
|
63
|
+
|
|
64
|
+
- **No cache.** `.fetch` responses are never cached by the SDK — caching is a `.graphql`-only
|
|
65
|
+
concern (see [02-caching-and-freshness.md](./02-caching-and-freshness.md)). Layer your own if you
|
|
66
|
+
need it.
|
|
67
|
+
- **No `errors` envelope.** `.fetch` resolves to a raw `Response`, so the HTTP status _is_ the
|
|
68
|
+
signal — check `response.ok` / `response.status` and parse the body yourself, exactly as with the
|
|
69
|
+
platform `fetch`. This is the inverse of the `.graphql` "HTTP 200 ≠ success" model below.
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
if (!sdk.fetch) {
|
|
73
|
+
// No fetch on this surface (e.g. OpenAI) — degrade gracefully.
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// REST read — the standard Response model: status is the signal.
|
|
78
|
+
const res = await sdk.fetch("/services/data/v64.0/sobjects/Account/001xx000003DGb0AAG");
|
|
79
|
+
if (!res.ok) {
|
|
80
|
+
throw new Error(`Account read failed: ${res.status}`);
|
|
81
|
+
}
|
|
82
|
+
const account = await res.json();
|
|
83
|
+
|
|
84
|
+
// Apex REST — POST to a custom endpoint.
|
|
85
|
+
const apexRes = await sdk.fetch("/services/apexrest/MyService", {
|
|
86
|
+
method: "POST",
|
|
87
|
+
headers: { "Content-Type": "application/json" },
|
|
88
|
+
body: JSON.stringify({ recordId: "001xx000003DGb0AAG" }),
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Guard `sdk.fetch` exactly as you guard `sdk.graphql` — the `!`-vs-guard decision above applies
|
|
93
|
+
identically.
|
|
94
|
+
|
|
95
|
+
## HTTP 200 ≠ success — always read `.graphql` `errors`
|
|
96
|
+
|
|
97
|
+
`DataSDKGraphQL.query` / `.mutate` **always resolve**; they do not reject. Every failure (GraphQL
|
|
98
|
+
errors, parse errors, wrong operation kind, non-serializable variables, network errors) is funnelled
|
|
99
|
+
into the resolved `result.errors` array (see `DataSDKGraphQL` in the `.d.ts`). So a settled Promise
|
|
100
|
+
is **not** a successful result — you must inspect `result.errors`. (This is `.graphql`-specific:
|
|
101
|
+
`.fetch` reports failure through the `Response` status instead, per the section above.)
|
|
102
|
+
|
|
103
|
+
Choose the product-appropriate stance; all three inspect `result.errors`:
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
// Strict — any errors = failure
|
|
107
|
+
if (result.errors?.length) throw new Error(result.errors.map((e) => e.message).join("; "));
|
|
108
|
+
|
|
109
|
+
// Tolerant — log, use whatever data came back (partial success)
|
|
110
|
+
if (result.errors?.length) console.warn("GraphQL partial errors:", result.errors);
|
|
111
|
+
|
|
112
|
+
// Discriminated — fail only when NO data returned
|
|
113
|
+
if (!result.data && result.errors?.length) {
|
|
114
|
+
throw new Error(result.errors.map((e) => e.message).join("; "));
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Per-surface options
|
|
119
|
+
|
|
120
|
+
`createDataSDK(options)` takes a `DataSDKOptions` bag; the fields you set depend on the surface. The
|
|
121
|
+
declarations own the field lists — this is just the routing:
|
|
122
|
+
|
|
123
|
+
- **WebApp** — `WebAppDataSDKOptions` (`options.webapp`): `basePath`, and `onStatus` per-HTTP-status
|
|
124
|
+
callbacks (each a `StatusCallback`) for cases like re-authenticating on a `401`. This is the
|
|
125
|
+
cached surface — see [02-caching-and-freshness.md](./02-caching-and-freshness.md).
|
|
126
|
+
- **Mosaic** — `MosaicDataSDKOptions` (`options.mosaic`): server-side (Node) auth via `instanceUrl` /
|
|
127
|
+
`accessToken` / `apiVersion`. Uncached.
|
|
128
|
+
- **OpenAI** — no surface options bag; delegates to an MCP tool. Uncached, and it exposes no header
|
|
129
|
+
channel, so per-request `headers` are ignored there.
|
|
130
|
+
|
|
131
|
+
## Uncached surfaces (Mosaic, OpenAI)
|
|
132
|
+
|
|
133
|
+
- No cache exists — every `query()` is a network request, and `cacheControl` is silently ignored.
|
|
134
|
+
- `subscribe` is real but only emits in response to `refresh()`.
|
|
135
|
+
|
|
136
|
+
The full uncached-surface behaviour and the caching model it contrasts with are in
|
|
137
|
+
[02-caching-and-freshness.md](./02-caching-and-freshness.md#uncached-surfaces-mosaic-openai) and
|
|
138
|
+
[03-reactive-handle-lifecycle.md](./03-reactive-handle-lifecycle.md). The `query`/`mutate` shape is
|
|
139
|
+
identical across surfaces, so the same call site runs everywhere — only caching differs.
|
|
140
|
+
|
|
141
|
+
## `NodeOfConnection` — unwrapping a connection
|
|
142
|
+
|
|
143
|
+
Read queries that return a Relay-style `{ edges: [{ node }] }` connection can have the element type
|
|
144
|
+
unwrapped with `NodeOfConnection<T>` (see the declaration in `dist/data/index.d.ts`). It resolves to
|
|
145
|
+
`never` when `T` isn't such a connection, and it is **read-side only** — only `query` returns
|
|
146
|
+
connections, so it never applies to `mutate` results.
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
<!-- sdk-symbols
|
|
2
|
+
createDataSDK: dist/data/index.d.ts
|
|
3
|
+
gql: dist/data/gql.d.ts
|
|
4
|
+
NodeOfConnection: dist/data/index.d.ts
|
|
5
|
+
CacheControl: dist/core/data.d.ts
|
|
6
|
+
-->
|
|
7
|
+
|
|
8
|
+
# Migration — old Data SDK → `@salesforce/platform-sdk`
|
|
9
|
+
|
|
10
|
+
The Data SDK changed in two breaking ways (PR #502, shipped in `@salesforce/platform-sdk` v10.10.1):
|
|
11
|
+
|
|
12
|
+
1. **Package renamed** — `@salesforce/sdk-data` → **`@salesforce/platform-sdk`**.
|
|
13
|
+
2. **`sdk.graphql` reshaped** from a **callable method** into a **namespace** with `.query()` and
|
|
14
|
+
`.mutate()`, both taking an **options object**.
|
|
15
|
+
|
|
16
|
+
> Any remaining `@salesforce/sdk-data` **import in consumer source** is a leftover from before this
|
|
17
|
+
> migration — not canonical. Always import from `@salesforce/platform-sdk`. (This does not cover
|
|
18
|
+
> intentional historical/comparative references elsewhere in the repo — e.g. this package's own
|
|
19
|
+
> `PACKAGING-STRATEGY.md` or the migration-facing skill docs — which are expected to keep the old
|
|
20
|
+
> name.)
|
|
21
|
+
|
|
22
|
+
This is the **only** page where the dead callable API appears, for comparison. The **new-side**
|
|
23
|
+
snippets below are illustrative of the transition, **not** the source of truth for the current call
|
|
24
|
+
contract — for the authoritative new signatures read `dist/core/data.d.ts` and `dist/data/index.d.ts`
|
|
25
|
+
(tier-2a). If a new-side snippet here has drifted from the installed declaration, the declaration
|
|
26
|
+
wins.
|
|
27
|
+
|
|
28
|
+
## 1. Import
|
|
29
|
+
|
|
30
|
+
```diff
|
|
31
|
+
- import { createDataSDK, gql, type NodeOfConnection } from "@salesforce/sdk-data";
|
|
32
|
+
+ import { createDataSDK, gql, type NodeOfConnection, type CacheControl } from "@salesforce/platform-sdk";
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`createDataSDK`, `gql`, `NodeOfConnection`, and `CacheControl` all export from
|
|
36
|
+
`@salesforce/platform-sdk`.
|
|
37
|
+
|
|
38
|
+
## 2. Query call
|
|
39
|
+
|
|
40
|
+
```diff
|
|
41
|
+
- // OLD — callable, positional args, returns a response object with .data / .errors
|
|
42
|
+
- const response = await sdk.graphql?.<GetAccountsQuery, GetAccountsQueryVariables>(GET_ACCOUNTS, variables);
|
|
43
|
+
- const accounts = response?.data?.uiapi?.query?.Account?.edges ?? [];
|
|
44
|
+
- if (response?.errors?.length) { /* ... */ }
|
|
45
|
+
|
|
46
|
+
+ // NEW — namespace method, options bag, returns a reactive QueryResult
|
|
47
|
+
+ const result = await sdk.graphql!.query<GetAccountsQuery, GetAccountsQueryVariables>({
|
|
48
|
+
+ query: GET_ACCOUNTS,
|
|
49
|
+
+ variables,
|
|
50
|
+
+ });
|
|
51
|
+
+ const accounts = result.data?.uiapi?.query?.Account?.edges ?? [];
|
|
52
|
+
+ if (result.errors?.length) { /* ... */ }
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Passing the generated `<GetAccountsQuery>` type parameter makes `result.data` fully typed — no
|
|
56
|
+
`as any` cast needed.
|
|
57
|
+
|
|
58
|
+
| Old | New |
|
|
59
|
+
| ------------------------------------- | -------------------------------------------------------- |
|
|
60
|
+
| `sdk.graphql?.(QUERY, vars)` | `sdk.graphql!.query({ query: QUERY, variables: vars })` |
|
|
61
|
+
| positional `<T, V>(query, variables)` | type params on `query<T, V>(options)` |
|
|
62
|
+
| `response.data` / `response.errors` | `result.data` / `result.errors` (same fields) |
|
|
63
|
+
| (none) | `result.subscribe(cb)` / `result.refresh()` — new handle |
|
|
64
|
+
| (none) | `cacheControl` option — new per-call cache policy |
|
|
65
|
+
|
|
66
|
+
## 3. Mutation call
|
|
67
|
+
|
|
68
|
+
The old callable form was used for mutations too. Mutations now have their own method, and the
|
|
69
|
+
operation key is **`mutation`**, not `query`:
|
|
70
|
+
|
|
71
|
+
```diff
|
|
72
|
+
- const response = await sdk.graphql?.<CreateAccountMutation>(CREATE_ACCOUNT, { input });
|
|
73
|
+
+ const { data, errors } = await sdk.graphql!.mutate<CreateAccountMutation, CreateAccountMutationVariables>({
|
|
74
|
+
+ mutation: CREATE_ACCOUNT, // key is `mutation`, not `query`
|
|
75
|
+
+ variables: { input },
|
|
76
|
+
+ });
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
`mutate()` returns `{ data, errors }` only — **no** `subscribe`/`refresh`, and it is **never cached**.
|
|
80
|
+
To refresh a list after a mutation, hold the query `result` and call `result.refresh()` (see
|
|
81
|
+
[03-reactive-handle-lifecycle.md](./03-reactive-handle-lifecycle.md)).
|
|
82
|
+
|
|
83
|
+
## 4. Optional chaining → non-null assertion
|
|
84
|
+
|
|
85
|
+
The old guidance was "always use optional chaining" (`sdk.graphql?.()`). Real consumer code now uses
|
|
86
|
+
the **non-null assertion** after `createDataSDK()` — but whether `!` or a guard is correct is a
|
|
87
|
+
surface decision, covered in [04-surfaces-and-guards.md](./04-surfaces-and-guards.md):
|
|
88
|
+
|
|
89
|
+
```diff
|
|
90
|
+
- const response = await sdk.graphql?.(QUERY, vars);
|
|
91
|
+
+ const result = await sdk.graphql!.query({ query: QUERY, variables: vars });
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 5. Generated types — unchanged generation, new call placement
|
|
95
|
+
|
|
96
|
+
The generated operation type **names** (`<Op>Query` / `<Op>QueryVariables` / `<Op>Mutation` /
|
|
97
|
+
`<Op>MutationVariables`) are **unchanged**. Only **where** the type params attach moves — from the
|
|
98
|
+
callable to the namespace method:
|
|
99
|
+
|
|
100
|
+
```diff
|
|
101
|
+
- await sdk.graphql?.<GetAccountsQuery, GetAccountsQueryVariables>(GET_ACCOUNTS, variables);
|
|
102
|
+
+ await sdk.graphql!.query<GetAccountsQuery, GetAccountsQueryVariables>({ query: GET_ACCOUNTS, variables });
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`NodeOfConnection` (read-side) still unwraps a connection the same way. **Regenerating** the types is
|
|
106
|
+
a repo/skill command, not something this package owns — this page only covers where the generated
|
|
107
|
+
names now attach.
|
|
108
|
+
|
|
109
|
+
## 6. New capabilities unlocked while migrating
|
|
110
|
+
|
|
111
|
+
The reshape also unlocked the freshness features (none required to get back to working). The one
|
|
112
|
+
migration-specific gotcha: **caching is now on by default** (WebApp) — if any code relied on every
|
|
113
|
+
call hitting the network, add `cacheControl: "no-cache"` to it. The reactive `subscribe`/`refresh`
|
|
114
|
+
handle and the rest of `CacheControl` are in
|
|
115
|
+
[02-caching-and-freshness.md](./02-caching-and-freshness.md) and
|
|
116
|
+
[03-reactive-handle-lifecycle.md](./03-reactive-handle-lifecycle.md).
|
|
117
|
+
|
|
118
|
+
## 7. Migration checklist
|
|
119
|
+
|
|
120
|
+
- [ ] Replace every `@salesforce/sdk-data` import with `@salesforce/platform-sdk`.
|
|
121
|
+
- [ ] Convert every `sdk.graphql?.(q, v)` query to `sdk.graphql!.query({ query: q, variables: v })`.
|
|
122
|
+
- [ ] Convert every mutation call to `sdk.graphql!.mutate({ mutation: m, variables: v })` (key is `mutation`).
|
|
123
|
+
- [ ] Move generated type params onto `query<T,V>` / `mutate<T,V>`.
|
|
124
|
+
- [ ] Rename `response` → `result`; `response.data`/`.errors` → `result.data`/`.errors`.
|
|
125
|
+
- [ ] Add `cacheControl: "no-cache"` to any call that genuinely must always hit the network.
|
|
126
|
+
- [ ] Re-check the `!`-vs-guard decision per surface ([surfaces & guards](./04-surfaces-and-guards.md)).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/platform-sdk",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.70.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -112,15 +112,20 @@
|
|
|
112
112
|
},
|
|
113
113
|
"./package.json": "./package.json"
|
|
114
114
|
},
|
|
115
|
+
"directories": {
|
|
116
|
+
"doc": "docs"
|
|
117
|
+
},
|
|
115
118
|
"files": [
|
|
116
|
-
"dist"
|
|
119
|
+
"dist",
|
|
120
|
+
"docs/data"
|
|
117
121
|
],
|
|
118
122
|
"scripts": {
|
|
119
123
|
"build": "vite build",
|
|
120
124
|
"clean": "rm -rf dist",
|
|
121
125
|
"dev": "vite build --watch",
|
|
122
|
-
"test": "vitest run --typecheck && npm run test:examples",
|
|
126
|
+
"test": "vitest run --typecheck && npm run test:examples && npm run test:docs",
|
|
123
127
|
"test:examples": "tsx scripts/check-examples.mts",
|
|
128
|
+
"test:docs": "tsx scripts/check-docs-symbols.mts",
|
|
124
129
|
"test:watch": "vitest",
|
|
125
130
|
"test:coverage": "vitest run --coverage --typecheck"
|
|
126
131
|
},
|
|
@@ -134,7 +139,7 @@
|
|
|
134
139
|
"@conduit-client/service-pubsub": "3.19.6",
|
|
135
140
|
"@conduit-client/service-retry": "3.19.6",
|
|
136
141
|
"@conduit-client/utils": "3.19.6",
|
|
137
|
-
"@salesforce/jsonrpc": "^11.
|
|
142
|
+
"@salesforce/jsonrpc": "^11.70.0",
|
|
138
143
|
"@salesforce/sf-embedding-bridge": "2.2.5-rc.2"
|
|
139
144
|
},
|
|
140
145
|
"peerDependencies": {
|