@rpcbase/server 0.508.0 → 0.510.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +168 -12
- package/dist/index.js.map +1 -1
- package/dist/queryExecutor-Xh88REQn.js +345 -0
- package/dist/queryExecutor-Xh88REQn.js.map +1 -0
- package/dist/renderSSR.d.ts +2 -0
- package/dist/renderSSR.d.ts.map +1 -1
- package/dist/rts/index.d.ts.map +1 -1
- package/dist/rts/index.js +93 -87
- package/dist/rts/index.js.map +1 -1
- package/dist/rts/queryExecutor.d.ts +48 -0
- package/dist/rts/queryExecutor.d.ts.map +1 -0
- package/dist/rts/ssrHydration.d.ts +14 -0
- package/dist/rts/ssrHydration.d.ts.map +1 -0
- package/dist/ssrMiddleware.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import MongoStore from "connect-mongo";
|
|
|
4
4
|
import { createClient } from "redis";
|
|
5
5
|
import { MongoClient } from "mongodb";
|
|
6
6
|
import env from "@rpcbase/env";
|
|
7
|
-
import { initApiClient, SsrErrorFallback, SSR_ERROR_STATE_GLOBAL_KEY, serializeSsrErrorState } from "@rpcbase/client";
|
|
7
|
+
import { initApiClient, STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY, RtsSsrRuntimeProvider, SsrErrorFallback, SSR_ERROR_STATE_GLOBAL_KEY, serializeSsrErrorState } from "@rpcbase/client";
|
|
8
8
|
import { dirname, posix, sep } from "path";
|
|
9
9
|
import fs, { createReadStream, readFileSync } from "node:fs";
|
|
10
10
|
import { createInterface } from "node:readline";
|
|
@@ -16,11 +16,12 @@ import fsPromises from "node:fs/promises";
|
|
|
16
16
|
import inspector from "node:inspector";
|
|
17
17
|
import path from "node:path";
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
|
-
import { Transform } from "node:stream";
|
|
19
|
+
import { Writable, Transform } from "node:stream";
|
|
20
20
|
import { StrictMode, createElement } from "react";
|
|
21
21
|
import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
|
|
22
22
|
import { jsx } from "react/jsx-runtime";
|
|
23
23
|
import { createPath, matchRoutes, parsePath, createStaticRouter, StaticRouterProvider } from "@rpcbase/router";
|
|
24
|
+
import { r as resolveRtsRequestTenantId, i as isRtsRequestAuthorized, b as buildRtsAbilityFromRequest, n as normalizeRtsQueryOptions, a as runRtsQuery } from "./queryExecutor-Xh88REQn.js";
|
|
24
25
|
import { s } from "./email-DEw8keax.js";
|
|
25
26
|
function getDefaultExportFromCjs(x) {
|
|
26
27
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
@@ -4423,6 +4424,142 @@ async function applyRouteLoaders(req, dataRoutes) {
|
|
|
4423
4424
|
statusCode
|
|
4424
4425
|
};
|
|
4425
4426
|
}
|
|
4427
|
+
const DEFAULT_MAX_QUERIES = 32;
|
|
4428
|
+
const DEFAULT_MAX_DOCS_PER_QUERY = 500;
|
|
4429
|
+
const DEFAULT_MAX_SERIALIZED_BYTES = 200 * 1024;
|
|
4430
|
+
const makeRegistrationKey = (modelName, queryKey) => `${modelName}.${queryKey}`;
|
|
4431
|
+
const hasSessionUser = (req) => {
|
|
4432
|
+
const id = req.session?.user?.id;
|
|
4433
|
+
return typeof id === "string" && id.trim().length > 0;
|
|
4434
|
+
};
|
|
4435
|
+
const escapeForInlineScript = (value) => {
|
|
4436
|
+
return value.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
4437
|
+
};
|
|
4438
|
+
const createRtsSsrCollector = (req, opts) => {
|
|
4439
|
+
const maxQueries = DEFAULT_MAX_QUERIES;
|
|
4440
|
+
const maxDocsPerQuery = DEFAULT_MAX_DOCS_PER_QUERY;
|
|
4441
|
+
const maxSerializedBytes = DEFAULT_MAX_SERIALIZED_BYTES;
|
|
4442
|
+
const registrations = /* @__PURE__ */ new Map();
|
|
4443
|
+
const resolved = /* @__PURE__ */ new Map();
|
|
4444
|
+
const runtime = {
|
|
4445
|
+
registerQuery(query) {
|
|
4446
|
+
const modelName = typeof query.modelName === "string" ? query.modelName.trim() : "";
|
|
4447
|
+
const queryKey = typeof query.queryKey === "string" ? query.queryKey.trim() : "";
|
|
4448
|
+
if (!modelName || !queryKey) return;
|
|
4449
|
+
const key = makeRegistrationKey(modelName, queryKey);
|
|
4450
|
+
if (registrations.has(key)) return;
|
|
4451
|
+
if (registrations.size >= maxQueries) return;
|
|
4452
|
+
registrations.set(key, {
|
|
4453
|
+
modelName,
|
|
4454
|
+
queryKey,
|
|
4455
|
+
query: query.query ?? {},
|
|
4456
|
+
options: query.options ?? {}
|
|
4457
|
+
});
|
|
4458
|
+
},
|
|
4459
|
+
getQueryData(modelName, queryKey) {
|
|
4460
|
+
return resolved.get(makeRegistrationKey(modelName, queryKey));
|
|
4461
|
+
}
|
|
4462
|
+
};
|
|
4463
|
+
const resolve = async () => {
|
|
4464
|
+
if (!registrations.size) return null;
|
|
4465
|
+
const tenantId = resolveRtsRequestTenantId(req);
|
|
4466
|
+
if (!tenantId) return null;
|
|
4467
|
+
if (hasSessionUser(req) && !isRtsRequestAuthorized(req, tenantId)) {
|
|
4468
|
+
return null;
|
|
4469
|
+
}
|
|
4470
|
+
const { ability, userId } = await buildRtsAbilityFromRequest(req, tenantId);
|
|
4471
|
+
const queryEntries = [];
|
|
4472
|
+
for (const registration of registrations.values()) {
|
|
4473
|
+
const options = normalizeRtsQueryOptions(registration.options);
|
|
4474
|
+
try {
|
|
4475
|
+
const data = await runRtsQuery({
|
|
4476
|
+
tenantId,
|
|
4477
|
+
ability,
|
|
4478
|
+
modelName: registration.modelName,
|
|
4479
|
+
query: registration.query,
|
|
4480
|
+
options
|
|
4481
|
+
});
|
|
4482
|
+
const boundedData = maxDocsPerQuery > 0 ? data.slice(0, maxDocsPerQuery) : data;
|
|
4483
|
+
queryEntries.push({
|
|
4484
|
+
modelName: registration.modelName,
|
|
4485
|
+
queryKey: registration.queryKey,
|
|
4486
|
+
data: boundedData
|
|
4487
|
+
});
|
|
4488
|
+
} catch {
|
|
4489
|
+
continue;
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
if (!queryEntries.length) return null;
|
|
4493
|
+
const payload = {
|
|
4494
|
+
v: 1,
|
|
4495
|
+
tenantId,
|
|
4496
|
+
uid: userId,
|
|
4497
|
+
queries: []
|
|
4498
|
+
};
|
|
4499
|
+
for (const entry of queryEntries) {
|
|
4500
|
+
payload.queries.push(entry);
|
|
4501
|
+
const bytes = Buffer.byteLength(JSON.stringify(payload), "utf8");
|
|
4502
|
+
if (bytes > maxSerializedBytes) {
|
|
4503
|
+
payload.queries.pop();
|
|
4504
|
+
break;
|
|
4505
|
+
}
|
|
4506
|
+
}
|
|
4507
|
+
resolved.clear();
|
|
4508
|
+
for (const entry of payload.queries) {
|
|
4509
|
+
resolved.set(makeRegistrationKey(entry.modelName, entry.queryKey), entry.data);
|
|
4510
|
+
}
|
|
4511
|
+
return payload.queries.length ? payload : null;
|
|
4512
|
+
};
|
|
4513
|
+
const hasRegistrations = () => registrations.size > 0;
|
|
4514
|
+
return { runtime, hasRegistrations, resolve };
|
|
4515
|
+
};
|
|
4516
|
+
const renderRtsHydrationScript = (data) => {
|
|
4517
|
+
if (!data) return "";
|
|
4518
|
+
const serialized = escapeForInlineScript(JSON.stringify(data));
|
|
4519
|
+
return `<script>window.${STATIC_RPCBASE_RTS_HYDRATION_DATA_KEY}=${serialized};<\/script>`;
|
|
4520
|
+
};
|
|
4521
|
+
const RTS_SSR_PREPASS_TIMEOUT_MS = 4e3;
|
|
4522
|
+
const runRtsPrepass = async (element) => {
|
|
4523
|
+
return await new Promise((resolve) => {
|
|
4524
|
+
let isDone = false;
|
|
4525
|
+
let timeoutId = null;
|
|
4526
|
+
const finish = (ok) => {
|
|
4527
|
+
if (isDone) return;
|
|
4528
|
+
isDone = true;
|
|
4529
|
+
if (timeoutId) {
|
|
4530
|
+
clearTimeout(timeoutId);
|
|
4531
|
+
}
|
|
4532
|
+
resolve(ok);
|
|
4533
|
+
};
|
|
4534
|
+
const sink = new Writable({
|
|
4535
|
+
write(_chunk, _encoding, callback) {
|
|
4536
|
+
callback();
|
|
4537
|
+
}
|
|
4538
|
+
});
|
|
4539
|
+
sink.on("finish", () => {
|
|
4540
|
+
finish(true);
|
|
4541
|
+
});
|
|
4542
|
+
sink.on("error", () => {
|
|
4543
|
+
finish(false);
|
|
4544
|
+
});
|
|
4545
|
+
const { pipe, abort } = renderToPipeableStream(element, {
|
|
4546
|
+
onAllReady() {
|
|
4547
|
+
if (isDone) return;
|
|
4548
|
+
pipe(sink);
|
|
4549
|
+
},
|
|
4550
|
+
onShellError() {
|
|
4551
|
+
finish(false);
|
|
4552
|
+
abort();
|
|
4553
|
+
},
|
|
4554
|
+
onError() {
|
|
4555
|
+
}
|
|
4556
|
+
});
|
|
4557
|
+
timeoutId = setTimeout(() => {
|
|
4558
|
+
abort();
|
|
4559
|
+
finish(false);
|
|
4560
|
+
}, RTS_SSR_PREPASS_TIMEOUT_MS);
|
|
4561
|
+
});
|
|
4562
|
+
};
|
|
4426
4563
|
async function renderSSR(req, dataRoutes) {
|
|
4427
4564
|
const routerContext = await applyRouteLoaders(req, dataRoutes);
|
|
4428
4565
|
const isMatched = routerContext.matches.length > 0;
|
|
@@ -4433,7 +4570,8 @@ async function renderSSR(req, dataRoutes) {
|
|
|
4433
4570
|
statusCode: routerContext.statusCode ?? routerContext.redirectResponse.status ?? 302,
|
|
4434
4571
|
redirectResponse: routerContext.redirectResponse,
|
|
4435
4572
|
redirectRouteId: routerContext.redirectRouteId,
|
|
4436
|
-
redirectRoutePath: routerContext.redirectRoutePath
|
|
4573
|
+
redirectRoutePath: routerContext.redirectRoutePath,
|
|
4574
|
+
rtsHydrationData: null
|
|
4437
4575
|
};
|
|
4438
4576
|
}
|
|
4439
4577
|
if (routerContext.errors) {
|
|
@@ -4468,18 +4606,34 @@ async function renderSSR(req, dataRoutes) {
|
|
|
4468
4606
|
throw error;
|
|
4469
4607
|
}
|
|
4470
4608
|
}
|
|
4471
|
-
const
|
|
4472
|
-
|
|
4473
|
-
|
|
4474
|
-
|
|
4475
|
-
|
|
4476
|
-
|
|
4609
|
+
const buildElement = (runtime2) => {
|
|
4610
|
+
const router = createStaticRouter(dataRoutes, routerContext);
|
|
4611
|
+
const app = /* @__PURE__ */ jsx(
|
|
4612
|
+
StaticRouterProvider,
|
|
4613
|
+
{
|
|
4614
|
+
router,
|
|
4615
|
+
context: routerContext
|
|
4616
|
+
}
|
|
4617
|
+
);
|
|
4618
|
+
return /* @__PURE__ */ jsx(StrictMode, { children: runtime2 ? /* @__PURE__ */ jsx(RtsSsrRuntimeProvider, { value: runtime2, children: app }) : app });
|
|
4619
|
+
};
|
|
4620
|
+
const tenantId = resolveRtsRequestTenantId(req);
|
|
4621
|
+
let rtsHydrationData = null;
|
|
4622
|
+
let runtime = null;
|
|
4623
|
+
if (tenantId) {
|
|
4624
|
+
const collector = createRtsSsrCollector(req);
|
|
4625
|
+
const prepassOk = await runRtsPrepass(buildElement(collector.runtime));
|
|
4626
|
+
if (prepassOk && collector.hasRegistrations()) {
|
|
4627
|
+
rtsHydrationData = await collector.resolve();
|
|
4477
4628
|
}
|
|
4478
|
-
|
|
4629
|
+
runtime = collector.runtime;
|
|
4630
|
+
}
|
|
4631
|
+
const element = buildElement(runtime);
|
|
4479
4632
|
return {
|
|
4480
4633
|
element,
|
|
4481
4634
|
isMatched,
|
|
4482
|
-
statusCode: routerContext.statusCode ?? (routerContext.errors ? 500 : 200)
|
|
4635
|
+
statusCode: routerContext.statusCode ?? (routerContext.errors ? 500 : 200),
|
|
4636
|
+
rtsHydrationData
|
|
4483
4637
|
};
|
|
4484
4638
|
}
|
|
4485
4639
|
const ABORT_DELAY_MS = 1e4;
|
|
@@ -4630,6 +4784,7 @@ const ssrMiddleware = ({
|
|
|
4630
4784
|
element,
|
|
4631
4785
|
isMatched,
|
|
4632
4786
|
statusCode,
|
|
4787
|
+
rtsHydrationData,
|
|
4633
4788
|
redirectResponse,
|
|
4634
4789
|
redirectRouteId,
|
|
4635
4790
|
redirectRoutePath
|
|
@@ -4697,9 +4852,10 @@ const ssrMiddleware = ({
|
|
|
4697
4852
|
});
|
|
4698
4853
|
const start = htmlStart;
|
|
4699
4854
|
const end = htmlEnd;
|
|
4855
|
+
const rtsHydrationScript = renderRtsHydrationScript(rtsHydrationData);
|
|
4700
4856
|
res.write(start);
|
|
4701
4857
|
transformStream.on("finish", () => {
|
|
4702
|
-
res.end(end);
|
|
4858
|
+
res.end(`${rtsHydrationScript}${end}`);
|
|
4703
4859
|
});
|
|
4704
4860
|
pipe(transformStream);
|
|
4705
4861
|
}
|