@qelos/integrator-next 4.0.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.
@@ -0,0 +1,68 @@
1
+ import { NextResponse, type NextRequest } from 'next/server';
2
+ import type { IUser } from '@qelos/sdk/dist/authentication';
3
+ import type { IWorkspace } from '@qelos/sdk/workspaces';
4
+ import type { QelosNextConfig, TokenRefreshHook } from './types';
5
+ export interface PendingCookie {
6
+ name: string;
7
+ value: string;
8
+ options: {
9
+ httpOnly: boolean;
10
+ secure: boolean;
11
+ sameSite: 'lax' | 'strict' | 'none';
12
+ path: string;
13
+ };
14
+ }
15
+ /**
16
+ * Carrier passed to the App Router middleware token-refresh hook. Cookies
17
+ * pushed onto `pendingCookies` are written to the outbound `NextResponse`
18
+ * after identification completes — Next.js does not let us mutate the
19
+ * response headers after `NextResponse.next()` has been constructed, so we
20
+ * stage them here and flush them once the response object exists.
21
+ */
22
+ export interface MiddlewareRefreshTarget {
23
+ req: NextRequest;
24
+ pendingCookies: PendingCookie[];
25
+ }
26
+ export interface CreateMiddlewareOptions {
27
+ config: QelosNextConfig;
28
+ /**
29
+ * Hook invoked after a successful token refresh. The default implementation
30
+ * stages new cookies for the outbound response.
31
+ */
32
+ onTokenRefresh?: TokenRefreshHook<MiddlewareRefreshTarget>;
33
+ /**
34
+ * Resolve the active workspace for a request. Defaults to picking the first
35
+ * workspace returned from `sdk.workspaces.getList()`.
36
+ */
37
+ resolveWorkspace?: (params: {
38
+ req: NextRequest;
39
+ user: IUser;
40
+ workspaces: IWorkspace[];
41
+ }) => IWorkspace | null | Promise<IWorkspace | null>;
42
+ }
43
+ export type QelosMiddleware = (req: NextRequest) => Promise<NextResponse>;
44
+ /**
45
+ * Create a Next.js (App Router) edge middleware that identifies the user and
46
+ * the active workspace via the Qelos SDK before the request reaches the
47
+ * application's own handler. Resolved identity is forwarded to downstream
48
+ * handlers via request headers (`x-qelos-user-id`, `x-qelos-workspace-id`);
49
+ * use `getQelosContext()` from `@qelos/integrator-next/context` to obtain the
50
+ * full user / workspace objects inside server components or route handlers.
51
+ */
52
+ export declare function createQelosMiddleware(options: CreateMiddlewareOptions): QelosMiddleware;
53
+ /**
54
+ * Pre-configured Next.js edge middleware that reads its config from the
55
+ * environment (see {@link loadQelosConfigFromEnv}). Re-export it from your
56
+ * `middleware.ts` to opt into Qelos identification with zero setup:
57
+ *
58
+ * ```ts
59
+ * // middleware.ts
60
+ * export { qelosMiddleware as middleware } from '@qelos/integrator-next/middleware';
61
+ * export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };
62
+ * ```
63
+ *
64
+ * Each invocation lazily instantiates the middleware on the first call so
65
+ * importing this module does not throw when env vars are not yet loaded.
66
+ * For full control, call {@link createQelosMiddleware} with an explicit config.
67
+ */
68
+ export declare const qelosMiddleware: QelosMiddleware;
@@ -0,0 +1,140 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { applyQelosForwardHeaders } from './forward-headers';
3
+ import { getDefaultQelosConfig } from './env-config';
4
+ import { createRequestSdk } from './sdk-factory';
5
+ const DEFAULT_ACCESS_COOKIE = 'q_access_token';
6
+ const DEFAULT_REFRESH_COOKIE = 'q_refresh_token';
7
+ function readTokens(req, config) {
8
+ const accessCookie = config.accessTokenCookie || DEFAULT_ACCESS_COOKIE;
9
+ const refreshCookie = config.refreshTokenCookie || DEFAULT_REFRESH_COOKIE;
10
+ const cookieAccess = req.cookies.get(accessCookie)?.value;
11
+ const cookieRefresh = req.cookies.get(refreshCookie)?.value;
12
+ const authHeader = req.headers.get('authorization');
13
+ const headerAccess = authHeader && authHeader.toLowerCase().startsWith('bearer ')
14
+ ? authHeader.slice(7).trim()
15
+ : undefined;
16
+ return {
17
+ accessToken: headerAccess || cookieAccess || undefined,
18
+ refreshToken: cookieRefresh || undefined,
19
+ };
20
+ }
21
+ function stagePendingCookies(pending, config, tokens) {
22
+ const accessCookie = config.accessTokenCookie || DEFAULT_ACCESS_COOKIE;
23
+ const refreshCookie = config.refreshTokenCookie || DEFAULT_REFRESH_COOKIE;
24
+ const secure = !/^http:\/\//i.test(config.appUrl);
25
+ const options = {
26
+ httpOnly: true,
27
+ secure,
28
+ sameSite: 'lax',
29
+ path: '/',
30
+ };
31
+ pending.push({ name: accessCookie, value: tokens.accessToken, options });
32
+ if (tokens.refreshToken) {
33
+ pending.push({ name: refreshCookie, value: tokens.refreshToken, options });
34
+ }
35
+ }
36
+ function shouldSkip(req, config) {
37
+ if (!config.skipPaths?.length)
38
+ return false;
39
+ const path = req.nextUrl.pathname;
40
+ return config.skipPaths.some((prefix) => path.startsWith(prefix));
41
+ }
42
+ function unauthorized() {
43
+ return new NextResponse(JSON.stringify({ code: 'UNAUTHORIZED' }), {
44
+ status: 401,
45
+ headers: { 'content-type': 'application/json' },
46
+ });
47
+ }
48
+ /**
49
+ * Create a Next.js (App Router) edge middleware that identifies the user and
50
+ * the active workspace via the Qelos SDK before the request reaches the
51
+ * application's own handler. Resolved identity is forwarded to downstream
52
+ * handlers via request headers (`x-qelos-user-id`, `x-qelos-workspace-id`);
53
+ * use `getQelosContext()` from `@qelos/integrator-next/context` to obtain the
54
+ * full user / workspace objects inside server components or route handlers.
55
+ */
56
+ export function createQelosMiddleware(options) {
57
+ const { config, resolveWorkspace } = options;
58
+ const onTokenRefresh = options.onTokenRefresh ||
59
+ (async ({ target, newTokens }) => {
60
+ stagePendingCookies(target.pendingCookies, config, newTokens);
61
+ });
62
+ return async function qelosMiddleware(req) {
63
+ if (shouldSkip(req, config)) {
64
+ return NextResponse.next();
65
+ }
66
+ const tokens = readTokens(req, config);
67
+ const pendingCookies = [];
68
+ const forwardedHeaders = new Headers(req.headers);
69
+ const sdk = createRequestSdk({
70
+ config,
71
+ tokens,
72
+ refreshTarget: { req, pendingCookies },
73
+ onTokenRefresh,
74
+ });
75
+ const hasAuthMaterial = Boolean(config.apiToken || tokens.accessToken || tokens.refreshToken);
76
+ let user = null;
77
+ let workspaces = [];
78
+ let workspace = null;
79
+ let identifyFailed = false;
80
+ if (hasAuthMaterial) {
81
+ try {
82
+ user = await sdk.authentication.getLoggedInUser();
83
+ }
84
+ catch {
85
+ identifyFailed = true;
86
+ }
87
+ if (user) {
88
+ try {
89
+ workspaces = await sdk.workspaces.getList();
90
+ }
91
+ catch {
92
+ workspaces = [];
93
+ }
94
+ if (workspaces.length) {
95
+ if (resolveWorkspace) {
96
+ workspace =
97
+ (await resolveWorkspace({ req, user, workspaces })) || null;
98
+ }
99
+ else {
100
+ workspace = workspaces[0] || null;
101
+ }
102
+ }
103
+ }
104
+ }
105
+ if (config.requireAuth && (!hasAuthMaterial || identifyFailed)) {
106
+ return unauthorized();
107
+ }
108
+ applyQelosForwardHeaders(forwardedHeaders, user, workspace);
109
+ const response = NextResponse.next({
110
+ request: { headers: forwardedHeaders },
111
+ });
112
+ for (const cookie of pendingCookies) {
113
+ response.cookies.set(cookie.name, cookie.value, cookie.options);
114
+ }
115
+ return response;
116
+ };
117
+ }
118
+ let defaultMiddleware = null;
119
+ /**
120
+ * Pre-configured Next.js edge middleware that reads its config from the
121
+ * environment (see {@link loadQelosConfigFromEnv}). Re-export it from your
122
+ * `middleware.ts` to opt into Qelos identification with zero setup:
123
+ *
124
+ * ```ts
125
+ * // middleware.ts
126
+ * export { qelosMiddleware as middleware } from '@qelos/integrator-next/middleware';
127
+ * export const config = { matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'] };
128
+ * ```
129
+ *
130
+ * Each invocation lazily instantiates the middleware on the first call so
131
+ * importing this module does not throw when env vars are not yet loaded.
132
+ * For full control, call {@link createQelosMiddleware} with an explicit config.
133
+ */
134
+ export const qelosMiddleware = (req) => {
135
+ if (!defaultMiddleware) {
136
+ defaultMiddleware = createQelosMiddleware({ config: getDefaultQelosConfig() });
137
+ }
138
+ return defaultMiddleware(req);
139
+ };
140
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../src/middleware.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,54 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult, NextApiHandler } from 'next';
3
+ import type { IUser } from '@qelos/sdk/dist/authentication';
4
+ import type { IWorkspace } from '@qelos/sdk/workspaces';
5
+ import type { QelosNextConfig, QelosRequestContext, TokenRefreshHook } from './types';
6
+ type CookieMap = Partial<Record<string, string>>;
7
+ interface RequestWithCookies extends IncomingMessage {
8
+ cookies?: CookieMap;
9
+ }
10
+ declare module 'http' {
11
+ interface IncomingMessage {
12
+ qelos?: QelosRequestContext;
13
+ }
14
+ }
15
+ export interface PagesIntegratorOptions {
16
+ config: QelosNextConfig;
17
+ /**
18
+ * Hook invoked after a successful token refresh. The default implementation
19
+ * writes the new tokens back to the response cookies.
20
+ */
21
+ onTokenRefresh?: TokenRefreshHook<ServerResponse>;
22
+ /**
23
+ * Resolve the active workspace for a request. Defaults to picking the first
24
+ * workspace returned from `sdk.workspaces.getList()`.
25
+ */
26
+ resolveWorkspace?: (params: {
27
+ req: RequestWithCookies;
28
+ user: IUser;
29
+ workspaces: IWorkspace[];
30
+ }) => IWorkspace | null | Promise<IWorkspace | null>;
31
+ }
32
+ /**
33
+ * Wrap a Pages Router API route handler so that `req.qelos` is populated
34
+ * before the handler runs. Anonymous requests pass through with
35
+ * `req.qelos.user = null` unless `config.requireAuth` is set.
36
+ */
37
+ export declare function withQelosApi(handler: NextApiHandler, options: PagesIntegratorOptions): NextApiHandler;
38
+ /**
39
+ * Wrap a Pages Router API route handler so that requests without a resolved
40
+ * user are rejected with `401`. The handler always sees a populated
41
+ * `req.qelos.user`.
42
+ */
43
+ export declare function requireQelosApiUser(handler: NextApiHandler, options: PagesIntegratorOptions): NextApiHandler;
44
+ /**
45
+ * Wrap a Pages Router `getServerSideProps` so that `context.req.qelos` is
46
+ * populated and the resolved context is also passed as a second argument for
47
+ * convenience.
48
+ */
49
+ export declare function withQelosSSR<P extends {
50
+ [key: string]: any;
51
+ } = {
52
+ [key: string]: any;
53
+ }>(handler: (context: GetServerSidePropsContext, qelos: QelosRequestContext) => Promise<GetServerSidePropsResult<P>> | GetServerSidePropsResult<P>, options: PagesIntegratorOptions): GetServerSideProps<P>;
54
+ export {};
package/dist/pages.js ADDED
@@ -0,0 +1,190 @@
1
+ import { createRequestSdk } from './sdk-factory';
2
+ import { runWithQelosContext } from './context';
3
+ const DEFAULT_ACCESS_COOKIE = 'q_access_token';
4
+ const DEFAULT_REFRESH_COOKIE = 'q_refresh_token';
5
+ function readCookieFromHeader(header, name) {
6
+ if (!header)
7
+ return undefined;
8
+ const prefix = name + '=';
9
+ for (const part of header.split(';')) {
10
+ const trimmed = part.trim();
11
+ if (trimmed.startsWith(prefix)) {
12
+ try {
13
+ return decodeURIComponent(trimmed.slice(prefix.length));
14
+ }
15
+ catch {
16
+ return trimmed.slice(prefix.length);
17
+ }
18
+ }
19
+ }
20
+ return undefined;
21
+ }
22
+ function readTokens(req, config) {
23
+ const accessCookie = config.accessTokenCookie || DEFAULT_ACCESS_COOKIE;
24
+ const refreshCookie = config.refreshTokenCookie || DEFAULT_REFRESH_COOKIE;
25
+ const parsed = req.cookies;
26
+ const cookieAccess = (parsed && typeof parsed[accessCookie] === 'string'
27
+ ? parsed[accessCookie]
28
+ : undefined) || readCookieFromHeader(req.headers.cookie, accessCookie);
29
+ const cookieRefresh = (parsed && typeof parsed[refreshCookie] === 'string'
30
+ ? parsed[refreshCookie]
31
+ : undefined) || readCookieFromHeader(req.headers.cookie, refreshCookie);
32
+ const authHeaderRaw = req.headers.authorization;
33
+ const authHeader = Array.isArray(authHeaderRaw) ? authHeaderRaw[0] : authHeaderRaw;
34
+ const headerAccess = authHeader && authHeader.toLowerCase().startsWith('bearer ')
35
+ ? authHeader.slice(7).trim()
36
+ : undefined;
37
+ return {
38
+ accessToken: headerAccess || cookieAccess || undefined,
39
+ refreshToken: cookieRefresh || undefined,
40
+ };
41
+ }
42
+ function serializeCookie(name, value, secure) {
43
+ const parts = [
44
+ `${name}=${encodeURIComponent(value)}`,
45
+ 'Path=/',
46
+ 'HttpOnly',
47
+ 'SameSite=Lax',
48
+ ];
49
+ if (secure)
50
+ parts.push('Secure');
51
+ return parts.join('; ');
52
+ }
53
+ function appendSetCookie(res, value) {
54
+ const existing = res.getHeader('set-cookie');
55
+ if (Array.isArray(existing)) {
56
+ res.setHeader('set-cookie', [...existing, value]);
57
+ }
58
+ else if (typeof existing === 'string') {
59
+ res.setHeader('set-cookie', [existing, value]);
60
+ }
61
+ else {
62
+ res.setHeader('set-cookie', [value]);
63
+ }
64
+ }
65
+ function writeTokensToResponse(res, config, tokens) {
66
+ const accessCookie = config.accessTokenCookie || DEFAULT_ACCESS_COOKIE;
67
+ const refreshCookie = config.refreshTokenCookie || DEFAULT_REFRESH_COOKIE;
68
+ const secure = !/^http:\/\//i.test(config.appUrl);
69
+ appendSetCookie(res, serializeCookie(accessCookie, tokens.accessToken, secure));
70
+ if (tokens.refreshToken) {
71
+ appendSetCookie(res, serializeCookie(refreshCookie, tokens.refreshToken, secure));
72
+ }
73
+ }
74
+ function shouldSkip(req, config) {
75
+ if (!config.skipPaths?.length)
76
+ return false;
77
+ const path = req.url || '';
78
+ return config.skipPaths.some((prefix) => path.startsWith(prefix));
79
+ }
80
+ async function buildContext(req, res, options) {
81
+ const { config, resolveWorkspace } = options;
82
+ const onTokenRefresh = options.onTokenRefresh ||
83
+ (async ({ target, newTokens }) => {
84
+ writeTokensToResponse(target, config, newTokens);
85
+ });
86
+ const tokens = readTokens(req, config);
87
+ const sdk = createRequestSdk({
88
+ config,
89
+ tokens,
90
+ refreshTarget: res,
91
+ onTokenRefresh,
92
+ });
93
+ const ctx = {
94
+ user: null,
95
+ workspace: null,
96
+ workspaces: [],
97
+ sdk,
98
+ tokens,
99
+ };
100
+ const hasAuthMaterial = Boolean(config.apiToken || tokens.accessToken || tokens.refreshToken);
101
+ if (!hasAuthMaterial) {
102
+ return { ctx, identifyFailed: false };
103
+ }
104
+ let identifyFailed = false;
105
+ try {
106
+ ctx.user = await sdk.authentication.getLoggedInUser();
107
+ }
108
+ catch {
109
+ identifyFailed = true;
110
+ return { ctx, identifyFailed };
111
+ }
112
+ if (!ctx.user)
113
+ return { ctx, identifyFailed };
114
+ try {
115
+ ctx.workspaces = await sdk.workspaces.getList();
116
+ }
117
+ catch {
118
+ ctx.workspaces = [];
119
+ }
120
+ if (ctx.workspaces.length) {
121
+ if (resolveWorkspace) {
122
+ ctx.workspace =
123
+ (await resolveWorkspace({
124
+ req,
125
+ user: ctx.user,
126
+ workspaces: ctx.workspaces,
127
+ })) || null;
128
+ }
129
+ else {
130
+ ctx.workspace = ctx.workspaces[0] || null;
131
+ }
132
+ }
133
+ return { ctx, identifyFailed };
134
+ }
135
+ /**
136
+ * Wrap a Pages Router API route handler so that `req.qelos` is populated
137
+ * before the handler runs. Anonymous requests pass through with
138
+ * `req.qelos.user = null` unless `config.requireAuth` is set.
139
+ */
140
+ export function withQelosApi(handler, options) {
141
+ const { config } = options;
142
+ return async function qelosApiHandler(req, res) {
143
+ if (shouldSkip(req, config)) {
144
+ return handler(req, res);
145
+ }
146
+ const { ctx, identifyFailed } = await buildContext(req, res, options);
147
+ req.qelos = ctx;
148
+ if (config.requireAuth && (!ctx.user || identifyFailed)) {
149
+ res.status(401).json({ code: 'UNAUTHORIZED' });
150
+ return;
151
+ }
152
+ await runWithQelosContext(ctx, () => handler(req, res));
153
+ };
154
+ }
155
+ /**
156
+ * Wrap a Pages Router API route handler so that requests without a resolved
157
+ * user are rejected with `401`. The handler always sees a populated
158
+ * `req.qelos.user`.
159
+ */
160
+ export function requireQelosApiUser(handler, options) {
161
+ return withQelosApi(handler, { ...options, config: { ...options.config, requireAuth: true } });
162
+ }
163
+ /**
164
+ * Wrap a Pages Router `getServerSideProps` so that `context.req.qelos` is
165
+ * populated and the resolved context is also passed as a second argument for
166
+ * convenience.
167
+ */
168
+ export function withQelosSSR(handler, options) {
169
+ const { config } = options;
170
+ return async function qelosGetServerSideProps(context) {
171
+ const req = context.req;
172
+ const res = context.res;
173
+ if (shouldSkip(req, config)) {
174
+ return runWithQelosContext({ user: null, workspace: null, workspaces: [], sdk: null, tokens: {} }, () => handler(context, {
175
+ user: null,
176
+ workspace: null,
177
+ workspaces: [],
178
+ sdk: null,
179
+ tokens: {},
180
+ }));
181
+ }
182
+ const { ctx, identifyFailed } = await buildContext(req, res, options);
183
+ req.qelos = ctx;
184
+ if (config.requireAuth && (!ctx.user || identifyFailed)) {
185
+ return { redirect: { destination: '/login', permanent: false } };
186
+ }
187
+ return (await runWithQelosContext(ctx, () => handler(context, ctx)));
188
+ };
189
+ }
190
+ //# sourceMappingURL=pages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pages.js","sourceRoot":"","sources":["../src/pages.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import { type NextRequest } from 'next/server';
2
+ import { type GetQelosContextOptions } from './context';
3
+ import type { QelosRequestContext } from './types';
4
+ export type AppRouteHandler<TContext = unknown> = (req: NextRequest, context: TContext) => Response | Promise<Response>;
5
+ export type QelosAppRouteHandler<TContext = unknown> = (req: NextRequest, context: TContext, qelos: QelosRequestContext) => Response | Promise<Response>;
6
+ /**
7
+ * Wrap an App Router route handler (e.g. `app/api/foo/route.ts`) so that the
8
+ * Qelos context is resolved and made available both as a third argument and
9
+ * via `getStoredQelosContext()` from anywhere in the handler's async stack.
10
+ * Anonymous requests pass through with `qelos.user = null` unless
11
+ * `options.config.requireAuth` is set.
12
+ *
13
+ * Omit `options` to use the env-derived default config.
14
+ */
15
+ export declare function withQelosRoute<TContext = unknown>(handler: QelosAppRouteHandler<TContext>, options?: GetQelosContextOptions): AppRouteHandler<TContext>;
package/dist/route.js ADDED
@@ -0,0 +1,21 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { getQelosContext, runWithQelosContext, } from './context';
3
+ /**
4
+ * Wrap an App Router route handler (e.g. `app/api/foo/route.ts`) so that the
5
+ * Qelos context is resolved and made available both as a third argument and
6
+ * via `getStoredQelosContext()` from anywhere in the handler's async stack.
7
+ * Anonymous requests pass through with `qelos.user = null` unless
8
+ * `options.config.requireAuth` is set.
9
+ *
10
+ * Omit `options` to use the env-derived default config.
11
+ */
12
+ export function withQelosRoute(handler, options) {
13
+ return async function qelosRouteHandler(req, ctx) {
14
+ const qelos = await getQelosContext(options);
15
+ if (options?.config.requireAuth && !qelos.user) {
16
+ return NextResponse.json({ code: 'UNAUTHORIZED' }, { status: 401 });
17
+ }
18
+ return await runWithQelosContext(qelos, () => Promise.resolve(handler(req, ctx, qelos)));
19
+ };
20
+ }
21
+ //# sourceMappingURL=route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route.js","sourceRoot":"","sources":["../src/route.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAoB,MAAM,aAAa,CAAC;AAC7D,OAAO,EACL,eAAe,EACf,mBAAmB,GAEpB,MAAM,WAAW,CAAC;AAcnB;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAuC,EACvC,OAAgC;IAEhC,OAAO,KAAK,UAAU,iBAAiB,CAAC,GAAG,EAAE,GAAG;QAC9C,MAAM,KAAK,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,OAAO,EAAE,MAAM,CAAC,WAAW,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YAC/C,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,MAAM,mBAAmB,CAAC,KAAK,EAAE,GAAG,EAAE,CAC3C,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,CAC9B,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,20 @@
1
+ import QelosSDK from '@qelos/sdk';
2
+ import type { QelosNextConfig, QelosTokenPair, TokenRefreshHook } from './types';
3
+ export interface CreateSdkParams<TRefreshTarget = unknown> {
4
+ config: QelosNextConfig;
5
+ /**
6
+ * Tokens for the current request. The factory mutates this object in place
7
+ * when a token refresh occurs, so callers can read the latest pair after
8
+ * the SDK has been used.
9
+ */
10
+ tokens: QelosTokenPair;
11
+ /**
12
+ * Transport-specific carrier passed through to `onTokenRefresh`. Edge
13
+ * middleware passes the outbound `NextResponse`; Pages Router passes the
14
+ * `NextApiResponse` / `ServerResponse`; App Router server components pass
15
+ * `null` because they cannot mutate cookies after rendering starts.
16
+ */
17
+ refreshTarget: TRefreshTarget;
18
+ onTokenRefresh?: TokenRefreshHook<TRefreshTarget>;
19
+ }
20
+ export declare function createRequestSdk<TRefreshTarget = unknown>({ config, tokens, refreshTarget, onTokenRefresh, }: CreateSdkParams<TRefreshTarget>): QelosSDK;
@@ -0,0 +1,90 @@
1
+ import QelosSDK from '@qelos/sdk';
2
+ const NO_AUTH_URLS = new Set([
3
+ '/api/token/refresh',
4
+ '/api/cookie/refresh',
5
+ '/api/signin',
6
+ '/api/signup',
7
+ ]);
8
+ export function createRequestSdk({ config, tokens, refreshTarget, onTokenRefresh, }) {
9
+ let sdk;
10
+ let refreshInFlight = null;
11
+ const baseOptions = config.sdkOptions || {};
12
+ async function performRefresh() {
13
+ if (!tokens.refreshToken && !tokens.accessToken) {
14
+ throw new Error('no refresh token available');
15
+ }
16
+ const previous = {
17
+ accessToken: tokens.accessToken,
18
+ refreshToken: tokens.refreshToken,
19
+ };
20
+ let refreshed;
21
+ if (tokens.refreshToken) {
22
+ const result = await sdk.authentication.refreshToken(tokens.refreshToken);
23
+ refreshed = {
24
+ accessToken: result.payload.token,
25
+ refreshToken: result.payload.refreshToken,
26
+ };
27
+ }
28
+ else {
29
+ const result = await sdk.authentication.refreshCookieToken(tokens.accessToken);
30
+ refreshed = {
31
+ accessToken: result.payload.cookieToken,
32
+ };
33
+ }
34
+ tokens.accessToken = refreshed.accessToken;
35
+ tokens.refreshToken = refreshed.refreshToken;
36
+ if (onTokenRefresh) {
37
+ await onTokenRefresh({
38
+ target: refreshTarget,
39
+ oldTokens: previous,
40
+ newTokens: refreshed,
41
+ sdk,
42
+ });
43
+ }
44
+ }
45
+ function ensureRefresh() {
46
+ if (!refreshInFlight) {
47
+ refreshInFlight = performRefresh().finally(() => {
48
+ refreshInFlight = null;
49
+ });
50
+ }
51
+ return refreshInFlight;
52
+ }
53
+ const options = {
54
+ appUrl: config.appUrl,
55
+ fetch: globalThis.fetch.bind(globalThis),
56
+ forceRefresh: !config.apiToken,
57
+ ...baseOptions,
58
+ };
59
+ if (config.apiToken) {
60
+ options.apiToken = config.apiToken;
61
+ }
62
+ else {
63
+ if (tokens.accessToken) {
64
+ options.accessToken = tokens.accessToken;
65
+ }
66
+ if (tokens.refreshToken) {
67
+ options.refreshToken = tokens.refreshToken;
68
+ }
69
+ options.extraHeaders = async (relativeUrl, forceRefresh) => {
70
+ const headers = {};
71
+ if (NO_AUTH_URLS.has(relativeUrl)) {
72
+ return headers;
73
+ }
74
+ if (forceRefresh && (tokens.refreshToken || tokens.accessToken)) {
75
+ await ensureRefresh();
76
+ }
77
+ const token = sdk?.authentication?.accessToken || tokens.accessToken;
78
+ if (token) {
79
+ headers.authorization = 'Bearer ' + token;
80
+ }
81
+ return headers;
82
+ };
83
+ options.onFailedRefreshToken = async () => {
84
+ await ensureRefresh();
85
+ };
86
+ }
87
+ sdk = new QelosSDK(options);
88
+ return sdk;
89
+ }
90
+ //# sourceMappingURL=sdk-factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-factory.js","sourceRoot":"","sources":["../src/sdk-factory.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type QelosSDK from '@qelos/sdk';
2
+ import type { SocialAuthCallbackPayload } from '@qelos/sdk';
3
+ /**
4
+ * Runs {@link QelosSDK.authentication.socialCallback} for `requestUrl` and
5
+ * forwards session `Set-Cookie` headers to `res` (Next.js Pages `NextApiResponse`,
6
+ * App Route handlers using `NextResponse` should set cookies via the Response API
7
+ * instead — use the SDK helpers from `@qelos/sdk` directly there).
8
+ */
9
+ export declare function completeSocialAuthCallback(sdk: QelosSDK, requestUrl: string, res: {
10
+ setHeader(name: string, value: string | string[]): void;
11
+ }): Promise<SocialAuthCallbackPayload>;
12
+ export { applySocialAuthCookiesToServerResponse, getSocialAuthSetCookieParts, parseSocialCallbackRefreshToken, type SocialAuthCallbackPayload, type SocialCallbackInput, } from '@qelos/sdk';