rwsdk 1.0.0-alpha.2 → 1.0.0-alpha.20

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 (158) hide show
  1. package/dist/lib/e2e/browser.d.mts +10 -0
  2. package/dist/lib/e2e/browser.mjs +124 -0
  3. package/dist/lib/e2e/dev.d.mts +8 -0
  4. package/dist/lib/e2e/dev.mjs +242 -0
  5. package/dist/lib/e2e/environment.d.mts +14 -0
  6. package/dist/lib/e2e/environment.mjs +266 -0
  7. package/dist/lib/e2e/index.d.mts +8 -0
  8. package/dist/lib/e2e/index.mjs +8 -0
  9. package/dist/lib/e2e/poll.d.mts +8 -0
  10. package/dist/lib/e2e/poll.mjs +31 -0
  11. package/dist/lib/e2e/release.d.mts +56 -0
  12. package/dist/lib/e2e/release.mjs +559 -0
  13. package/dist/lib/e2e/retry.d.mts +4 -0
  14. package/dist/lib/e2e/retry.mjs +16 -0
  15. package/dist/lib/e2e/setup.d.mts +2 -0
  16. package/dist/lib/e2e/setup.mjs +1 -0
  17. package/dist/lib/e2e/tarball.d.mts +14 -0
  18. package/dist/lib/e2e/tarball.mjs +99 -0
  19. package/dist/lib/e2e/testHarness.d.mts +132 -0
  20. package/dist/lib/e2e/testHarness.mjs +437 -0
  21. package/dist/lib/e2e/types.d.mts +32 -0
  22. package/dist/lib/getShortName.mjs +6 -1
  23. package/dist/lib/getShortName.test.d.mts +1 -0
  24. package/dist/lib/getShortName.test.mjs +25 -0
  25. package/dist/lib/hasPkgScript.d.mts +4 -1
  26. package/dist/lib/hasPkgScript.mjs +9 -6
  27. package/dist/lib/hasPkgScript.test.d.mts +1 -0
  28. package/dist/lib/hasPkgScript.test.mjs +33 -0
  29. package/dist/lib/jsonUtils.mjs +3 -0
  30. package/dist/lib/jsonUtils.test.d.mts +1 -0
  31. package/dist/lib/jsonUtils.test.mjs +90 -0
  32. package/dist/lib/normalizeModulePath.d.mts +5 -0
  33. package/dist/lib/normalizeModulePath.mjs +1 -1
  34. package/dist/lib/normalizeModulePath.test.d.mts +1 -0
  35. package/dist/lib/{normalizeModulePath.test.js → normalizeModulePath.test.mjs} +20 -1
  36. package/dist/lib/smokeTests/browser.mjs +3 -94
  37. package/dist/lib/smokeTests/development.mjs +2 -223
  38. package/dist/lib/smokeTests/environment.d.mts +4 -11
  39. package/dist/lib/smokeTests/environment.mjs +10 -158
  40. package/dist/lib/smokeTests/release.d.mts +2 -49
  41. package/dist/lib/smokeTests/release.mjs +3 -503
  42. package/dist/llms/rules/middleware.d.ts +1 -1
  43. package/dist/llms/rules/middleware.js +4 -4
  44. package/dist/runtime/entries/worker.d.ts +0 -1
  45. package/dist/runtime/entries/worker.js +0 -1
  46. package/dist/runtime/lib/auth/session.d.ts +2 -2
  47. package/dist/runtime/lib/auth/session.js +4 -4
  48. package/dist/runtime/lib/memoizeOnId.test.d.ts +1 -0
  49. package/dist/runtime/lib/memoizeOnId.test.js +49 -0
  50. package/dist/runtime/lib/realtime/protocol.test.d.ts +1 -0
  51. package/dist/runtime/lib/realtime/protocol.test.js +107 -0
  52. package/dist/runtime/lib/realtime/shared.test.d.ts +1 -0
  53. package/dist/runtime/lib/realtime/shared.test.js +18 -0
  54. package/dist/runtime/lib/realtime/validateUpgradeRequest.test.d.ts +1 -0
  55. package/dist/runtime/lib/realtime/validateUpgradeRequest.test.js +66 -0
  56. package/dist/runtime/lib/realtime/worker.d.ts +1 -1
  57. package/dist/runtime/lib/router.js +40 -22
  58. package/dist/runtime/lib/router.test.js +590 -2
  59. package/dist/runtime/lib/rwContext.d.ts +22 -0
  60. package/dist/runtime/lib/rwContext.js +1 -0
  61. package/dist/runtime/lib/stitchDocumentAndAppStreams.d.ts +18 -0
  62. package/dist/runtime/lib/stitchDocumentAndAppStreams.js +143 -0
  63. package/dist/runtime/lib/turnstile/verifyTurnstileToken.d.ts +2 -1
  64. package/dist/runtime/lib/turnstile/verifyTurnstileToken.js +6 -6
  65. package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.d.ts +1 -0
  66. package/dist/runtime/lib/turnstile/verifyTurnstileToken.test.js +49 -0
  67. package/dist/runtime/register/worker.d.ts +1 -1
  68. package/dist/runtime/register/worker.js +33 -21
  69. package/dist/runtime/render/assembleDocument.d.ts +6 -0
  70. package/dist/runtime/render/assembleDocument.js +22 -0
  71. package/dist/runtime/render/createThenableFromReadableStream.d.ts +1 -0
  72. package/dist/runtime/render/createThenableFromReadableStream.js +9 -0
  73. package/dist/runtime/render/normalizeActionResult.d.ts +1 -0
  74. package/dist/runtime/render/normalizeActionResult.js +43 -0
  75. package/dist/runtime/render/preloads.d.ts +2 -2
  76. package/dist/runtime/render/preloads.js +2 -3
  77. package/dist/runtime/render/{renderRscThenableToHtmlStream.d.ts → renderDocumentHtmlStream.d.ts} +3 -3
  78. package/dist/runtime/render/renderDocumentHtmlStream.js +39 -0
  79. package/dist/runtime/render/renderHtmlStream.d.ts +7 -0
  80. package/dist/runtime/render/renderHtmlStream.js +31 -0
  81. package/dist/runtime/render/renderToRscStream.d.ts +5 -3
  82. package/dist/runtime/render/renderToRscStream.js +12 -41
  83. package/dist/runtime/render/renderToStream.d.ts +2 -1
  84. package/dist/runtime/render/renderToStream.js +15 -8
  85. package/dist/runtime/render/stylesheets.d.ts +2 -2
  86. package/dist/runtime/render/stylesheets.js +2 -3
  87. package/dist/runtime/requestInfo/types.d.ts +0 -2
  88. package/dist/runtime/requestInfo/worker.js +1 -9
  89. package/dist/runtime/ssrBridge.d.ts +2 -1
  90. package/dist/runtime/ssrBridge.js +2 -1
  91. package/dist/runtime/worker.d.ts +1 -0
  92. package/dist/runtime/worker.js +11 -14
  93. package/dist/scripts/debug-sync.mjs +102 -133
  94. package/dist/vite/buildApp.d.mts +2 -1
  95. package/dist/vite/buildApp.mjs +9 -5
  96. package/dist/vite/checkIsUsingPrisma.d.mts +4 -0
  97. package/dist/vite/checkIsUsingPrisma.mjs +2 -2
  98. package/dist/vite/checkIsUsingPrisma.test.d.mts +1 -0
  99. package/dist/vite/checkIsUsingPrisma.test.mjs +30 -0
  100. package/dist/vite/configPlugin.mjs +54 -14
  101. package/dist/vite/createDirectiveLookupPlugin.d.mts +9 -0
  102. package/dist/vite/createDirectiveLookupPlugin.mjs +33 -29
  103. package/dist/vite/createDirectiveLookupPlugin.test.d.mts +1 -0
  104. package/dist/vite/createDirectiveLookupPlugin.test.mjs +40 -0
  105. package/dist/vite/directiveModulesDevPlugin.d.mts +4 -1
  106. package/dist/vite/directiveModulesDevPlugin.mjs +6 -5
  107. package/dist/vite/directiveModulesDevPlugin.test.d.mts +1 -0
  108. package/dist/vite/directiveModulesDevPlugin.test.mjs +59 -0
  109. package/dist/vite/directivesPlugin.d.mts +1 -0
  110. package/dist/vite/directivesPlugin.mjs +1 -1
  111. package/dist/vite/directivesPlugin.test.d.mts +1 -0
  112. package/dist/vite/directivesPlugin.test.mjs +24 -0
  113. package/dist/vite/ensureAliasArray.test.d.mts +1 -0
  114. package/dist/vite/ensureAliasArray.test.mjs +71 -0
  115. package/dist/vite/findSpecifiers.mjs +2 -1
  116. package/dist/vite/findSpecifiers.test.d.mts +1 -0
  117. package/dist/vite/findSpecifiers.test.mjs +202 -0
  118. package/dist/vite/findSsrSpecifiers.test.d.mts +1 -0
  119. package/dist/vite/findSsrSpecifiers.test.mjs +99 -0
  120. package/dist/vite/hasDirective.d.mts +6 -3
  121. package/dist/vite/hasDirective.mjs +43 -27
  122. package/dist/vite/hasDirective.test.d.mts +1 -0
  123. package/dist/vite/hasDirective.test.mjs +107 -0
  124. package/dist/vite/isJsFile.test.d.mts +1 -0
  125. package/dist/vite/isJsFile.test.mjs +38 -0
  126. package/dist/vite/{reactConditionsResolverPlugin.d.mts → knownDepsResolverPlugin.d.mts} +2 -2
  127. package/dist/vite/{reactConditionsResolverPlugin.mjs → knownDepsResolverPlugin.mjs} +28 -23
  128. package/dist/vite/linkerPlugin.d.mts +8 -0
  129. package/dist/vite/linkerPlugin.mjs +30 -22
  130. package/dist/vite/linkerPlugin.test.d.mts +1 -0
  131. package/dist/vite/linkerPlugin.test.mjs +41 -0
  132. package/dist/vite/miniflareHMRPlugin.d.mts +5 -0
  133. package/dist/vite/miniflareHMRPlugin.mjs +2 -2
  134. package/dist/vite/miniflareHMRPlugin.test.d.mts +1 -0
  135. package/dist/vite/miniflareHMRPlugin.test.mjs +42 -0
  136. package/dist/vite/redwoodPlugin.d.mts +9 -0
  137. package/dist/vite/redwoodPlugin.mjs +29 -5
  138. package/dist/vite/redwoodPlugin.test.d.mts +1 -0
  139. package/dist/vite/redwoodPlugin.test.mjs +34 -0
  140. package/dist/vite/resolveForcedPaths.d.mts +4 -0
  141. package/dist/vite/resolveForcedPaths.mjs +9 -0
  142. package/dist/vite/runDirectivesScan.d.mts +22 -1
  143. package/dist/vite/runDirectivesScan.mjs +105 -58
  144. package/dist/vite/runDirectivesScan.test.d.mts +1 -0
  145. package/dist/vite/runDirectivesScan.test.mjs +73 -0
  146. package/dist/vite/ssrBridgePlugin.mjs +8 -1
  147. package/dist/vite/transformClientComponents.mjs +6 -4
  148. package/dist/vite/transformClientComponents.test.mjs +116 -58
  149. package/dist/vite/transformServerFunctions.d.mts +1 -1
  150. package/dist/vite/transformServerFunctions.mjs +1 -1
  151. package/dist/vite/transformServerFunctions.test.mjs +3 -3
  152. package/package.json +56 -47
  153. package/dist/runtime/imports/resolveSSRValue.d.ts +0 -1
  154. package/dist/runtime/imports/resolveSSRValue.js +0 -8
  155. package/dist/runtime/render/renderRscThenableToHtmlStream.js +0 -54
  156. package/dist/runtime/render/transformRscToHtmlStream.d.ts +0 -8
  157. package/dist/runtime/render/transformRscToHtmlStream.js +0 -19
  158. /package/dist/lib/{normalizeModulePath.test.d.ts → e2e/types.mjs} +0 -0
@@ -0,0 +1,143 @@
1
+ /**
2
+ * A utility to orchestrate and interleave two ReadableStreams (a document shell and an app shell)
3
+ * based on a set of markers within their content. This is designed to solve a specific
4
+ * race condition in streaming Server-Side Rendering (SSR) with Suspense.
5
+ *
6
+ * The logic is as follows:
7
+ * 1. Stream the document until a start marker is found.
8
+ * 2. Switch to the app stream and stream it until an end marker is found. This is the non-suspended shell.
9
+ * 3. Switch back to the document stream and stream it until the closing body tag. This sends the client script.
10
+ * 4. Switch back to the app stream and stream the remainder (the suspended content).
11
+ * 5. Switch back to the document stream and stream the remainder (closing body and html tags).
12
+ *
13
+ * @param outerHtml The stream for the document shell (`<Document>`).
14
+ * @param innerHtml The stream for the application's content.
15
+ * @param startMarker The marker in the document to start injecting the app.
16
+ * @param endMarker The marker in the app stream that signals the end of the initial, non-suspended render.
17
+ */
18
+ export function stitchDocumentAndAppStreams(outerHtml, innerHtml, startMarker, endMarker) {
19
+ const decoder = new TextDecoder();
20
+ const encoder = new TextEncoder();
21
+ let outerReader;
22
+ let innerReader;
23
+ let buffer = "";
24
+ let outerBufferRemains = "";
25
+ let phase = "outer-head";
26
+ const pump = async (controller) => {
27
+ try {
28
+ if (phase === "outer-head") {
29
+ const { done, value } = await outerReader.read();
30
+ if (done) {
31
+ if (buffer)
32
+ controller.enqueue(encoder.encode(buffer));
33
+ controller.close();
34
+ return;
35
+ }
36
+ buffer += decoder.decode(value, { stream: true });
37
+ const markerIndex = buffer.indexOf(startMarker);
38
+ if (markerIndex !== -1) {
39
+ controller.enqueue(encoder.encode(buffer.slice(0, markerIndex)));
40
+ outerBufferRemains = buffer.slice(markerIndex + startMarker.length);
41
+ buffer = "";
42
+ phase = "inner-shell";
43
+ }
44
+ else {
45
+ const flushIndex = buffer.lastIndexOf("\n");
46
+ if (flushIndex !== -1) {
47
+ controller.enqueue(encoder.encode(buffer.slice(0, flushIndex + 1)));
48
+ buffer = buffer.slice(flushIndex + 1);
49
+ }
50
+ }
51
+ }
52
+ else if (phase === "inner-shell") {
53
+ const { done, value } = await innerReader.read();
54
+ if (done) {
55
+ if (buffer)
56
+ controller.enqueue(encoder.encode(buffer));
57
+ phase = "outer-tail";
58
+ }
59
+ else {
60
+ buffer += decoder.decode(value, { stream: true });
61
+ const markerIndex = buffer.indexOf(endMarker);
62
+ if (markerIndex !== -1) {
63
+ const endOfMarkerIndex = markerIndex + endMarker.length;
64
+ controller.enqueue(encoder.encode(buffer.slice(0, endOfMarkerIndex)));
65
+ buffer = buffer.slice(endOfMarkerIndex);
66
+ phase = "outer-tail";
67
+ }
68
+ else {
69
+ const flushIndex = buffer.lastIndexOf("\n");
70
+ if (flushIndex !== -1) {
71
+ controller.enqueue(encoder.encode(buffer.slice(0, flushIndex + 1)));
72
+ buffer = buffer.slice(flushIndex + 1);
73
+ }
74
+ }
75
+ }
76
+ }
77
+ else if (phase === "outer-tail") {
78
+ if (outerBufferRemains) {
79
+ buffer = outerBufferRemains;
80
+ outerBufferRemains = "";
81
+ }
82
+ const { done, value } = await outerReader.read();
83
+ if (done) {
84
+ if (buffer)
85
+ controller.enqueue(encoder.encode(buffer));
86
+ phase = "inner-suspended";
87
+ }
88
+ else {
89
+ buffer += decoder.decode(value, { stream: true });
90
+ const markerIndex = buffer.indexOf("</body>");
91
+ if (markerIndex !== -1) {
92
+ controller.enqueue(encoder.encode(buffer.slice(0, markerIndex)));
93
+ buffer = buffer.slice(markerIndex);
94
+ phase = "inner-suspended";
95
+ }
96
+ else {
97
+ const flushIndex = buffer.lastIndexOf("\n");
98
+ if (flushIndex !== -1) {
99
+ controller.enqueue(encoder.encode(buffer.slice(0, flushIndex + 1)));
100
+ buffer = buffer.slice(flushIndex + 1);
101
+ }
102
+ }
103
+ }
104
+ }
105
+ else if (phase === "inner-suspended") {
106
+ const { done, value } = await innerReader.read();
107
+ if (done) {
108
+ phase = "outer-end";
109
+ }
110
+ else {
111
+ controller.enqueue(value);
112
+ }
113
+ }
114
+ else if (phase === "outer-end") {
115
+ if (buffer) {
116
+ controller.enqueue(encoder.encode(buffer));
117
+ buffer = "";
118
+ }
119
+ const { done, value } = await outerReader.read();
120
+ if (done) {
121
+ controller.close();
122
+ return;
123
+ }
124
+ controller.enqueue(value);
125
+ }
126
+ await pump(controller);
127
+ }
128
+ catch (e) {
129
+ controller.error(e);
130
+ }
131
+ };
132
+ return new ReadableStream({
133
+ start(controller) {
134
+ outerReader = outerHtml.getReader();
135
+ innerReader = innerHtml.getReader();
136
+ pump(controller).catch((e) => controller.error(e));
137
+ },
138
+ cancel(reason) {
139
+ outerReader?.cancel(reason);
140
+ innerReader?.cancel(reason);
141
+ },
142
+ });
143
+ }
@@ -1,4 +1,5 @@
1
- export declare const verifyTurnstileToken: ({ token, secretKey, }: {
1
+ export declare const verifyTurnstileToken: ({ token, secretKey, fetchFn, }: {
2
2
  token: string;
3
3
  secretKey: string;
4
+ fetchFn?: typeof fetch;
4
5
  }) => Promise<boolean>;
@@ -1,10 +1,10 @@
1
- export const verifyTurnstileToken = async ({ token, secretKey, }) => {
2
- const response = await fetch("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
3
- method: "POST",
4
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
5
- body: new URLSearchParams({ secret: secretKey, response: token }),
6
- });
1
+ export const verifyTurnstileToken = async ({ token, secretKey, fetchFn = fetch, }) => {
7
2
  try {
3
+ const response = await fetchFn("https://challenges.cloudflare.com/turnstile/v0/siteverify", {
4
+ method: "POST",
5
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
6
+ body: new URLSearchParams({ secret: secretKey, response: token }),
7
+ });
8
8
  const data = (await response.json());
9
9
  return data?.success === true;
10
10
  }
@@ -0,0 +1,49 @@
1
+ import { describe, it, expect, vi } from "vitest";
2
+ import { verifyTurnstileToken } from "./verifyTurnstileToken";
3
+ describe("verifyTurnstileToken", () => {
4
+ it("should return true for a successful verification", async () => {
5
+ const mockFetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ success: true }), {
6
+ status: 200,
7
+ headers: { "Content-Type": "application/json" },
8
+ }));
9
+ const result = await verifyTurnstileToken({
10
+ token: "valid-token",
11
+ secretKey: "secret",
12
+ fetchFn: mockFetch,
13
+ });
14
+ expect(result).toBe(true);
15
+ expect(mockFetch).toHaveBeenCalledWith("https://challenges.cloudflare.com/turnstile/v0/siteverify", expect.any(Object));
16
+ });
17
+ it("should return false for a failed verification", async () => {
18
+ const mockFetch = vi.fn().mockResolvedValue(new Response(JSON.stringify({ success: false }), {
19
+ status: 200,
20
+ headers: { "Content-Type": "application/json" },
21
+ }));
22
+ const result = await verifyTurnstileToken({
23
+ token: "invalid-token",
24
+ secretKey: "secret",
25
+ fetchFn: mockFetch,
26
+ });
27
+ expect(result).toBe(false);
28
+ });
29
+ it("should return false if the fetch call fails", async () => {
30
+ const mockFetch = vi.fn().mockRejectedValue(new Error("Network error"));
31
+ const result = await verifyTurnstileToken({
32
+ token: "any-token",
33
+ secretKey: "secret",
34
+ fetchFn: mockFetch,
35
+ });
36
+ expect(result).toBe(false);
37
+ });
38
+ it("should return false for a non-JSON response", async () => {
39
+ const mockFetch = vi
40
+ .fn()
41
+ .mockResolvedValue(new Response("not json", { status: 200 }));
42
+ const result = await verifyTurnstileToken({
43
+ token: "any-token",
44
+ secretKey: "secret",
45
+ fetchFn: mockFetch,
46
+ });
47
+ expect(result).toBe(false);
48
+ });
49
+ });
@@ -1,4 +1,4 @@
1
1
  export declare function registerServerReference(action: Function, id: string, name: string): Function;
2
- export declare function registerClientReference<Target extends Record<string, any>>(id: string, exportName: string, value: any): any;
2
+ export declare function registerClientReference<Target extends Record<string, unknown>>(ssrModule: Target, id: string, exportName: string): {};
3
3
  export declare function __smokeTestActionHandler(timestamp?: number): Promise<unknown>;
4
4
  export declare function rscActionHandler(req: Request): Promise<unknown>;
@@ -1,4 +1,5 @@
1
1
  import { registerServerReference as baseRegisterServerReference, registerClientReference as baseRegisterClientReference, decodeReply, } from "react-server-dom-webpack/server.edge";
2
+ import { isValidElementType } from "react-is";
2
3
  import { getServerModuleExport } from "../imports/worker.js";
3
4
  import { requestInfo } from "../requestInfo/worker.js";
4
5
  export function registerServerReference(action, id, name) {
@@ -8,28 +9,39 @@ export function registerServerReference(action, id, name) {
8
9
  // Note: We no longer need to register in a Map since we use virtual lookup
9
10
  return baseRegisterServerReference(action, id, name);
10
11
  }
11
- export function registerClientReference(id, exportName, value) {
12
- const wrappedValue = (value && typeof value === "function") || typeof value === "object"
13
- ? value
14
- : () => null;
15
- const reference = baseRegisterClientReference({}, id, exportName);
16
- reference.__rwsdk_clientReferenceId = `${id}#${exportName}`;
17
- const finalDescriptors = Object.getOwnPropertyDescriptors(reference);
18
- const idDescriptor = finalDescriptors.$$id;
19
- if (idDescriptor && idDescriptor.hasOwnProperty("value")) {
20
- const originalValue = idDescriptor.value;
21
- finalDescriptors.$$id = {
22
- configurable: idDescriptor.configurable,
23
- enumerable: idDescriptor.enumerable,
24
- get() {
25
- requestInfo.rw.scriptsToBeLoaded.add(id);
26
- return originalValue;
27
- },
28
- };
12
+ const isComponent = (target) => isValidElementType(target) && target?.toString().includes("jsx");
13
+ export function registerClientReference(ssrModule, id, exportName) {
14
+ const target = ssrModule[exportName] ?? {};
15
+ if (isValidElementType(target)) {
16
+ // This is the original logic from 'main'.
17
+ // For React components, we create a serializable reference for the RSC pass.
18
+ const reference = baseRegisterClientReference({}, id, exportName);
19
+ const finalDescriptors = Object.getOwnPropertyDescriptors(reference);
20
+ const idDescriptor = finalDescriptors.$$id;
21
+ if (idDescriptor) {
22
+ const originalValue = idDescriptor.value;
23
+ // Create a new accessor descriptor, NOT by spreading the old one.
24
+ finalDescriptors.$$id = {
25
+ enumerable: idDescriptor.enumerable,
26
+ configurable: idDescriptor.configurable,
27
+ get() {
28
+ requestInfo.rw.scriptsToBeLoaded.add(id);
29
+ return originalValue;
30
+ },
31
+ };
32
+ }
33
+ finalDescriptors.$$async = { value: true };
34
+ finalDescriptors.$$isClientReference = { value: true };
35
+ // context(justinvdm, 25 Sep 2025): We create a wrapper function to avoid
36
+ // getting the SSR component's property descriptors - otherwise
37
+ // this will take precedence over the client reference descriptors
38
+ const fn = typeof target === "function"
39
+ ? (...args) => target(...args)
40
+ : () => null;
41
+ return Object.defineProperties(fn, finalDescriptors);
29
42
  }
30
- finalDescriptors.$$async = { value: true };
31
- finalDescriptors.$$isClientReference = { value: true };
32
- return Object.defineProperties(wrappedValue, finalDescriptors);
43
+ // For non-components, return the target object directly for use in SSR.
44
+ return target;
33
45
  }
34
46
  export async function __smokeTestActionHandler(timestamp) {
35
47
  await new Promise((resolve) => setTimeout(resolve, 0));
@@ -0,0 +1,6 @@
1
+ import { type RequestInfo } from "../requestInfo/types.js";
2
+ export declare const assembleDocument: ({ requestInfo, pageElement, shouldSSR, }: {
3
+ requestInfo: RequestInfo;
4
+ pageElement: React.ReactNode;
5
+ shouldSSR: boolean;
6
+ }) => import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Preloads } from "./preloads.js";
3
+ import { Stylesheets } from "./stylesheets.js";
4
+ // Note: This is a server component, even though it doesn't have the "use server"
5
+ // directive. It's intended to be imported and used within the RSC render pass.
6
+ export const assembleDocument = ({ requestInfo, pageElement, shouldSSR, }) => {
7
+ // todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
8
+ // surface context. e.g:
9
+ // * we assign `user: requestInfo.clientCtx` here
10
+ // * user populates requestInfo.clientCtx on worker side
11
+ // * user can import a read only `import { clientCtx } from "rwsdk/client"`
12
+ // on client side
13
+ const clientContext = {
14
+ rw: {
15
+ ssr: shouldSSR,
16
+ },
17
+ };
18
+ const Document = requestInfo.rw.Document;
19
+ return (_jsxs(Document, { ...requestInfo, children: [_jsx("script", { nonce: requestInfo.rw.nonce, dangerouslySetInnerHTML: {
20
+ __html: `globalThis.__RWSDK_CONTEXT = ${JSON.stringify(clientContext)}`,
21
+ } }), _jsx(Stylesheets, { requestInfo: requestInfo }), _jsx(Preloads, { requestInfo: requestInfo }), _jsx("div", { id: "hydrate-root", children: pageElement })] }));
22
+ };
@@ -0,0 +1 @@
1
+ export declare const createThenableFromReadableStream: (stream: ReadableStream) => Thenable<T>;
@@ -0,0 +1,9 @@
1
+ import { createModuleMap } from "./createModuleMap.js";
2
+ import ReactServerDom from "react-server-dom-webpack/client.edge";
3
+ const { createFromReadableStream } = ReactServerDom;
4
+ export const createThenableFromReadableStream = (stream) => createFromReadableStream(stream, {
5
+ serverConsumerManifest: {
6
+ moduleMap: createModuleMap(),
7
+ moduleLoading: null,
8
+ },
9
+ });
@@ -0,0 +1 @@
1
+ export declare const normalizeActionResult: (actionResult: any) => any;
@@ -0,0 +1,43 @@
1
+ // context(justinvdm, 24 Mar 2025): React flight limits chunks to 28 bytes, so we need to rechunk
2
+ // the stream to avoid losing data
3
+ function rechunkStream(stream, maxChunkSize = 28) {
4
+ const reader = stream.getReader();
5
+ return new ReadableStream({
6
+ async pull(controller) {
7
+ let buffer = new Uint8Array(0);
8
+ try {
9
+ while (true) {
10
+ const { done, value } = await reader.read();
11
+ if (done && buffer.length === 0) {
12
+ controller.close();
13
+ return;
14
+ }
15
+ if (value) {
16
+ buffer = new Uint8Array([...buffer, ...value]);
17
+ }
18
+ while (buffer.length >= maxChunkSize || (done && buffer.length > 0)) {
19
+ const chunk = buffer.slice(0, maxChunkSize);
20
+ buffer = buffer.slice(maxChunkSize);
21
+ controller.enqueue(chunk);
22
+ }
23
+ if (done) {
24
+ controller.close();
25
+ return;
26
+ }
27
+ }
28
+ }
29
+ catch (error) {
30
+ controller.error(error);
31
+ }
32
+ },
33
+ });
34
+ }
35
+ export const normalizeActionResult = (actionResult) => {
36
+ if (actionResult instanceof Response) {
37
+ return null;
38
+ }
39
+ if (actionResult instanceof ReadableStream) {
40
+ return rechunkStream(actionResult);
41
+ }
42
+ return actionResult;
43
+ };
@@ -1,6 +1,6 @@
1
1
  import type { RequestInfo } from "../requestInfo/types.js";
2
2
  import type { Manifest, ManifestChunk } from "../lib/manifest.js";
3
3
  export declare function findScriptForModule(id: string, manifest: Manifest): ManifestChunk | undefined;
4
- export declare const Preloads: ({ requestInfo }: {
4
+ export declare const Preloads: ({ requestInfo, }: {
5
5
  requestInfo: RequestInfo;
6
- }) => import("react/jsx-runtime.js").JSX.Element;
6
+ }) => Promise<import("react/jsx-runtime").JSX.Element>;
@@ -1,5 +1,4 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { use } from "react";
3
2
  import { getManifest } from "../lib/manifest.js";
4
3
  export function findScriptForModule(id, manifest) {
5
4
  const visited = new Set();
@@ -27,8 +26,8 @@ export function findScriptForModule(id, manifest) {
27
26
  }
28
27
  return find(id);
29
28
  }
30
- export const Preloads = ({ requestInfo }) => {
31
- const manifest = use(getManifest());
29
+ export const Preloads = async ({ requestInfo, }) => {
30
+ const manifest = await getManifest();
32
31
  const allScripts = new Set();
33
32
  for (const scriptId of requestInfo.rw.scriptsToBeLoaded) {
34
33
  const script = findScriptForModule(scriptId, manifest);
@@ -1,9 +1,9 @@
1
1
  import { type DocumentProps } from "../lib/router.js";
2
2
  import { type RequestInfo } from "../requestInfo/types.js";
3
- export declare const renderRscThenableToHtmlStream: ({ thenable, Document, requestInfo, shouldSSR, onError, }: {
4
- thenable: any;
3
+ export declare const renderDocumentHtmlStream: ({ rscPayloadStream, Document, requestInfo, shouldSSR, onError, }: {
4
+ rscPayloadStream: ReadableStream;
5
5
  Document: React.FC<DocumentProps>;
6
6
  requestInfo: RequestInfo;
7
7
  shouldSSR: boolean;
8
8
  onError: (error: unknown) => void;
9
- }) => Promise<import("react-dom/server.js").ReactDOMServerReadableStream>;
9
+ }) => Promise<ReadableStream<Uint8Array<ArrayBufferLike>>>;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Preloads } from "./preloads.js";
3
+ import { Stylesheets } from "./stylesheets.js";
4
+ import { renderHtmlStream, createThenableFromReadableStream, } from "rwsdk/__ssr_bridge";
5
+ import { stitchDocumentAndAppStreams } from "../lib/stitchDocumentAndAppStreams.js";
6
+ export const renderDocumentHtmlStream = async ({ rscPayloadStream, Document, requestInfo, shouldSSR, onError, }) => {
7
+ // Extract the app node from the RSC payload
8
+ const rscAppThenable = createThenableFromReadableStream(rscPayloadStream);
9
+ const { node: innerAppNode } = (await rscAppThenable);
10
+ // todo(justinvdm, 18 Jun 2025): We can build on this later to allow users
11
+ // surface context. e.g:
12
+ // * we assign `user: requestInfo.clientCtx` here
13
+ // * user populates requestInfo.clientCtx on worker side
14
+ // * user can import a read only `import { clientCtx } from "rwsdk/client"`
15
+ // on client side
16
+ const clientContext = {
17
+ rw: {
18
+ ssr: shouldSSR,
19
+ },
20
+ };
21
+ // Create the outer document with a marker for injection
22
+ const documentElement = (_jsxs(Document, { ...requestInfo, children: [_jsx("script", { nonce: requestInfo.rw.nonce, dangerouslySetInnerHTML: {
23
+ __html: `globalThis.__RWSDK_CONTEXT = ${JSON.stringify(clientContext)}`,
24
+ } }), _jsx(Stylesheets, { requestInfo: requestInfo }), _jsx(Preloads, { requestInfo: requestInfo }), _jsx("div", { id: "hydrate-root", children: _jsx("div", { id: "rwsdk-app-start" }) })] }));
25
+ const outerHtmlStream = await renderHtmlStream({
26
+ node: documentElement,
27
+ requestInfo,
28
+ onError,
29
+ identifierPrefix: "__RWSDK_DOCUMENT__",
30
+ });
31
+ const appHtmlStream = await renderHtmlStream({
32
+ node: innerAppNode,
33
+ requestInfo,
34
+ onError,
35
+ });
36
+ // Stitch the streams together
37
+ const stitchedStream = stitchDocumentAndAppStreams(outerHtmlStream, appHtmlStream, '<div id="rwsdk-app-start"></div>', '<div id="rwsdk-app-end"></div>');
38
+ return stitchedStream;
39
+ };
@@ -0,0 +1,7 @@
1
+ import { type RequestInfo } from "../requestInfo/types.js";
2
+ export declare const renderHtmlStream: ({ node, identifierPrefix, requestInfo, onError, }: {
3
+ node: React.ReactNode;
4
+ requestInfo: RequestInfo;
5
+ onError: (error: unknown) => void;
6
+ identifierPrefix?: string;
7
+ }) => Promise<import("react-dom/server").ReactDOMServerReadableStream>;
@@ -0,0 +1,31 @@
1
+ import { renderToReadableStream } from "react-dom/server.edge";
2
+ export const renderHtmlStream = async ({ node, identifierPrefix, requestInfo, onError, }) => {
3
+ return await renderToReadableStream(node, {
4
+ nonce: requestInfo.rw.nonce,
5
+ identifierPrefix,
6
+ onError(error, { componentStack }) {
7
+ try {
8
+ if (!error) {
9
+ error = new Error(`A falsy value was thrown during rendering: ${String(error)}.`);
10
+ }
11
+ const message = error
12
+ ? (error.stack ?? error.message ?? error)
13
+ : error;
14
+ const wrappedMessage = `${message}\n\nComponent stack:${componentStack}`;
15
+ if (error instanceof Error) {
16
+ const wrappedError = new Error(wrappedMessage);
17
+ wrappedError.stack = error.stack;
18
+ error = wrappedError;
19
+ }
20
+ else {
21
+ error = new Error(wrappedMessage);
22
+ error.stack = componentStack;
23
+ }
24
+ onError(error);
25
+ }
26
+ catch {
27
+ onError(error);
28
+ }
29
+ },
30
+ });
31
+ };
@@ -1,5 +1,7 @@
1
- export declare const renderToRscStream: (app: {
2
- node: React.ReactElement;
3
- actionResult: any;
1
+ export declare const renderToRscStream: ({ input, onError, }: {
2
+ input: {
3
+ node: React.ReactNode;
4
+ actionResult: unknown;
5
+ };
4
6
  onError?: (error: unknown) => void;
5
7
  }) => ReadableStream;
@@ -1,46 +1,17 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
2
  import { renderToReadableStream as baseRenderToRscStream } from "react-server-dom-webpack/server.edge";
2
3
  import { createClientManifest } from "./createClientManifest.js";
3
- // context(justinvdm, 24 Mar 2025): React flight limits chunks to 28 bytes, so we need to rechunk
4
- // the stream to avoid losing data
5
- function rechunkStream(stream, maxChunkSize = 28) {
6
- const reader = stream.getReader();
7
- return new ReadableStream({
8
- async pull(controller) {
9
- let buffer = new Uint8Array(0);
10
- try {
11
- while (true) {
12
- const { done, value } = await reader.read();
13
- if (done && buffer.length === 0) {
14
- controller.close();
15
- return;
16
- }
17
- if (value) {
18
- buffer = new Uint8Array([...buffer, ...value]);
19
- }
20
- while (buffer.length >= maxChunkSize || (done && buffer.length > 0)) {
21
- const chunk = buffer.slice(0, maxChunkSize);
22
- buffer = buffer.slice(maxChunkSize);
23
- controller.enqueue(chunk);
24
- }
25
- if (done) {
26
- controller.close();
27
- return;
28
- }
29
- }
30
- }
31
- catch (error) {
32
- controller.error(error);
33
- }
34
- },
35
- });
36
- }
37
- export const renderToRscStream = (app) => {
38
- const { node, onError } = app;
39
- let { actionResult } = app;
40
- if (actionResult instanceof ReadableStream) {
41
- actionResult = rechunkStream(actionResult);
42
- }
43
- return baseRenderToRscStream({ node, actionResult }, createClientManifest(), {
4
+ export const renderToRscStream = ({ input, onError, }) => {
5
+ const { node: inputNode, actionResult } = input;
6
+ // context(justinvdm, 2025-09-26): We add a marker here for our stitching logic in
7
+ // renderDocumentHtmlStream() to find and use. It needs to live here rather than there,
8
+ // since it needs to live in the RSC payload for hydration to work.
9
+ const wrappedNode = (_jsxs(_Fragment, { children: [inputNode, _jsx("div", { id: "rwsdk-app-end" })] }));
10
+ const wrappedInput = {
11
+ node: wrappedNode,
12
+ actionResult,
13
+ };
14
+ return baseRenderToRscStream(wrappedInput, createClientManifest(), {
44
15
  onError,
45
16
  });
46
17
  };
@@ -2,8 +2,9 @@ import { ReactElement, FC } from "react";
2
2
  import { DocumentProps } from "../lib/router";
3
3
  export interface RenderToStreamOptions {
4
4
  Document?: FC<DocumentProps>;
5
+ ssr?: boolean;
5
6
  injectRSCPayload?: boolean;
6
7
  onError?: (error: unknown) => void;
7
8
  }
8
9
  export declare const IdentityDocument: FC<DocumentProps>;
9
- export declare const renderToStream: (element: ReactElement, { Document, injectRSCPayload: shouldInjectRSCPayload, onError, }?: RenderToStreamOptions) => Promise<ReadableStream>;
10
+ export declare const renderToStream: (element: ReactElement, { ssr: shouldSSR, Document, injectRSCPayload: shouldInjectRSCPayload, onError, }?: RenderToStreamOptions) => Promise<ReadableStream>;
@@ -1,27 +1,34 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { renderToRscStream } from "./renderToRscStream";
3
- import { transformRscToHtmlStream } from "./transformRscToHtmlStream";
4
3
  import { requestInfo } from "../requestInfo/worker";
5
4
  import { injectRSCPayload } from "rsc-html-stream/server";
5
+ import { renderDocumentHtmlStream } from "./renderDocumentHtmlStream";
6
6
  export const IdentityDocument = ({ children }) => (_jsx(_Fragment, { children: children }));
7
- export const renderToStream = async (element, { Document = IdentityDocument, injectRSCPayload: shouldInjectRSCPayload = false, onError = () => { }, } = {}) => {
7
+ export const renderToStream = async (element, { ssr: shouldSSR = true, Document = IdentityDocument, injectRSCPayload: shouldInjectRSCPayload = true, onError = () => { }, } = {}) => {
8
8
  let rscStream = renderToRscStream({
9
- node: element,
10
- actionResult: null,
9
+ input: {
10
+ node: element,
11
+ actionResult: undefined,
12
+ },
11
13
  onError,
12
14
  });
15
+ let injectRSCStream;
13
16
  if (shouldInjectRSCPayload) {
14
17
  const [rscPayloadStream1, rscPayloadStream2] = rscStream.tee();
15
18
  rscStream = rscPayloadStream1;
16
- rscStream = rscStream.pipeThrough(injectRSCPayload(rscPayloadStream2, {
19
+ injectRSCStream = injectRSCPayload(rscPayloadStream2, {
17
20
  nonce: requestInfo.rw.nonce,
18
- }));
21
+ });
19
22
  }
20
- const htmlStream = await transformRscToHtmlStream({
21
- stream: rscStream,
23
+ let htmlStream = await renderDocumentHtmlStream({
24
+ rscPayloadStream: rscStream,
22
25
  Document,
23
26
  requestInfo,
27
+ shouldSSR,
24
28
  onError,
25
29
  });
30
+ if (injectRSCStream) {
31
+ htmlStream = htmlStream.pipeThrough(injectRSCStream);
32
+ }
26
33
  return htmlStream;
27
34
  };
@@ -1,4 +1,4 @@
1
1
  import { type RequestInfo } from "../requestInfo/types.js";
2
- export declare const Stylesheets: ({ requestInfo }: {
2
+ export declare const Stylesheets: ({ requestInfo, }: {
3
3
  requestInfo: RequestInfo;
4
- }) => import("react/jsx-runtime.js").JSX.Element;
4
+ }) => Promise<import("react/jsx-runtime").JSX.Element>;