@quilted/create 0.2.47 → 0.2.49
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/CHANGELOG.md +16 -0
- package/build/esm/node_modules/.pnpm/@nodelib_fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/index.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/providers/async.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/providers/sync.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.stat@2.0.5/node_modules/@nodelib/fs.stat/out/settings.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/index.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/async.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/providers/sync.mjs +1 -1
- package/build/esm/node_modules/.pnpm/@nodelib_fs.walk@1.2.8/node_modules/@nodelib/fs.walk/out/settings.mjs +1 -1
- package/package.json +1 -1
- package/templates/app-basic/browser.tsx +41 -11
- package/templates/app-basic/server.tsx +18 -14
- package/templates/app-empty/server.tsx +1 -3
- package/templates/app-graphql/browser.tsx +52 -21
- package/templates/app-graphql/server.tsx +32 -24
- package/templates/app-graphql/shared/context.ts +9 -1
- package/templates/app-trpc/browser.tsx +49 -21
- package/templates/app-trpc/server.tsx +26 -22
- package/templates/workspace/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @quilted/create
|
|
2
2
|
|
|
3
|
+
## 0.2.49
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#871](https://github.com/lemonmade/quilt/pull/871) [`c16b322`](https://github.com/lemonmade/quilt/commit/c16b3224d3c86bc3a3b6f6af44267650d1e8dc1d) Thanks [@lemonmade](https://github.com/lemonmade)! - Remove @quilt/quilt/globals module
|
|
8
|
+
|
|
9
|
+
- [`fc772fb`](https://github.com/lemonmade/quilt/commit/fc772fb47bd077eb10f67a986a9f4ae792f631f0) Thanks [@lemonmade](https://github.com/lemonmade)! - Fix old @quilted/rollup dependency in workspace template
|
|
10
|
+
|
|
11
|
+
- [`64fc42b`](https://github.com/lemonmade/quilt/commit/64fc42b87ca72eec8dfab211d2ddecdbbaa0e4ac) Thanks [@lemonmade](https://github.com/lemonmade)! - Transition browser header helpers to use classes
|
|
12
|
+
|
|
13
|
+
## 0.2.48
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- [`1abfd9e`](https://github.com/lemonmade/quilt/commit/1abfd9e7f163065bec12db6220fda7a800641b7b) Thanks [@lemonmade](https://github.com/lemonmade)! - Use classes for complex template browser and server entrypoints
|
|
18
|
+
|
|
3
19
|
## 0.2.47
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as out } from '../../../../../../../_virtual/
|
|
1
|
+
import { __exports as out } from '../../../../../../../_virtual/index11.mjs';
|
|
2
2
|
import { __require as requireAsync } from './providers/async.mjs';
|
|
3
3
|
import { __require as requireSync } from './providers/sync.mjs';
|
|
4
4
|
import { __require as requireSettings } from './settings.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as out } from '../../../../../../../_virtual/
|
|
1
|
+
import { __exports as out } from '../../../../../../../_virtual/index10.mjs';
|
|
2
2
|
import { __require as requireAsync } from './providers/async.mjs';
|
|
3
3
|
import { __require as requireStream } from './providers/stream.mjs';
|
|
4
4
|
import { __require as requireSync } from './providers/sync.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { __exports as settings } from '../../../../../../../_virtual/
|
|
1
|
+
import { __exports as settings } from '../../../../../../../_virtual/settings2.mjs';
|
|
2
2
|
import path__default from 'node:path';
|
|
3
3
|
import { __require as requireOut } from '../../../../../@nodelib_fs.scandir@2.1.5/node_modules/@nodelib/fs.scandir/out/index.mjs';
|
|
4
4
|
|
package/package.json
CHANGED
|
@@ -1,19 +1,49 @@
|
|
|
1
|
-
import '
|
|
1
|
+
import type {ComponentChild} from 'preact';
|
|
2
2
|
import {hydrate} from '@quilted/quilt/browser';
|
|
3
3
|
import {Router} from '@quilted/quilt/navigation';
|
|
4
4
|
|
|
5
5
|
import type {AppContext} from '~/shared/context.ts';
|
|
6
6
|
import {App} from './App.tsx';
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
router:
|
|
10
|
-
} satisfies AppContext;
|
|
8
|
+
class BrowserAppContext implements AppContext {
|
|
9
|
+
readonly router: Router;
|
|
11
10
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
configurable: true,
|
|
17
|
-
});
|
|
11
|
+
constructor() {
|
|
12
|
+
this.router = new Router();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
18
15
|
|
|
19
|
-
|
|
16
|
+
class BrowserApp {
|
|
17
|
+
/**
|
|
18
|
+
* The app’s globally-available context.
|
|
19
|
+
*/
|
|
20
|
+
readonly context: BrowserAppContext;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The app’s root JSX element, seeded with the necessary app context.
|
|
24
|
+
*/
|
|
25
|
+
readonly rendered: ComponentChild;
|
|
26
|
+
|
|
27
|
+
constructor() {
|
|
28
|
+
this.context = new BrowserAppContext();
|
|
29
|
+
this.rendered = <App context={this.context} />;
|
|
30
|
+
|
|
31
|
+
// Makes key parts of the app available in the browser console.
|
|
32
|
+
//
|
|
33
|
+
// @example
|
|
34
|
+
// ```js
|
|
35
|
+
// // Log the current URL
|
|
36
|
+
// console.log(globalThis.app.context.router.currentRequest.url);
|
|
37
|
+
// ```
|
|
38
|
+
Object.defineProperty(globalThis, 'app', {
|
|
39
|
+
value: this,
|
|
40
|
+
enumerable: false,
|
|
41
|
+
configurable: true,
|
|
42
|
+
writable: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const app = new BrowserApp();
|
|
48
|
+
|
|
49
|
+
hydrate(app.rendered);
|
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import '@quilted/quilt/globals';
|
|
2
|
-
|
|
3
1
|
import {RequestRouter} from '@quilted/quilt/request-router';
|
|
4
2
|
import {Router} from '@quilted/quilt/navigation';
|
|
5
3
|
import {
|
|
6
4
|
renderAppToHTMLResponse,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
CacheControlHeader,
|
|
6
|
+
ContentSecurityPolicyHeader,
|
|
7
|
+
PermissionsPolicyHeader,
|
|
8
|
+
StrictTransportSecurityHeader,
|
|
11
9
|
} from '@quilted/quilt/server';
|
|
12
10
|
|
|
13
11
|
import Env from 'quilt:module/env';
|
|
@@ -20,11 +18,17 @@ import {App} from './App.tsx';
|
|
|
20
18
|
const router = new RequestRouter();
|
|
21
19
|
const assets = new BrowserAssets();
|
|
22
20
|
|
|
21
|
+
class ServerAppContext implements AppContext {
|
|
22
|
+
readonly router: Router;
|
|
23
|
+
|
|
24
|
+
constructor(request: Request) {
|
|
25
|
+
this.router = new Router(request.url);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
23
29
|
// For all GET requests, render our Preact application.
|
|
24
30
|
router.get(async (request) => {
|
|
25
|
-
const context =
|
|
26
|
-
router: new Router(request.url),
|
|
27
|
-
} satisfies AppContext;
|
|
31
|
+
const context = new ServerAppContext(request);
|
|
28
32
|
|
|
29
33
|
const isHttps = request.url.startsWith('https://');
|
|
30
34
|
|
|
@@ -47,7 +51,7 @@ router.get(async (request) => {
|
|
|
47
51
|
// app or deployment, make sure to update this component accordingly!
|
|
48
52
|
//
|
|
49
53
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
|
50
|
-
'Cache-Control':
|
|
54
|
+
'Cache-Control': CacheControlHeader.stringify({
|
|
51
55
|
cache: false,
|
|
52
56
|
}),
|
|
53
57
|
|
|
@@ -58,7 +62,7 @@ router.get(async (request) => {
|
|
|
58
62
|
// to the allowlist for more specific directives.
|
|
59
63
|
//
|
|
60
64
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
|
61
|
-
'Content-Security-Policy':
|
|
65
|
+
'Content-Security-Policy': ContentSecurityPolicyHeader.stringify({
|
|
62
66
|
// By default, only allow sources from the page's origin.
|
|
63
67
|
defaultSources: ["'self'"],
|
|
64
68
|
// In development, allow connections to local websockets for hot reloading.
|
|
@@ -83,8 +87,8 @@ router.get(async (request) => {
|
|
|
83
87
|
// Sets a strict permissions policy for this page, which limits access
|
|
84
88
|
// to some native browser features.
|
|
85
89
|
//
|
|
86
|
-
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/
|
|
87
|
-
'Permissions-Policy':
|
|
90
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy
|
|
91
|
+
'Permissions-Policy': PermissionsPolicyHeader.stringify({
|
|
88
92
|
// Disables Google's Federated Learning of Cohorts ("FLoC") tracking initiative.
|
|
89
93
|
// @see https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea
|
|
90
94
|
interestCohort: false,
|
|
@@ -104,7 +108,7 @@ router.get(async (request) => {
|
|
|
104
108
|
// Instructs browsers to only load this page over HTTPS.
|
|
105
109
|
//
|
|
106
110
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
|
107
|
-
'Strict-Transport-Security':
|
|
111
|
+
'Strict-Transport-Security': StrictTransportSecurityHeader.stringify(),
|
|
108
112
|
},
|
|
109
113
|
});
|
|
110
114
|
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
import '@quilted/quilt/globals';
|
|
2
|
-
|
|
3
|
-
import {RequestRouter} from '@quilted/quilt/request-router';
|
|
4
1
|
import {renderAppToHTMLResponse} from '@quilted/quilt/server';
|
|
2
|
+
import {RequestRouter} from '@quilted/quilt/request-router';
|
|
5
3
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
6
4
|
|
|
7
5
|
import {App} from './App.tsx';
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import '
|
|
2
|
-
|
|
1
|
+
import type {ComponentChild} from 'preact';
|
|
3
2
|
import {hydrate} from '@quilted/quilt/browser';
|
|
4
3
|
import {Router} from '@quilted/quilt/navigation';
|
|
5
4
|
import {createGraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
@@ -8,22 +7,54 @@ import type {AppContext} from '~/shared/context.ts';
|
|
|
8
7
|
|
|
9
8
|
import {App} from './App.tsx';
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
10
|
+
class BrowserAppContext implements AppContext {
|
|
11
|
+
readonly router: Router;
|
|
12
|
+
readonly graphql: AppContext['graphql'];
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
this.router = new Router();
|
|
16
|
+
|
|
17
|
+
const graphQLFetch = createGraphQLFetch({url: '/api/graphql'});
|
|
18
|
+
const graphQLCache = new GraphQLCache({fetch: graphQLFetch});
|
|
19
|
+
|
|
20
|
+
this.graphql = {
|
|
21
|
+
fetch: graphQLFetch,
|
|
22
|
+
cache: graphQLCache,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class BrowserApp {
|
|
28
|
+
/**
|
|
29
|
+
* The app's globally-available context.
|
|
30
|
+
*/
|
|
31
|
+
readonly context: BrowserAppContext;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The app's root JSX element, seeded with the necessary app context.
|
|
35
|
+
*/
|
|
36
|
+
readonly rendered: ComponentChild;
|
|
37
|
+
|
|
38
|
+
constructor() {
|
|
39
|
+
this.context = new BrowserAppContext();
|
|
40
|
+
this.rendered = <App context={this.context} />;
|
|
41
|
+
|
|
42
|
+
// Makes key parts of the app available in the browser console.
|
|
43
|
+
//
|
|
44
|
+
// @example
|
|
45
|
+
// ```js
|
|
46
|
+
// // Log the current URL
|
|
47
|
+
// console.log(globalThis.app.context.router.currentRequest.url);
|
|
48
|
+
// ```
|
|
49
|
+
Object.defineProperty(globalThis, 'app', {
|
|
50
|
+
value: this,
|
|
51
|
+
enumerable: false,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const app = new BrowserApp();
|
|
59
|
+
|
|
60
|
+
hydrate(app.rendered);
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import '@quilted/quilt/globals';
|
|
2
|
-
|
|
3
1
|
import {RequestRouter, JSONResponse} from '@quilted/quilt/request-router';
|
|
4
2
|
import {Router} from '@quilted/quilt/navigation';
|
|
5
3
|
import {
|
|
6
4
|
renderAppToHTMLResponse,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
CacheControlHeader,
|
|
6
|
+
ContentSecurityPolicyHeader,
|
|
7
|
+
PermissionsPolicyHeader,
|
|
8
|
+
StrictTransportSecurityHeader,
|
|
11
9
|
} from '@quilted/quilt/server';
|
|
10
|
+
import {GraphQLCache} from '@quilted/quilt/graphql';
|
|
12
11
|
|
|
13
12
|
import Env from 'quilt:module/env';
|
|
14
13
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
@@ -18,6 +17,25 @@ import type {AppContext} from '~/shared/context.ts';
|
|
|
18
17
|
const router = new RequestRouter();
|
|
19
18
|
const assets = new BrowserAssets();
|
|
20
19
|
|
|
20
|
+
class ServerAppContext implements AppContext {
|
|
21
|
+
readonly router: Router;
|
|
22
|
+
readonly graphql: AppContext['graphql'];
|
|
23
|
+
|
|
24
|
+
constructor(request: Request) {
|
|
25
|
+
this.router = new Router(request.url);
|
|
26
|
+
|
|
27
|
+
const graphQLFetch: AppContext['graphql']['fetch'] = async (...args) => {
|
|
28
|
+
const {performGraphQLOperation} = await import('./server/graphql.ts');
|
|
29
|
+
return performGraphQLOperation(...args);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.graphql = {
|
|
33
|
+
fetch: graphQLFetch,
|
|
34
|
+
cache: new GraphQLCache({fetch: graphQLFetch}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
21
39
|
// GraphQL API, called from the client
|
|
22
40
|
router.post('/api/graphql', async (request) => {
|
|
23
41
|
const [{query, operationName, variables}, {performGraphQLOperation}] =
|
|
@@ -33,19 +51,9 @@ router.post('/api/graphql', async (request) => {
|
|
|
33
51
|
|
|
34
52
|
// For all GET requests, render our Preact application.
|
|
35
53
|
router.get(async (request) => {
|
|
36
|
-
const [{App}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
import('@quilted/quilt/graphql'),
|
|
40
|
-
]);
|
|
41
|
-
|
|
42
|
-
const context = {
|
|
43
|
-
router: new Router(request.url),
|
|
44
|
-
graphql: {
|
|
45
|
-
fetch: performGraphQLOperation,
|
|
46
|
-
cache: new GraphQLCache({fetch: performGraphQLOperation}),
|
|
47
|
-
},
|
|
48
|
-
} satisfies AppContext;
|
|
54
|
+
const [{App}] = await Promise.all([import('./App.tsx')]);
|
|
55
|
+
|
|
56
|
+
const context = new ServerAppContext(request);
|
|
49
57
|
|
|
50
58
|
const isHttps = request.url.startsWith('https://');
|
|
51
59
|
|
|
@@ -68,7 +76,7 @@ router.get(async (request) => {
|
|
|
68
76
|
// app or deployment, make sure to update this component accordingly!
|
|
69
77
|
//
|
|
70
78
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
|
71
|
-
'Cache-Control':
|
|
79
|
+
'Cache-Control': CacheControlHeader.stringify({
|
|
72
80
|
cache: false,
|
|
73
81
|
}),
|
|
74
82
|
|
|
@@ -79,7 +87,7 @@ router.get(async (request) => {
|
|
|
79
87
|
// to the allowlist for more specific directives.
|
|
80
88
|
//
|
|
81
89
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
|
82
|
-
'Content-Security-Policy':
|
|
90
|
+
'Content-Security-Policy': ContentSecurityPolicyHeader.stringify({
|
|
83
91
|
// By default, only allow sources from the page's origin.
|
|
84
92
|
defaultSources: ["'self'"],
|
|
85
93
|
// In development, allow connections to local websockets for hot reloading.
|
|
@@ -104,8 +112,8 @@ router.get(async (request) => {
|
|
|
104
112
|
// Sets a strict permissions policy for this page, which limits access
|
|
105
113
|
// to some native browser features.
|
|
106
114
|
//
|
|
107
|
-
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/
|
|
108
|
-
'Permissions-Policy':
|
|
115
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy
|
|
116
|
+
'Permissions-Policy': PermissionsPolicyHeader.stringify({
|
|
109
117
|
// Disables Google's Federated Learning of Cohorts ("FLoC") tracking initiative.
|
|
110
118
|
// @see https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea
|
|
111
119
|
interestCohort: false,
|
|
@@ -125,7 +133,7 @@ router.get(async (request) => {
|
|
|
125
133
|
// Instructs browsers to only load this page over HTTPS.
|
|
126
134
|
//
|
|
127
135
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
|
128
|
-
'Strict-Transport-Security':
|
|
136
|
+
'Strict-Transport-Security': StrictTransportSecurityHeader.stringify(),
|
|
129
137
|
},
|
|
130
138
|
});
|
|
131
139
|
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import {createOptionalContext} from '@quilted/quilt/context';
|
|
2
|
+
import type {Router} from '@quilted/quilt/navigation';
|
|
3
|
+
import type {GraphQLFetch, GraphQLCache} from '@quilted/quilt/graphql';
|
|
2
4
|
|
|
3
|
-
export interface AppContext {
|
|
5
|
+
export interface AppContext {
|
|
6
|
+
readonly router: Router;
|
|
7
|
+
readonly graphql: {
|
|
8
|
+
readonly fetch: GraphQLFetch<any>;
|
|
9
|
+
readonly cache: GraphQLCache;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
export const AppContextReact = createOptionalContext<AppContext>();
|
|
6
14
|
export const useAppContext = AppContextReact.use;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import '
|
|
2
|
-
|
|
1
|
+
import type {ComponentChild} from 'preact';
|
|
3
2
|
import {hydrate} from '@quilted/quilt/browser';
|
|
4
3
|
import {Router} from '@quilted/quilt/navigation';
|
|
5
4
|
|
|
@@ -11,22 +10,51 @@ import {trpc} from '~/shared/trpc.ts';
|
|
|
11
10
|
|
|
12
11
|
import {App} from './App.tsx';
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
13
|
+
class BrowserAppContext implements AppContext {
|
|
14
|
+
readonly router: Router;
|
|
15
|
+
readonly trpc: AppContext['trpc'];
|
|
16
|
+
readonly queryClient: QueryClient;
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
this.router = new Router();
|
|
20
|
+
this.queryClient = new QueryClient();
|
|
21
|
+
this.trpc = trpc.createClient({
|
|
22
|
+
links: [httpBatchLink({url: new URL('/api', window.location.href).href})],
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
class BrowserApp {
|
|
28
|
+
/**
|
|
29
|
+
* The app's globally-available context.
|
|
30
|
+
*/
|
|
31
|
+
readonly context: BrowserAppContext;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The app's root JSX element, seeded with the necessary app context.
|
|
35
|
+
*/
|
|
36
|
+
readonly rendered: ComponentChild;
|
|
37
|
+
|
|
38
|
+
constructor() {
|
|
39
|
+
this.context = new BrowserAppContext();
|
|
40
|
+
this.rendered = <App context={this.context} />;
|
|
41
|
+
|
|
42
|
+
// Makes key parts of the app available in the browser console.
|
|
43
|
+
//
|
|
44
|
+
// @example
|
|
45
|
+
// ```js
|
|
46
|
+
// // Log the current URL
|
|
47
|
+
// console.log(globalThis.app.context.router.currentRequest.url);
|
|
48
|
+
// ```
|
|
49
|
+
Object.defineProperty(globalThis, 'app', {
|
|
50
|
+
value: this,
|
|
51
|
+
enumerable: false,
|
|
52
|
+
configurable: true,
|
|
53
|
+
writable: true,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const app = new BrowserApp();
|
|
59
|
+
|
|
60
|
+
hydrate(app.rendered);
|
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import '@quilted/quilt/globals';
|
|
2
|
-
|
|
3
|
-
import {RequestRouter} from '@quilted/quilt/request-router';
|
|
4
|
-
import {Router} from '@quilted/quilt/navigation';
|
|
5
1
|
import {
|
|
6
2
|
renderAppToHTMLResponse,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
CacheControlHeader,
|
|
4
|
+
ContentSecurityPolicyHeader,
|
|
5
|
+
PermissionsPolicyHeader,
|
|
6
|
+
StrictTransportSecurityHeader,
|
|
11
7
|
} from '@quilted/quilt/server';
|
|
8
|
+
import {RequestRouter} from '@quilted/quilt/request-router';
|
|
9
|
+
import {Router} from '@quilted/quilt/navigation';
|
|
12
10
|
|
|
13
11
|
import Env from 'quilt:module/env';
|
|
14
12
|
import {BrowserAssets} from 'quilt:module/assets';
|
|
15
13
|
|
|
16
14
|
import {createDirectClient} from '@quilted/trpc/server';
|
|
17
15
|
import {fetchRequestHandler} from '@trpc/server/adapters/fetch';
|
|
16
|
+
import {QueryClient} from '@tanstack/react-query';
|
|
18
17
|
|
|
19
18
|
import type {AppContext} from '~/shared/context.ts';
|
|
20
19
|
|
|
@@ -23,6 +22,18 @@ import {appRouter} from './trpc.ts';
|
|
|
23
22
|
const router = new RequestRouter();
|
|
24
23
|
const assets = new BrowserAssets();
|
|
25
24
|
|
|
25
|
+
class ServerAppContext implements AppContext {
|
|
26
|
+
readonly router: Router;
|
|
27
|
+
readonly trpc: AppContext['trpc'];
|
|
28
|
+
readonly queryClient: QueryClient;
|
|
29
|
+
|
|
30
|
+
constructor(request: Request) {
|
|
31
|
+
this.router = new Router(request.url);
|
|
32
|
+
this.trpc = createDirectClient(appRouter);
|
|
33
|
+
this.queryClient = new QueryClient();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
26
37
|
router.any(
|
|
27
38
|
'api',
|
|
28
39
|
(request) => {
|
|
@@ -38,16 +49,9 @@ router.any(
|
|
|
38
49
|
|
|
39
50
|
// For all GET requests, render our React application.
|
|
40
51
|
router.get(async (request) => {
|
|
41
|
-
const [{App}
|
|
42
|
-
import('./App.tsx'),
|
|
43
|
-
import('@tanstack/react-query'),
|
|
44
|
-
]);
|
|
52
|
+
const [{App}] = await Promise.all([import('./App.tsx')]);
|
|
45
53
|
|
|
46
|
-
const context =
|
|
47
|
-
router: new Router(request.url),
|
|
48
|
-
trpc: createDirectClient(appRouter),
|
|
49
|
-
queryClient: new QueryClient(),
|
|
50
|
-
} satisfies AppContext;
|
|
54
|
+
const context = new ServerAppContext(request);
|
|
51
55
|
|
|
52
56
|
const isHttps = request.url.startsWith('https://');
|
|
53
57
|
|
|
@@ -70,7 +74,7 @@ router.get(async (request) => {
|
|
|
70
74
|
// app or deployment, make sure to update this component accordingly!
|
|
71
75
|
//
|
|
72
76
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
|
|
73
|
-
'Cache-Control':
|
|
77
|
+
'Cache-Control': CacheControlHeader.stringify({
|
|
74
78
|
cache: false,
|
|
75
79
|
}),
|
|
76
80
|
|
|
@@ -81,7 +85,7 @@ router.get(async (request) => {
|
|
|
81
85
|
// to the allowlist for more specific directives.
|
|
82
86
|
//
|
|
83
87
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
|
|
84
|
-
'Content-Security-Policy':
|
|
88
|
+
'Content-Security-Policy': ContentSecurityPolicyHeader.stringify({
|
|
85
89
|
// By default, only allow sources from the page's origin.
|
|
86
90
|
defaultSources: ["'self'"],
|
|
87
91
|
// In development, allow connections to local websockets for hot reloading.
|
|
@@ -106,8 +110,8 @@ router.get(async (request) => {
|
|
|
106
110
|
// Sets a strict permissions policy for this page, which limits access
|
|
107
111
|
// to some native browser features.
|
|
108
112
|
//
|
|
109
|
-
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/
|
|
110
|
-
'Permissions-Policy':
|
|
113
|
+
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy
|
|
114
|
+
'Permissions-Policy': PermissionsPolicyHeader.stringify({
|
|
111
115
|
// Disables Google's Federated Learning of Cohorts ("FLoC") tracking initiative.
|
|
112
116
|
// @see https://www.eff.org/deeplinks/2021/03/googles-floc-terrible-idea
|
|
113
117
|
interestCohort: false,
|
|
@@ -127,7 +131,7 @@ router.get(async (request) => {
|
|
|
127
131
|
// Instructs browsers to only load this page over HTTPS.
|
|
128
132
|
//
|
|
129
133
|
// @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security
|
|
130
|
-
'Strict-Transport-Security':
|
|
134
|
+
'Strict-Transport-Security': StrictTransportSecurityHeader.stringify(),
|
|
131
135
|
},
|
|
132
136
|
});
|
|
133
137
|
|