@decocms/tanstack 7.2.1 → 7.2.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/tanstack",
3
- "version": "7.2.1",
3
+ "version": "7.2.3",
4
4
  "type": "module",
5
5
  "description": "Deco framework binding for TanStack Start + Cloudflare Workers",
6
6
  "repository": {
@@ -12,7 +12,8 @@
12
12
  "exports": {
13
13
  ".": "./src/index.ts",
14
14
  "./vite": "./src/vite/plugin.js",
15
- "./daemon": "./src/daemon/index.ts"
15
+ "./daemon": "./src/daemon/index.ts",
16
+ "./sdk/createInvoke": "./src/sdk/createInvoke.ts"
16
17
  },
17
18
  "scripts": {
18
19
  "build": "tsc",
@@ -21,9 +22,9 @@
21
22
  "lint:unused": "knip"
22
23
  },
23
24
  "dependencies": {
24
- "@decocms/blocks": "7.2.1",
25
- "@decocms/blocks-admin": "7.2.1",
26
- "@decocms/blocks-cli": "7.2.1",
25
+ "@decocms/blocks": "7.2.3",
26
+ "@decocms/blocks-admin": "7.2.3",
27
+ "@decocms/blocks-cli": "7.2.3",
27
28
  "@deco-cx/warp-node": "^0.3.16",
28
29
  "fast-json-patch": "^3.1.0",
29
30
  "ws": "^8.18.0"
package/src/index.ts CHANGED
@@ -28,5 +28,17 @@ export {
28
28
  decoStringifySearch,
29
29
  } from "./sdk/router";
30
30
  export type { CreateDecoRouterOptions } from "./sdk/router";
31
- export { createInvokeFn } from "./sdk/createInvoke";
32
- export type { InvokeFnOpts } from "./sdk/createInvoke";
31
+ // createInvokeFn is intentionally NOT re-exported from this root barrel.
32
+ // Its body contains a `createServerFn(...).handler(...)` call that is not a
33
+ // top-level variable declarator (it's returned from a factory function) --
34
+ // TanStack Start's compiler statically scans every file it processes for
35
+ // this pattern and throws "createServerFn must be assigned to a variable!"
36
+ // on ANY occurrence, whether or not the factory is ever actually called.
37
+ // Re-exporting it here would pull sdk/createInvoke.ts into the module graph
38
+ // of every site that imports anything else from this barrel (which is
39
+ // every site, for DecoRootLayout etc.), tripping that check even though
40
+ // createInvokeFn itself is never invoked at runtime -- it exists purely as
41
+ // a source-of-truth template that blocks-cli's generate-invoke.ts statically
42
+ // parses (never imports) to emit real top-level createServerFn declarations
43
+ // into each site's own src/server/invoke.gen.ts. Import it from the
44
+ // dedicated "@decocms/tanstack/sdk/createInvoke" subpath instead.
@@ -1,12 +1,31 @@
1
1
  /**
2
- * Generic bridge that turns any async function into a TanStack Start server function.
2
+ * Type-only marker for apps-* `invoke.ts` codegen templates (e.g.
3
+ * `@decocms/apps-vtex/invoke.ts`) — NOT a real, callable createServerFn
4
+ * wrapper. Deliberately does not call `createServerFn` at runtime.
3
5
  *
4
- * Used by @decocms/apps to expose commerce actions/loaders as typed
5
- * `invoke.*` calls that execute on the server with full credentials.
6
+ * `invoke.ts` files are never imported/executed by any real site they
7
+ * are a source-of-truth that `blocks-cli`'s `generate-invoke.ts` statically
8
+ * parses (via AST inspection of each `createInvokeFn(action, opts)` call
9
+ * site's arguments) to emit real, literal top-level `createServerFn(...)`
10
+ * declarations into each site's own `src/server/invoke.gen.ts`. This
11
+ * function's ONLY job is to give those template files a correctly-typed
12
+ * call site to author against.
13
+ *
14
+ * It must not contain a real `createServerFn(...)` call: TanStack Start's
15
+ * compiler statically scans every file reachable by a site's SSR bundle for
16
+ * that literal pattern and throws "createServerFn must be assigned to a
17
+ * variable!" on ANY occurrence that isn't a top-level declarator — even one
18
+ * inside a function body that's never actually invoked. `invoke.ts` isn't
19
+ * supposed to be reachable by a site's bundler at all (it's excluded from
20
+ * every apps-* package's public exports map), but Vite's `optimizeDeps`
21
+ * pre-bundling has been observed sweeping it in regardless of that
22
+ * exports-map restriction — so the only fully robust fix is for this file
23
+ * to never contain the literal pattern the compiler is looking for, full
24
+ * stop, regardless of what does or doesn't end up bundled.
6
25
  *
7
26
  * @example
8
27
  * ```ts
9
- * import { createInvokeFn } from "@decocms/start/sdk/createInvoke";
28
+ * import { createInvokeFn } from "@decocms/tanstack/sdk/createInvoke";
10
29
  * import { addItemsToCart } from "./actions/checkout";
11
30
  *
12
31
  * export const invoke = {
@@ -20,12 +39,13 @@
20
39
  * },
21
40
  * },
22
41
  * };
23
- *
24
- * // Client-side usage:
25
- * await invoke.vtex.actions.addItemsToCart({ data: { orderFormId, orderItems } });
26
42
  * ```
43
+ *
44
+ * The real, callable version of each action above only exists in the
45
+ * site's generated `src/server/invoke.gen.ts` (run `npm run generate:invoke`),
46
+ * which uses real top-level `createServerFn(...)` calls directly — never
47
+ * this function.
27
48
  */
28
- import { createServerFn } from "@tanstack/react-start";
29
49
 
30
50
  export interface InvokeFnOpts {
31
51
  /**
@@ -37,21 +57,21 @@ export interface InvokeFnOpts {
37
57
  }
38
58
 
39
59
  /**
40
- * Transforms an async function into a `createServerFn` wrapper.
41
- *
42
- * - Client calls: `fn({ data: input })`
43
- * - Server executes: `action(input)`
44
- * - If `unwrap: true`, extracts `.data` from VtexFetchResult-shaped results
60
+ * Template-only see the module doc comment above. Throws if actually
61
+ * called at runtime, since that would mean something imported an
62
+ * `invoke.ts` template file directly instead of using the site's generated
63
+ * `invoke.gen.ts`, which is itself a bug worth surfacing loudly rather than
64
+ * silently returning wrong behavior.
45
65
  */
46
66
  export function createInvokeFn<TInput, TOutput>(
47
- action: (input: TInput) => Promise<TOutput>,
48
- opts?: InvokeFnOpts,
67
+ _action: (input: TInput) => Promise<TOutput>,
68
+ _opts?: InvokeFnOpts,
49
69
  ): (ctx: { data: TInput }) => Promise<TOutput> {
50
- return createServerFn({ method: "POST" }).handler(async (ctx) => {
51
- const result = await action(ctx.data as TInput);
52
- if (opts?.unwrap && result && typeof result === "object" && "data" in result) {
53
- return (result as any).data;
54
- }
55
- return result;
56
- }) as unknown as (ctx: { data: TInput }) => Promise<TOutput>;
70
+ return () => {
71
+ throw new Error(
72
+ "createInvokeFn() is a codegen-time-only template marker and was never meant to be called at " +
73
+ "runtime. If you're seeing this, something imported an apps-*/invoke.ts template file directly " +
74
+ "instead of using the site's generated src/server/invoke.gen.ts (run `npm run generate:invoke`).",
75
+ );
76
+ };
57
77
  }
@@ -597,13 +597,14 @@ export function decoVitePlugin() {
597
597
  // manifest, and every POST /_serverFn/* call from the browser returns
598
598
  // HTTP 500 ("Invalid server function ID"). See #197.
599
599
  //
600
- // @decocms/blocks does NOT need this: despite its
601
- // validateSection.ts / useScript.ts doc comments mentioning
602
- // `createServerFn`, neither file (nor anything else in runtime)
603
- // actually calls it those are a JSDoc usage example for a site's
604
- // own server-fn file and a deprecation-message suggestion,
605
- // respectively. Only add a package here once it has a real
606
- // `createServerFn(...)` call site.
600
+ // @decocms/apps-vtex does NOT need this: its invoke.ts has zero real
601
+ // createServerFn call sites reachable by any site (it's a
602
+ // codegen-time-only template that blocks-cli's generate-invoke.ts
603
+ // statically parses, never imports, to emit each site's own
604
+ // src/server/invoke.gen.ts with real top-level createServerFn consts;
605
+ // createInvokeFn itself is a non-functional type-only marker for that
606
+ // template, see sdk/createInvoke.ts's doc comment). Only add a
607
+ // package here once it has a real `createServerFn(...)` call site.
607
608
  if (name === "ssr") {
608
609
  env.resolve = env.resolve || {};
609
610
  const existing = env.resolve.noExternal;
@@ -617,6 +618,34 @@ export function decoVitePlugin() {
617
618
  } else {
618
619
  env.resolve.noExternal = additions;
619
620
  }
621
+
622
+ // The noExternal setting above only controls whether Vite's SSR
623
+ // *output* inlines vs. externalizes this package — it does NOT
624
+ // stop Vite's dev-server-startup dependency optimizer (esbuild via
625
+ // `optimizeDeps`) from pre-bundling it. Some Cloudflare Workers
626
+ // targets (via @cloudflare/vite-plugin) already force
627
+ // `resolve.noExternal = true` unconditionally for this same "ssr"
628
+ // environment before this hook even runs, making the block above a
629
+ // complete no-op there — yet the crash below still happened,
630
+ // proving pre-bundling (not noExternal) was always the real cause.
631
+ //
632
+ // Confirmed via direct diagnostic instrumentation of
633
+ // @tanstack/start-plugin-core: once esbuild pre-bundles this
634
+ // package into a single node_modules/.vite/deps_ssr/*.js chunk,
635
+ // Cloudflare's separate worker-export static-analysis pass
636
+ // (getWorkerEntryExportTypes, a distinct pipeline stage from
637
+ // Vite's own SSR transform — see workers/runner-worker in the
638
+ // stack trace) throws "createServerFn must be assigned to a
639
+ // variable!" on this package's own legitimate, already-top-level
640
+ // createServerFn calls (loadCmsPage / loadCmsHomePage /
641
+ // loadDeferredSection) — apparently that scan doesn't recognize
642
+ // esbuild-bundled `var X = createServerFn(...)` output the same
643
+ // way it recognizes the original, unbundled source. Excluding this
644
+ // package from `optimizeDeps` entirely routes it through Vite's
645
+ // normal plugin pipeline (where TanStack Start's compiler
646
+ // transforms it correctly) instead of esbuild's pre-bundler.
647
+ env.optimizeDeps = env.optimizeDeps || {};
648
+ env.optimizeDeps.exclude = [...new Set([...(env.optimizeDeps.exclude || []), "@decocms/tanstack"])];
620
649
  }
621
650
  },
622
651