@ruiapp/rapid-core 0.10.11 → 0.10.12

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.
@@ -1,4 +1,5 @@
1
1
  import { HttpStatus } from "./http-types";
2
+ import { Cookie } from "../deno-std/http/cookie";
2
3
  export declare const GlobalResponse: {
3
4
  new (body?: BodyInit, init?: ResponseInit): Response;
4
5
  prototype: Response;
@@ -13,5 +14,6 @@ export declare class RapidResponse {
13
14
  constructor(body?: BodyInit, init?: ResponseInit);
14
15
  json(obj: any, status?: HttpStatus, headers?: HeadersInit): void;
15
16
  redirect(location: string, status?: HttpStatus): void;
17
+ setCookie(cookie: Cookie): void;
16
18
  getResponse(): Response;
17
19
  }
package/dist/index.d.ts CHANGED
@@ -37,6 +37,7 @@ export * from "./plugins/sequence/SequencePluginTypes";
37
37
  export { default as WebhooksPlugin } from "./plugins/webhooks/WebhooksPlugin";
38
38
  export { default as AuthPlugin } from "./plugins/auth/AuthPlugin";
39
39
  export * from "./plugins/auth/AuthPluginTypes";
40
+ export { default as AuthService } from "./plugins/auth/services/AuthService";
40
41
  export { default as FileManagePlugin } from "./plugins/fileManage/FileManagePlugin";
41
42
  export { default as LicensePlugin } from "./plugins/license/LicensePlugin";
42
43
  export * from "./plugins/license/LicensePluginTypes";
package/dist/index.js CHANGED
@@ -1072,158 +1072,509 @@ async function buildRoutes(server, applicationConfig) {
1072
1072
  return router.routes();
1073
1073
  }
1074
1074
 
1075
- function mergeHeaders(target, source) {
1076
- if (source instanceof Headers) {
1077
- for (const keyValuePair of source.entries()) {
1078
- target.set(keyValuePair[0], keyValuePair[1]);
1079
- }
1075
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
1076
+ class AssertionError extends Error {
1077
+ name = "AssertionError";
1078
+ constructor(message) {
1079
+ super(message);
1080
1080
  }
1081
- else if (lodash.isArray(source)) {
1082
- for (const keyValuePair of source) {
1083
- target.set(keyValuePair[0], keyValuePair[1]);
1084
- }
1081
+ }
1082
+
1083
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
1084
+ /** Make an assertion, error will be thrown if `expr` does not have truthy value. */
1085
+ function assert(expr, msg = "") {
1086
+ if (!expr) {
1087
+ throw new AssertionError(msg);
1085
1088
  }
1086
- else if (lodash.isObject(source)) {
1087
- Object.entries(source).forEach(([key, value]) => target.set(key, value));
1089
+ }
1090
+
1091
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
1092
+ // This module is browser compatible.
1093
+ /**
1094
+ * Formats the given date to IMF date time format. (Reference:
1095
+ * https://tools.ietf.org/html/rfc7231#section-7.1.1.1).
1096
+ * IMF is the time format to use when generating times in HTTP
1097
+ * headers. The time being formatted must be in UTC for Format to
1098
+ * generate the correct format.
1099
+ *
1100
+ * @example
1101
+ * ```ts
1102
+ * import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/to_imf.ts";
1103
+ *
1104
+ * toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT"
1105
+ * ```
1106
+ * @param date Date to parse
1107
+ * @return IMF date formatted string
1108
+ */
1109
+ function toIMF(date) {
1110
+ function dtPad(v, lPad = 2) {
1111
+ return v.padStart(lPad, "0");
1088
1112
  }
1089
- }
1090
- function newResponse(options) {
1091
- return new Response(options.body, {
1092
- headers: options.headers,
1093
- status: options.status || 200,
1094
- });
1095
- }
1096
- class RapidResponse {
1097
- // TODO: remove this field.
1098
- #response;
1099
- status;
1100
- body;
1101
- headers;
1102
- constructor(body, init) {
1103
- this.body = body;
1104
- this.headers = new Headers(init?.headers);
1105
- this.status = init?.status;
1113
+ const d = dtPad(date.getUTCDate().toString());
1114
+ const h = dtPad(date.getUTCHours().toString());
1115
+ const min = dtPad(date.getUTCMinutes().toString());
1116
+ const s = dtPad(date.getUTCSeconds().toString());
1117
+ const y = date.getUTCFullYear();
1118
+ const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
1119
+ const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
1120
+ return `${days[date.getUTCDay()]}, ${d} ${months[date.getUTCMonth()]} ${y} ${h}:${min}:${s} GMT`;
1121
+ }
1122
+
1123
+ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
1124
+ const FIELD_CONTENT_REGEXP = /^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/;
1125
+ function toString(cookie) {
1126
+ if (!cookie.name) {
1127
+ return "";
1106
1128
  }
1107
- json(obj, status, headers) {
1108
- let body = null;
1109
- if (obj) {
1110
- body = JSON.stringify(obj);
1111
- }
1112
- this.headers.set("Content-Type", "application/json");
1113
- const responseHeaders = new Headers(this.headers);
1114
- if (headers) {
1115
- mergeHeaders(responseHeaders, headers);
1116
- }
1117
- this.status = status || 200;
1118
- this.body = body;
1119
- this.#response = newResponse({ body, status: this.status, headers: responseHeaders });
1129
+ const out = [];
1130
+ validateName(cookie.name);
1131
+ validateValue(cookie.name, cookie.value);
1132
+ out.push(`${cookie.name}=${cookie.value}`);
1133
+ // Fallback for invalid Set-Cookie
1134
+ // ref: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
1135
+ if (cookie.name.startsWith("__Secure")) {
1136
+ cookie.secure = true;
1120
1137
  }
1121
- redirect(location, status) {
1122
- this.headers.set("Location", location);
1123
- this.status = status || 302;
1124
- this.#response = newResponse({
1125
- headers: this.headers,
1126
- status: this.status,
1127
- });
1138
+ if (cookie.name.startsWith("__Host")) {
1139
+ cookie.path = "/";
1140
+ cookie.secure = true;
1141
+ delete cookie.domain;
1128
1142
  }
1129
- getResponse() {
1130
- if (!this.#response) {
1131
- this.#response = new Response(this.body, {
1132
- status: this.status || 200,
1133
- headers: this.headers,
1134
- });
1135
- }
1136
- return this.#response;
1143
+ if (cookie.secure) {
1144
+ out.push("Secure");
1137
1145
  }
1138
- }
1139
-
1140
- // TODO: should divide to RequestContext and OperationContext
1141
- class RouteContext {
1142
- request;
1143
- response;
1144
- state;
1145
- databaseAccessor;
1146
- method;
1147
- path;
1148
- params;
1149
- routeConfig;
1150
- #server;
1151
- #dbTransactionClient;
1152
- #dbTransactionState;
1153
- static newSystemOperationContext(server) {
1154
- return new RouteContext(server);
1146
+ if (cookie.httpOnly) {
1147
+ out.push("HttpOnly");
1155
1148
  }
1156
- constructor(server, request) {
1157
- this.#server = server;
1158
- this.databaseAccessor = server.getDatabaseAccessor();
1159
- this.#dbTransactionState = "uninited";
1160
- this.request = request;
1161
- this.state = {};
1162
- this.response = new RapidResponse();
1163
- // `method` and `path` are used by `koa-tree-router` to match route
1164
- if (this.request) {
1165
- this.method = request.method;
1166
- this.path = request.url.pathname;
1167
- }
1149
+ if (typeof cookie.maxAge === "number" && Number.isInteger(cookie.maxAge)) {
1150
+ assert(cookie.maxAge >= 0, "Max-Age must be an integer superior or equal to 0");
1151
+ out.push(`Max-Age=${cookie.maxAge}`);
1168
1152
  }
1169
- clone() {
1170
- const clonedContext = new RouteContext(this.#server);
1171
- clonedContext.method = this.method;
1172
- clonedContext.path = this.path;
1173
- clonedContext.params = this.params;
1174
- clonedContext.setState(this.state);
1175
- return clonedContext;
1153
+ if (cookie.domain) {
1154
+ validateDomain(cookie.domain);
1155
+ out.push(`Domain=${cookie.domain}`);
1176
1156
  }
1177
- setState(state) {
1178
- Object.assign(this.state, state);
1157
+ if (cookie.sameSite) {
1158
+ out.push(`SameSite=${cookie.sameSite}`);
1179
1159
  }
1180
- // `koa-tree-router` uses this method to set headers
1181
- set(headerName, headerValue) {
1182
- this.response.headers.set(headerName, headerValue);
1160
+ if (cookie.path) {
1161
+ validatePath(cookie.path);
1162
+ out.push(`Path=${cookie.path}`);
1183
1163
  }
1184
- json(obj, status, headers) {
1185
- this.response.json(obj, status, headers);
1164
+ if (cookie.expires) {
1165
+ const { expires } = cookie;
1166
+ const dateString = toIMF(typeof expires === "number" ? new Date(expires) : expires);
1167
+ out.push(`Expires=${dateString}`);
1186
1168
  }
1187
- redirect(url, status) {
1188
- this.response.redirect(url, status);
1169
+ if (cookie.unparsed) {
1170
+ out.push(cookie.unparsed.join("; "));
1189
1171
  }
1190
- getDbTransactionClient() {
1191
- return this.#dbTransactionClient;
1172
+ return out.join("; ");
1173
+ }
1174
+ /**
1175
+ * Validate Cookie Name.
1176
+ * @param name Cookie name.
1177
+ */
1178
+ function validateName(name) {
1179
+ if (name && !FIELD_CONTENT_REGEXP.test(name)) {
1180
+ throw new TypeError(`Invalid cookie name: "${name}".`);
1192
1181
  }
1193
- async initDbTransactionClient() {
1194
- let dbClient = this.#dbTransactionClient;
1195
- if (dbClient) {
1196
- return dbClient;
1197
- }
1198
- dbClient = await this.databaseAccessor.getClient();
1199
- this.#dbTransactionState = "inited";
1200
- this.#dbTransactionClient = dbClient;
1201
- return dbClient;
1182
+ }
1183
+ /**
1184
+ * Validate Path Value.
1185
+ * See {@link https://tools.ietf.org/html/rfc6265#section-4.1.2.4}.
1186
+ * @param path Path value.
1187
+ */
1188
+ function validatePath(path) {
1189
+ if (path == null) {
1190
+ return;
1202
1191
  }
1203
- async beginDbTransaction() {
1204
- if (!this.#dbTransactionClient) {
1205
- throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1206
- }
1207
- if (this.#dbTransactionState === "started") {
1208
- throw new Error("Database transaction has been started. You can not begin a new transaction before you commit or rollback it.");
1192
+ for (let i = 0; i < path.length; i++) {
1193
+ const c = path.charAt(i);
1194
+ if (c < String.fromCharCode(0x20) || c > String.fromCharCode(0x7e) || c == ";") {
1195
+ throw new Error(path + ": Invalid cookie path char '" + c + "'");
1209
1196
  }
1210
- await this.databaseAccessor.queryDatabaseObject("BEGIN", [], this.#dbTransactionClient);
1211
- this.#dbTransactionState = "started";
1212
1197
  }
1213
- async commitDbTransaction() {
1214
- if (!this.#dbTransactionClient) {
1215
- throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1198
+ }
1199
+ /**
1200
+ * Validate Cookie Value.
1201
+ * See {@link https://tools.ietf.org/html/rfc6265#section-4.1}.
1202
+ * @param value Cookie value.
1203
+ */
1204
+ function validateValue(name, value) {
1205
+ if (value == null || name == null)
1206
+ return;
1207
+ for (let i = 0; i < value.length; i++) {
1208
+ const c = value.charAt(i);
1209
+ if (c < String.fromCharCode(0x21) ||
1210
+ c == String.fromCharCode(0x22) ||
1211
+ c == String.fromCharCode(0x2c) ||
1212
+ c == String.fromCharCode(0x3b) ||
1213
+ c == String.fromCharCode(0x5c) ||
1214
+ c == String.fromCharCode(0x7f)) {
1215
+ throw new Error("RFC2616 cookie '" + name + "' cannot contain character '" + c + "'");
1216
1216
  }
1217
- if (this.#dbTransactionState !== "started") {
1218
- throw new Error("Database transaction has not been started. You should call beginDbTransaction() first.");
1217
+ if (c > String.fromCharCode(0x80)) {
1218
+ throw new Error("RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" + c.charCodeAt(0).toString(16));
1219
1219
  }
1220
- await this.databaseAccessor.queryDatabaseObject("COMMIT", [], this.#dbTransactionClient);
1221
- this.#dbTransactionState = "inited";
1222
1220
  }
1223
- async rollbackDbTransaction() {
1224
- if (!this.#dbTransactionClient) {
1225
- throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1226
- }
1221
+ }
1222
+ /**
1223
+ * Validate Cookie Domain.
1224
+ * See {@link https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.3}.
1225
+ * @param domain Cookie domain.
1226
+ */
1227
+ function validateDomain(domain) {
1228
+ if (domain == null) {
1229
+ return;
1230
+ }
1231
+ const char1 = domain.charAt(0);
1232
+ const charN = domain.charAt(domain.length - 1);
1233
+ if (char1 == "-" || charN == "." || charN == "-") {
1234
+ throw new Error("Invalid first/last char in cookie domain: " + domain);
1235
+ }
1236
+ }
1237
+ /**
1238
+ * Parse cookies of a header
1239
+ *
1240
+ * @example
1241
+ * ```ts
1242
+ * import { getCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
1243
+ *
1244
+ * const headers = new Headers();
1245
+ * headers.set("Cookie", "full=of; tasty=chocolate");
1246
+ *
1247
+ * const cookies = getCookies(headers);
1248
+ * console.log(cookies); // { full: "of", tasty: "chocolate" }
1249
+ * ```
1250
+ *
1251
+ * @param headers The headers instance to get cookies from
1252
+ * @return Object with cookie names as keys
1253
+ */
1254
+ function getCookies(headers) {
1255
+ const cookie = headers.get("Cookie");
1256
+ if (cookie != null) {
1257
+ const out = {};
1258
+ const c = cookie.split(";");
1259
+ for (const kv of c) {
1260
+ const [cookieKey, ...cookieVal] = kv.split("=");
1261
+ assert(cookieKey != null);
1262
+ const key = cookieKey.trim();
1263
+ out[key] = cookieVal.join("=");
1264
+ }
1265
+ return out;
1266
+ }
1267
+ return {};
1268
+ }
1269
+ /**
1270
+ * Set the cookie header properly in the headers
1271
+ *
1272
+ * @example
1273
+ * ```ts
1274
+ * import {
1275
+ * Cookie,
1276
+ * setCookie,
1277
+ * } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
1278
+ *
1279
+ * const headers = new Headers();
1280
+ * const cookie: Cookie = { name: "Space", value: "Cat" };
1281
+ * setCookie(headers, cookie);
1282
+ *
1283
+ * const cookieHeader = headers.get("set-cookie");
1284
+ * console.log(cookieHeader); // Space=Cat
1285
+ * ```
1286
+ *
1287
+ * @param headers The headers instance to set the cookie to
1288
+ * @param cookie Cookie to set
1289
+ */
1290
+ function setCookie(headers, cookie) {
1291
+ // Parsing cookie headers to make consistent set-cookie header
1292
+ // ref: https://tools.ietf.org/html/rfc6265#section-4.1.1
1293
+ const v = toString(cookie);
1294
+ if (v) {
1295
+ headers.append("Set-Cookie", v);
1296
+ }
1297
+ }
1298
+ /**
1299
+ * Set the cookie header with empty value in the headers to delete it
1300
+ *
1301
+ * > Note: Deleting a `Cookie` will set its expiration date before now. Forcing
1302
+ * > the browser to delete it.
1303
+ *
1304
+ * @example
1305
+ * ```ts
1306
+ * import { deleteCookie } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
1307
+ *
1308
+ * const headers = new Headers();
1309
+ * deleteCookie(headers, "deno");
1310
+ *
1311
+ * const cookieHeader = headers.get("set-cookie");
1312
+ * console.log(cookieHeader); // deno=; Expires=Thus, 01 Jan 1970 00:00:00 GMT
1313
+ * ```
1314
+ *
1315
+ * @param headers The headers instance to delete the cookie from
1316
+ * @param name Name of cookie
1317
+ * @param attributes Additional cookie attributes
1318
+ */
1319
+ function deleteCookie(headers, name, attributes) {
1320
+ setCookie(headers, {
1321
+ name: name,
1322
+ value: "",
1323
+ expires: new Date(0),
1324
+ ...attributes,
1325
+ });
1326
+ }
1327
+ function parseSetCookie(value) {
1328
+ const attrs = value.split(";").map((attr) => {
1329
+ const [key, ...values] = attr.trim().split("=");
1330
+ return [key, values.join("=")];
1331
+ });
1332
+ const cookie = {
1333
+ name: attrs[0][0],
1334
+ value: attrs[0][1],
1335
+ };
1336
+ for (const [key, value] of attrs.slice(1)) {
1337
+ switch (key.toLocaleLowerCase()) {
1338
+ case "expires":
1339
+ cookie.expires = new Date(value);
1340
+ break;
1341
+ case "max-age":
1342
+ cookie.maxAge = Number(value);
1343
+ if (cookie.maxAge < 0) {
1344
+ console.warn("Max-Age must be an integer superior or equal to 0. Cookie ignored.");
1345
+ return null;
1346
+ }
1347
+ break;
1348
+ case "domain":
1349
+ cookie.domain = value;
1350
+ break;
1351
+ case "path":
1352
+ cookie.path = value;
1353
+ break;
1354
+ case "secure":
1355
+ cookie.secure = true;
1356
+ break;
1357
+ case "httponly":
1358
+ cookie.httpOnly = true;
1359
+ break;
1360
+ case "samesite":
1361
+ cookie.sameSite = value;
1362
+ break;
1363
+ default:
1364
+ if (!Array.isArray(cookie.unparsed)) {
1365
+ cookie.unparsed = [];
1366
+ }
1367
+ cookie.unparsed.push([key, value].join("="));
1368
+ }
1369
+ }
1370
+ if (cookie.name.startsWith("__Secure-")) {
1371
+ /** This requirement is mentioned in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie but not the RFC. */
1372
+ if (!cookie.secure) {
1373
+ console.warn("Cookies with names starting with `__Secure-` must be set with the secure flag. Cookie ignored.");
1374
+ return null;
1375
+ }
1376
+ }
1377
+ if (cookie.name.startsWith("__Host-")) {
1378
+ if (!cookie.secure) {
1379
+ console.warn("Cookies with names starting with `__Host-` must be set with the secure flag. Cookie ignored.");
1380
+ return null;
1381
+ }
1382
+ if (cookie.domain !== undefined) {
1383
+ console.warn("Cookies with names starting with `__Host-` must not have a domain specified. Cookie ignored.");
1384
+ return null;
1385
+ }
1386
+ if (cookie.path !== "/") {
1387
+ console.warn("Cookies with names starting with `__Host-` must have path be `/`. Cookie has been ignored.");
1388
+ return null;
1389
+ }
1390
+ }
1391
+ return cookie;
1392
+ }
1393
+ /**
1394
+ * Parse set-cookies of a header
1395
+ *
1396
+ * @example
1397
+ * ```ts
1398
+ * import { getSetCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
1399
+ *
1400
+ * const headers = new Headers([
1401
+ * ["Set-Cookie", "lulu=meow; Secure; Max-Age=3600"],
1402
+ * ["Set-Cookie", "booya=kasha; HttpOnly; Path=/"],
1403
+ * ]);
1404
+ *
1405
+ * const cookies = getSetCookies(headers);
1406
+ * console.log(cookies); // [{ name: "lulu", value: "meow", secure: true, maxAge: 3600 }, { name: "booya", value: "kahsa", httpOnly: true, path: "/ }]
1407
+ * ```
1408
+ *
1409
+ * @param headers The headers instance to get set-cookies from
1410
+ * @return List of cookies
1411
+ */
1412
+ function getSetCookies(headers) {
1413
+ // TODO(lino-levan): remove this ts-ignore when Typescript 5.2 lands in Deno
1414
+ // @ts-ignore Typescript's TS Dom types will be out of date until 5.2
1415
+ return headers
1416
+ .getSetCookie()
1417
+ /** Parse each `set-cookie` header separately */
1418
+ .map(parseSetCookie)
1419
+ /** Skip empty cookies */
1420
+ .filter(Boolean);
1421
+ }
1422
+
1423
+ function mergeHeaders(target, source) {
1424
+ if (source instanceof Headers) {
1425
+ for (const keyValuePair of source.entries()) {
1426
+ target.set(keyValuePair[0], keyValuePair[1]);
1427
+ }
1428
+ }
1429
+ else if (lodash.isArray(source)) {
1430
+ for (const keyValuePair of source) {
1431
+ target.set(keyValuePair[0], keyValuePair[1]);
1432
+ }
1433
+ }
1434
+ else if (lodash.isObject(source)) {
1435
+ Object.entries(source).forEach(([key, value]) => target.set(key, value));
1436
+ }
1437
+ }
1438
+ function newResponse(options) {
1439
+ return new Response(options.body, {
1440
+ headers: options.headers,
1441
+ status: options.status || 200,
1442
+ });
1443
+ }
1444
+ class RapidResponse {
1445
+ // TODO: remove this field.
1446
+ #response;
1447
+ status;
1448
+ body;
1449
+ headers;
1450
+ constructor(body, init) {
1451
+ this.body = body;
1452
+ this.headers = new Headers(init?.headers);
1453
+ this.status = init?.status;
1454
+ }
1455
+ json(obj, status, headers) {
1456
+ let body = null;
1457
+ if (obj) {
1458
+ body = JSON.stringify(obj);
1459
+ }
1460
+ this.headers.set("Content-Type", "application/json");
1461
+ const responseHeaders = new Headers(this.headers);
1462
+ if (headers) {
1463
+ mergeHeaders(responseHeaders, headers);
1464
+ }
1465
+ this.status = status || 200;
1466
+ this.body = body;
1467
+ this.#response = newResponse({ body, status: this.status, headers: responseHeaders });
1468
+ }
1469
+ redirect(location, status) {
1470
+ this.headers.set("Location", location);
1471
+ this.status = status || 302;
1472
+ this.#response = newResponse({
1473
+ headers: this.headers,
1474
+ status: this.status,
1475
+ });
1476
+ }
1477
+ setCookie(cookie) {
1478
+ setCookie(this.headers, cookie);
1479
+ }
1480
+ getResponse() {
1481
+ if (!this.#response) {
1482
+ this.#response = new Response(this.body, {
1483
+ status: this.status || 200,
1484
+ headers: this.headers,
1485
+ });
1486
+ }
1487
+ return this.#response;
1488
+ }
1489
+ }
1490
+
1491
+ // TODO: should divide to RequestContext and OperationContext
1492
+ class RouteContext {
1493
+ request;
1494
+ response;
1495
+ state;
1496
+ databaseAccessor;
1497
+ method;
1498
+ path;
1499
+ params;
1500
+ routeConfig;
1501
+ #server;
1502
+ #dbTransactionClient;
1503
+ #dbTransactionState;
1504
+ static newSystemOperationContext(server) {
1505
+ return new RouteContext(server);
1506
+ }
1507
+ constructor(server, request) {
1508
+ this.#server = server;
1509
+ this.databaseAccessor = server.getDatabaseAccessor();
1510
+ this.#dbTransactionState = "uninited";
1511
+ this.request = request;
1512
+ this.state = {};
1513
+ this.response = new RapidResponse();
1514
+ // `method` and `path` are used by `koa-tree-router` to match route
1515
+ if (this.request) {
1516
+ this.method = request.method;
1517
+ this.path = request.url.pathname;
1518
+ }
1519
+ }
1520
+ clone() {
1521
+ const clonedContext = new RouteContext(this.#server);
1522
+ clonedContext.method = this.method;
1523
+ clonedContext.path = this.path;
1524
+ clonedContext.params = this.params;
1525
+ clonedContext.setState(this.state);
1526
+ return clonedContext;
1527
+ }
1528
+ setState(state) {
1529
+ Object.assign(this.state, state);
1530
+ }
1531
+ // `koa-tree-router` uses this method to set headers
1532
+ set(headerName, headerValue) {
1533
+ this.response.headers.set(headerName, headerValue);
1534
+ }
1535
+ json(obj, status, headers) {
1536
+ this.response.json(obj, status, headers);
1537
+ }
1538
+ redirect(url, status) {
1539
+ this.response.redirect(url, status);
1540
+ }
1541
+ getDbTransactionClient() {
1542
+ return this.#dbTransactionClient;
1543
+ }
1544
+ async initDbTransactionClient() {
1545
+ let dbClient = this.#dbTransactionClient;
1546
+ if (dbClient) {
1547
+ return dbClient;
1548
+ }
1549
+ dbClient = await this.databaseAccessor.getClient();
1550
+ this.#dbTransactionState = "inited";
1551
+ this.#dbTransactionClient = dbClient;
1552
+ return dbClient;
1553
+ }
1554
+ async beginDbTransaction() {
1555
+ if (!this.#dbTransactionClient) {
1556
+ throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1557
+ }
1558
+ if (this.#dbTransactionState === "started") {
1559
+ throw new Error("Database transaction has been started. You can not begin a new transaction before you commit or rollback it.");
1560
+ }
1561
+ await this.databaseAccessor.queryDatabaseObject("BEGIN", [], this.#dbTransactionClient);
1562
+ this.#dbTransactionState = "started";
1563
+ }
1564
+ async commitDbTransaction() {
1565
+ if (!this.#dbTransactionClient) {
1566
+ throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1567
+ }
1568
+ if (this.#dbTransactionState !== "started") {
1569
+ throw new Error("Database transaction has not been started. You should call beginDbTransaction() first.");
1570
+ }
1571
+ await this.databaseAccessor.queryDatabaseObject("COMMIT", [], this.#dbTransactionClient);
1572
+ this.#dbTransactionState = "inited";
1573
+ }
1574
+ async rollbackDbTransaction() {
1575
+ if (!this.#dbTransactionClient) {
1576
+ throw new Error("Database transaction has not been inited. You should call initDbTransactionClient() first.");
1577
+ }
1227
1578
  if (this.#dbTransactionState !== "started") {
1228
1579
  throw new Error("Database transaction has not been started. You should call beginDbTransaction() first.");
1229
1580
  }
@@ -4584,434 +4935,86 @@ class RapidServer {
4584
4935
  emitter = this.#entityCreateEventEmitters;
4585
4936
  }
4586
4937
  else if (eventName === "entity.beforeUpdate") {
4587
- emitter = this.#entityBeforeUpdateEventEmitters;
4588
- }
4589
- else if (eventName === "entity.update") {
4590
- emitter = this.#entityUpdateEventEmitters;
4591
- }
4592
- else if (eventName === "entity.beforeDelete") {
4593
- emitter = this.#entityBeforeDeleteEventEmitters;
4594
- }
4595
- else if (eventName === "entity.delete") {
4596
- emitter = this.#entityDeleteEventEmitters;
4597
- }
4598
- else if (eventName === "entity.addRelations") {
4599
- emitter = this.#entityAddRelationsEventEmitters;
4600
- }
4601
- else if (eventName === "entity.removeRelations") {
4602
- emitter = this.#entityRemoveRelationsEventEmitters;
4603
- }
4604
- else if (eventName === "entity.beforeResponse") {
4605
- emitter = this.#entityBeforeResponseEventEmitters;
4606
- }
4607
- await emitter.emit(modelSingularCode, entityWatchHandlerContext);
4608
- if (baseModelSingularCode) {
4609
- await emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
4610
- }
4611
- }
4612
- }
4613
-
4614
- const parseFormDataBody = async (request, options = { all: false }) => {
4615
- const contentType = request.headers.get("Content-Type");
4616
- if (isFormDataContent(contentType)) {
4617
- return parseFormData(request, options);
4618
- }
4619
- return {};
4620
- };
4621
- function isFormDataContent(contentType) {
4622
- if (contentType === null) {
4623
- return false;
4624
- }
4625
- return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
4626
- }
4627
- async function parseFormData(request, options) {
4628
- const formData = await request.formData();
4629
- if (formData) {
4630
- return convertFormDataToBodyData(formData, options);
4631
- }
4632
- return {};
4633
- }
4634
- function convertFormDataToBodyData(formData, options) {
4635
- const form = {};
4636
- formData.forEach((value, key) => {
4637
- const shouldParseAllValues = options.all || key.endsWith("[]");
4638
- if (!shouldParseAllValues) {
4639
- form[key] = value;
4640
- }
4641
- else {
4642
- handleParsingAllValues(form, key, value);
4643
- }
4644
- });
4645
- return form;
4646
- }
4647
- const handleParsingAllValues = (form, key, value) => {
4648
- if (form[key] && isArrayField(form[key])) {
4649
- appendToExistingArray(form[key], value);
4650
- }
4651
- else if (form[key]) {
4652
- convertToNewArray(form, key, value);
4653
- }
4654
- else {
4655
- form[key] = value;
4656
- }
4657
- };
4658
- function isArrayField(field) {
4659
- return Array.isArray(field);
4660
- }
4661
- const appendToExistingArray = (arr, value) => {
4662
- arr.push(value);
4663
- };
4664
- const convertToNewArray = (form, key, value) => {
4665
- form[key] = [form[key], value];
4666
- };
4667
-
4668
- // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
4669
- class AssertionError extends Error {
4670
- name = "AssertionError";
4671
- constructor(message) {
4672
- super(message);
4673
- }
4674
- }
4675
-
4676
- // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
4677
- /** Make an assertion, error will be thrown if `expr` does not have truthy value. */
4678
- function assert(expr, msg = "") {
4679
- if (!expr) {
4680
- throw new AssertionError(msg);
4681
- }
4682
- }
4683
-
4684
- // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
4685
- // This module is browser compatible.
4686
- /**
4687
- * Formats the given date to IMF date time format. (Reference:
4688
- * https://tools.ietf.org/html/rfc7231#section-7.1.1.1).
4689
- * IMF is the time format to use when generating times in HTTP
4690
- * headers. The time being formatted must be in UTC for Format to
4691
- * generate the correct format.
4692
- *
4693
- * @example
4694
- * ```ts
4695
- * import { toIMF } from "https://deno.land/std@$STD_VERSION/datetime/to_imf.ts";
4696
- *
4697
- * toIMF(new Date(0)); // => returns "Thu, 01 Jan 1970 00:00:00 GMT"
4698
- * ```
4699
- * @param date Date to parse
4700
- * @return IMF date formatted string
4701
- */
4702
- function toIMF(date) {
4703
- function dtPad(v, lPad = 2) {
4704
- return v.padStart(lPad, "0");
4705
- }
4706
- const d = dtPad(date.getUTCDate().toString());
4707
- const h = dtPad(date.getUTCHours().toString());
4708
- const min = dtPad(date.getUTCMinutes().toString());
4709
- const s = dtPad(date.getUTCSeconds().toString());
4710
- const y = date.getUTCFullYear();
4711
- const days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
4712
- const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
4713
- return `${days[date.getUTCDay()]}, ${d} ${months[date.getUTCMonth()]} ${y} ${h}:${min}:${s} GMT`;
4714
- }
4715
-
4716
- // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
4717
- const FIELD_CONTENT_REGEXP = /^(?=[\x20-\x7E]*$)[^()@<>,;:\\"\[\]?={}\s]+$/;
4718
- function toString(cookie) {
4719
- if (!cookie.name) {
4720
- return "";
4721
- }
4722
- const out = [];
4723
- validateName(cookie.name);
4724
- validateValue(cookie.name, cookie.value);
4725
- out.push(`${cookie.name}=${cookie.value}`);
4726
- // Fallback for invalid Set-Cookie
4727
- // ref: https://tools.ietf.org/html/draft-ietf-httpbis-cookie-prefixes-00#section-3.1
4728
- if (cookie.name.startsWith("__Secure")) {
4729
- cookie.secure = true;
4730
- }
4731
- if (cookie.name.startsWith("__Host")) {
4732
- cookie.path = "/";
4733
- cookie.secure = true;
4734
- delete cookie.domain;
4735
- }
4736
- if (cookie.secure) {
4737
- out.push("Secure");
4738
- }
4739
- if (cookie.httpOnly) {
4740
- out.push("HttpOnly");
4741
- }
4742
- if (typeof cookie.maxAge === "number" && Number.isInteger(cookie.maxAge)) {
4743
- assert(cookie.maxAge >= 0, "Max-Age must be an integer superior or equal to 0");
4744
- out.push(`Max-Age=${cookie.maxAge}`);
4745
- }
4746
- if (cookie.domain) {
4747
- validateDomain(cookie.domain);
4748
- out.push(`Domain=${cookie.domain}`);
4749
- }
4750
- if (cookie.sameSite) {
4751
- out.push(`SameSite=${cookie.sameSite}`);
4752
- }
4753
- if (cookie.path) {
4754
- validatePath(cookie.path);
4755
- out.push(`Path=${cookie.path}`);
4756
- }
4757
- if (cookie.expires) {
4758
- const { expires } = cookie;
4759
- const dateString = toIMF(typeof expires === "number" ? new Date(expires) : expires);
4760
- out.push(`Expires=${dateString}`);
4761
- }
4762
- if (cookie.unparsed) {
4763
- out.push(cookie.unparsed.join("; "));
4764
- }
4765
- return out.join("; ");
4766
- }
4767
- /**
4768
- * Validate Cookie Name.
4769
- * @param name Cookie name.
4770
- */
4771
- function validateName(name) {
4772
- if (name && !FIELD_CONTENT_REGEXP.test(name)) {
4773
- throw new TypeError(`Invalid cookie name: "${name}".`);
4774
- }
4775
- }
4776
- /**
4777
- * Validate Path Value.
4778
- * See {@link https://tools.ietf.org/html/rfc6265#section-4.1.2.4}.
4779
- * @param path Path value.
4780
- */
4781
- function validatePath(path) {
4782
- if (path == null) {
4783
- return;
4784
- }
4785
- for (let i = 0; i < path.length; i++) {
4786
- const c = path.charAt(i);
4787
- if (c < String.fromCharCode(0x20) || c > String.fromCharCode(0x7e) || c == ";") {
4788
- throw new Error(path + ": Invalid cookie path char '" + c + "'");
4789
- }
4790
- }
4791
- }
4792
- /**
4793
- * Validate Cookie Value.
4794
- * See {@link https://tools.ietf.org/html/rfc6265#section-4.1}.
4795
- * @param value Cookie value.
4796
- */
4797
- function validateValue(name, value) {
4798
- if (value == null || name == null)
4799
- return;
4800
- for (let i = 0; i < value.length; i++) {
4801
- const c = value.charAt(i);
4802
- if (c < String.fromCharCode(0x21) ||
4803
- c == String.fromCharCode(0x22) ||
4804
- c == String.fromCharCode(0x2c) ||
4805
- c == String.fromCharCode(0x3b) ||
4806
- c == String.fromCharCode(0x5c) ||
4807
- c == String.fromCharCode(0x7f)) {
4808
- throw new Error("RFC2616 cookie '" + name + "' cannot contain character '" + c + "'");
4938
+ emitter = this.#entityBeforeUpdateEventEmitters;
4809
4939
  }
4810
- if (c > String.fromCharCode(0x80)) {
4811
- throw new Error("RFC2616 cookie '" + name + "' can only have US-ASCII chars as value" + c.charCodeAt(0).toString(16));
4940
+ else if (eventName === "entity.update") {
4941
+ emitter = this.#entityUpdateEventEmitters;
4942
+ }
4943
+ else if (eventName === "entity.beforeDelete") {
4944
+ emitter = this.#entityBeforeDeleteEventEmitters;
4945
+ }
4946
+ else if (eventName === "entity.delete") {
4947
+ emitter = this.#entityDeleteEventEmitters;
4948
+ }
4949
+ else if (eventName === "entity.addRelations") {
4950
+ emitter = this.#entityAddRelationsEventEmitters;
4951
+ }
4952
+ else if (eventName === "entity.removeRelations") {
4953
+ emitter = this.#entityRemoveRelationsEventEmitters;
4954
+ }
4955
+ else if (eventName === "entity.beforeResponse") {
4956
+ emitter = this.#entityBeforeResponseEventEmitters;
4957
+ }
4958
+ await emitter.emit(modelSingularCode, entityWatchHandlerContext);
4959
+ if (baseModelSingularCode) {
4960
+ await emitter.emit(baseModelSingularCode, entityWatchHandlerContext);
4812
4961
  }
4813
4962
  }
4814
- }
4815
- /**
4816
- * Validate Cookie Domain.
4817
- * See {@link https://datatracker.ietf.org/doc/html/rfc6265#section-4.1.2.3}.
4818
- * @param domain Cookie domain.
4819
- */
4820
- function validateDomain(domain) {
4821
- if (domain == null) {
4822
- return;
4963
+ }
4964
+
4965
+ const parseFormDataBody = async (request, options = { all: false }) => {
4966
+ const contentType = request.headers.get("Content-Type");
4967
+ if (isFormDataContent(contentType)) {
4968
+ return parseFormData(request, options);
4823
4969
  }
4824
- const char1 = domain.charAt(0);
4825
- const charN = domain.charAt(domain.length - 1);
4826
- if (char1 == "-" || charN == "." || charN == "-") {
4827
- throw new Error("Invalid first/last char in cookie domain: " + domain);
4970
+ return {};
4971
+ };
4972
+ function isFormDataContent(contentType) {
4973
+ if (contentType === null) {
4974
+ return false;
4828
4975
  }
4976
+ return contentType.startsWith("multipart/form-data") || contentType.startsWith("application/x-www-form-urlencoded");
4829
4977
  }
4830
- /**
4831
- * Parse cookies of a header
4832
- *
4833
- * @example
4834
- * ```ts
4835
- * import { getCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
4836
- *
4837
- * const headers = new Headers();
4838
- * headers.set("Cookie", "full=of; tasty=chocolate");
4839
- *
4840
- * const cookies = getCookies(headers);
4841
- * console.log(cookies); // { full: "of", tasty: "chocolate" }
4842
- * ```
4843
- *
4844
- * @param headers The headers instance to get cookies from
4845
- * @return Object with cookie names as keys
4846
- */
4847
- function getCookies(headers) {
4848
- const cookie = headers.get("Cookie");
4849
- if (cookie != null) {
4850
- const out = {};
4851
- const c = cookie.split(";");
4852
- for (const kv of c) {
4853
- const [cookieKey, ...cookieVal] = kv.split("=");
4854
- assert(cookieKey != null);
4855
- const key = cookieKey.trim();
4856
- out[key] = cookieVal.join("=");
4857
- }
4858
- return out;
4978
+ async function parseFormData(request, options) {
4979
+ const formData = await request.formData();
4980
+ if (formData) {
4981
+ return convertFormDataToBodyData(formData, options);
4859
4982
  }
4860
4983
  return {};
4861
4984
  }
4862
- /**
4863
- * Set the cookie header properly in the headers
4864
- *
4865
- * @example
4866
- * ```ts
4867
- * import {
4868
- * Cookie,
4869
- * setCookie,
4870
- * } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
4871
- *
4872
- * const headers = new Headers();
4873
- * const cookie: Cookie = { name: "Space", value: "Cat" };
4874
- * setCookie(headers, cookie);
4875
- *
4876
- * const cookieHeader = headers.get("set-cookie");
4877
- * console.log(cookieHeader); // Space=Cat
4878
- * ```
4879
- *
4880
- * @param headers The headers instance to set the cookie to
4881
- * @param cookie Cookie to set
4882
- */
4883
- function setCookie(headers, cookie) {
4884
- // Parsing cookie headers to make consistent set-cookie header
4885
- // ref: https://tools.ietf.org/html/rfc6265#section-4.1.1
4886
- const v = toString(cookie);
4887
- if (v) {
4888
- headers.append("Set-Cookie", v);
4889
- }
4890
- }
4891
- /**
4892
- * Set the cookie header with empty value in the headers to delete it
4893
- *
4894
- * > Note: Deleting a `Cookie` will set its expiration date before now. Forcing
4895
- * > the browser to delete it.
4896
- *
4897
- * @example
4898
- * ```ts
4899
- * import { deleteCookie } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
4900
- *
4901
- * const headers = new Headers();
4902
- * deleteCookie(headers, "deno");
4903
- *
4904
- * const cookieHeader = headers.get("set-cookie");
4905
- * console.log(cookieHeader); // deno=; Expires=Thus, 01 Jan 1970 00:00:00 GMT
4906
- * ```
4907
- *
4908
- * @param headers The headers instance to delete the cookie from
4909
- * @param name Name of cookie
4910
- * @param attributes Additional cookie attributes
4911
- */
4912
- function deleteCookie(headers, name, attributes) {
4913
- setCookie(headers, {
4914
- name: name,
4915
- value: "",
4916
- expires: new Date(0),
4917
- ...attributes,
4985
+ function convertFormDataToBodyData(formData, options) {
4986
+ const form = {};
4987
+ formData.forEach((value, key) => {
4988
+ const shouldParseAllValues = options.all || key.endsWith("[]");
4989
+ if (!shouldParseAllValues) {
4990
+ form[key] = value;
4991
+ }
4992
+ else {
4993
+ handleParsingAllValues(form, key, value);
4994
+ }
4918
4995
  });
4996
+ return form;
4919
4997
  }
4920
- function parseSetCookie(value) {
4921
- const attrs = value.split(";").map((attr) => {
4922
- const [key, ...values] = attr.trim().split("=");
4923
- return [key, values.join("=")];
4924
- });
4925
- const cookie = {
4926
- name: attrs[0][0],
4927
- value: attrs[0][1],
4928
- };
4929
- for (const [key, value] of attrs.slice(1)) {
4930
- switch (key.toLocaleLowerCase()) {
4931
- case "expires":
4932
- cookie.expires = new Date(value);
4933
- break;
4934
- case "max-age":
4935
- cookie.maxAge = Number(value);
4936
- if (cookie.maxAge < 0) {
4937
- console.warn("Max-Age must be an integer superior or equal to 0. Cookie ignored.");
4938
- return null;
4939
- }
4940
- break;
4941
- case "domain":
4942
- cookie.domain = value;
4943
- break;
4944
- case "path":
4945
- cookie.path = value;
4946
- break;
4947
- case "secure":
4948
- cookie.secure = true;
4949
- break;
4950
- case "httponly":
4951
- cookie.httpOnly = true;
4952
- break;
4953
- case "samesite":
4954
- cookie.sameSite = value;
4955
- break;
4956
- default:
4957
- if (!Array.isArray(cookie.unparsed)) {
4958
- cookie.unparsed = [];
4959
- }
4960
- cookie.unparsed.push([key, value].join("="));
4961
- }
4998
+ const handleParsingAllValues = (form, key, value) => {
4999
+ if (form[key] && isArrayField(form[key])) {
5000
+ appendToExistingArray(form[key], value);
4962
5001
  }
4963
- if (cookie.name.startsWith("__Secure-")) {
4964
- /** This requirement is mentioned in https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie but not the RFC. */
4965
- if (!cookie.secure) {
4966
- console.warn("Cookies with names starting with `__Secure-` must be set with the secure flag. Cookie ignored.");
4967
- return null;
4968
- }
5002
+ else if (form[key]) {
5003
+ convertToNewArray(form, key, value);
4969
5004
  }
4970
- if (cookie.name.startsWith("__Host-")) {
4971
- if (!cookie.secure) {
4972
- console.warn("Cookies with names starting with `__Host-` must be set with the secure flag. Cookie ignored.");
4973
- return null;
4974
- }
4975
- if (cookie.domain !== undefined) {
4976
- console.warn("Cookies with names starting with `__Host-` must not have a domain specified. Cookie ignored.");
4977
- return null;
4978
- }
4979
- if (cookie.path !== "/") {
4980
- console.warn("Cookies with names starting with `__Host-` must have path be `/`. Cookie has been ignored.");
4981
- return null;
4982
- }
5005
+ else {
5006
+ form[key] = value;
4983
5007
  }
4984
- return cookie;
5008
+ };
5009
+ function isArrayField(field) {
5010
+ return Array.isArray(field);
4985
5011
  }
4986
- /**
4987
- * Parse set-cookies of a header
4988
- *
4989
- * @example
4990
- * ```ts
4991
- * import { getSetCookies } from "https://deno.land/std@$STD_VERSION/http/cookie.ts";
4992
- *
4993
- * const headers = new Headers([
4994
- * ["Set-Cookie", "lulu=meow; Secure; Max-Age=3600"],
4995
- * ["Set-Cookie", "booya=kasha; HttpOnly; Path=/"],
4996
- * ]);
4997
- *
4998
- * const cookies = getSetCookies(headers);
4999
- * console.log(cookies); // [{ name: "lulu", value: "meow", secure: true, maxAge: 3600 }, { name: "booya", value: "kahsa", httpOnly: true, path: "/ }]
5000
- * ```
5001
- *
5002
- * @param headers The headers instance to get set-cookies from
5003
- * @return List of cookies
5004
- */
5005
- function getSetCookies(headers) {
5006
- // TODO(lino-levan): remove this ts-ignore when Typescript 5.2 lands in Deno
5007
- // @ts-ignore Typescript's TS Dom types will be out of date until 5.2
5008
- return headers
5009
- .getSetCookie()
5010
- /** Parse each `set-cookie` header separately */
5011
- .map(parseSetCookie)
5012
- /** Skip empty cookies */
5013
- .filter(Boolean);
5014
- }
5012
+ const appendToExistingArray = (arr, value) => {
5013
+ arr.push(value);
5014
+ };
5015
+ const convertToNewArray = (form, key, value) => {
5016
+ form[key] = [form[key], value];
5017
+ };
5015
5018
 
5016
5019
  const GlobalRequest = global.Request;
5017
5020
  class RapidRequest {
@@ -10075,6 +10078,7 @@ class EntityAccessControlPlugin {
10075
10078
  fixBigIntJSONSerialize();
10076
10079
 
10077
10080
  exports.AuthPlugin = AuthPlugin;
10081
+ exports.AuthService = AuthService;
10078
10082
  exports.CacheFactory = CacheFactory;
10079
10083
  exports.CronJobPlugin = CronJobPlugin;
10080
10084
  exports.DataAccessor = DataAccessor;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruiapp/rapid-core",
3
- "version": "0.10.11",
3
+ "version": "0.10.12",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,5 +1,6 @@
1
1
  import { isArray, isObject } from "lodash";
2
2
  import { HttpStatus, ResponseData } from "./http-types";
3
+ import { Cookie, setCookie } from "~/deno-std/http/cookie";
3
4
 
4
5
  export const GlobalResponse = global.Response;
5
6
 
@@ -67,6 +68,10 @@ export class RapidResponse {
67
68
  });
68
69
  }
69
70
 
71
+ setCookie(cookie: Cookie) {
72
+ setCookie(this.headers, cookie);
73
+ }
74
+
70
75
  getResponse() {
71
76
  if (!this.#response) {
72
77
  this.#response = new Response(this.body, {
package/src/index.ts CHANGED
@@ -55,6 +55,7 @@ export { default as WebhooksPlugin } from "./plugins/webhooks/WebhooksPlugin";
55
55
 
56
56
  export { default as AuthPlugin } from "./plugins/auth/AuthPlugin";
57
57
  export * from "./plugins/auth/AuthPluginTypes";
58
+ export { default as AuthService } from "./plugins/auth/services/AuthService";
58
59
 
59
60
  export { default as FileManagePlugin } from "./plugins/fileManage/FileManagePlugin";
60
61