@replohq/sdk 2.18.0 → 2.18.1

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.
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sdk",
3
- "version": "2.18.0",
3
+ "version": "2.18.1",
4
4
  "branch": "HEAD",
5
- "commit": "aa9296f4104e4be2271247d775a27fa69288bbe8",
6
- "builtAt": "2026-11-02T03:31:48.722Z"
5
+ "commit": "31098580021d6a05449acd44d0850054eec6e0ec",
6
+ "builtAt": "2026-11-02T23:35:33.205Z"
7
7
  };
@@ -0,0 +1,15 @@
1
+ import { CanopyError } from "../lib/canopy-error";
2
+ /** A loader argument that can't be sent to canopy-api, such as a function. */
3
+ export declare class LoaderArgsError extends CanopyError {
4
+ }
5
+ /**
6
+ * Finds the first function in a loader's args. JSON drops functions silently,
7
+ * so canopy-api would receive the args without that key and reject them. The
8
+ * usual cause is a value exported from a "use client" file and imported into a
9
+ * server component: on the server, every such export is a client reference
10
+ * (a function), never the value itself.
11
+ */
12
+ export declare function getLoaderArgsError({ loaderKey, args, }: {
13
+ loaderKey: string;
14
+ args: Record<string, unknown>;
15
+ }): LoaderArgsError | null;
@@ -0,0 +1,46 @@
1
+ import { CanopyError } from "../lib/canopy-error";
2
+ const CLIENT_REFERENCE_TAG = Symbol.for("react.client.reference");
3
+ class LoaderArgsError extends CanopyError {
4
+ }
5
+ function findFunctionArg(value, path, seen) {
6
+ if (typeof value === "function") {
7
+ return {
8
+ path,
9
+ isClientReference: Reflect.get(value, "$$typeof") === CLIENT_REFERENCE_TAG
10
+ };
11
+ }
12
+ if (typeof value !== "object" || value === null || seen.has(value) || typeof Reflect.get(value, "toJSON") === "function") {
13
+ return null;
14
+ }
15
+ seen.add(value);
16
+ for (const [key, child] of Object.entries(value)) {
17
+ const childPath = Array.isArray(value) ? `${path}[${key}]` : `${path}.${key}`;
18
+ const found = findFunctionArg(child, childPath, seen);
19
+ if (found !== null) {
20
+ return found;
21
+ }
22
+ }
23
+ return null;
24
+ }
25
+ function getLoaderArgsError({
26
+ loaderKey,
27
+ args
28
+ }) {
29
+ const found = findFunctionArg(args, "args", /* @__PURE__ */ new Set());
30
+ if (found === null) {
31
+ return null;
32
+ }
33
+ const what = found.isClientReference ? "a client reference, not a value" : "a function";
34
+ return new LoaderArgsError({
35
+ message: `[Replo] Loader ${loaderKey}: ${found.path} is ${what}, so it can't be sent to the loader. It was probably imported from a "use client" file into a server component. Move the value into a module without "use client" and import it from both places.`,
36
+ additionalData: {
37
+ loaderKey,
38
+ path: found.path,
39
+ isClientReference: found.isClientReference
40
+ }
41
+ });
42
+ }
43
+ export {
44
+ LoaderArgsError,
45
+ getLoaderArgsError
46
+ };
@@ -21,6 +21,9 @@ export declare class LoaderOutputError extends CanopyError {
21
21
  *
22
22
  * Malformed output is logged with the loader key and the failing paths, then
23
23
  * rejected with a `LoaderOutputError`; callers decide whether to fail soft.
24
+ * Args holding a function are logged and rejected with a `LoaderArgsError`
25
+ * before any request, since JSON would drop them and canopy-api would reject
26
+ * the rest. The log matters because `PrefetchedLoaders` swallows rejections.
24
27
  */
25
28
  export declare function fetchValidatedLoaderOutput({ loaderKey, args, }: {
26
29
  loaderKey: string;
@@ -9,6 +9,7 @@ import {
9
9
  smileRewardsOutputSchema,
10
10
  smileVipTiersOutputSchema
11
11
  } from "./integration-loader-schemas";
12
+ import { getLoaderArgsError } from "./loader-args";
12
13
  import { DATA_LOADER_KEYS } from "./loader-keys";
13
14
  import {
14
15
  contentfulEntriesLoaderOutputSchema,
@@ -93,6 +94,11 @@ async function fetchValidatedLoaderOutput({
93
94
  loaderKey,
94
95
  args
95
96
  }) {
97
+ const argsError = getLoaderArgsError({ loaderKey, args });
98
+ if (argsError !== null) {
99
+ console.error(argsError.message);
100
+ throw argsError;
101
+ }
96
102
  const gateway = await getCanopyApiGateway();
97
103
  const { data } = await gateway.invokeLoader({ loaderKey, args });
98
104
  const knownKey = dataLoaderKeySchema.safeParse(loaderKey);
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sdk",
3
- "version": "2.18.0",
3
+ "version": "2.18.1",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sdk",
6
- "version": "2.18.0",
6
+ "version": "2.18.1",
7
7
  "branch": "HEAD",
8
- "commit": "aa9296f4104e4be2271247d775a27fa69288bbe8",
9
- "builtAt": "2026-11-02T03:31:48.722Z"
8
+ "commit": "31098580021d6a05449acd44d0850054eec6e0ec",
9
+ "builtAt": "2026-11-02T23:35:33.205Z"
10
10
  },
11
11
  "description": "Replo SDK — cart, analytics, and data loaders for agent-built Next.js sites.",
12
12
  "license": "SEE LICENSE IN LICENSE",