@trpc/next 11.0.0-next.91 → 11.0.0-rc.329
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/README.md +5 -5
- package/dist/app-dir/client.d.ts +3 -3
- package/dist/app-dir/client.d.ts.map +1 -1
- package/dist/app-dir/client.js +5 -105
- package/dist/app-dir/client.mjs +4 -102
- package/dist/app-dir/create-action-hook.d.ts +18 -6
- package/dist/app-dir/create-action-hook.d.ts.map +1 -1
- package/dist/app-dir/create-action-hook.js +108 -0
- package/dist/app-dir/create-action-hook.mjs +105 -0
- package/dist/app-dir/formDataToObject.js +34 -0
- package/dist/app-dir/formDataToObject.mjs +32 -0
- package/dist/app-dir/links/nextCache.d.ts +4 -3
- package/dist/app-dir/links/nextCache.d.ts.map +1 -1
- package/dist/app-dir/links/nextCache.js +9 -10
- package/dist/app-dir/links/nextCache.mjs +8 -7
- package/dist/app-dir/links/nextHttp.d.ts +11 -5
- package/dist/app-dir/links/nextHttp.d.ts.map +1 -1
- package/dist/app-dir/links/nextHttp.js +22 -23
- package/dist/app-dir/links/nextHttp.mjs +22 -21
- package/dist/app-dir/server.d.ts +19 -12
- package/dist/app-dir/server.d.ts.map +1 -1
- package/dist/app-dir/server.js +39 -55
- package/dist/app-dir/server.mjs +29 -43
- package/dist/app-dir/shared.d.ts +19 -13
- package/dist/app-dir/shared.d.ts.map +1 -1
- package/dist/{shared-e49b9cdc.js → app-dir/shared.js} +1 -1
- package/dist/{shared-f6996341.mjs → app-dir/shared.mjs} +2 -2
- package/dist/app-dir/types.d.ts +23 -11
- package/dist/app-dir/types.d.ts.map +1 -1
- package/dist/bundle-analysis.json +56 -44
- package/dist/createTRPCNext.d.ts +10 -8
- package/dist/createTRPCNext.d.ts.map +1 -1
- package/dist/createTRPCNext.js +38 -0
- package/dist/createTRPCNext.mjs +36 -0
- package/dist/index.js +4 -190
- package/dist/index.mjs +2 -185
- package/dist/ssrPrepass.d.ts +3 -0
- package/dist/ssrPrepass.d.ts.map +1 -0
- package/dist/ssrPrepass.js +139 -0
- package/dist/ssrPrepass.mjs +137 -0
- package/dist/withTRPC.d.ts +41 -13
- package/dist/withTRPC.d.ts.map +1 -1
- package/dist/withTRPC.js +86 -0
- package/dist/withTRPC.mjs +84 -0
- package/package.json +36 -25
- package/src/app-dir/client.ts +4 -4
- package/src/app-dir/create-action-hook.tsx +49 -19
- package/src/app-dir/links/nextCache.ts +20 -8
- package/src/app-dir/links/nextHttp.ts +50 -30
- package/src/app-dir/server.ts +86 -34
- package/src/app-dir/shared.ts +52 -25
- package/src/app-dir/types.ts +41 -29
- package/src/createTRPCNext.tsx +25 -16
- package/src/ssrPrepass.ts +185 -0
- package/src/withTRPC.tsx +102 -180
- package/ssrPrepass/index.d.ts +1 -0
- package/ssrPrepass/index.js +1 -0
- package/dist/shared-642894f4.js +0 -19
package/dist/index.mjs
CHANGED
|
@@ -1,185 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { createRootHooks, getQueryClient, createReactQueryUtils, createReactDecoration } from '@trpc/react-query/shared';
|
|
4
|
-
import React, { createElement, useState, useMemo } from 'react';
|
|
5
|
-
import ssrPrepass from 'react-ssr-prepass';
|
|
6
|
-
import { createFlatProxy } from '@trpc/server/shared';
|
|
7
|
-
|
|
8
|
-
function transformQueryOrMutationCacheErrors(result) {
|
|
9
|
-
const error = result.state.error;
|
|
10
|
-
if (error instanceof Error && error.name === 'TRPCClientError') {
|
|
11
|
-
const newError = {
|
|
12
|
-
message: error.message,
|
|
13
|
-
data: error.data,
|
|
14
|
-
shape: error.shape
|
|
15
|
-
};
|
|
16
|
-
return {
|
|
17
|
-
...result,
|
|
18
|
-
state: {
|
|
19
|
-
...result.state,
|
|
20
|
-
error: newError
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
return result;
|
|
25
|
-
}
|
|
26
|
-
function withTRPC(opts) {
|
|
27
|
-
const { config: getClientConfig } = opts;
|
|
28
|
-
return (AppOrPage)=>{
|
|
29
|
-
const trpc = createRootHooks(opts);
|
|
30
|
-
const WithTRPC = (props)=>{
|
|
31
|
-
const [prepassProps] = useState(()=>{
|
|
32
|
-
if (props.trpc) {
|
|
33
|
-
return props.trpc;
|
|
34
|
-
}
|
|
35
|
-
const config = getClientConfig({});
|
|
36
|
-
const queryClient = getQueryClient(config);
|
|
37
|
-
const trpcClient = trpc.createClient(config);
|
|
38
|
-
return {
|
|
39
|
-
abortOnUnmount: config.abortOnUnmount,
|
|
40
|
-
queryClient,
|
|
41
|
-
trpcClient,
|
|
42
|
-
ssrState: opts.ssr ? 'mounting' : false,
|
|
43
|
-
ssrContext: null
|
|
44
|
-
};
|
|
45
|
-
});
|
|
46
|
-
const { queryClient , trpcClient , ssrState , ssrContext } = prepassProps;
|
|
47
|
-
// allow normal components to be wrapped, not just app/pages
|
|
48
|
-
const hydratedState = trpc.useDehydratedState(trpcClient, props.pageProps?.trpcState);
|
|
49
|
-
return /*#__PURE__*/ React.createElement(trpc.Provider, {
|
|
50
|
-
abortOnUnmount: prepassProps.abortOnUnmount ?? false,
|
|
51
|
-
client: trpcClient,
|
|
52
|
-
queryClient: queryClient,
|
|
53
|
-
ssrState: ssrState,
|
|
54
|
-
ssrContext: ssrContext
|
|
55
|
-
}, /*#__PURE__*/ React.createElement(QueryClientProvider, {
|
|
56
|
-
client: queryClient
|
|
57
|
-
}, /*#__PURE__*/ React.createElement(HydrationBoundary, {
|
|
58
|
-
state: hydratedState
|
|
59
|
-
}, /*#__PURE__*/ React.createElement(AppOrPage, Object.assign({}, props)))));
|
|
60
|
-
};
|
|
61
|
-
if (AppOrPage.getInitialProps ?? opts.ssr) {
|
|
62
|
-
WithTRPC.getInitialProps = async (appOrPageCtx)=>{
|
|
63
|
-
const AppTree = appOrPageCtx.AppTree;
|
|
64
|
-
// Determine if we are wrapping an App component or a Page component.
|
|
65
|
-
const isApp = !!appOrPageCtx.Component;
|
|
66
|
-
const ctx = isApp ? appOrPageCtx.ctx : appOrPageCtx;
|
|
67
|
-
// Run the wrapped component's getInitialProps function.
|
|
68
|
-
let pageProps = {};
|
|
69
|
-
if (AppOrPage.getInitialProps) {
|
|
70
|
-
const originalProps = await AppOrPage.getInitialProps(appOrPageCtx);
|
|
71
|
-
const originalPageProps = isApp ? originalProps.pageProps ?? {} : originalProps;
|
|
72
|
-
pageProps = {
|
|
73
|
-
...originalPageProps,
|
|
74
|
-
...pageProps
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
const getAppTreeProps = (props)=>isApp ? {
|
|
78
|
-
pageProps: props
|
|
79
|
-
} : props;
|
|
80
|
-
if (typeof window !== 'undefined' || !opts.ssr) {
|
|
81
|
-
return getAppTreeProps(pageProps);
|
|
82
|
-
}
|
|
83
|
-
const config = getClientConfig({
|
|
84
|
-
ctx
|
|
85
|
-
});
|
|
86
|
-
const trpcClient = createTRPCUntypedClient(config);
|
|
87
|
-
const queryClient = getQueryClient(config);
|
|
88
|
-
const trpcProp = {
|
|
89
|
-
config,
|
|
90
|
-
trpcClient,
|
|
91
|
-
queryClient,
|
|
92
|
-
ssrState: 'prepass',
|
|
93
|
-
ssrContext: ctx
|
|
94
|
-
};
|
|
95
|
-
const prepassProps = {
|
|
96
|
-
pageProps,
|
|
97
|
-
trpc: trpcProp
|
|
98
|
-
};
|
|
99
|
-
// Run the prepass step on AppTree. This will run all trpc queries on the server.
|
|
100
|
-
// multiple prepass ensures that we can do batching on the server
|
|
101
|
-
while(true){
|
|
102
|
-
// render full tree
|
|
103
|
-
await ssrPrepass(/*#__PURE__*/ createElement(AppTree, prepassProps));
|
|
104
|
-
if (!queryClient.isFetching()) {
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
// wait until the query cache has settled it's promises
|
|
108
|
-
await new Promise((resolve)=>{
|
|
109
|
-
const unsub = queryClient.getQueryCache().subscribe((event)=>{
|
|
110
|
-
if (event?.query.getObserversCount() === 0) {
|
|
111
|
-
resolve();
|
|
112
|
-
unsub();
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
const dehydratedCache = dehydrate(queryClient, {
|
|
118
|
-
shouldDehydrateQuery () {
|
|
119
|
-
// makes sure errors are also dehydrated
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
// since error instances can't be serialized, let's make them into `TRPCClientErrorLike`-objects
|
|
124
|
-
const dehydratedCacheWithErrors = {
|
|
125
|
-
...dehydratedCache,
|
|
126
|
-
queries: dehydratedCache.queries.map(transformQueryOrMutationCacheErrors),
|
|
127
|
-
mutations: dehydratedCache.mutations.map(transformQueryOrMutationCacheErrors)
|
|
128
|
-
};
|
|
129
|
-
// dehydrate query client's state and add it to the props
|
|
130
|
-
pageProps.trpcState = trpcClient.runtime.combinedTransformer.output.serialize(dehydratedCacheWithErrors);
|
|
131
|
-
const appTreeProps = getAppTreeProps(pageProps);
|
|
132
|
-
const meta = opts.responseMeta?.({
|
|
133
|
-
ctx,
|
|
134
|
-
clientErrors: [
|
|
135
|
-
...dehydratedCache.queries,
|
|
136
|
-
...dehydratedCache.mutations
|
|
137
|
-
].map((v)=>v.state.error).flatMap((err)=>err instanceof Error && err.name === 'TRPCClientError' ? [
|
|
138
|
-
err
|
|
139
|
-
] : [])
|
|
140
|
-
}) ?? {};
|
|
141
|
-
for (const [key, value] of Object.entries(meta.headers ?? {})){
|
|
142
|
-
if (typeof value === 'string') {
|
|
143
|
-
ctx.res?.setHeader(key, value);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (meta.status && ctx.res) {
|
|
147
|
-
ctx.res.statusCode = meta.status;
|
|
148
|
-
}
|
|
149
|
-
return appTreeProps;
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
const displayName = AppOrPage.displayName ?? AppOrPage.name ?? 'Component';
|
|
153
|
-
WithTRPC.displayName = `withTRPC(${displayName})`;
|
|
154
|
-
return WithTRPC;
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/* istanbul ignore file -- @preserve */ // We're testing this through E2E-testing
|
|
159
|
-
function createTRPCNext(opts) {
|
|
160
|
-
const hooks = createRootHooks(opts);
|
|
161
|
-
// TODO: maybe set TSSRContext to `never` when using `WithTRPCNoSSROptions`
|
|
162
|
-
const _withTRPC = withTRPC(opts);
|
|
163
|
-
return createFlatProxy((key)=>{
|
|
164
|
-
if (key === 'useContext' || key === 'useUtils') {
|
|
165
|
-
return ()=>{
|
|
166
|
-
const context = hooks.useUtils();
|
|
167
|
-
// create a stable reference of the utils context
|
|
168
|
-
return useMemo(()=>{
|
|
169
|
-
return createReactQueryUtils(context);
|
|
170
|
-
}, [
|
|
171
|
-
context
|
|
172
|
-
]);
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
if (key === 'useQueries') {
|
|
176
|
-
return hooks.useQueries;
|
|
177
|
-
}
|
|
178
|
-
if (key === 'withTRPC') {
|
|
179
|
-
return _withTRPC;
|
|
180
|
-
}
|
|
181
|
-
return createReactDecoration(key, hooks);
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
export { createTRPCNext, withTRPC };
|
|
1
|
+
export { withTRPC } from './withTRPC.mjs';
|
|
2
|
+
export { createTRPCNext } from './createTRPCNext.mjs';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssrPrepass.d.ts","sourceRoot":"","sources":["../src/ssrPrepass.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,iBAAiB,EAAoB,MAAM,YAAY,CAAC;AAyBtE,eAAO,MAAM,UAAU,EAAE,iBA0IxB,CAAC"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactQuery = require('@tanstack/react-query');
|
|
4
|
+
var client = require('@trpc/client');
|
|
5
|
+
var unstableInternals = require('@trpc/client/unstable-internals');
|
|
6
|
+
var shared = require('@trpc/react-query/shared');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
|
|
9
|
+
function transformQueryOrMutationCacheErrors(result) {
|
|
10
|
+
const error = result.state.error;
|
|
11
|
+
if (error instanceof Error && error.name === 'TRPCClientError') {
|
|
12
|
+
const newError = {
|
|
13
|
+
message: error.message,
|
|
14
|
+
data: error.data,
|
|
15
|
+
shape: error.shape
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
...result,
|
|
19
|
+
state: {
|
|
20
|
+
...result.state,
|
|
21
|
+
error: newError
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
const ssrPrepass = (opts)=>{
|
|
28
|
+
const { parent , WithTRPC , AppOrPage } = opts;
|
|
29
|
+
const transformer = unstableInternals.getTransformer(parent.transformer);
|
|
30
|
+
WithTRPC.getInitialProps = async (appOrPageCtx)=>{
|
|
31
|
+
const shouldSsr = async ()=>{
|
|
32
|
+
if (typeof window !== 'undefined') {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
if (typeof parent.ssr === 'function') {
|
|
36
|
+
try {
|
|
37
|
+
return await parent.ssr({
|
|
38
|
+
ctx: appOrPageCtx.ctx
|
|
39
|
+
});
|
|
40
|
+
} catch (e) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return parent.ssr;
|
|
45
|
+
};
|
|
46
|
+
const ssrEnabled = await shouldSsr();
|
|
47
|
+
const AppTree = appOrPageCtx.AppTree;
|
|
48
|
+
// Determine if we are wrapping an App component or a Page component.
|
|
49
|
+
const isApp = !!appOrPageCtx.Component;
|
|
50
|
+
const ctx = isApp ? appOrPageCtx.ctx : appOrPageCtx;
|
|
51
|
+
// Run the wrapped component's getInitialProps function.
|
|
52
|
+
let pageProps = {};
|
|
53
|
+
if (AppOrPage.getInitialProps) {
|
|
54
|
+
const originalProps = await AppOrPage.getInitialProps(appOrPageCtx);
|
|
55
|
+
const originalPageProps = isApp ? originalProps.pageProps ?? {} : originalProps;
|
|
56
|
+
pageProps = {
|
|
57
|
+
...originalPageProps,
|
|
58
|
+
...pageProps
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const getAppTreeProps = (props)=>isApp ? {
|
|
62
|
+
pageProps: props
|
|
63
|
+
} : props;
|
|
64
|
+
if (typeof window !== 'undefined' || !ssrEnabled) {
|
|
65
|
+
return getAppTreeProps(pageProps);
|
|
66
|
+
}
|
|
67
|
+
const config = parent.config({
|
|
68
|
+
ctx
|
|
69
|
+
});
|
|
70
|
+
const trpcClient = client.createTRPCUntypedClient(config);
|
|
71
|
+
const queryClient = shared.getQueryClient(config);
|
|
72
|
+
const trpcProp = {
|
|
73
|
+
config,
|
|
74
|
+
trpcClient,
|
|
75
|
+
queryClient,
|
|
76
|
+
ssrState: 'prepass',
|
|
77
|
+
ssrContext: ctx
|
|
78
|
+
};
|
|
79
|
+
const prepassProps = {
|
|
80
|
+
pageProps,
|
|
81
|
+
trpc: trpcProp
|
|
82
|
+
};
|
|
83
|
+
const reactDomServer = await import('react-dom/server');
|
|
84
|
+
// Run the prepass step on AppTree. This will run all trpc queries on the server.
|
|
85
|
+
// multiple prepass ensures that we can do batching on the server
|
|
86
|
+
while(true){
|
|
87
|
+
// render full tree
|
|
88
|
+
reactDomServer.renderToString(React.createElement(AppTree, prepassProps));
|
|
89
|
+
if (!queryClient.isFetching()) {
|
|
90
|
+
break;
|
|
91
|
+
}
|
|
92
|
+
// wait until the query cache has settled it's promises
|
|
93
|
+
await new Promise((resolve)=>{
|
|
94
|
+
const unsub = queryClient.getQueryCache().subscribe((event)=>{
|
|
95
|
+
if (event?.query.getObserversCount() === 0) {
|
|
96
|
+
resolve();
|
|
97
|
+
unsub();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const dehydratedCache = reactQuery.dehydrate(queryClient, {
|
|
103
|
+
shouldDehydrateQuery (query) {
|
|
104
|
+
// filter out queries that are marked as trpc: { ssr: false } or are not enabled, but make sure errors are dehydrated
|
|
105
|
+
const isExcludedFromSSr = query.state.fetchStatus === 'idle' && query.state.status === 'pending';
|
|
106
|
+
return !isExcludedFromSSr;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
// since error instances can't be serialized, let's make them into `TRPCClientErrorLike`-objects
|
|
110
|
+
const dehydratedCacheWithErrors = {
|
|
111
|
+
...dehydratedCache,
|
|
112
|
+
queries: dehydratedCache.queries.map(transformQueryOrMutationCacheErrors),
|
|
113
|
+
mutations: dehydratedCache.mutations.map(transformQueryOrMutationCacheErrors)
|
|
114
|
+
};
|
|
115
|
+
// dehydrate query client's state and add it to the props
|
|
116
|
+
pageProps['trpcState'] = transformer.input.serialize(dehydratedCacheWithErrors);
|
|
117
|
+
const appTreeProps = getAppTreeProps(pageProps);
|
|
118
|
+
const meta = parent.responseMeta?.({
|
|
119
|
+
ctx,
|
|
120
|
+
clientErrors: [
|
|
121
|
+
...dehydratedCache.queries,
|
|
122
|
+
...dehydratedCache.mutations
|
|
123
|
+
].map((v)=>v.state.error).flatMap((err)=>err instanceof Error && err.name === 'TRPCClientError' ? [
|
|
124
|
+
err
|
|
125
|
+
] : [])
|
|
126
|
+
}) ?? {};
|
|
127
|
+
for (const [key, value] of Object.entries(meta.headers ?? {})){
|
|
128
|
+
if (typeof value === 'string') {
|
|
129
|
+
ctx.res?.setHeader(key, value);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (meta.status && ctx.res) {
|
|
133
|
+
ctx.res.statusCode = meta.status;
|
|
134
|
+
}
|
|
135
|
+
return appTreeProps;
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
exports.ssrPrepass = ssrPrepass;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { dehydrate } from '@tanstack/react-query';
|
|
2
|
+
import { createTRPCUntypedClient } from '@trpc/client';
|
|
3
|
+
import { getTransformer } from '@trpc/client/unstable-internals';
|
|
4
|
+
import { getQueryClient } from '@trpc/react-query/shared';
|
|
5
|
+
import { createElement } from 'react';
|
|
6
|
+
|
|
7
|
+
function transformQueryOrMutationCacheErrors(result) {
|
|
8
|
+
const error = result.state.error;
|
|
9
|
+
if (error instanceof Error && error.name === 'TRPCClientError') {
|
|
10
|
+
const newError = {
|
|
11
|
+
message: error.message,
|
|
12
|
+
data: error.data,
|
|
13
|
+
shape: error.shape
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
...result,
|
|
17
|
+
state: {
|
|
18
|
+
...result.state,
|
|
19
|
+
error: newError
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
const ssrPrepass = (opts)=>{
|
|
26
|
+
const { parent , WithTRPC , AppOrPage } = opts;
|
|
27
|
+
const transformer = getTransformer(parent.transformer);
|
|
28
|
+
WithTRPC.getInitialProps = async (appOrPageCtx)=>{
|
|
29
|
+
const shouldSsr = async ()=>{
|
|
30
|
+
if (typeof window !== 'undefined') {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (typeof parent.ssr === 'function') {
|
|
34
|
+
try {
|
|
35
|
+
return await parent.ssr({
|
|
36
|
+
ctx: appOrPageCtx.ctx
|
|
37
|
+
});
|
|
38
|
+
} catch (e) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return parent.ssr;
|
|
43
|
+
};
|
|
44
|
+
const ssrEnabled = await shouldSsr();
|
|
45
|
+
const AppTree = appOrPageCtx.AppTree;
|
|
46
|
+
// Determine if we are wrapping an App component or a Page component.
|
|
47
|
+
const isApp = !!appOrPageCtx.Component;
|
|
48
|
+
const ctx = isApp ? appOrPageCtx.ctx : appOrPageCtx;
|
|
49
|
+
// Run the wrapped component's getInitialProps function.
|
|
50
|
+
let pageProps = {};
|
|
51
|
+
if (AppOrPage.getInitialProps) {
|
|
52
|
+
const originalProps = await AppOrPage.getInitialProps(appOrPageCtx);
|
|
53
|
+
const originalPageProps = isApp ? originalProps.pageProps ?? {} : originalProps;
|
|
54
|
+
pageProps = {
|
|
55
|
+
...originalPageProps,
|
|
56
|
+
...pageProps
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const getAppTreeProps = (props)=>isApp ? {
|
|
60
|
+
pageProps: props
|
|
61
|
+
} : props;
|
|
62
|
+
if (typeof window !== 'undefined' || !ssrEnabled) {
|
|
63
|
+
return getAppTreeProps(pageProps);
|
|
64
|
+
}
|
|
65
|
+
const config = parent.config({
|
|
66
|
+
ctx
|
|
67
|
+
});
|
|
68
|
+
const trpcClient = createTRPCUntypedClient(config);
|
|
69
|
+
const queryClient = getQueryClient(config);
|
|
70
|
+
const trpcProp = {
|
|
71
|
+
config,
|
|
72
|
+
trpcClient,
|
|
73
|
+
queryClient,
|
|
74
|
+
ssrState: 'prepass',
|
|
75
|
+
ssrContext: ctx
|
|
76
|
+
};
|
|
77
|
+
const prepassProps = {
|
|
78
|
+
pageProps,
|
|
79
|
+
trpc: trpcProp
|
|
80
|
+
};
|
|
81
|
+
const reactDomServer = await import('react-dom/server');
|
|
82
|
+
// Run the prepass step on AppTree. This will run all trpc queries on the server.
|
|
83
|
+
// multiple prepass ensures that we can do batching on the server
|
|
84
|
+
while(true){
|
|
85
|
+
// render full tree
|
|
86
|
+
reactDomServer.renderToString(createElement(AppTree, prepassProps));
|
|
87
|
+
if (!queryClient.isFetching()) {
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
// wait until the query cache has settled it's promises
|
|
91
|
+
await new Promise((resolve)=>{
|
|
92
|
+
const unsub = queryClient.getQueryCache().subscribe((event)=>{
|
|
93
|
+
if (event?.query.getObserversCount() === 0) {
|
|
94
|
+
resolve();
|
|
95
|
+
unsub();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
const dehydratedCache = dehydrate(queryClient, {
|
|
101
|
+
shouldDehydrateQuery (query) {
|
|
102
|
+
// filter out queries that are marked as trpc: { ssr: false } or are not enabled, but make sure errors are dehydrated
|
|
103
|
+
const isExcludedFromSSr = query.state.fetchStatus === 'idle' && query.state.status === 'pending';
|
|
104
|
+
return !isExcludedFromSSr;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
// since error instances can't be serialized, let's make them into `TRPCClientErrorLike`-objects
|
|
108
|
+
const dehydratedCacheWithErrors = {
|
|
109
|
+
...dehydratedCache,
|
|
110
|
+
queries: dehydratedCache.queries.map(transformQueryOrMutationCacheErrors),
|
|
111
|
+
mutations: dehydratedCache.mutations.map(transformQueryOrMutationCacheErrors)
|
|
112
|
+
};
|
|
113
|
+
// dehydrate query client's state and add it to the props
|
|
114
|
+
pageProps['trpcState'] = transformer.input.serialize(dehydratedCacheWithErrors);
|
|
115
|
+
const appTreeProps = getAppTreeProps(pageProps);
|
|
116
|
+
const meta = parent.responseMeta?.({
|
|
117
|
+
ctx,
|
|
118
|
+
clientErrors: [
|
|
119
|
+
...dehydratedCache.queries,
|
|
120
|
+
...dehydratedCache.mutations
|
|
121
|
+
].map((v)=>v.state.error).flatMap((err)=>err instanceof Error && err.name === 'TRPCClientError' ? [
|
|
122
|
+
err
|
|
123
|
+
] : [])
|
|
124
|
+
}) ?? {};
|
|
125
|
+
for (const [key, value] of Object.entries(meta.headers ?? {})){
|
|
126
|
+
if (typeof value === 'string') {
|
|
127
|
+
ctx.res?.setHeader(key, value);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (meta.status && ctx.res) {
|
|
131
|
+
ctx.res.statusCode = meta.status;
|
|
132
|
+
}
|
|
133
|
+
return appTreeProps;
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
export { ssrPrepass };
|
package/dist/withTRPC.d.ts
CHANGED
|
@@ -1,27 +1,55 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
import {
|
|
1
|
+
/**
|
|
2
|
+
* Heavily based on urql's ssr
|
|
3
|
+
* https://github.com/FormidableLabs/urql/blob/main/packages/next-urql/src/with-urql-client.ts
|
|
4
|
+
*/
|
|
5
|
+
import type { QueryClient } from '@tanstack/react-query';
|
|
6
|
+
import type { CreateTRPCClientOptions, TRPCUntypedClient } from '@trpc/client';
|
|
7
|
+
import { type TransformerOptions } from '@trpc/client/unstable-internals';
|
|
8
|
+
import type { TRPCClientError } from '@trpc/react-query';
|
|
9
|
+
import type { CreateTRPCReactOptions, CreateTRPCReactQueryClientConfig } from '@trpc/react-query/shared';
|
|
10
|
+
import type { AnyRouter, inferClientTypes, ResponseMeta } from '@trpc/server/unstable-core-do-not-import';
|
|
11
|
+
import type { NextComponentType, NextPageContext } from 'next/dist/shared/lib/utils';
|
|
7
12
|
export type WithTRPCConfig<TRouter extends AnyRouter> = CreateTRPCClientOptions<TRouter> & CreateTRPCReactQueryClientConfig & {
|
|
8
13
|
abortOnUnmount?: boolean;
|
|
9
14
|
};
|
|
10
|
-
|
|
15
|
+
type WithTRPCOptions<TRouter extends AnyRouter> = CreateTRPCReactOptions<TRouter> & {
|
|
11
16
|
config: (info: {
|
|
12
17
|
ctx?: NextPageContext;
|
|
13
18
|
}) => WithTRPCConfig<TRouter>;
|
|
14
|
-
}
|
|
15
|
-
export
|
|
16
|
-
|
|
19
|
+
} & TransformerOptions<inferClientTypes<TRouter>>;
|
|
20
|
+
export type TRPCPrepassHelper = (opts: {
|
|
21
|
+
parent: WithTRPCSSROptions<AnyRouter>;
|
|
22
|
+
WithTRPC: NextComponentType<any, any, any>;
|
|
23
|
+
AppOrPage: NextComponentType<any, any, any>;
|
|
24
|
+
}) => void;
|
|
25
|
+
export type WithTRPCSSROptions<TRouter extends AnyRouter> = WithTRPCOptions<TRouter> & {
|
|
26
|
+
/**
|
|
27
|
+
* If you enable this, you also need to add a `ssrPrepass`-prop
|
|
28
|
+
* @link https://trpc.io/docs/client/nextjs/ssr
|
|
29
|
+
*/
|
|
30
|
+
ssr: true | ((opts: {
|
|
31
|
+
ctx: NextPageContext;
|
|
32
|
+
}) => boolean | Promise<boolean>);
|
|
17
33
|
responseMeta?: (opts: {
|
|
18
34
|
ctx: NextPageContext;
|
|
19
35
|
clientErrors: TRPCClientError<TRouter>[];
|
|
20
36
|
}) => ResponseMeta;
|
|
21
|
-
|
|
22
|
-
|
|
37
|
+
/**
|
|
38
|
+
* use `import { ssrPrepass } from '@trpc/next/ssrPrepass'`
|
|
39
|
+
* @link https://trpc.io/docs/client/nextjs/ssr
|
|
40
|
+
*/
|
|
41
|
+
ssrPrepass: TRPCPrepassHelper;
|
|
42
|
+
};
|
|
43
|
+
export type WithTRPCNoSSROptions<TRouter extends AnyRouter> = WithTRPCOptions<TRouter> & {
|
|
23
44
|
ssr?: false;
|
|
24
|
-
}
|
|
45
|
+
};
|
|
46
|
+
export type TRPCPrepassProps<TRouter extends AnyRouter, TSSRContext extends NextPageContext = NextPageContext> = {
|
|
47
|
+
config: WithTRPCConfig<TRouter>;
|
|
48
|
+
queryClient: QueryClient;
|
|
49
|
+
trpcClient: TRPCUntypedClient<TRouter>;
|
|
50
|
+
ssrState: 'prepass';
|
|
51
|
+
ssrContext: TSSRContext;
|
|
52
|
+
};
|
|
25
53
|
export declare function withTRPC<TRouter extends AnyRouter, TSSRContext extends NextPageContext = NextPageContext>(opts: WithTRPCNoSSROptions<TRouter> | WithTRPCSSROptions<TRouter>): (AppOrPage: NextComponentType<any, any, any>) => NextComponentType;
|
|
26
54
|
export {};
|
|
27
55
|
//# sourceMappingURL=withTRPC.d.ts.map
|
package/dist/withTRPC.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"withTRPC.d.ts","sourceRoot":"","sources":["../src/withTRPC.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"withTRPC.d.ts","sourceRoot":"","sources":["../src/withTRPC.tsx"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAmB,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAE1E,OAAO,KAAK,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAE/E,OAAO,EAEL,KAAK,kBAAkB,EACxB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EACV,sBAAsB,EACtB,gCAAgC,EACjC,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EACV,SAAS,EAET,gBAAgB,EAChB,YAAY,EACb,MAAM,0CAA0C,CAAC;AAClD,OAAO,KAAK,EAGV,iBAAiB,EACjB,eAAe,EAChB,MAAM,4BAA4B,CAAC;AAIpC,MAAM,MAAM,cAAc,CAAC,OAAO,SAAS,SAAS,IAClD,uBAAuB,CAAC,OAAO,CAAC,GAC9B,gCAAgC,GAAG;IACjC,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEN,KAAK,eAAe,CAAC,OAAO,SAAS,SAAS,IAC5C,sBAAsB,CAAC,OAAO,CAAC,GAAG;IAChC,MAAM,EAAE,CAAC,IAAI,EAAE;QAAE,GAAG,CAAC,EAAE,eAAe,CAAA;KAAE,KAAK,cAAc,CAAC,OAAO,CAAC,CAAC;CACtE,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;AAEpD,MAAM,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE;IACrC,MAAM,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;IACtC,QAAQ,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;IAC3C,SAAS,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;CAC7C,KAAK,IAAI,CAAC;AACX,MAAM,MAAM,kBAAkB,CAAC,OAAO,SAAS,SAAS,IACtD,eAAe,CAAC,OAAO,CAAC,GAAG;IACzB;;;OAGG;IACH,GAAG,EACC,IAAI,GACJ,CAAC,CAAC,IAAI,EAAE;QAAE,GAAG,EAAE,eAAe,CAAA;KAAE,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IACrE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE;QACpB,GAAG,EAAE,eAAe,CAAC;QACrB,YAAY,EAAE,eAAe,CAAC,OAAO,CAAC,EAAE,CAAC;KAC1C,KAAK,YAAY,CAAC;IACnB;;;OAGG;IACH,UAAU,EAAE,iBAAiB,CAAC;CAC/B,CAAC;AAEJ,MAAM,MAAM,oBAAoB,CAAC,OAAO,SAAS,SAAS,IACxD,eAAe,CAAC,OAAO,CAAC,GAAG;IACzB,GAAG,CAAC,EAAE,KAAK,CAAC;CACb,CAAC;AAEJ,MAAM,MAAM,gBAAgB,CAC1B,OAAO,SAAS,SAAS,EACzB,WAAW,SAAS,eAAe,GAAG,eAAe,IACnD;IACF,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC;IAChC,WAAW,EAAE,WAAW,CAAC;IACzB,UAAU,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;IACvC,QAAQ,EAAE,SAAS,CAAC;IACpB,UAAU,EAAE,WAAW,CAAC;CACzB,CAAC;AAEF,wBAAgB,QAAQ,CACtB,OAAO,SAAS,SAAS,EACzB,WAAW,SAAS,eAAe,GAAG,eAAe,EACrD,IAAI,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,eAO9C,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,KAAG,iBAAiB,CAgGxE"}
|
package/dist/withTRPC.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var reactQuery = require('@tanstack/react-query');
|
|
4
|
+
var unstableInternals = require('@trpc/client/unstable-internals');
|
|
5
|
+
var shared = require('@trpc/react-query/shared');
|
|
6
|
+
var React = require('react');
|
|
7
|
+
|
|
8
|
+
function withTRPC(opts) {
|
|
9
|
+
const { config: getClientConfig } = opts;
|
|
10
|
+
const transformer = unstableInternals.getTransformer(opts.transformer);
|
|
11
|
+
return (AppOrPage)=>{
|
|
12
|
+
const trpc = shared.createRootHooks(opts);
|
|
13
|
+
const WithTRPC = (props)=>{
|
|
14
|
+
const [prepassProps] = React.useState(()=>{
|
|
15
|
+
if (props.trpc) {
|
|
16
|
+
return props.trpc;
|
|
17
|
+
}
|
|
18
|
+
const config = getClientConfig({});
|
|
19
|
+
const queryClient = shared.getQueryClient(config);
|
|
20
|
+
const trpcClient = trpc.createClient(config);
|
|
21
|
+
return {
|
|
22
|
+
abortOnUnmount: config.abortOnUnmount,
|
|
23
|
+
queryClient,
|
|
24
|
+
trpcClient,
|
|
25
|
+
ssrState: opts.ssr ? 'mounting' : false,
|
|
26
|
+
ssrContext: null
|
|
27
|
+
};
|
|
28
|
+
});
|
|
29
|
+
const { queryClient , trpcClient , ssrState , ssrContext } = prepassProps;
|
|
30
|
+
// allow normal components to be wrapped, not just app/pages
|
|
31
|
+
const trpcState = props.pageProps?.trpcState;
|
|
32
|
+
const hydratedState = React.useMemo(()=>{
|
|
33
|
+
if (!trpcState) {
|
|
34
|
+
return trpcState;
|
|
35
|
+
}
|
|
36
|
+
return transformer.input.deserialize(trpcState);
|
|
37
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
38
|
+
}, [
|
|
39
|
+
trpcState,
|
|
40
|
+
trpcClient
|
|
41
|
+
]);
|
|
42
|
+
return /*#__PURE__*/ React.createElement(trpc.Provider, {
|
|
43
|
+
abortOnUnmount: prepassProps.abortOnUnmount ?? false,
|
|
44
|
+
client: trpcClient,
|
|
45
|
+
queryClient: queryClient,
|
|
46
|
+
ssrState: ssrState,
|
|
47
|
+
ssrContext: ssrContext
|
|
48
|
+
}, /*#__PURE__*/ React.createElement(reactQuery.QueryClientProvider, {
|
|
49
|
+
client: queryClient
|
|
50
|
+
}, /*#__PURE__*/ React.createElement(reactQuery.HydrationBoundary, {
|
|
51
|
+
state: hydratedState
|
|
52
|
+
}, /*#__PURE__*/ React.createElement(AppOrPage, Object.assign({}, props)))));
|
|
53
|
+
};
|
|
54
|
+
if (opts.ssr) {
|
|
55
|
+
opts.ssrPrepass({
|
|
56
|
+
parent: opts,
|
|
57
|
+
AppOrPage,
|
|
58
|
+
WithTRPC
|
|
59
|
+
});
|
|
60
|
+
} else if (AppOrPage.getInitialProps) {
|
|
61
|
+
// Allow combining `getServerSideProps` and `getInitialProps`
|
|
62
|
+
WithTRPC.getInitialProps = async (appOrPageCtx)=>{
|
|
63
|
+
// Determine if we are wrapping an App component or a Page component.
|
|
64
|
+
const isApp = !!appOrPageCtx.Component;
|
|
65
|
+
// Run the wrapped component's getInitialProps function.
|
|
66
|
+
let pageProps = {};
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
68
|
+
const originalProps = await AppOrPage.getInitialProps(appOrPageCtx);
|
|
69
|
+
const originalPageProps = isApp ? originalProps.pageProps ?? {} : originalProps;
|
|
70
|
+
pageProps = {
|
|
71
|
+
...originalPageProps,
|
|
72
|
+
...pageProps
|
|
73
|
+
};
|
|
74
|
+
const getAppTreeProps = (props)=>isApp ? {
|
|
75
|
+
pageProps: props
|
|
76
|
+
} : props;
|
|
77
|
+
return getAppTreeProps(pageProps);
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
const displayName = AppOrPage.displayName ?? AppOrPage.name ?? 'Component';
|
|
81
|
+
WithTRPC.displayName = `withTRPC(${displayName})`;
|
|
82
|
+
return WithTRPC;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
exports.withTRPC = withTRPC;
|