@grest-ts/http 0.0.48 → 0.0.49

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 (127) hide show
  1. package/README.md +243 -175
  2. package/dist/src/_dedupCheck.js +1 -1
  3. package/dist/src/client/GGContextKeySynchronizer.d.ts +11 -0
  4. package/dist/src/client/GGContextKeySynchronizer.d.ts.map +1 -0
  5. package/dist/src/client/GGContextKeySynchronizer.js +26 -0
  6. package/dist/src/client/GGContextKeySynchronizer.js.map +1 -0
  7. package/dist/src/index-browser.d.ts +4 -2
  8. package/dist/src/index-browser.d.ts.map +1 -1
  9. package/dist/src/index-browser.js +4 -2
  10. package/dist/src/index-browser.js.map +1 -1
  11. package/dist/src/index-node.d.ts +8 -3
  12. package/dist/src/index-node.d.ts.map +1 -1
  13. package/dist/src/index-node.js +16 -7
  14. package/dist/src/index-node.js.map +1 -1
  15. package/dist/src/rpc/RpcRequest/GGRpcRequestBuilder.d.ts +4 -3
  16. package/dist/src/rpc/RpcRequest/GGRpcRequestBuilder.d.ts.map +1 -1
  17. package/dist/src/rpc/RpcRequest/GGRpcRequestBuilder.js +9 -2
  18. package/dist/src/rpc/RpcRequest/GGRpcRequestBuilder.js.map +1 -1
  19. package/dist/src/rpc/RpcRequest/GGRpcRequestParser.d.ts +3 -3
  20. package/dist/src/rpc/RpcRequest/GGRpcRequestParser.d.ts.map +1 -1
  21. package/dist/src/rpc/RpcRequest/GGRpcRequestParser.js +13 -16
  22. package/dist/src/rpc/RpcRequest/GGRpcRequestParser.js.map +1 -1
  23. package/dist/src/rpc/RpcResponse/GGRpcResponseBuilder.d.ts +1 -1
  24. package/dist/src/rpc/RpcResponse/GGRpcResponseBuilder.d.ts.map +1 -1
  25. package/dist/src/rpc/RpcResponse/GGRpcResponseBuilder.js +3 -3
  26. package/dist/src/rpc/RpcResponse/GGRpcResponseBuilder.js.map +1 -1
  27. package/dist/src/schema/GGCookie.d.ts +50 -0
  28. package/dist/src/schema/GGCookie.d.ts.map +1 -0
  29. package/dist/src/schema/GGCookie.js +130 -0
  30. package/dist/src/schema/GGCookie.js.map +1 -0
  31. package/dist/src/schema/GGHeader.d.ts +23 -0
  32. package/dist/src/schema/GGHeader.d.ts.map +1 -0
  33. package/dist/src/schema/GGHeader.js +41 -0
  34. package/dist/src/schema/GGHeader.js.map +1 -0
  35. package/dist/src/schema/GGHttpPermissionsChecker.d.ts +9 -0
  36. package/dist/src/schema/GGHttpPermissionsChecker.d.ts.map +1 -0
  37. package/dist/src/schema/GGHttpPermissionsChecker.js +29 -0
  38. package/dist/src/schema/GGHttpPermissionsChecker.js.map +1 -0
  39. package/dist/src/schema/GGHttpSchema.d.ts +8 -59
  40. package/dist/src/schema/GGHttpSchema.d.ts.map +1 -1
  41. package/dist/src/schema/GGHttpSchema.js.map +1 -1
  42. package/dist/src/schema/GGWireContextKey.d.ts +30 -0
  43. package/dist/src/schema/GGWireContextKey.d.ts.map +1 -0
  44. package/dist/src/schema/GGWireContextKey.js +36 -0
  45. package/dist/src/schema/GGWireContextKey.js.map +1 -0
  46. package/dist/src/schema/GGWireContextKey.node.d.ts +36 -0
  47. package/dist/src/schema/GGWireContextKey.node.d.ts.map +1 -0
  48. package/dist/src/schema/GGWireContextKey.node.js +83 -0
  49. package/dist/src/schema/GGWireContextKey.node.js.map +1 -0
  50. package/dist/src/schema/httpSchema.d.ts +2 -2
  51. package/dist/src/schema/httpSchema.d.ts.map +1 -1
  52. package/dist/src/schema/httpSchema.js.map +1 -1
  53. package/dist/src/server/GGHttp.d.ts +2 -26
  54. package/dist/src/server/GGHttp.d.ts.map +1 -1
  55. package/dist/src/server/GGHttp.js +0 -15
  56. package/dist/src/server/GGHttp.js.map +1 -1
  57. package/dist/src/server/GGHttpSchema.startServer.d.ts +2 -11
  58. package/dist/src/server/GGHttpSchema.startServer.d.ts.map +1 -1
  59. package/dist/src/server/GGHttpSchema.startServer.js +11 -28
  60. package/dist/src/server/GGHttpSchema.startServer.js.map +1 -1
  61. package/dist/src/server/GGHttpServer.d.ts +16 -14
  62. package/dist/src/server/GGHttpServer.d.ts.map +1 -1
  63. package/dist/src/server/GGHttpServer.js +26 -79
  64. package/dist/src/server/GGHttpServer.js.map +1 -1
  65. package/dist/src/server/applyRequestMiddleware.d.ts +11 -0
  66. package/dist/src/server/applyRequestMiddleware.d.ts.map +1 -0
  67. package/dist/src/server/applyRequestMiddleware.js +38 -0
  68. package/dist/src/server/applyRequestMiddleware.js.map +1 -0
  69. package/dist/src/server/applyResponseMiddleware.d.ts +2 -2
  70. package/dist/src/server/applyResponseMiddleware.d.ts.map +1 -1
  71. package/dist/src/server/applyResponseMiddleware.js +2 -2
  72. package/dist/src/server/applyResponseMiddleware.js.map +1 -1
  73. package/dist/src/server/checkers/checkPermissionsAtStart.d.ts +39 -0
  74. package/dist/src/server/checkers/checkPermissionsAtStart.d.ts.map +1 -0
  75. package/dist/src/server/checkers/checkPermissionsAtStart.js +82 -0
  76. package/dist/src/server/checkers/checkPermissionsAtStart.js.map +1 -0
  77. package/dist/src/server/checkers/checkWireConflicts.d.ts +18 -0
  78. package/dist/src/server/checkers/checkWireConflicts.d.ts.map +1 -0
  79. package/dist/src/server/checkers/checkWireConflicts.js +43 -0
  80. package/dist/src/server/checkers/checkWireConflicts.js.map +1 -0
  81. package/dist/src/server/checkers/checkWiresImplemented.d.ts +11 -0
  82. package/dist/src/server/checkers/checkWiresImplemented.d.ts.map +1 -0
  83. package/dist/src/server/checkers/checkWiresImplemented.js +27 -0
  84. package/dist/src/server/checkers/checkWiresImplemented.js.map +1 -0
  85. package/dist/src/tsconfig.json +2 -1
  86. package/dist/tsconfig.publish.tsbuildinfo +1 -1
  87. package/package.json +14 -11
  88. package/src/_dedupCheck.ts +1 -1
  89. package/src/client/GGContextKeySynchronizer.test.ts +106 -0
  90. package/src/client/GGContextKeySynchronizer.ts +42 -0
  91. package/src/index-browser.ts +4 -2
  92. package/src/index-node.ts +17 -7
  93. package/src/rpc/RpcRequest/GGRpcRequestBuilder.ts +11 -4
  94. package/src/rpc/RpcRequest/GGRpcRequestParser.ts +16 -17
  95. package/src/rpc/RpcResponse/GGRpcResponseBuilder.ts +5 -4
  96. package/src/schema/GGCookie.ts +151 -0
  97. package/src/schema/GGHeader.ts +48 -0
  98. package/src/schema/GGHttpPermissionsChecker.test.ts +125 -0
  99. package/src/schema/GGHttpPermissionsChecker.ts +35 -0
  100. package/src/schema/GGHttpSchema.ts +8 -71
  101. package/src/schema/GGWireContextKey.node.ts +119 -0
  102. package/src/schema/GGWireContextKey.ts +46 -0
  103. package/src/schema/httpSchema.ts +3 -3
  104. package/src/server/GGHttp.ts +3 -34
  105. package/src/server/GGHttpSchema.startServer.ts +13 -38
  106. package/src/server/GGHttpServer.ts +33 -89
  107. package/src/server/applyRequestMiddleware.ts +40 -0
  108. package/src/server/applyResponseMiddleware.ts +5 -5
  109. package/src/server/checkers/checkPermissionsAtStart.ts +112 -0
  110. package/src/server/checkers/checkWireConflicts.ts +65 -0
  111. package/src/server/checkers/checkWiresImplemented.ts +42 -0
  112. package/src/tsconfig.json +2 -1
  113. package/dist/src/schema/cookieMiddleware.d.ts +0 -71
  114. package/dist/src/schema/cookieMiddleware.d.ts.map +0 -1
  115. package/dist/src/schema/cookieMiddleware.js +0 -126
  116. package/dist/src/schema/cookieMiddleware.js.map +0 -1
  117. package/dist/src/schema/headerBinding.d.ts +0 -35
  118. package/dist/src/schema/headerBinding.d.ts.map +0 -1
  119. package/dist/src/schema/headerBinding.js +0 -49
  120. package/dist/src/schema/headerBinding.js.map +0 -1
  121. package/dist/src/server/GG_PERMISSIONS.d.ts +0 -15
  122. package/dist/src/server/GG_PERMISSIONS.d.ts.map +0 -1
  123. package/dist/src/server/GG_PERMISSIONS.js +0 -15
  124. package/dist/src/server/GG_PERMISSIONS.js.map +0 -1
  125. package/src/schema/cookieMiddleware.ts +0 -164
  126. package/src/schema/headerBinding.ts +0 -65
  127. package/src/server/GG_PERMISSIONS.ts +0 -15
@@ -1,126 +0,0 @@
1
- import { GGContextKey } from "@grest-ts/context";
2
- import { IsAny, IsString, SERVER_ERROR } from "@grest-ts/schema";
3
- /**
4
- * Per-request set of context-key names the current route is permitted to write a cookie
5
- * for (declared via GGRpc.*(...).updatesCookie(key)). Populated by the server route
6
- * handler; read by setCookie() to reject undeclared cookie writes.
7
- */
8
- export const GG_COOKIE_WRITES = new GGContextKey("cookie:writes", IsAny);
9
- const SAFE_DEFAULTS = { httpOnly: true, secure: true, sameSite: "lax", path: "/" };
10
- /**
11
- * Per-request Set-Cookie writes scheduled via setCookie()/clearCookie(), keyed by the
12
- * context key's name. Flushed onto the response by the matching cookie() binding.
13
- */
14
- const GG_COOKIE_PENDING = new GGContextKey("cookie:pending", IsAny);
15
- // Reject anything that could break out of the Set-Cookie line / inject attributes.
16
- function assertCookieSafe(label, value) {
17
- if (/[\x00-\x1f;]/.test(value)) {
18
- throw new Error(`Cookie ${label} contains illegal characters (control chars or ';'): ${JSON.stringify(value)}`);
19
- }
20
- }
21
- /**
22
- * Read a single named cookie out of a raw `Cookie` header value. Shared by the HTTP
23
- * binding (parseRequest) and the WebSocket binding (parseHandshake). A malformed
24
- * percent-encoding (e.g. "sid=%") must not crash — fall back to the raw value.
25
- */
26
- export function readCookie(rawCookieHeader, name) {
27
- if (typeof rawCookieHeader !== "string")
28
- return undefined;
29
- for (const part of rawCookieHeader.split(";")) {
30
- const eq = part.indexOf("=");
31
- if (eq === -1)
32
- continue;
33
- if (part.slice(0, eq).trim() === name) {
34
- const rawValue = part.slice(eq + 1).trim();
35
- try {
36
- return decodeURIComponent(rawValue);
37
- }
38
- catch {
39
- return rawValue;
40
- }
41
- }
42
- }
43
- return undefined;
44
- }
45
- function serializeSetCookie(name, value, options) {
46
- const o = { ...SAFE_DEFAULTS, ...options };
47
- const secure = o.sameSite === "none" ? true : o.secure;
48
- const sameSite = `${o.sameSite.charAt(0).toUpperCase()}${o.sameSite.slice(1)}`;
49
- const attrs = `; Path=${o.path}${o.domain ? `; Domain=${o.domain}` : ""}; SameSite=${sameSite}${secure ? "; Secure" : ""}${o.httpOnly ? "; HttpOnly" : ""}`;
50
- return value
51
- ? `${name}=${encodeURIComponent(value)}${o.maxAgeSec !== undefined ? `; Max-Age=${Math.trunc(o.maxAgeSec)}` : ""}${attrs}`
52
- : `${name}=; Max-Age=0${attrs}`;
53
- }
54
- /**
55
- * Bind a context key to a cookie, read-only on the wire. Reads ONLY the named cookie —
56
- * never a header — from the incoming HTTP Cookie (parseRequest) or the real WS upgrade
57
- * Cookie (parseHandshake), into key.get(). The cookie's wire name defaults to the key's
58
- * name. To write the cookie, call setCookie(key, …) from a route that declared
59
- * .updatesCookie(key); the matching binding flushes it as Set-Cookie (HTTP only — a
60
- * WebSocket has no response to set a cookie on).
61
- */
62
- export function cookie(key, opts) {
63
- const cookieName = opts?.name ?? key.name;
64
- assertCookieSafe("name", cookieName);
65
- return {
66
- headers: {},
67
- responseHeaders: {},
68
- cookieParams: { [cookieName]: opts?.schema ?? IsString.orUndefined },
69
- parseRequest(req) {
70
- const raw = req.headers?.["cookie"];
71
- const value = readCookie(typeof raw === "string" ? raw : undefined, cookieName);
72
- if (value !== undefined)
73
- key.set(value);
74
- },
75
- updateResponse(res) {
76
- const pending = GG_COOKIE_PENDING.get()?.get(key.name);
77
- if (pending === undefined)
78
- return;
79
- const line = serializeSetCookie(cookieName, pending.value, pending.options);
80
- const existing = res.headers["set-cookie"];
81
- const arr = existing === undefined ? [] : Array.isArray(existing) ? existing : [existing];
82
- arr.push(line);
83
- res.headers["set-cookie"] = arr;
84
- },
85
- parseHandshake(ctx) {
86
- const value = readCookie(ctx.upgradeHeaders?.["cookie"], cookieName);
87
- if (value !== undefined)
88
- key.set(value);
89
- },
90
- };
91
- }
92
- /**
93
- * Schedule a Set-Cookie on the response (HTTP only). The value never touches key.get() —
94
- * inbound reads stay set-once. The route must declare .updatesCookie(key); an undeclared
95
- * call is invalid usage and throws SERVER_ERROR. The matching cookie() binding serializes
96
- * the pending write with these options (HttpOnly/Secure/SameSite/Path/Domain/Max-Age).
97
- */
98
- export function setCookie(key, value, options) {
99
- if (options) {
100
- if (options.maxAgeSec !== undefined && !Number.isFinite(options.maxAgeSec)) {
101
- throw new Error(`setCookie("${key.name}"): maxAgeSec must be a finite number, got ${options.maxAgeSec}.`);
102
- }
103
- if (options.path !== undefined)
104
- assertCookieSafe("path", options.path);
105
- if (options.domain !== undefined)
106
- assertCookieSafe("domain", options.domain);
107
- }
108
- if (!GG_COOKIE_WRITES.get()?.has(key.name)) {
109
- throw new SERVER_ERROR({ debugMessage: `setCookie("${key.name}") was called, but this route did not declare .updatesCookie(<key>). Only routes that declare it may set or clear the cookie.` });
110
- }
111
- let pending = GG_COOKIE_PENDING.get();
112
- if (pending === undefined) {
113
- pending = new Map();
114
- GG_COOKIE_PENDING.set(pending);
115
- }
116
- pending.set(key.name, { value, options });
117
- }
118
- /**
119
- * Clear the cookie — schedules a Set-Cookie with Max-Age=0. Pass path/domain to match a
120
- * scoped cookie (deletion only works when path+domain match what was set). Equivalent to
121
- * setCookie(key, undefined, options), but reads clearer at logout-style call sites.
122
- */
123
- export function clearCookie(key, options) {
124
- setCookie(key, undefined, options);
125
- }
126
- //# sourceMappingURL=cookieMiddleware.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cookieMiddleware.js","sourceRoot":"","sources":["../../../src/schema/cookieMiddleware.ts"],"names":[],"mappings":"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"}
@@ -1,35 +0,0 @@
1
- import { GGContextKey } from "@grest-ts/context";
2
- import { type GGSchema } from "@grest-ts/schema";
3
- import type { GGHttpTransportMiddleware } from "./GGHttpSchema";
4
- /**
5
- * Local structural copy of GGWebSocketHandshakeContext (defined in @grest-ts/websocket).
6
- * http must not import websocket; header()'s WS half is typed against this shape and
7
- * matches GGWebSocketMiddleware structurally at the webSocketSchema.use() call site.
8
- * `headers` is the in-band handshake bag (client-set, spoofable — same trust level as an
9
- * HTTP request header).
10
- */
11
- interface WsHandshakeContextLike {
12
- headers: Record<string, string>;
13
- upgradeHeaders?: Record<string, string>;
14
- queryArgs: Record<string, string>;
15
- }
16
- export interface GGHeaderBinding extends GGHttpTransportMiddleware {
17
- parseHandshake(ctx: WsHandshakeContextLike): void;
18
- updateHandshake(ctx: WsHandshakeContextLike): void;
19
- }
20
- /**
21
- * Bind a context key to a single named header, on both transports. Reads ONLY that header
22
- * — never a cookie — from the HTTP request (parseRequest) or the in-band WS handshake
23
- * (parseHandshake) into key.get(), and writes it from key.get() on the client (HTTP
24
- * updateRequest / WS updateHandshake). `scheme: "bearer"` strips/adds the `Bearer ` prefix;
25
- * default is verbatim. The wire name defaults to the key's name (lowercased — HTTP headers
26
- * are case-insensitive). Client-attachable, so it is the service-to-service variant (a
27
- * Node client can carry it; a browser cookie cannot).
28
- */
29
- export declare function header(key: GGContextKey<string | undefined>, opts?: {
30
- name?: string;
31
- scheme?: "bearer";
32
- schema?: GGSchema<string | undefined>;
33
- }): GGHeaderBinding;
34
- export {};
35
- //# sourceMappingURL=headerBinding.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"headerBinding.d.ts","sourceRoot":"","sources":["../../../src/schema/headerBinding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAA;AAC9C,OAAO,EAAW,KAAK,QAAQ,EAAC,MAAM,kBAAkB,CAAA;AACxD,OAAO,KAAK,EAAgB,yBAAyB,EAAC,MAAM,gBAAgB,CAAA;AAE5E;;;;;;GAMG;AACH,UAAU,sBAAsB;IAC5B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AAED,MAAM,WAAW,eAAgB,SAAQ,yBAAyB;IAC9D,cAAc,CAAC,GAAG,EAAE,sBAAsB,GAAG,IAAI,CAAA;IACjD,eAAe,CAAC,GAAG,EAAE,sBAAsB,GAAG,IAAI,CAAA;CACrD;AAID;;;;;;;;GAQG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,YAAY,CAAC,MAAM,GAAG,SAAS,CAAC,EAAE,IAAI,CAAC,EAAE;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,CAAC;IAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,GAAG,SAAS,CAAC,CAAA;CAAC,GAAG,eAAe,CA+B/J"}
@@ -1,49 +0,0 @@
1
- import { IsString } from "@grest-ts/schema";
2
- const BEARER = "Bearer ";
3
- /**
4
- * Bind a context key to a single named header, on both transports. Reads ONLY that header
5
- * — never a cookie — from the HTTP request (parseRequest) or the in-band WS handshake
6
- * (parseHandshake) into key.get(), and writes it from key.get() on the client (HTTP
7
- * updateRequest / WS updateHandshake). `scheme: "bearer"` strips/adds the `Bearer ` prefix;
8
- * default is verbatim. The wire name defaults to the key's name (lowercased — HTTP headers
9
- * are case-insensitive). Client-attachable, so it is the service-to-service variant (a
10
- * Node client can carry it; a browser cookie cannot).
11
- */
12
- export function header(key, opts) {
13
- const name = (opts?.name ?? key.name).toLowerCase();
14
- const bearer = opts?.scheme === "bearer";
15
- const unwrap = (raw) => {
16
- if (raw === undefined)
17
- return undefined;
18
- return bearer && raw.startsWith(BEARER) ? raw.slice(BEARER.length) : raw;
19
- };
20
- const wrap = (value) => bearer ? `${BEARER}${value}` : value;
21
- return {
22
- headers: { [name]: opts?.schema ?? IsString.orUndefined },
23
- responseHeaders: {},
24
- parseRequest(req) {
25
- const raw = req.headers?.[name];
26
- const value = unwrap(typeof raw === "string" ? raw : Array.isArray(raw) ? raw[0] : undefined);
27
- if (value !== undefined)
28
- key.set(value);
29
- },
30
- updateRequest(req) {
31
- const value = key.get();
32
- if (value === undefined)
33
- return;
34
- req.headers = req.headers ?? {};
35
- req.headers[name] = wrap(value);
36
- },
37
- parseHandshake(ctx) {
38
- const value = unwrap(ctx.headers?.[name]);
39
- if (value !== undefined)
40
- key.set(value);
41
- },
42
- updateHandshake(ctx) {
43
- const value = key.get();
44
- if (value !== undefined)
45
- ctx.headers[name] = wrap(value);
46
- },
47
- };
48
- }
49
- //# sourceMappingURL=headerBinding.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"headerBinding.js","sourceRoot":"","sources":["../../../src/schema/headerBinding.ts"],"names":[],"mappings":"AACA,OAAO,EAAC,QAAQ,EAAgB,MAAM,kBAAkB,CAAA;AAqBxD,MAAM,MAAM,GAAG,SAAS,CAAA;AAExB;;;;;;;;GAQG;AACH,MAAM,UAAU,MAAM,CAAC,GAAqC,EAAE,IAAgF;IAC1I,MAAM,IAAI,GAAG,CAAC,IAAI,EAAE,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;IACnD,MAAM,MAAM,GAAG,IAAI,EAAE,MAAM,KAAK,QAAQ,CAAA;IACxC,MAAM,MAAM,GAAG,CAAC,GAAuB,EAAsB,EAAE;QAC3D,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QACvC,OAAO,MAAM,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA;IAC5E,CAAC,CAAA;IACD,MAAM,IAAI,GAAG,CAAC,KAAa,EAAU,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,CAAA;IAC5E,OAAO;QACH,OAAO,EAAE,EAAC,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,IAAI,QAAQ,CAAC,WAAW,EAAC;QACvD,eAAe,EAAE,EAAE;QACnB,YAAY,CAAC,GAAkB;YAC3B,MAAM,GAAG,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAA;YAC/B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;YAC7F,IAAI,KAAK,KAAK,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC3C,CAAC;QACD,aAAa,CAAC,GAAkB;YAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,CAAA;YACvB,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAM;YAC/B,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,IAAI,EAAE,CAAA;YAC/B,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QACnC,CAAC;QACD,cAAc,CAAC,GAA2B;YACtC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC,CAAA;YACzC,IAAI,KAAK,KAAK,SAAS;gBAAE,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC3C,CAAC;QACD,eAAe,CAAC,GAA2B;YACvC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,EAAE,CAAA;YACvB,IAAI,KAAK,KAAK,SAAS;gBAAE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAA;QAC5D,CAAC;KACJ,CAAA;AACL,CAAC"}
@@ -1,15 +0,0 @@
1
- import { GGContextKey } from "@grest-ts/context";
2
- import { GGPermissionChecker } from "@grest-ts/schema";
3
- /**
4
- * Per-request permission context populated by the gate when a scope resolver
5
- * is wired via GGHttp.usePermissions(...). Handler code can read it to make
6
- * sub-decisions inside an authorized method:
7
- *
8
- * const perm = GG_PERMISSIONS.get();
9
- * if (perm.has(AppPermission.Admin)) { ... }
10
- *
11
- * The checker uses the same satisfies() the framework gate uses — no parallel
12
- * implementations.
13
- */
14
- export declare const GG_PERMISSIONS: GGContextKey<GGPermissionChecker>;
15
- //# sourceMappingURL=GG_PERMISSIONS.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GG_PERMISSIONS.d.ts","sourceRoot":"","sources":["../../../src/server/GG_PERMISSIONS.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAC,mBAAmB,EAAQ,MAAM,kBAAkB,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,mCAAqE,CAAC"}
@@ -1,15 +0,0 @@
1
- import { GGContextKey } from "@grest-ts/context";
2
- import { IsAny } from "@grest-ts/schema";
3
- /**
4
- * Per-request permission context populated by the gate when a scope resolver
5
- * is wired via GGHttp.usePermissions(...). Handler code can read it to make
6
- * sub-decisions inside an authorized method:
7
- *
8
- * const perm = GG_PERMISSIONS.get();
9
- * if (perm.has(AppPermission.Admin)) { ... }
10
- *
11
- * The checker uses the same satisfies() the framework gate uses — no parallel
12
- * implementations.
13
- */
14
- export const GG_PERMISSIONS = new GGContextKey("permissions", IsAny);
15
- //# sourceMappingURL=GG_PERMISSIONS.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"GG_PERMISSIONS.js","sourceRoot":"","sources":["../../../src/server/GG_PERMISSIONS.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAsB,KAAK,EAAC,MAAM,kBAAkB,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,YAAY,CAAsB,aAAa,EAAE,KAAY,CAAC,CAAC"}
@@ -1,164 +0,0 @@
1
- import {GGContextKey} from "@grest-ts/context"
2
- import {IsAny, IsString, SERVER_ERROR, type GGSchema} from "@grest-ts/schema"
3
- import type {GGHttpRequest, GGHttpResponse, GGHttpTransportMiddleware} from "./GGHttpSchema"
4
-
5
- /**
6
- * Per-request set of context-key names the current route is permitted to write a cookie
7
- * for (declared via GGRpc.*(...).updatesCookie(key)). Populated by the server route
8
- * handler; read by setCookie() to reject undeclared cookie writes.
9
- */
10
- export const GG_COOKIE_WRITES = new GGContextKey<Set<string>>("cookie:writes", IsAny as unknown as GGSchema<Set<string>>)
11
-
12
- /**
13
- * Cookie write rules — passed to setCookie(key, value, options). They live at the write
14
- * call site (the handler that mints the cookie), never in the shared API.
15
- */
16
- export interface CookieOptions {
17
- httpOnly?: boolean
18
- secure?: boolean
19
- sameSite?: "lax" | "strict" | "none"
20
- path?: string
21
- domain?: string
22
- maxAgeSec?: number
23
- }
24
-
25
- const SAFE_DEFAULTS = {httpOnly: true, secure: true, sameSite: "lax", path: "/"} as const
26
-
27
- interface PendingCookie {
28
- value: string | undefined
29
- options?: CookieOptions
30
- }
31
-
32
- /**
33
- * Per-request Set-Cookie writes scheduled via setCookie()/clearCookie(), keyed by the
34
- * context key's name. Flushed onto the response by the matching cookie() binding.
35
- */
36
- const GG_COOKIE_PENDING = new GGContextKey<Map<string, PendingCookie>>("cookie:pending", IsAny as unknown as GGSchema<Map<string, PendingCookie>>)
37
-
38
- // Reject anything that could break out of the Set-Cookie line / inject attributes.
39
- function assertCookieSafe(label: string, value: string): void {
40
- if (/[\x00-\x1f;]/.test(value)) {
41
- throw new Error(`Cookie ${label} contains illegal characters (control chars or ';'): ${JSON.stringify(value)}`)
42
- }
43
- }
44
-
45
- /**
46
- * Read a single named cookie out of a raw `Cookie` header value. Shared by the HTTP
47
- * binding (parseRequest) and the WebSocket binding (parseHandshake). A malformed
48
- * percent-encoding (e.g. "sid=%") must not crash — fall back to the raw value.
49
- */
50
- export function readCookie(rawCookieHeader: string | undefined, name: string): string | undefined {
51
- if (typeof rawCookieHeader !== "string") return undefined
52
- for (const part of rawCookieHeader.split(";")) {
53
- const eq = part.indexOf("=")
54
- if (eq === -1) continue
55
- if (part.slice(0, eq).trim() === name) {
56
- const rawValue = part.slice(eq + 1).trim()
57
- try {
58
- return decodeURIComponent(rawValue)
59
- } catch {
60
- return rawValue
61
- }
62
- }
63
- }
64
- return undefined
65
- }
66
-
67
- function serializeSetCookie(name: string, value: string | undefined, options?: CookieOptions): string {
68
- const o = {...SAFE_DEFAULTS, ...options}
69
- const secure = o.sameSite === "none" ? true : o.secure
70
- const sameSite = `${o.sameSite.charAt(0).toUpperCase()}${o.sameSite.slice(1)}`
71
- const attrs = `; Path=${o.path}${o.domain ? `; Domain=${o.domain}` : ""}; SameSite=${sameSite}${secure ? "; Secure" : ""}${o.httpOnly ? "; HttpOnly" : ""}`
72
- return value
73
- ? `${name}=${encodeURIComponent(value)}${o.maxAgeSec !== undefined ? `; Max-Age=${Math.trunc(o.maxAgeSec)}` : ""}${attrs}`
74
- : `${name}=; Max-Age=0${attrs}`
75
- }
76
-
77
- /**
78
- * Local structural copy of GGWebSocketHandshakeContext (defined in @grest-ts/websocket).
79
- * http must not import websocket; cookie()'s WS half is typed against this shape and
80
- * matches GGWebSocketMiddleware structurally at the webSocketSchema.use() call site.
81
- * `upgradeHeaders` is the real browser upgrade request (carries httpOnly cookies); the
82
- * in-band `headers` map cannot spoof it.
83
- */
84
- interface WsHandshakeContextLike {
85
- headers: Record<string, string>
86
- upgradeHeaders?: Record<string, string>
87
- queryArgs: Record<string, string>
88
- }
89
-
90
- export interface GGCookieBinding extends GGHttpTransportMiddleware {
91
- /** The cookie this binding reads, mapped to its value schema — emitted as an `in: cookie` doc param. */
92
- readonly cookieParams: Record<string, GGSchema<string | undefined>>
93
- parseHandshake(ctx: WsHandshakeContextLike): void
94
- }
95
-
96
- /**
97
- * Bind a context key to a cookie, read-only on the wire. Reads ONLY the named cookie —
98
- * never a header — from the incoming HTTP Cookie (parseRequest) or the real WS upgrade
99
- * Cookie (parseHandshake), into key.get(). The cookie's wire name defaults to the key's
100
- * name. To write the cookie, call setCookie(key, …) from a route that declared
101
- * .updatesCookie(key); the matching binding flushes it as Set-Cookie (HTTP only — a
102
- * WebSocket has no response to set a cookie on).
103
- */
104
- export function cookie(key: GGContextKey<string | undefined>, opts?: {name?: string; schema?: GGSchema<string | undefined>}): GGCookieBinding {
105
- const cookieName = opts?.name ?? key.name
106
- assertCookieSafe("name", cookieName)
107
- return {
108
- headers: {},
109
- responseHeaders: {},
110
- cookieParams: {[cookieName]: opts?.schema ?? IsString.orUndefined},
111
- parseRequest(req: GGHttpRequest): void {
112
- const raw = req.headers?.["cookie"]
113
- const value = readCookie(typeof raw === "string" ? raw : undefined, cookieName)
114
- if (value !== undefined) key.set(value)
115
- },
116
- updateResponse(res: GGHttpResponse): void {
117
- const pending = GG_COOKIE_PENDING.get()?.get(key.name)
118
- if (pending === undefined) return
119
- const line = serializeSetCookie(cookieName, pending.value, pending.options)
120
- const existing = res.headers["set-cookie"]
121
- const arr = existing === undefined ? [] : Array.isArray(existing) ? existing : [existing]
122
- arr.push(line)
123
- res.headers["set-cookie"] = arr
124
- },
125
- parseHandshake(ctx: WsHandshakeContextLike): void {
126
- const value = readCookie(ctx.upgradeHeaders?.["cookie"], cookieName)
127
- if (value !== undefined) key.set(value)
128
- },
129
- }
130
- }
131
-
132
- /**
133
- * Schedule a Set-Cookie on the response (HTTP only). The value never touches key.get() —
134
- * inbound reads stay set-once. The route must declare .updatesCookie(key); an undeclared
135
- * call is invalid usage and throws SERVER_ERROR. The matching cookie() binding serializes
136
- * the pending write with these options (HttpOnly/Secure/SameSite/Path/Domain/Max-Age).
137
- */
138
- export function setCookie(key: GGContextKey<string | undefined>, value: string | undefined, options?: CookieOptions): void {
139
- if (options) {
140
- if (options.maxAgeSec !== undefined && !Number.isFinite(options.maxAgeSec)) {
141
- throw new Error(`setCookie("${key.name}"): maxAgeSec must be a finite number, got ${options.maxAgeSec}.`)
142
- }
143
- if (options.path !== undefined) assertCookieSafe("path", options.path)
144
- if (options.domain !== undefined) assertCookieSafe("domain", options.domain)
145
- }
146
- if (!GG_COOKIE_WRITES.get()?.has(key.name)) {
147
- throw new SERVER_ERROR({debugMessage: `setCookie("${key.name}") was called, but this route did not declare .updatesCookie(<key>). Only routes that declare it may set or clear the cookie.`})
148
- }
149
- let pending = GG_COOKIE_PENDING.get()
150
- if (pending === undefined) {
151
- pending = new Map()
152
- GG_COOKIE_PENDING.set(pending)
153
- }
154
- pending.set(key.name, {value, options})
155
- }
156
-
157
- /**
158
- * Clear the cookie — schedules a Set-Cookie with Max-Age=0. Pass path/domain to match a
159
- * scoped cookie (deletion only works when path+domain match what was set). Equivalent to
160
- * setCookie(key, undefined, options), but reads clearer at logout-style call sites.
161
- */
162
- export function clearCookie(key: GGContextKey<string | undefined>, options?: CookieOptions): void {
163
- setCookie(key, undefined, options)
164
- }
@@ -1,65 +0,0 @@
1
- import {GGContextKey} from "@grest-ts/context"
2
- import {IsString, type GGSchema} from "@grest-ts/schema"
3
- import type {GGHttpRequest, GGHttpTransportMiddleware} from "./GGHttpSchema"
4
-
5
- /**
6
- * Local structural copy of GGWebSocketHandshakeContext (defined in @grest-ts/websocket).
7
- * http must not import websocket; header()'s WS half is typed against this shape and
8
- * matches GGWebSocketMiddleware structurally at the webSocketSchema.use() call site.
9
- * `headers` is the in-band handshake bag (client-set, spoofable — same trust level as an
10
- * HTTP request header).
11
- */
12
- interface WsHandshakeContextLike {
13
- headers: Record<string, string>
14
- upgradeHeaders?: Record<string, string>
15
- queryArgs: Record<string, string>
16
- }
17
-
18
- export interface GGHeaderBinding extends GGHttpTransportMiddleware {
19
- parseHandshake(ctx: WsHandshakeContextLike): void
20
- updateHandshake(ctx: WsHandshakeContextLike): void
21
- }
22
-
23
- const BEARER = "Bearer "
24
-
25
- /**
26
- * Bind a context key to a single named header, on both transports. Reads ONLY that header
27
- * — never a cookie — from the HTTP request (parseRequest) or the in-band WS handshake
28
- * (parseHandshake) into key.get(), and writes it from key.get() on the client (HTTP
29
- * updateRequest / WS updateHandshake). `scheme: "bearer"` strips/adds the `Bearer ` prefix;
30
- * default is verbatim. The wire name defaults to the key's name (lowercased — HTTP headers
31
- * are case-insensitive). Client-attachable, so it is the service-to-service variant (a
32
- * Node client can carry it; a browser cookie cannot).
33
- */
34
- export function header(key: GGContextKey<string | undefined>, opts?: {name?: string; scheme?: "bearer"; schema?: GGSchema<string | undefined>}): GGHeaderBinding {
35
- const name = (opts?.name ?? key.name).toLowerCase()
36
- const bearer = opts?.scheme === "bearer"
37
- const unwrap = (raw: string | undefined): string | undefined => {
38
- if (raw === undefined) return undefined
39
- return bearer && raw.startsWith(BEARER) ? raw.slice(BEARER.length) : raw
40
- }
41
- const wrap = (value: string): string => bearer ? `${BEARER}${value}` : value
42
- return {
43
- headers: {[name]: opts?.schema ?? IsString.orUndefined},
44
- responseHeaders: {},
45
- parseRequest(req: GGHttpRequest): void {
46
- const raw = req.headers?.[name]
47
- const value = unwrap(typeof raw === "string" ? raw : Array.isArray(raw) ? raw[0] : undefined)
48
- if (value !== undefined) key.set(value)
49
- },
50
- updateRequest(req: GGHttpRequest): void {
51
- const value = key.get()
52
- if (value === undefined) return
53
- req.headers = req.headers ?? {}
54
- req.headers[name] = wrap(value)
55
- },
56
- parseHandshake(ctx: WsHandshakeContextLike): void {
57
- const value = unwrap(ctx.headers?.[name])
58
- if (value !== undefined) key.set(value)
59
- },
60
- updateHandshake(ctx: WsHandshakeContextLike): void {
61
- const value = key.get()
62
- if (value !== undefined) ctx.headers[name] = wrap(value)
63
- },
64
- }
65
- }
@@ -1,15 +0,0 @@
1
- import {GGContextKey} from "@grest-ts/context";
2
- import {GGPermissionChecker, IsAny} from "@grest-ts/schema";
3
-
4
- /**
5
- * Per-request permission context populated by the gate when a scope resolver
6
- * is wired via GGHttp.usePermissions(...). Handler code can read it to make
7
- * sub-decisions inside an authorized method:
8
- *
9
- * const perm = GG_PERMISSIONS.get();
10
- * if (perm.has(AppPermission.Admin)) { ... }
11
- *
12
- * The checker uses the same satisfies() the framework gate uses — no parallel
13
- * implementations.
14
- */
15
- export const GG_PERMISSIONS = new GGContextKey<GGPermissionChecker>("permissions", IsAny as any);