mates-fullstack 1.0.0-beta.1

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.
Files changed (202) hide show
  1. package/README.md +311 -0
  2. package/dist/arctic-auth.d.ts +101 -0
  3. package/dist/arctic-auth.d.ts.map +1 -0
  4. package/dist/arctic-auth.js +538 -0
  5. package/dist/arctic-auth.js.map +1 -0
  6. package/dist/asset-manifest.d.ts +14 -0
  7. package/dist/asset-manifest.d.ts.map +1 -0
  8. package/dist/asset-manifest.js +102 -0
  9. package/dist/asset-manifest.js.map +1 -0
  10. package/dist/browser.d.ts +18 -0
  11. package/dist/browser.d.ts.map +1 -0
  12. package/dist/browser.js +25 -0
  13. package/dist/browser.js.map +1 -0
  14. package/dist/build-esbuild.d.ts +29 -0
  15. package/dist/build-esbuild.d.ts.map +1 -0
  16. package/dist/build-esbuild.js +699 -0
  17. package/dist/build-esbuild.js.map +1 -0
  18. package/dist/build-prod.d.ts +126 -0
  19. package/dist/build-prod.d.ts.map +1 -0
  20. package/dist/build-prod.js +1014 -0
  21. package/dist/build-prod.js.map +1 -0
  22. package/dist/cli-new.d.ts +14 -0
  23. package/dist/cli-new.d.ts.map +1 -0
  24. package/dist/cli-new.js +637 -0
  25. package/dist/cli-new.js.map +1 -0
  26. package/dist/client.d.ts +43 -0
  27. package/dist/client.d.ts.map +1 -0
  28. package/dist/client.js +130 -0
  29. package/dist/client.js.map +1 -0
  30. package/dist/cors.d.ts +16 -0
  31. package/dist/cors.d.ts.map +1 -0
  32. package/dist/cors.js +60 -0
  33. package/dist/cors.js.map +1 -0
  34. package/dist/ctx.d.ts +78 -0
  35. package/dist/ctx.d.ts.map +1 -0
  36. package/dist/ctx.js +280 -0
  37. package/dist/ctx.js.map +1 -0
  38. package/dist/dev-watcher.d.ts +23 -0
  39. package/dist/dev-watcher.d.ts.map +1 -0
  40. package/dist/dev-watcher.js +136 -0
  41. package/dist/dev-watcher.js.map +1 -0
  42. package/dist/docs-generator.d.ts +69 -0
  43. package/dist/docs-generator.d.ts.map +1 -0
  44. package/dist/docs-generator.js +557 -0
  45. package/dist/docs-generator.js.map +1 -0
  46. package/dist/docs-page.d.ts +20 -0
  47. package/dist/docs-page.d.ts.map +1 -0
  48. package/dist/docs-page.js +1152 -0
  49. package/dist/docs-page.js.map +1 -0
  50. package/dist/download.d.ts +78 -0
  51. package/dist/download.d.ts.map +1 -0
  52. package/dist/download.js +202 -0
  53. package/dist/download.js.map +1 -0
  54. package/dist/env-loader.d.ts +76 -0
  55. package/dist/env-loader.d.ts.map +1 -0
  56. package/dist/env-loader.js +213 -0
  57. package/dist/env-loader.js.map +1 -0
  58. package/dist/errors.d.ts +146 -0
  59. package/dist/errors.d.ts.map +1 -0
  60. package/dist/errors.js +386 -0
  61. package/dist/errors.js.map +1 -0
  62. package/dist/head.d.ts +31 -0
  63. package/dist/head.d.ts.map +1 -0
  64. package/dist/head.js +245 -0
  65. package/dist/head.js.map +1 -0
  66. package/dist/index.d.ts +30 -0
  67. package/dist/index.d.ts.map +1 -0
  68. package/dist/index.js +30 -0
  69. package/dist/index.js.map +1 -0
  70. package/dist/internal-prefixes.d.ts +16 -0
  71. package/dist/internal-prefixes.d.ts.map +1 -0
  72. package/dist/internal-prefixes.js +16 -0
  73. package/dist/internal-prefixes.js.map +1 -0
  74. package/dist/internal.d.ts +25 -0
  75. package/dist/internal.d.ts.map +1 -0
  76. package/dist/internal.js +25 -0
  77. package/dist/internal.js.map +1 -0
  78. package/dist/jwt.d.ts +166 -0
  79. package/dist/jwt.d.ts.map +1 -0
  80. package/dist/jwt.js +261 -0
  81. package/dist/jwt.js.map +1 -0
  82. package/dist/log.d.ts +44 -0
  83. package/dist/log.d.ts.map +1 -0
  84. package/dist/log.js +66 -0
  85. package/dist/log.js.map +1 -0
  86. package/dist/logger.d.ts +76 -0
  87. package/dist/logger.d.ts.map +1 -0
  88. package/dist/logger.js +138 -0
  89. package/dist/logger.js.map +1 -0
  90. package/dist/main-runner.d.ts +59 -0
  91. package/dist/main-runner.d.ts.map +1 -0
  92. package/dist/main-runner.js +157 -0
  93. package/dist/main-runner.js.map +1 -0
  94. package/dist/mates-auth.d.ts +82 -0
  95. package/dist/mates-auth.d.ts.map +1 -0
  96. package/dist/mates-auth.js +323 -0
  97. package/dist/mates-auth.js.map +1 -0
  98. package/dist/middleware.d.ts +30 -0
  99. package/dist/middleware.d.ts.map +1 -0
  100. package/dist/middleware.js +67 -0
  101. package/dist/middleware.js.map +1 -0
  102. package/dist/project-resolver.d.ts +102 -0
  103. package/dist/project-resolver.d.ts.map +1 -0
  104. package/dist/project-resolver.js +271 -0
  105. package/dist/project-resolver.js.map +1 -0
  106. package/dist/rate-limit.d.ts +37 -0
  107. package/dist/rate-limit.d.ts.map +1 -0
  108. package/dist/rate-limit.js +109 -0
  109. package/dist/rate-limit.js.map +1 -0
  110. package/dist/redirect.d.ts +84 -0
  111. package/dist/redirect.d.ts.map +1 -0
  112. package/dist/redirect.js +105 -0
  113. package/dist/redirect.js.map +1 -0
  114. package/dist/renderer.d.ts +91 -0
  115. package/dist/renderer.d.ts.map +1 -0
  116. package/dist/renderer.js +630 -0
  117. package/dist/renderer.js.map +1 -0
  118. package/dist/request-logger.d.ts +12 -0
  119. package/dist/request-logger.d.ts.map +1 -0
  120. package/dist/request-logger.js +55 -0
  121. package/dist/request-logger.js.map +1 -0
  122. package/dist/rest.d.ts +25 -0
  123. package/dist/rest.d.ts.map +1 -0
  124. package/dist/rest.js +93 -0
  125. package/dist/rest.js.map +1 -0
  126. package/dist/router.d.ts +71 -0
  127. package/dist/router.d.ts.map +1 -0
  128. package/dist/router.js +222 -0
  129. package/dist/router.js.map +1 -0
  130. package/dist/rpc-registry.d.ts +84 -0
  131. package/dist/rpc-registry.d.ts.map +1 -0
  132. package/dist/rpc-registry.js +271 -0
  133. package/dist/rpc-registry.js.map +1 -0
  134. package/dist/rpc-runner.d.ts +82 -0
  135. package/dist/rpc-runner.d.ts.map +1 -0
  136. package/dist/rpc-runner.js +564 -0
  137. package/dist/rpc-runner.js.map +1 -0
  138. package/dist/sanitize.d.ts +61 -0
  139. package/dist/sanitize.d.ts.map +1 -0
  140. package/dist/sanitize.js +193 -0
  141. package/dist/sanitize.js.map +1 -0
  142. package/dist/security-headers.d.ts +114 -0
  143. package/dist/security-headers.d.ts.map +1 -0
  144. package/dist/security-headers.js +121 -0
  145. package/dist/security-headers.js.map +1 -0
  146. package/dist/server-fn.d.ts +323 -0
  147. package/dist/server-fn.d.ts.map +1 -0
  148. package/dist/server-fn.js +373 -0
  149. package/dist/server-fn.js.map +1 -0
  150. package/dist/server-public.d.ts +13 -0
  151. package/dist/server-public.d.ts.map +1 -0
  152. package/dist/server-public.js +12 -0
  153. package/dist/server-public.js.map +1 -0
  154. package/dist/server-timeout.d.ts +38 -0
  155. package/dist/server-timeout.d.ts.map +1 -0
  156. package/dist/server-timeout.js +46 -0
  157. package/dist/server-timeout.js.map +1 -0
  158. package/dist/server.d.ts +100 -0
  159. package/dist/server.d.ts.map +1 -0
  160. package/dist/server.js +1218 -0
  161. package/dist/server.js.map +1 -0
  162. package/dist/socket-router.d.ts +153 -0
  163. package/dist/socket-router.d.ts.map +1 -0
  164. package/dist/socket-router.js +612 -0
  165. package/dist/socket-router.js.map +1 -0
  166. package/dist/sso.d.ts +90 -0
  167. package/dist/sso.d.ts.map +1 -0
  168. package/dist/sso.js +261 -0
  169. package/dist/sso.js.map +1 -0
  170. package/dist/ssr-context.d.ts +49 -0
  171. package/dist/ssr-context.d.ts.map +1 -0
  172. package/dist/ssr-context.js +85 -0
  173. package/dist/ssr-context.js.map +1 -0
  174. package/dist/ssr-globals.d.ts +32 -0
  175. package/dist/ssr-globals.d.ts.map +1 -0
  176. package/dist/ssr-globals.js +1010 -0
  177. package/dist/ssr-globals.js.map +1 -0
  178. package/dist/ssr-template.d.ts +73 -0
  179. package/dist/ssr-template.d.ts.map +1 -0
  180. package/dist/ssr-template.js +507 -0
  181. package/dist/ssr-template.js.map +1 -0
  182. package/dist/stack-mapper.d.ts +25 -0
  183. package/dist/stack-mapper.d.ts.map +1 -0
  184. package/dist/stack-mapper.js +139 -0
  185. package/dist/stack-mapper.js.map +1 -0
  186. package/dist/stream.d.ts +89 -0
  187. package/dist/stream.d.ts.map +1 -0
  188. package/dist/stream.js +299 -0
  189. package/dist/stream.js.map +1 -0
  190. package/dist/upload.d.ts +69 -0
  191. package/dist/upload.d.ts.map +1 -0
  192. package/dist/upload.js +110 -0
  193. package/dist/upload.js.map +1 -0
  194. package/dist/validate.d.ts +58 -0
  195. package/dist/validate.d.ts.map +1 -0
  196. package/dist/validate.js +89 -0
  197. package/dist/validate.js.map +1 -0
  198. package/dist/verify-package.d.ts +3 -0
  199. package/dist/verify-package.d.ts.map +1 -0
  200. package/dist/verify-package.js +128 -0
  201. package/dist/verify-package.js.map +1 -0
  202. package/package.json +79 -0
package/dist/sso.d.ts ADDED
@@ -0,0 +1,90 @@
1
+ /**
2
+ * mates-fullstack — sso.ts
3
+ *
4
+ * Optional cross-domain SSO middleware.
5
+ *
6
+ * Setup:
7
+ * Auth server (auth.com): useSsoProvider({ secret, login: "/login" })
8
+ * App server (app1.com): useSsoClient({ authUrl, secret, protected: ["/dashboard"] })
9
+ *
10
+ * Flow:
11
+ * app1.com/dashboard → auth.com/api/sso/code?redirect=... → login (if needed)
12
+ * → code JWT → app1.com/auth/sso/callback → auth.login()
13
+ */
14
+ export interface SsoProviderOptions {
15
+ /**
16
+ * Shared secret between auth server and app servers.
17
+ * Used to sign and verify the short-lived code JWTs.
18
+ */
19
+ secret: string;
20
+ /**
21
+ * Path to the login page on the auth server.
22
+ * Unauthenticated users are redirected here with a `?next` param
23
+ * so they return to `/api/sso/code` after login.
24
+ * @default "/login"
25
+ */
26
+ login?: string;
27
+ /**
28
+ * Lifetime of the code JWT in seconds.
29
+ * @default 30
30
+ */
31
+ codeLifetime?: number;
32
+ /**
33
+ * Allowed redirect origins for the `redirect` parameter.
34
+ * Only URLs starting with these prefixes are accepted.
35
+ * Must include the protocol (e.g. "https://app1.com").
36
+ * When unset, only relative paths (starting with `/`) are allowed.
37
+ */
38
+ allowedOrigins?: string[];
39
+ }
40
+ export interface SsoClientOptions {
41
+ /**
42
+ * Base URL of the auth server (e.g. "https://auth.com").
43
+ */
44
+ authUrl: string;
45
+ /**
46
+ * Shared secret — must match exactly what the auth server uses.
47
+ */
48
+ secret: string;
49
+ /**
50
+ * Paths that require authentication.
51
+ * Unauthenticated visitors are redirected to the auth server.
52
+ * @default []
53
+ */
54
+ protected?: string[];
55
+ /**
56
+ * Where to redirect after a successful SSO login.
57
+ * @default "/"
58
+ */
59
+ afterLogin?: string;
60
+ }
61
+ /**
62
+ * Register SSO provider endpoints on the auth server.
63
+ *
64
+ * Creates:
65
+ * GET /api/sso/code — generates a code JWT if authenticated
66
+ * GET /api/sso/after-login — where the login page redirects after auth
67
+ *
68
+ * @example
69
+ * useSsoProvider({
70
+ * secret: process.env.SSO_SECRET!,
71
+ * login: "/login",
72
+ * });
73
+ */
74
+ export declare function useSsoProvider(options: SsoProviderOptions): void;
75
+ /**
76
+ * Register SSO client endpoints and guarded routes on an app server.
77
+ *
78
+ * Creates:
79
+ * GET /auth/sso/callback — verifies the code JWT, calls auth.login()
80
+ * onRequest guard — redirects protected routes to the auth server
81
+ *
82
+ * @example
83
+ * useSsoClient({
84
+ * authUrl: "https://auth.com",
85
+ * secret: process.env.SSO_SECRET!,
86
+ * protected: ["/dashboard", "/settings"],
87
+ * });
88
+ */
89
+ export declare function useSsoClient(options: SsoClientOptions): void;
90
+ //# sourceMappingURL=sso.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.d.ts","sourceRoot":"","sources":["../src/sso.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AA4CD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CA8GhE;AAMD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,CAyE5D"}
package/dist/sso.js ADDED
@@ -0,0 +1,261 @@
1
+ /**
2
+ * mates-fullstack — sso.ts
3
+ *
4
+ * Optional cross-domain SSO middleware.
5
+ *
6
+ * Setup:
7
+ * Auth server (auth.com): useSsoProvider({ secret, login: "/login" })
8
+ * App server (app1.com): useSsoClient({ authUrl, secret, protected: ["/dashboard"] })
9
+ *
10
+ * Flow:
11
+ * app1.com/dashboard → auth.com/api/sso/code?redirect=... → login (if needed)
12
+ * → code JWT → app1.com/auth/sso/callback → auth.login()
13
+ */
14
+ import { onRequest } from "./middleware.js";
15
+ import { auth } from "./mates-auth.js";
16
+ import { jwt } from "./jwt.js";
17
+ // ─── Cookie helpers (internal, temporary) ─────────────────────────────────────
18
+ const SSO_REDIRECT_COOKIE = "sso_redirect";
19
+ const SSO_AFTER_LOGIN_COOKIE = "sso_after_login";
20
+ const SSO_CODE_PARAM = "code";
21
+ function _setTempCookie(ctx, name, value, path) {
22
+ ctx.cookie.set(name, value, {
23
+ httpOnly: true,
24
+ sameSite: "lax",
25
+ path,
26
+ maxAge: 300, // 5 min
27
+ secure: process.env.NODE_ENV === "production",
28
+ });
29
+ }
30
+ function _clearTempCookie(ctx, name, path) {
31
+ ctx.cookie.delete(name, { path });
32
+ }
33
+ /**
34
+ * Resolve the public origin for the current request.
35
+ * Used by the client to build the callback URL for the auth server.
36
+ */
37
+ function _publicOrigin(req) {
38
+ const url = new URL(req.url);
39
+ const host = req.headers.get("x-forwarded-host") ?? url.host;
40
+ const proto = req.headers.get("x-forwarded-proto") ?? url.protocol.slice(0, -1);
41
+ return `${proto}://${host}`;
42
+ }
43
+ // ─── Provider side (auth.com) ────────────────────────────────────────────────
44
+ const SSO_CODE_PATH = "/api/sso/code";
45
+ const SSO_AFTER_LOGIN_PATH = "/api/sso/after-login";
46
+ /**
47
+ * Register SSO provider endpoints on the auth server.
48
+ *
49
+ * Creates:
50
+ * GET /api/sso/code — generates a code JWT if authenticated
51
+ * GET /api/sso/after-login — where the login page redirects after auth
52
+ *
53
+ * @example
54
+ * useSsoProvider({
55
+ * secret: process.env.SSO_SECRET!,
56
+ * login: "/login",
57
+ * });
58
+ */
59
+ export function useSsoProvider(options) {
60
+ const secret = options.secret;
61
+ if (!secret || secret.length < 8) {
62
+ throw new Error("useSsoProvider: secret must be at least 8 characters.");
63
+ }
64
+ const login = options.login ?? "/login";
65
+ const codeLifetime = options.codeLifetime ?? 30;
66
+ const tokens = jwt(secret);
67
+ onRequest(async (c) => {
68
+ const url = new URL(c.req.raw.url);
69
+ const pathname = url.pathname;
70
+ // ── Entry: GET /api/sso/code?redirect=... ──────────────────────────────
71
+ if (pathname === SSO_CODE_PATH) {
72
+ if (c.req.method !== "GET") {
73
+ return new Response(JSON.stringify({ error: "Method not allowed" }), {
74
+ status: 405,
75
+ headers: { "content-type": "application/json" },
76
+ });
77
+ }
78
+ const redirect = url.searchParams.get("redirect");
79
+ if (!redirect) {
80
+ return new Response(JSON.stringify({ error: 'Missing "redirect" parameter' }), { status: 400, headers: { "content-type": "application/json" } });
81
+ }
82
+ if (!_isValidRedirect(redirect, options.allowedOrigins)) {
83
+ return new Response(JSON.stringify({ error: "Invalid redirect target" }), { status: 400, headers: { "content-type": "application/json" } });
84
+ }
85
+ // Store the redirect target in a cookie so it survives login redirect
86
+ _setTempCookie(c, SSO_REDIRECT_COOKIE, redirect, SSO_CODE_PATH);
87
+ _setTempCookie(c, SSO_AFTER_LOGIN_COOKIE, redirect, SSO_AFTER_LOGIN_PATH);
88
+ const userId = (c.auth?.sub ?? c.auth?.userId);
89
+ if (!userId) {
90
+ // Not authenticated — redirect to login page
91
+ const next = encodeURIComponent(SSO_CODE_PATH);
92
+ const loginUrl = `${login}?next=${next}`;
93
+ return new Response(null, {
94
+ status: 302,
95
+ headers: { location: loginUrl },
96
+ });
97
+ }
98
+ // Authenticated — generate a short-lived code JWT and redirect
99
+ const code = await tokens.sign({
100
+ sub: userId,
101
+ ..._pickClaims(c.auth ?? {}),
102
+ jti: crypto.randomUUID(),
103
+ }, { expiresIn: codeLifetime });
104
+ _clearTempCookie(c, SSO_REDIRECT_COOKIE, SSO_CODE_PATH);
105
+ _clearTempCookie(c, SSO_AFTER_LOGIN_COOKIE, SSO_AFTER_LOGIN_PATH);
106
+ const location = `${redirect}${redirect.includes("?") ? "&" : "?"}${SSO_CODE_PARAM}=${code}`;
107
+ return new Response(null, { status: 302, headers: { location } });
108
+ }
109
+ // ── After login: GET /api/sso/after-login ──────────────────────────────
110
+ if (pathname === SSO_AFTER_LOGIN_PATH) {
111
+ if (c.req.method !== "GET") {
112
+ return new Response(JSON.stringify({ error: "Method not allowed" }), {
113
+ status: 405,
114
+ headers: { "content-type": "application/json" },
115
+ });
116
+ }
117
+ const redirect = c.cookie.get(SSO_AFTER_LOGIN_COOKIE) ??
118
+ c.cookie.get(SSO_REDIRECT_COOKIE);
119
+ _clearTempCookie(c, SSO_AFTER_LOGIN_COOKIE, SSO_AFTER_LOGIN_PATH);
120
+ const userId = (c.auth?.sub ?? c.auth?.userId);
121
+ if (!userId) {
122
+ const loginUrl = `${login}?next=${encodeURIComponent(SSO_AFTER_LOGIN_PATH)}`;
123
+ return new Response(null, {
124
+ status: 302,
125
+ headers: { location: loginUrl },
126
+ });
127
+ }
128
+ if (redirect) {
129
+ const code = await tokens.sign({
130
+ sub: userId,
131
+ ..._pickClaims(c.auth ?? {}),
132
+ jti: crypto.randomUUID(),
133
+ }, { expiresIn: codeLifetime });
134
+ const location = `${redirect}${redirect.includes("?") ? "&" : "?"}${SSO_CODE_PARAM}=${code}`;
135
+ return new Response(null, { status: 302, headers: { location } });
136
+ }
137
+ // No redirect stored — go to home
138
+ return new Response(null, { status: 302, headers: { location: "/" } });
139
+ }
140
+ });
141
+ }
142
+ // ─── Client side (app1.com, app2.com) ─────────────────────────────────────────
143
+ const SSO_CALLBACK_PATH = "/auth/sso/callback";
144
+ /**
145
+ * Register SSO client endpoints and guarded routes on an app server.
146
+ *
147
+ * Creates:
148
+ * GET /auth/sso/callback — verifies the code JWT, calls auth.login()
149
+ * onRequest guard — redirects protected routes to the auth server
150
+ *
151
+ * @example
152
+ * useSsoClient({
153
+ * authUrl: "https://auth.com",
154
+ * secret: process.env.SSO_SECRET!,
155
+ * protected: ["/dashboard", "/settings"],
156
+ * });
157
+ */
158
+ export function useSsoClient(options) {
159
+ const { authUrl, secret, afterLogin } = options;
160
+ const protectedPaths = options.protected ?? [];
161
+ if (!secret || secret.length < 8) {
162
+ throw new Error("useSsoClient: secret must be at least 8 characters.");
163
+ }
164
+ if (!authUrl) {
165
+ throw new Error("useSsoClient: authUrl is required.");
166
+ }
167
+ const tokens = jwt(secret);
168
+ // ── Callback: GET /auth/sso/callback?code=... ────────────────────────────
169
+ onRequest(async (c) => {
170
+ const url = new URL(c.req.raw.url);
171
+ if (url.pathname !== SSO_CALLBACK_PATH)
172
+ return;
173
+ const code = url.searchParams.get(SSO_CODE_PARAM);
174
+ const next = url.searchParams.get("next");
175
+ if (!code) {
176
+ return new Response(JSON.stringify({ error: `Missing "${SSO_CODE_PARAM}" parameter` }), { status: 400, headers: { "content-type": "application/json" } });
177
+ }
178
+ // Verify the code JWT — ensures it was signed by the auth server
179
+ const payload = await tokens.verify(code);
180
+ if (!payload) {
181
+ return new Response(JSON.stringify({ error: "Invalid or expired code" }), { status: 401, headers: { "content-type": "application/json" } });
182
+ }
183
+ // Extract claims — strip JWT timing fields
184
+ const { iat: _iat, exp: _exp, nbf: _nbf, jti: _jti, ...claims } = payload;
185
+ await auth.login(c, claims);
186
+ const location = next ?? afterLogin ?? "/";
187
+ return new Response(null, { status: 302, headers: { location } });
188
+ });
189
+ // ── Guard protected routes ──────────────────────────────────────────────
190
+ if (protectedPaths.length > 0) {
191
+ onRequest((c) => {
192
+ const url = new URL(c.req.raw.url);
193
+ const pathname = url.pathname;
194
+ const isProtected = protectedPaths.some((p) => pathname === p || pathname.startsWith(p + "/"));
195
+ if (!isProtected)
196
+ return;
197
+ // Already authenticated
198
+ const userId = c.auth?.sub ?? c.auth?.userId;
199
+ if (userId)
200
+ return;
201
+ // Redirect to auth server
202
+ const origin = _publicOrigin(c.req.raw);
203
+ const callbackUrl = `${origin}${SSO_CALLBACK_PATH}`;
204
+ const codeUrl = `${authUrl}${SSO_CODE_PATH}`;
205
+ const redirectTarget = `${codeUrl}?redirect=${encodeURIComponent(callbackUrl + "?next=" + encodeURIComponent(pathname))}`;
206
+ return new Response(null, {
207
+ status: 302,
208
+ headers: { location: redirectTarget },
209
+ });
210
+ });
211
+ }
212
+ }
213
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
214
+ /**
215
+ * Validate the redirect URL to prevent open redirect attacks.
216
+ * - Relative paths (starting with `/`) are always allowed.
217
+ * - Absolute URLs with a host are only allowed if their origin matches
218
+ * one of the allowedOrigins.
219
+ */
220
+ function _isValidRedirect(redirect, allowedOrigins) {
221
+ if (!redirect)
222
+ return false;
223
+ // Reject unsafe patterns
224
+ if (redirect.startsWith("//"))
225
+ return false;
226
+ if (redirect.includes("\\"))
227
+ return false;
228
+ if (/[\r\n]/.test(redirect))
229
+ return false;
230
+ // Relative paths are safe — always allow
231
+ if (redirect.startsWith("/"))
232
+ return true;
233
+ // Absolute URL — must exactly match an allowed origin
234
+ if (!allowedOrigins || allowedOrigins.length === 0)
235
+ return false;
236
+ try {
237
+ const u = new URL(redirect);
238
+ // Only HTTP(S) is allowed for cross-origin redirects
239
+ if (u.protocol !== "http:" && u.protocol !== "https:")
240
+ return false;
241
+ const origin = u.origin;
242
+ return allowedOrigins.some((allowed) => allowed === origin);
243
+ }
244
+ catch {
245
+ return false;
246
+ }
247
+ }
248
+ /**
249
+ * Pick safe display claims from ctx.auth to include in the code JWT.
250
+ */
251
+ function _pickClaims(auth) {
252
+ const safe = {};
253
+ const keys = ["userId", "userName", "username", "name", "email", "roles"];
254
+ for (const key of keys) {
255
+ const val = auth[key];
256
+ if (val !== undefined)
257
+ safe[key] = val;
258
+ }
259
+ return safe;
260
+ }
261
+ //# sourceMappingURL=sso.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sso.js","sourceRoot":"","sources":["../src/sso.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ import type { Context } from "./ctx.js";
2
+ /**
3
+ * Wrap a request handler in the per-request ALS context.
4
+ * Called once per request in server.ts before any handler runs.
5
+ */
6
+ export declare function runWithContext<T>(req: Request, ssrMode: boolean, fn: () => Promise<T>): Promise<T>;
7
+ /**
8
+ * Get the current request. Returns null outside a request context.
9
+ */
10
+ export declare function getSSRRequest(): Request | null;
11
+ /**
12
+ * Store the shared Hono-style Context in the per-request ALS scope.
13
+ * Called in server.ts after creating the Context, so that SSR direct calls
14
+ * to RPC functions via callServerFn can read c.auth and shared state.
15
+ */
16
+ export declare function setSSRContext(ctx: Context): void;
17
+ /**
18
+ * Retrieve the shared Hono-style Context from the per-request ALS scope.
19
+ * Returns undefined outside a request context.
20
+ */
21
+ export declare function getSSRContext(): Context | undefined;
22
+ /**
23
+ * Returns true when currently executing inside an SSR render pass.
24
+ */
25
+ export declare function isSSRContext(): boolean;
26
+ /**
27
+ * Store the refreshed auth payload in the current ALS context.
28
+ * Called by the transparent refresh middleware in server.ts.
29
+ * @internal
30
+ */
31
+ export declare function setRefreshedPayload(payload: Record<string, any> | null): void;
32
+ /**
33
+ * Get the refreshed auth payload from the current ALS context.
34
+ * Returns undefined if no refresh happened this request.
35
+ * Returns null if refresh was attempted but denied (user deleted, etc.).
36
+ * @internal
37
+ */
38
+ export declare function getRefreshedPayload(): Record<string, any> | null | undefined;
39
+ /**
40
+ * Store custom context data from onRequest hooks in the ALS context.
41
+ * Called by server.ts after running onRequest hooks.
42
+ */
43
+ export declare function setCustomContext(custom: Record<string, any>): void;
44
+ /**
45
+ * Get custom context data set by onRequest hooks.
46
+ * Returns an empty object if no custom context was set.
47
+ */
48
+ export declare function getCustomContext(): Record<string, any>;
49
+ //# sourceMappingURL=ssr-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-context.d.ts","sourceRoot":"","sources":["../src/ssr-context.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAYxC;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,GAAG,EAAE,OAAO,EACZ,OAAO,EAAE,OAAO,EAChB,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAEZ;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,OAAO,GAAG,IAAI,CAE9C;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,IAAI,CAGhD;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,OAAO,GAAG,SAAS,CAEnD;AAED;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAEtC;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,IAAI,CAG7E;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,GAAG,SAAS,CAE5E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAGlE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAEtD"}
@@ -0,0 +1,85 @@
1
+ /**
2
+ * mates-ssr — ssr-context.ts
3
+ *
4
+ * Per-request AsyncLocalStorage context.
5
+ * Replaces the unsafe module-level _ssrRequest slot in server-fn.ts.
6
+ *
7
+ * Every request — SSR page render, API route, serverFn RPC — is wrapped
8
+ * in runWithContext(req, fn) by server.ts. All downstream code reads the
9
+ * request via getSSRRequest() which reads from ALS — fully isolated per
10
+ * request even under high concurrency.
11
+ */
12
+ import { AsyncLocalStorage } from "node:async_hooks";
13
+ const _storage = new AsyncLocalStorage();
14
+ /**
15
+ * Wrap a request handler in the per-request ALS context.
16
+ * Called once per request in server.ts before any handler runs.
17
+ */
18
+ export function runWithContext(req, ssrMode, fn) {
19
+ return _storage.run({ request: req, ssrMode }, fn);
20
+ }
21
+ /**
22
+ * Get the current request. Returns null outside a request context.
23
+ */
24
+ export function getSSRRequest() {
25
+ return _storage.getStore()?.request ?? null;
26
+ }
27
+ /**
28
+ * Store the shared Hono-style Context in the per-request ALS scope.
29
+ * Called in server.ts after creating the Context, so that SSR direct calls
30
+ * to RPC functions via callServerFn can read c.auth and shared state.
31
+ */
32
+ export function setSSRContext(ctx) {
33
+ const store = _storage.getStore();
34
+ if (store)
35
+ store.ctx = ctx;
36
+ }
37
+ /**
38
+ * Retrieve the shared Hono-style Context from the per-request ALS scope.
39
+ * Returns undefined outside a request context.
40
+ */
41
+ export function getSSRContext() {
42
+ return _storage.getStore()?.ctx;
43
+ }
44
+ /**
45
+ * Returns true when currently executing inside an SSR render pass.
46
+ */
47
+ export function isSSRContext() {
48
+ return _storage.getStore()?.ssrMode === true;
49
+ }
50
+ /**
51
+ * Store the refreshed auth payload in the current ALS context.
52
+ * Called by the transparent refresh middleware in server.ts.
53
+ * @internal
54
+ */
55
+ export function setRefreshedPayload(payload) {
56
+ const store = _storage.getStore();
57
+ if (store)
58
+ store.refreshedPayload = payload;
59
+ }
60
+ /**
61
+ * Get the refreshed auth payload from the current ALS context.
62
+ * Returns undefined if no refresh happened this request.
63
+ * Returns null if refresh was attempted but denied (user deleted, etc.).
64
+ * @internal
65
+ */
66
+ export function getRefreshedPayload() {
67
+ return _storage.getStore()?.refreshedPayload;
68
+ }
69
+ /**
70
+ * Store custom context data from onRequest hooks in the ALS context.
71
+ * Called by server.ts after running onRequest hooks.
72
+ */
73
+ export function setCustomContext(custom) {
74
+ const store = _storage.getStore();
75
+ if (store)
76
+ store.customContext = custom;
77
+ }
78
+ /**
79
+ * Get custom context data set by onRequest hooks.
80
+ * Returns an empty object if no custom context was set.
81
+ */
82
+ export function getCustomContext() {
83
+ return _storage.getStore()?.customContext ?? {};
84
+ }
85
+ //# sourceMappingURL=ssr-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-context.js","sourceRoot":"","sources":["../src/ssr-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAWrD,MAAM,QAAQ,GAAG,IAAI,iBAAiB,EAAc,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAC5B,GAAY,EACZ,OAAgB,EAChB,EAAoB;IAEpB,OAAO,QAAQ,CAAC,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,QAAQ,CAAC,QAAQ,EAAE,EAAE,OAAO,IAAI,IAAI,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAClC,IAAI,KAAK;QAAE,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,QAAQ,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY;IAC1B,OAAO,QAAQ,CAAC,QAAQ,EAAE,EAAE,OAAO,KAAK,IAAI,CAAC;AAC/C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAmC;IACrE,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAClC,IAAI,KAAK;QAAE,KAAK,CAAC,gBAAgB,GAAG,OAAO,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,QAAQ,CAAC,QAAQ,EAAE,EAAE,gBAAgB,CAAC;AAC/C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA2B;IAC1D,MAAM,KAAK,GAAG,QAAQ,CAAC,QAAQ,EAAE,CAAC;IAClC,IAAI,KAAK;QAAE,KAAK,CAAC,aAAa,GAAG,MAAM,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB;IAC9B,OAAO,QAAQ,CAAC,QAAQ,EAAE,EAAE,aAAa,IAAI,EAAE,CAAC;AAClD,CAAC"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * mates-fullstack — ssr-globals.ts
3
+ *
4
+ * Install stateless no-op browser global stubs ONCE at boot.
5
+ *
6
+ * Why this exists:
7
+ * lit-html reads `const r = document` at module parse time.
8
+ * Mates CSS-in-JS calls `document.adoptedStyleSheets` at module init.
9
+ * Mates calls `customElements.define("x-view", ...)` at module init.
10
+ * User code may accidentally read `window.innerWidth` outside `onMount`.
11
+ *
12
+ * Without stubs, importing mates in a pure Node.js environment throws
13
+ * "document is not defined".
14
+ *
15
+ * Design rules:
16
+ * - Set ONCE at boot — never swapped per request (unlike happy-dom).
17
+ * - Stateless — every stub returns a predictable no-op value.
18
+ * No internal maps, no accumulation, no side effects.
19
+ * - Idempotent — safe to call multiple times.
20
+ * - Only sets globals that are undefined — won't overwrite a real browser or
21
+ * a happy-dom window if one is already present.
22
+ * - Not a full polyfill — just enough to prevent crashes.
23
+ *
24
+ * App code that needs real values during SSR should use isSSR() guards:
25
+ * const width = isSSR() ? 1024 : window.innerWidth;
26
+ */
27
+ /**
28
+ * Install stateless no-op browser global stubs.
29
+ * Call once at server startup, before importing mates or lit-html.
30
+ */
31
+ export declare function installSsrGlobals(): void;
32
+ //# sourceMappingURL=ssr-globals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssr-globals.d.ts","sourceRoot":"","sources":["../src/ssr-globals.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AA8OH;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,IAAI,CA6yBxC"}