@proteinjs/server 3.5.1 → 3.5.3
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 +22 -0
- package/dist/generated/index.js +1 -1
- package/dist/generated/index.js.map +1 -1
- package/dist/src/NodeSessionDataStorage.d.ts +11 -0
- package/dist/src/NodeSessionDataStorage.d.ts.map +1 -1
- package/dist/src/NodeSessionDataStorage.js +16 -0
- package/dist/src/NodeSessionDataStorage.js.map +1 -1
- package/dist/src/loadRoutes.js +7 -0
- package/dist/src/loadRoutes.js.map +1 -1
- package/dist/src/routes/reactApp.d.ts.map +1 -1
- package/dist/src/routes/reactApp.js +7 -0
- package/dist/src/routes/reactApp.js.map +1 -1
- package/generated/index.ts +1 -1
- package/package.json +3 -3
- package/src/NodeSessionDataStorage.ts +18 -0
- package/src/loadRoutes.ts +7 -0
- package/src/routes/reactApp.ts +7 -0
- package/test/fixture/gracefulShutdownServer.js +4 -0
- package/test/htmlCacheControl.test.ts +129 -0
- package/test/sessionDataRequestBoundary.test.ts +51 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [3.5.3](https://github.com/proteinjs/server/compare/@proteinjs/server@3.5.2...@proteinjs/server@3.5.3) (2026-09-01)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* **server:** the react-app HTML response says Cache-Control: no-cache — the page always revalidates (found live 2026-09-01, the mobile-app stale-page investigation). The '*' route sent HTML with an ETag but NO Cache-Control: with no explicit policy, HTTP heuristic caching applies (RFC 9111 §4.2.2) and WKWebView in particular can serve the cached page without revalidating — across app force-quits — pinning stale bundle POINTERS long after a deploy. The hashed bundles under /static keep their long cache (a fresh page always points at fresh hashes); the PAGE must always revalidate. no-cache + the ETag express already stamps = a cheap 304 on every load, never a stale page. Red-before-green: htmlCacheControl.test.ts (front door — a real spawned server, the LB's X-Forwarded-Proto topology) read cache-control undefined at the pre-fix dist (2 red), green after; / and /chat both pinned (the catch-all is the one HTML owner). Fixture gains a bundlePaths pointer so the react-app route serves its page; full estate 5 suites / 11 green (9 pre-existing). ([076536f](https://github.com/proteinjs/server/commit/076536f158ce1c91b073061345fe7aae802b4f38))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
## [3.5.2](https://github.com/proteinjs/server/compare/@proteinjs/server@3.5.1...@proteinjs/server@3.5.2) (2026-08-27)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* a request always seeds its OWN session context — kills the keep-alive session-bag bleed ([24f84e3](https://github.com/proteinjs/server/commit/24f84e38cdc4317e5e2c5ef819ed8d58289cfc5b))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
## [3.5.1](https://github.com/proteinjs/server/compare/@proteinjs/server@3.5.0...@proteinjs/server@3.5.1) (2026-08-19)
|
|
7
29
|
|
|
8
30
|
|
package/dist/generated/index.js
CHANGED
|
@@ -33,7 +33,7 @@ require("react-helmet");
|
|
|
33
33
|
require("socket.io");
|
|
34
34
|
require("typescript");
|
|
35
35
|
/** Generate Source Graph */
|
|
36
|
-
var sourceGraph = "{\"options\":{\"directed\":true,\"multigraph\":false,\"compound\":false},\"nodes\":[{\"v\":\"@proteinjs/server/NodeGlobalDataStorage\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"NodeGlobalDataStorage\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/NodeGlobalDataStorage.ts\",\"qualifiedName\":\"@proteinjs/server/NodeGlobalDataStorage\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"HOOK_INITIALIZED\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"GLOBAL_DATA\",\"type\":{\"packageName\":\"@proteinjs/server\",\"name\":\"{ [id: string]: GlobalData }\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server/{ [id: string]: GlobalData }\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"environment\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"setData\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"data\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalData\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"getData\",\"returnType\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalData\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]},{\"name\":\"initHook\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalDataStorage\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalDataStorage\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/server-api/GlobalDataStorage\"},{\"v\":\"@proteinjs/server/NodeSessionDataStorage\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"NodeSessionDataStorage\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/NodeSessionDataStorage.ts\",\"qualifiedName\":\"@proteinjs/server/NodeSessionDataStorage\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"HOOK_INITIALIZED\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"SESSION_DATA\",\"type\":{\"packageName\":\"@proteinjs/server\",\"name\":\"{ [id: string]: SessionData }\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server/{ [id: string]: SessionData }\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"environment\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"setData\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"data\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"SessionData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/SessionData\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"getData\",\"returnType\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"SessionData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/SessionData\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]},{\"name\":\"initHook\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"SessionDataStorage\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/SessionDataStorage\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/server-api/SessionDataStorage\"},{\"v\":\"@proteinjs/server/ExtendedIncomingMessage\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"ExtendedIncomingMessage\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts\",\"qualifiedName\":\"@proteinjs/server/ExtendedIncomingMessage\",\"properties\":[{\"name\":\"sessionID\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"user\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[],\"typeParameters\":[],\"directParents\":[{\"packageName\":\"http\",\"name\":\"IncomingMessage\",\"filePath\":null,\"qualifiedName\":\"http/IncomingMessage\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"sourceType\":3}},{\"v\":\"http/IncomingMessage\"},{\"v\":\"@proteinjs/server/ExtendedSocket\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"ExtendedSocket\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts\",\"qualifiedName\":\"@proteinjs/server/ExtendedSocket\",\"properties\":[{\"name\":\"request\",\"type\":{\"packageName\":\"@proteinjs/server\",\"name\":\"ExtendedIncomingMessage\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server/ExtendedIncomingMessage\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[],\"typeParameters\":[],\"directParents\":[{\"packageName\":\"socket.io\",\"name\":\"Socket\",\"filePath\":null,\"qualifiedName\":\"socket.io/Socket\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"sourceType\":3}},{\"v\":\"socket.io/Socket\"},{\"v\":\"@proteinjs/server/environmentCache\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"environmentCache\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/envCache.ts\",\"qualifiedName\":\"@proteinjs/server/environmentCache\",\"type\":{\"packageName\":\"\",\"name\":\"GlobalDataCache<Env>\",\"filePath\":null,\"qualifiedName\":\"/GlobalDataCache<Env>\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalDataCache\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalDataCache\",\"typeParameters\":[\"@proteinjs/server-api/Env\"],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/server-api/GlobalDataCache\"},{\"v\":\"@proteinjs/server/healthCheck\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"healthCheck\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/routes/healthCheck.ts\",\"qualifiedName\":\"@proteinjs/server/healthCheck\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/server-api/Route\"}],\"edges\":[{\"v\":\"@proteinjs/server/NodeGlobalDataStorage\",\"w\":\"@proteinjs/server-api/GlobalDataStorage\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/server/NodeSessionDataStorage\",\"w\":\"@proteinjs/server-api/SessionDataStorage\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/server/ExtendedIncomingMessage\",\"w\":\"http/IncomingMessage\",\"value\":\"extends interface\"},{\"v\":\"@proteinjs/server/ExtendedSocket\",\"w\":\"socket.io/Socket\",\"value\":\"extends interface\"},{\"v\":\"@proteinjs/server/environmentCache\",\"w\":\"@proteinjs/server-api/GlobalDataCache\",\"value\":\"has type\"},{\"v\":\"@proteinjs/server/healthCheck\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"}]}";
|
|
36
|
+
var sourceGraph = "{\"options\":{\"directed\":true,\"multigraph\":false,\"compound\":false},\"nodes\":[{\"v\":\"@proteinjs/server/NodeGlobalDataStorage\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"NodeGlobalDataStorage\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/NodeGlobalDataStorage.ts\",\"qualifiedName\":\"@proteinjs/server/NodeGlobalDataStorage\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"HOOK_INITIALIZED\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"GLOBAL_DATA\",\"type\":{\"packageName\":\"@proteinjs/server\",\"name\":\"{ [id: string]: GlobalData }\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server/{ [id: string]: GlobalData }\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"environment\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"setData\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"data\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalData\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"getData\",\"returnType\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalData\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]},{\"name\":\"initHook\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalDataStorage\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalDataStorage\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/server-api/GlobalDataStorage\"},{\"v\":\"@proteinjs/server/NodeSessionDataStorage\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"NodeSessionDataStorage\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/NodeSessionDataStorage.ts\",\"qualifiedName\":\"@proteinjs/server/NodeSessionDataStorage\",\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"properties\":[{\"name\":\"HOOK_INITIALIZED\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"SESSION_DATA\",\"type\":{\"packageName\":\"@proteinjs/server\",\"name\":\"{ [id: string]: SessionData }\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server/{ [id: string]: SessionData }\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":true,\"visibility\":\"private\"},{\"name\":\"environment\",\"type\":null,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[{\"name\":\"setData\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[{\"name\":\"data\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"SessionData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/SessionData\",\"typeParameters\":null,\"directParents\":null}}]},{\"name\":\"getData\",\"returnType\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"SessionData\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/SessionData\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]},{\"name\":\"clearData\",\"returnType\":{\"packageName\":\"\",\"name\":\"void\",\"filePath\":null,\"qualifiedName\":\"/void\",\"typeParameters\":null,\"directParents\":null},\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\",\"parameters\":[]},{\"name\":\"initHook\",\"returnType\":null,\"isAsync\":false,\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"private\",\"parameters\":[]}],\"typeParameters\":[],\"directParentInterfaces\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"SessionDataStorage\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/SessionDataStorage\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"directParentClasses\":[],\"sourceType\":2}},{\"v\":\"@proteinjs/server-api/SessionDataStorage\"},{\"v\":\"@proteinjs/server/ExtendedIncomingMessage\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"ExtendedIncomingMessage\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts\",\"qualifiedName\":\"@proteinjs/server/ExtendedIncomingMessage\",\"properties\":[{\"name\":\"sessionID\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"},{\"name\":\"user\",\"type\":{\"packageName\":\"\",\"name\":\"string\",\"filePath\":null,\"qualifiedName\":\"/string\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":true,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[],\"typeParameters\":[],\"directParents\":[{\"packageName\":\"http\",\"name\":\"IncomingMessage\",\"filePath\":null,\"qualifiedName\":\"http/IncomingMessage\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"sourceType\":3}},{\"v\":\"http/IncomingMessage\"},{\"v\":\"@proteinjs/server/ExtendedSocket\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"ExtendedSocket\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts\",\"qualifiedName\":\"@proteinjs/server/ExtendedSocket\",\"properties\":[{\"name\":\"request\",\"type\":{\"packageName\":\"@proteinjs/server\",\"name\":\"ExtendedIncomingMessage\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server/ExtendedIncomingMessage\",\"typeParameters\":null,\"directParents\":null},\"isOptional\":false,\"isAbstract\":false,\"isStatic\":false,\"visibility\":\"public\"}],\"methods\":[],\"typeParameters\":[],\"directParents\":[{\"packageName\":\"socket.io\",\"name\":\"Socket\",\"filePath\":null,\"qualifiedName\":\"socket.io/Socket\",\"properties\":[],\"methods\":[],\"typeParameters\":[],\"directParents\":[]}],\"sourceType\":3}},{\"v\":\"socket.io/Socket\"},{\"v\":\"@proteinjs/server/environmentCache\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"environmentCache\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/envCache.ts\",\"qualifiedName\":\"@proteinjs/server/environmentCache\",\"type\":{\"packageName\":\"\",\"name\":\"GlobalDataCache<Env>\",\"filePath\":null,\"qualifiedName\":\"/GlobalDataCache<Env>\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"GlobalDataCache\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/GlobalDataCache\",\"typeParameters\":[\"@proteinjs/server-api/Env\"],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/server-api/GlobalDataCache\"},{\"v\":\"@proteinjs/server/healthCheck\",\"value\":{\"packageName\":\"@proteinjs/server\",\"name\":\"healthCheck\",\"filePath\":\"/home/runner/work/server/server/packages/server/src/routes/healthCheck.ts\",\"qualifiedName\":\"@proteinjs/server/healthCheck\",\"type\":{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":[{\"packageName\":\"@proteinjs/server-api\",\"name\":\"Route\",\"filePath\":null,\"qualifiedName\":\"@proteinjs/server-api/Route\",\"typeParameters\":[],\"directParents\":null}]},\"isExported\":true,\"isConst\":true,\"sourceType\":0}},{\"v\":\"@proteinjs/server-api/Route\"}],\"edges\":[{\"v\":\"@proteinjs/server/NodeGlobalDataStorage\",\"w\":\"@proteinjs/server-api/GlobalDataStorage\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/server/NodeSessionDataStorage\",\"w\":\"@proteinjs/server-api/SessionDataStorage\",\"value\":\"implements interface\"},{\"v\":\"@proteinjs/server/ExtendedIncomingMessage\",\"w\":\"http/IncomingMessage\",\"value\":\"extends interface\"},{\"v\":\"@proteinjs/server/ExtendedSocket\",\"w\":\"socket.io/Socket\",\"value\":\"extends interface\"},{\"v\":\"@proteinjs/server/environmentCache\",\"w\":\"@proteinjs/server-api/GlobalDataCache\",\"value\":\"has type\"},{\"v\":\"@proteinjs/server/healthCheck\",\"w\":\"@proteinjs/server-api/Route\",\"value\":\"has type\"}]}";
|
|
37
37
|
/** Generate Source Links */
|
|
38
38
|
var NodeGlobalDataStorage_1 = require("../src/NodeGlobalDataStorage");
|
|
39
39
|
var NodeSessionDataStorage_1 = require("../src/NodeSessionDataStorage");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../generated/index.ts"],"names":[],"mappings":";AAAA,oCAAoC;;;;;;;;;;;;;;;;AAEpC,4BAA0B;AAC1B,6BAA2B;AAC3B,iCAA+B;AAC/B,iCAA+B;AAC/B,gCAA8B;AAC9B,uBAAqB;AACrB,yBAAuB;AACvB,kBAAgB;AAChB,mBAAiB;AACjB,2BAAyB;AACzB,oBAAkB;AAClB,0BAAwB;AACxB,mBAAiB;AACjB,iBAAe;AACf,wBAAsB;AACtB,qBAAmB;AACnB,sBAAoB;AAGpB,4BAA4B;AAE5B,IAAM,WAAW,GAAG,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../generated/index.ts"],"names":[],"mappings":";AAAA,oCAAoC;;;;;;;;;;;;;;;;AAEpC,4BAA0B;AAC1B,6BAA2B;AAC3B,iCAA+B;AAC/B,iCAA+B;AAC/B,gCAA8B;AAC9B,uBAAqB;AACrB,yBAAuB;AACvB,kBAAgB;AAChB,mBAAiB;AACjB,2BAAyB;AACzB,oBAAkB;AAClB,0BAAwB;AACxB,mBAAiB;AACjB,iBAAe;AACf,wBAAsB;AACtB,qBAAmB;AACnB,sBAAoB;AAGpB,4BAA4B;AAE5B,IAAM,WAAW,GAAG,y8RAAy8R,CAAC;AAG99R,4BAA4B;AAE5B,sEAAqE;AACrE,wEAAuE;AACvE,4CAAmD;AACnD,yDAAwD;AAExD,IAAM,WAAW,GAAG;IACnB,yCAAyC,EAAE,6CAAqB;IAChE,0CAA0C,EAAE,+CAAsB;IAClE,oCAAoC,EAAE,2BAAgB;IACtD,+BAA+B,EAAE,yBAAW;CAC5C,CAAC;AAGF,kCAAkC;AAElC,oDAAyD;AACzD,6BAAgB,CAAC,KAAK,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;AAGjD,2CAAyB"}
|
|
@@ -5,6 +5,17 @@ export declare class NodeSessionDataStorage implements SessionDataStorage {
|
|
|
5
5
|
environment: "node";
|
|
6
6
|
setData(data: SessionData): void;
|
|
7
7
|
getData(): SessionData;
|
|
8
|
+
/**
|
|
9
|
+
* Drop the CURRENT async id's entry so the next `setData` seeds fresh. The init hook copies
|
|
10
|
+
* a populated bag onto every descendant async resource and `setData` is first-write-wins, so
|
|
11
|
+
* a request dispatched inside a PREVIOUS request's lineage (keep-alive socket reuse — the
|
|
12
|
+
* normal case under a 620s keepAliveTimeout) would otherwise run as the prior request's user
|
|
13
|
+
* (observed 2026-08-26: /dev/login account switch, home greeting server-rendered with the
|
|
14
|
+
* previous account's name). Scoped to the current id on purpose: in-flight hops of the
|
|
15
|
+
* previous request keep their own already-copied entries, and descendants created after the
|
|
16
|
+
* re-seed inherit the fresh bag.
|
|
17
|
+
*/
|
|
18
|
+
clearData(): void;
|
|
8
19
|
private initHook;
|
|
9
20
|
}
|
|
10
21
|
//# sourceMappingURL=NodeSessionDataStorage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeSessionDataStorage.d.ts","sourceRoot":"","sources":["../../src/NodeSessionDataStorage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAExE,qBAAa,sBAAuB,YAAW,kBAAkB;IAC/D,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAqC;IACzE,WAAW,SAAoB;IAE/B,OAAO,CAAC,IAAI,EAAE,WAAW;IAYzB,OAAO,IAAI,WAAW;IAQtB,OAAO,CAAC,QAAQ;CAiBjB"}
|
|
1
|
+
{"version":3,"file":"NodeSessionDataStorage.d.ts","sourceRoot":"","sources":["../../src/NodeSessionDataStorage.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAExE,qBAAa,sBAAuB,YAAW,kBAAkB;IAC/D,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAS;IACxC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAqC;IACzE,WAAW,SAAoB;IAE/B,OAAO,CAAC,IAAI,EAAE,WAAW;IAYzB,OAAO,IAAI,WAAW;IAQtB;;;;;;;;;OASG;IACH,SAAS,IAAI,IAAI;IAQjB,OAAO,CAAC,QAAQ;CAiBjB"}
|
|
@@ -24,6 +24,22 @@ var NodeSessionDataStorage = /** @class */ (function () {
|
|
|
24
24
|
}
|
|
25
25
|
return NodeSessionDataStorage.SESSION_DATA[async_hooks_1.default.executionAsyncId()];
|
|
26
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Drop the CURRENT async id's entry so the next `setData` seeds fresh. The init hook copies
|
|
29
|
+
* a populated bag onto every descendant async resource and `setData` is first-write-wins, so
|
|
30
|
+
* a request dispatched inside a PREVIOUS request's lineage (keep-alive socket reuse — the
|
|
31
|
+
* normal case under a 620s keepAliveTimeout) would otherwise run as the prior request's user
|
|
32
|
+
* (observed 2026-08-26: /dev/login account switch, home greeting server-rendered with the
|
|
33
|
+
* previous account's name). Scoped to the current id on purpose: in-flight hops of the
|
|
34
|
+
* previous request keep their own already-copied entries, and descendants created after the
|
|
35
|
+
* re-seed inherit the fresh bag.
|
|
36
|
+
*/
|
|
37
|
+
NodeSessionDataStorage.prototype.clearData = function () {
|
|
38
|
+
if (!NodeSessionDataStorage.HOOK_INITIALIZED) {
|
|
39
|
+
this.initHook();
|
|
40
|
+
}
|
|
41
|
+
delete NodeSessionDataStorage.SESSION_DATA[async_hooks_1.default.executionAsyncId()];
|
|
42
|
+
};
|
|
27
43
|
NodeSessionDataStorage.prototype.initHook = function () {
|
|
28
44
|
async_hooks_1.default
|
|
29
45
|
.createHook({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NodeSessionDataStorage.js","sourceRoot":"","sources":["../../src/NodeSessionDataStorage.ts"],"names":[],"mappings":";;;;;;AAAA,4DAAqC;AAGrC;IAAA;QAGE,gBAAW,GAAG,MAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"NodeSessionDataStorage.js","sourceRoot":"","sources":["../../src/NodeSessionDataStorage.ts"],"names":[],"mappings":";;;;;;AAAA,4DAAqC;AAGrC;IAAA;QAGE,gBAAW,GAAG,MAAgB,CAAC;IAyDjC,CAAC;IAvDC,wCAAO,GAAP,UAAQ,IAAiB;QACvB,IAAI,CAAC,sBAAsB,CAAC,gBAAgB,EAAE;YAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;SACjB;QAED,IAAI,sBAAsB,CAAC,YAAY,CAAC,qBAAU,CAAC,gBAAgB,EAAE,CAAC,EAAE;YACtE,OAAO;SACR;QAED,sBAAsB,CAAC,YAAY,CAAC,qBAAU,CAAC,gBAAgB,EAAE,CAAC,GAAG,IAAI,CAAC;IAC5E,CAAC;IAED,wCAAO,GAAP;QACE,IAAI,CAAC,sBAAsB,CAAC,gBAAgB,EAAE;YAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;SACjB;QAED,OAAO,sBAAsB,CAAC,YAAY,CAAC,qBAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED;;;;;;;;;OASG;IACH,0CAAS,GAAT;QACE,IAAI,CAAC,sBAAsB,CAAC,gBAAgB,EAAE;YAC5C,IAAI,CAAC,QAAQ,EAAE,CAAC;SACjB;QAED,OAAO,sBAAsB,CAAC,YAAY,CAAC,qBAAU,CAAC,gBAAgB,EAAE,CAAC,CAAC;IAC5E,CAAC;IAEO,yCAAQ,GAAhB;QACE,qBAAU;aACP,UAAU,CAAC;YACV,IAAI,EAAE,UAAC,OAAe,EAAE,IAAY,EAAE,cAAsB,EAAE,QAAgB;gBAC5E,IAAI,CAAC,sBAAsB,CAAC,YAAY,CAAC,cAAc,CAAC,EAAE;oBACxD,OAAO;iBACR;gBAED,sBAAsB,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,sBAAsB,CAAC,YAAY,CAAC,cAAc,CAAC,CAAC;YACrG,CAAC;YACD,OAAO,EAAE,UAAC,OAAe;gBACvB,OAAO,sBAAsB,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;YACtD,CAAC;SACF,CAAC;aACD,MAAM,EAAE,CAAC;QACZ,sBAAsB,CAAC,gBAAgB,GAAG,IAAI,CAAC;IACjD,CAAC;IA1Dc,uCAAgB,GAAG,KAAK,AAAR,CAAS;IAChB,mCAAY,GAAkC,EAAE,AAApC,CAAqC;IA0D3E,6BAAC;CAAA,AA5DD,IA4DC;AA5DY,wDAAsB"}
|
package/dist/src/loadRoutes.js
CHANGED
|
@@ -126,6 +126,13 @@ function wrapRoute(route, config) {
|
|
|
126
126
|
_i++;
|
|
127
127
|
return [3 /*break*/, 1];
|
|
128
128
|
case 4:
|
|
129
|
+
// A request always seeds ITS OWN session context: on a reused keep-alive socket this
|
|
130
|
+
// dispatch is born inside the previous request's async lineage (the storage's init hook
|
|
131
|
+
// copies bags onto descendants), and first-write-wins setData would silently drop this
|
|
132
|
+
// request's seed — the request then runs (and server-renders proteinjs.sessionData) as
|
|
133
|
+
// the PRIOR request's user (observed 2026-08-26: stale home greeting after a /dev/login
|
|
134
|
+
// account switch; roster correct, greeting wrong). Clear the inherited entry first.
|
|
135
|
+
server_api_1.Session.clearData();
|
|
129
136
|
server_api_1.Session.setData(sessionData);
|
|
130
137
|
if (shouldLogRequest(request, config)) {
|
|
131
138
|
message = "Started ".concat(request.originalUrl);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loadRoutes.js","sourceRoot":"","sources":["../../src/loadRoutes.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"loadRoutes.js","sourceRoot":"","sources":["../../src/loadRoutes.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactApp.d.ts","sourceRoot":"","sources":["../../../src/routes/reactApp.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAA4B,MAAM,uBAAuB,CAAC;AAU/E,eAAO,MAAM,cAAc,iBAAkB,YAAY;;;yBAI1B,GAAG,YAAY,GAAG,KAAG,QAAQ,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"reactApp.d.ts","sourceRoot":"","sources":["../../../src/routes/reactApp.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAA4B,MAAM,uBAAuB,CAAC;AAU/E,eAAO,MAAM,cAAc,iBAAkB,YAAY;;;yBAI1B,GAAG,YAAY,GAAG,KAAG,QAAQ,IAAI,CAAC;CAsChE,CAAC"}
|
|
@@ -67,6 +67,13 @@ var createReactApp = function (serverConfig) {
|
|
|
67
67
|
throw new Error("ServerConfig.bundlePath or ServerConfig.bundlesDir must be provided to serve a react app");
|
|
68
68
|
}
|
|
69
69
|
helmet = react_helmet_1.default.renderStatic();
|
|
70
|
+
// The page must ALWAYS revalidate (found live 2026-09-01, the mobile-app stale-page
|
|
71
|
+
// investigation): without an explicit policy, HTTP heuristic caching applies (RFC 9111
|
|
72
|
+
// §4.2.2) — WKWebView in particular can serve the cached page without revalidating,
|
|
73
|
+
// across app force-quits, pinning stale bundle POINTERS long after a deploy. The hashed
|
|
74
|
+
// bundles under /static keep their long cache (a fresh page always points at fresh
|
|
75
|
+
// hashes); `no-cache` + the ETag express already stamps = a cheap 304 on every load.
|
|
76
|
+
response.set('Cache-Control', 'no-cache');
|
|
70
77
|
_b = (_a = response).send;
|
|
71
78
|
_e = (_d = "<!DOCTYPE html>\n <html ".concat(helmet.htmlAttributes, ">\n <head>\n <meta charset='utf-8' />\n <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1, viewport-fit=cover'>\n <meta name='theme-color' content='").concat(THEME_COLOR_LIGHT, "' media='(prefers-color-scheme: light)'>\n <meta name='theme-color' content='").concat(THEME_COLOR_DARK, "' media='(prefers-color-scheme: dark)'>\n <link href='").concat(((_j = serverConfig.staticContent) === null || _j === void 0 ? void 0 : _j.faviconPath) ? path_1.default.join('/static/', serverConfig.staticContent.faviconPath) : '', "' rel='icon' type='image/png' />\n ").concat(helmet.title.toString(), "\n ").concat(helmet.meta.toString(), "\n ").concat(helmet.link.toString(), "\n </head>\n <body ").concat(helmet.bodyAttributes.toString(), ">\n <div id='app'></div>\n <script>proteinjs = {};</script>\n ")).concat;
|
|
72
79
|
return [4 /*yield*/, serverRenderedScriptTags()];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reactApp.js","sourceRoot":"","sources":["../../../src/routes/reactApp.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8CAAwB;AACxB,8DAAuC;AACvC,oDAA+E;AAC/E,kDAA0C;AAC1C,oDAAmD;AAEnD,iFAAiF;AACjF,mFAAmF;AACnF,kEAAkE;AAClE,IAAM,iBAAiB,GAAG,SAAS,CAAC;AACpC,IAAM,gBAAgB,GAAG,SAAS,CAAC;AAE5B,IAAM,cAAc,GAAG,UAAC,YAA0B;IACvD,OAAO;QACL,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,KAAc;QACtB,SAAS,EAAE,UAAO,OAAY,EAAE,QAAa;;;;;;wBAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;4BACtC,sBAAO;yBACR;wBAED,IAAI,CAAC,CAAC,CAAA,MAAA,YAAY,CAAC,aAAa,0CAAE,WAAW,MAAI,MAAA,YAAY,CAAC,aAAa,0CAAE,UAAU,CAAA,CAAC,EAAE;4BACxF,MAAM,IAAI,KAAK,CAAC,0FAA0F,CAAC,CAAC;yBAC7G;wBAEK,MAAM,GAAG,sBAAW,CAAC,YAAY,EAAE,CAAC;wBAC1C,KAAA,CAAA,KAAA,QAAQ,CAAA,CAAC,IAAI,CAAA;oFACK,MAAM,CAAC,cAAc,6RAIe,iBAAiB,iHACjB,gBAAgB,0FACtC,CAAA,MAAA,YAAY,CAAC,aAAa,0CAAE,WAAW,EAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,uEACxH,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,uCACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,uCACtB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,sEAEpB,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE;wBAGlC,qBAAM,wBAAwB,EAAE,EAAA;;iDAAhC,SAAgC;wBAChC,qBAAM,gBAAgB,CAAC,YAAY,CAAC,EAAA;;wBAhBxD,cAAc,cAgBM,SAAoC,4DAEtC,EAAC,CAAC;;;;aACrB;KACF,CAAC;AACJ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"reactApp.js","sourceRoot":"","sources":["../../../src/routes/reactApp.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8CAAwB;AACxB,8DAAuC;AACvC,oDAA+E;AAC/E,kDAA0C;AAC1C,oDAAmD;AAEnD,iFAAiF;AACjF,mFAAmF;AACnF,kEAAkE;AAClE,IAAM,iBAAiB,GAAG,SAAS,CAAC;AACpC,IAAM,gBAAgB,GAAG,SAAS,CAAC;AAE5B,IAAM,cAAc,GAAG,UAAC,YAA0B;IACvD,OAAO;QACL,IAAI,EAAE,GAAG;QACT,MAAM,EAAE,KAAc;QACtB,SAAS,EAAE,UAAO,OAAY,EAAE,QAAa;;;;;;wBAC3C,IAAI,OAAO,CAAC,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE;4BACtC,sBAAO;yBACR;wBAED,IAAI,CAAC,CAAC,CAAA,MAAA,YAAY,CAAC,aAAa,0CAAE,WAAW,MAAI,MAAA,YAAY,CAAC,aAAa,0CAAE,UAAU,CAAA,CAAC,EAAE;4BACxF,MAAM,IAAI,KAAK,CAAC,0FAA0F,CAAC,CAAC;yBAC7G;wBAEK,MAAM,GAAG,sBAAW,CAAC,YAAY,EAAE,CAAC;wBAC1C,oFAAoF;wBACpF,uFAAuF;wBACvF,oFAAoF;wBACpF,wFAAwF;wBACxF,mFAAmF;wBACnF,qFAAqF;wBACrF,QAAQ,CAAC,GAAG,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;wBAC1C,KAAA,CAAA,KAAA,QAAQ,CAAA,CAAC,IAAI,CAAA;oFACK,MAAM,CAAC,cAAc,6RAIe,iBAAiB,iHACjB,gBAAgB,0FACtC,CAAA,MAAA,YAAY,CAAC,aAAa,0CAAE,WAAW,EAAC,CAAC,CAAC,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAAE,uEACxH,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,uCACvB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,uCACtB,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,sEAEpB,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE;wBAGlC,qBAAM,wBAAwB,EAAE,EAAA;;iDAAhC,SAAgC;wBAChC,qBAAM,gBAAgB,CAAC,YAAY,CAAC,EAAA;;wBAhBxD,cAAc,cAgBM,SAAoC,4DAEtC,EAAC,CAAC;;;;aACrB;KACF,CAAC;AACJ,CAAC,CAAC;AA1CW,QAAA,cAAc,kBA0CzB;AAEF,SAAe,gBAAgB,CAAC,YAA0B;;;;;;;oBACxD,IAAI,CAAC,CAAC,CAAA,MAAA,YAAY,CAAC,aAAa,0CAAE,WAAW,MAAI,MAAA,YAAY,CAAC,aAAa,0CAAE,UAAU,CAAA,CAAC,EAAE;wBACxF,sBAAO;qBACR;oBAEK,UAAU,GAAa,EAAE,CAAC;yBAC5B,CAAA,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAA,EAAjE,wBAAiE;oBAI7D,KAAK,GAAG,+BAAc,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,aAAM,+BAAc,CAAC,GAAG,EAAG,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC7E,UAAU,CAAC,IAAI,CAAC,uBAAgB,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,SAAG,KAAK,gBAAa,CAAC,CAAC;oBACtF,UAAU,CAAC,IAAI,CAAC,uBAAgB,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,WAAW,CAAC,SAAG,KAAK,gBAAa,CAAC,CAAC;;;yBAChF,CAAA,MAAA,YAAY,CAAC,aAAa,0CAAE,WAAW,CAAA,EAAvC,wBAAuC;oBAChD,WAA+D,EAAtC,KAAA,YAAY,CAAC,aAAa,CAAC,WAAW,EAAtC,cAAsC,EAAtC,IAAsC,EAAE;wBAAtD,UAAU;wBACnB,UAAU,CAAC,IAAI,CAAC,uBAAgB,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,UAAU,CAAC,gBAAa,CAAC,CAAC;qBACjF;;;yBACQ,CAAA,CAAA,MAAA,YAAY,CAAC,aAAa,0CAAE,UAAU,MAAI,MAAA,YAAY,CAAC,aAAa,0CAAE,gBAAgB,CAAA,CAAA,EAAtF,wBAAsF;oBACzF,kBAAkB,GAAG,cAAI,CAAC,IAAI,CAClC,YAAY,CAAC,aAAa,CAAC,gBAAgB,EAC3C,YAAY,CAAC,aAAa,CAAC,UAAU,CACtC,CAAC;oBACgB,qBAAM,cAAE,CAAC,wBAAwB,CAAC,kBAAkB,EAAE,SAAS,CAAC,EAAA;;oBAA5E,SAAS,GAAG,SAAgE;oBAClF,WAAgC,EAAT,uBAAS,EAAT,uBAAS,EAAT,IAAS,EAAE;wBAAvB,QAAQ;wBACX,YAAY,GAAG,cAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,aAAa,CAAC,gBAAgB,EAAE,QAAQ,CAAC,CAAC;wBAC1F,UAAU,CAAC,IAAI,CAAC,uBAAgB,cAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,gBAAa,CAAC,CAAC;qBACnF;;wBAGH,sBAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAC;;;;CAC9B;AAED,SAAe,wBAAwB;;;;;;oBAC/B,OAAO,GAAG,IAAA,qCAAwB,GAAE,CAAC;oBACrC,UAAU,GAAa,EAAE,CAAC;0BACJ,EAAP,mBAAO;;;yBAAP,CAAA,qBAAO,CAAA;oBAAjB,MAAM;oBACf,KAAA,CAAA,KAAA,UAAU,CAAA,CAAC,IAAI,CAAA;;oBAAY,qBAAM,MAAM,CAAC,MAAM,EAAE,EAAA;;oBAAhD,cAAgB,sBAAW,SAAqB,eAAW,EAAC,CAAC;;;oBAD1C,IAAO,CAAA;;wBAI5B,sBAAO,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,EAAC;;;;CAC9B"}
|
package/generated/index.ts
CHANGED
|
@@ -21,7 +21,7 @@ import 'typescript';
|
|
|
21
21
|
/** Generate Source Graph */
|
|
22
22
|
|
|
23
23
|
const sourceGraph =
|
|
24
|
-
'{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/server/NodeGlobalDataStorage","value":{"packageName":"@proteinjs/server","name":"NodeGlobalDataStorage","filePath":"/home/runner/work/server/server/packages/server/src/NodeGlobalDataStorage.ts","qualifiedName":"@proteinjs/server/NodeGlobalDataStorage","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"HOOK_INITIALIZED","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"GLOBAL_DATA","type":{"packageName":"@proteinjs/server","name":"{ [id: string]: GlobalData }","filePath":null,"qualifiedName":"@proteinjs/server/{ [id: string]: GlobalData }","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"environment","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"setData","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"data","type":{"packageName":"@proteinjs/server-api","name":"GlobalData","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalData","typeParameters":null,"directParents":null}}]},{"name":"getData","returnType":{"packageName":"@proteinjs/server-api","name":"GlobalData","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalData","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"initHook","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/server-api","name":"GlobalDataStorage","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalDataStorage","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/server-api/GlobalDataStorage"},{"v":"@proteinjs/server/NodeSessionDataStorage","value":{"packageName":"@proteinjs/server","name":"NodeSessionDataStorage","filePath":"/home/runner/work/server/server/packages/server/src/NodeSessionDataStorage.ts","qualifiedName":"@proteinjs/server/NodeSessionDataStorage","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"HOOK_INITIALIZED","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"SESSION_DATA","type":{"packageName":"@proteinjs/server","name":"{ [id: string]: SessionData }","filePath":null,"qualifiedName":"@proteinjs/server/{ [id: string]: SessionData }","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"environment","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"setData","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"data","type":{"packageName":"@proteinjs/server-api","name":"SessionData","filePath":null,"qualifiedName":"@proteinjs/server-api/SessionData","typeParameters":null,"directParents":null}}]},{"name":"getData","returnType":{"packageName":"@proteinjs/server-api","name":"SessionData","filePath":null,"qualifiedName":"@proteinjs/server-api/SessionData","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"initHook","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/server-api","name":"SessionDataStorage","filePath":null,"qualifiedName":"@proteinjs/server-api/SessionDataStorage","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/server-api/SessionDataStorage"},{"v":"@proteinjs/server/ExtendedIncomingMessage","value":{"packageName":"@proteinjs/server","name":"ExtendedIncomingMessage","filePath":"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts","qualifiedName":"@proteinjs/server/ExtendedIncomingMessage","properties":[{"name":"sessionID","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"user","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"http","name":"IncomingMessage","filePath":null,"qualifiedName":"http/IncomingMessage","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"http/IncomingMessage"},{"v":"@proteinjs/server/ExtendedSocket","value":{"packageName":"@proteinjs/server","name":"ExtendedSocket","filePath":"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts","qualifiedName":"@proteinjs/server/ExtendedSocket","properties":[{"name":"request","type":{"packageName":"@proteinjs/server","name":"ExtendedIncomingMessage","filePath":null,"qualifiedName":"@proteinjs/server/ExtendedIncomingMessage","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"socket.io","name":"Socket","filePath":null,"qualifiedName":"socket.io/Socket","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"socket.io/Socket"},{"v":"@proteinjs/server/environmentCache","value":{"packageName":"@proteinjs/server","name":"environmentCache","filePath":"/home/runner/work/server/server/packages/server/src/envCache.ts","qualifiedName":"@proteinjs/server/environmentCache","type":{"packageName":"","name":"GlobalDataCache<Env>","filePath":null,"qualifiedName":"/GlobalDataCache<Env>","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"GlobalDataCache","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalDataCache","typeParameters":["@proteinjs/server-api/Env"],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/GlobalDataCache"},{"v":"@proteinjs/server/healthCheck","value":{"packageName":"@proteinjs/server","name":"healthCheck","filePath":"/home/runner/work/server/server/packages/server/src/routes/healthCheck.ts","qualifiedName":"@proteinjs/server/healthCheck","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/Route"}],"edges":[{"v":"@proteinjs/server/NodeGlobalDataStorage","w":"@proteinjs/server-api/GlobalDataStorage","value":"implements interface"},{"v":"@proteinjs/server/NodeSessionDataStorage","w":"@proteinjs/server-api/SessionDataStorage","value":"implements interface"},{"v":"@proteinjs/server/ExtendedIncomingMessage","w":"http/IncomingMessage","value":"extends interface"},{"v":"@proteinjs/server/ExtendedSocket","w":"socket.io/Socket","value":"extends interface"},{"v":"@proteinjs/server/environmentCache","w":"@proteinjs/server-api/GlobalDataCache","value":"has type"},{"v":"@proteinjs/server/healthCheck","w":"@proteinjs/server-api/Route","value":"has type"}]}';
|
|
24
|
+
'{"options":{"directed":true,"multigraph":false,"compound":false},"nodes":[{"v":"@proteinjs/server/NodeGlobalDataStorage","value":{"packageName":"@proteinjs/server","name":"NodeGlobalDataStorage","filePath":"/home/runner/work/server/server/packages/server/src/NodeGlobalDataStorage.ts","qualifiedName":"@proteinjs/server/NodeGlobalDataStorage","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"HOOK_INITIALIZED","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"GLOBAL_DATA","type":{"packageName":"@proteinjs/server","name":"{ [id: string]: GlobalData }","filePath":null,"qualifiedName":"@proteinjs/server/{ [id: string]: GlobalData }","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"environment","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"setData","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"data","type":{"packageName":"@proteinjs/server-api","name":"GlobalData","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalData","typeParameters":null,"directParents":null}}]},{"name":"getData","returnType":{"packageName":"@proteinjs/server-api","name":"GlobalData","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalData","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"initHook","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/server-api","name":"GlobalDataStorage","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalDataStorage","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/server-api/GlobalDataStorage"},{"v":"@proteinjs/server/NodeSessionDataStorage","value":{"packageName":"@proteinjs/server","name":"NodeSessionDataStorage","filePath":"/home/runner/work/server/server/packages/server/src/NodeSessionDataStorage.ts","qualifiedName":"@proteinjs/server/NodeSessionDataStorage","isAbstract":false,"isStatic":false,"visibility":"public","properties":[{"name":"HOOK_INITIALIZED","type":null,"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"SESSION_DATA","type":{"packageName":"@proteinjs/server","name":"{ [id: string]: SessionData }","filePath":null,"qualifiedName":"@proteinjs/server/{ [id: string]: SessionData }","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":true,"visibility":"private"},{"name":"environment","type":null,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[{"name":"setData","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[{"name":"data","type":{"packageName":"@proteinjs/server-api","name":"SessionData","filePath":null,"qualifiedName":"@proteinjs/server-api/SessionData","typeParameters":null,"directParents":null}}]},{"name":"getData","returnType":{"packageName":"@proteinjs/server-api","name":"SessionData","filePath":null,"qualifiedName":"@proteinjs/server-api/SessionData","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"clearData","returnType":{"packageName":"","name":"void","filePath":null,"qualifiedName":"/void","typeParameters":null,"directParents":null},"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public","parameters":[]},{"name":"initHook","returnType":null,"isAsync":false,"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"private","parameters":[]}],"typeParameters":[],"directParentInterfaces":[{"packageName":"@proteinjs/server-api","name":"SessionDataStorage","filePath":null,"qualifiedName":"@proteinjs/server-api/SessionDataStorage","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"directParentClasses":[],"sourceType":2}},{"v":"@proteinjs/server-api/SessionDataStorage"},{"v":"@proteinjs/server/ExtendedIncomingMessage","value":{"packageName":"@proteinjs/server","name":"ExtendedIncomingMessage","filePath":"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts","qualifiedName":"@proteinjs/server/ExtendedIncomingMessage","properties":[{"name":"sessionID","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"},{"name":"user","type":{"packageName":"","name":"string","filePath":null,"qualifiedName":"/string","typeParameters":null,"directParents":null},"isOptional":true,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"http","name":"IncomingMessage","filePath":null,"qualifiedName":"http/IncomingMessage","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"http/IncomingMessage"},{"v":"@proteinjs/server/ExtendedSocket","value":{"packageName":"@proteinjs/server","name":"ExtendedSocket","filePath":"/home/runner/work/server/server/packages/server/src/SocketIOServerRepo.ts","qualifiedName":"@proteinjs/server/ExtendedSocket","properties":[{"name":"request","type":{"packageName":"@proteinjs/server","name":"ExtendedIncomingMessage","filePath":null,"qualifiedName":"@proteinjs/server/ExtendedIncomingMessage","typeParameters":null,"directParents":null},"isOptional":false,"isAbstract":false,"isStatic":false,"visibility":"public"}],"methods":[],"typeParameters":[],"directParents":[{"packageName":"socket.io","name":"Socket","filePath":null,"qualifiedName":"socket.io/Socket","properties":[],"methods":[],"typeParameters":[],"directParents":[]}],"sourceType":3}},{"v":"socket.io/Socket"},{"v":"@proteinjs/server/environmentCache","value":{"packageName":"@proteinjs/server","name":"environmentCache","filePath":"/home/runner/work/server/server/packages/server/src/envCache.ts","qualifiedName":"@proteinjs/server/environmentCache","type":{"packageName":"","name":"GlobalDataCache<Env>","filePath":null,"qualifiedName":"/GlobalDataCache<Env>","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"GlobalDataCache","filePath":null,"qualifiedName":"@proteinjs/server-api/GlobalDataCache","typeParameters":["@proteinjs/server-api/Env"],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/GlobalDataCache"},{"v":"@proteinjs/server/healthCheck","value":{"packageName":"@proteinjs/server","name":"healthCheck","filePath":"/home/runner/work/server/server/packages/server/src/routes/healthCheck.ts","qualifiedName":"@proteinjs/server/healthCheck","type":{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":[{"packageName":"@proteinjs/server-api","name":"Route","filePath":null,"qualifiedName":"@proteinjs/server-api/Route","typeParameters":[],"directParents":null}]},"isExported":true,"isConst":true,"sourceType":0}},{"v":"@proteinjs/server-api/Route"}],"edges":[{"v":"@proteinjs/server/NodeGlobalDataStorage","w":"@proteinjs/server-api/GlobalDataStorage","value":"implements interface"},{"v":"@proteinjs/server/NodeSessionDataStorage","w":"@proteinjs/server-api/SessionDataStorage","value":"implements interface"},{"v":"@proteinjs/server/ExtendedIncomingMessage","w":"http/IncomingMessage","value":"extends interface"},{"v":"@proteinjs/server/ExtendedSocket","w":"socket.io/Socket","value":"extends interface"},{"v":"@proteinjs/server/environmentCache","w":"@proteinjs/server-api/GlobalDataCache","value":"has type"},{"v":"@proteinjs/server/healthCheck","w":"@proteinjs/server-api/Route","value":"has type"}]}';
|
|
25
25
|
|
|
26
26
|
/** Generate Source Links */
|
|
27
27
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@proteinjs/server",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.3",
|
|
4
4
|
"description": "A server impl",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"@proteinjs/event": "^1.0.18",
|
|
26
26
|
"@proteinjs/logger": "^1.0.21",
|
|
27
27
|
"@proteinjs/reflection": "^1.1.12",
|
|
28
|
-
"@proteinjs/server-api": "^3.1.
|
|
28
|
+
"@proteinjs/server-api": "^3.1.2",
|
|
29
29
|
"@proteinjs/util-node": "^1.10.1",
|
|
30
30
|
"body-parser": "1.19.0",
|
|
31
31
|
"cookie-parser": "1.4.5",
|
|
@@ -64,5 +64,5 @@
|
|
|
64
64
|
},
|
|
65
65
|
"main": "./dist/generated/index.js",
|
|
66
66
|
"types": "./dist/generated/index.d.ts",
|
|
67
|
-
"gitHead": "
|
|
67
|
+
"gitHead": "10387d6d8f4af3b4f6d6a320741504e1ce5d1bdb"
|
|
68
68
|
}
|
|
@@ -26,6 +26,24 @@ export class NodeSessionDataStorage implements SessionDataStorage {
|
|
|
26
26
|
return NodeSessionDataStorage.SESSION_DATA[asyncHooks.executionAsyncId()];
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Drop the CURRENT async id's entry so the next `setData` seeds fresh. The init hook copies
|
|
31
|
+
* a populated bag onto every descendant async resource and `setData` is first-write-wins, so
|
|
32
|
+
* a request dispatched inside a PREVIOUS request's lineage (keep-alive socket reuse — the
|
|
33
|
+
* normal case under a 620s keepAliveTimeout) would otherwise run as the prior request's user
|
|
34
|
+
* (observed 2026-08-26: /dev/login account switch, home greeting server-rendered with the
|
|
35
|
+
* previous account's name). Scoped to the current id on purpose: in-flight hops of the
|
|
36
|
+
* previous request keep their own already-copied entries, and descendants created after the
|
|
37
|
+
* re-seed inherit the fresh bag.
|
|
38
|
+
*/
|
|
39
|
+
clearData(): void {
|
|
40
|
+
if (!NodeSessionDataStorage.HOOK_INITIALIZED) {
|
|
41
|
+
this.initHook();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
delete NodeSessionDataStorage.SESSION_DATA[asyncHooks.executionAsyncId()];
|
|
45
|
+
}
|
|
46
|
+
|
|
29
47
|
private initHook() {
|
|
30
48
|
asyncHooks
|
|
31
49
|
.createHook({
|
package/src/loadRoutes.ts
CHANGED
|
@@ -87,6 +87,13 @@ function wrapRoute(
|
|
|
87
87
|
for (const sessionDataCache of getSessionDataCaches()) {
|
|
88
88
|
sessionData.data[sessionDataCache.key] = await sessionDataCache.create(sessionData.sessionId, sessionData.user);
|
|
89
89
|
}
|
|
90
|
+
// A request always seeds ITS OWN session context: on a reused keep-alive socket this
|
|
91
|
+
// dispatch is born inside the previous request's async lineage (the storage's init hook
|
|
92
|
+
// copies bags onto descendants), and first-write-wins setData would silently drop this
|
|
93
|
+
// request's seed — the request then runs (and server-renders proteinjs.sessionData) as
|
|
94
|
+
// the PRIOR request's user (observed 2026-08-26: stale home greeting after a /dev/login
|
|
95
|
+
// account switch; roster correct, greeting wrong). Clear the inherited entry first.
|
|
96
|
+
Session.clearData();
|
|
90
97
|
Session.setData(sessionData);
|
|
91
98
|
|
|
92
99
|
if (shouldLogRequest(request, config)) {
|
package/src/routes/reactApp.ts
CHANGED
|
@@ -24,6 +24,13 @@ export const createReactApp = (serverConfig: ServerConfig) => {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
const helmet = ReactHelmet.renderStatic();
|
|
27
|
+
// The page must ALWAYS revalidate (found live 2026-09-01, the mobile-app stale-page
|
|
28
|
+
// investigation): without an explicit policy, HTTP heuristic caching applies (RFC 9111
|
|
29
|
+
// §4.2.2) — WKWebView in particular can serve the cached page without revalidating,
|
|
30
|
+
// across app force-quits, pinning stale bundle POINTERS long after a deploy. The hashed
|
|
31
|
+
// bundles under /static keep their long cache (a fresh page always points at fresh
|
|
32
|
+
// hashes); `no-cache` + the ETag express already stamps = a cheap 304 on every load.
|
|
33
|
+
response.set('Cache-Control', 'no-cache');
|
|
27
34
|
response.send(`<!DOCTYPE html>
|
|
28
35
|
<html ${helmet.htmlAttributes}>
|
|
29
36
|
<head>
|
|
@@ -54,6 +54,10 @@ startServer({
|
|
|
54
54
|
response.status(200).send('slow-done');
|
|
55
55
|
},
|
|
56
56
|
},
|
|
57
|
+
// A bundle pointer so the '*' react-app route serves its HTML — the html-cache-control
|
|
58
|
+
// suite asserts response headers on the page the app actually ships. No staticContentDir:
|
|
59
|
+
// nothing needs the bundle to resolve, only the page response to render.
|
|
60
|
+
staticContent: { bundlePaths: ['bundles/app.test.js'] },
|
|
57
61
|
shutdown: {
|
|
58
62
|
drainDelayMs: process.env.FIXTURE_DRAIN_DELAY_MS ? Number(process.env.FIXTURE_DRAIN_DELAY_MS) : undefined,
|
|
59
63
|
drainTimeoutMs: process.env.FIXTURE_DRAIN_TIMEOUT_MS ? Number(process.env.FIXTURE_DRAIN_TIMEOUT_MS) : undefined,
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import * as net from 'net';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { ChildProcess, spawn } from 'child_process';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* HTML cache policy of a real server process (spawns the built dist — run `npm run build`
|
|
8
|
+
* first), asserted through the front door on the page the react-app route actually sends.
|
|
9
|
+
*
|
|
10
|
+
* THE CLASS THIS PINS (found live 2026-09-01, the mobile-app stale-page investigation): the
|
|
11
|
+
* HTML response carried NO Cache-Control header — an ETag only. With no explicit policy,
|
|
12
|
+
* HTTP heuristic caching applies (RFC 9111 §4.2.2), and WKWebView in particular can serve
|
|
13
|
+
* the cached page without revalidating — across app force-quits — pinning stale bundle
|
|
14
|
+
* POINTERS long after a deploy. The hashed bundles under /static keep their long cache (a
|
|
15
|
+
* new page always points at new hashes); the PAGE must always revalidate. `no-cache` states
|
|
16
|
+
* exactly that, and the ETag the response already carries keeps revalidation a cheap 304.
|
|
17
|
+
*/
|
|
18
|
+
const packageRoot = path.join(__dirname, '..');
|
|
19
|
+
const fixturePath = path.join(__dirname, 'fixture', 'gracefulShutdownServer.js');
|
|
20
|
+
|
|
21
|
+
describe('html cache policy', () => {
|
|
22
|
+
let fixture: Fixture | undefined;
|
|
23
|
+
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
if (fixture && fixture.child.exitCode === null && fixture.child.signalCode === null) {
|
|
26
|
+
fixture.child.kill('SIGKILL');
|
|
27
|
+
await fixture.exited.catch(() => undefined);
|
|
28
|
+
}
|
|
29
|
+
fixture = undefined;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('the react-app HTML response says Cache-Control: no-cache — the page always revalidates', async () => {
|
|
33
|
+
fixture = await startFixture();
|
|
34
|
+
|
|
35
|
+
const result = await request(fixture.port, '/');
|
|
36
|
+
expect(result.status).toBe(200);
|
|
37
|
+
// Sanity: this IS the react-app page (the configured bundle pointer is in the body).
|
|
38
|
+
expect(result.body).toContain('bundles/app.test.js');
|
|
39
|
+
expect(result.headers['content-type']).toContain('text/html');
|
|
40
|
+
expect(result.headers['cache-control']).toBe('no-cache');
|
|
41
|
+
}, 30000);
|
|
42
|
+
|
|
43
|
+
it('every page path rides the same policy (the catch-all route is the one HTML owner)', async () => {
|
|
44
|
+
fixture = await startFixture();
|
|
45
|
+
|
|
46
|
+
const result = await request(fixture.port, '/chat');
|
|
47
|
+
expect(result.status).toBe(200);
|
|
48
|
+
expect(result.headers['content-type']).toContain('text/html');
|
|
49
|
+
expect(result.headers['cache-control']).toBe('no-cache');
|
|
50
|
+
}, 30000);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
type Fixture = {
|
|
54
|
+
child: ChildProcess;
|
|
55
|
+
port: number;
|
|
56
|
+
exited: Promise<{ code: number | null; signal: NodeJS.Signals | null }>;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
async function startFixture(): Promise<Fixture> {
|
|
60
|
+
const port = await ephemeralPort();
|
|
61
|
+
// Scrub the env vars startServer reads (dev machines export some of these): the fixture's
|
|
62
|
+
// behavior must come from its own config only.
|
|
63
|
+
const env = { ...process.env };
|
|
64
|
+
delete env.SERVER_PORT;
|
|
65
|
+
delete env.DEVELOPMENT;
|
|
66
|
+
delete env.DISABLE_HOT_CLIENT_BUILDS;
|
|
67
|
+
delete env.HMR_PORT;
|
|
68
|
+
const child = spawn(process.execPath, [fixturePath], {
|
|
69
|
+
cwd: packageRoot,
|
|
70
|
+
env: { ...env, FIXTURE_PORT: String(port) },
|
|
71
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
72
|
+
});
|
|
73
|
+
let output = '';
|
|
74
|
+
child.stdout!.on('data', (chunk) => (output += String(chunk)));
|
|
75
|
+
child.stderr!.on('data', (chunk) => (output += String(chunk)));
|
|
76
|
+
const exited = new Promise<{ code: number | null; signal: NodeJS.Signals | null }>((resolve) =>
|
|
77
|
+
child.once('exit', (code, signal) => resolve({ code, signal }))
|
|
78
|
+
);
|
|
79
|
+
// Ready = the health check answers 200 (readiness through the front door, not log lines).
|
|
80
|
+
const deadline = Date.now() + 20000;
|
|
81
|
+
for (;;) {
|
|
82
|
+
if (child.exitCode !== null || child.signalCode !== null) {
|
|
83
|
+
throw new Error(`Fixture exited during boot; output:\n${output}`);
|
|
84
|
+
}
|
|
85
|
+
const result = await request(port, '/health-check').catch(() => undefined);
|
|
86
|
+
if (result?.status === 200) {
|
|
87
|
+
return { child, port, exited };
|
|
88
|
+
}
|
|
89
|
+
if (Date.now() > deadline) {
|
|
90
|
+
throw new Error(`Fixture never became ready; output:\n${output}`);
|
|
91
|
+
}
|
|
92
|
+
await sleep(100);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** One request on its OWN connection (agent: false — no keep-alive pooling). The forwarded
|
|
97
|
+
* proto is the LB topology in miniature: the prod shape 302s plain http to https. */
|
|
98
|
+
function request(
|
|
99
|
+
port: number,
|
|
100
|
+
requestPath: string
|
|
101
|
+
): Promise<{ status: number; body: string; headers: http.IncomingHttpHeaders }> {
|
|
102
|
+
return new Promise((resolve, reject) => {
|
|
103
|
+
const req = http.get(
|
|
104
|
+
{ host: '127.0.0.1', port, path: requestPath, agent: false, headers: { 'X-Forwarded-Proto': 'https' } },
|
|
105
|
+
(res) => {
|
|
106
|
+
let body = '';
|
|
107
|
+
res.on('data', (chunk) => (body += String(chunk)));
|
|
108
|
+
res.on('end', () => resolve({ status: res.statusCode ?? 0, body, headers: res.headers }));
|
|
109
|
+
res.on('error', reject);
|
|
110
|
+
}
|
|
111
|
+
);
|
|
112
|
+
req.on('error', reject);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function ephemeralPort(): Promise<number> {
|
|
117
|
+
return new Promise((resolve, reject) => {
|
|
118
|
+
const probe = net.createServer();
|
|
119
|
+
probe.once('error', reject);
|
|
120
|
+
probe.listen(0, '127.0.0.1', () => {
|
|
121
|
+
const address = probe.address() as net.AddressInfo;
|
|
122
|
+
probe.close(() => resolve(address.port));
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function sleep(ms: number): Promise<void> {
|
|
128
|
+
return new Promise((resolve) => setTimeout(resolve, Math.max(0, ms)));
|
|
129
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { SessionData } from '@proteinjs/server-api';
|
|
2
|
+
import { NodeSessionDataStorage } from '../src/NodeSessionDataStorage';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The keep-alive session-bleed class (observed 2026-08-26: after a /dev/login account
|
|
6
|
+
* switch, the home page server-rendered the PREVIOUS account's display name — roster
|
|
7
|
+
* correct, greeting wrong). Mechanism: the storage's init hook copies a populated bag onto
|
|
8
|
+
* every descendant async resource, `setData` is first-write-wins, and a request dispatched
|
|
9
|
+
* on a reused keep-alive socket is born inside the previous request's lineage — so the new
|
|
10
|
+
* request's seed silently loses and the request runs as the prior request's user.
|
|
11
|
+
*
|
|
12
|
+
* These tests pin the hazard AND the request-boundary cure `wrapRoute` now applies:
|
|
13
|
+
* `clearData()` before the seed makes the request's own `setData` authoritative.
|
|
14
|
+
*/
|
|
15
|
+
describe('session data at the request boundary', () => {
|
|
16
|
+
const storage = new NodeSessionDataStorage();
|
|
17
|
+
const asData = (user: string): SessionData => ({ sessionId: `session-${user}`, user, data: {} });
|
|
18
|
+
|
|
19
|
+
/** A continuation born INSIDE the current async context — the keep-alive shape: request B's
|
|
20
|
+
* dispatch created while request A's lineage still tags the trigger ids. */
|
|
21
|
+
const bornInLineage = (run: () => void): Promise<void> =>
|
|
22
|
+
new Promise((resolve, reject) => {
|
|
23
|
+
setImmediate(() => {
|
|
24
|
+
try {
|
|
25
|
+
run();
|
|
26
|
+
resolve();
|
|
27
|
+
} catch (e) {
|
|
28
|
+
reject(e);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('pins the hazard: a dispatch born in a prior lineage INHERITS the bag, and a bare re-seed silently loses', async () => {
|
|
34
|
+
storage.setData(asData('account-a'));
|
|
35
|
+
await bornInLineage(() => {
|
|
36
|
+
expect(storage.getData()?.user).toBe('account-a'); // inherited from the trigger lineage
|
|
37
|
+
storage.setData(asData('account-b')); // first-write-wins: this seed is DROPPED
|
|
38
|
+
expect(storage.getData()?.user).toBe('account-a'); // the silent bleed
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('the request-boundary cure: clearData() then setData makes the new request authoritative', async () => {
|
|
43
|
+
storage.setData(asData('account-a'));
|
|
44
|
+
await bornInLineage(() => {
|
|
45
|
+
// What wrapRoute does per request now: clear the inherited entry, then seed.
|
|
46
|
+
storage.clearData();
|
|
47
|
+
storage.setData(asData('account-b'));
|
|
48
|
+
expect(storage.getData()?.user).toBe('account-b');
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
});
|