@flexireact/core 1.0.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.
@@ -0,0 +1,198 @@
1
+ /**
2
+ * FlexiReact React Server Components (RSC) System
3
+ *
4
+ * RSC allows components to run exclusively on the server, reducing client bundle size
5
+ * and enabling direct database/filesystem access in components.
6
+ *
7
+ * Usage:
8
+ * - Add 'use server' at the top of a component file to make it a Server Component
9
+ * - Add 'use client' at the top to make it a Client Component (hydrated on client)
10
+ * - Server Components can import Client Components, but not vice versa
11
+ */
12
+
13
+ import React from 'react';
14
+ import { renderToString } from 'react-dom/server';
15
+ import { isServerComponent, isClientComponent } from '../utils.js';
16
+
17
+ /**
18
+ * RSC Payload format for streaming
19
+ */
20
+ export const RSC_CONTENT_TYPE = 'text/x-component';
21
+
22
+ /**
23
+ * Processes a component tree for RSC
24
+ * Server components are rendered to HTML, client components are serialized
25
+ */
26
+ export async function processServerComponent(Component, props, context) {
27
+ const componentInfo = {
28
+ isServer: true,
29
+ rendered: null,
30
+ clientComponents: [],
31
+ serverData: {}
32
+ };
33
+
34
+ try {
35
+ // If component has async data fetching
36
+ if (Component.getServerData) {
37
+ componentInfo.serverData = await Component.getServerData(context);
38
+ props = { ...props, ...componentInfo.serverData };
39
+ }
40
+
41
+ // Render the server component
42
+ const element = React.createElement(Component, props);
43
+ componentInfo.rendered = renderToString(element);
44
+
45
+ } catch (error) {
46
+ console.error('RSC Error:', error);
47
+ throw error;
48
+ }
49
+
50
+ return componentInfo;
51
+ }
52
+
53
+ /**
54
+ * Creates a client component reference for RSC payload
55
+ */
56
+ export function createClientReference(componentPath, props) {
57
+ return {
58
+ $$typeof: Symbol.for('react.client.reference'),
59
+ $$id: componentPath,
60
+ $$props: props
61
+ };
62
+ }
63
+
64
+ /**
65
+ * Serializes RSC payload for streaming
66
+ */
67
+ export function serializeRSCPayload(componentTree) {
68
+ const payload = {
69
+ type: 'rsc',
70
+ tree: serializeNode(componentTree),
71
+ timestamp: Date.now()
72
+ };
73
+
74
+ return JSON.stringify(payload);
75
+ }
76
+
77
+ /**
78
+ * Serializes a React node for RSC
79
+ */
80
+ function serializeNode(node) {
81
+ if (node === null || node === undefined) {
82
+ return null;
83
+ }
84
+
85
+ if (typeof node === 'string' || typeof node === 'number' || typeof node === 'boolean') {
86
+ return node;
87
+ }
88
+
89
+ if (Array.isArray(node)) {
90
+ return node.map(serializeNode);
91
+ }
92
+
93
+ if (React.isValidElement(node)) {
94
+ const { type, props } = node;
95
+
96
+ // Handle client component references
97
+ if (type.$$typeof === Symbol.for('react.client.reference')) {
98
+ return {
99
+ $$type: 'client',
100
+ $$id: type.$$id,
101
+ props: serializeProps(props)
102
+ };
103
+ }
104
+
105
+ // Handle regular elements
106
+ const typeName = typeof type === 'string' ? type : type.displayName || type.name || 'Unknown';
107
+
108
+ return {
109
+ $$type: 'element',
110
+ type: typeName,
111
+ props: serializeProps(props)
112
+ };
113
+ }
114
+
115
+ return String(node);
116
+ }
117
+
118
+ /**
119
+ * Serializes props, handling special cases
120
+ */
121
+ function serializeProps(props) {
122
+ const serialized = {};
123
+
124
+ for (const [key, value] of Object.entries(props)) {
125
+ if (key === 'children') {
126
+ serialized.children = serializeNode(value);
127
+ } else if (typeof value === 'function') {
128
+ // Functions can't be serialized - mark as client action
129
+ serialized[key] = { $$type: 'action', name: value.name };
130
+ } else if (value instanceof Date) {
131
+ serialized[key] = { $$type: 'date', value: value.toISOString() };
132
+ } else if (typeof value === 'object' && value !== null) {
133
+ serialized[key] = JSON.parse(JSON.stringify(value));
134
+ } else {
135
+ serialized[key] = value;
136
+ }
137
+ }
138
+
139
+ return serialized;
140
+ }
141
+
142
+ /**
143
+ * Server Actions support
144
+ * Allows calling server functions from client components
145
+ */
146
+ export function createServerAction(fn, actionId) {
147
+ // Mark function as server action
148
+ fn.$$typeof = Symbol.for('react.server.action');
149
+ fn.$$id = actionId;
150
+
151
+ return fn;
152
+ }
153
+
154
+ /**
155
+ * Handles server action invocation
156
+ */
157
+ export async function handleServerAction(actionId, args, context) {
158
+ // Actions are registered during build
159
+ const action = globalThis.__FLEXI_ACTIONS__?.[actionId];
160
+
161
+ if (!action) {
162
+ throw new Error(`Server action not found: ${actionId}`);
163
+ }
164
+
165
+ return await action(...args, context);
166
+ }
167
+
168
+ /**
169
+ * RSC Boundary component
170
+ * Marks the boundary between server and client rendering
171
+ */
172
+ export function ServerBoundary({ children, fallback }) {
173
+ return children;
174
+ }
175
+
176
+ /**
177
+ * Client Boundary component
178
+ * Marks components that should be hydrated on the client
179
+ */
180
+ export function ClientBoundary({ children, fallback }) {
181
+ // On server, render children normally
182
+ // On client, this will be hydrated
183
+ return React.createElement('div', {
184
+ 'data-flexi-client': 'true',
185
+ children
186
+ });
187
+ }
188
+
189
+ export default {
190
+ processServerComponent,
191
+ createClientReference,
192
+ serializeRSCPayload,
193
+ createServerAction,
194
+ handleServerAction,
195
+ ServerBoundary,
196
+ ClientBoundary,
197
+ RSC_CONTENT_TYPE
198
+ };