@revealui/core 0.14.5 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
13
13
  import { isSafeUrl, sanitizeUrl } from '@revealui/security/sanitize';
14
14
  import { Fragment } from 'react';
15
15
  // URL sanitization is owned by @revealui/security (single source of truth
16
- // for every untrusted-string sink across RevFleet). Re-exported here so
16
+ // for every untrusted-string sink across RevealFleet). Re-exported here so
17
17
  // existing consumers keep working.
18
18
  export { isSafeUrl, sanitizeUrl };
19
19
  // ============================================
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Session cookie domain for a request host (GAP-343).
3
+ *
4
+ * Staging hosts always resolve to `staging.revealui.com` so a production
5
+ * `SESSION_COOKIE_DOMAIN` (for example `.revealui.com`) is not attached to
6
+ * the staging subtree. Every other host returns `configuredDomain` unchanged.
7
+ * This function does not read the environment and does not apply the
8
+ * production-only gate. Callers keep that gate.
9
+ */
10
+ /** Cookie Domain attribute for the staging.revealui.com subtree. No leading dot. */
11
+ export declare const STAGING_SESSION_COOKIE_DOMAIN = "staging.revealui.com";
12
+ /**
13
+ * Hostname from a Host or X-Forwarded-Host value.
14
+ * Strips a scheme, path, trailing dot, and numeric port. Uses the first
15
+ * comma-separated entry. Returns null when nothing usable remains.
16
+ * String methods only (no regular expressions).
17
+ */
18
+ export declare function normalizeRequestHost(raw: string | null | undefined): string | null;
19
+ /**
20
+ * True for `staging.revealui.com` and any host under `*.staging.revealui.com`.
21
+ * Label-boundary match: `evilstaging.revealui.com` and
22
+ * `staging.revealui.com.evil.com` are not staging hosts.
23
+ */
24
+ export declare function isStagingRevealHost(requestHost: string | null | undefined): boolean;
25
+ /**
26
+ * Cookie Domain for this request.
27
+ *
28
+ * Staging hosts return `staging.revealui.com` even when `configuredDomain`
29
+ * is the production domain or unset. Every other host, including a missing
30
+ * host, returns `configuredDomain` exactly (including `undefined`).
31
+ */
32
+ export declare function sessionCookieDomainForHost(requestHost: string | null | undefined, configuredDomain: string | undefined): string | undefined;
33
+ /**
34
+ * Public request host. Prefer `Host` so a client-supplied `X-Forwarded-Host`
35
+ * cannot override the host the platform already set. Fall back to
36
+ * `X-Forwarded-Host` only when `Host` is absent.
37
+ */
38
+ export declare function requestHostFromHeaders(getHeader: (name: string) => string | null | undefined): string | null;
39
+ //# sourceMappingURL=session-cookie-domain.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-cookie-domain.d.ts","sourceRoot":"","sources":["../src/session-cookie-domain.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,oFAAoF;AACpF,eAAO,MAAM,6BAA6B,yBAAyB,CAAC;AAEpE;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CA0ClF;AAUD;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAKnF;AAED;;;;;;GAMG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACtC,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,MAAM,GAAG,SAAS,CAKpB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,GAAG,IAAI,GAAG,SAAS,GACrD,MAAM,GAAG,IAAI,CAMf"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Session cookie domain for a request host (GAP-343).
3
+ *
4
+ * Staging hosts always resolve to `staging.revealui.com` so a production
5
+ * `SESSION_COOKIE_DOMAIN` (for example `.revealui.com`) is not attached to
6
+ * the staging subtree. Every other host returns `configuredDomain` unchanged.
7
+ * This function does not read the environment and does not apply the
8
+ * production-only gate. Callers keep that gate.
9
+ */
10
+ /** Cookie Domain attribute for the staging.revealui.com subtree. No leading dot. */
11
+ export const STAGING_SESSION_COOKIE_DOMAIN = 'staging.revealui.com';
12
+ /**
13
+ * Hostname from a Host or X-Forwarded-Host value.
14
+ * Strips a scheme, path, trailing dot, and numeric port. Uses the first
15
+ * comma-separated entry. Returns null when nothing usable remains.
16
+ * String methods only (no regular expressions).
17
+ */
18
+ export function normalizeRequestHost(raw) {
19
+ if (raw == null)
20
+ return null;
21
+ let value = raw.trim().toLowerCase();
22
+ if (value.length === 0)
23
+ return null;
24
+ const comma = value.indexOf(',');
25
+ if (comma !== -1) {
26
+ value = value.slice(0, comma).trim();
27
+ }
28
+ if (value.length === 0)
29
+ return null;
30
+ const scheme = value.indexOf('://');
31
+ if (scheme !== -1) {
32
+ value = value.slice(scheme + 3);
33
+ }
34
+ const slash = value.indexOf('/');
35
+ if (slash !== -1) {
36
+ value = value.slice(0, slash);
37
+ }
38
+ if (value.startsWith('[')) {
39
+ const end = value.indexOf(']');
40
+ if (end === -1)
41
+ return null;
42
+ const bracketed = value.slice(0, end + 1);
43
+ return bracketed.length > 2 ? bracketed : null;
44
+ }
45
+ const colon = value.lastIndexOf(':');
46
+ if (colon !== -1) {
47
+ const port = value.slice(colon + 1);
48
+ if (port.length > 0 && isAllDigits(port)) {
49
+ value = value.slice(0, colon);
50
+ }
51
+ }
52
+ if (value.endsWith('.')) {
53
+ value = value.slice(0, -1);
54
+ }
55
+ if (value.length === 0)
56
+ return null;
57
+ return value;
58
+ }
59
+ function isAllDigits(value) {
60
+ for (let i = 0; i < value.length; i++) {
61
+ const code = value.charCodeAt(i);
62
+ if (code < 48 || code > 57)
63
+ return false;
64
+ }
65
+ return true;
66
+ }
67
+ /**
68
+ * True for `staging.revealui.com` and any host under `*.staging.revealui.com`.
69
+ * Label-boundary match: `evilstaging.revealui.com` and
70
+ * `staging.revealui.com.evil.com` are not staging hosts.
71
+ */
72
+ export function isStagingRevealHost(requestHost) {
73
+ const host = normalizeRequestHost(requestHost);
74
+ if (!host)
75
+ return false;
76
+ if (host === STAGING_SESSION_COOKIE_DOMAIN)
77
+ return true;
78
+ return host.endsWith(`.${STAGING_SESSION_COOKIE_DOMAIN}`);
79
+ }
80
+ /**
81
+ * Cookie Domain for this request.
82
+ *
83
+ * Staging hosts return `staging.revealui.com` even when `configuredDomain`
84
+ * is the production domain or unset. Every other host, including a missing
85
+ * host, returns `configuredDomain` exactly (including `undefined`).
86
+ */
87
+ export function sessionCookieDomainForHost(requestHost, configuredDomain) {
88
+ if (isStagingRevealHost(requestHost)) {
89
+ return STAGING_SESSION_COOKIE_DOMAIN;
90
+ }
91
+ return configuredDomain;
92
+ }
93
+ /**
94
+ * Public request host. Prefer `Host` so a client-supplied `X-Forwarded-Host`
95
+ * cannot override the host the platform already set. Fall back to
96
+ * `X-Forwarded-Host` only when `Host` is absent.
97
+ */
98
+ export function requestHostFromHeaders(getHeader) {
99
+ const host = getHeader('host');
100
+ if (typeof host === 'string' && host.trim().length > 0)
101
+ return host;
102
+ const forwarded = getHeader('x-forwarded-host');
103
+ if (typeof forwarded === 'string' && forwarded.trim().length > 0)
104
+ return forwarded;
105
+ return null;
106
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revealui/core",
3
- "version": "0.14.5",
3
+ "version": "0.15.0",
4
4
  "description": "RevealUI's runtime engine — admin UI, REST API, auth, rich text, plugin system, and access control. The heart of the open-source platform.",
5
5
  "license": "MIT",
6
6
  "dependencies": {
@@ -23,8 +23,8 @@
23
23
  "pg": "^8.22.0",
24
24
  "yjs": "^13.6.31",
25
25
  "zod": "^4.4.3",
26
- "@revealui/contracts": "0.9.0",
27
- "@revealui/presentation": "0.14.5",
26
+ "@revealui/contracts": "0.12.0",
27
+ "@revealui/presentation": "0.14.8",
28
28
  "@revealui/resilience": "0.2.4",
29
29
  "@revealui/security": "0.8.2",
30
30
  "@revealui/utils": "0.3.6"
@@ -35,11 +35,11 @@
35
35
  "@types/pg": "^8.20.0",
36
36
  "@types/react": "^19.2.17",
37
37
  "@types/react-dom": "^19.2.3",
38
- "@vitest/coverage-v8": "^4.1.10",
38
+ "@vitest/coverage-v8": "^4.1.11",
39
39
  "expect-type": "^1.4.0",
40
40
  "jsdom": "29.1.1",
41
41
  "tsup": "^8.5.1",
42
- "vitest": "^4.1.10",
42
+ "vitest": "^4.1.11",
43
43
  "@revealui/dev": "0.1.0"
44
44
  },
45
45
  "engines": {
@@ -66,6 +66,11 @@
66
66
  "import": "./dist/deployment-mode.js",
67
67
  "default": "./dist/deployment-mode.js"
68
68
  },
69
+ "./session-cookie-domain": {
70
+ "types": "./dist/session-cookie-domain.d.ts",
71
+ "import": "./dist/session-cookie-domain.js",
72
+ "default": "./dist/session-cookie-domain.js"
73
+ },
69
74
  "./margin-governor": {
70
75
  "types": "./dist/margin-governor.d.ts",
71
76
  "import": "./dist/margin-governor.js",