@open-rap/runtime 0.0.0-stage → 0.1.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.
Files changed (144) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +14 -2
  3. package/dist/abap/background.d.ts +14 -0
  4. package/dist/abap/background.js +130 -0
  5. package/dist/abap/background.js.map +1 -0
  6. package/dist/abap/bridge.d.ts +62 -0
  7. package/dist/abap/bridge.js +402 -0
  8. package/dist/abap/bridge.js.map +1 -0
  9. package/dist/abap/convert.d.ts +26 -0
  10. package/dist/abap/convert.js +197 -0
  11. package/dist/abap/convert.js.map +1 -0
  12. package/dist/abap/http.d.ts +34 -0
  13. package/dist/abap/http.js +133 -0
  14. package/dist/abap/http.js.map +1 -0
  15. package/dist/abap/mail.d.ts +10 -0
  16. package/dist/abap/mail.js +83 -0
  17. package/dist/abap/mail.js.map +1 -0
  18. package/dist/abap/odata-client.d.ts +33 -0
  19. package/dist/abap/odata-client.js +576 -0
  20. package/dist/abap/odata-client.js.map +1 -0
  21. package/dist/abap/sql.d.ts +92 -0
  22. package/dist/abap/sql.js +261 -0
  23. package/dist/abap/sql.js.map +1 -0
  24. package/dist/abap/utils.d.ts +41 -0
  25. package/dist/abap/utils.js +976 -0
  26. package/dist/abap/utils.js.map +1 -0
  27. package/dist/app.d.ts +53 -0
  28. package/dist/app.js +169 -0
  29. package/dist/app.js.map +1 -0
  30. package/dist/auth/auth.d.ts +68 -0
  31. package/dist/auth/auth.js +140 -0
  32. package/dist/auth/auth.js.map +1 -0
  33. package/dist/auth/authorizations.d.ts +39 -0
  34. package/dist/auth/authorizations.js +103 -0
  35. package/dist/auth/authorizations.js.map +1 -0
  36. package/dist/auth/business-users.d.ts +15 -0
  37. package/dist/auth/business-users.js +36 -0
  38. package/dist/auth/business-users.js.map +1 -0
  39. package/dist/auth/dev-oauth.d.ts +38 -0
  40. package/dist/auth/dev-oauth.js +156 -0
  41. package/dist/auth/dev-oauth.js.map +1 -0
  42. package/dist/auth/environment.d.ts +7 -0
  43. package/dist/auth/environment.js +57 -0
  44. package/dist/auth/environment.js.map +1 -0
  45. package/dist/auth/session.d.ts +45 -0
  46. package/dist/auth/session.js +257 -0
  47. package/dist/auth/session.js.map +1 -0
  48. package/dist/auth/user.d.ts +21 -0
  49. package/dist/auth/user.js +12 -0
  50. package/dist/auth/user.js.map +1 -0
  51. package/dist/db/database.d.ts +30 -0
  52. package/dist/db/database.js +131 -0
  53. package/dist/db/database.js.map +1 -0
  54. package/dist/db/seed.d.ts +9 -0
  55. package/dist/db/seed.js +63 -0
  56. package/dist/db/seed.js.map +1 -0
  57. package/dist/index.d.ts +20 -0
  58. package/dist/index.js +21 -0
  59. package/dist/index.js.map +1 -0
  60. package/dist/launchpad/dev.d.ts +14 -0
  61. package/dist/launchpad/dev.js +48 -0
  62. package/dist/launchpad/dev.js.map +1 -0
  63. package/dist/launchpad/proxy.d.ts +30 -0
  64. package/dist/launchpad/proxy.js +98 -0
  65. package/dist/launchpad/proxy.js.map +1 -0
  66. package/dist/launchpad/server.d.ts +77 -0
  67. package/dist/launchpad/server.js +177 -0
  68. package/dist/launchpad/server.js.map +1 -0
  69. package/dist/mcp/server.d.ts +22 -0
  70. package/dist/mcp/server.js +222 -0
  71. package/dist/mcp/server.js.map +1 -0
  72. package/dist/odata/access.d.ts +31 -0
  73. package/dist/odata/access.js +142 -0
  74. package/dist/odata/access.js.map +1 -0
  75. package/dist/odata/batch.d.ts +36 -0
  76. package/dist/odata/batch.js +147 -0
  77. package/dist/odata/batch.js.map +1 -0
  78. package/dist/odata/csrf.d.ts +13 -0
  79. package/dist/odata/csrf.js +27 -0
  80. package/dist/odata/csrf.js.map +1 -0
  81. package/dist/odata/custom.d.ts +90 -0
  82. package/dist/odata/custom.js +373 -0
  83. package/dist/odata/custom.js.map +1 -0
  84. package/dist/odata/draft.d.ts +78 -0
  85. package/dist/odata/draft.js +590 -0
  86. package/dist/odata/draft.js.map +1 -0
  87. package/dist/odata/errors.d.ts +27 -0
  88. package/dist/odata/errors.js +41 -0
  89. package/dist/odata/errors.js.map +1 -0
  90. package/dist/odata/handler.d.ts +90 -0
  91. package/dist/odata/handler.js +312 -0
  92. package/dist/odata/handler.js.map +1 -0
  93. package/dist/odata/json.d.ts +23 -0
  94. package/dist/odata/json.js +85 -0
  95. package/dist/odata/json.js.map +1 -0
  96. package/dist/odata/read.d.ts +90 -0
  97. package/dist/odata/read.js +686 -0
  98. package/dist/odata/read.js.map +1 -0
  99. package/dist/odata/service.d.ts +45 -0
  100. package/dist/odata/service.js +145 -0
  101. package/dist/odata/service.js.map +1 -0
  102. package/dist/odata/uri/ast.d.ts +103 -0
  103. package/dist/odata/uri/ast.js +3 -0
  104. package/dist/odata/uri/ast.js.map +1 -0
  105. package/dist/odata/uri/parser.d.ts +26 -0
  106. package/dist/odata/uri/parser.js +697 -0
  107. package/dist/odata/uri/parser.js.map +1 -0
  108. package/dist/odata/write.d.ts +127 -0
  109. package/dist/odata/write.js +799 -0
  110. package/dist/odata/write.js.map +1 -0
  111. package/dist/platform.d.ts +14 -0
  112. package/dist/platform.js +66 -0
  113. package/dist/platform.js.map +1 -0
  114. package/dist/rap/bo.d.ts +76 -0
  115. package/dist/rap/bo.js +184 -0
  116. package/dist/rap/bo.js.map +1 -0
  117. package/dist/rap/kernel.d.ts +7 -0
  118. package/dist/rap/kernel.js +287 -0
  119. package/dist/rap/kernel.js.map +1 -0
  120. package/dist/rap/runtime.d.ts +84 -0
  121. package/dist/rap/runtime.js +128 -0
  122. package/dist/rap/runtime.js.map +1 -0
  123. package/dist/rap/transaction.d.ts +227 -0
  124. package/dist/rap/transaction.js +1385 -0
  125. package/dist/rap/transaction.js.map +1 -0
  126. package/dist/rap/types.d.ts +74 -0
  127. package/dist/rap/types.js +25 -0
  128. package/dist/rap/types.js.map +1 -0
  129. package/dist/server.d.ts +34 -0
  130. package/dist/server.js +114 -0
  131. package/dist/server.js.map +1 -0
  132. package/dist/ui/plugin.d.ts +14 -0
  133. package/dist/ui/plugin.js +47 -0
  134. package/dist/ui/plugin.js.map +1 -0
  135. package/dist/ui/resources.d.ts +22 -0
  136. package/dist/ui/resources.js +88 -0
  137. package/dist/ui/resources.js.map +1 -0
  138. package/dist/ui/variants.d.ts +25 -0
  139. package/dist/ui/variants.js +68 -0
  140. package/dist/ui/variants.js.map +1 -0
  141. package/dist/values.d.ts +43 -0
  142. package/dist/values.js +367 -0
  143. package/dist/values.js.map +1 -0
  144. package/package.json +60 -3
@@ -0,0 +1,222 @@
1
+ /**
2
+ * The MCP server of the application (decision D47): the Model Context Protocol over HTTP
3
+ * ("Streamable HTTP" transport, JSON responses) at `/mcp`. Each tool is an OData request run by the
4
+ * service itself, as the user of the MCP request: authorizations, access controls, feature
5
+ * control, validations and messages are those of the OData service.
6
+ *
7
+ * Authentication: a bearer token (OIDC), or the development user with AUTH_MODE=none. Browser
8
+ * sessions (cookies) are not accepted: a web page could otherwise call the tools in the user's name.
9
+ */
10
+ import { AuthenticationError } from "../auth/auth.js";
11
+ import { withReader } from "../odata/access.js";
12
+ export const MCP_PATH = "/mcp";
13
+ /** Versions of the protocol this server speaks, the newest first. */
14
+ const PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
15
+ /** The literal of a key value in a key predicate or a function parameter. */
16
+ function literal(edm, value) {
17
+ if (value === null || value === undefined) {
18
+ return "null";
19
+ }
20
+ switch (edm) {
21
+ case "Edm.String":
22
+ return `'${String(value).replace(/'/g, "''")}'`;
23
+ default:
24
+ return String(value);
25
+ }
26
+ }
27
+ function keyPredicate(tool, args) {
28
+ const keys = tool.call.keys;
29
+ const missing = keys.filter((k) => args[k.name] === undefined || args[k.name] === "");
30
+ if (missing.length > 0) {
31
+ throw new ToolInputError(`missing key ${missing.map((k) => k.name).join(", ")}`);
32
+ }
33
+ if (keys.length === 1 && keys[0] !== undefined) {
34
+ return `(${literal(keys[0].edm, args[keys[0].name])})`;
35
+ }
36
+ return `(${keys.map((k) => `${k.name}=${literal(k.edm, args[k.name])}`).join(",")})`;
37
+ }
38
+ class ToolInputError extends Error {
39
+ }
40
+ /** The fields of the arguments for the body of a create, an update or an action. */
41
+ function body(tool, args) {
42
+ const result = {};
43
+ for (const field of tool.call.fields ?? []) {
44
+ if (args[field.name] !== undefined) {
45
+ result[field.name] = args[field.name];
46
+ }
47
+ }
48
+ return JSON.stringify(result);
49
+ }
50
+ /** The OData request of a tool call. */
51
+ export function odataRequest(tool, args) {
52
+ const set = tool.call.set;
53
+ const json = { "content-type": "application/json", accept: "application/json", "if-match": "*" };
54
+ switch (tool.call.kind) {
55
+ case "list": {
56
+ const query = new URLSearchParams();
57
+ const top = typeof args["top"] === "number" ? Math.min(Math.max(1, Math.trunc(args["top"])), 1000) : 50;
58
+ query.set("$top", String(top));
59
+ query.set("$count", "true");
60
+ if (typeof args["skip"] === "number" && args["skip"] > 0)
61
+ query.set("$skip", String(Math.trunc(args["skip"])));
62
+ if (typeof args["filter"] === "string" && args["filter"].trim() !== "")
63
+ query.set("$filter", args["filter"]);
64
+ if (typeof args["orderby"] === "string" && args["orderby"].trim() !== "")
65
+ query.set("$orderby", args["orderby"]);
66
+ if (typeof args["search"] === "string" && args["search"].trim() !== "")
67
+ query.set("$search", args["search"]);
68
+ if (Array.isArray(args["select"]) && args["select"].length > 0)
69
+ query.set("$select", args["select"].map(String).join(","));
70
+ return { method: "GET", path: set, query: query.toString().replace(/\+/g, "%20"), headers: { accept: "application/json" } };
71
+ }
72
+ case "get":
73
+ return { method: "GET", path: `${set}${keyPredicate(tool, args)}`, query: "", headers: { accept: "application/json" } };
74
+ case "create":
75
+ return { method: "POST", path: set, query: "", headers: json, body: body(tool, args) };
76
+ case "createChild":
77
+ return { method: "POST", path: `${set}${keyPredicate(tool, args)}/${tool.call.navigation ?? ""}`, query: "", headers: json, body: body(tool, args) };
78
+ case "update":
79
+ return { method: "PATCH", path: `${set}${keyPredicate(tool, args)}`, query: "", headers: json, body: body(tool, args) };
80
+ case "delete":
81
+ return { method: "DELETE", path: `${set}${keyPredicate(tool, args)}`, query: "", headers: json };
82
+ case "action": {
83
+ const target = tool.call.collectionBound === true ? set : `${set}${keyPredicate(tool, args)}`;
84
+ return { method: "POST", path: `${target}/${tool.call.operation ?? ""}`, query: "", headers: json, body: body(tool, args) };
85
+ }
86
+ case "function": {
87
+ const target = tool.call.collectionBound === true ? set : `${set}${keyPredicate(tool, args)}`;
88
+ const parameters = (tool.call.fields ?? []).filter((f) => args[f.name] !== undefined).map((f) => `${f.name}=${literal(f.edm, args[f.name])}`);
89
+ return { method: "GET", path: `${target}/${tool.call.operation ?? ""}(${parameters.join(",")})`, query: "", headers: { accept: "application/json" } };
90
+ }
91
+ }
92
+ }
93
+ /** The text of an OData response for the agent: the JSON without the OData control information. */
94
+ function resultText(status, text) {
95
+ if (status === 204 || text === undefined || text === "") {
96
+ return { text: status < 400 ? "Done." : `Failed (HTTP ${status}).`, error: status >= 400 };
97
+ }
98
+ try {
99
+ const json = JSON.parse(text);
100
+ if (status >= 400) {
101
+ const error = json["error"];
102
+ const details = (error?.details ?? []).map((d) => `- ${d.message ?? ""}${d.target === undefined ? "" : ` (${d.target})`}`);
103
+ return { text: [`Failed (HTTP ${status}): ${error?.message ?? text}`, ...details].join("\n"), error: true };
104
+ }
105
+ const clean = JSON.parse(JSON.stringify(json, (key, value) => (key.startsWith("@odata.") && key !== "@odata.count" ? undefined : key.startsWith("__") ? undefined : value)));
106
+ return { text: JSON.stringify(clean, null, 2), error: false, structured: clean };
107
+ }
108
+ catch {
109
+ return { text, error: status >= 400 };
110
+ }
111
+ }
112
+ export function registerMcp(app, options) {
113
+ const tools = new Map(options.tools.tools.map((t) => [t.name, t]));
114
+ const user = async (request) => {
115
+ const found = await options.authenticator.fromAuthorization(request.headers.authorization);
116
+ if (found === undefined) {
117
+ throw new AuthenticationError("a bearer token is required");
118
+ }
119
+ return found;
120
+ };
121
+ const call = async (who, params) => {
122
+ const name = String(params["name"] ?? "");
123
+ const tool = tools.get(name);
124
+ if (tool === undefined) {
125
+ throw new JsonRpcError(-32602, `unknown tool ${name}`);
126
+ }
127
+ const args = (typeof params["arguments"] === "object" && params["arguments"] !== null ? params["arguments"] : {});
128
+ let request;
129
+ try {
130
+ request = odataRequest(tool, args);
131
+ }
132
+ catch (error) {
133
+ if (error instanceof ToolInputError) {
134
+ return { content: [{ type: "text", text: error.message }], isError: true };
135
+ }
136
+ throw error;
137
+ }
138
+ const response = await withReader(who, () => options.handler.handle(request, { user: who, ieee754: false, language: "en" }));
139
+ const result = resultText(response.status, response.body);
140
+ return {
141
+ content: [{ type: "text", text: result.text }],
142
+ ...(result.structured !== undefined && typeof result.structured === "object" && !Array.isArray(result.structured) ? { structuredContent: result.structured } : {}),
143
+ isError: result.error,
144
+ };
145
+ };
146
+ const dispatch = async (who, message) => {
147
+ const id = message.id ?? null;
148
+ try {
149
+ let result;
150
+ switch (message.method) {
151
+ case "initialize": {
152
+ const asked = typeof message.params?.["protocolVersion"] === "string" ? message.params["protocolVersion"] : undefined;
153
+ result = {
154
+ protocolVersion: asked !== undefined && PROTOCOL_VERSIONS.includes(asked) ? asked : PROTOCOL_VERSIONS[0],
155
+ capabilities: { tools: { listChanged: false } },
156
+ serverInfo: { name: options.tools.server.name, version: options.tools.server.version },
157
+ ...(options.tools.server.instructions === undefined ? {} : { instructions: options.tools.server.instructions }),
158
+ };
159
+ break;
160
+ }
161
+ case "ping":
162
+ result = {};
163
+ break;
164
+ case "tools/list":
165
+ result = {
166
+ tools: options.tools.tools.map((t) => ({ name: t.name, title: t.title, description: t.description, inputSchema: t.inputSchema, annotations: { title: t.title, ...t.annotations } })),
167
+ };
168
+ break;
169
+ case "tools/call":
170
+ result = await call(who, message.params ?? {});
171
+ break;
172
+ default:
173
+ if (message.method.startsWith("notifications/")) {
174
+ return undefined;
175
+ }
176
+ throw new JsonRpcError(-32601, `method ${message.method} not found`);
177
+ }
178
+ return message.id === undefined ? undefined : { jsonrpc: "2.0", id, result };
179
+ }
180
+ catch (error) {
181
+ const code = error instanceof JsonRpcError ? error.code : -32603;
182
+ return message.id === undefined ? undefined : { jsonrpc: "2.0", id, error: { code, message: error.message } };
183
+ }
184
+ };
185
+ app.post(MCP_PATH, async (request, reply) => {
186
+ let who;
187
+ try {
188
+ who = await user(request);
189
+ }
190
+ catch (error) {
191
+ const status = error instanceof AuthenticationError ? error.status : 401;
192
+ return reply.status(status).header("WWW-Authenticate", 'Bearer realm="open-rap-runtime"').send({ jsonrpc: "2.0", id: null, error: { code: -32001, message: error.message } });
193
+ }
194
+ let payload;
195
+ try {
196
+ payload = typeof request.body === "string" ? JSON.parse(request.body) : request.body;
197
+ }
198
+ catch {
199
+ return reply.status(400).send({ jsonrpc: "2.0", id: null, error: { code: -32700, message: "parse error" } });
200
+ }
201
+ const messages = (Array.isArray(payload) ? payload : [payload]);
202
+ if (messages.some((m) => typeof m !== "object" || m === null || typeof m.method !== "string")) {
203
+ return reply.status(400).send({ jsonrpc: "2.0", id: null, error: { code: -32600, message: "invalid request" } });
204
+ }
205
+ const responses = (await Promise.all(messages.map((m) => dispatch(who, m)))).filter((r) => r !== undefined);
206
+ if (responses.length === 0) {
207
+ return reply.status(202).send();
208
+ }
209
+ return reply.header("Content-Type", "application/json").send(JSON.stringify(Array.isArray(payload) ? responses : responses[0]));
210
+ });
211
+ // no server-sent events: the responses come in the body of the POST
212
+ app.get(MCP_PATH, async (_request, reply) => reply.status(405).header("Allow", "POST").send());
213
+ app.delete(MCP_PATH, async (_request, reply) => reply.status(405).header("Allow", "POST").send());
214
+ }
215
+ class JsonRpcError extends Error {
216
+ code;
217
+ constructor(code, message) {
218
+ super(message);
219
+ this.code = code;
220
+ }
221
+ }
222
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/mcp/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * The access controls (DCL) on the reads of the service (decision D35): the conditions of the
3
+ * roles of an entity, evaluated for the user of the request into an SQL condition on the view
4
+ * of the entity. `aspect pfcg_auth` reads the authorizations of the user (D34) for the
5
+ * authorization object, `aspect user` its ABAP user name. As in ABAP CDS, a row is granted when
6
+ * one of the conditions of the roles of the entity is true; without role, every row is.
7
+ */
8
+ import { type RawBuilder } from "kysely";
9
+ import type { AbapType, AccessControlModel } from "@open-rap/model";
10
+ import { type Authorizations } from "../auth/authorizations.js";
11
+ import type { User } from "../auth/user.js";
12
+ /** The user of the running read, if any. */
13
+ export declare function currentReader(): User | undefined;
14
+ /** Runs `fn` with the user whose access controls apply to the reads. */
15
+ export declare function withReader<T>(user: User, fn: () => Promise<T>): Promise<T>;
16
+ export declare class AccessControls {
17
+ private readonly authorizations;
18
+ private readonly byEntity;
19
+ constructor(controls: AccessControlModel[] | undefined, authorizations: Authorizations);
20
+ /**
21
+ * The SQL condition of the access controls of an entity for the user of the request, the
22
+ * elements read on `column`; undefined when the entity has no role (every row).
23
+ */
24
+ condition(entity: string, column: (element: string) => {
25
+ sql: RawBuilder<unknown>;
26
+ type: AbapType;
27
+ }): RawBuilder<unknown> | undefined;
28
+ private toSql;
29
+ /** `( E1, … ) = aspect pfcg_auth( OBJECT, F1, …, FILTER = 'v' )`: one authorization of the user grants the row. */
30
+ private pfcg;
31
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * The access controls (DCL) on the reads of the service (decision D35): the conditions of the
3
+ * roles of an entity, evaluated for the user of the request into an SQL condition on the view
4
+ * of the entity. `aspect pfcg_auth` reads the authorizations of the user (D34) for the
5
+ * authorization object, `aspect user` its ABAP user name. As in ABAP CDS, a row is granted when
6
+ * one of the conditions of the roles of the entity is true; without role, every row is.
7
+ */
8
+ import { AsyncLocalStorage } from "node:async_hooks";
9
+ import { sql } from "kysely";
10
+ import { valueMatches } from "../auth/authorizations.js";
11
+ import { toSql } from "../values.js";
12
+ const reader = new AsyncLocalStorage();
13
+ /** The user of the running read, if any. */
14
+ export function currentReader() {
15
+ return reader.getStore();
16
+ }
17
+ /** Runs `fn` with the user whose access controls apply to the reads. */
18
+ export function withReader(user, fn) {
19
+ return reader.run(user, fn);
20
+ }
21
+ /** A literal of a DCL condition (ABAP format: dates YYYYMMDD, booleans X) as a canonical value. */
22
+ function literalValue(type, text) {
23
+ switch (type.kind) {
24
+ case "dats":
25
+ return /^\d{8}$/.test(text) ? `${text.slice(0, 4)}-${text.slice(4, 6)}-${text.slice(6, 8)}` : text;
26
+ case "tims":
27
+ return /^\d{6}$/.test(text) ? `${text.slice(0, 2)}:${text.slice(2, 4)}:${text.slice(4, 6)}` : text;
28
+ case "bool":
29
+ return text.trim() !== "";
30
+ case "int1":
31
+ case "int2":
32
+ case "int4":
33
+ case "fltp":
34
+ return Number(text);
35
+ default:
36
+ return text;
37
+ }
38
+ }
39
+ const escapeLike = (value) => value.replace(/[\\%_]/g, (c) => `\\${c}`);
40
+ export class AccessControls {
41
+ authorizations;
42
+ byEntity = new Map();
43
+ constructor(controls, authorizations) {
44
+ this.authorizations = authorizations;
45
+ for (const control of controls ?? []) {
46
+ const key = control.entity.toUpperCase();
47
+ this.byEntity.set(key, [...(this.byEntity.get(key) ?? []), control]);
48
+ }
49
+ }
50
+ /**
51
+ * The SQL condition of the access controls of an entity for the user of the request, the
52
+ * elements read on `column`; undefined when the entity has no role (every row).
53
+ */
54
+ condition(entity, column) {
55
+ const controls = this.byEntity.get(entity.toUpperCase());
56
+ if (controls === undefined || controls.length === 0) {
57
+ return undefined;
58
+ }
59
+ const user = reader.getStore();
60
+ if (user === undefined) {
61
+ // a read outside of a request: nothing is granted
62
+ return sql `FALSE`;
63
+ }
64
+ if (this.authorizations.isUnrestricted(user)) {
65
+ return undefined;
66
+ }
67
+ const conditions = controls.flatMap((c) => c.conditions).map((c) => this.toSql(c, user, column));
68
+ return sql `(${sql.join(conditions, sql ` OR `)})`;
69
+ }
70
+ toSql(condition, user, column) {
71
+ switch (condition.kind) {
72
+ case "true":
73
+ return sql `TRUE`;
74
+ case "false":
75
+ return sql `FALSE`;
76
+ case "and":
77
+ case "or":
78
+ return condition.operands.length === 0 ? (condition.kind === "and" ? sql `TRUE` : sql `FALSE`) : sql `(${sql.join(condition.operands.map((o) => this.toSql(o, user, column)), condition.kind === "and" ? sql ` AND ` : sql ` OR `)})`;
79
+ case "not":
80
+ return sql `(NOT COALESCE(${this.toSql(condition.operand, user, column)}, FALSE))`;
81
+ case "null": {
82
+ const { sql: ref } = column(condition.element);
83
+ return condition.negated ? sql `(${ref} IS NOT NULL)` : sql `(${ref} IS NULL)`;
84
+ }
85
+ case "compare": {
86
+ const { sql: ref, type } = column(condition.element);
87
+ if (condition.operator === "like") {
88
+ return sql `(CAST(${ref} AS text) LIKE ${condition.value})`;
89
+ }
90
+ return sql `(${ref} ${sql.raw(condition.operator)} ${toSql(type, literalValue(type, condition.value))})`;
91
+ }
92
+ case "user": {
93
+ const { sql: ref } = column(condition.element);
94
+ const own = sql `CAST(${ref} AS text) = ${user.abapName}`;
95
+ return condition.orInitial ? sql `(${own} OR ${ref} IS NULL OR CAST(${ref} AS text) = '')` : sql `(${own})`;
96
+ }
97
+ case "pfcg":
98
+ return this.pfcg(condition, user, column);
99
+ }
100
+ }
101
+ /** `( E1, … ) = aspect pfcg_auth( OBJECT, F1, …, FILTER = 'v' )`: one authorization of the user grants the row. */
102
+ pfcg(condition, user, column) {
103
+ const authorizations = this.authorizations.of(user, condition.object).filter((a) => Object.entries(condition.filters).every(([field, value]) => (a[field] ?? []).some((accepted) => valueMatches(value, accepted))));
104
+ const values = (ref, accepted) => {
105
+ const text = sql `CAST(${ref} AS text)`;
106
+ const parts = accepted.map((v) => {
107
+ if (typeof v !== "string") {
108
+ return sql `(${text} BETWEEN ${v.from} AND ${v.to})`;
109
+ }
110
+ if (v === "*") {
111
+ return sql `TRUE`;
112
+ }
113
+ if (v.endsWith("*")) {
114
+ return sql `(${text} LIKE ${`${escapeLike(v.slice(0, -1))}%`})`;
115
+ }
116
+ return sql `(${text} = ${v})`;
117
+ });
118
+ return parts.length === 0 ? sql `FALSE` : sql `(${sql.join(parts, sql ` OR `)})`;
119
+ };
120
+ const granted = authorizations.map((authorization) => {
121
+ const checks = condition.elements.map((element, i) => {
122
+ const { sql: ref } = column(element);
123
+ const check = values(ref, authorization[condition.fields[i]] ?? []);
124
+ return condition.orInitial ? sql `(${check} OR ${ref} IS NULL OR CAST(${ref} AS text) = '')` : check;
125
+ });
126
+ return sql `(${sql.join(checks, sql ` AND `)})`;
127
+ });
128
+ if (granted.length === 0) {
129
+ if (!condition.orInitial) {
130
+ return sql `FALSE`;
131
+ }
132
+ // ?= : the rows whose elements are initial are granted without authorization
133
+ const initial = condition.elements.map((element) => {
134
+ const { sql: ref } = column(element);
135
+ return sql `(${ref} IS NULL OR CAST(${ref} AS text) = '')`;
136
+ });
137
+ return sql `(${sql.join(initial, sql ` AND `)})`;
138
+ }
139
+ return sql `(${sql.join(granted, sql ` OR `)})`;
140
+ }
141
+ }
142
+ //# sourceMappingURL=access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"access.js","sourceRoot":"","sources":["../../src/odata/access.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ /** OData $batch in multipart/mixed format: parsing of requests and serialization of responses. */
2
+ export interface BatchRequestPart {
3
+ method: string;
4
+ /** URL as written in the part, relative to the service root (or absolute). */
5
+ url: string;
6
+ headers: Record<string, string>;
7
+ body: string;
8
+ contentId?: string;
9
+ }
10
+ export type BatchItem = {
11
+ kind: "request";
12
+ request: BatchRequestPart;
13
+ } | {
14
+ kind: "changeset";
15
+ requests: BatchRequestPart[];
16
+ };
17
+ export interface BatchResponsePart {
18
+ status: number;
19
+ statusText?: string;
20
+ headers: Record<string, string>;
21
+ body?: string;
22
+ contentId?: string;
23
+ }
24
+ export type BatchResponseItem = {
25
+ kind: "response";
26
+ response: BatchResponsePart;
27
+ } | {
28
+ kind: "changeset";
29
+ responses: BatchResponsePart[];
30
+ };
31
+ export declare function boundaryOf(contentType: string | undefined): string;
32
+ export declare function parseBatch(body: string, contentType: string | undefined): BatchItem[];
33
+ export declare function serializeBatch(items: BatchResponseItem[]): {
34
+ contentType: string;
35
+ body: string;
36
+ };
@@ -0,0 +1,147 @@
1
+ /** OData $batch in multipart/mixed format: parsing of requests and serialization of responses. */
2
+ import { randomUUID } from "node:crypto";
3
+ import { badRequest } from "./errors.js";
4
+ export function boundaryOf(contentType) {
5
+ const match = /boundary=(?:"([^"]+)"|([^;\s]+))/i.exec(contentType ?? "");
6
+ const boundary = match?.[1] ?? match?.[2];
7
+ if (boundary === undefined) {
8
+ throw badRequest("The $batch request has no multipart boundary");
9
+ }
10
+ return boundary;
11
+ }
12
+ function splitParts(body, boundary) {
13
+ const delimiter = `--${boundary}`;
14
+ const parts = [];
15
+ let index = body.indexOf(delimiter);
16
+ if (index < 0) {
17
+ throw badRequest("The $batch body does not contain its boundary");
18
+ }
19
+ while (index >= 0) {
20
+ const start = index + delimiter.length;
21
+ if (body.startsWith("--", start)) {
22
+ break; // close delimiter
23
+ }
24
+ const next = body.indexOf(`\n${delimiter}`, start);
25
+ const end = next < 0 ? body.length : next;
26
+ parts.push(body.slice(start, end).replace(/^[ \t]*\r?\n/, "").replace(/\r$/, ""));
27
+ index = next < 0 ? -1 : next + 1;
28
+ }
29
+ return parts;
30
+ }
31
+ function splitHead(text) {
32
+ const match = /\r?\n\r?\n/.exec(text);
33
+ if (match === null) {
34
+ return { head: text.split(/\r?\n/).filter((l) => l !== ""), rest: "" };
35
+ }
36
+ return { head: text.slice(0, match.index).split(/\r?\n/), rest: text.slice(match.index + match[0].length) };
37
+ }
38
+ function headersOf(lines) {
39
+ const headers = {};
40
+ for (const line of lines) {
41
+ const colon = line.indexOf(":");
42
+ if (colon > 0) {
43
+ headers[line.slice(0, colon).trim().toLowerCase()] = line.slice(colon + 1).trim();
44
+ }
45
+ }
46
+ return headers;
47
+ }
48
+ function parseRequest(part, mimeHeaders) {
49
+ const { head, rest } = splitHead(part);
50
+ const requestLine = head[0] ?? "";
51
+ const match = /^(GET|POST|PATCH|PUT|DELETE|HEAD)\s+(\S+)(?:\s+HTTP\/1\.1)?$/i.exec(requestLine.trim());
52
+ if (match === null) {
53
+ throw badRequest(`Invalid request line in $batch: ${requestLine}`);
54
+ }
55
+ const request = {
56
+ method: match[1].toUpperCase(),
57
+ url: match[2],
58
+ headers: headersOf(head.slice(1)),
59
+ body: rest.replace(/\r?\n$/, ""),
60
+ };
61
+ const contentId = mimeHeaders["content-id"];
62
+ if (contentId !== undefined) {
63
+ request.contentId = contentId;
64
+ }
65
+ return request;
66
+ }
67
+ export function parseBatch(body, contentType) {
68
+ const boundary = boundaryOf(contentType);
69
+ const items = [];
70
+ for (const part of splitParts(body, boundary)) {
71
+ const { head, rest } = splitHead(part);
72
+ const headers = headersOf(head);
73
+ const type = headers["content-type"] ?? "";
74
+ if (/^multipart\/mixed/i.test(type)) {
75
+ const inner = boundaryOf(type);
76
+ const requests = splitParts(rest, inner).map((p) => {
77
+ const split = splitHead(p);
78
+ return parseRequest(split.rest, headersOf(split.head));
79
+ });
80
+ for (const r of requests) {
81
+ if (r.method === "GET") {
82
+ throw badRequest("A change set cannot contain GET requests");
83
+ }
84
+ }
85
+ items.push({ kind: "changeset", requests });
86
+ }
87
+ else if (/^application\/http/i.test(type)) {
88
+ items.push({ kind: "request", request: parseRequest(rest, headers) });
89
+ }
90
+ else {
91
+ throw badRequest(`Unexpected $batch part of type ${type || "(none)"}`);
92
+ }
93
+ }
94
+ return items;
95
+ }
96
+ const STATUS_TEXT = {
97
+ 200: "OK",
98
+ 201: "Created",
99
+ 204: "No Content",
100
+ 304: "Not Modified",
101
+ 400: "Bad Request",
102
+ 401: "Unauthorized",
103
+ 403: "Forbidden",
104
+ 404: "Not Found",
105
+ 405: "Method Not Allowed",
106
+ 406: "Not Acceptable",
107
+ 409: "Conflict",
108
+ 412: "Precondition Failed",
109
+ 415: "Unsupported Media Type",
110
+ 423: "Locked",
111
+ 428: "Precondition Required",
112
+ 500: "Internal Server Error",
113
+ 501: "Not Implemented",
114
+ };
115
+ function serializeResponse(response) {
116
+ const lines = ["Content-Type: application/http", "Content-Transfer-Encoding: binary"];
117
+ if (response.contentId !== undefined) {
118
+ lines.push(`Content-ID: ${response.contentId}`);
119
+ }
120
+ lines.push("", `HTTP/1.1 ${response.status} ${response.statusText ?? STATUS_TEXT[response.status] ?? ""}`.trimEnd());
121
+ for (const [name, value] of Object.entries(response.headers)) {
122
+ lines.push(`${name}: ${value}`);
123
+ }
124
+ lines.push("", response.body ?? "");
125
+ return lines.join("\r\n");
126
+ }
127
+ export function serializeBatch(items) {
128
+ const boundary = `batchresponse_${randomUUID()}`;
129
+ const chunks = [];
130
+ for (const item of items) {
131
+ chunks.push(`--${boundary}`);
132
+ if (item.kind === "response") {
133
+ chunks.push(serializeResponse(item.response));
134
+ }
135
+ else {
136
+ const inner = `changesetresponse_${randomUUID()}`;
137
+ chunks.push(`Content-Type: multipart/mixed; boundary=${inner}`, "");
138
+ for (const response of item.responses) {
139
+ chunks.push(`--${inner}`, serializeResponse(response));
140
+ }
141
+ chunks.push(`--${inner}--`);
142
+ }
143
+ }
144
+ chunks.push(`--${boundary}--`, "");
145
+ return { contentType: `multipart/mixed; boundary=${boundary}`, body: chunks.join("\r\n") };
146
+ }
147
+ //# sourceMappingURL=batch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"batch.js","sourceRoot":"","sources":["../../src/odata/batch.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAElG,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAuBzC,MAAM,UAAU,UAAU,CAAC,WAA+B;IACxD,MAAM,KAAK,GAAG,mCAAmC,CAAC,IAAI,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC;IAC1E,MAAM,QAAQ,GAAG,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,MAAM,UAAU,CAAC,8CAA8C,CAAC,CAAC;IACnE,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,UAAU,CAAC,IAAY,EAAE,QAAgB;IAChD,MAAM,SAAS,GAAG,KAAK,QAAQ,EAAE,CAAC;IAClC,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACpC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,UAAU,CAAC,+CAA+C,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,KAAK,IAAI,CAAC,EAAE,CAAC;QAClB,MAAM,KAAK,GAAG,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC;QACvC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,kBAAkB;QAC3B,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,EAAE,KAAK,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;QAC1C,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC;QAClF,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,SAAS,CAAC,IAAY;IAC7B,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;IACzE,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC;AAC9G,CAAC;AAED,SAAS,SAAS,CAAC,KAAe;IAChC,MAAM,OAAO,GAA2B,EAAE,CAAC;IAC3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACpF,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,YAAY,CAAC,IAAY,EAAE,WAAmC;IACrE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,+DAA+D,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACvG,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QACnB,MAAM,UAAU,CAAC,mCAAmC,WAAW,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,MAAM,OAAO,GAAqB;QAChC,MAAM,EAAG,KAAK,CAAC,CAAC,CAAY,CAAC,WAAW,EAAE;QAC1C,GAAG,EAAE,KAAK,CAAC,CAAC,CAAW;QACvB,OAAO,EAAE,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACjC,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;KACjC,CAAC;IACF,MAAM,SAAS,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC;IAChC,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY,EAAE,WAA+B;IACtE,MAAM,QAAQ,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACzC,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,MAAM,IAAI,IAAI,UAAU,CAAC,IAAI,EAAE,QAAQ,CAAC,EAAE,CAAC;QAC9C,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QACvC,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,IAAI,GAAG,OAAO,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;QAC3C,IAAI,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;YAC/B,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBACjD,MAAM,KAAK,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC;gBAC3B,OAAO,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACzD,CAAC,CAAC,CAAC;YACH,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;gBACzB,IAAI,CAAC,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;oBACvB,MAAM,UAAU,CAAC,0CAA0C,CAAC,CAAC;gBAC/D,CAAC;YACH,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9C,CAAC;aAAM,IAAI,qBAAqB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;QACxE,CAAC;aAAM,CAAC;YACN,MAAM,UAAU,CAAC,kCAAkC,IAAI,IAAI,QAAQ,EAAE,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,WAAW,GAA2B;IAC1C,GAAG,EAAE,IAAI;IACT,GAAG,EAAE,SAAS;IACd,GAAG,EAAE,YAAY;IACjB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,aAAa;IAClB,GAAG,EAAE,cAAc;IACnB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,WAAW;IAChB,GAAG,EAAE,oBAAoB;IACzB,GAAG,EAAE,gBAAgB;IACrB,GAAG,EAAE,UAAU;IACf,GAAG,EAAE,qBAAqB;IAC1B,GAAG,EAAE,wBAAwB;IAC7B,GAAG,EAAE,QAAQ;IACb,GAAG,EAAE,uBAAuB;IAC5B,GAAG,EAAE,uBAAuB;IAC5B,GAAG,EAAE,iBAAiB;CACvB,CAAC;AAEF,SAAS,iBAAiB,CAAC,QAA2B;IACpD,MAAM,KAAK,GAAG,CAAC,gCAAgC,EAAE,mCAAmC,CAAC,CAAC;IACtF,IAAI,QAAQ,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QACrC,KAAK,CAAC,IAAI,CAAC,eAAe,QAAQ,CAAC,SAAS,EAAE,CAAC,CAAC;IAClD,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,YAAY,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,IAAI,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;IACrH,KAAK,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7D,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,KAAK,EAAE,CAAC,CAAC;IAClC,CAAC;IACD,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAA0B;IACvD,MAAM,QAAQ,GAAG,iBAAiB,UAAU,EAAE,EAAE,CAAC;IACjD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;QAC7B,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7B,MAAM,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QAChD,CAAC;aAAM,CAAC;YACN,MAAM,KAAK,GAAG,qBAAqB,UAAU,EAAE,EAAE,CAAC;YAClD,MAAM,CAAC,IAAI,CAAC,2CAA2C,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;YACpE,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACtC,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,EAAE,iBAAiB,CAAC,QAAQ,CAAC,CAAC,CAAC;YACzD,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IACD,MAAM,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,EAAE,EAAE,CAAC,CAAC;IACnC,OAAO,EAAE,WAAW,EAAE,6BAA6B,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;AAC7F,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * CSRF tokens in the SAP Gateway style: a client fetches a token with `X-CSRF-Token: Fetch` on a
3
+ * GET or HEAD request, and sends it back on modifying requests. Only requests authenticated by a
4
+ * session cookie need it: bearer tokens and the development mode carry no ambient credentials.
5
+ */
6
+ import type { User } from "../auth/user.js";
7
+ export declare class CsrfTokens {
8
+ private readonly secret;
9
+ constructor(secret?: string);
10
+ token(user: User, session?: string): string;
11
+ valid(user: User, token: string | undefined, session?: string): boolean;
12
+ required(user: User, method: string): boolean;
13
+ }