@farm.js/plugin 0.1.0-beta.10 → 0.1.0-beta.100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/dist/api/index.d.ts +44 -0
  2. package/dist/api/index.d.ts.map +1 -0
  3. package/dist/api/index.js +497 -0
  4. package/dist/context/index.d.ts +61 -0
  5. package/dist/context/index.d.ts.map +1 -0
  6. package/dist/context/index.js +75 -0
  7. package/dist/dev-static.d.ts +8 -0
  8. package/dist/dev-static.d.ts.map +1 -0
  9. package/dist/dev-static.js +37 -0
  10. package/dist/index.d.ts +43 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +49 -0
  13. package/dist/middleware/index.d.ts +97 -0
  14. package/dist/middleware/index.d.ts.map +1 -0
  15. package/dist/middleware/index.js +444 -0
  16. package/dist/middleware/matching.d.ts +29 -0
  17. package/dist/middleware/matching.d.ts.map +1 -0
  18. package/dist/middleware/matching.js +79 -0
  19. package/dist/observability/index.d.ts +190 -0
  20. package/dist/observability/index.d.ts.map +1 -0
  21. package/dist/observability/index.js +399 -0
  22. package/dist/rsc/automatic-optimized-boundary.d.ts +11 -0
  23. package/dist/rsc/automatic-optimized-boundary.d.ts.map +1 -0
  24. package/dist/rsc/automatic-optimized-boundary.js +162 -0
  25. package/dist/rsc/build-paths.d.ts +3 -0
  26. package/dist/rsc/build-paths.d.ts.map +1 -0
  27. package/dist/rsc/build-paths.js +8 -0
  28. package/dist/rsc/compatibility.d.ts +8 -0
  29. package/dist/rsc/compatibility.d.ts.map +1 -0
  30. package/dist/rsc/compatibility.js +46 -0
  31. package/dist/rsc/dev-response.d.ts +3 -0
  32. package/dist/rsc/dev-response.d.ts.map +1 -0
  33. package/dist/rsc/dev-response.js +10 -0
  34. package/dist/rsc/entries/client.d.ts +15 -0
  35. package/dist/rsc/entries/client.d.ts.map +1 -0
  36. package/dist/rsc/entries/client.js +320 -0
  37. package/dist/rsc/entries/error-fallback.d.ts +3 -0
  38. package/dist/rsc/entries/error-fallback.d.ts.map +1 -0
  39. package/dist/rsc/entries/error-fallback.js +12 -0
  40. package/dist/rsc/entries/rsc.d.ts +13 -0
  41. package/dist/rsc/entries/rsc.d.ts.map +1 -0
  42. package/dist/rsc/entries/rsc.js +930 -0
  43. package/dist/rsc/entries/ssr.d.ts +13 -0
  44. package/dist/rsc/entries/ssr.d.ts.map +1 -0
  45. package/dist/rsc/entries/ssr.js +215 -0
  46. package/dist/rsc/index.d.ts +91 -0
  47. package/dist/rsc/index.d.ts.map +1 -0
  48. package/dist/rsc/index.js +1466 -0
  49. package/dist/rsc/nitro-build.d.ts +36 -0
  50. package/dist/rsc/nitro-build.d.ts.map +1 -0
  51. package/dist/rsc/nitro-build.js +416 -0
  52. package/dist/rsc/optimized-boundary.d.ts +29 -0
  53. package/dist/rsc/optimized-boundary.d.ts.map +1 -0
  54. package/dist/rsc/optimized-boundary.js +243 -0
  55. package/dist/rsc/route-action-transform.d.ts +11 -0
  56. package/dist/rsc/route-action-transform.d.ts.map +1 -0
  57. package/dist/rsc/route-action-transform.js +369 -0
  58. package/dist/rsc/server-fn-transform.d.ts +6 -0
  59. package/dist/rsc/server-fn-transform.d.ts.map +1 -0
  60. package/dist/rsc/server-fn-transform.js +152 -0
  61. package/dist/rsc/test-fixture-dependencies.d.ts +2 -0
  62. package/dist/rsc/test-fixture-dependencies.d.ts.map +1 -0
  63. package/dist/rsc/test-fixture-dependencies.js +23 -0
  64. package/dist/rsc/types.d.ts +132 -0
  65. package/dist/rsc/types.d.ts.map +1 -0
  66. package/dist/rsc/types.js +1 -0
  67. package/dist/rsc/vite-plugin-nitro.d.ts +46 -0
  68. package/dist/rsc/vite-plugin-nitro.d.ts.map +1 -0
  69. package/dist/rsc/vite-plugin-nitro.js +148 -0
  70. package/package.json +4 -4
@@ -0,0 +1,13 @@
1
+ import type { EntryContext } from "../types.js";
2
+ /**
3
+ * Generates the SSR environment entry file.
4
+ *
5
+ * This entry file:
6
+ * - Receives the RSC stream from the RSC environment
7
+ * - Tees (duplicates) the stream - one copy for SSR, one for client hydration
8
+ * - Deserializes the RSC stream back to React elements
9
+ * - Renders those elements to HTML using React DOM's SSR
10
+ * - Injects the RSC payload into the HTML so the client can hydrate
11
+ */
12
+ export declare function generateSsrEntry(ctx: EntryContext): string;
13
+ //# sourceMappingURL=ssr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr.d.ts","sourceRoot":"","sources":["../../../src/rsc/entries/ssr.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CA+M1D"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Generates the SSR environment entry file.
3
+ *
4
+ * This entry file:
5
+ * - Receives the RSC stream from the RSC environment
6
+ * - Tees (duplicates) the stream - one copy for SSR, one for client hydration
7
+ * - Deserializes the RSC stream back to React elements
8
+ * - Renders those elements to HTML using React DOM's SSR
9
+ * - Injects the RSC payload into the HTML so the client can hydrate
10
+ */
11
+ export function generateSsrEntry(ctx) {
12
+ const debugLog = `// Debug disabled`;
13
+ const routesDir = ctx.routesDir === undefined ? "app" : ctx.routesDir.trim();
14
+ const routesPath = routesDir ? `/${routesDir}` : "";
15
+ const globalsCssPath = ctx.globalCssPath ?? `/${ctx.srcDir}${routesPath}/globals.css`;
16
+ return `
17
+ import React from 'react';
18
+ import { createFromReadableStream } from '@vitejs/plugin-rsc/ssr';
19
+ import { injectRSCPayload } from 'rsc-html-stream/server';
20
+ import { renderToPipeableStream } from 'react-dom/server';
21
+
22
+ function debug(...args) {
23
+ ${debugLog}
24
+ }
25
+
26
+ const DEV_GLOBAL_CSS_HREF = ${JSON.stringify(globalsCssPath)};
27
+ const CLIENT_CSS_HREF = "__FARM_CLIENT_CSS_HREF__";
28
+ const PLACEHOLDER_CSS_HREF = "__FARM_CLIENT_CSS_HREF__";
29
+ const resolvedCssHref =
30
+ typeof CLIENT_CSS_HREF === "string" &&
31
+ CLIENT_CSS_HREF.indexOf(PLACEHOLDER_CSS_HREF) < 0
32
+ ? CLIENT_CSS_HREF
33
+ : DEV_GLOBAL_CSS_HREF;
34
+
35
+ function escapeHtmlAttribute(value) {
36
+ return String(value).replace(/[&"]/g, (char) => (char === '&' ? '&amp;' : '&quot;'));
37
+ }
38
+
39
+ const cssLinkTag = resolvedCssHref
40
+ ? '<link rel="stylesheet" href="' + escapeHtmlAttribute(resolvedCssHref) + '" as="style" data-precedence="default">'
41
+ : '';
42
+
43
+ // Work on bytes: chunk boundaries need not align with UTF-8 code points.
44
+ function injectBeforeStream(markerText, tag) {
45
+ const encoder = new TextEncoder();
46
+ const marker = encoder.encode(markerText);
47
+ const insertion = encoder.encode(tag);
48
+ let buffer = new Uint8Array(0);
49
+ let injected = false;
50
+
51
+ return new TransformStream({
52
+ transform(chunk, controller) {
53
+ const bytes = typeof chunk === 'string' ? encoder.encode(chunk) : chunk;
54
+ if (injected || !tag) {
55
+ controller.enqueue(bytes);
56
+ return;
57
+ }
58
+
59
+ const pending = new Uint8Array(buffer.length + bytes.length);
60
+ pending.set(buffer);
61
+ pending.set(bytes, buffer.length);
62
+ let index = -1;
63
+ outer: for (let i = 0; i <= pending.length - marker.length; i++) {
64
+ for (let j = 0; j < marker.length; j++) {
65
+ if (pending[i + j] !== marker[j]) continue outer;
66
+ }
67
+ index = i;
68
+ break;
69
+ }
70
+ if (index >= 0) {
71
+ if (index) controller.enqueue(pending.subarray(0, index));
72
+ controller.enqueue(insertion);
73
+ controller.enqueue(pending.subarray(index));
74
+ buffer = new Uint8Array(0);
75
+ injected = true;
76
+ return;
77
+ }
78
+
79
+ // Retain only a possible marker prefix, never the whole response.
80
+ const emitLength = Math.max(0, pending.length - marker.length + 1);
81
+ if (emitLength) controller.enqueue(pending.subarray(0, emitLength));
82
+ buffer = pending.slice(emitLength);
83
+ },
84
+ flush(controller) {
85
+ if (buffer.length) controller.enqueue(buffer);
86
+ },
87
+ });
88
+ }
89
+
90
+ function injectIntoHeadStream(tag) {
91
+ return injectBeforeStream('</head>', tag);
92
+ }
93
+
94
+ /** Collect Node stream (from renderToPipeableStream) into a single string. Supports Suspense. */
95
+ function pipeableStreamToString(stream) {
96
+ return new Promise((resolve, reject) => {
97
+ const chunks = [];
98
+ stream.on('data', (c) => chunks.push(c));
99
+ stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
100
+ stream.on('error', reject);
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Renders an RSC stream to HTML
106
+ * Called by the RSC environment's handler
107
+ * @param { { payload, rscStream } | ReadableStream } firstArg - Either { payload, rscStream } for initial load (stream tree so loading.tsx shows first) or legacy rscStream only
108
+ */
109
+ export async function renderHTML(firstArg, options = {}) {
110
+ debug('Starting SSR render');
111
+
112
+ const hasPayload = firstArg && typeof firstArg === 'object' && firstArg.payload != null && firstArg.rscStream != null;
113
+ const rscStream = hasPayload ? firstArg.rscStream : firstArg;
114
+ const [s1, s2] = rscStream.tee();
115
+
116
+ // Always deserialize the RSC stream before SSR. Rendering the raw payload directly
117
+ // calls client references on the server (for example, a "use client" Counter).
118
+ const payload = await createFromReadableStream(s1);
119
+ const rootElement = payload.root;
120
+
121
+ function Root() {
122
+ return rootElement;
123
+ }
124
+
125
+ // Get the bootstrap script that loads the client bundle (for hydration).
126
+ // In dev: Farm sets globalThis.__FARM_VITE_RSC_LOAD_BOOTSTRAP__ or import.meta.viteRsc.
127
+ // In production: __FARM_BOOTSTRAP_SCRIPT__ is replaced at build time by nitro-build from the manifest.
128
+ const PROD_BOOTSTRAP = "__FARM_BOOTSTRAP_SCRIPT__";
129
+ let bootstrap = '';
130
+ try {
131
+ if (typeof globalThis.__FARM_VITE_RSC_LOAD_BOOTSTRAP__ === 'function') {
132
+ bootstrap = await globalThis.__FARM_VITE_RSC_LOAD_BOOTSTRAP__();
133
+ } else if (typeof import.meta.viteRsc?.loadBootstrapScriptContent === 'function') {
134
+ bootstrap = await import.meta.viteRsc.loadBootstrapScriptContent('index');
135
+ }
136
+ } catch (e) {
137
+ debug('Could not load bootstrap script:', e);
138
+ }
139
+ if (!bootstrap && PROD_BOOTSTRAP !== "__FARM_BOOTSTRAP_SCRIPT__") bootstrap = PROD_BOOTSTRAP;
140
+
141
+ // Use renderToPipeableStream so Suspense streams: fallback (loading.tsx) first, then resolved content.
142
+ const { PassThrough, Readable } = await import('stream');
143
+ const passThrough = new PassThrough();
144
+ // Do not commit an empty response when the shell cannot render. Reject back
145
+ // to the request handler so it can render error.tsx through RSC and SSR.
146
+ await new Promise((resolve, reject) => {
147
+ const { pipe } = renderToPipeableStream(React.createElement(Root), {
148
+ ...(bootstrap && { bootstrapScriptContent: bootstrap }),
149
+ formState: options.formState,
150
+ onShellReady() { pipe(passThrough); resolve(); },
151
+ onShellError: reject,
152
+ onError(error) { console.error('[RSC] SSR render error:', error); },
153
+ });
154
+ });
155
+
156
+ const CLIENT_ENTRY_HREF = "__FARM_CLIENT_ENTRY_HREF__";
157
+ const PLACEHOLDER_STR = "__FARM_CLIENT_ENTRY_HREF__";
158
+ const injectClientScriptInStream = typeof CLIENT_ENTRY_HREF === "string" && CLIENT_ENTRY_HREF.length > 0 && CLIENT_ENTRY_HREF.indexOf(PLACEHOLDER_STR) < 0;
159
+ const clientScriptTag = injectClientScriptInStream ? '<script type="module" src="' + CLIENT_ENTRY_HREF + '"></script>' : '';
160
+
161
+ if (hasPayload) {
162
+ // Stream HTML to the client while preserving the RSC payload for hydration.
163
+ debug('Streaming HTML (loading fallback then content)');
164
+ const streamingClientScriptInjector = injectBeforeStream('</body></html>', clientScriptTag);
165
+ const webStream = typeof Readable.toWeb === 'function'
166
+ ? Readable.toWeb(passThrough)
167
+ : new ReadableStream({
168
+ start(controller) {
169
+ passThrough.on('data', (c) => controller.enqueue(c instanceof Buffer ? c : new TextEncoder().encode(String(c))));
170
+ passThrough.on('end', () => controller.close());
171
+ passThrough.on('error', (e) => controller.error(e));
172
+ }
173
+ });
174
+ let out = webStream.pipeThrough(injectIntoHeadStream(cssLinkTag));
175
+ out = out.pipeThrough(injectRSCPayload(s2));
176
+ out = out.pipeThrough(streamingClientScriptInjector);
177
+ return out;
178
+ }
179
+
180
+ // Legacy path: buffer full HTML then send (no loading streaming)
181
+ debug('Rendering to HTML stream (then buffering)');
182
+ const fullHtmlRaw = await pipeableStreamToString(passThrough);
183
+ let fullHtml = fullHtmlRaw;
184
+ if (cssLinkTag && fullHtml.includes("</head>")) {
185
+ fullHtml = fullHtml.replace("</head>", cssLinkTag + "</head>");
186
+ }
187
+ ${ctx.actionsEnabled
188
+ ? `// Ensure __viteRscCallServer is set before any module runs (fixes "is not a function" when form submits)
189
+ if (fullHtml.includes("<body")) {
190
+ fullHtml = fullHtml.replace(/<body[^>]*>/, function(m) {
191
+ return m + "<script>(function(){if(typeof globalThis.__viteRscCallServer!=='function'){globalThis.__viteRscCallServer=function(){return Promise.reject(new Error('Farm.js: server actions not ready'));}}})();</script>";
192
+ });
193
+ }
194
+ `
195
+ : ""}
196
+ debug('Injecting RSC payload into HTML');
197
+ const streamFromHtml = new ReadableStream({
198
+ start(controller) {
199
+ controller.enqueue(new TextEncoder().encode(fullHtml));
200
+ controller.close();
201
+ }
202
+ });
203
+ const injectClientScriptStream = injectBeforeStream('</body></html>', clientScriptTag);
204
+
205
+ let out = streamFromHtml.pipeThrough(injectRSCPayload(s2));
206
+ out = out.pipeThrough(injectClientScriptStream);
207
+ return out;
208
+ }
209
+
210
+ // Enable HMR
211
+ if (import.meta.hot) {
212
+ import.meta.hot.accept();
213
+ }
214
+ `;
215
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Farm.js RSC Plugin
3
+ *
4
+ * Provides React Server Components support for Farm.js.
5
+ * When enabled, this plugin:
6
+ * - Configures three build environments (rsc, ssr, client)
7
+ * - Generates virtual entry files for routing, rendering, and hydration
8
+ * - Supports server actions when experimental.serverActions is true
9
+ * - Integrates with @vitejs/plugin-rsc for core RSC transforms
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { defineConfig } from '@farm.js/plugin/rsc'
14
+ *
15
+ * export default defineConfig({
16
+ * srcDir: 'src',
17
+ * experimental: {
18
+ * serverComponents: true,
19
+ * serverActions: true,
20
+ * },
21
+ * })
22
+ * ```
23
+ */
24
+ import { type Plugin, type UserConfig } from "vite";
25
+ import type { FarmRscPluginOptions, EntryContext } from "./types.js";
26
+ import type { FarmServerActionsConfig } from "@farm.js/core/server-action-security";
27
+ import type { FarmAPIConfig } from "@farm.js/core/api";
28
+ import type { FarmServerConfig } from "@farm.js/core/internal/production-runtime";
29
+ import type { FarmLayerEntry, ResolvedFarmLayer } from "@farm.js/core/server";
30
+ export type { FarmRscPluginOptions, EntryContext };
31
+ export { buildRscNitro, waitForRscManifest, waitForRscOutputs } from "./nitro-build.js";
32
+ export type { BuildRscNitroOptions } from "./nitro-build.js";
33
+ export { default as nitro } from "./vite-plugin-nitro.js";
34
+ export type { NitroPluginOptions } from "./vite-plugin-nitro.js";
35
+ export interface FarmRscConfig {
36
+ srcDir?: string;
37
+ extends?: readonly FarmLayerEntry[];
38
+ layers?: readonly ResolvedFarmLayer[];
39
+ outDir?: string;
40
+ basePath?: string;
41
+ api?: FarmAPIConfig;
42
+ server?: FarmServerConfig;
43
+ port?: number;
44
+ experimental?: {
45
+ /**
46
+ * Enable React Server Components rendering.
47
+ * @default true
48
+ */
49
+ serverComponents?: boolean;
50
+ /**
51
+ * Enable React Server Actions and their public POST decoder.
52
+ * @default false
53
+ */
54
+ serverActions?: boolean;
55
+ /**
56
+ * Automatically optimize eligible server-only host-element subtrees with
57
+ * the native Strata renderer. Unsupported trees keep normal React
58
+ * rendering; application components do not need a boundary import.
59
+ *
60
+ * @experimental
61
+ * @default false
62
+ */
63
+ optimizedBoundary?: boolean;
64
+ };
65
+ debug?: boolean;
66
+ encryptActions?: boolean;
67
+ serverActions?: FarmServerActionsConfig;
68
+ deploymentId?: string;
69
+ generateBuildId?: () => string | Promise<string>;
70
+ routesDir?: string;
71
+ entries?: {
72
+ rsc?: string;
73
+ ssr?: string;
74
+ client?: string;
75
+ };
76
+ /** Extra Vite plugins (e.g. rsc() with entries so client build resolves virtual:vite-rsc/client-references). */
77
+ plugins?: any[];
78
+ }
79
+ /**
80
+ * Define a Farm.js RSC configuration
81
+ * This is the recommended way to configure your RSC app
82
+ */
83
+ export declare function defineConfig(config?: FarmRscConfig): UserConfig;
84
+ /**
85
+ * Farm.js RSC Plugin
86
+ *
87
+ * @param options - Plugin configuration options
88
+ * @returns Array of Vite plugins
89
+ */
90
+ export default function farmRsc(options?: FarmRscPluginOptions): Plugin[];
91
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/rsc/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAIL,KAAK,MAAM,EACX,KAAK,UAAU,EAChB,MAAM,MAAM,CAAC;AAEd,OAAO,KAAK,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACrE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sCAAsC,CAAC;AACpF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2CAA2C,CAAC;AAClF,OAAO,KAAK,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAwD9E,YAAY,EAAE,oBAAoB,EAAE,YAAY,EAAE,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAExF,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC1D,YAAY,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAGjE,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,SAAS,cAAc,EAAE,CAAC;IACpC,MAAM,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;IACtC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,aAAa,CAAC;IACpB,MAAM,CAAC,EAAE,gBAAgB,CAAC;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE;QACb;;;WAGG;QACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;QAC3B;;;WAGG;QACH,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB;;;;;;;WAOG;QACH,iBAAiB,CAAC,EAAE,OAAO,CAAC;KAC7B,CAAC;IACF,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE;QACR,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,gHAAgH;IAChH,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,GAAE,aAAkB,GAAG,UAAU,CAuDnE;AA8TD;;;;;GAKG;AACH,MAAM,CAAC,OAAO,UAAU,OAAO,CAAC,OAAO,GAAE,oBAAyB,GAAG,MAAM,EAAE,CAgvC5E"}