@solidjs/web 2.0.0-beta.27 → 2.0.0-beta.29
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/dist/dev.cjs +40 -2
- package/dist/dev.js +39 -4
- package/dist/server.cjs +113 -37
- package/dist/server.js +112 -39
- package/dist/web.cjs +40 -2
- package/dist/web.js +39 -4
- package/frames/dist/client.cjs +370 -209
- package/frames/dist/client.dev.cjs +370 -210
- package/frames/dist/client.dev.js +371 -211
- package/frames/dist/client.js +371 -210
- package/frames/dist/server.cjs +373 -74
- package/frames/dist/server.js +373 -75
- package/package.json +3 -3
- package/server-functions/dist/client.cjs +100 -3
- package/server-functions/dist/client.js +92 -4
- package/server-functions/dist/server.cjs +85 -17
- package/server-functions/dist/server.js +83 -17
- package/types/client.d.ts +15 -0
- package/types/core.d.ts +1 -1
- package/types/frames/client.d.ts +8 -5
- package/types/frames/frame-client.d.ts +17 -0
- package/types/frames/frame-sink.d.ts +29 -6
- package/types/frames/frame-transport.d.ts +76 -12
- package/types/frames/server.d.ts +29 -1
- package/types/index.d.ts +74 -0
- package/types/server-functions/client.d.ts +25 -0
- package/types/server-functions/server.d.ts +105 -18
- package/types/server-functions/shared.d.ts +22 -0
- package/types/server-mock.d.ts +6 -2
- package/types/server.d.ts +39 -1
- package/types-cjs/client.d.cts +15 -0
- package/types-cjs/core.d.cts +1 -1
- package/types-cjs/frames/client.d.cts +8 -5
- package/types-cjs/frames/frame-client.d.cts +17 -0
- package/types-cjs/frames/frame-sink.d.cts +29 -6
- package/types-cjs/frames/frame-transport.d.cts +76 -12
- package/types-cjs/frames/server.d.cts +29 -1
- package/types-cjs/index.d.cts +74 -0
- package/types-cjs/server-functions/client.d.cts +25 -0
- package/types-cjs/server-functions/server.d.cts +105 -18
- package/types-cjs/server-functions/shared.d.cts +22 -0
- package/types-cjs/server-mock.d.cts +6 -2
- package/types-cjs/server.d.cts +39 -1
|
@@ -6,6 +6,7 @@ const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
|
|
|
6
6
|
function isResponseEnvelope(value) {
|
|
7
7
|
return !!(value && typeof value === "object" && value[ENVELOPE]);
|
|
8
8
|
}
|
|
9
|
+
const REVALIDATE_HEADER = "X-Revalidate";
|
|
9
10
|
|
|
10
11
|
Feature.AggregateError | Feature.BigIntTypedArray;
|
|
11
12
|
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
|
|
@@ -341,6 +342,18 @@ async function decodeResponse(response, codecOptions) {
|
|
|
341
342
|
if (!response.body) return undefined;
|
|
342
343
|
return await extractBody(response, codecOptions === undefined ? codecConfig.codec : codecOptions);
|
|
343
344
|
}
|
|
345
|
+
async function decodeResponsePayload(response, codecOptions) {
|
|
346
|
+
const decoded = await decodeResponse(response, codecOptions);
|
|
347
|
+
if (decoded !== undefined && response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
348
|
+
return {
|
|
349
|
+
value: decoded.value,
|
|
350
|
+
flightData: decoded.data
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
value: decoded
|
|
355
|
+
};
|
|
356
|
+
}
|
|
344
357
|
|
|
345
358
|
function encodeInputValue(value) {
|
|
346
359
|
if (value instanceof FormData) return {
|
|
@@ -395,6 +408,7 @@ const config = {
|
|
|
395
408
|
provideEvent: undefined,
|
|
396
409
|
collectFlightData: undefined,
|
|
397
410
|
transformResult: undefined,
|
|
411
|
+
transformFlightResult: undefined,
|
|
398
412
|
transformDirectResult: undefined,
|
|
399
413
|
handleNoJS: undefined,
|
|
400
414
|
endpoint: "/_server"
|
|
@@ -403,6 +417,7 @@ function configureServerFunctionsServer({
|
|
|
403
417
|
provideEvent,
|
|
404
418
|
collectFlightData,
|
|
405
419
|
transformResult,
|
|
420
|
+
transformFlightResult,
|
|
406
421
|
transformDirectResult,
|
|
407
422
|
handleNoJS,
|
|
408
423
|
endpoint,
|
|
@@ -411,6 +426,7 @@ function configureServerFunctionsServer({
|
|
|
411
426
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
412
427
|
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
|
|
413
428
|
if (transformResult !== undefined) config.transformResult = transformResult;
|
|
429
|
+
if (transformFlightResult !== undefined) config.transformFlightResult = transformFlightResult;
|
|
414
430
|
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
|
|
415
431
|
if (handleNoJS !== undefined) config.handleNoJS = handleNoJS;
|
|
416
432
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
@@ -424,6 +440,7 @@ function provideEvent(event, fn) {
|
|
|
424
440
|
}
|
|
425
441
|
const REGISTRATIONS = new Map();
|
|
426
442
|
const METHODS = new Map();
|
|
443
|
+
const INVOCATIONS = new WeakMap();
|
|
427
444
|
function registerServerFunction(id, callback) {
|
|
428
445
|
REGISTRATIONS.set(id, callback);
|
|
429
446
|
return callback;
|
|
@@ -467,9 +484,9 @@ function createServerReference({
|
|
|
467
484
|
const evt = {
|
|
468
485
|
...ogEvt
|
|
469
486
|
};
|
|
470
|
-
evt
|
|
487
|
+
INVOCATIONS.set(evt, {
|
|
471
488
|
id
|
|
472
|
-
};
|
|
489
|
+
});
|
|
473
490
|
evt.serverOnly = true;
|
|
474
491
|
const result = provideEvent(evt, () => {
|
|
475
492
|
return fn.apply(thisArg, args);
|
|
@@ -478,11 +495,13 @@ function createServerReference({
|
|
|
478
495
|
if (transform && result && typeof result.then === "function") {
|
|
479
496
|
return result.then(value => transform(value, {
|
|
480
497
|
id,
|
|
498
|
+
args,
|
|
481
499
|
event: evt
|
|
482
500
|
}));
|
|
483
501
|
}
|
|
484
502
|
return transform ? transform(result, {
|
|
485
503
|
id,
|
|
504
|
+
args,
|
|
486
505
|
event: evt
|
|
487
506
|
}) : result;
|
|
488
507
|
}
|
|
@@ -497,9 +516,11 @@ function GET(fn) {
|
|
|
497
516
|
method: "GET"
|
|
498
517
|
});
|
|
499
518
|
}
|
|
500
|
-
function
|
|
501
|
-
|
|
502
|
-
|
|
519
|
+
function getServerFunctionInvocation() {
|
|
520
|
+
return getEventServerFunctionInvocation(getRequestEvent());
|
|
521
|
+
}
|
|
522
|
+
function getEventServerFunctionInvocation(event) {
|
|
523
|
+
return event && INVOCATIONS.get(event);
|
|
503
524
|
}
|
|
504
525
|
function resolveFunctionId(request, url) {
|
|
505
526
|
const reference = request.headers.get(FUNCTION_HEADER);
|
|
@@ -529,15 +550,49 @@ async function parseArguments(request, url, instance, codec) {
|
|
|
529
550
|
}
|
|
530
551
|
return parsed;
|
|
531
552
|
}
|
|
532
|
-
async function foldFlightData(hook, event, headers, outcome) {
|
|
553
|
+
async function foldFlightData(hook, event, headers, outcome, context = {}) {
|
|
554
|
+
if (outcome.value instanceof Response && outcome.value.body) return outcome.value;
|
|
555
|
+
digestOutcome(event, outcome);
|
|
533
556
|
const data = await hook(event, outcome);
|
|
534
557
|
if (data === undefined) return outcome.value;
|
|
535
558
|
headers.set(SINGLE_FLIGHT_HEADER, "true");
|
|
559
|
+
if (context.transformFlightResult) {
|
|
560
|
+
const transformed = await context.transformFlightResult(event, {
|
|
561
|
+
value: outcome.value,
|
|
562
|
+
data
|
|
563
|
+
}, context);
|
|
564
|
+
if (transformed !== undefined) {
|
|
565
|
+
for (const cookie of headers.getSetCookie()) transformed.headers.append("Set-Cookie", cookie);
|
|
566
|
+
headers.forEach((value, key) => {
|
|
567
|
+
if (key !== "set-cookie" && !transformed.headers.has(key)) {
|
|
568
|
+
transformed.headers.set(key, value);
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
return transformed;
|
|
572
|
+
}
|
|
573
|
+
}
|
|
536
574
|
return {
|
|
537
575
|
value: outcome.value,
|
|
538
576
|
data
|
|
539
577
|
};
|
|
540
578
|
}
|
|
579
|
+
function digestOutcome(event, outcome) {
|
|
580
|
+
const {
|
|
581
|
+
request,
|
|
582
|
+
response
|
|
583
|
+
} = outcome;
|
|
584
|
+
outcome.revalidateKeys = response?.headers.get(REVALIDATE_HEADER)?.split(",");
|
|
585
|
+
outcome.foldedHeaders = foldSetCookies(request.headers, [...(event.response?.headers?.getSetCookie() ?? []), ...(response?.headers?.getSetCookie() ?? [])]);
|
|
586
|
+
try {
|
|
587
|
+
const referrer = request.headers.get("referer");
|
|
588
|
+
if (referrer) {
|
|
589
|
+
const location = response?.headers.get("Location");
|
|
590
|
+
const target = location ? new URL(location, request.url) : new URL(referrer);
|
|
591
|
+
if (target.origin === new URL(request.url).origin) outcome.targetUrl = target.toString();
|
|
592
|
+
}
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
595
|
+
}
|
|
541
596
|
function parseSetCookie(setCookie) {
|
|
542
597
|
const [pair, ...attributes] = setCookie.split(";");
|
|
543
598
|
const eq = pair.indexOf("=");
|
|
@@ -676,22 +731,29 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
676
731
|
const provide = options.provideEvent || provideEvent;
|
|
677
732
|
const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
|
|
678
733
|
const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
|
|
734
|
+
const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;
|
|
679
735
|
const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;
|
|
680
736
|
const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
|
|
681
737
|
const parsed = await parseArguments(request, url, instance, codec);
|
|
738
|
+
const flightContext = {
|
|
739
|
+
id: functionId,
|
|
740
|
+
args: parsed,
|
|
741
|
+
instance,
|
|
742
|
+
request,
|
|
743
|
+
collectsFlight,
|
|
744
|
+
codec,
|
|
745
|
+
transformFlightResult
|
|
746
|
+
};
|
|
682
747
|
const headers = new Headers();
|
|
683
748
|
try {
|
|
684
749
|
let result = await provide(event, async () => {
|
|
685
|
-
event
|
|
750
|
+
INVOCATIONS.set(event, {
|
|
686
751
|
id: functionId
|
|
687
|
-
};
|
|
752
|
+
});
|
|
688
753
|
return serverFunction(...parsed);
|
|
689
754
|
});
|
|
690
755
|
if (transformResult) {
|
|
691
|
-
result = await transformResult(event, result,
|
|
692
|
-
instance,
|
|
693
|
-
request
|
|
694
|
-
});
|
|
756
|
+
result = await transformResult(event, result, flightContext);
|
|
695
757
|
}
|
|
696
758
|
let status = 200;
|
|
697
759
|
let metadata;
|
|
@@ -733,7 +795,8 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
733
795
|
response: metadata,
|
|
734
796
|
request,
|
|
735
797
|
thrown: false
|
|
736
|
-
});
|
|
798
|
+
}, flightContext);
|
|
799
|
+
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
|
|
737
800
|
}
|
|
738
801
|
if (!instance) {
|
|
739
802
|
if (handleNoJS) return handleNoJS(result, request, parsed);
|
|
@@ -745,8 +808,7 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
745
808
|
if (x instanceof Response || isResponseEnvelope(x)) {
|
|
746
809
|
if (transformResult) {
|
|
747
810
|
x = await transformResult(event, x, {
|
|
748
|
-
|
|
749
|
-
request,
|
|
811
|
+
...flightContext,
|
|
750
812
|
thrown: true
|
|
751
813
|
});
|
|
752
814
|
}
|
|
@@ -784,7 +846,11 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
784
846
|
response: metadata,
|
|
785
847
|
request,
|
|
786
848
|
thrown: true
|
|
787
|
-
});
|
|
849
|
+
}, flightContext);
|
|
850
|
+
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
|
|
851
|
+
x.headers.set(ERROR_HEADER, "true");
|
|
852
|
+
return x;
|
|
853
|
+
}
|
|
788
854
|
}
|
|
789
855
|
headers.set(ERROR_HEADER, "true");
|
|
790
856
|
if (!instance) {
|
|
@@ -806,4 +872,4 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
806
872
|
}
|
|
807
873
|
}
|
|
808
874
|
|
|
809
|
-
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getServerFunction,
|
|
875
|
+
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
|
package/types/client.d.ts
CHANGED
|
@@ -148,6 +148,21 @@ export function generateHydrationScript(options?: {
|
|
|
148
148
|
eventNames?: string[];
|
|
149
149
|
}): string;
|
|
150
150
|
export function Assets(props: { children?: JSX.Element }): JSX.Element;
|
|
151
|
+
/**
|
|
152
|
+
* See the server entry's `ResponseStub` — the shape of the mutable response
|
|
153
|
+
* head integrations expose as `event.response` via module augmentation.
|
|
154
|
+
*/
|
|
155
|
+
export interface ResponseStub {
|
|
156
|
+
status?: number;
|
|
157
|
+
statusText?: string;
|
|
158
|
+
headers: Headers;
|
|
159
|
+
/**
|
|
160
|
+
* Set by the integration once the response head has been derived/sent
|
|
161
|
+
* from this stub (status/headers can no longer change); consumers must
|
|
162
|
+
* treat later writes and cleanup-time retractions as no-ops.
|
|
163
|
+
*/
|
|
164
|
+
committed?: boolean;
|
|
165
|
+
}
|
|
151
166
|
export interface RequestEvent {
|
|
152
167
|
request: Request;
|
|
153
168
|
locals: Record<string | number | symbol, any>;
|
package/types/core.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration } from "solid-js";
|
|
1
|
+
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope } from "solid-js";
|
|
2
2
|
export declare const effect: (fn: any, effectFn: any, options: any) => void;
|
|
3
3
|
export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
|
|
4
4
|
export declare const runWithHydrationScope: (id: any, fn: any) => unknown;
|
package/types/frames/client.d.ts
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
export { createFrame, createFrameHost, createFrameElement, FRAME_APPLIED_EVENT } from "./frame-client.js";
|
|
2
2
|
export { FRAME_STREAM_HEADER, applyFrameResponse, isFrameStreamResponse, createServerComponentHandler } from "./frame-transport.js";
|
|
3
3
|
export { createJSONDataTable } from "./serializer.js";
|
|
4
|
+
export type { Slot } from "./server.js";
|
|
4
5
|
export declare function getFrameHost(): any;
|
|
5
6
|
/**
|
|
6
7
|
* Installs the server-component transport policy on the server-function
|
|
7
|
-
* client: boundary identity
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
8
|
+
* client: boundary identity is the call's intrinsic (function, arguments)
|
|
9
|
+
* address — per-args, exactly like the query cache, so a cached component
|
|
10
|
+
* always mounts the boundary showing the call it was cached for. Repeat
|
|
11
|
+
* calls for the same args resolve the identical component (refetches morph
|
|
12
|
+
* in place, cache hits pass `dynamic`'s equals-gate); a source switching
|
|
13
|
+
* args swaps boundaries, re-materialized instantly from the host's
|
|
14
|
+
* retained state.
|
|
12
15
|
*
|
|
13
16
|
* Call once in the client entry (an explicit call — the package is
|
|
14
17
|
* `sideEffects: false`, so a bare import would be tree-shaken away);
|
|
@@ -71,6 +71,14 @@ export interface SlotContext {
|
|
|
71
71
|
* re-call displaced (e.g. `{$frame}` region ranges) is dropped.
|
|
72
72
|
*/
|
|
73
73
|
adopted?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Whether this occurrence is a render-prop CALL (the producer placed it
|
|
76
|
+
* with arguments — possibly empty — via a slot record) as opposed to a
|
|
77
|
+
* direct-insert position. Consumers cannot tell from the resolved props
|
|
78
|
+
* alone: an argless render prop and a direct insert both arrive as `{}`,
|
|
79
|
+
* but one is a function to invoke and the other a value to place.
|
|
80
|
+
*/
|
|
81
|
+
invoked?: boolean;
|
|
74
82
|
/**
|
|
75
83
|
* Register cleanup for when this occurrence's range is removed from the
|
|
76
84
|
* server content, or the owning frame is disposed.
|
|
@@ -83,6 +91,15 @@ export interface SlotContext {
|
|
|
83
91
|
* in place (zero DOM mutation).
|
|
84
92
|
*/
|
|
85
93
|
existing: ChildNode[];
|
|
94
|
+
/**
|
|
95
|
+
* The range's own marker comments, when the occurrence has a placed range.
|
|
96
|
+
* A framework binding whose slot content is reactive at the top level (a
|
|
97
|
+
* boundary accessor, changing route children) owns the interior instead of
|
|
98
|
+
* returning nodes: bind before `end` with the framework's insert primitive
|
|
99
|
+
* and return `undefined` — the frame leaves the range alone (server morphs
|
|
100
|
+
* already protect slot ranges).
|
|
101
|
+
*/
|
|
102
|
+
range?: { start: Comment; end: Comment };
|
|
86
103
|
}
|
|
87
104
|
|
|
88
105
|
/**
|
|
@@ -126,16 +126,39 @@ export function createDocumentSlotProps(
|
|
|
126
126
|
* as `configureServerFunctionsServer({ transformDirectResult })` and a
|
|
127
127
|
* direct (same-process) server-function result that is a function comes back
|
|
128
128
|
* as an inline-renderable server component (frame markers + document
|
|
129
|
-
* slot props)
|
|
129
|
+
* slot props), branded with its function id and the call's wire address.
|
|
130
|
+
* Non-function results pass through.
|
|
130
131
|
*/
|
|
131
|
-
export function frameTransformDirectResult<T>(
|
|
132
|
+
export function frameTransformDirectResult<T>(
|
|
133
|
+
value: T,
|
|
134
|
+
options: { id: string; args?: unknown[] }
|
|
135
|
+
): T;
|
|
132
136
|
|
|
133
137
|
/**
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
* (
|
|
138
|
+
* The frame half of single-flight, as a `transformFlightResult` policy for
|
|
139
|
+
* `handleServerFunctionRequest`: when part of what a mutation invalidated is
|
|
140
|
+
* markup (a component-valued flight-data entry), the frame stream carries
|
|
141
|
+
* the whole payload — each component's content as a region addressed by its
|
|
142
|
+
* call, the `{ value, data }` envelope as `outcome` chunks with the
|
|
143
|
+
* component entries serialized as flight references. Returns `undefined`
|
|
144
|
+
* when nothing invalidated is markup (the response stays the plain
|
|
145
|
+
* single-flight envelope).
|
|
137
146
|
*/
|
|
138
|
-
export
|
|
147
|
+
export function frameTransformFlightResult(
|
|
148
|
+
event: unknown,
|
|
149
|
+
outcome: { value: unknown; data: unknown },
|
|
150
|
+
context?: unknown
|
|
151
|
+
): Promise<Response | undefined>;
|
|
152
|
+
|
|
153
|
+
// The brands and the codec plugin live with the transport (client bundles
|
|
154
|
+
// resolve flight references against the live registry); re-exported here for
|
|
155
|
+
// server integrations importing the document-SSR surface.
|
|
156
|
+
export {
|
|
157
|
+
SERVER_COMPONENT,
|
|
158
|
+
SERVER_COMPONENT_ADDRESS,
|
|
159
|
+
SERVER_COMPONENT_SOURCE,
|
|
160
|
+
ServerComponentPlugin
|
|
161
|
+
} from "./frame-transport.js";
|
|
139
162
|
|
|
140
163
|
/**
|
|
141
164
|
* Inline bootstrap for the document shell: installs the `self._$SC`
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { FrameChunk, FrameHost } from "./frame-client.js";
|
|
2
|
+
import { JSONCodecOptions } from "./serializer.js";
|
|
3
|
+
|
|
4
|
+
// Structural mirror of server-functions/shared.js's FlightDataConsumer:
|
|
5
|
+
// this file may only reference siblings that ship with it when integrations
|
|
6
|
+
// copy the frames declaration set (solid-web's types build), and the
|
|
7
|
+
// server-functions declarations are copied to a different root.
|
|
8
|
+
type FlightConsumer = (data: unknown, context: { response: Response }) => void | Promise<void>;
|
|
2
9
|
|
|
3
10
|
/**
|
|
4
11
|
* Header tagging a Response as a frame stream; its value is the producing
|
|
@@ -23,9 +30,24 @@ export interface ApplyFrameResponseOptions {
|
|
|
23
30
|
* Restamp every chunk of the response with this version (one response IS
|
|
24
31
|
* one version). Versions belong to the client too: the producer cannot
|
|
25
32
|
* know how many streams a boundary has consumed, so pass the Nth-response
|
|
26
|
-
* counter to make policy A's stale-guard real across navigations.
|
|
33
|
+
* counter to make policy A's stale-guard real across navigations. A
|
|
34
|
+
* single-flight response addresses several boundaries, each with its own
|
|
35
|
+
* history — pass a function and it is called once per frame in the
|
|
36
|
+
* response.
|
|
37
|
+
*/
|
|
38
|
+
version?: number | ((frameId: string) => number);
|
|
39
|
+
/**
|
|
40
|
+
* Remap any frame id other than the response's own root onto a local one
|
|
41
|
+
* — how a consumer resolves the addresses a single-flight response uses
|
|
42
|
+
* for the regions it refreshed.
|
|
27
43
|
*/
|
|
28
|
-
|
|
44
|
+
route?(id: string): string;
|
|
45
|
+
/**
|
|
46
|
+
* Receives the payload text of each `outcome` chunk — the response-scoped
|
|
47
|
+
* single-flight envelope, the caller's result rather than anything the
|
|
48
|
+
* host renders.
|
|
49
|
+
*/
|
|
50
|
+
onOutcome?(payload: string): void;
|
|
29
51
|
}
|
|
30
52
|
|
|
31
53
|
/**
|
|
@@ -48,6 +70,31 @@ export function applyFrameResponse(
|
|
|
48
70
|
options?: ApplyFrameResponseOptions
|
|
49
71
|
): Promise<string>;
|
|
50
72
|
|
|
73
|
+
/** Brands an inline-rendered server component with its function id. */
|
|
74
|
+
export const SERVER_COMPONENT: unique symbol;
|
|
75
|
+
|
|
76
|
+
/** The unwrapped server component behind an inline-render wrap. */
|
|
77
|
+
export const SERVER_COMPONENT_SOURCE: unique symbol;
|
|
78
|
+
|
|
79
|
+
/** The call's wire address (`frameAddress`), for regions to be emitted under. */
|
|
80
|
+
export const SERVER_COMPONENT_ADDRESS: unique symbol;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Seroval plugin for a server component crossing a serialization boundary:
|
|
84
|
+
* a branded component serializes as a REFERENCE — a per-function document
|
|
85
|
+
* placeholder in the hydration serializer, a live-registry lookup by call
|
|
86
|
+
* address in the JSON codec (single-flight envelopes) — its markup never
|
|
87
|
+
* rides as data.
|
|
88
|
+
*/
|
|
89
|
+
export const ServerComponentPlugin: unknown;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The codec options for a single-flight envelope: `codec` plus
|
|
93
|
+
* `ServerComponentPlugin` (deduped by tag). Injected by the protocol on both
|
|
94
|
+
* legs; exported for integrations composing their own flight carriers.
|
|
95
|
+
*/
|
|
96
|
+
export function flightCodec(codec?: JSONCodecOptions): JSONCodecOptions;
|
|
97
|
+
|
|
51
98
|
/** Options for `createServerComponentHandler`. */
|
|
52
99
|
export interface ServerComponentHandlerOptions<C = unknown> {
|
|
53
100
|
host: FrameHost;
|
|
@@ -57,12 +104,6 @@ export interface ServerComponentHandlerOptions<C = unknown> {
|
|
|
57
104
|
* own frame instance under the boundary id (multi-mount fans out).
|
|
58
105
|
*/
|
|
59
106
|
component(frameId: string): C;
|
|
60
|
-
/**
|
|
61
|
-
* Runs synchronously at each server-function call site (before any
|
|
62
|
-
* await); its return is the call's ambient identity — e.g. Solid's
|
|
63
|
-
* `getOwner`. Calls sharing a captured context share one boundary.
|
|
64
|
-
*/
|
|
65
|
-
capture?(info: { id: string; meta: unknown }): unknown;
|
|
66
107
|
/**
|
|
67
108
|
* A new response is about to stream into a boundary: rotate
|
|
68
109
|
* response-scoped state (codec data tables) here. `version` is the
|
|
@@ -83,6 +124,18 @@ export interface ServerComponentHandlerOptions<C = unknown> {
|
|
|
83
124
|
* never observes a pending beat.
|
|
84
125
|
*/
|
|
85
126
|
intercept?(info: { id: string; meta: unknown; args: unknown[] }): C | undefined;
|
|
127
|
+
/**
|
|
128
|
+
* Reads the registered single-flight consumer at delivery time. The
|
|
129
|
+
* consumer is module state in the server-function client's SHARED
|
|
130
|
+
* instance; pass a getter reading that instance when your bundling gives
|
|
131
|
+
* this module a private copy. Defaults to the local copy's reader.
|
|
132
|
+
*/
|
|
133
|
+
consumer?(): FlightConsumer | undefined;
|
|
134
|
+
/**
|
|
135
|
+
* Reads the configured codec options at decode time — same instance-
|
|
136
|
+
* identity contract as `consumer`. Defaults to the local copy's reader.
|
|
137
|
+
*/
|
|
138
|
+
codec?(): JSONCodecOptions | undefined;
|
|
86
139
|
}
|
|
87
140
|
|
|
88
141
|
/**
|
|
@@ -92,15 +145,26 @@ export interface ServerComponentHandlerOptions<C = unknown> {
|
|
|
92
145
|
* (Solid's `dynamic`) never remounts across refetches — the response streams
|
|
93
146
|
* into the boundary underneath as the only observable effect.
|
|
94
147
|
*
|
|
95
|
-
* Boundary identity is derived, never declared:
|
|
96
|
-
*
|
|
97
|
-
*
|
|
148
|
+
* Boundary identity is derived, never declared: every call keys by its
|
|
149
|
+
* intrinsic (function, arguments) address — the query cache's per-args rule,
|
|
150
|
+
* so cached components and boundaries stay one-to-one. Same-args calls
|
|
151
|
+
* resolve the identical component and morph in place; an args switch swaps
|
|
152
|
+
* boundaries, re-materialized from the host's retained state.
|
|
98
153
|
*/
|
|
99
154
|
export function createServerComponentHandler<C>(options: ServerComponentHandlerOptions<C>): {
|
|
100
|
-
capture?(info: { id: string; meta: unknown }): unknown;
|
|
101
155
|
intercept?(info: { id: string; meta: unknown; args: unknown[] }): C | undefined;
|
|
102
156
|
handle(
|
|
103
157
|
response: Response,
|
|
104
158
|
ctx: { id: string; meta: unknown; args: unknown[]; context: unknown }
|
|
105
159
|
): C | undefined;
|
|
160
|
+
/**
|
|
161
|
+
* Declares that the document is showing a call: hydration-data references
|
|
162
|
+
* carry their call's address (`_$SC.r(id, address)`) but never travel
|
|
163
|
+
* through the transport, so the integration forwards those records here —
|
|
164
|
+
* they are how a post-load call for the same (function, arguments) finds
|
|
165
|
+
* its way back to the adopted boundary. `component` must be the exact
|
|
166
|
+
* reference the integration's cache holds for the call (the per-function
|
|
167
|
+
* placeholder), or readers' equals-gates fail into remounts.
|
|
168
|
+
*/
|
|
169
|
+
showing(address: string, functionId: string, component: C): void;
|
|
106
170
|
};
|
package/types/frames/server.d.ts
CHANGED
|
@@ -1,2 +1,30 @@
|
|
|
1
|
-
|
|
1
|
+
import type { Element as SolidElement } from "solid-js";
|
|
2
|
+
/**
|
|
3
|
+
* A client position in a server component: a prop the server renders (as JSX
|
|
4
|
+
* or by calling it) where client-owned markup belongs. `P` is the client
|
|
5
|
+
* component's own props, so a server component can reference the client
|
|
6
|
+
* component's type directly instead of restating it.
|
|
7
|
+
*
|
|
8
|
+
* Arguments are classified by VALUE, not by name — any prop may carry any of
|
|
9
|
+
* these:
|
|
10
|
+
*
|
|
11
|
+
* - primitives ride the chunk;
|
|
12
|
+
* - server JSX streams as a nested region (html once, never data);
|
|
13
|
+
* - anything else serializes as a data record.
|
|
14
|
+
*
|
|
15
|
+
* Async server JSX in an argument needs its own boundary: the region is
|
|
16
|
+
* emitted as one finished string, so a bare async read has no fallback to
|
|
17
|
+
* show and no fragment to reveal into.
|
|
18
|
+
*
|
|
19
|
+
* `$key` names the occurrence so client state follows an entity across
|
|
20
|
+
* responses rather than being positional — the slot-level analogue of `For`'s
|
|
21
|
+
* `keyed`, for when references can't carry identity because every response
|
|
22
|
+
* re-creates everything. It is occurrence identity, not client data: it is
|
|
23
|
+
* stripped before the client component sees its props. Positional identity is
|
|
24
|
+
* the right default; `$key` matters when a live list reorders.
|
|
25
|
+
*/
|
|
26
|
+
export type Slot<P = {}> = (props: P & {
|
|
27
|
+
$key?: string | number;
|
|
28
|
+
}) => SolidElement;
|
|
29
|
+
export { renderToFrameStream, renderServerComponent, serverComponentResponse, frameTransformResult, frameTransformFlightResult, createFrameSink, frameTransformDirectResult, ServerComponentPlugin, SERVER_COMPONENT_BOOTSTRAP } from "./frame-sink.js";
|
|
2
30
|
export { FRAME_STREAM_HEADER, isFrameStreamResponse } from "./frame-transport.js";
|
package/types/index.d.ts
CHANGED
|
@@ -176,3 +176,77 @@ export declare function dynamic<T extends ValidComponent>(source: () => T | Prom
|
|
|
176
176
|
* @description https://docs.solidjs.com/reference/components/dynamic
|
|
177
177
|
*/
|
|
178
178
|
export declare function Dynamic<T extends ValidComponent>(props: DynamicProps<T>): JSX.Element;
|
|
179
|
+
/**
|
|
180
|
+
* Wraps a dynamically imported component so it renders only in the browser.
|
|
181
|
+
* The server renders `props.fallback` (and nothing else); the client shows
|
|
182
|
+
* the fallback until the import resolves and the tree has mounted, then
|
|
183
|
+
* swaps the real component in.
|
|
184
|
+
*
|
|
185
|
+
* Unlike `lazy()`, this avoids Suspense entirely and never server-renders
|
|
186
|
+
* the wrapped component — only the fallback — so the component participates
|
|
187
|
+
* in no hydration asset manifest and its code is guaranteed to never run on
|
|
188
|
+
* the server (safe for browser-only libraries touching `window`, DOM
|
|
189
|
+
* measurement, etc.). The mount gate keeps hydration safe: during hydration
|
|
190
|
+
* the fallback is rendered exactly as the server did, and the swap happens
|
|
191
|
+
* only after settle, so there is no mismatch.
|
|
192
|
+
*
|
|
193
|
+
* By default the import starts as soon as `clientOnly` is called (module
|
|
194
|
+
* load); pass `{ lazy: true }` to defer the import to the component's first
|
|
195
|
+
* render.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```tsx
|
|
199
|
+
* const Chart = clientOnly(() => import("./Chart.jsx"));
|
|
200
|
+
* // <Chart fallback={<div>Loading chart…</div>} data={data()} />
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
203
|
+
export declare function clientOnly<T extends Component<any>>(fn: () => Promise<{
|
|
204
|
+
default: T;
|
|
205
|
+
}>, options?: {
|
|
206
|
+
lazy?: boolean;
|
|
207
|
+
}): Component<ComponentProps<T> & {
|
|
208
|
+
fallback?: JSX.Element;
|
|
209
|
+
}>;
|
|
210
|
+
/**
|
|
211
|
+
* Declares the HTTP response status (and optional status text) for the
|
|
212
|
+
* lifetime of the current reactive scope during SSR — call it bare in a
|
|
213
|
+
* component or reactive-scope body where the status is decided (a 404
|
|
214
|
+
* route, an error fallback). Client build: a no-op — the response head was
|
|
215
|
+
* sent long ago.
|
|
216
|
+
*
|
|
217
|
+
* Naming note — this is a scope-tied *declaration*, not a mutation: "while
|
|
218
|
+
* this reactive scope is live, the response has this status." Solid
|
|
219
|
+
* reserves `set*` verbs for event-time mutation; like
|
|
220
|
+
* `createSignal`/`onCleanup` this is called in scope bodies and un-declares
|
|
221
|
+
* on scope disposal.
|
|
222
|
+
*
|
|
223
|
+
* Retraction semantics (server): the write snapshots the previous
|
|
224
|
+
* `event.response` status at write time and restores it when the owning
|
|
225
|
+
* scope is disposed — so a boundary that errored, declared a status, and
|
|
226
|
+
* then recovered retracts its write instead of stomping a status a
|
|
227
|
+
* surviving part of the tree legitimately set. Once the integration marks
|
|
228
|
+
* the response head `committed` (head derived/sent), writes and
|
|
229
|
+
* retractions are no-ops.
|
|
230
|
+
*/
|
|
231
|
+
export declare function httpStatus(_code: number, _text?: string): void;
|
|
232
|
+
/**
|
|
233
|
+
* Declares an HTTP response header (or with `append`, appends to one) for
|
|
234
|
+
* the lifetime of the current reactive scope during SSR — call it bare in a
|
|
235
|
+
* component or reactive-scope body. Client build: a no-op — the response
|
|
236
|
+
* head was sent long ago.
|
|
237
|
+
*
|
|
238
|
+
* Naming note — this is a scope-tied *declaration*, not a mutation: "while
|
|
239
|
+
* this reactive scope is live, the response has this header." Solid
|
|
240
|
+
* reserves `set*` verbs for event-time mutation; like
|
|
241
|
+
* `createSignal`/`onCleanup` this is called in scope bodies and un-declares
|
|
242
|
+
* on scope disposal.
|
|
243
|
+
*
|
|
244
|
+
* Retraction semantics (server): the header's prior value is snapshotted at
|
|
245
|
+
* write time and restored when the owning scope is disposed (deleted if
|
|
246
|
+
* there was none) — a boundary that errors or recovers retracts its writes.
|
|
247
|
+
* Once the integration marks the response head `committed` (head
|
|
248
|
+
* derived/sent), writes and retractions are no-ops.
|
|
249
|
+
*/
|
|
250
|
+
export declare function httpHeader(_name: string, _value: string, _options?: {
|
|
251
|
+
append?: boolean;
|
|
252
|
+
}): void;
|
|
@@ -2,21 +2,29 @@ import { JSONCodecOptions } from "../serializer.js";
|
|
|
2
2
|
import { ServerFunction, ServerFunctionMetadata } from "./shared.js";
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
|
+
ChunkReader,
|
|
5
6
|
ERROR_HEADER,
|
|
6
7
|
FLASH_COOKIE,
|
|
7
8
|
FUNCTION_HEADER,
|
|
8
9
|
INSTANCE_HEADER,
|
|
9
10
|
SINGLE_FLIGHT_HEADER,
|
|
10
11
|
clearFlashCookie,
|
|
12
|
+
createChunk,
|
|
11
13
|
decodeErrorHeaderValue,
|
|
12
14
|
decodeResponse,
|
|
15
|
+
decodeResponsePayload,
|
|
16
|
+
deserializeStream,
|
|
13
17
|
encodeErrorHeaderValue,
|
|
18
|
+
frameAddress,
|
|
19
|
+
getFlightDataConsumer,
|
|
14
20
|
getServerFunctionMetadata,
|
|
21
|
+
getServerFunctionsCodec,
|
|
15
22
|
hasFlashCookie,
|
|
16
23
|
isServerFunction,
|
|
17
24
|
subscribeFlightData,
|
|
18
25
|
withMeta
|
|
19
26
|
} from "./shared.js";
|
|
27
|
+
export { REVALIDATE_HEADER } from "../response.js";
|
|
20
28
|
export type {
|
|
21
29
|
FlightDataConsumer,
|
|
22
30
|
FlightDataContext,
|
|
@@ -173,3 +181,20 @@ export function createServerReference(id: string, name?: string, base?: string):
|
|
|
173
181
|
* @internal
|
|
174
182
|
*/
|
|
175
183
|
export function registerServerReference(): never;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Identity of the currently executing server function call — see the
|
|
187
|
+
* server entry. Named here so isomorphic code can import the type from
|
|
188
|
+
* either entry.
|
|
189
|
+
*/
|
|
190
|
+
export interface ServerFunctionInvocation {
|
|
191
|
+
id: string;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Client no-op mirror of the server entry's accessor: there is never a
|
|
196
|
+
* server function call in flight on the client, so this always returns
|
|
197
|
+
* undefined. Present so `"use server"` modules that import it stay
|
|
198
|
+
* import-stable in client builds before dead-code elimination.
|
|
199
|
+
*/
|
|
200
|
+
export function getServerFunctionInvocation(): ServerFunctionInvocation | undefined;
|