tina4-nodejs 3.13.147 → 3.13.148
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/CLAUDE.md +2 -2
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +28 -10
- package/packages/core/dist/index.js +28 -10
- package/packages/core/src/dispatchPipeline.ts +4 -12
- package/packages/core/src/session.ts +45 -3
- package/packages/orm/dist/index.js +28 -10
- package/types/core/src/session.d.ts +25 -0
package/CLAUDE.md
CHANGED
|
@@ -13,13 +13,13 @@ Even if the skill text is not currently loaded, these are non-negotiable:
|
|
|
13
13
|
|
|
14
14
|
The full discipline lives in `.claude/skills/tina4-maintainer/SKILL.md`; this block is the always-on floor.
|
|
15
15
|
|
|
16
|
-
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.
|
|
16
|
+
# CLAUDE.md - AI Developer Guide for tina4-nodejs (v3.13.148)
|
|
17
17
|
|
|
18
18
|
> This file helps AI assistants (Claude, Copilot, Cursor, etc.) understand and work on this codebase effectively.
|
|
19
19
|
|
|
20
20
|
## What This Project Is
|
|
21
21
|
|
|
22
|
-
Tina4 for Node.js/TypeScript v3.13.
|
|
22
|
+
Tina4 for Node.js/TypeScript v3.13.148 - The Intelligent Native Application 4ramework. A convention-over-configuration structural paradigm. The developer writes TypeScript; Tina4 is invisible infrastructure.
|
|
23
23
|
|
|
24
24
|
The philosophy: zero ceremony, batteries included, file system as source of truth.
|
|
25
25
|
|
package/package.json
CHANGED
package/packages/cli/dist/bin.js
CHANGED
|
@@ -23878,6 +23878,7 @@ __export(session_exports, {
|
|
|
23878
23878
|
isSecureScheme: () => isSecureScheme,
|
|
23879
23879
|
isValidSessionId: () => isValidSessionId,
|
|
23880
23880
|
sessionCookieName: () => sessionCookieName,
|
|
23881
|
+
sessionCookieToSend: () => sessionCookieToSend,
|
|
23881
23882
|
sessionStrictMode: () => sessionStrictMode
|
|
23882
23883
|
});
|
|
23883
23884
|
import { createHash as createHash5, randomBytes as randomBytes6 } from "node:crypto";
|
|
@@ -23938,6 +23939,15 @@ function buildSessionCookie(sessionId, ttl, cookieName, forwardedProto, socketEn
|
|
|
23938
23939
|
parts.push(`Max-Age=${ttl}`);
|
|
23939
23940
|
return parts.join("; ");
|
|
23940
23941
|
}
|
|
23942
|
+
function sessionCookieToSend(sess, existingSid, rawReq, rawRes) {
|
|
23943
|
+
const newSid = sess.sessionId ?? sess.getSessionId?.();
|
|
23944
|
+
if (!newSid || newSid === existingSid || sess.isFresh() || rawRes.headersSent) return null;
|
|
23945
|
+
const ttl = parseInt(process.env.TINA4_SESSION_TTL ?? "3600", 10);
|
|
23946
|
+
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
23947
|
+
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
23948
|
+
const socketEncrypted = rawReq.socket?.encrypted === true;
|
|
23949
|
+
return buildSessionCookie(newSid, ttl, void 0, forwardedProto, socketEncrypted);
|
|
23950
|
+
}
|
|
23941
23951
|
var SESSION_ID_PATTERN, VALID_SESSION_BACKENDS, CANONICAL_SESSION_BACKENDS, RETIRED_SESSION_BACKENDS, FileSessionHandler, RedisSessionHandler, FLASH_PREFIX, Session;
|
|
23942
23952
|
var init_session = __esm({
|
|
23943
23953
|
"../core/src/session.ts"() {
|
|
@@ -24300,7 +24310,6 @@ var init_session = __esm({
|
|
|
24300
24310
|
const now = Math.floor(Date.now() / 1e3);
|
|
24301
24311
|
this.data = { _created: now, _accessed: now };
|
|
24302
24312
|
this.dirty = false;
|
|
24303
|
-
this.safeWrite(this.sessionId, this.data, this.ttl);
|
|
24304
24313
|
this.stored = false;
|
|
24305
24314
|
this.loaded = /* @__PURE__ */ new Map();
|
|
24306
24315
|
this.cleared = false;
|
|
@@ -24451,6 +24460,19 @@ var init_session = __esm({
|
|
|
24451
24460
|
const result = this.flash(key);
|
|
24452
24461
|
return result !== void 0 ? result : defaultValue;
|
|
24453
24462
|
}
|
|
24463
|
+
/**
|
|
24464
|
+
* Whether this session is only an id: nothing has stored it and it holds no
|
|
24465
|
+
* data of its own (its `_created` / `_accessed` bookkeeping does not count).
|
|
24466
|
+
* Such a session has no record and needs no cookie, so save() writes nothing
|
|
24467
|
+
* for it and the server sends no cookie for it. It stops being fresh when
|
|
24468
|
+
* something is stored in it. Calls that mark a session changed without leaving
|
|
24469
|
+
* anything in it (deleting a key it never had, clear(), regenerate() on an
|
|
24470
|
+
* empty session) leave it fresh: a record with no data is not a session.
|
|
24471
|
+
*/
|
|
24472
|
+
isFresh() {
|
|
24473
|
+
if (this.stored) return false;
|
|
24474
|
+
return !this.data || Object.keys(this.data).every((k) => k === "_created" || k === "_accessed");
|
|
24475
|
+
}
|
|
24454
24476
|
/**
|
|
24455
24477
|
* Get the current session ID.
|
|
24456
24478
|
*/
|
|
@@ -24513,7 +24535,7 @@ var init_session = __esm({
|
|
|
24513
24535
|
*/
|
|
24514
24536
|
save() {
|
|
24515
24537
|
if (!this.sessionId || !this.data) return true;
|
|
24516
|
-
if (
|
|
24538
|
+
if (this.isFresh()) return true;
|
|
24517
24539
|
let record = this.data;
|
|
24518
24540
|
if (this.stored) {
|
|
24519
24541
|
const current = this.safeRead(this.sessionId);
|
|
@@ -24700,7 +24722,7 @@ function errorLabel(err) {
|
|
|
24700
24722
|
return err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
24701
24723
|
}
|
|
24702
24724
|
async function sessionAutoStart(rawReq, rawRes, req2) {
|
|
24703
|
-
const { Session: Session2,
|
|
24725
|
+
const { Session: Session2, sessionCookieToSend: sessionCookieToSend2, sessionCookieName: sessionCookieName2, sessionStrictMode: sessionStrictMode2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
24704
24726
|
const cookieHeader = rawReq.headers.cookie ?? "";
|
|
24705
24727
|
const cookiePrefix = sessionCookieName2() + "=";
|
|
24706
24728
|
let existingSid;
|
|
@@ -24739,13 +24761,9 @@ async function sessionAutoStart(rawReq, rawRes, req2) {
|
|
|
24739
24761
|
} catch {
|
|
24740
24762
|
}
|
|
24741
24763
|
}
|
|
24742
|
-
const
|
|
24743
|
-
if (
|
|
24744
|
-
|
|
24745
|
-
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
24746
|
-
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
24747
|
-
const socketEncrypted = rawReq.socket?.encrypted === true;
|
|
24748
|
-
rawRes.appendHeader("Set-Cookie", buildSessionCookie2(newSid, ttl, void 0, forwardedProto, socketEncrypted));
|
|
24764
|
+
const sessionCookie = sessionCookieToSend2(sess, existingSid, rawReq, rawRes);
|
|
24765
|
+
if (sessionCookie !== null) {
|
|
24766
|
+
rawRes.appendHeader("Set-Cookie", sessionCookie);
|
|
24749
24767
|
}
|
|
24750
24768
|
return origEnd(...args);
|
|
24751
24769
|
};
|
|
@@ -23857,6 +23857,7 @@ __export(session_exports, {
|
|
|
23857
23857
|
isSecureScheme: () => isSecureScheme,
|
|
23858
23858
|
isValidSessionId: () => isValidSessionId,
|
|
23859
23859
|
sessionCookieName: () => sessionCookieName,
|
|
23860
|
+
sessionCookieToSend: () => sessionCookieToSend,
|
|
23860
23861
|
sessionStrictMode: () => sessionStrictMode
|
|
23861
23862
|
});
|
|
23862
23863
|
import { createHash as createHash5, randomBytes as randomBytes6 } from "node:crypto";
|
|
@@ -23917,6 +23918,15 @@ function buildSessionCookie(sessionId, ttl, cookieName, forwardedProto, socketEn
|
|
|
23917
23918
|
parts.push(`Max-Age=${ttl}`);
|
|
23918
23919
|
return parts.join("; ");
|
|
23919
23920
|
}
|
|
23921
|
+
function sessionCookieToSend(sess, existingSid, rawReq, rawRes) {
|
|
23922
|
+
const newSid = sess.sessionId ?? sess.getSessionId?.();
|
|
23923
|
+
if (!newSid || newSid === existingSid || sess.isFresh() || rawRes.headersSent) return null;
|
|
23924
|
+
const ttl = parseInt(process.env.TINA4_SESSION_TTL ?? "3600", 10);
|
|
23925
|
+
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
23926
|
+
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
23927
|
+
const socketEncrypted = rawReq.socket?.encrypted === true;
|
|
23928
|
+
return buildSessionCookie(newSid, ttl, void 0, forwardedProto, socketEncrypted);
|
|
23929
|
+
}
|
|
23920
23930
|
var SESSION_ID_PATTERN, VALID_SESSION_BACKENDS, CANONICAL_SESSION_BACKENDS, RETIRED_SESSION_BACKENDS, FileSessionHandler, RedisSessionHandler, FLASH_PREFIX, Session;
|
|
23921
23931
|
var init_session = __esm({
|
|
23922
23932
|
"src/session.ts"() {
|
|
@@ -24279,7 +24289,6 @@ var init_session = __esm({
|
|
|
24279
24289
|
const now = Math.floor(Date.now() / 1e3);
|
|
24280
24290
|
this.data = { _created: now, _accessed: now };
|
|
24281
24291
|
this.dirty = false;
|
|
24282
|
-
this.safeWrite(this.sessionId, this.data, this.ttl);
|
|
24283
24292
|
this.stored = false;
|
|
24284
24293
|
this.loaded = /* @__PURE__ */ new Map();
|
|
24285
24294
|
this.cleared = false;
|
|
@@ -24430,6 +24439,19 @@ var init_session = __esm({
|
|
|
24430
24439
|
const result = this.flash(key);
|
|
24431
24440
|
return result !== void 0 ? result : defaultValue;
|
|
24432
24441
|
}
|
|
24442
|
+
/**
|
|
24443
|
+
* Whether this session is only an id: nothing has stored it and it holds no
|
|
24444
|
+
* data of its own (its `_created` / `_accessed` bookkeeping does not count).
|
|
24445
|
+
* Such a session has no record and needs no cookie, so save() writes nothing
|
|
24446
|
+
* for it and the server sends no cookie for it. It stops being fresh when
|
|
24447
|
+
* something is stored in it. Calls that mark a session changed without leaving
|
|
24448
|
+
* anything in it (deleting a key it never had, clear(), regenerate() on an
|
|
24449
|
+
* empty session) leave it fresh: a record with no data is not a session.
|
|
24450
|
+
*/
|
|
24451
|
+
isFresh() {
|
|
24452
|
+
if (this.stored) return false;
|
|
24453
|
+
return !this.data || Object.keys(this.data).every((k) => k === "_created" || k === "_accessed");
|
|
24454
|
+
}
|
|
24433
24455
|
/**
|
|
24434
24456
|
* Get the current session ID.
|
|
24435
24457
|
*/
|
|
@@ -24492,7 +24514,7 @@ var init_session = __esm({
|
|
|
24492
24514
|
*/
|
|
24493
24515
|
save() {
|
|
24494
24516
|
if (!this.sessionId || !this.data) return true;
|
|
24495
|
-
if (
|
|
24517
|
+
if (this.isFresh()) return true;
|
|
24496
24518
|
let record = this.data;
|
|
24497
24519
|
if (this.stored) {
|
|
24498
24520
|
const current = this.safeRead(this.sessionId);
|
|
@@ -24679,7 +24701,7 @@ function errorLabel(err) {
|
|
|
24679
24701
|
return err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
24680
24702
|
}
|
|
24681
24703
|
async function sessionAutoStart(rawReq, rawRes, req2) {
|
|
24682
|
-
const { Session: Session2,
|
|
24704
|
+
const { Session: Session2, sessionCookieToSend: sessionCookieToSend2, sessionCookieName: sessionCookieName2, sessionStrictMode: sessionStrictMode2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
24683
24705
|
const cookieHeader = rawReq.headers.cookie ?? "";
|
|
24684
24706
|
const cookiePrefix = sessionCookieName2() + "=";
|
|
24685
24707
|
let existingSid;
|
|
@@ -24718,13 +24740,9 @@ async function sessionAutoStart(rawReq, rawRes, req2) {
|
|
|
24718
24740
|
} catch {
|
|
24719
24741
|
}
|
|
24720
24742
|
}
|
|
24721
|
-
const
|
|
24722
|
-
if (
|
|
24723
|
-
|
|
24724
|
-
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
24725
|
-
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
24726
|
-
const socketEncrypted = rawReq.socket?.encrypted === true;
|
|
24727
|
-
rawRes.appendHeader("Set-Cookie", buildSessionCookie2(newSid, ttl, void 0, forwardedProto, socketEncrypted));
|
|
24743
|
+
const sessionCookie = sessionCookieToSend2(sess, existingSid, rawReq, rawRes);
|
|
24744
|
+
if (sessionCookie !== null) {
|
|
24745
|
+
rawRes.appendHeader("Set-Cookie", sessionCookie);
|
|
24728
24746
|
}
|
|
24729
24747
|
return origEnd(...args);
|
|
24730
24748
|
};
|
|
@@ -367,7 +367,7 @@ export async function sessionAutoStart(
|
|
|
367
367
|
rawRes: ServerResponse,
|
|
368
368
|
req: Tina4Request,
|
|
369
369
|
): Promise<void> {
|
|
370
|
-
const { Session,
|
|
370
|
+
const { Session, sessionCookieToSend, sessionCookieName, sessionStrictMode } =
|
|
371
371
|
await import("./session.js");
|
|
372
372
|
const cookieHeader = rawReq.headers.cookie ?? "";
|
|
373
373
|
|
|
@@ -433,16 +433,8 @@ export async function sessionAutoStart(
|
|
|
433
433
|
try { sess.gc(); } catch { /* GC failure is non-critical */ }
|
|
434
434
|
}
|
|
435
435
|
|
|
436
|
-
const
|
|
437
|
-
if (
|
|
438
|
-
const ttl = parseInt(process.env.TINA4_SESSION_TTL ?? "3600", 10);
|
|
439
|
-
// Thread the client's real scheme in so an HTTPS deploy behind a
|
|
440
|
-
// TLS-terminating proxy ships the session cookie with `Secure`
|
|
441
|
-
// (nodejs#34). `x-forwarded-proto` is the same header request.ts trusts
|
|
442
|
-
// for URL construction; native socket TLS is the fallback.
|
|
443
|
-
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
444
|
-
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
445
|
-
const socketEncrypted = (rawReq.socket as { encrypted?: boolean })?.encrypted === true;
|
|
436
|
+
const sessionCookie = sessionCookieToSend(sess, existingSid, rawReq, rawRes);
|
|
437
|
+
if (sessionCookie !== null) {
|
|
446
438
|
// appendHeader, not setHeader (feature 131 fix, found while proving
|
|
447
439
|
// TC-DEC-02): setHeader REPLACES any existing Set-Cookie value wholesale,
|
|
448
440
|
// so a route that had already called response.cookie() of its own — on
|
|
@@ -452,7 +444,7 @@ export async function sessionAutoStart(
|
|
|
452
444
|
// appendHeader adds to whatever is already there (promoting a scalar to
|
|
453
445
|
// an array, extending an existing array) and behaves exactly like
|
|
454
446
|
// setHeader when nothing is set yet, so the common case is unchanged.
|
|
455
|
-
rawRes.appendHeader("Set-Cookie",
|
|
447
|
+
rawRes.appendHeader("Set-Cookie", sessionCookie);
|
|
456
448
|
}
|
|
457
449
|
return origEnd(...args);
|
|
458
450
|
} as typeof rawRes.end;
|
|
@@ -610,12 +610,14 @@ export class Session {
|
|
|
610
610
|
}
|
|
611
611
|
}
|
|
612
612
|
|
|
613
|
-
// Generate new session
|
|
613
|
+
// Generate new session. Nothing is written for it yet: a session nobody
|
|
614
|
+
// writes to has nothing to keep, and writing it here stored one record for
|
|
615
|
+
// every request that arrived without a cookie (static files, 404s and
|
|
616
|
+
// /health included). Its first save() after a change writes it whole.
|
|
614
617
|
this.sessionId = randomBytes(16).toString("hex");
|
|
615
618
|
const now = Math.floor(Date.now() / 1000);
|
|
616
619
|
this.data = { _created: now, _accessed: now };
|
|
617
620
|
this.dirty = false;
|
|
618
|
-
this.safeWrite(this.sessionId, this.data, this.ttl);
|
|
619
621
|
this.stored = false;
|
|
620
622
|
this.loaded = new Map();
|
|
621
623
|
this.cleared = false;
|
|
@@ -785,6 +787,20 @@ export class Session {
|
|
|
785
787
|
return result !== undefined ? result : defaultValue;
|
|
786
788
|
}
|
|
787
789
|
|
|
790
|
+
/**
|
|
791
|
+
* Whether this session is only an id: nothing has stored it and it holds no
|
|
792
|
+
* data of its own (its `_created` / `_accessed` bookkeeping does not count).
|
|
793
|
+
* Such a session has no record and needs no cookie, so save() writes nothing
|
|
794
|
+
* for it and the server sends no cookie for it. It stops being fresh when
|
|
795
|
+
* something is stored in it. Calls that mark a session changed without leaving
|
|
796
|
+
* anything in it (deleting a key it never had, clear(), regenerate() on an
|
|
797
|
+
* empty session) leave it fresh: a record with no data is not a session.
|
|
798
|
+
*/
|
|
799
|
+
isFresh(): boolean {
|
|
800
|
+
if (this.stored) return false;
|
|
801
|
+
return !this.data || Object.keys(this.data).every((k) => k === "_created" || k === "_accessed");
|
|
802
|
+
}
|
|
803
|
+
|
|
788
804
|
/**
|
|
789
805
|
* Get the current session ID.
|
|
790
806
|
*/
|
|
@@ -850,7 +866,7 @@ export class Session {
|
|
|
850
866
|
*/
|
|
851
867
|
save(): boolean {
|
|
852
868
|
if (!this.sessionId || !this.data) return true;
|
|
853
|
-
if (
|
|
869
|
+
if (this.isFresh()) return true;
|
|
854
870
|
let record: SessionData = this.data;
|
|
855
871
|
if (this.stored) {
|
|
856
872
|
const current = this.safeRead(this.sessionId);
|
|
@@ -1018,3 +1034,29 @@ export function buildSessionCookie(
|
|
|
1018
1034
|
parts.push(`Max-Age=${ttl}`);
|
|
1019
1035
|
return parts.join("; ");
|
|
1020
1036
|
}
|
|
1037
|
+
|
|
1038
|
+
/**
|
|
1039
|
+
* The Set-Cookie value that hands the client this request's session, or null
|
|
1040
|
+
* when none is owed: the client already holds the id, headers have gone out, or
|
|
1041
|
+
* the session is fresh. A fresh session (only an id: minted for this request
|
|
1042
|
+
* and holding no data) was not stored, so a cookie for it would name nothing:
|
|
1043
|
+
* the next request could not resume it and would be handed another one.
|
|
1044
|
+
*/
|
|
1045
|
+
export function sessionCookieToSend(
|
|
1046
|
+
sess: { isFresh(): boolean },
|
|
1047
|
+
existingSid: string | undefined,
|
|
1048
|
+
rawReq: { headers: Record<string, string | string[] | undefined>; socket?: unknown },
|
|
1049
|
+
rawRes: { headersSent: boolean },
|
|
1050
|
+
): string | null {
|
|
1051
|
+
const newSid = (sess as any).sessionId ?? (sess as any).getSessionId?.();
|
|
1052
|
+
if (!newSid || newSid === existingSid || sess.isFresh() || rawRes.headersSent) return null;
|
|
1053
|
+
const ttl = parseInt(process.env.TINA4_SESSION_TTL ?? "3600", 10);
|
|
1054
|
+
// Thread the client's real scheme in so an HTTPS deploy behind a
|
|
1055
|
+
// TLS-terminating proxy ships the session cookie with `Secure`
|
|
1056
|
+
// (nodejs#34). `x-forwarded-proto` is the same header request.ts trusts
|
|
1057
|
+
// for URL construction; native socket TLS is the fallback.
|
|
1058
|
+
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
1059
|
+
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
1060
|
+
const socketEncrypted = (rawReq.socket as { encrypted?: boolean } | undefined)?.encrypted === true;
|
|
1061
|
+
return buildSessionCookie(newSid, ttl, undefined, forwardedProto, socketEncrypted);
|
|
1062
|
+
}
|
|
@@ -11833,6 +11833,7 @@ __export(session_exports, {
|
|
|
11833
11833
|
isSecureScheme: () => isSecureScheme,
|
|
11834
11834
|
isValidSessionId: () => isValidSessionId,
|
|
11835
11835
|
sessionCookieName: () => sessionCookieName,
|
|
11836
|
+
sessionCookieToSend: () => sessionCookieToSend,
|
|
11836
11837
|
sessionStrictMode: () => sessionStrictMode
|
|
11837
11838
|
});
|
|
11838
11839
|
import { createHash as createHash5, randomBytes as randomBytes4 } from "node:crypto";
|
|
@@ -11893,6 +11894,15 @@ function buildSessionCookie(sessionId, ttl, cookieName, forwardedProto, socketEn
|
|
|
11893
11894
|
parts.push(`Max-Age=${ttl}`);
|
|
11894
11895
|
return parts.join("; ");
|
|
11895
11896
|
}
|
|
11897
|
+
function sessionCookieToSend(sess, existingSid, rawReq, rawRes) {
|
|
11898
|
+
const newSid = sess.sessionId ?? sess.getSessionId?.();
|
|
11899
|
+
if (!newSid || newSid === existingSid || sess.isFresh() || rawRes.headersSent) return null;
|
|
11900
|
+
const ttl = parseInt(process.env.TINA4_SESSION_TTL ?? "3600", 10);
|
|
11901
|
+
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
11902
|
+
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
11903
|
+
const socketEncrypted = rawReq.socket?.encrypted === true;
|
|
11904
|
+
return buildSessionCookie(newSid, ttl, void 0, forwardedProto, socketEncrypted);
|
|
11905
|
+
}
|
|
11896
11906
|
var SESSION_ID_PATTERN, VALID_SESSION_BACKENDS, CANONICAL_SESSION_BACKENDS, RETIRED_SESSION_BACKENDS, FileSessionHandler, RedisSessionHandler, FLASH_PREFIX, Session;
|
|
11897
11907
|
var init_session = __esm({
|
|
11898
11908
|
"../core/src/session.ts"() {
|
|
@@ -12255,7 +12265,6 @@ var init_session = __esm({
|
|
|
12255
12265
|
const now = Math.floor(Date.now() / 1e3);
|
|
12256
12266
|
this.data = { _created: now, _accessed: now };
|
|
12257
12267
|
this.dirty = false;
|
|
12258
|
-
this.safeWrite(this.sessionId, this.data, this.ttl);
|
|
12259
12268
|
this.stored = false;
|
|
12260
12269
|
this.loaded = /* @__PURE__ */ new Map();
|
|
12261
12270
|
this.cleared = false;
|
|
@@ -12406,6 +12415,19 @@ var init_session = __esm({
|
|
|
12406
12415
|
const result = this.flash(key);
|
|
12407
12416
|
return result !== void 0 ? result : defaultValue;
|
|
12408
12417
|
}
|
|
12418
|
+
/**
|
|
12419
|
+
* Whether this session is only an id: nothing has stored it and it holds no
|
|
12420
|
+
* data of its own (its `_created` / `_accessed` bookkeeping does not count).
|
|
12421
|
+
* Such a session has no record and needs no cookie, so save() writes nothing
|
|
12422
|
+
* for it and the server sends no cookie for it. It stops being fresh when
|
|
12423
|
+
* something is stored in it. Calls that mark a session changed without leaving
|
|
12424
|
+
* anything in it (deleting a key it never had, clear(), regenerate() on an
|
|
12425
|
+
* empty session) leave it fresh: a record with no data is not a session.
|
|
12426
|
+
*/
|
|
12427
|
+
isFresh() {
|
|
12428
|
+
if (this.stored) return false;
|
|
12429
|
+
return !this.data || Object.keys(this.data).every((k) => k === "_created" || k === "_accessed");
|
|
12430
|
+
}
|
|
12409
12431
|
/**
|
|
12410
12432
|
* Get the current session ID.
|
|
12411
12433
|
*/
|
|
@@ -12468,7 +12490,7 @@ var init_session = __esm({
|
|
|
12468
12490
|
*/
|
|
12469
12491
|
save() {
|
|
12470
12492
|
if (!this.sessionId || !this.data) return true;
|
|
12471
|
-
if (
|
|
12493
|
+
if (this.isFresh()) return true;
|
|
12472
12494
|
let record = this.data;
|
|
12473
12495
|
if (this.stored) {
|
|
12474
12496
|
const current = this.safeRead(this.sessionId);
|
|
@@ -12655,7 +12677,7 @@ function errorLabel(err) {
|
|
|
12655
12677
|
return err instanceof Error ? `${err.name}: ${err.message}` : String(err);
|
|
12656
12678
|
}
|
|
12657
12679
|
async function sessionAutoStart(rawReq, rawRes, req2) {
|
|
12658
|
-
const { Session: Session2,
|
|
12680
|
+
const { Session: Session2, sessionCookieToSend: sessionCookieToSend2, sessionCookieName: sessionCookieName2, sessionStrictMode: sessionStrictMode2 } = await Promise.resolve().then(() => (init_session(), session_exports));
|
|
12659
12681
|
const cookieHeader = rawReq.headers.cookie ?? "";
|
|
12660
12682
|
const cookiePrefix = sessionCookieName2() + "=";
|
|
12661
12683
|
let existingSid;
|
|
@@ -12694,13 +12716,9 @@ async function sessionAutoStart(rawReq, rawRes, req2) {
|
|
|
12694
12716
|
} catch {
|
|
12695
12717
|
}
|
|
12696
12718
|
}
|
|
12697
|
-
const
|
|
12698
|
-
if (
|
|
12699
|
-
|
|
12700
|
-
const xfProto = rawReq.headers["x-forwarded-proto"];
|
|
12701
|
-
const forwardedProto = Array.isArray(xfProto) ? xfProto[0] : xfProto;
|
|
12702
|
-
const socketEncrypted = rawReq.socket?.encrypted === true;
|
|
12703
|
-
rawRes.appendHeader("Set-Cookie", buildSessionCookie2(newSid, ttl, void 0, forwardedProto, socketEncrypted));
|
|
12719
|
+
const sessionCookie = sessionCookieToSend2(sess, existingSid, rawReq, rawRes);
|
|
12720
|
+
if (sessionCookie !== null) {
|
|
12721
|
+
rawRes.appendHeader("Set-Cookie", sessionCookie);
|
|
12704
12722
|
}
|
|
12705
12723
|
return origEnd(...args);
|
|
12706
12724
|
};
|
|
@@ -258,6 +258,16 @@ export declare class Session {
|
|
|
258
258
|
* Get flash data by key (alias for flash(key) without value).
|
|
259
259
|
*/
|
|
260
260
|
getFlash(key: string, defaultValue?: unknown): unknown;
|
|
261
|
+
/**
|
|
262
|
+
* Whether this session is only an id: nothing has stored it and it holds no
|
|
263
|
+
* data of its own (its `_created` / `_accessed` bookkeeping does not count).
|
|
264
|
+
* Such a session has no record and needs no cookie, so save() writes nothing
|
|
265
|
+
* for it and the server sends no cookie for it. It stops being fresh when
|
|
266
|
+
* something is stored in it. Calls that mark a session changed without leaving
|
|
267
|
+
* anything in it (deleting a key it never had, clear(), regenerate() on an
|
|
268
|
+
* empty session) leave it fresh: a record with no data is not a session.
|
|
269
|
+
*/
|
|
270
|
+
isFresh(): boolean;
|
|
261
271
|
/**
|
|
262
272
|
* Get the current session ID.
|
|
263
273
|
*/
|
|
@@ -388,4 +398,19 @@ export declare function sessionStrictMode(): boolean;
|
|
|
388
398
|
* http://localhost dev cookies undeliverable.
|
|
389
399
|
*/
|
|
390
400
|
export declare function buildSessionCookie(sessionId: string | null, ttl: number, cookieName?: string, forwardedProto?: string, socketEncrypted?: boolean): string;
|
|
401
|
+
/**
|
|
402
|
+
* The Set-Cookie value that hands the client this request's session, or null
|
|
403
|
+
* when none is owed: the client already holds the id, headers have gone out, or
|
|
404
|
+
* the session is fresh. A fresh session (only an id: minted for this request
|
|
405
|
+
* and holding no data) was not stored, so a cookie for it would name nothing:
|
|
406
|
+
* the next request could not resume it and would be handed another one.
|
|
407
|
+
*/
|
|
408
|
+
export declare function sessionCookieToSend(sess: {
|
|
409
|
+
isFresh(): boolean;
|
|
410
|
+
}, existingSid: string | undefined, rawReq: {
|
|
411
|
+
headers: Record<string, string | string[] | undefined>;
|
|
412
|
+
socket?: unknown;
|
|
413
|
+
}, rawRes: {
|
|
414
|
+
headersSent: boolean;
|
|
415
|
+
}): string | null;
|
|
391
416
|
export {};
|