diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6c79fa2b8..2b87a62d6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [unreleased]
+# Adds
+
+- Support for h3 framework.
+
## [11.0.0] - 2022-07-05
### Breaking change:
@@ -159,6 +163,10 @@ SuperTokens.init({
})
```
+### Adds
+
+- Support for h3 freamwork.
+
## [9.2.3] - 2022-06-03
- Changes `getUserMetadata` to return `any` type for metadata so that it's easier to use.
diff --git a/add-ts-no-check.js b/add-ts-no-check.js
index 4a256e98f..0edd5f5c8 100644
--- a/add-ts-no-check.js
+++ b/add-ts-no-check.js
@@ -8,7 +8,7 @@ glob(__dirname + "/lib/**/*.d.ts", (err, files) => {
let lines = contents.split("\n");
let newContents = `// @ts-nocheck`;
for (line of lines) {
- if (line.match(/\/\/\/\ \
+import { H3Event } from "h3";
+import type { IncomingMessage, ServerResponse } from "http";
+import { SessionContainerInterface } from "../../recipe/session/types";
+import { BaseRequest } from "../request";
+import { BaseResponse } from "../response";
+import { Framework } from "../types";
+export declare class H3Request extends BaseRequest {
+ private request;
+ constructor(request: IncomingMessage);
+ getCookieValue: (key: string) => string | undefined;
+ getFormData: () => Promise;
+ getMethod: () => import("../../types").HTTPMethod;
+ getHeaderValue: (key: string) => string | undefined;
+ getOriginalURL: () => string;
+ getKeyValueFromQuery: (key: string) => string | undefined;
+ getJSONBody: () => Promise;
+}
+export declare class H3ResponseTokens extends BaseResponse {
+ private response;
+ private statusCode;
+ constructor(response: ServerResponse);
+ sendHTMLResponse: (html: string) => void;
+ setHeader: (key: string, value: string, allowDuplicateKey: boolean) => void;
+ setCookie: (
+ key: string,
+ value: string,
+ domain: string | undefined,
+ secure: boolean,
+ httpOnly: boolean,
+ expires: number,
+ path: string,
+ sameSite: "strict" | "lax" | "none"
+ ) => void;
+ setStatusCode: (statusCode: number) => void;
+ sendJSONResponse: (content: any) => void;
+}
+export interface SessionRequest extends IncomingMessage {
+ session?: SessionContainerInterface;
+}
+export declare const middlware: () => (
+ req: IncomingMessage,
+ res: ServerResponse,
+ next: (err?: Error | undefined) => any
+) => Promise;
+export declare const errorHandler: () => (event: H3Event, errorPlain: Error, statusCode: number) => Promise;
+export interface H3Framework extends Framework {
+ middlware: () => (req: IncomingMessage, res: ServerResponse, next: (err?: Error) => any) => Promise;
+ errorHandler: () => (event: H3Event, errorPlain: Error, statusCode: number) => Promise;
+}
+export declare const H3Wrapper: H3Framework;
diff --git a/lib/build/framework/h3/framework.js b/lib/build/framework/h3/framework.js
new file mode 100644
index 000000000..09d148c15
--- /dev/null
+++ b/lib/build/framework/h3/framework.js
@@ -0,0 +1,185 @@
+"use strict";
+var __awaiter =
+ (this && this.__awaiter) ||
+ function (thisArg, _arguments, P, generator) {
+ function adopt(value) {
+ return value instanceof P
+ ? value
+ : new P(function (resolve) {
+ resolve(value);
+ });
+ }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) {
+ try {
+ step(generator.next(value));
+ } catch (e) {
+ reject(e);
+ }
+ }
+ function rejected(value) {
+ try {
+ step(generator["throw"](value));
+ } catch (e) {
+ reject(e);
+ }
+ }
+ function step(result) {
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
+ }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ };
+Object.defineProperty(exports, "__esModule", { value: true });
+const h3_1 = require("h3");
+const supertokens_1 = require("../../supertokens");
+const utils_1 = require("../../utils");
+const request_1 = require("../request");
+const response_1 = require("../response");
+const utils_2 = require("../utils");
+class H3Request extends request_1.BaseRequest {
+ constructor(request) {
+ super();
+ this.getCookieValue = (key) => {
+ return utils_2.getCookieValueFromIncomingMessage(this.request, key);
+ };
+ this.getFormData = () =>
+ __awaiter(this, void 0, void 0, function* () {
+ return utils_2.useRawBody(this.request);
+ });
+ this.getMethod = () => {
+ return utils_1.normaliseHttpMethod(this.request.method);
+ };
+ this.getHeaderValue = (key) => {
+ return utils_2.getHeaderValueFromIncomingMessage(this.request, key);
+ };
+ this.getOriginalURL = () => {
+ return this.request.url;
+ };
+ this.getKeyValueFromQuery = (key) => {
+ let path = this.request.url || "/";
+ const queryIndex = path.lastIndexOf("?");
+ if (queryIndex > -1) {
+ const queryArray = path.substring(queryIndex + 1, path.length).split("&");
+ const index = queryArray.findIndex((el) => el.includes(key));
+ if (index === -1) return undefined;
+ const value = queryArray[index].split("=")[1];
+ if (value === undefined || typeof value !== "string") {
+ return undefined;
+ }
+ return value;
+ } else {
+ return undefined;
+ }
+ };
+ this.getJSONBody = () =>
+ __awaiter(this, void 0, void 0, function* () {
+ return yield utils_2.useBody(this.request);
+ });
+ this.original = request;
+ this.request = request;
+ }
+}
+exports.H3Request = H3Request;
+class H3ResponseTokens extends response_1.BaseResponse {
+ constructor(response) {
+ super();
+ this.sendHTMLResponse = (html) => {
+ if (this.response.writable) {
+ this.response.setHeader("Content-Type", "text/html");
+ this.response.statusCode = this.statusCode;
+ this.response.end(html);
+ }
+ };
+ this.setHeader = (key, value, allowDuplicateKey) => {
+ try {
+ const allheaders = this.response.getHeaders();
+ let existingValue = allheaders[key];
+ // we have the this.response.header for compatibility with nextJS
+ if (existingValue === undefined) {
+ this.response.setHeader(key, value);
+ } else if (allowDuplicateKey) {
+ this.response.setHeader(key, existingValue + ", " + value);
+ } else {
+ // we overwrite the current one with the new one
+ this.response.setHeader(key, value);
+ }
+ } catch (err) {
+ throw new Error("Error while setting header with key: " + key + " and value: " + value);
+ }
+ };
+ this.setCookie = (key, value, domain, secure, httpOnly, expires, path, sameSite) => {
+ utils_2.setCookieForServerResponse(
+ this.response,
+ key,
+ value,
+ domain,
+ secure,
+ httpOnly,
+ expires,
+ path,
+ sameSite
+ );
+ };
+ this.setStatusCode = (statusCode) => {
+ if (this.response.writable) {
+ this.statusCode = statusCode;
+ }
+ };
+ this.sendJSONResponse = (content) => {
+ if (this.response.writable) {
+ content = JSON.stringify(content);
+ this.response.setHeader("Content-Type", "application/json");
+ this.response.statusCode = this.statusCode;
+ this.response.end(content, "utf-8");
+ }
+ };
+ this.original = response;
+ this.response = response;
+ this.statusCode = 200;
+ }
+}
+exports.H3ResponseTokens = H3ResponseTokens;
+exports.middlware = () => {
+ return (req, res, next) =>
+ __awaiter(void 0, void 0, void 0, function* () {
+ let supertokens;
+ const request = new H3Request(req);
+ const response = new H3ResponseTokens(res);
+ try {
+ supertokens = supertokens_1.default.getInstanceOrThrowError();
+ const result = yield supertokens.middleware(request, response);
+ if (!result) {
+ return next();
+ }
+ } catch (err) {
+ if (supertokens) {
+ try {
+ yield supertokens.errorHandler(err, request, response);
+ } catch (_a) {
+ next(err);
+ }
+ } else {
+ next(err);
+ }
+ }
+ });
+};
+exports.errorHandler = () => {
+ return (event, errorPlain, statusCode) =>
+ __awaiter(void 0, void 0, void 0, function* () {
+ const error = h3_1.createError(errorPlain);
+ error.statusCode = statusCode;
+ h3_1.sendError(event, error);
+ });
+};
+exports.H3Wrapper = {
+ middlware: exports.middlware,
+ errorHandler: exports.errorHandler,
+ wrapRequest: (unwrapped) => {
+ return new H3Request(unwrapped.req);
+ },
+ wrapResponse: (unwrapped) => {
+ return new H3ResponseTokens(unwrapped.res);
+ },
+};
diff --git a/lib/build/framework/h3/index.d.ts b/lib/build/framework/h3/index.d.ts
new file mode 100644
index 000000000..27774d1ef
--- /dev/null
+++ b/lib/build/framework/h3/index.d.ts
@@ -0,0 +1,15 @@
+// @ts-nocheck
+///
+export type { SessionRequest } from "./framework";
+export declare const middleware: () => (
+ req: import("http").IncomingMessage,
+ res: import("http").ServerResponse,
+ next: (err?: Error | undefined) => any
+) => Promise;
+export declare const errorHandler: () => (
+ event: import("h3").H3Event,
+ errorPlain: Error,
+ statusCode: number
+) => Promise;
+export declare const wrapRequest: (unwrapped: any) => import("..").BaseRequest;
+export declare const wrapResponse: (unwrapped: any) => import("..").BaseResponse;
diff --git a/lib/build/framework/h3/index.js b/lib/build/framework/h3/index.js
new file mode 100644
index 000000000..fa25d0f02
--- /dev/null
+++ b/lib/build/framework/h3/index.js
@@ -0,0 +1,7 @@
+"use strict";
+Object.defineProperty(exports, "__esModule", { value: true });
+const framework_1 = require("./framework");
+exports.middleware = framework_1.H3Wrapper.middlware;
+exports.errorHandler = framework_1.H3Wrapper.errorHandler;
+exports.wrapRequest = framework_1.H3Wrapper.wrapRequest;
+exports.wrapResponse = framework_1.H3Wrapper.wrapResponse;
diff --git a/lib/build/framework/index.d.ts b/lib/build/framework/index.d.ts
index 8fd8891ce..ae5393bdb 100644
--- a/lib/build/framework/index.d.ts
+++ b/lib/build/framework/index.d.ts
@@ -7,6 +7,7 @@ import * as hapiFramework from "./hapi";
import * as loopbackFramework from "./loopback";
import * as koaFramework from "./koa";
import * as awsLambdaFramework from "./awsLambda";
+import * as h3Framework from "./h3";
declare const _default: {
express: typeof expressFramework;
fastify: typeof fastifyFramework;
@@ -14,6 +15,7 @@ declare const _default: {
loopback: typeof loopbackFramework;
koa: typeof koaFramework;
awsLambda: typeof awsLambdaFramework;
+ h3: typeof h3Framework;
};
export default _default;
export declare let express: typeof expressFramework;
@@ -22,3 +24,4 @@ export declare let hapi: typeof hapiFramework;
export declare let loopback: typeof loopbackFramework;
export declare let koa: typeof koaFramework;
export declare let awsLambda: typeof awsLambdaFramework;
+export declare let h3: typeof h3Framework;
diff --git a/lib/build/framework/index.js b/lib/build/framework/index.js
index de9aa84e2..222e80db6 100644
--- a/lib/build/framework/index.js
+++ b/lib/build/framework/index.js
@@ -24,6 +24,7 @@ const hapiFramework = require("./hapi");
const loopbackFramework = require("./loopback");
const koaFramework = require("./koa");
const awsLambdaFramework = require("./awsLambda");
+const h3Framework = require("./h3");
exports.default = {
express: expressFramework,
fastify: fastifyFramework,
@@ -31,6 +32,7 @@ exports.default = {
loopback: loopbackFramework,
koa: koaFramework,
awsLambda: awsLambdaFramework,
+ h3: h3Framework,
};
exports.express = expressFramework;
exports.fastify = fastifyFramework;
@@ -38,3 +40,4 @@ exports.hapi = hapiFramework;
exports.loopback = loopbackFramework;
exports.koa = koaFramework;
exports.awsLambda = awsLambdaFramework;
+exports.h3 = h3Framework;
diff --git a/lib/build/framework/types.d.ts b/lib/build/framework/types.d.ts
index f685b5e0a..96e89c023 100644
--- a/lib/build/framework/types.d.ts
+++ b/lib/build/framework/types.d.ts
@@ -1,5 +1,5 @@
// @ts-nocheck
-export declare type TypeFramework = "express" | "fastify" | "hapi" | "loopback" | "koa" | "awsLambda";
+export declare type TypeFramework = "express" | "fastify" | "hapi" | "loopback" | "koa" | "awsLambda" | "h3";
import { BaseRequest, BaseResponse } from ".";
export declare let SchemaFramework: {
type: string;
diff --git a/lib/build/framework/types.js b/lib/build/framework/types.js
index e3e7797ed..a635dfe9f 100644
--- a/lib/build/framework/types.js
+++ b/lib/build/framework/types.js
@@ -2,5 +2,5 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaFramework = {
type: "string",
- enum: ["express", "fastify", "hapi", "loopback", "koa", "awsLambda"],
+ enum: ["express", "fastify", "hapi", "loopback", "koa", "awsLambda", "h3"],
};
diff --git a/lib/build/framework/utils.d.ts b/lib/build/framework/utils.d.ts
index 1fafc184f..987586d65 100644
--- a/lib/build/framework/utils.d.ts
+++ b/lib/build/framework/utils.d.ts
@@ -24,6 +24,8 @@ export declare function setHeaderForExpressLikeResponse(
value: string,
allowDuplicateKey: boolean
): void;
+export declare function useRawBody(req: IncomingMessage): Promise;
+export declare function useBody(req: IncomingMessage): Promise;
/**
*
* @param res
diff --git a/lib/build/framework/utils.js b/lib/build/framework/utils.js
index 86fcbc54b..60e674cd7 100644
--- a/lib/build/framework/utils.js
+++ b/lib/build/framework/utils.js
@@ -50,6 +50,7 @@ const body_parser_1 = require("body-parser");
const http_1 = require("http");
const error_1 = require("../error");
const constants_1 = require("./constants");
+const destr_1 = require("destr");
function getCookieValueFromHeaders(headers, key) {
if (headers === undefined || headers === null) {
return undefined;
@@ -237,6 +238,41 @@ function setHeaderForExpressLikeResponse(res, key, value, allowDuplicateKey) {
}
}
exports.setHeaderForExpressLikeResponse = setHeaderForExpressLikeResponse;
+function useRawBody(req) {
+ const RawBodySymbol = Symbol("h3RawBody");
+ if (RawBodySymbol in this.request) {
+ const promise = Promise.resolve(req[RawBodySymbol]);
+ return promise.then((buff) => buff.toString("utf-8"));
+ }
+ if ("body" in req) {
+ return Promise.resolve(this.request.body);
+ }
+ const promise = (req[RawBodySymbol] = new Promise((resolve, reject) => {
+ const bodyData = [];
+ req.on("error", (err) => reject(err))
+ .on("data", (chunk) => {
+ bodyData.push(chunk);
+ })
+ .on("end", () => {
+ resolve(Buffer.concat(bodyData));
+ });
+ }));
+ return promise.then((buff) => buff.toString("utf-8"));
+}
+exports.useRawBody = useRawBody;
+function useBody(req) {
+ return __awaiter(this, void 0, void 0, function* () {
+ const ParsedBodySymbol = Symbol("h3RawBody");
+ if (ParsedBodySymbol in req) {
+ return req[ParsedBodySymbol];
+ }
+ const body = yield useRawBody(req);
+ const json = destr_1.default(body);
+ req[ParsedBodySymbol] = json;
+ return json;
+ });
+}
+exports.useBody = useBody;
/**
*
* @param res
diff --git a/lib/build/recipe/session/framework/h3.d.ts b/lib/build/recipe/session/framework/h3.d.ts
new file mode 100644
index 000000000..d5d122039
--- /dev/null
+++ b/lib/build/recipe/session/framework/h3.d.ts
@@ -0,0 +1,8 @@
+// @ts-nocheck
+///
+import type { VerifySessionOptions } from "../types";
+import type { SessionRequest } from "../../../framework/h3";
+import { ServerResponse } from "http";
+export declare function verifySession(
+ options?: VerifySessionOptions
+): (req: SessionRequest, res: ServerResponse, next: (err?: Error | undefined) => any) => Promise;
diff --git a/lib/build/recipe/session/framework/h3.js b/lib/build/recipe/session/framework/h3.js
new file mode 100644
index 000000000..e5a0d2968
--- /dev/null
+++ b/lib/build/recipe/session/framework/h3.js
@@ -0,0 +1,56 @@
+"use strict";
+var __awaiter =
+ (this && this.__awaiter) ||
+ function (thisArg, _arguments, P, generator) {
+ function adopt(value) {
+ return value instanceof P
+ ? value
+ : new P(function (resolve) {
+ resolve(value);
+ });
+ }
+ return new (P || (P = Promise))(function (resolve, reject) {
+ function fulfilled(value) {
+ try {
+ step(generator.next(value));
+ } catch (e) {
+ reject(e);
+ }
+ }
+ function rejected(value) {
+ try {
+ step(generator["throw"](value));
+ } catch (e) {
+ reject(e);
+ }
+ }
+ function step(result) {
+ result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
+ }
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
+ });
+ };
+Object.defineProperty(exports, "__esModule", { value: true });
+const recipe_1 = require("../recipe");
+const framework_1 = require("../../../framework/h3/framework");
+const supertokens_1 = require("../../../supertokens");
+function verifySession(options) {
+ return (req, res, next) =>
+ __awaiter(this, void 0, void 0, function* () {
+ const request = new framework_1.H3Request(req);
+ const response = new framework_1.H3ResponseTokens(res);
+ try {
+ const sessionRecipe = recipe_1.default.getInstanceOrThrowError();
+ req.session = yield sessionRecipe.verifySession(options, request, response);
+ next();
+ } catch (err) {
+ try {
+ const supertokens = supertokens_1.default.getInstanceOrThrowError();
+ yield supertokens.errorHandler(err, request, response);
+ } catch (err) {
+ next(err);
+ }
+ }
+ });
+}
+exports.verifySession = verifySession;
diff --git a/lib/build/recipe/session/framework/index.d.ts b/lib/build/recipe/session/framework/index.d.ts
index dfba9d1f7..79cfd213e 100644
--- a/lib/build/recipe/session/framework/index.d.ts
+++ b/lib/build/recipe/session/framework/index.d.ts
@@ -5,6 +5,7 @@ import * as hapiFramework from "./hapi";
import * as loopbackFramework from "./loopback";
import * as koaFramework from "./koa";
import * as awsLambdaFramework from "./awsLambda";
+import * as h3Framework from "./h3";
declare const _default: {
express: typeof expressFramework;
fastify: typeof fastifyFramework;
@@ -12,6 +13,7 @@ declare const _default: {
loopback: typeof loopbackFramework;
koa: typeof koaFramework;
awsLambda: typeof awsLambdaFramework;
+ h3: typeof h3Framework;
};
export default _default;
export declare let express: typeof expressFramework;
@@ -20,3 +22,4 @@ export declare let hapi: typeof hapiFramework;
export declare let loopback: typeof loopbackFramework;
export declare let koa: typeof koaFramework;
export declare let awsLambda: typeof awsLambdaFramework;
+export declare let h3: typeof h3Framework;
diff --git a/lib/build/recipe/session/framework/index.js b/lib/build/recipe/session/framework/index.js
index 0f0ccf5cd..6bee76f22 100644
--- a/lib/build/recipe/session/framework/index.js
+++ b/lib/build/recipe/session/framework/index.js
@@ -20,6 +20,7 @@ const hapiFramework = require("./hapi");
const loopbackFramework = require("./loopback");
const koaFramework = require("./koa");
const awsLambdaFramework = require("./awsLambda");
+const h3Framework = require("./h3");
exports.default = {
express: expressFramework,
fastify: fastifyFramework,
@@ -27,6 +28,7 @@ exports.default = {
loopback: loopbackFramework,
koa: koaFramework,
awsLambda: awsLambdaFramework,
+ h3: h3Framework,
};
exports.express = expressFramework;
exports.fastify = fastifyFramework;
@@ -34,3 +36,4 @@ exports.hapi = hapiFramework;
exports.loopback = loopbackFramework;
exports.koa = koaFramework;
exports.awsLambda = awsLambdaFramework;
+exports.h3 = h3Framework;
diff --git a/lib/ts/framework/h3/framework.ts b/lib/ts/framework/h3/framework.ts
new file mode 100644
index 000000000..e6df8218b
--- /dev/null
+++ b/lib/ts/framework/h3/framework.ts
@@ -0,0 +1,172 @@
+import { createError, H3Event, sendError } from "h3";
+import type { IncomingMessage, ServerResponse } from "http";
+import { SessionContainerInterface } from "../../recipe/session/types";
+import SuperTokens from "../../supertokens";
+import { normaliseHttpMethod } from "../../utils";
+import { BaseRequest } from "../request";
+import { BaseResponse } from "../response";
+import { Framework } from "../types";
+import {
+ getCookieValueFromIncomingMessage,
+ getHeaderValueFromIncomingMessage,
+ useBody,
+ useRawBody,
+ setCookieForServerResponse,
+} from "../utils";
+
+export class H3Request extends BaseRequest {
+ private request: IncomingMessage;
+ constructor(request: IncomingMessage) {
+ super();
+ this.original = request;
+ this.request = request;
+ }
+ getCookieValue = (key: string) => {
+ return getCookieValueFromIncomingMessage(this.request, key);
+ };
+ getFormData = async (): Promise => {
+ return useRawBody(this.request);
+ };
+ getMethod = () => {
+ return normaliseHttpMethod(this.request.method!);
+ };
+ getHeaderValue = (key: string) => {
+ return getHeaderValueFromIncomingMessage(this.request, key);
+ };
+ getOriginalURL = () => {
+ return this.request.url!;
+ };
+ getKeyValueFromQuery = (key: string) => {
+ let path = this.request.url || "/";
+ const queryIndex = path.lastIndexOf("?");
+ if (queryIndex > -1) {
+ const queryArray = path.substring(queryIndex + 1, path.length).split("&");
+ const index = queryArray.findIndex((el) => el.includes(key));
+ if (index === -1) return undefined;
+ const value = queryArray[index].split("=")[1];
+ if (value === undefined || typeof value !== "string") {
+ return undefined;
+ }
+ return value;
+ } else {
+ return undefined;
+ }
+ };
+ getJSONBody = async () => {
+ return await useBody(this.request);
+ };
+}
+
+export class H3ResponseTokens extends BaseResponse {
+ private response: ServerResponse;
+ private statusCode: number;
+ constructor(response: ServerResponse) {
+ super();
+ this.original = response;
+ this.response = response;
+ this.statusCode = 200;
+ }
+
+ sendHTMLResponse = (html: string) => {
+ if (this.response.writable) {
+ this.response.setHeader("Content-Type", "text/html");
+ this.response.statusCode = this.statusCode;
+ this.response.end(html);
+ }
+ };
+ setHeader = (key: string, value: string, allowDuplicateKey: boolean) => {
+ try {
+ const allheaders = this.response.getHeaders();
+ let existingValue = allheaders[key];
+
+ // we have the this.response.header for compatibility with nextJS
+ if (existingValue === undefined) {
+ this.response.setHeader(key, value);
+ } else if (allowDuplicateKey) {
+ this.response.setHeader(key, existingValue + ", " + value);
+ } else {
+ // we overwrite the current one with the new one
+ this.response.setHeader(key, value);
+ }
+ } catch (err) {
+ throw new Error("Error while setting header with key: " + key + " and value: " + value);
+ }
+ };
+ setCookie = (
+ key: string,
+ value: string,
+ domain: string | undefined,
+ secure: boolean,
+ httpOnly: boolean,
+ expires: number,
+ path: string,
+ sameSite: "strict" | "lax" | "none"
+ ) => {
+ setCookieForServerResponse(this.response, key, value, domain, secure, httpOnly, expires, path, sameSite);
+ };
+ setStatusCode = (statusCode: number) => {
+ if (this.response.writable) {
+ this.statusCode = statusCode;
+ }
+ };
+ sendJSONResponse = (content: any) => {
+ if (this.response.writable) {
+ content = JSON.stringify(content);
+ this.response.setHeader("Content-Type", "application/json");
+ this.response.statusCode = this.statusCode;
+ this.response.end(content, "utf-8");
+ }
+ };
+}
+export interface SessionRequest extends IncomingMessage {
+ session?: SessionContainerInterface;
+}
+
+export const middlware = () => {
+ return async (req: IncomingMessage, res: ServerResponse, next: (err?: Error) => any) => {
+ let supertokens;
+ const request = new H3Request(req);
+ const response = new H3ResponseTokens(res);
+ try {
+ supertokens = SuperTokens.getInstanceOrThrowError();
+ const result = await supertokens.middleware(request, response);
+ if (!result) {
+ return next();
+ }
+ } catch (err) {
+ if (supertokens) {
+ try {
+ await supertokens.errorHandler(err, request, response);
+ } catch {
+ next(err);
+ }
+ } else {
+ next(err);
+ }
+ }
+ };
+};
+
+export const errorHandler = () => {
+ return async (event: H3Event, errorPlain: Error, statusCode: number) => {
+ const error = createError(errorPlain);
+ error.statusCode = statusCode;
+ sendError(event, error);
+ };
+};
+
+export interface H3Framework extends Framework {
+ middlware: () => (req: IncomingMessage, res: ServerResponse, next: (err?: Error) => any) => Promise;
+ errorHandler: () => (event: H3Event, errorPlain: Error, statusCode: number) => Promise;
+}
+
+export const H3Wrapper: H3Framework = {
+ middlware,
+ errorHandler,
+ wrapRequest: (unwrapped) => {
+ return new H3Request(unwrapped.req);
+ },
+ wrapResponse: (unwrapped) => {
+ return new H3ResponseTokens(unwrapped.res);
+ },
+};
diff --git a/lib/ts/framework/h3/index.ts b/lib/ts/framework/h3/index.ts
new file mode 100644
index 000000000..5dcc5a938
--- /dev/null
+++ b/lib/ts/framework/h3/index.ts
@@ -0,0 +1,7 @@
+import { H3Wrapper } from "./framework";
+export type { SessionRequest } from "./framework";
+
+export const middleware = H3Wrapper.middlware;
+export const errorHandler = H3Wrapper.errorHandler;
+export const wrapRequest = H3Wrapper.wrapRequest;
+export const wrapResponse = H3Wrapper.wrapResponse;
diff --git a/lib/ts/framework/index.ts b/lib/ts/framework/index.ts
index 3d38c264a..e83df0391 100644
--- a/lib/ts/framework/index.ts
+++ b/lib/ts/framework/index.ts
@@ -21,6 +21,7 @@ import * as hapiFramework from "./hapi";
import * as loopbackFramework from "./loopback";
import * as koaFramework from "./koa";
import * as awsLambdaFramework from "./awsLambda";
+import * as h3Framework from "./h3";
export default {
express: expressFramework,
@@ -29,6 +30,7 @@ export default {
loopback: loopbackFramework,
koa: koaFramework,
awsLambda: awsLambdaFramework,
+ h3: h3Framework,
};
export let express = expressFramework;
@@ -37,3 +39,4 @@ export let hapi = hapiFramework;
export let loopback = loopbackFramework;
export let koa = koaFramework;
export let awsLambda = awsLambdaFramework;
+export let h3 = h3Framework;
diff --git a/lib/ts/framework/types.ts b/lib/ts/framework/types.ts
index 5707c65d5..505adc5e4 100644
--- a/lib/ts/framework/types.ts
+++ b/lib/ts/framework/types.ts
@@ -12,12 +12,12 @@
* License for the specific language governing permissions and limitations
* under the License.
*/
-export type TypeFramework = "express" | "fastify" | "hapi" | "loopback" | "koa" | "awsLambda";
+export type TypeFramework = "express" | "fastify" | "hapi" | "loopback" | "koa" | "awsLambda" | "h3";
import { BaseRequest, BaseResponse } from ".";
export let SchemaFramework = {
type: "string",
- enum: ["express", "fastify", "hapi", "loopback", "koa", "awsLambda"],
+ enum: ["express", "fastify", "hapi", "loopback", "koa", "awsLambda", "h3"],
};
export interface Framework {
diff --git a/lib/ts/framework/utils.ts b/lib/ts/framework/utils.ts
index c7ad29993..39e7eaf07 100644
--- a/lib/ts/framework/utils.ts
+++ b/lib/ts/framework/utils.ts
@@ -22,6 +22,7 @@ import STError from "../error";
import type { HTTPMethod } from "../types";
import { NextApiRequest } from "next";
import { COOKIE_HEADER } from "./constants";
+import destr from "destr";
export function getCookieValueFromHeaders(headers: any, key: string): string | undefined {
if (headers === undefined || headers === null) {
@@ -223,6 +224,42 @@ export function setHeaderForExpressLikeResponse(res: Response, key: string, valu
}
}
+export function useRawBody(req: IncomingMessage): Promise {
+ const RawBodySymbol = Symbol("h3RawBody");
+ if (RawBodySymbol in this.request) {
+ const promise = Promise.resolve((req as any)[RawBodySymbol]);
+ return promise.then((buff) => buff.toString("utf-8"));
+ }
+ if ("body" in req) {
+ return Promise.resolve((this.request as any).body);
+ }
+
+ const promise = ((req as any)[RawBodySymbol] = new Promise((resolve, reject) => {
+ const bodyData: any[] = [];
+ req.on("error", (err) => reject(err))
+ .on("data", (chunk) => {
+ bodyData.push(chunk);
+ })
+ .on("end", () => {
+ resolve(Buffer.concat(bodyData));
+ });
+ }));
+
+ return promise.then((buff) => buff.toString("utf-8"));
+}
+
+export async function useBody(req: IncomingMessage): Promise {
+ const ParsedBodySymbol = Symbol("h3RawBody");
+ if (ParsedBodySymbol in req) {
+ return (req as any)[ParsedBodySymbol];
+ }
+
+ const body = (await useRawBody(req)) as string;
+
+ const json = destr(body);
+ (req as any)[ParsedBodySymbol] = json;
+ return json;
+}
/**
*
* @param res
diff --git a/lib/ts/recipe/session/framework/h3.ts b/lib/ts/recipe/session/framework/h3.ts
new file mode 100644
index 000000000..9431f0015
--- /dev/null
+++ b/lib/ts/recipe/session/framework/h3.ts
@@ -0,0 +1,25 @@
+import Session from "../recipe";
+import type { VerifySessionOptions } from "../types";
+import { H3Request, H3ResponseTokens } from "../../../framework/h3/framework";
+import type { SessionRequest } from "../../../framework/h3";
+import SuperTokens from "../../../supertokens";
+import { ServerResponse } from "http";
+
+export function verifySession(options?: VerifySessionOptions) {
+ return async (req: SessionRequest, res: ServerResponse, next: (err?: Error) => any) => {
+ const request = new H3Request(req);
+ const response = new H3ResponseTokens(res);
+ try {
+ const sessionRecipe = Session.getInstanceOrThrowError();
+ req.session = await sessionRecipe.verifySession(options, request, response);
+ next();
+ } catch (err) {
+ try {
+ const supertokens = SuperTokens.getInstanceOrThrowError();
+ await supertokens.errorHandler(err, request, response);
+ } catch (err) {
+ next(err);
+ }
+ }
+ };
+}
diff --git a/lib/ts/recipe/session/framework/index.ts b/lib/ts/recipe/session/framework/index.ts
index c984f7879..5cd93c271 100644
--- a/lib/ts/recipe/session/framework/index.ts
+++ b/lib/ts/recipe/session/framework/index.ts
@@ -18,6 +18,7 @@ import * as hapiFramework from "./hapi";
import * as loopbackFramework from "./loopback";
import * as koaFramework from "./koa";
import * as awsLambdaFramework from "./awsLambda";
+import * as h3Framework from "./h3";
export default {
express: expressFramework,
@@ -26,6 +27,7 @@ export default {
loopback: loopbackFramework,
koa: koaFramework,
awsLambda: awsLambdaFramework,
+ h3: h3Framework,
};
export let express = expressFramework;
@@ -34,3 +36,4 @@ export let hapi = hapiFramework;
export let loopback = loopbackFramework;
export let koa = koaFramework;
export let awsLambda = awsLambdaFramework;
+export let h3 = h3Framework;
diff --git a/package-lock.json b/package-lock.json
index 13924b03d..ea5cde0ee 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"co-body": "6.1.0",
"cookie": "0.4.0",
"debug": "^4.3.3",
+ "destr": "^1.1.1",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^2.0.5",
"libphonenumber-js": "^1.9.44",
@@ -45,6 +46,7 @@
"express": "4.17.1",
"fastify": "3.18.1",
"glob": "7.1.7",
+ "h3": "^0.7.6",
"koa": "^2.13.3",
"lambda-tester": "^4.0.1",
"loopback-datasource-juggler": "^4.26.0",
@@ -2878,6 +2880,11 @@
"node": ">= 0.6"
}
},
+ "node_modules/cookie-es": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-0.5.0.tgz",
+ "integrity": "sha512-RyZrFi6PNpBFbIaQjXDlFIhFVqV42QeKSZX1yQIl6ihImq6vcHNGMtqQ/QzY3RMPuYSkvsRwtnt5M9NeYxKt0g=="
+ },
"node_modules/cookie-parser": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
@@ -3202,6 +3209,11 @@
"minimalistic-assert": "^1.0.0"
}
},
+ "node_modules/destr": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/destr/-/destr-1.1.1.tgz",
+ "integrity": "sha512-QqkneF8LrYmwATMdnuD2MLI3GHQIcBnG6qFC2q9bSH430VTCDAVjcspPmUaKhPGtAtPAftIUFqY1obQYQuwmbg=="
+ },
"node_modules/destroy": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.1.0.tgz",
@@ -4102,6 +4114,17 @@
"node": ">=4.x"
}
},
+ "node_modules/h3": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-0.7.6.tgz",
+ "integrity": "sha512-OoxDWBBpGNAStSVCOQagN+3EKRbCSgwQKFIeu4p4XvsLvd4L9KaQV6GDY5H8ZDYHsWfnsx4KTa2eFwSw530jnQ==",
+ "dependencies": {
+ "cookie-es": "^0.5.0",
+ "destr": "^1.1.1",
+ "radix3": "^0.1.1",
+ "ufo": "^0.8.3"
+ }
+ },
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -7199,6 +7222,11 @@
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
"dev": true
},
+ "node_modules/radix3": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/radix3/-/radix3-0.1.1.tgz",
+ "integrity": "sha512-9Np01fn+penHvC05A9EkRpyObPMS0ht3t1UP6KlnQPCfTNzArmEZW/+t2SLsDtPcZyXPDbYCGWA8dSQqWaVwQQ=="
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -8473,6 +8501,11 @@
"node": ">=4.2.0"
}
},
+ "node_modules/ufo": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-0.8.3.tgz",
+ "integrity": "sha512-AIkk06G21y/P+NCatfU+1qldCmI0XCszZLn8AkuKotffF3eqCvlce0KuwM7ZemLE/my0GSYADOAeM5zDYWMB+A=="
+ },
"node_modules/unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
@@ -11754,6 +11787,11 @@
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz",
"integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg=="
},
+ "cookie-es": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-0.5.0.tgz",
+ "integrity": "sha512-RyZrFi6PNpBFbIaQjXDlFIhFVqV42QeKSZX1yQIl6ihImq6vcHNGMtqQ/QzY3RMPuYSkvsRwtnt5M9NeYxKt0g=="
+ },
"cookie-parser": {
"version": "1.4.6",
"resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.6.tgz",
@@ -12016,6 +12054,11 @@
"minimalistic-assert": "^1.0.0"
}
},
+ "destr": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/destr/-/destr-1.1.1.tgz",
+ "integrity": "sha512-QqkneF8LrYmwATMdnuD2MLI3GHQIcBnG6qFC2q9bSH430VTCDAVjcspPmUaKhPGtAtPAftIUFqY1obQYQuwmbg=="
+ },
"destroy": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.1.0.tgz",
@@ -12732,6 +12775,17 @@
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
+ "h3": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/h3/-/h3-0.7.6.tgz",
+ "integrity": "sha512-OoxDWBBpGNAStSVCOQagN+3EKRbCSgwQKFIeu4p4XvsLvd4L9KaQV6GDY5H8ZDYHsWfnsx4KTa2eFwSw530jnQ==",
+ "requires": {
+ "cookie-es": "^0.5.0",
+ "destr": "^1.1.1",
+ "radix3": "^0.1.1",
+ "ufo": "^0.8.3"
+ }
+ },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -15193,6 +15247,11 @@
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
"dev": true
},
+ "radix3": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/radix3/-/radix3-0.1.1.tgz",
+ "integrity": "sha512-9Np01fn+penHvC05A9EkRpyObPMS0ht3t1UP6KlnQPCfTNzArmEZW/+t2SLsDtPcZyXPDbYCGWA8dSQqWaVwQQ=="
+ },
"randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -16253,6 +16312,11 @@
"integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
"dev": true
},
+ "ufo": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-0.8.3.tgz",
+ "integrity": "sha512-AIkk06G21y/P+NCatfU+1qldCmI0XCszZLn8AkuKotffF3eqCvlce0KuwM7ZemLE/my0GSYADOAeM5zDYWMB+A=="
+ },
"unbox-primitive": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.1.tgz",
diff --git a/package.json b/package.json
index ada63e1ff..a712de14e 100644
--- a/package.json
+++ b/package.json
@@ -39,6 +39,7 @@
"co-body": "6.1.0",
"cookie": "0.4.0",
"debug": "^4.3.3",
+ "destr": "^1.1.1",
"jsonwebtoken": "^8.5.1",
"jwks-rsa": "^2.0.5",
"libphonenumber-js": "^1.9.44",
@@ -49,6 +50,7 @@
"verify-apple-id-token": "^2.1.0"
},
"devDependencies": {
+ "h3": "^0.7.6",
"@hapi/hapi": "^20.2.0",
"@koa/router": "^10.1.1",
"@loopback/core": "2.16.2",
diff --git a/recipe/session/framework/h3/index.d.ts b/recipe/session/framework/h3/index.d.ts
new file mode 100644
index 000000000..b76888d1e
--- /dev/null
+++ b/recipe/session/framework/h3/index.d.ts
@@ -0,0 +1,3 @@
+export * from "../../../../lib/build/recipe/session/framework/h3";
+import * as _default from "../../../../lib/build/recipe/session/framework/h3";
+export default _default;
diff --git a/recipe/session/framework/h3/index.js b/recipe/session/framework/h3/index.js
new file mode 100644
index 000000000..06fb4c8b7
--- /dev/null
+++ b/recipe/session/framework/h3/index.js
@@ -0,0 +1,6 @@
+"use strict";
+function __export(m) {
+ for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
+}
+exports.__esModule = true;
+__export(require("../../../../lib/build/recipe/session/framework/h3"));