@c9up/aurora 0.1.20 → 0.1.22
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/AuroraManager.d.ts +5 -5
- package/dist/AuroraManager.js +4 -4
- package/dist/AuroraProvider.d.ts +2 -2
- package/dist/AuroraProvider.js +3 -3
- package/dist/Pages.d.ts +1 -1
- package/dist/Pages.js +1 -1
- package/dist/browser.d.ts +29 -7
- package/dist/browser.js +43 -16
- package/dist/liveClient.d.ts +1 -1
- package/dist/liveClient.js +2 -2
- package/dist/liveServer.d.ts +1 -1
- package/dist/liveServer.js +1 -1
- package/dist/rpc.d.ts +15 -1
- package/dist/rpc.js +31 -2
- package/dist/server/renderPage.d.ts +3 -3
- package/dist/server/renderPage.js +3 -3
- package/dist/server/serveAssets.d.ts +2 -2
- package/dist/server/serveAssets.js +2 -2
- package/package.json +1 -1
- package/src/AuroraManager.ts +6 -6
- package/src/AuroraProvider.ts +3 -3
- package/src/Pages.ts +2 -2
- package/src/browser.ts +47 -18
- package/src/liveClient.ts +2 -2
- package/src/liveServer.ts +1 -1
- package/src/rpc.ts +42 -3
- package/src/server/renderPage.ts +4 -4
- package/src/server/serveAssets.ts +2 -2
package/dist/AuroraManager.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export interface AuroraManagerConfig {
|
|
|
33
33
|
* URL prefix the asset routes mount under. The aurora runtime is served
|
|
34
34
|
* from `<assetsPrefix>/aurora/*` and the app's pages from
|
|
35
35
|
* `<assetsPrefix>/pages/*`, and the SSR importmap + page URLs derive from
|
|
36
|
-
* it. Default `/
|
|
36
|
+
* it. Default `/__assets` (the leading underscore namespaces framework
|
|
37
37
|
* assets away from app routes, Next.js `/_next` style). Set e.g. `/assets`
|
|
38
38
|
* for an underscore-free scheme. An explicit `pages.urlPrefix` still wins.
|
|
39
39
|
*/
|
|
@@ -44,7 +44,7 @@ export interface AuroraManagerConfig {
|
|
|
44
44
|
* importmaps). Merged over aurora's auto defaults (`@c9up/aurora`,
|
|
45
45
|
* `@c9up/aurora/rpc`, `@c9up/comet`) on every `render()`. Use to point a bare
|
|
46
46
|
* specifier at a curated browser entry, e.g.
|
|
47
|
-
* `{ '@c9up/aurora': '/
|
|
47
|
+
* `{ '@c9up/aurora': '/__assets/pages/browser/aurora.js' }`. A per-call
|
|
48
48
|
* `render(..., { importmap })` still wins over this.
|
|
49
49
|
*
|
|
50
50
|
* (Importmaps are aurora's no-bundler particularity — AdonisJS bundles via Vite
|
|
@@ -55,7 +55,7 @@ export interface AuroraManagerConfig {
|
|
|
55
55
|
export declare class AuroraManager {
|
|
56
56
|
readonly pages: Pages;
|
|
57
57
|
readonly auroraDistRoot: string;
|
|
58
|
-
/** Resolved asset prefix (default `/
|
|
58
|
+
/** Resolved asset prefix (default `/__assets`). */
|
|
59
59
|
readonly assetsPrefix: string;
|
|
60
60
|
/** Mount path for the aurora runtime — `<assetsPrefix>/aurora`. */
|
|
61
61
|
readonly auroraAssetPath: string;
|
|
@@ -78,12 +78,12 @@ export declare class AuroraManager {
|
|
|
78
78
|
render(ctx: RenderHttpContext, name: string, props: unknown, options?: RenderPageOptions): Promise<void>;
|
|
79
79
|
/**
|
|
80
80
|
* Handler for aurora's pre-built ESM runtime. Mount on
|
|
81
|
-
* `GET /
|
|
81
|
+
* `GET /__assets/aurora/*`.
|
|
82
82
|
*/
|
|
83
83
|
auroraAssetsHandler(): (ctx: AssetsHttpContext) => Promise<void>;
|
|
84
84
|
/**
|
|
85
85
|
* Handler for the app's pages directory. Mount on
|
|
86
|
-
* `GET /
|
|
86
|
+
* `GET /__assets/pages/*`.
|
|
87
87
|
*/
|
|
88
88
|
pageAssetsHandler(): (ctx: AssetsHttpContext) => Promise<void>;
|
|
89
89
|
/**
|
package/dist/AuroraManager.js
CHANGED
|
@@ -38,7 +38,7 @@ function normalizePrefix(prefix) {
|
|
|
38
38
|
export class AuroraManager {
|
|
39
39
|
pages;
|
|
40
40
|
auroraDistRoot;
|
|
41
|
-
/** Resolved asset prefix (default `/
|
|
41
|
+
/** Resolved asset prefix (default `/__assets`). */
|
|
42
42
|
assetsPrefix;
|
|
43
43
|
/** Mount path for the aurora runtime — `<assetsPrefix>/aurora`. */
|
|
44
44
|
auroraAssetPath;
|
|
@@ -51,7 +51,7 @@ export class AuroraManager {
|
|
|
51
51
|
/** App-level importmap overrides from `config/aurora.ts`, merged on render. */
|
|
52
52
|
importmap;
|
|
53
53
|
constructor(config) {
|
|
54
|
-
this.assetsPrefix = normalizePrefix(config.assetsPrefix ?? "/
|
|
54
|
+
this.assetsPrefix = normalizePrefix(config.assetsPrefix ?? "/__assets");
|
|
55
55
|
this.auroraAssetPath = `${this.assetsPrefix}/aurora`;
|
|
56
56
|
this.pageAssetPath = `${this.assetsPrefix}/pages`;
|
|
57
57
|
this.cometAssetPath = `${this.assetsPrefix}/comet`;
|
|
@@ -97,14 +97,14 @@ export class AuroraManager {
|
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
99
|
* Handler for aurora's pre-built ESM runtime. Mount on
|
|
100
|
-
* `GET /
|
|
100
|
+
* `GET /__assets/aurora/*`.
|
|
101
101
|
*/
|
|
102
102
|
auroraAssetsHandler() {
|
|
103
103
|
return serveAssets({ root: this.auroraDistRoot });
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
106
106
|
* Handler for the app's pages directory. Mount on
|
|
107
|
-
* `GET /
|
|
107
|
+
* `GET /__assets/pages/*`.
|
|
108
108
|
*/
|
|
109
109
|
pageAssetsHandler() {
|
|
110
110
|
return serveAssets({ root: this.pages.root });
|
package/dist/AuroraProvider.d.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* AuroraProvider — registers the AuroraManager singleton and auto-mounts
|
|
3
3
|
* the two asset routes the browser needs:
|
|
4
4
|
*
|
|
5
|
-
* GET /
|
|
6
|
-
* GET /
|
|
5
|
+
* GET /__assets/aurora/* → packages/@c9up/aurora/dist/*
|
|
6
|
+
* GET /__assets/pages/* → resources/pages/*
|
|
7
7
|
*
|
|
8
8
|
* Config (in `config/aurora.ts`):
|
|
9
9
|
*
|
package/dist/AuroraProvider.js
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* AuroraProvider — registers the AuroraManager singleton and auto-mounts
|
|
3
3
|
* the two asset routes the browser needs:
|
|
4
4
|
*
|
|
5
|
-
* GET /
|
|
6
|
-
* GET /
|
|
5
|
+
* GET /__assets/aurora/* → packages/@c9up/aurora/dist/*
|
|
6
|
+
* GET /__assets/pages/* → resources/pages/*
|
|
7
7
|
*
|
|
8
8
|
* Config (in `config/aurora.ts`):
|
|
9
9
|
*
|
|
@@ -64,7 +64,7 @@ export default class AuroraProvider {
|
|
|
64
64
|
const router = this.app.container.resolve("router");
|
|
65
65
|
const manager = this.app.container.resolve(AuroraManager);
|
|
66
66
|
// Mount paths derive from the configured `assetsPrefix` (default
|
|
67
|
-
// `/
|
|
67
|
+
// `/__assets`) — set `config.aurora.assetsPrefix` to change the scheme.
|
|
68
68
|
router.get(`${manager.auroraAssetPath}/*`, adaptHandler(manager.auroraAssetsHandler()));
|
|
69
69
|
router.get(`${manager.pageAssetPath}/*`, adaptHandler(manager.pageAssetsHandler()));
|
|
70
70
|
// Serve @c9up/comet's runtime so the RPC client's bare `import
|
package/dist/Pages.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface PagesConfig {
|
|
|
25
25
|
root: string;
|
|
26
26
|
/**
|
|
27
27
|
* URL prefix the browser uses to fetch a page's compiled JS.
|
|
28
|
-
* Defaults to `/
|
|
28
|
+
* Defaults to `/__assets/pages`. A name `"Foo"` maps to
|
|
29
29
|
* `${urlPrefix}/Foo.js`.
|
|
30
30
|
*/
|
|
31
31
|
urlPrefix?: string;
|
package/dist/Pages.js
CHANGED
|
@@ -29,7 +29,7 @@ export class Pages {
|
|
|
29
29
|
registry = new Map();
|
|
30
30
|
constructor(config) {
|
|
31
31
|
this.root = config.root;
|
|
32
|
-
this.urlPrefix = (config.urlPrefix ?? "/
|
|
32
|
+
this.urlPrefix = (config.urlPrefix ?? "/__assets/pages").replace(/\/$/, "");
|
|
33
33
|
this.extension = config.extension ?? ".js";
|
|
34
34
|
}
|
|
35
35
|
/**
|
package/dist/browser.d.ts
CHANGED
|
@@ -31,10 +31,14 @@ export interface WebStorageOptions {
|
|
|
31
31
|
area?: StorageArea;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
34
|
-
*
|
|
34
|
+
* SSR-safe key/value store over `localStorage` / `sessionStorage`.
|
|
35
35
|
*
|
|
36
|
-
* -
|
|
37
|
-
*
|
|
36
|
+
* - {@link get} / {@link set} are a thin pass-through — raw strings, no JSON,
|
|
37
|
+
* exactly like the native `localStorage` API (so a token round-trips as-is,
|
|
38
|
+
* not double-encoded). {@link getJSON} / {@link setJSON} are the opt-in
|
|
39
|
+
* variants for structured values.
|
|
40
|
+
* - Reads return `null` on the server or a missing key (`getJSON` also on
|
|
41
|
+
* malformed JSON).
|
|
38
42
|
* - Writes swallow quota / private-mode errors so a full store never crashes
|
|
39
43
|
* the app (best-effort).
|
|
40
44
|
* - `prefix` namespaces keys; {@link keys} and {@link clear} stay scoped to it,
|
|
@@ -45,13 +49,31 @@ export declare class WebStorage {
|
|
|
45
49
|
constructor(options?: WebStorageOptions);
|
|
46
50
|
/** The on-disk key for `key`, namespaced by the configured prefix. */
|
|
47
51
|
fullKey(key: string): string;
|
|
48
|
-
|
|
49
|
-
|
|
52
|
+
/**
|
|
53
|
+
* Read the raw string at `key` — a thin, SSR-safe pass-through over
|
|
54
|
+
* `localStorage.getItem` (returns `null` on the server or a missing key).
|
|
55
|
+
* No JSON: what you {@link set} is what you get. Use {@link getJSON} for
|
|
56
|
+
* structured values.
|
|
57
|
+
*/
|
|
58
|
+
get(key: string): string | null;
|
|
59
|
+
/**
|
|
60
|
+
* Write the raw string `value` at `key` (SSR no-op; quota / private-mode
|
|
61
|
+
* errors are swallowed — best-effort). No encoding, like
|
|
62
|
+
* `localStorage.setItem`. Use {@link setJSON} for objects/arrays/etc.
|
|
63
|
+
*/
|
|
64
|
+
set(key: string, value: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Read `key` and JSON-parse it into `T`. Returns `null` on the server, a
|
|
67
|
+
* missing key, or malformed JSON. Pair with {@link setJSON}.
|
|
68
|
+
*/
|
|
69
|
+
getJSON<T>(key: string): T | null;
|
|
70
|
+
/** JSON-serialise `value` and store it at `key`. Pair with {@link getJSON}. */
|
|
71
|
+
setJSON(key: string, value: unknown): void;
|
|
50
72
|
/** Whether `key` is present (and not the server). */
|
|
51
73
|
has(key: string): boolean;
|
|
52
74
|
remove(key: string): void;
|
|
53
|
-
/** Read `key`, or compute + persist `factory()` on a miss, returning the
|
|
54
|
-
getOrSet
|
|
75
|
+
/** Read `key`, or compute + persist `factory()` on a miss, returning the string. */
|
|
76
|
+
getOrSet(key: string, factory: () => string): string;
|
|
55
77
|
/** Keys in this store, prefix stripped. Empty array during SSR. */
|
|
56
78
|
keys(): string[];
|
|
57
79
|
/** Remove this store's keys. With no prefix this clears the whole area. */
|
package/dist/browser.js
CHANGED
|
@@ -29,10 +29,14 @@ export function reload() {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
|
-
*
|
|
32
|
+
* SSR-safe key/value store over `localStorage` / `sessionStorage`.
|
|
33
33
|
*
|
|
34
|
-
* -
|
|
35
|
-
*
|
|
34
|
+
* - {@link get} / {@link set} are a thin pass-through — raw strings, no JSON,
|
|
35
|
+
* exactly like the native `localStorage` API (so a token round-trips as-is,
|
|
36
|
+
* not double-encoded). {@link getJSON} / {@link setJSON} are the opt-in
|
|
37
|
+
* variants for structured values.
|
|
38
|
+
* - Reads return `null` on the server or a missing key (`getJSON` also on
|
|
39
|
+
* malformed JSON).
|
|
36
40
|
* - Writes swallow quota / private-mode errors so a full store never crashes
|
|
37
41
|
* the app (best-effort).
|
|
38
42
|
* - `prefix` namespaces keys; {@link keys} and {@link clear} stay scoped to it,
|
|
@@ -56,31 +60,53 @@ export class WebStorage {
|
|
|
56
60
|
fullKey(key) {
|
|
57
61
|
return this.#prefix + key;
|
|
58
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Read the raw string at `key` — a thin, SSR-safe pass-through over
|
|
65
|
+
* `localStorage.getItem` (returns `null` on the server or a missing key).
|
|
66
|
+
* No JSON: what you {@link set} is what you get. Use {@link getJSON} for
|
|
67
|
+
* structured values.
|
|
68
|
+
*/
|
|
59
69
|
get(key) {
|
|
60
70
|
const backend = this.#backend();
|
|
61
71
|
if (!backend)
|
|
62
72
|
return null;
|
|
63
|
-
|
|
64
|
-
if (raw === null)
|
|
65
|
-
return null;
|
|
66
|
-
try {
|
|
67
|
-
return JSON.parse(raw);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return null;
|
|
71
|
-
}
|
|
73
|
+
return backend.getItem(this.fullKey(key));
|
|
72
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* Write the raw string `value` at `key` (SSR no-op; quota / private-mode
|
|
77
|
+
* errors are swallowed — best-effort). No encoding, like
|
|
78
|
+
* `localStorage.setItem`. Use {@link setJSON} for objects/arrays/etc.
|
|
79
|
+
*/
|
|
73
80
|
set(key, value) {
|
|
74
81
|
const backend = this.#backend();
|
|
75
82
|
if (!backend)
|
|
76
83
|
return;
|
|
77
84
|
try {
|
|
78
|
-
backend.setItem(this.fullKey(key),
|
|
85
|
+
backend.setItem(this.fullKey(key), value);
|
|
79
86
|
}
|
|
80
87
|
catch {
|
|
81
88
|
// QuotaExceededError / Safari private mode — best-effort write.
|
|
82
89
|
}
|
|
83
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Read `key` and JSON-parse it into `T`. Returns `null` on the server, a
|
|
93
|
+
* missing key, or malformed JSON. Pair with {@link setJSON}.
|
|
94
|
+
*/
|
|
95
|
+
getJSON(key) {
|
|
96
|
+
const raw = this.get(key);
|
|
97
|
+
if (raw === null)
|
|
98
|
+
return null;
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(raw);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
/** JSON-serialise `value` and store it at `key`. Pair with {@link getJSON}. */
|
|
107
|
+
setJSON(key, value) {
|
|
108
|
+
this.set(key, JSON.stringify(value));
|
|
109
|
+
}
|
|
84
110
|
/** Whether `key` is present (and not the server). */
|
|
85
111
|
has(key) {
|
|
86
112
|
const backend = this.#backend();
|
|
@@ -91,7 +117,7 @@ export class WebStorage {
|
|
|
91
117
|
remove(key) {
|
|
92
118
|
this.#backend()?.removeItem(this.fullKey(key));
|
|
93
119
|
}
|
|
94
|
-
/** Read `key`, or compute + persist `factory()` on a miss, returning the
|
|
120
|
+
/** Read `key`, or compute + persist `factory()` on a miss, returning the string. */
|
|
95
121
|
getOrSet(key, factory) {
|
|
96
122
|
const existing = this.get(key);
|
|
97
123
|
if (existing !== null)
|
|
@@ -148,11 +174,12 @@ export const session = new WebStorage({ area: "session" });
|
|
|
148
174
|
*/
|
|
149
175
|
export function persistedSignal(key, initial, options = {}) {
|
|
150
176
|
const store = new WebStorage(options);
|
|
151
|
-
const stored = store.
|
|
177
|
+
const stored = store.getJSON(key);
|
|
152
178
|
const sig = signal(stored !== null ? stored : initial);
|
|
153
179
|
// Mirror every change back to storage; runs once immediately, then on change.
|
|
180
|
+
// JSON so any T (object, number, boolean, string) round-trips.
|
|
154
181
|
effect(() => {
|
|
155
|
-
store.
|
|
182
|
+
store.setJSON(key, sig());
|
|
156
183
|
});
|
|
157
184
|
if ((options.crossTab ?? true) &&
|
|
158
185
|
(options.area ?? "local") === "local" &&
|
package/dist/liveClient.d.ts
CHANGED
|
@@ -61,7 +61,7 @@ export interface LiveHttpPoster {
|
|
|
61
61
|
/**
|
|
62
62
|
* Build a {@link LiveClientTransport} from a relay client (SSE down) + an HTTP
|
|
63
63
|
* client (events up). `path` must match the server's `wireLiveEvents` route
|
|
64
|
-
* (default `/
|
|
64
|
+
* (default `/__live/event`). Keeps `liveClient` itself transport-agnostic.
|
|
65
65
|
*
|
|
66
66
|
* @example
|
|
67
67
|
* import { relay } from '@c9up/aurora/relay'
|
package/dist/liveClient.js
CHANGED
|
@@ -58,7 +58,7 @@ export function liveClient(opts) {
|
|
|
58
58
|
/**
|
|
59
59
|
* Build a {@link LiveClientTransport} from a relay client (SSE down) + an HTTP
|
|
60
60
|
* client (events up). `path` must match the server's `wireLiveEvents` route
|
|
61
|
-
* (default `/
|
|
61
|
+
* (default `/__live/event`). Keeps `liveClient` itself transport-agnostic.
|
|
62
62
|
*
|
|
63
63
|
* @example
|
|
64
64
|
* import { relay } from '@c9up/aurora/relay'
|
|
@@ -67,7 +67,7 @@ export function liveClient(opts) {
|
|
|
67
67
|
* liveClient({ container, factory, mount, transport })
|
|
68
68
|
*/
|
|
69
69
|
export function buildLiveTransport(relayClient, http, options = {}) {
|
|
70
|
-
const path = options.path ?? "/
|
|
70
|
+
const path = options.path ?? "/__live/event";
|
|
71
71
|
return {
|
|
72
72
|
subscribe: (channel, handler) => relayClient.subscribe(channel, handler),
|
|
73
73
|
post: (id, event, payload) => {
|
package/dist/liveServer.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export interface WireLiveEventsOptions {
|
|
|
29
29
|
path?: string;
|
|
30
30
|
}
|
|
31
31
|
/** Default inbound-event route — keep the client transport's `path` in sync. */
|
|
32
|
-
export declare const DEFAULT_LIVE_EVENT_PATH = "/
|
|
32
|
+
export declare const DEFAULT_LIVE_EVENT_PATH = "/__live/event";
|
|
33
33
|
/**
|
|
34
34
|
* Register the inbound live-event route on the host router. Call once at boot
|
|
35
35
|
* (e.g. from a provider that resolved the router + relay from the container).
|
package/dist/liveServer.js
CHANGED
|
@@ -18,7 +18,7 @@ function isLiveEventBody(value) {
|
|
|
18
18
|
return typeof value.id === "string" && typeof value.event === "string";
|
|
19
19
|
}
|
|
20
20
|
/** Default inbound-event route — keep the client transport's `path` in sync. */
|
|
21
|
-
export const DEFAULT_LIVE_EVENT_PATH = "/
|
|
21
|
+
export const DEFAULT_LIVE_EVENT_PATH = "/__live/event";
|
|
22
22
|
/**
|
|
23
23
|
* Register the inbound live-event route on the host router. Call once at boot
|
|
24
24
|
* (e.g. from a provider that resolved the router + relay from the container).
|
package/dist/rpc.d.ts
CHANGED
|
@@ -8,9 +8,23 @@ export interface RpcClientOptions {
|
|
|
8
8
|
http?: HttpClient;
|
|
9
9
|
/** Default headers — only used when no `http` client is supplied. */
|
|
10
10
|
headers?: Record<string, string>;
|
|
11
|
+
/**
|
|
12
|
+
* Auto CSRF: read the `XSRF-TOKEN` cookie and echo it as `X-XSRF-TOKEN` on
|
|
13
|
+
* every call (Axios/Angular convention), so RPC POSTs pass blackhole's
|
|
14
|
+
* signed double-submit check when the route is cookie/session-authed. No-op
|
|
15
|
+
* outside the browser and when the cookie is absent. Default `true`.
|
|
16
|
+
* `/rpc` under a bearer (JWT) guard is CSRF-exempt, so the missing-cookie
|
|
17
|
+
* no-op is exactly right there too.
|
|
18
|
+
*/
|
|
19
|
+
xsrf?: boolean;
|
|
20
|
+
/** Cookie to read the CSRF token from. Default `XSRF-TOKEN`. */
|
|
21
|
+
xsrfCookieName?: string;
|
|
22
|
+
/** Header to echo the CSRF token in. Default `X-XSRF-TOKEN`. */
|
|
23
|
+
xsrfHeaderName?: string;
|
|
11
24
|
}
|
|
12
25
|
/**
|
|
13
26
|
* Create a JSON-RPC client bound to aurora's HttpClient transport. Inherits the
|
|
14
|
-
* supplied (or a fresh) HttpClient's base URL, auth headers, and timeouts
|
|
27
|
+
* supplied (or a fresh) HttpClient's base URL, auth headers, and timeouts, and
|
|
28
|
+
* (by default) auto-attaches the `X-XSRF-TOKEN` CSRF header from the cookie.
|
|
15
29
|
*/
|
|
16
30
|
export declare function createRpcClient(options?: RpcClientOptions): RpcClient;
|
package/dist/rpc.js
CHANGED
|
@@ -16,14 +16,43 @@
|
|
|
16
16
|
import { createRpcClient as createCometRpcClient } from "@c9up/comet";
|
|
17
17
|
import { HttpClient } from "./http.js";
|
|
18
18
|
export { isRpcError, RpcError, } from "@c9up/comet";
|
|
19
|
+
/**
|
|
20
|
+
* Read a cookie's raw value from `document.cookie`. Returns `undefined`
|
|
21
|
+
* server-side (no `document`) or when the cookie is absent. The value is sent
|
|
22
|
+
* verbatim — double-submit compares it byte-for-byte against the cookie, so it
|
|
23
|
+
* must not be decoded.
|
|
24
|
+
*/
|
|
25
|
+
function readCookie(name) {
|
|
26
|
+
if (typeof document === "undefined")
|
|
27
|
+
return undefined;
|
|
28
|
+
const prefix = `${name}=`;
|
|
29
|
+
for (const part of document.cookie.split(";")) {
|
|
30
|
+
const trimmed = part.trimStart();
|
|
31
|
+
if (trimmed.startsWith(prefix))
|
|
32
|
+
return trimmed.slice(prefix.length);
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
19
36
|
/**
|
|
20
37
|
* Create a JSON-RPC client bound to aurora's HttpClient transport. Inherits the
|
|
21
|
-
* supplied (or a fresh) HttpClient's base URL, auth headers, and timeouts
|
|
38
|
+
* supplied (or a fresh) HttpClient's base URL, auth headers, and timeouts, and
|
|
39
|
+
* (by default) auto-attaches the `X-XSRF-TOKEN` CSRF header from the cookie.
|
|
22
40
|
*/
|
|
23
41
|
export function createRpcClient(options = {}) {
|
|
24
42
|
const http = options.http ?? new HttpClient({ headers: options.headers });
|
|
43
|
+
const xsrfEnabled = options.xsrf ?? true;
|
|
44
|
+
const cookieName = options.xsrfCookieName ?? "XSRF-TOKEN";
|
|
45
|
+
const headerName = options.xsrfHeaderName ?? "X-XSRF-TOKEN";
|
|
25
46
|
return createCometRpcClient({
|
|
26
47
|
url: options.url,
|
|
27
|
-
transport: (url, body, { signal }) =>
|
|
48
|
+
transport: (url, body, { signal }) => {
|
|
49
|
+
let headers;
|
|
50
|
+
if (xsrfEnabled) {
|
|
51
|
+
const token = readCookie(cookieName);
|
|
52
|
+
if (token !== undefined)
|
|
53
|
+
headers = { [headerName]: token };
|
|
54
|
+
}
|
|
55
|
+
return http.post(url, body, { signal, headers });
|
|
56
|
+
},
|
|
28
57
|
});
|
|
29
58
|
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* <head>
|
|
9
9
|
* ...
|
|
10
10
|
* <script type="importmap">
|
|
11
|
-
* { "imports": { "@c9up/aurora": "/
|
|
11
|
+
* { "imports": { "@c9up/aurora": "/__assets/aurora/index.js" } }
|
|
12
12
|
* </script>
|
|
13
13
|
* </head>
|
|
14
14
|
* <body>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* <script id="aurora-page-data" type="application/json">{…}</script>
|
|
17
17
|
* <script type="module">
|
|
18
18
|
* import { hydrate } from '@c9up/aurora'
|
|
19
|
-
* import Page from '/
|
|
19
|
+
* import Page from '/__assets/pages/ProjectPage.js'
|
|
20
20
|
* const data = JSON.parse(document.getElementById('aurora-page-data').textContent)
|
|
21
21
|
* hydrate(document.getElementById('aurora-root'), () => Page(data.props))
|
|
22
22
|
* </script>
|
|
@@ -39,7 +39,7 @@ export interface RenderHttpContext {
|
|
|
39
39
|
export interface RenderPageOptions {
|
|
40
40
|
/**
|
|
41
41
|
* Importmap entries injected into `<head>`. Defaults to mapping
|
|
42
|
-
* `@c9up/aurora` to `/
|
|
42
|
+
* `@c9up/aurora` to `/__assets/aurora/index.js`. Override to point
|
|
43
43
|
* at a different mount or to add app-side aliases.
|
|
44
44
|
*/
|
|
45
45
|
importmap?: Record<string, string>;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* <head>
|
|
9
9
|
* ...
|
|
10
10
|
* <script type="importmap">
|
|
11
|
-
* { "imports": { "@c9up/aurora": "/
|
|
11
|
+
* { "imports": { "@c9up/aurora": "/__assets/aurora/index.js" } }
|
|
12
12
|
* </script>
|
|
13
13
|
* </head>
|
|
14
14
|
* <body>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* <script id="aurora-page-data" type="application/json">{…}</script>
|
|
17
17
|
* <script type="module">
|
|
18
18
|
* import { hydrate } from '@c9up/aurora'
|
|
19
|
-
* import Page from '/
|
|
19
|
+
* import Page from '/__assets/pages/ProjectPage.js'
|
|
20
20
|
* const data = JSON.parse(document.getElementById('aurora-page-data').textContent)
|
|
21
21
|
* hydrate(document.getElementById('aurora-root'), () => Page(data.props))
|
|
22
22
|
* </script>
|
|
@@ -58,7 +58,7 @@ export async function renderPage(ctx, pages, name, props, options = {}) {
|
|
|
58
58
|
const tree = await factory(props);
|
|
59
59
|
const body = renderToString(tree);
|
|
60
60
|
const importmap = {
|
|
61
|
-
"@c9up/aurora": "/
|
|
61
|
+
"@c9up/aurora": "/__assets/aurora/index.js",
|
|
62
62
|
...options.importmap,
|
|
63
63
|
};
|
|
64
64
|
const rootId = options.rootId ?? "aurora-root";
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `serveAssets` — generic static-file handler exposed by aurora so an
|
|
3
3
|
* app can mount the runtime + the pages dist with a couple of routes:
|
|
4
4
|
*
|
|
5
|
-
* router.get('/
|
|
6
|
-
* router.get('/
|
|
5
|
+
* router.get('/__assets/aurora/*', serveAssets({ root: auroraDistPath }))
|
|
6
|
+
* router.get('/__assets/pages/*', serveAssets({ root: pagesPath }))
|
|
7
7
|
*
|
|
8
8
|
* The handler is framework-agnostic: it reads `ctx.request.param('*')`
|
|
9
9
|
* and writes to `ctx.response`. Any context that satisfies
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `serveAssets` — generic static-file handler exposed by aurora so an
|
|
3
3
|
* app can mount the runtime + the pages dist with a couple of routes:
|
|
4
4
|
*
|
|
5
|
-
* router.get('/
|
|
6
|
-
* router.get('/
|
|
5
|
+
* router.get('/__assets/aurora/*', serveAssets({ root: auroraDistPath }))
|
|
6
|
+
* router.get('/__assets/pages/*', serveAssets({ root: pagesPath }))
|
|
7
7
|
*
|
|
8
8
|
* The handler is framework-agnostic: it reads `ctx.request.param('*')`
|
|
9
9
|
* and writes to `ctx.response`. Any context that satisfies
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@c9up/aurora",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.22",
|
|
4
4
|
"description": "Aurora — reactive UI runtime for the Ream framework. Tagged-template DOM, signal-based state, isomorphic SSR + hydration, zero build step.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
package/src/AuroraManager.ts
CHANGED
|
@@ -45,7 +45,7 @@ export interface AuroraManagerConfig {
|
|
|
45
45
|
* URL prefix the asset routes mount under. The aurora runtime is served
|
|
46
46
|
* from `<assetsPrefix>/aurora/*` and the app's pages from
|
|
47
47
|
* `<assetsPrefix>/pages/*`, and the SSR importmap + page URLs derive from
|
|
48
|
-
* it. Default `/
|
|
48
|
+
* it. Default `/__assets` (the leading underscore namespaces framework
|
|
49
49
|
* assets away from app routes, Next.js `/_next` style). Set e.g. `/assets`
|
|
50
50
|
* for an underscore-free scheme. An explicit `pages.urlPrefix` still wins.
|
|
51
51
|
*/
|
|
@@ -56,7 +56,7 @@ export interface AuroraManagerConfig {
|
|
|
56
56
|
* importmaps). Merged over aurora's auto defaults (`@c9up/aurora`,
|
|
57
57
|
* `@c9up/aurora/rpc`, `@c9up/comet`) on every `render()`. Use to point a bare
|
|
58
58
|
* specifier at a curated browser entry, e.g.
|
|
59
|
-
* `{ '@c9up/aurora': '/
|
|
59
|
+
* `{ '@c9up/aurora': '/__assets/pages/browser/aurora.js' }`. A per-call
|
|
60
60
|
* `render(..., { importmap })` still wins over this.
|
|
61
61
|
*
|
|
62
62
|
* (Importmaps are aurora's no-bundler particularity — AdonisJS bundles via Vite
|
|
@@ -92,7 +92,7 @@ function normalizePrefix(prefix: string): string {
|
|
|
92
92
|
export class AuroraManager {
|
|
93
93
|
readonly pages: Pages;
|
|
94
94
|
readonly auroraDistRoot: string;
|
|
95
|
-
/** Resolved asset prefix (default `/
|
|
95
|
+
/** Resolved asset prefix (default `/__assets`). */
|
|
96
96
|
readonly assetsPrefix: string;
|
|
97
97
|
/** Mount path for the aurora runtime — `<assetsPrefix>/aurora`. */
|
|
98
98
|
readonly auroraAssetPath: string;
|
|
@@ -106,7 +106,7 @@ export class AuroraManager {
|
|
|
106
106
|
readonly importmap: Record<string, string>;
|
|
107
107
|
|
|
108
108
|
constructor(config: AuroraManagerConfig) {
|
|
109
|
-
this.assetsPrefix = normalizePrefix(config.assetsPrefix ?? "/
|
|
109
|
+
this.assetsPrefix = normalizePrefix(config.assetsPrefix ?? "/__assets");
|
|
110
110
|
this.auroraAssetPath = `${this.assetsPrefix}/aurora`;
|
|
111
111
|
this.pageAssetPath = `${this.assetsPrefix}/pages`;
|
|
112
112
|
this.cometAssetPath = `${this.assetsPrefix}/comet`;
|
|
@@ -159,7 +159,7 @@ export class AuroraManager {
|
|
|
159
159
|
|
|
160
160
|
/**
|
|
161
161
|
* Handler for aurora's pre-built ESM runtime. Mount on
|
|
162
|
-
* `GET /
|
|
162
|
+
* `GET /__assets/aurora/*`.
|
|
163
163
|
*/
|
|
164
164
|
auroraAssetsHandler(): (ctx: AssetsHttpContext) => Promise<void> {
|
|
165
165
|
return serveAssets({ root: this.auroraDistRoot });
|
|
@@ -167,7 +167,7 @@ export class AuroraManager {
|
|
|
167
167
|
|
|
168
168
|
/**
|
|
169
169
|
* Handler for the app's pages directory. Mount on
|
|
170
|
-
* `GET /
|
|
170
|
+
* `GET /__assets/pages/*`.
|
|
171
171
|
*/
|
|
172
172
|
pageAssetsHandler(): (ctx: AssetsHttpContext) => Promise<void> {
|
|
173
173
|
return serveAssets({ root: this.pages.root });
|
package/src/AuroraProvider.ts
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* AuroraProvider — registers the AuroraManager singleton and auto-mounts
|
|
3
3
|
* the two asset routes the browser needs:
|
|
4
4
|
*
|
|
5
|
-
* GET /
|
|
6
|
-
* GET /
|
|
5
|
+
* GET /__assets/aurora/* → packages/@c9up/aurora/dist/*
|
|
6
|
+
* GET /__assets/pages/* → resources/pages/*
|
|
7
7
|
*
|
|
8
8
|
* Config (in `config/aurora.ts`):
|
|
9
9
|
*
|
|
@@ -92,7 +92,7 @@ export default class AuroraProvider {
|
|
|
92
92
|
const router = this.app.container.resolve<ReamRouter>("router");
|
|
93
93
|
const manager = this.app.container.resolve<AuroraManager>(AuroraManager);
|
|
94
94
|
// Mount paths derive from the configured `assetsPrefix` (default
|
|
95
|
-
// `/
|
|
95
|
+
// `/__assets`) — set `config.aurora.assetsPrefix` to change the scheme.
|
|
96
96
|
router.get(
|
|
97
97
|
`${manager.auroraAssetPath}/*`,
|
|
98
98
|
adaptHandler(manager.auroraAssetsHandler()),
|
package/src/Pages.ts
CHANGED
|
@@ -33,7 +33,7 @@ export interface PagesConfig {
|
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* URL prefix the browser uses to fetch a page's compiled JS.
|
|
36
|
-
* Defaults to `/
|
|
36
|
+
* Defaults to `/__assets/pages`. A name `"Foo"` maps to
|
|
37
37
|
* `${urlPrefix}/Foo.js`.
|
|
38
38
|
*/
|
|
39
39
|
urlPrefix?: string;
|
|
@@ -63,7 +63,7 @@ export class Pages {
|
|
|
63
63
|
|
|
64
64
|
constructor(config: PagesConfig) {
|
|
65
65
|
this.root = config.root;
|
|
66
|
-
this.urlPrefix = (config.urlPrefix ?? "/
|
|
66
|
+
this.urlPrefix = (config.urlPrefix ?? "/__assets/pages").replace(/\/$/, "");
|
|
67
67
|
this.extension = config.extension ?? ".js";
|
|
68
68
|
}
|
|
69
69
|
|
package/src/browser.ts
CHANGED
|
@@ -50,10 +50,14 @@ export interface WebStorageOptions {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
53
|
+
* SSR-safe key/value store over `localStorage` / `sessionStorage`.
|
|
54
54
|
*
|
|
55
|
-
* -
|
|
56
|
-
*
|
|
55
|
+
* - {@link get} / {@link set} are a thin pass-through — raw strings, no JSON,
|
|
56
|
+
* exactly like the native `localStorage` API (so a token round-trips as-is,
|
|
57
|
+
* not double-encoded). {@link getJSON} / {@link setJSON} are the opt-in
|
|
58
|
+
* variants for structured values.
|
|
59
|
+
* - Reads return `null` on the server or a missing key (`getJSON` also on
|
|
60
|
+
* malformed JSON).
|
|
57
61
|
* - Writes swallow quota / private-mode errors so a full store never crashes
|
|
58
62
|
* the app (best-effort).
|
|
59
63
|
* - `prefix` namespaces keys; {@link keys} and {@link clear} stay scoped to it,
|
|
@@ -80,28 +84,52 @@ export class WebStorage {
|
|
|
80
84
|
return this.#prefix + key;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
/**
|
|
88
|
+
* Read the raw string at `key` — a thin, SSR-safe pass-through over
|
|
89
|
+
* `localStorage.getItem` (returns `null` on the server or a missing key).
|
|
90
|
+
* No JSON: what you {@link set} is what you get. Use {@link getJSON} for
|
|
91
|
+
* structured values.
|
|
92
|
+
*/
|
|
93
|
+
get(key: string): string | null {
|
|
84
94
|
const backend = this.#backend();
|
|
85
95
|
if (!backend) return null;
|
|
86
|
-
|
|
87
|
-
if (raw === null) return null;
|
|
88
|
-
try {
|
|
89
|
-
return JSON.parse(raw) as T;
|
|
90
|
-
} catch {
|
|
91
|
-
return null;
|
|
92
|
-
}
|
|
96
|
+
return backend.getItem(this.fullKey(key));
|
|
93
97
|
}
|
|
94
98
|
|
|
95
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Write the raw string `value` at `key` (SSR no-op; quota / private-mode
|
|
101
|
+
* errors are swallowed — best-effort). No encoding, like
|
|
102
|
+
* `localStorage.setItem`. Use {@link setJSON} for objects/arrays/etc.
|
|
103
|
+
*/
|
|
104
|
+
set(key: string, value: string): void {
|
|
96
105
|
const backend = this.#backend();
|
|
97
106
|
if (!backend) return;
|
|
98
107
|
try {
|
|
99
|
-
backend.setItem(this.fullKey(key),
|
|
108
|
+
backend.setItem(this.fullKey(key), value);
|
|
100
109
|
} catch {
|
|
101
110
|
// QuotaExceededError / Safari private mode — best-effort write.
|
|
102
111
|
}
|
|
103
112
|
}
|
|
104
113
|
|
|
114
|
+
/**
|
|
115
|
+
* Read `key` and JSON-parse it into `T`. Returns `null` on the server, a
|
|
116
|
+
* missing key, or malformed JSON. Pair with {@link setJSON}.
|
|
117
|
+
*/
|
|
118
|
+
getJSON<T>(key: string): T | null {
|
|
119
|
+
const raw = this.get(key);
|
|
120
|
+
if (raw === null) return null;
|
|
121
|
+
try {
|
|
122
|
+
return JSON.parse(raw) as T;
|
|
123
|
+
} catch {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** JSON-serialise `value` and store it at `key`. Pair with {@link getJSON}. */
|
|
129
|
+
setJSON(key: string, value: unknown): void {
|
|
130
|
+
this.set(key, JSON.stringify(value));
|
|
131
|
+
}
|
|
132
|
+
|
|
105
133
|
/** Whether `key` is present (and not the server). */
|
|
106
134
|
has(key: string): boolean {
|
|
107
135
|
const backend = this.#backend();
|
|
@@ -113,9 +141,9 @@ export class WebStorage {
|
|
|
113
141
|
this.#backend()?.removeItem(this.fullKey(key));
|
|
114
142
|
}
|
|
115
143
|
|
|
116
|
-
/** Read `key`, or compute + persist `factory()` on a miss, returning the
|
|
117
|
-
getOrSet
|
|
118
|
-
const existing = this.get
|
|
144
|
+
/** Read `key`, or compute + persist `factory()` on a miss, returning the string. */
|
|
145
|
+
getOrSet(key: string, factory: () => string): string {
|
|
146
|
+
const existing = this.get(key);
|
|
119
147
|
if (existing !== null) return existing;
|
|
120
148
|
const value = factory();
|
|
121
149
|
this.set(key, value);
|
|
@@ -183,12 +211,13 @@ export function persistedSignal<T>(
|
|
|
183
211
|
options: PersistedSignalOptions = {},
|
|
184
212
|
): Signal<T> {
|
|
185
213
|
const store = new WebStorage(options);
|
|
186
|
-
const stored = store.
|
|
214
|
+
const stored = store.getJSON<T>(key);
|
|
187
215
|
const sig = signal<T>(stored !== null ? stored : initial);
|
|
188
216
|
|
|
189
217
|
// Mirror every change back to storage; runs once immediately, then on change.
|
|
218
|
+
// JSON so any T (object, number, boolean, string) round-trips.
|
|
190
219
|
effect(() => {
|
|
191
|
-
store.
|
|
220
|
+
store.setJSON(key, sig());
|
|
192
221
|
});
|
|
193
222
|
|
|
194
223
|
if (
|
package/src/liveClient.ts
CHANGED
|
@@ -91,7 +91,7 @@ export interface LiveHttpPoster {
|
|
|
91
91
|
/**
|
|
92
92
|
* Build a {@link LiveClientTransport} from a relay client (SSE down) + an HTTP
|
|
93
93
|
* client (events up). `path` must match the server's `wireLiveEvents` route
|
|
94
|
-
* (default `/
|
|
94
|
+
* (default `/__live/event`). Keeps `liveClient` itself transport-agnostic.
|
|
95
95
|
*
|
|
96
96
|
* @example
|
|
97
97
|
* import { relay } from '@c9up/aurora/relay'
|
|
@@ -104,7 +104,7 @@ export function buildLiveTransport(
|
|
|
104
104
|
http: LiveHttpPoster,
|
|
105
105
|
options: { path?: string } = {},
|
|
106
106
|
): LiveClientTransport {
|
|
107
|
-
const path = options.path ?? "/
|
|
107
|
+
const path = options.path ?? "/__live/event";
|
|
108
108
|
return {
|
|
109
109
|
subscribe: (channel, handler) =>
|
|
110
110
|
relayClient.subscribe<SlotPatch[]>(channel, handler),
|
package/src/liveServer.ts
CHANGED
|
@@ -45,7 +45,7 @@ function isLiveEventBody(value: unknown): value is LiveEventBody {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
/** Default inbound-event route — keep the client transport's `path` in sync. */
|
|
48
|
-
export const DEFAULT_LIVE_EVENT_PATH = "/
|
|
48
|
+
export const DEFAULT_LIVE_EVENT_PATH = "/__live/event";
|
|
49
49
|
|
|
50
50
|
/**
|
|
51
51
|
* Register the inbound live-event route on the host router. Call once at boot
|
package/src/rpc.ts
CHANGED
|
@@ -34,17 +34,56 @@ export interface RpcClientOptions {
|
|
|
34
34
|
http?: HttpClient;
|
|
35
35
|
/** Default headers — only used when no `http` client is supplied. */
|
|
36
36
|
headers?: Record<string, string>;
|
|
37
|
+
/**
|
|
38
|
+
* Auto CSRF: read the `XSRF-TOKEN` cookie and echo it as `X-XSRF-TOKEN` on
|
|
39
|
+
* every call (Axios/Angular convention), so RPC POSTs pass blackhole's
|
|
40
|
+
* signed double-submit check when the route is cookie/session-authed. No-op
|
|
41
|
+
* outside the browser and when the cookie is absent. Default `true`.
|
|
42
|
+
* `/rpc` under a bearer (JWT) guard is CSRF-exempt, so the missing-cookie
|
|
43
|
+
* no-op is exactly right there too.
|
|
44
|
+
*/
|
|
45
|
+
xsrf?: boolean;
|
|
46
|
+
/** Cookie to read the CSRF token from. Default `XSRF-TOKEN`. */
|
|
47
|
+
xsrfCookieName?: string;
|
|
48
|
+
/** Header to echo the CSRF token in. Default `X-XSRF-TOKEN`. */
|
|
49
|
+
xsrfHeaderName?: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Read a cookie's raw value from `document.cookie`. Returns `undefined`
|
|
54
|
+
* server-side (no `document`) or when the cookie is absent. The value is sent
|
|
55
|
+
* verbatim — double-submit compares it byte-for-byte against the cookie, so it
|
|
56
|
+
* must not be decoded.
|
|
57
|
+
*/
|
|
58
|
+
function readCookie(name: string): string | undefined {
|
|
59
|
+
if (typeof document === "undefined") return undefined;
|
|
60
|
+
const prefix = `${name}=`;
|
|
61
|
+
for (const part of document.cookie.split(";")) {
|
|
62
|
+
const trimmed = part.trimStart();
|
|
63
|
+
if (trimmed.startsWith(prefix)) return trimmed.slice(prefix.length);
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
37
66
|
}
|
|
38
67
|
|
|
39
68
|
/**
|
|
40
69
|
* Create a JSON-RPC client bound to aurora's HttpClient transport. Inherits the
|
|
41
|
-
* supplied (or a fresh) HttpClient's base URL, auth headers, and timeouts
|
|
70
|
+
* supplied (or a fresh) HttpClient's base URL, auth headers, and timeouts, and
|
|
71
|
+
* (by default) auto-attaches the `X-XSRF-TOKEN` CSRF header from the cookie.
|
|
42
72
|
*/
|
|
43
73
|
export function createRpcClient(options: RpcClientOptions = {}): RpcClient {
|
|
44
74
|
const http = options.http ?? new HttpClient({ headers: options.headers });
|
|
75
|
+
const xsrfEnabled = options.xsrf ?? true;
|
|
76
|
+
const cookieName = options.xsrfCookieName ?? "XSRF-TOKEN";
|
|
77
|
+
const headerName = options.xsrfHeaderName ?? "X-XSRF-TOKEN";
|
|
45
78
|
return createCometRpcClient({
|
|
46
79
|
url: options.url,
|
|
47
|
-
transport: (url, body, { signal }) =>
|
|
48
|
-
|
|
80
|
+
transport: (url, body, { signal }) => {
|
|
81
|
+
let headers: Record<string, string> | undefined;
|
|
82
|
+
if (xsrfEnabled) {
|
|
83
|
+
const token = readCookie(cookieName);
|
|
84
|
+
if (token !== undefined) headers = { [headerName]: token };
|
|
85
|
+
}
|
|
86
|
+
return http.post<unknown>(url, body, { signal, headers });
|
|
87
|
+
},
|
|
49
88
|
});
|
|
50
89
|
}
|
package/src/server/renderPage.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* <head>
|
|
9
9
|
* ...
|
|
10
10
|
* <script type="importmap">
|
|
11
|
-
* { "imports": { "@c9up/aurora": "/
|
|
11
|
+
* { "imports": { "@c9up/aurora": "/__assets/aurora/index.js" } }
|
|
12
12
|
* </script>
|
|
13
13
|
* </head>
|
|
14
14
|
* <body>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* <script id="aurora-page-data" type="application/json">{…}</script>
|
|
17
17
|
* <script type="module">
|
|
18
18
|
* import { hydrate } from '@c9up/aurora'
|
|
19
|
-
* import Page from '/
|
|
19
|
+
* import Page from '/__assets/pages/ProjectPage.js'
|
|
20
20
|
* const data = JSON.parse(document.getElementById('aurora-page-data').textContent)
|
|
21
21
|
* hydrate(document.getElementById('aurora-root'), () => Page(data.props))
|
|
22
22
|
* </script>
|
|
@@ -45,7 +45,7 @@ export interface RenderHttpContext {
|
|
|
45
45
|
export interface RenderPageOptions {
|
|
46
46
|
/**
|
|
47
47
|
* Importmap entries injected into `<head>`. Defaults to mapping
|
|
48
|
-
* `@c9up/aurora` to `/
|
|
48
|
+
* `@c9up/aurora` to `/__assets/aurora/index.js`. Override to point
|
|
49
49
|
* at a different mount or to add app-side aliases.
|
|
50
50
|
*/
|
|
51
51
|
importmap?: Record<string, string>;
|
|
@@ -139,7 +139,7 @@ export async function renderPage<P>(
|
|
|
139
139
|
const body = renderToString(tree);
|
|
140
140
|
|
|
141
141
|
const importmap = {
|
|
142
|
-
"@c9up/aurora": "/
|
|
142
|
+
"@c9up/aurora": "/__assets/aurora/index.js",
|
|
143
143
|
...options.importmap,
|
|
144
144
|
};
|
|
145
145
|
const rootId = options.rootId ?? "aurora-root";
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* `serveAssets` — generic static-file handler exposed by aurora so an
|
|
3
3
|
* app can mount the runtime + the pages dist with a couple of routes:
|
|
4
4
|
*
|
|
5
|
-
* router.get('/
|
|
6
|
-
* router.get('/
|
|
5
|
+
* router.get('/__assets/aurora/*', serveAssets({ root: auroraDistPath }))
|
|
6
|
+
* router.get('/__assets/pages/*', serveAssets({ root: pagesPath }))
|
|
7
7
|
*
|
|
8
8
|
* The handler is framework-agnostic: it reads `ctx.request.param('*')`
|
|
9
9
|
* and writes to `ctx.response`. Any context that satisfies
|