better-ship 0.4.0 → 0.5.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.
- package/dist/{application-BpSwCKJ5.js → application-DEerezCl.js} +2 -2
- package/dist/{application-BpSwCKJ5.js.map → application-DEerezCl.js.map} +1 -1
- package/dist/application.d.ts +1 -1
- package/dist/application.js +1 -1
- package/dist/better-auth/apple-client-secret.d.ts +21 -0
- package/dist/better-auth/apple-client-secret.d.ts.map +1 -0
- package/dist/better-auth/apple-client-secret.js +44 -0
- package/dist/better-auth/apple-client-secret.js.map +1 -0
- package/dist/better-auth/auth-client.d.ts +16 -0
- package/dist/better-auth/auth-client.d.ts.map +1 -0
- package/dist/better-auth/auth-client.js +24 -0
- package/dist/better-auth/auth-client.js.map +1 -0
- package/dist/better-auth/rate-limit.d.ts +34 -0
- package/dist/better-auth/rate-limit.d.ts.map +1 -0
- package/dist/better-auth/rate-limit.js +59 -0
- package/dist/better-auth/rate-limit.js.map +1 -0
- package/dist/better-auth/secondary-storage.d.ts +11 -0
- package/dist/better-auth/secondary-storage.d.ts.map +1 -0
- package/dist/better-auth/secondary-storage.js +18 -0
- package/dist/better-auth/secondary-storage.js.map +1 -0
- package/dist/better-auth/stripe.d.ts +9 -0
- package/dist/better-auth/stripe.d.ts.map +1 -0
- package/dist/better-auth/stripe.js +17 -0
- package/dist/better-auth/stripe.js.map +1 -0
- package/dist/better-auth.d.ts +100 -0
- package/dist/better-auth.d.ts.map +1 -0
- package/dist/better-auth.js +112 -0
- package/dist/better-auth.js.map +1 -0
- package/dist/cloudflare/auth-secondary-storage.d.ts +36 -0
- package/dist/cloudflare/auth-secondary-storage.d.ts.map +1 -0
- package/dist/cloudflare/auth-secondary-storage.js +98 -0
- package/dist/cloudflare/auth-secondary-storage.js.map +1 -0
- package/dist/cloudflare.d.ts +3 -3
- package/dist/cloudflare.js +3 -3
- package/dist/core-CnwWj4MY.js +620 -0
- package/dist/core-CnwWj4MY.js.map +1 -0
- package/dist/core.d.ts +2 -2
- package/dist/core.js +2 -2
- package/dist/index-B9Y1hVh6.d.ts +460 -0
- package/dist/index-B9Y1hVh6.d.ts.map +1 -0
- package/dist/{index-CkaFqbbV.d.ts → index-Bdyh3_xC.d.ts} +2 -2
- package/dist/{index-CkaFqbbV.d.ts.map → index-Bdyh3_xC.d.ts.map} +1 -1
- package/dist/postgres.d.ts +3 -3
- package/dist/postgres.js +3 -3
- package/dist/tanstack.d.ts +81 -1
- package/dist/tanstack.d.ts.map +1 -0
- package/dist/tanstack.js +214 -0
- package/dist/tanstack.js.map +1 -0
- package/dist/{unit-of-work-context-CILhzKWb.js → unit-of-work-context-C7vz5OYS.js} +2 -2
- package/dist/{unit-of-work-context-CILhzKWb.js.map → unit-of-work-context-C7vz5OYS.js.map} +1 -1
- package/dist/{unit-of-work-context-DOkIB7HK.d.ts → unit-of-work-context-ml9L9cyY.d.ts} +2 -2
- package/dist/{unit-of-work-context-DOkIB7HK.d.ts.map → unit-of-work-context-ml9L9cyY.d.ts.map} +1 -1
- package/package.json +57 -12
- package/src/core/authentication.error.ts +11 -0
- package/src/core/error-table.ts +188 -0
- package/src/core/index.ts +31 -0
- package/src/core/internal.errors.ts +21 -0
- package/src/core/logger.ts +40 -19
- package/src/core/not-authorized.error.ts +11 -0
- package/src/core/problem-details.ts +57 -0
- package/src/core/rate-limit.error.ts +12 -0
- package/src/core/request.errors.ts +86 -0
- package/src/core/stale-session.error.ts +11 -0
- package/src/infrastructure/better-auth/apple-client-secret.ts +54 -0
- package/src/infrastructure/better-auth/auth-client.ts +26 -0
- package/src/infrastructure/better-auth/options.ts +232 -0
- package/src/infrastructure/better-auth/rate-limit.ts +69 -0
- package/src/infrastructure/better-auth/secondary-storage.ts +21 -0
- package/src/infrastructure/better-auth/stripe.ts +11 -0
- package/src/infrastructure/cloudflare/auth-secondary-storage.ts +113 -0
- package/src/infrastructure/tanstack/auth.middleware.ts +89 -0
- package/src/infrastructure/tanstack/error.middleware.ts +81 -0
- package/src/infrastructure/tanstack/http-error-response.ts +30 -0
- package/src/infrastructure/tanstack/index.ts +12 -0
- package/src/infrastructure/tanstack/resolve-failure.ts +24 -0
- package/src/infrastructure/tanstack/session-cookies.ts +36 -0
- package/src/infrastructure/tanstack/to-error-payload.ts +32 -0
- package/dist/core-D3G-2gIx.js +0 -298
- package/dist/core-D3G-2gIx.js.map +0 -1
- package/dist/index-CT1yZd4D.d.ts +0 -212
- package/dist/index-CT1yZd4D.d.ts.map +0 -1
package/dist/tanstack.d.ts
CHANGED
|
@@ -1 +1,81 @@
|
|
|
1
|
-
|
|
1
|
+
import { A as ErrorTable } from "./index-B9Y1hVh6.js";
|
|
2
|
+
import { Register, RequestServerOptions } from "@tanstack/react-start";
|
|
3
|
+
import { AnyRedirect } from "@tanstack/react-router";
|
|
4
|
+
import { BetterAuthCookies } from "better-auth";
|
|
5
|
+
//#region src/infrastructure/tanstack/auth.middleware.d.ts
|
|
6
|
+
/** The fields middleware reads; generic callers retain every additional session and user field. */
|
|
7
|
+
type AuthSession = {
|
|
8
|
+
readonly user: {
|
|
9
|
+
readonly id: string;
|
|
10
|
+
};
|
|
11
|
+
readonly session: {
|
|
12
|
+
readonly id: string;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
/** Authenticated context retains the application's complete user and session types. */
|
|
16
|
+
type RequiredAuthContext<Session extends AuthSession> = {
|
|
17
|
+
readonly user: Session['user'];
|
|
18
|
+
readonly session: Session['session'];
|
|
19
|
+
};
|
|
20
|
+
/** Optional authentication supplies both fields together or neither field. */
|
|
21
|
+
type AuthContext<Session extends AuthSession> = RequiredAuthContext<Session> | {
|
|
22
|
+
readonly user: undefined;
|
|
23
|
+
readonly session: undefined;
|
|
24
|
+
};
|
|
25
|
+
/** Select the current request's Better Auth instance from TanStack's registered context. */
|
|
26
|
+
type AuthMiddlewareOptions<Session extends AuthSession> = {
|
|
27
|
+
readonly getAuth: (context: RequestServerOptions<Register, undefined>['context']) => {
|
|
28
|
+
readonly api: {
|
|
29
|
+
readonly getSession: (options: {
|
|
30
|
+
headers: Headers;
|
|
31
|
+
}) => Promise<Session | null>;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Preserve Porte's function and request middleware contracts with application-owned services.
|
|
37
|
+
* Optional lookup failures log once and continue as guests. Required lookup failures propagate.
|
|
38
|
+
*/
|
|
39
|
+
export declare function createAuthMiddleware<Session extends AuthSession>(options: AuthMiddlewareOptions<Session>): {
|
|
40
|
+
extractAuth: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, AuthContext<Session>, undefined, undefined, undefined>;
|
|
41
|
+
requireAuth: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, RequiredAuthContext<Session>, undefined, undefined, undefined>;
|
|
42
|
+
requireAuthRequest: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, RequiredAuthContext<Session>>;
|
|
43
|
+
};
|
|
44
|
+
//#endregion
|
|
45
|
+
//#region src/infrastructure/tanstack/error.middleware.d.ts
|
|
46
|
+
type RequestContext = RequestServerOptions<Register, undefined>['context'];
|
|
47
|
+
/** The part of a Better Auth instance the boundary reads: its computed cookie definitions. */
|
|
48
|
+
type AuthCookieSource = {
|
|
49
|
+
readonly $context: Promise<{
|
|
50
|
+
readonly authCookies: BetterAuthCookies;
|
|
51
|
+
}>;
|
|
52
|
+
};
|
|
53
|
+
/** The application's error table, its stale-session redirect, and its Better Auth instance. */
|
|
54
|
+
type ErrorMiddlewareOptions<Entries> = {
|
|
55
|
+
readonly errors: ErrorTable<Entries>;
|
|
56
|
+
/** Build the redirect a server function throws for a stale session. */
|
|
57
|
+
readonly signIn: () => AnyRedirect;
|
|
58
|
+
/** Select the request's Better Auth instance; its cookie definitions say what to clear. */
|
|
59
|
+
readonly getAuth: (context: RequestContext) => AuthCookieSource;
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Build both transport boundaries from one error table.
|
|
63
|
+
* Register `functionErrorMiddleware` globally. Put `routeErrorMiddleware` first in an API route's `server.middleware`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function createErrorMiddleware<Entries>(options: ErrorMiddlewareOptions<Entries>): {
|
|
66
|
+
functionErrorMiddleware: import("@tanstack/react-start").FunctionMiddlewareAfterServer<{}, unknown, undefined, undefined, undefined, undefined, undefined>;
|
|
67
|
+
routeErrorMiddleware: import("@tanstack/react-start").RequestMiddlewareAfterServer<{}, undefined, undefined>;
|
|
68
|
+
toErrorPayload: (cause: unknown) => { [Tag in keyof Entries & string]: Entries[Tag] extends (infer T) ? T extends Entries[Tag] ? T extends {
|
|
69
|
+
readonly fields: infer Fields extends import("zod").ZodType;
|
|
70
|
+
} ? {
|
|
71
|
+
readonly _tag: Tag;
|
|
72
|
+
readonly message: string;
|
|
73
|
+
} & import("zod").infer<Fields> : {
|
|
74
|
+
readonly _tag: Tag;
|
|
75
|
+
readonly message: string;
|
|
76
|
+
} : never : never; }[keyof Entries & string];
|
|
77
|
+
toHttpErrorResponse: (cause: unknown) => Response;
|
|
78
|
+
};
|
|
79
|
+
//#endregion
|
|
80
|
+
export type { AuthContext, AuthCookieSource, AuthMiddlewareOptions, AuthSession, ErrorMiddlewareOptions, RequiredAuthContext };
|
|
81
|
+
//# sourceMappingURL=tanstack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tanstack.d.ts","names":[],"sources":["../src/infrastructure/tanstack/auth.middleware.ts","../src/infrastructure/tanstack/error.middleware.ts"],"mappings":";;;;;;KASY;WACD;aAAiB;;WACjB;aAAoB;;;;KAInB,oBAAoB,gBAAgB;WACrC,MAAM;WACN,SAAS;;;KAIR,YAAY,gBAAgB,eACpC,oBAAoB;WAET;WACA;;;KAIH,sBAAsB,gBAAgB;WACvC,UAAU,SAAS,qBAAqB;aACtC;eACE,aAAa;QAAW,SAAS;YAAc,QAAQ;;;;;;;;wBAStD,qBAAqB,gBAAgB,aACnD,SAAS,sBAAsB;;;;;;;KChC5B,iBAAiB,qBAAqB;;KAG/B;WACD,UAAU;aAAmB,aAAa;;;;KAIzC,uBAAuB;WACxB,QAAQ,WAAW;;WAEnB,cAAc;;WAEd,UAAU,SAAS,mBAAmB;;;;;;wBAOjC,sBAAsB,SAAS,SAAS,uBAAuB"}
|
package/dist/tanstack.js
CHANGED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { A as AppError, C as ValidationError, D as StaleSessionError, a as createLogger, k as AuthenticationError } from "./core-CnwWj4MY.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { createMiddleware } from "@tanstack/react-start";
|
|
4
|
+
import { deleteCookie, getRequestHeaders } from "@tanstack/react-start/server";
|
|
5
|
+
import { getSessionCookie, parseCookies } from "better-auth/cookies";
|
|
6
|
+
import { isNotFound, isRedirect } from "@tanstack/react-router";
|
|
7
|
+
import { getReasonPhrase } from "http-status-codes";
|
|
8
|
+
|
|
9
|
+
//#region src/infrastructure/tanstack/auth.middleware.ts
|
|
10
|
+
const logger$1 = createLogger("auth-middleware");
|
|
11
|
+
/**
|
|
12
|
+
* Preserve Porte's function and request middleware contracts with application-owned services.
|
|
13
|
+
* Optional lookup failures log once and continue as guests. Required lookup failures propagate.
|
|
14
|
+
*/
|
|
15
|
+
function createAuthMiddleware(options) {
|
|
16
|
+
const extractAuth = createMiddleware({ type: "function" }).server(async ({ next, context }) => {
|
|
17
|
+
let resolved;
|
|
18
|
+
try {
|
|
19
|
+
resolved = await options.getAuth(context).api.getSession({ headers: getRequestHeaders() });
|
|
20
|
+
} catch (error) {
|
|
21
|
+
logger$1.error("get_session_failed", { error });
|
|
22
|
+
resolved = null;
|
|
23
|
+
}
|
|
24
|
+
if (resolved === null) return next({ context: {
|
|
25
|
+
user: void 0,
|
|
26
|
+
session: void 0
|
|
27
|
+
} });
|
|
28
|
+
return next({ context: {
|
|
29
|
+
user: resolved.user,
|
|
30
|
+
session: resolved.session
|
|
31
|
+
} });
|
|
32
|
+
});
|
|
33
|
+
async function resolveRequiredSession(context, headers) {
|
|
34
|
+
const resolved = await options.getAuth(context).api.getSession({ headers });
|
|
35
|
+
if (resolved === null) {
|
|
36
|
+
if (getSessionCookie(headers)) throw new StaleSessionError();
|
|
37
|
+
throw new AuthenticationError();
|
|
38
|
+
}
|
|
39
|
+
return resolved;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
extractAuth,
|
|
43
|
+
requireAuth: createMiddleware({ type: "function" }).server(async ({ next, context }) => {
|
|
44
|
+
const resolved = await resolveRequiredSession(context, getRequestHeaders());
|
|
45
|
+
return next({ context: {
|
|
46
|
+
user: resolved.user,
|
|
47
|
+
session: resolved.session
|
|
48
|
+
} });
|
|
49
|
+
}),
|
|
50
|
+
requireAuthRequest: createMiddleware({ type: "request" }).server(async ({ request, next, context }) => {
|
|
51
|
+
const resolved = await resolveRequiredSession(context, request.headers);
|
|
52
|
+
return next({ context: {
|
|
53
|
+
user: resolved.user,
|
|
54
|
+
session: resolved.session
|
|
55
|
+
} });
|
|
56
|
+
})
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/infrastructure/tanstack/resolve-failure.ts
|
|
62
|
+
const logger = createLogger("error-boundary");
|
|
63
|
+
/**
|
|
64
|
+
* Resolve one failure through the table or fall back to the generic replacement, and log it once.
|
|
65
|
+
* Both transport converters call this, so one rule decides the log level: mapped is a warning, unmapped is an error with the cause.
|
|
66
|
+
*/
|
|
67
|
+
function resolveFailure(table, cause) {
|
|
68
|
+
const resolved = table.resolve(cause);
|
|
69
|
+
if (resolved !== null) {
|
|
70
|
+
logger.warn("handled_error", { details: {
|
|
71
|
+
tag: resolved.payload._tag,
|
|
72
|
+
status: resolved.status
|
|
73
|
+
} });
|
|
74
|
+
return resolved;
|
|
75
|
+
}
|
|
76
|
+
const classification = cause instanceof AppError ? cause.classification : "unknown";
|
|
77
|
+
logger.error("unexpected_error", {
|
|
78
|
+
error: cause,
|
|
79
|
+
details: { classification }
|
|
80
|
+
});
|
|
81
|
+
return table.fallback(cause);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region src/infrastructure/tanstack/http-error-response.ts
|
|
86
|
+
const RetryAfterSchema = z.object({ retryAfter: z.number() });
|
|
87
|
+
/**
|
|
88
|
+
* Build the API route converter for one error table.
|
|
89
|
+
* The body is RFC 9457 problem details; public fields become extension members.
|
|
90
|
+
*/
|
|
91
|
+
function createHttpErrorResponseConverter(table) {
|
|
92
|
+
return function toHttpErrorResponse(cause) {
|
|
93
|
+
const { status, payload } = resolveFailure(table, cause);
|
|
94
|
+
const { _tag, message, ...fields } = payload;
|
|
95
|
+
const body = {
|
|
96
|
+
type: "about:blank",
|
|
97
|
+
title: getReasonPhrase(status),
|
|
98
|
+
detail: message,
|
|
99
|
+
...fields
|
|
100
|
+
};
|
|
101
|
+
const headers = new Headers({ "Content-Type": "application/problem+json" });
|
|
102
|
+
const retry = RetryAfterSchema.safeParse(payload);
|
|
103
|
+
if (retry.success) headers.set("Retry-After", String(retry.data.retryAfter));
|
|
104
|
+
return Response.json(body, {
|
|
105
|
+
status,
|
|
106
|
+
headers
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//#endregion
|
|
112
|
+
//#region src/infrastructure/tanstack/session-cookies.ts
|
|
113
|
+
/**
|
|
114
|
+
* Delete the session, session cache, account cache, and remember-me cookies of one instance.
|
|
115
|
+
* Chunks present on the request go too. Names and attributes come from the instance, so custom prefixes and domain cookies clear.
|
|
116
|
+
*/
|
|
117
|
+
function clearStaleSessionCookies(cookies) {
|
|
118
|
+
const present = [...parseCookies(getRequestHeaders().get("cookie") ?? "").keys()];
|
|
119
|
+
const definitions = [
|
|
120
|
+
cookies.sessionToken,
|
|
121
|
+
cookies.sessionData,
|
|
122
|
+
cookies.accountData,
|
|
123
|
+
cookies.dontRememberToken
|
|
124
|
+
];
|
|
125
|
+
for (const cookie of definitions) {
|
|
126
|
+
const chunks = present.filter((name) => name.startsWith(`${cookie.name}.`));
|
|
127
|
+
for (const name of /* @__PURE__ */ new Set([cookie.name, ...chunks])) deleteCookie(name, deletionOf(cookie.attributes));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function deletionOf(attributes) {
|
|
131
|
+
const options = {
|
|
132
|
+
path: attributes.path ?? "/",
|
|
133
|
+
secure: attributes.secure ?? false
|
|
134
|
+
};
|
|
135
|
+
if (attributes.domain !== void 0) options.domain = attributes.domain;
|
|
136
|
+
return options;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/infrastructure/tanstack/to-error-payload.ts
|
|
141
|
+
const SerializedIssuesSchema = z.array(z.object({
|
|
142
|
+
message: z.string(),
|
|
143
|
+
path: z.array(z.union([z.string(), z.number()]))
|
|
144
|
+
})).min(1);
|
|
145
|
+
function reconstructValidationError(cause) {
|
|
146
|
+
if (!(cause instanceof Error)) return null;
|
|
147
|
+
let message;
|
|
148
|
+
try {
|
|
149
|
+
message = JSON.parse(cause.message);
|
|
150
|
+
} catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
const parsed = SerializedIssuesSchema.safeParse(message);
|
|
154
|
+
return parsed.success ? ValidationError.fromIssues(parsed.data) : null;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Build the server-function converter for one error table.
|
|
158
|
+
* The result is a plain object, because only own data survives serialization.
|
|
159
|
+
*/
|
|
160
|
+
function createErrorPayloadConverter(table) {
|
|
161
|
+
return function toErrorPayload(cause) {
|
|
162
|
+
return resolveFailure(table, reconstructValidationError(cause) ?? cause).payload;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/infrastructure/tanstack/error.middleware.ts
|
|
168
|
+
/**
|
|
169
|
+
* Build both transport boundaries from one error table.
|
|
170
|
+
* Register `functionErrorMiddleware` globally. Put `routeErrorMiddleware` first in an API route's `server.middleware`.
|
|
171
|
+
*/
|
|
172
|
+
function createErrorMiddleware(options) {
|
|
173
|
+
const toErrorPayload = createErrorPayloadConverter(options.errors);
|
|
174
|
+
const toHttpErrorResponse = createHttpErrorResponseConverter(options.errors);
|
|
175
|
+
async function clearSession(context) {
|
|
176
|
+
const { authCookies } = await options.getAuth(context).$context;
|
|
177
|
+
clearStaleSessionCookies(authCookies);
|
|
178
|
+
}
|
|
179
|
+
async function handleFunctionError(cause, context) {
|
|
180
|
+
if (isNotFound(cause) || isRedirect(cause)) throw cause;
|
|
181
|
+
if (cause instanceof StaleSessionError) {
|
|
182
|
+
await clearSession(context);
|
|
183
|
+
throw options.signIn();
|
|
184
|
+
}
|
|
185
|
+
throw toErrorPayload(cause);
|
|
186
|
+
}
|
|
187
|
+
async function handleRouteError(cause, context) {
|
|
188
|
+
if (isNotFound(cause) || isRedirect(cause)) throw cause;
|
|
189
|
+
if (cause instanceof StaleSessionError) await clearSession(context);
|
|
190
|
+
return toHttpErrorResponse(cause);
|
|
191
|
+
}
|
|
192
|
+
return {
|
|
193
|
+
functionErrorMiddleware: createMiddleware({ type: "function" }).server(async ({ next, context }) => {
|
|
194
|
+
try {
|
|
195
|
+
return await next();
|
|
196
|
+
} catch (cause) {
|
|
197
|
+
return handleFunctionError(cause, context);
|
|
198
|
+
}
|
|
199
|
+
}),
|
|
200
|
+
routeErrorMiddleware: createMiddleware({ type: "request" }).server(async ({ next, context }) => {
|
|
201
|
+
try {
|
|
202
|
+
return await next();
|
|
203
|
+
} catch (cause) {
|
|
204
|
+
return handleRouteError(cause, context);
|
|
205
|
+
}
|
|
206
|
+
}),
|
|
207
|
+
toErrorPayload,
|
|
208
|
+
toHttpErrorResponse
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
export { createAuthMiddleware, createErrorMiddleware };
|
|
214
|
+
//# sourceMappingURL=tanstack.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tanstack.js","names":["logger"],"sources":["../src/infrastructure/tanstack/auth.middleware.ts","../src/infrastructure/tanstack/resolve-failure.ts","../src/infrastructure/tanstack/http-error-response.ts","../src/infrastructure/tanstack/session-cookies.ts","../src/infrastructure/tanstack/to-error-payload.ts","../src/infrastructure/tanstack/error.middleware.ts"],"sourcesContent":["import { createMiddleware, type Register, type RequestServerOptions } from '@tanstack/react-start'\nimport { getRequestHeaders } from '@tanstack/react-start/server'\nimport { getSessionCookie } from 'better-auth/cookies'\n\nimport { AuthenticationError, StaleSessionError, createLogger } from '@/core'\n\nconst logger = createLogger('auth-middleware')\n\n/** The fields middleware reads; generic callers retain every additional session and user field. */\nexport type AuthSession = {\n readonly user: { readonly id: string }\n readonly session: { readonly id: string }\n}\n\n/** Authenticated context retains the application's complete user and session types. */\nexport type RequiredAuthContext<Session extends AuthSession> = {\n readonly user: Session['user']\n readonly session: Session['session']\n}\n\n/** Optional authentication supplies both fields together or neither field. */\nexport type AuthContext<Session extends AuthSession> =\n | RequiredAuthContext<Session>\n | {\n readonly user: undefined\n readonly session: undefined\n }\n\n/** Select the current request's Better Auth instance from TanStack's registered context. */\nexport type AuthMiddlewareOptions<Session extends AuthSession> = {\n readonly getAuth: (context: RequestServerOptions<Register, undefined>['context']) => {\n readonly api: {\n readonly getSession: (options: { headers: Headers }) => Promise<Session | null>\n }\n }\n}\n\n/**\n * Preserve Porte's function and request middleware contracts with application-owned services.\n * Optional lookup failures log once and continue as guests. Required lookup failures propagate.\n */\nexport function createAuthMiddleware<Session extends AuthSession>(\n options: AuthMiddlewareOptions<Session>,\n) {\n const extractAuth = createMiddleware({ type: 'function' }).server<AuthContext<Session>>(\n async ({ next, context }) => {\n let resolved: Session | null\n try {\n resolved = await options.getAuth(context).api.getSession({ headers: getRequestHeaders() })\n } catch (error) {\n logger.error('get_session_failed', { error })\n resolved = null\n }\n if (resolved === null)\n return next<AuthContext<Session>>({ context: { user: undefined, session: undefined } })\n return next<AuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n },\n )\n\n async function resolveRequiredSession(\n context: RequestServerOptions<Register, undefined>['context'],\n headers: Headers,\n ): Promise<Session> {\n const resolved = await options.getAuth(context).api.getSession({ headers })\n if (resolved === null) {\n if (getSessionCookie(headers)) throw new StaleSessionError()\n throw new AuthenticationError()\n }\n return resolved\n }\n\n const requireAuth = createMiddleware({ type: 'function' }).server(async ({ next, context }) => {\n const resolved = await resolveRequiredSession(context, getRequestHeaders())\n return next<RequiredAuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n })\n const requireAuthRequest = createMiddleware({ type: 'request' }).server(\n async ({ request, next, context }) => {\n const resolved = await resolveRequiredSession(context, request.headers)\n return next<RequiredAuthContext<Session>>({\n context: { user: resolved.user, session: resolved.session },\n })\n },\n )\n return { extractAuth, requireAuth, requireAuthRequest }\n}\n","import { AppError, createLogger, type ErrorTable, type ResolvedError } from '@/core'\nimport type { ErrorPayloadOf } from '@/core'\n\nconst logger = createLogger('error-boundary')\n\n/**\n * Resolve one failure through the table or fall back to the generic replacement, and log it once.\n * Both transport converters call this, so one rule decides the log level: mapped is a warning, unmapped is an error with the cause.\n */\nexport function resolveFailure<Entries>(\n table: ErrorTable<Entries>,\n cause: unknown,\n): ResolvedError<ErrorPayloadOf<ErrorTable<Entries>>> {\n const resolved = table.resolve(cause)\n if (resolved !== null) {\n logger.warn('handled_error', {\n details: { tag: resolved.payload._tag, status: resolved.status },\n })\n return resolved\n }\n const classification = cause instanceof AppError ? cause.classification : 'unknown'\n logger.error('unexpected_error', { error: cause, details: { classification } })\n return table.fallback(cause)\n}\n","import { getReasonPhrase } from 'http-status-codes'\nimport { z } from 'zod'\n\nimport type { ErrorTable, ProblemDetails } from '@/core'\n\nimport { resolveFailure } from './resolve-failure.ts'\n\n// A known delay becomes the header; a null delay sends no header.\nconst RetryAfterSchema = z.object({ retryAfter: z.number() })\n\n/**\n * Build the API route converter for one error table.\n * The body is RFC 9457 problem details; public fields become extension members.\n */\nexport function createHttpErrorResponseConverter<Entries>(table: ErrorTable<Entries>) {\n return function toHttpErrorResponse(cause: unknown): Response {\n const { status, payload } = resolveFailure(table, cause)\n const { _tag, message, ...fields } = payload\n const body: ProblemDetails = {\n type: 'about:blank',\n title: getReasonPhrase(status),\n detail: message,\n ...fields,\n }\n const headers = new Headers({ 'Content-Type': 'application/problem+json' })\n const retry = RetryAfterSchema.safeParse(payload)\n if (retry.success) headers.set('Retry-After', String(retry.data.retryAfter))\n return Response.json(body, { status, headers })\n }\n}\n","import { deleteCookie, getRequestHeaders } from '@tanstack/react-start/server'\nimport type { BetterAuthCookies } from 'better-auth'\nimport { parseCookies } from 'better-auth/cookies'\n\ntype AuthCookie = BetterAuthCookies['sessionToken']\ntype DeleteCookieOptions = NonNullable<Parameters<typeof deleteCookie>[1]>\n\n/**\n * Delete the session, session cache, account cache, and remember-me cookies of one instance.\n * Chunks present on the request go too. Names and attributes come from the instance, so custom prefixes and domain cookies clear.\n */\nexport function clearStaleSessionCookies(cookies: BetterAuthCookies): void {\n const present = [...parseCookies(getRequestHeaders().get('cookie') ?? '').keys()]\n const definitions = [\n cookies.sessionToken,\n cookies.sessionData,\n cookies.accountData,\n cookies.dontRememberToken,\n ]\n for (const cookie of definitions) {\n // Better Auth names a chunk `${name}.${index}`, so a missed chunk would resurrect the value.\n const chunks = present.filter((name) => name.startsWith(`${cookie.name}.`))\n for (const name of new Set([cookie.name, ...chunks]))\n deleteCookie(name, deletionOf(cookie.attributes))\n }\n}\n\nfunction deletionOf(attributes: AuthCookie['attributes']): DeleteCookieOptions {\n // Better Auth sets `/` and the secure flag at runtime; the types leave both optional.\n const options: DeleteCookieOptions = {\n path: attributes.path ?? '/',\n secure: attributes.secure ?? false,\n }\n if (attributes.domain !== undefined) options.domain = attributes.domain\n return options\n}\n","import { z } from 'zod'\n\nimport { ValidationError, type ErrorPayloadOf, type ErrorTable } from '@/core'\n\nimport { resolveFailure } from './resolve-failure.ts'\n\n// TanStack serializes a validator's issues into `Error.message` as JSON.\nconst SerializedIssuesSchema = z\n .array(z.object({ message: z.string(), path: z.array(z.union([z.string(), z.number()])) }))\n .min(1)\n\nfunction reconstructValidationError(cause: unknown): ValidationError | null {\n if (!(cause instanceof Error)) return null\n let message: unknown\n try {\n message = JSON.parse(cause.message)\n } catch {\n return null\n }\n const parsed = SerializedIssuesSchema.safeParse(message)\n return parsed.success ? ValidationError.fromIssues(parsed.data) : null\n}\n\n/**\n * Build the server-function converter for one error table.\n * The result is a plain object, because only own data survives serialization.\n */\nexport function createErrorPayloadConverter<Entries>(table: ErrorTable<Entries>) {\n return function toErrorPayload(cause: unknown): ErrorPayloadOf<ErrorTable<Entries>> {\n return resolveFailure(table, reconstructValidationError(cause) ?? cause).payload\n }\n}\n","import { isNotFound, isRedirect, type AnyRedirect } from '@tanstack/react-router'\nimport { createMiddleware, type Register, type RequestServerOptions } from '@tanstack/react-start'\nimport type { BetterAuthCookies } from 'better-auth'\n\nimport { StaleSessionError, type ErrorTable } from '@/core'\n\nimport { createHttpErrorResponseConverter } from './http-error-response.ts'\nimport { clearStaleSessionCookies } from './session-cookies.ts'\nimport { createErrorPayloadConverter } from './to-error-payload.ts'\n\ntype RequestContext = RequestServerOptions<Register, undefined>['context']\n\n/** The part of a Better Auth instance the boundary reads: its computed cookie definitions. */\nexport type AuthCookieSource = {\n readonly $context: Promise<{ readonly authCookies: BetterAuthCookies }>\n}\n\n/** The application's error table, its stale-session redirect, and its Better Auth instance. */\nexport type ErrorMiddlewareOptions<Entries> = {\n readonly errors: ErrorTable<Entries>\n /** Build the redirect a server function throws for a stale session. */\n readonly signIn: () => AnyRedirect\n /** Select the request's Better Auth instance; its cookie definitions say what to clear. */\n readonly getAuth: (context: RequestContext) => AuthCookieSource\n}\n\n/**\n * Build both transport boundaries from one error table.\n * Register `functionErrorMiddleware` globally. Put `routeErrorMiddleware` first in an API route's `server.middleware`.\n */\nexport function createErrorMiddleware<Entries>(options: ErrorMiddlewareOptions<Entries>) {\n const toErrorPayload = createErrorPayloadConverter(options.errors)\n const toHttpErrorResponse = createHttpErrorResponseConverter(options.errors)\n\n async function clearSession(context: RequestContext): Promise<void> {\n const { authCookies } = await options.getAuth(context).$context\n clearStaleSessionCookies(authCookies)\n }\n\n async function handleFunctionError(cause: unknown, context: RequestContext): Promise<never> {\n // oxlint-disable-next-line typescript/only-throw-error -- TanStack uses throwable control-flow objects.\n if (isNotFound(cause) || isRedirect(cause)) throw cause\n if (cause instanceof StaleSessionError) {\n await clearSession(context)\n // oxlint-disable-next-line typescript/only-throw-error -- Router redirects by throwing this.\n throw options.signIn()\n }\n // oxlint-disable-next-line typescript/only-throw-error -- The client rejects with this and reads its tag.\n throw toErrorPayload(cause)\n }\n\n // A route answers 401 instead of a redirect because fetch would follow it into the HTML shell.\n async function handleRouteError(cause: unknown, context: RequestContext): Promise<Response> {\n // oxlint-disable-next-line typescript/only-throw-error -- TanStack uses throwable control-flow objects.\n if (isNotFound(cause) || isRedirect(cause)) throw cause\n if (cause instanceof StaleSessionError) await clearSession(context)\n return toHttpErrorResponse(cause)\n }\n\n const functionErrorMiddleware = createMiddleware({ type: 'function' }).server(\n async ({ next, context }) => {\n try {\n return await next()\n } catch (cause) {\n return handleFunctionError(cause, context)\n }\n },\n )\n\n const routeErrorMiddleware = createMiddleware({ type: 'request' }).server(\n async ({ next, context }) => {\n try {\n return await next()\n } catch (cause) {\n return handleRouteError(cause, context)\n }\n },\n )\n\n return { functionErrorMiddleware, routeErrorMiddleware, toErrorPayload, toHttpErrorResponse }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as UnitOfWorkAlreadyActiveError } from "./application-
|
|
1
|
+
import { t as UnitOfWorkAlreadyActiveError } from "./application-DEerezCl.js";
|
|
2
2
|
import { panic } from "better-result";
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
4
|
|
|
@@ -45,4 +45,4 @@ function currentContext() {
|
|
|
45
45
|
|
|
46
46
|
//#endregion
|
|
47
47
|
export { stageD1Statement as i, eventCollector as n, runWithUnitOfWorkContext as r, collectD1Statements as t };
|
|
48
|
-
//# sourceMappingURL=unit-of-work-context-
|
|
48
|
+
//# sourceMappingURL=unit-of-work-context-C7vz5OYS.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unit-of-work-context-
|
|
1
|
+
{"version":3,"file":"unit-of-work-context-C7vz5OYS.js","names":[],"sources":["../src/infrastructure/unit-of-work-context.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\n\nimport { panic } from 'better-result'\nimport type { BatchItem } from 'drizzle-orm/batch'\n\nimport { UnitOfWorkAlreadyActiveError, type Event, type IEventCollector } from '@/application'\n\ntype UnitOfWorkContext = { readonly events: Event[] } & (\n | { readonly database: 'd1'; readonly statements: BatchItem<'sqlite'>[] }\n | { readonly database: 'postgres' }\n)\n\n// One async scope isolates events and D1 statements and rejects nesting across both adapters.\nconst storage = new AsyncLocalStorage<UnitOfWorkContext>()\n\n/** Reject nested transactions before database work starts; preserve independent concurrent execution. */\nexport async function runWithUnitOfWorkContext<T>(\n database: UnitOfWorkContext['database'],\n work: () => Promise<T>,\n): Promise<T> {\n if (storage.getStore() !== undefined) throw new UnitOfWorkAlreadyActiveError()\n const context: UnitOfWorkContext =\n database === 'd1' ? { database, events: [], statements: [] } : { database, events: [] }\n return storage.run(context, work)\n}\n\n/** Inject this shared scope into the unit of work and expose its emitter to application handlers. */\nexport const eventCollector: IEventCollector = {\n emit(event) {\n currentContext().events.push(event)\n },\n collect() {\n return [...currentContext().events]\n },\n}\n\n/** Queue one native D1 statement for the active batch; other contexts cannot accept D1 writes. */\nexport function stageD1Statement(statement: BatchItem<'sqlite'>): void {\n d1Context().statements.push(statement)\n}\n\n/** Read the active D1 batch in staging order for commit. */\nexport function collectD1Statements(): ReadonlyArray<BatchItem<'sqlite'>> {\n return [...d1Context().statements]\n}\n\nfunction d1Context(): Extract<UnitOfWorkContext, { database: 'd1' }> {\n const context = currentContext()\n if (context.database !== 'd1') return panic('D1 statements require a D1 unit of work')\n return context\n}\n\nfunction currentContext(): UnitOfWorkContext {\n return storage.getStore() ?? panic('operation requires an active unit of work')\n}\n"],"mappings":";;;;;AAaA,MAAM,UAAU,IAAI,kBAAqC;;AAGzD,eAAsB,yBACpB,UACA,MACY;CACZ,IAAI,QAAQ,SAAS,MAAM,QAAW,MAAM,IAAI,6BAA6B;CAC7E,MAAM,UACJ,aAAa,OAAO;EAAE;EAAU,QAAQ,CAAC;EAAG,YAAY,CAAC;CAAE,IAAI;EAAE;EAAU,QAAQ,CAAC;CAAE;CACxF,OAAO,QAAQ,IAAI,SAAS,IAAI;AAClC;;AAGA,MAAa,iBAAkC;CAC7C,KAAK,OAAO;EACV,eAAe,CAAC,CAAC,OAAO,KAAK,KAAK;CACpC;CACA,UAAU;EACR,OAAO,CAAC,GAAG,eAAe,CAAC,CAAC,MAAM;CACpC;AACF;;AAGA,SAAgB,iBAAiB,WAAsC;CACrE,UAAU,CAAC,CAAC,WAAW,KAAK,SAAS;AACvC;;AAGA,SAAgB,sBAA0D;CACxE,OAAO,CAAC,GAAG,UAAU,CAAC,CAAC,UAAU;AACnC;AAEA,SAAS,YAA4D;CACnE,MAAM,UAAU,eAAe;CAC/B,IAAI,QAAQ,aAAa,MAAM,OAAO,MAAM,yCAAyC;CACrF,OAAO;AACT;AAEA,SAAS,iBAAoC;CAC3C,OAAO,QAAQ,SAAS,KAAK,MAAM,2CAA2C;AAChF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { o as IEventCollector } from "./index-
|
|
1
|
+
import { o as IEventCollector } from "./index-Bdyh3_xC.js";
|
|
2
2
|
import { BatchItem } from "drizzle-orm/batch";
|
|
3
3
|
//#region src/infrastructure/drizzle-schema.d.ts
|
|
4
4
|
/**
|
|
@@ -12,4 +12,4 @@ type DrizzleSchema = Record<string, unknown>;
|
|
|
12
12
|
declare const eventCollector: IEventCollector;
|
|
13
13
|
//#endregion
|
|
14
14
|
export { DrizzleSchema as n, eventCollector as t };
|
|
15
|
-
//# sourceMappingURL=unit-of-work-context-
|
|
15
|
+
//# sourceMappingURL=unit-of-work-context-ml9L9cyY.d.ts.map
|
package/dist/{unit-of-work-context-DOkIB7HK.d.ts.map → unit-of-work-context-ml9L9cyY.d.ts.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unit-of-work-context-
|
|
1
|
+
{"version":3,"file":"unit-of-work-context-ml9L9cyY.d.ts","names":[],"sources":["../src/infrastructure/drizzle-schema.ts","../src/infrastructure/unit-of-work-context.ts"],"mappings":";;;;;;;KAKY,gBAAgB;;;;cCsBf,gBAAgB"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://www.schemastore.org/package.json",
|
|
3
3
|
"name": "better-ship",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.5.0",
|
|
5
5
|
"description": "Foundation for applications on Cloudflare Workers: logger, errors, message bus, infrastructure adapters, and UI primitives",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -16,7 +16,14 @@
|
|
|
16
16
|
"type": "module",
|
|
17
17
|
"exports": {
|
|
18
18
|
"./application": "./dist/application.js",
|
|
19
|
+
"./better-auth": "./dist/better-auth.js",
|
|
20
|
+
"./better-auth/apple-client-secret": "./dist/better-auth/apple-client-secret.js",
|
|
21
|
+
"./better-auth/auth-client": "./dist/better-auth/auth-client.js",
|
|
22
|
+
"./better-auth/rate-limit": "./dist/better-auth/rate-limit.js",
|
|
23
|
+
"./better-auth/secondary-storage": "./dist/better-auth/secondary-storage.js",
|
|
24
|
+
"./better-auth/stripe": "./dist/better-auth/stripe.js",
|
|
19
25
|
"./cloudflare": "./dist/cloudflare.js",
|
|
26
|
+
"./cloudflare/auth-secondary-storage": "./dist/cloudflare/auth-secondary-storage.js",
|
|
20
27
|
"./core": "./dist/core.js",
|
|
21
28
|
"./postgres": "./dist/postgres.js",
|
|
22
29
|
"./tanstack": "./dist/tanstack.js",
|
|
@@ -27,23 +34,60 @@
|
|
|
27
34
|
"access": "public"
|
|
28
35
|
},
|
|
29
36
|
"dependencies": {
|
|
30
|
-
"better-result": "3.0.1"
|
|
37
|
+
"better-result": "^3.0.1",
|
|
38
|
+
"http-status-codes": "^2.3.0",
|
|
39
|
+
"uuid": "^14.0.2",
|
|
40
|
+
"zod": "^4.6.2"
|
|
31
41
|
},
|
|
32
42
|
"devDependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"@cloudflare/
|
|
35
|
-
"@types
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
43
|
+
"@better-auth/stripe": "^1.7.4",
|
|
44
|
+
"@cloudflare/vitest-plugin": "^1.1.7",
|
|
45
|
+
"@cloudflare/workers-types": "5.20260911.1",
|
|
46
|
+
"@tanstack/react-router": "^1.170.35",
|
|
47
|
+
"@tanstack/react-start": "^1.168.52",
|
|
48
|
+
"@tanstack/start-client-core": "^1.170.29",
|
|
49
|
+
"@tanstack/start-storage-context": "^1.167.31",
|
|
50
|
+
"@types/node": "^26.5.1",
|
|
51
|
+
"auth": "^1.7.4",
|
|
52
|
+
"better-auth": "^1.7.4",
|
|
53
|
+
"drizzle-kit": "^0.31.10",
|
|
54
|
+
"drizzle-orm": "^0.45.2",
|
|
55
|
+
"jose": "^6.2.12",
|
|
56
|
+
"postgres": "^3.4.9",
|
|
57
|
+
"stripe": "^22.6.2",
|
|
58
|
+
"tsdown": "^0.23.0",
|
|
59
|
+
"typescript": "^7.0.2",
|
|
60
|
+
"vitest": "^4.1.11",
|
|
61
|
+
"wrangler": "^4.131.0"
|
|
42
62
|
},
|
|
43
63
|
"peerDependencies": {
|
|
44
|
-
"
|
|
64
|
+
"@better-auth/stripe": "^1.7.4",
|
|
65
|
+
"@tanstack/react-router": "^1.170.35",
|
|
66
|
+
"@tanstack/react-start": "^1.168.52",
|
|
67
|
+
"better-auth": "^1.7.4",
|
|
68
|
+
"drizzle-orm": "^0.45.2",
|
|
69
|
+
"jose": "^6.2.12",
|
|
70
|
+
"stripe": "^22.6.2"
|
|
45
71
|
},
|
|
46
72
|
"peerDependenciesMeta": {
|
|
73
|
+
"@better-auth/stripe": {
|
|
74
|
+
"optional": true
|
|
75
|
+
},
|
|
76
|
+
"stripe": {
|
|
77
|
+
"optional": true
|
|
78
|
+
},
|
|
79
|
+
"jose": {
|
|
80
|
+
"optional": true
|
|
81
|
+
},
|
|
82
|
+
"better-auth": {
|
|
83
|
+
"optional": true
|
|
84
|
+
},
|
|
85
|
+
"@tanstack/react-router": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"@tanstack/react-start": {
|
|
89
|
+
"optional": true
|
|
90
|
+
},
|
|
47
91
|
"drizzle-orm": {
|
|
48
92
|
"optional": true
|
|
49
93
|
}
|
|
@@ -58,6 +102,7 @@
|
|
|
58
102
|
"lint": "oxlint . --quiet",
|
|
59
103
|
"lint:fix": "oxlint . --fix --quiet",
|
|
60
104
|
"test": "vitest run",
|
|
105
|
+
"cf-typegen": "wrangler types tests/infrastructure/cloudflare/worker-configuration.d.ts --config tests/infrastructure/cloudflare/wrangler.jsonc --include-runtime=false",
|
|
61
106
|
"db:generate:d1": "drizzle-kit generate --config drizzle.d1.config.ts",
|
|
62
107
|
"db:generate:postgres": "drizzle-kit generate --config drizzle.postgres.config.ts"
|
|
63
108
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { AppError } from './app-error.ts'
|
|
2
|
+
|
|
3
|
+
/** A protected operation requires a session that the caller did not provide. */
|
|
4
|
+
export class AuthenticationError extends AppError {
|
|
5
|
+
readonly _tag = 'AuthenticationError'
|
|
6
|
+
readonly classification = 'terminal'
|
|
7
|
+
|
|
8
|
+
constructor() {
|
|
9
|
+
super('Authentication required')
|
|
10
|
+
}
|
|
11
|
+
}
|