@qelos/integrator-next 4.0.0 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +109 -164
- package/dist/context.d.ts +5 -11
- package/dist/context.js +63 -43
- package/dist/context.js.map +1 -1
- package/dist/cookies.d.ts +19 -0
- package/dist/cookies.js +39 -0
- package/dist/cookies.js.map +1 -0
- package/dist/env-config.d.ts +3 -3
- package/dist/env-config.js +6 -7
- package/dist/env-config.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/me-client.d.ts +15 -0
- package/dist/me-client.js +35 -0
- package/dist/me-client.js.map +1 -0
- package/dist/middleware.d.ts +10 -34
- package/dist/middleware.js +75 -80
- package/dist/middleware.js.map +1 -1
- package/dist/pages.d.ts +5 -9
- package/dist/pages.js +61 -98
- package/dist/pages.js.map +1 -1
- package/dist/proxy-target.d.ts +17 -0
- package/dist/proxy-target.js +26 -0
- package/dist/proxy-target.js.map +1 -0
- package/dist/runtime/api-proxy.d.ts +16 -0
- package/dist/runtime/api-proxy.js +90 -0
- package/dist/runtime/api-proxy.js.map +1 -0
- package/dist/sdk-factory.d.ts +13 -16
- package/dist/sdk-factory.js +29 -79
- package/dist/sdk-factory.js.map +1 -1
- package/dist/types.d.ts +28 -16
- package/dist/types.js.map +1 -1
- package/package.json +8 -3
package/dist/pages.js
CHANGED
|
@@ -1,54 +1,20 @@
|
|
|
1
|
+
import { rewriteSetCookieDomain } from './cookies';
|
|
2
|
+
import { fetchMeIdentity } from './me-client';
|
|
3
|
+
import { resolveQelosProxyTarget } from './proxy-target';
|
|
1
4
|
import { createRequestSdk } from './sdk-factory';
|
|
2
5
|
import { runWithQelosContext } from './context';
|
|
3
|
-
|
|
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);
|
|
6
|
+
function getAuthorization(req) {
|
|
32
7
|
const authHeaderRaw = req.headers.authorization;
|
|
33
8
|
const authHeader = Array.isArray(authHeaderRaw) ? authHeaderRaw[0] : authHeaderRaw;
|
|
34
|
-
|
|
35
|
-
? authHeader.slice(7).trim()
|
|
36
|
-
: undefined;
|
|
37
|
-
return {
|
|
38
|
-
accessToken: headerAccess || cookieAccess || undefined,
|
|
39
|
-
refreshToken: cookieRefresh || undefined,
|
|
40
|
-
};
|
|
9
|
+
return authHeader ?? undefined;
|
|
41
10
|
}
|
|
42
|
-
function
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
'
|
|
48
|
-
|
|
49
|
-
if (secure)
|
|
50
|
-
parts.push('Secure');
|
|
51
|
-
return parts.join('; ');
|
|
11
|
+
function getCookieHeader(req) {
|
|
12
|
+
const raw = req.headers.cookie;
|
|
13
|
+
if (typeof raw === 'string')
|
|
14
|
+
return raw;
|
|
15
|
+
if (Array.isArray(raw))
|
|
16
|
+
return raw.join('; ');
|
|
17
|
+
return undefined;
|
|
52
18
|
}
|
|
53
19
|
function appendSetCookie(res, value) {
|
|
54
20
|
const existing = res.getHeader('set-cookie');
|
|
@@ -62,75 +28,71 @@ function appendSetCookie(res, value) {
|
|
|
62
28
|
res.setHeader('set-cookie', [value]);
|
|
63
29
|
}
|
|
64
30
|
}
|
|
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
31
|
function shouldSkip(req, config) {
|
|
75
32
|
if (!config.skipPaths?.length)
|
|
76
33
|
return false;
|
|
77
34
|
const path = req.url || '';
|
|
78
35
|
return config.skipPaths.some((prefix) => path.startsWith(prefix));
|
|
79
36
|
}
|
|
37
|
+
function requestHost(req) {
|
|
38
|
+
const h = req.headers.host;
|
|
39
|
+
return Array.isArray(h) ? h[0] ?? null : h ?? null;
|
|
40
|
+
}
|
|
80
41
|
async function buildContext(req, res, options) {
|
|
81
42
|
const { config, resolveWorkspace } = options;
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
});
|
|
86
|
-
const tokens = readTokens(req, config);
|
|
87
|
-
const sdk = createRequestSdk({
|
|
88
|
-
config,
|
|
89
|
-
tokens,
|
|
90
|
-
refreshTarget: res,
|
|
91
|
-
onTokenRefresh,
|
|
43
|
+
const getHeaders = () => ({
|
|
44
|
+
cookie: getCookieHeader(req),
|
|
45
|
+
authorization: getAuthorization(req),
|
|
92
46
|
});
|
|
47
|
+
const sdk = createRequestSdk({ config, getHeaders });
|
|
93
48
|
const ctx = {
|
|
94
49
|
user: null,
|
|
95
50
|
workspace: null,
|
|
96
51
|
workspaces: [],
|
|
97
52
|
sdk,
|
|
98
|
-
tokens,
|
|
99
53
|
};
|
|
100
|
-
const
|
|
101
|
-
if (!
|
|
102
|
-
return { ctx
|
|
54
|
+
const base = resolveQelosProxyTarget(config);
|
|
55
|
+
if (!base && !config.apiToken) {
|
|
56
|
+
return { ctx };
|
|
103
57
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
ctx.user = await sdk.authentication.getLoggedInUser();
|
|
58
|
+
if (!base) {
|
|
59
|
+
return { ctx };
|
|
107
60
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
61
|
+
const h = getHeaders();
|
|
62
|
+
const { response, user } = await fetchMeIdentity({
|
|
63
|
+
base,
|
|
64
|
+
cookie: h.cookie,
|
|
65
|
+
authorization: h.authorization,
|
|
66
|
+
});
|
|
67
|
+
const host = requestHost(req);
|
|
68
|
+
const setCookieValues = response.headers.getSetCookie?.() ?? [];
|
|
69
|
+
for (const value of setCookieValues) {
|
|
70
|
+
appendSetCookie(res, rewriteSetCookieDomain(value, host ?? undefined));
|
|
71
|
+
}
|
|
72
|
+
ctx.user = user;
|
|
73
|
+
if (!ctx.user) {
|
|
74
|
+
return { ctx };
|
|
111
75
|
}
|
|
112
|
-
if (!ctx.user)
|
|
113
|
-
return { ctx, identifyFailed };
|
|
114
76
|
try {
|
|
115
77
|
ctx.workspaces = await sdk.workspaces.getList();
|
|
116
78
|
}
|
|
117
79
|
catch {
|
|
118
80
|
ctx.workspaces = [];
|
|
119
81
|
}
|
|
120
|
-
if (
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
ctx.workspace
|
|
131
|
-
|
|
82
|
+
if (resolveWorkspace) {
|
|
83
|
+
ctx.workspace =
|
|
84
|
+
(await resolveWorkspace({
|
|
85
|
+
req,
|
|
86
|
+
user: ctx.user,
|
|
87
|
+
workspaces: ctx.workspaces,
|
|
88
|
+
})) || null;
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
ctx.workspace =
|
|
92
|
+
ctx.user.workspace ||
|
|
93
|
+
null;
|
|
132
94
|
}
|
|
133
|
-
return { ctx
|
|
95
|
+
return { ctx };
|
|
134
96
|
}
|
|
135
97
|
/**
|
|
136
98
|
* Wrap a Pages Router API route handler so that `req.qelos` is populated
|
|
@@ -143,9 +105,9 @@ export function withQelosApi(handler, options) {
|
|
|
143
105
|
if (shouldSkip(req, config)) {
|
|
144
106
|
return handler(req, res);
|
|
145
107
|
}
|
|
146
|
-
const { ctx
|
|
108
|
+
const { ctx } = await buildContext(req, res, options);
|
|
147
109
|
req.qelos = ctx;
|
|
148
|
-
if (config.requireAuth &&
|
|
110
|
+
if (config.requireAuth && !ctx.user) {
|
|
149
111
|
res.status(401).json({ code: 'UNAUTHORIZED' });
|
|
150
112
|
return;
|
|
151
113
|
}
|
|
@@ -171,17 +133,18 @@ export function withQelosSSR(handler, options) {
|
|
|
171
133
|
const req = context.req;
|
|
172
134
|
const res = context.res;
|
|
173
135
|
if (shouldSkip(req, config)) {
|
|
174
|
-
|
|
136
|
+
const emptySdk = null;
|
|
137
|
+
const emptyCtx = {
|
|
175
138
|
user: null,
|
|
176
139
|
workspace: null,
|
|
177
140
|
workspaces: [],
|
|
178
|
-
sdk:
|
|
179
|
-
|
|
180
|
-
|
|
141
|
+
sdk: emptySdk,
|
|
142
|
+
};
|
|
143
|
+
return runWithQelosContext(emptyCtx, () => handler(context, emptyCtx));
|
|
181
144
|
}
|
|
182
|
-
const { ctx
|
|
145
|
+
const { ctx } = await buildContext(req, res, options);
|
|
183
146
|
req.qelos = ctx;
|
|
184
|
-
if (config.requireAuth &&
|
|
147
|
+
if (config.requireAuth && !ctx.user) {
|
|
185
148
|
return { redirect: { destination: '/login', permanent: false } };
|
|
186
149
|
}
|
|
187
150
|
return (await runWithQelosContext(ctx, () => handler(context, ctx)));
|
package/dist/pages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../src/pages.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"pages.js","sourceRoot":"","sources":["../src/pages.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { QelosNextConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the origin to which `/api/**` requests are proxied. The managed
|
|
4
|
+
* Qelos app URL (`config.appUrl`) is the proxy target — env vars are only
|
|
5
|
+
* dev-time overrides when the configured `appUrl` is not reachable from
|
|
6
|
+
* the local host.
|
|
7
|
+
*
|
|
8
|
+
* Priority (highest first):
|
|
9
|
+
* 1. `process.env.NEXT_QELOS_PROXY_TARGET`
|
|
10
|
+
* 2. `process.env.QELOS_IP`
|
|
11
|
+
* 3. `process.env.QELOS_API_IP`
|
|
12
|
+
* 4. `config.appUrl`
|
|
13
|
+
*
|
|
14
|
+
* Empty / whitespace-only values are skipped, and `undefined` is returned when
|
|
15
|
+
* nothing is configured so the caller can surface a clear error.
|
|
16
|
+
*/
|
|
17
|
+
export declare function resolveQelosProxyTarget(config: QelosNextConfig): string | undefined;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the origin to which `/api/**` requests are proxied. The managed
|
|
3
|
+
* Qelos app URL (`config.appUrl`) is the proxy target — env vars are only
|
|
4
|
+
* dev-time overrides when the configured `appUrl` is not reachable from
|
|
5
|
+
* the local host.
|
|
6
|
+
*
|
|
7
|
+
* Priority (highest first):
|
|
8
|
+
* 1. `process.env.NEXT_QELOS_PROXY_TARGET`
|
|
9
|
+
* 2. `process.env.QELOS_IP`
|
|
10
|
+
* 3. `process.env.QELOS_API_IP`
|
|
11
|
+
* 4. `config.appUrl`
|
|
12
|
+
*
|
|
13
|
+
* Empty / whitespace-only values are skipped, and `undefined` is returned when
|
|
14
|
+
* nothing is configured so the caller can surface a clear error.
|
|
15
|
+
*/
|
|
16
|
+
export function resolveQelosProxyTarget(config) {
|
|
17
|
+
const env = process.env;
|
|
18
|
+
const fromEnv = env.NEXT_QELOS_PROXY_TARGET?.trim() ||
|
|
19
|
+
env.QELOS_IP?.trim() ||
|
|
20
|
+
env.QELOS_API_IP?.trim();
|
|
21
|
+
if (fromEnv)
|
|
22
|
+
return fromEnv;
|
|
23
|
+
const fromAppUrl = config.appUrl?.trim();
|
|
24
|
+
return fromAppUrl || undefined;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=proxy-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-target.js","sourceRoot":"","sources":["../src/proxy-target.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAuB;IAEvB,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IACxB,MAAM,OAAO,GACX,GAAG,CAAC,uBAAuB,EAAE,IAAI,EAAE;QACnC,GAAG,CAAC,QAAQ,EAAE,IAAI,EAAE;QACpB,GAAG,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;IAC3B,IAAI,OAAO;QAAE,OAAO,OAAO,CAAC;IAE5B,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC;IACzC,OAAO,UAAU,IAAI,SAAS,CAAC;AACjC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type NextRequest } from 'next/server';
|
|
2
|
+
import type { QelosNextConfig } from '../types';
|
|
3
|
+
/**
|
|
4
|
+
* Handlers for a catch-all App Router route (e.g. `app/api/[...qelos]/route.ts`)
|
|
5
|
+
* that reverse-proxies every `/api/**` call to Qelos with `Set-Cookie` domain
|
|
6
|
+
* rewriting. Use `export const runtime = 'nodejs'`.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createQelosApiProxyHandlers(config: QelosNextConfig): {
|
|
9
|
+
runtime: "nodejs";
|
|
10
|
+
GET: (req: NextRequest) => Promise<Response>;
|
|
11
|
+
POST: (req: NextRequest) => Promise<Response>;
|
|
12
|
+
PUT: (req: NextRequest) => Promise<Response>;
|
|
13
|
+
PATCH: (req: NextRequest) => Promise<Response>;
|
|
14
|
+
DELETE: (req: NextRequest) => Promise<Response>;
|
|
15
|
+
OPTIONS: (req: NextRequest) => Promise<Response>;
|
|
16
|
+
};
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
|
+
import { rewriteSetCookieDomain } from '../cookies';
|
|
3
|
+
import { resolveQelosProxyTarget } from '../proxy-target';
|
|
4
|
+
const HOP_BY_HOP = new Set([
|
|
5
|
+
'connection',
|
|
6
|
+
'keep-alive',
|
|
7
|
+
'proxy-authenticate',
|
|
8
|
+
'proxy-authorization',
|
|
9
|
+
'te',
|
|
10
|
+
'trailers',
|
|
11
|
+
'transfer-encoding',
|
|
12
|
+
'upgrade',
|
|
13
|
+
]);
|
|
14
|
+
function buildUpstreamHeaders(req) {
|
|
15
|
+
const out = new Headers();
|
|
16
|
+
req.headers.forEach((value, key) => {
|
|
17
|
+
const lk = key.toLowerCase();
|
|
18
|
+
if (HOP_BY_HOP.has(lk))
|
|
19
|
+
return;
|
|
20
|
+
if (lk === 'host')
|
|
21
|
+
return;
|
|
22
|
+
out.append(key, value);
|
|
23
|
+
});
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
async function proxyRequest(req, config) {
|
|
27
|
+
const base = resolveQelosProxyTarget(config);
|
|
28
|
+
if (!base) {
|
|
29
|
+
return NextResponse.json({
|
|
30
|
+
error: '[@qelos/integrator-next] Qelos API proxy is not configured. Set appUrl (or NEXT_QELOS_PROXY_TARGET / QELOS_IP / QELOS_API_IP in development).',
|
|
31
|
+
}, { status: 503 });
|
|
32
|
+
}
|
|
33
|
+
const pathWithSearch = req.nextUrl.pathname + req.nextUrl.search;
|
|
34
|
+
const targetUrl = `${base.replace(/\/+$/, '')}${pathWithSearch}`;
|
|
35
|
+
const originalHost = req.headers.get('host');
|
|
36
|
+
const init = {
|
|
37
|
+
method: req.method,
|
|
38
|
+
headers: buildUpstreamHeaders(req),
|
|
39
|
+
redirect: 'manual',
|
|
40
|
+
};
|
|
41
|
+
if (req.method !== 'GET' && req.method !== 'HEAD') {
|
|
42
|
+
init.body = req.body;
|
|
43
|
+
init.duplex = 'half';
|
|
44
|
+
}
|
|
45
|
+
let upstream;
|
|
46
|
+
try {
|
|
47
|
+
upstream = await fetch(targetUrl, init);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return NextResponse.json({ error: 'upstream fetch failed' }, { status: 502 });
|
|
51
|
+
}
|
|
52
|
+
const outHeaders = new Headers();
|
|
53
|
+
upstream.headers.forEach((value, key) => {
|
|
54
|
+
const lk = key.toLowerCase();
|
|
55
|
+
if (lk === 'set-cookie')
|
|
56
|
+
return;
|
|
57
|
+
if (HOP_BY_HOP.has(lk))
|
|
58
|
+
return;
|
|
59
|
+
outHeaders.append(key, value);
|
|
60
|
+
});
|
|
61
|
+
const rawCookies = upstream.headers.getSetCookie?.() ?? [];
|
|
62
|
+
for (const c of rawCookies) {
|
|
63
|
+
outHeaders.append('set-cookie', rewriteSetCookieDomain(c, originalHost ?? undefined));
|
|
64
|
+
}
|
|
65
|
+
return new Response(upstream.body, {
|
|
66
|
+
status: upstream.status,
|
|
67
|
+
statusText: upstream.statusText,
|
|
68
|
+
headers: outHeaders,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Handlers for a catch-all App Router route (e.g. `app/api/[...qelos]/route.ts`)
|
|
73
|
+
* that reverse-proxies every `/api/**` call to Qelos with `Set-Cookie` domain
|
|
74
|
+
* rewriting. Use `export const runtime = 'nodejs'`.
|
|
75
|
+
*/
|
|
76
|
+
export function createQelosApiProxyHandlers(config) {
|
|
77
|
+
async function handle(req) {
|
|
78
|
+
return proxyRequest(req, config);
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
runtime: 'nodejs',
|
|
82
|
+
GET: handle,
|
|
83
|
+
POST: handle,
|
|
84
|
+
PUT: handle,
|
|
85
|
+
PATCH: handle,
|
|
86
|
+
DELETE: handle,
|
|
87
|
+
OPTIONS: handle,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=api-proxy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-proxy.js","sourceRoot":"","sources":["../../src/runtime/api-proxy.ts"],"names":[],"mappings":"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"}
|
package/dist/sdk-factory.d.ts
CHANGED
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type { QelosNextConfig
|
|
3
|
-
export interface CreateSdkParams
|
|
1
|
+
import type QelosSDKInstance from '@qelos/sdk';
|
|
2
|
+
import type { QelosNextConfig } from './types';
|
|
3
|
+
export interface CreateSdkParams {
|
|
4
4
|
config: QelosNextConfig;
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* the SDK has been used.
|
|
6
|
+
* Live header source so every SDK call forwards the current request cookies
|
|
7
|
+
* and `Authorization` (no mutable token state).
|
|
9
8
|
*/
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
refreshTarget: TRefreshTarget;
|
|
18
|
-
onTokenRefresh?: TokenRefreshHook<TRefreshTarget>;
|
|
9
|
+
getHeaders: () => {
|
|
10
|
+
cookie?: string;
|
|
11
|
+
authorization?: string;
|
|
12
|
+
} | Promise<{
|
|
13
|
+
cookie?: string;
|
|
14
|
+
authorization?: string;
|
|
15
|
+
}>;
|
|
19
16
|
}
|
|
20
|
-
export declare function createRequestSdk
|
|
17
|
+
export declare function createRequestSdk({ config, getHeaders, }: CreateSdkParams): QelosSDKInstance;
|
package/dist/sdk-factory.js
CHANGED
|
@@ -1,90 +1,40 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
'
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
-
}
|
|
1
|
+
import sdkModule from '@qelos/sdk';
|
|
2
|
+
function resolveQelosSdkConstructor() {
|
|
3
|
+
const top = sdkModule;
|
|
4
|
+
if (typeof top === 'function') {
|
|
5
|
+
return top;
|
|
6
|
+
}
|
|
7
|
+
const once = top.default;
|
|
8
|
+
if (typeof once === 'function') {
|
|
9
|
+
return once;
|
|
44
10
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
refreshInFlight = null;
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
return refreshInFlight;
|
|
11
|
+
const twice = once?.default;
|
|
12
|
+
if (typeof twice === 'function') {
|
|
13
|
+
return twice;
|
|
52
14
|
}
|
|
15
|
+
throw new Error('[@qelos/integrator-next] Could not resolve QelosSDK constructor from @qelos/sdk');
|
|
16
|
+
}
|
|
17
|
+
const QelosSDK = resolveQelosSdkConstructor();
|
|
18
|
+
export function createRequestSdk({ config, getHeaders, }) {
|
|
19
|
+
const baseOptions = config.sdkOptions || {};
|
|
53
20
|
const options = {
|
|
54
21
|
appUrl: config.appUrl,
|
|
55
|
-
fetch: globalThis.fetch
|
|
56
|
-
forceRefresh: !config.apiToken,
|
|
22
|
+
fetch: globalThis.fetch,
|
|
57
23
|
...baseOptions,
|
|
58
24
|
};
|
|
59
25
|
if (config.apiToken) {
|
|
60
26
|
options.apiToken = config.apiToken;
|
|
27
|
+
return new QelosSDK(options);
|
|
61
28
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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;
|
|
29
|
+
options.extraHeaders = async () => {
|
|
30
|
+
const h = await getHeaders();
|
|
31
|
+
const headers = {};
|
|
32
|
+
if (h.cookie)
|
|
33
|
+
headers.cookie = h.cookie;
|
|
34
|
+
if (h.authorization)
|
|
35
|
+
headers.authorization = h.authorization;
|
|
36
|
+
return headers;
|
|
37
|
+
};
|
|
38
|
+
return new QelosSDK(options);
|
|
89
39
|
}
|
|
90
40
|
//# sourceMappingURL=sdk-factory.js.map
|
package/dist/sdk-factory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sdk-factory.js","sourceRoot":"","sources":["../src/sdk-factory.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"sdk-factory.js","sourceRoot":"","sources":["../src/sdk-factory.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,YAAY,CAAC;AAOnC,SAAS,0BAA0B;IACjC,MAAM,GAAG,GAAG,SAAoB,CAAC;IACjC,IAAI,OAAO,GAAG,KAAK,UAAU,EAAE,CAAC;QAC9B,OAAO,GAAmB,CAAC;IAC7B,CAAC;IACD,MAAM,IAAI,GAAI,GAA6B,CAAC,OAAO,CAAC;IACpD,IAAI,OAAO,IAAI,KAAK,UAAU,EAAE,CAAC;QAC/B,OAAO,IAAoB,CAAC;IAC9B,CAAC;IACD,MAAM,KAAK,GAAI,IAA0C,EAAE,OAAO,CAAC;IACnE,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,OAAO,KAAqB,CAAC;IAC/B,CAAC;IACD,MAAM,IAAI,KAAK,CACb,iFAAiF,CAClF,CAAC;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,0BAA0B,EAAE,CAAC;AAa9C,MAAM,UAAU,gBAAgB,CAAC,EAC/B,MAAM,EACN,UAAU,GACM;IAChB,MAAM,WAAW,GAA6B,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;IAEtE,MAAM,OAAO,GAAoB;QAC/B,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,KAAK,EAAE,UAAU,CAAC,KAA6B;QAC/C,GAAG,WAAW;KACf,CAAC;IAEF,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACpB,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;QACnC,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,CAAC;IAED,OAAO,CAAC,YAAY,GAAG,KAAK,IAAI,EAAE;QAChC,MAAM,CAAC,GAAG,MAAM,UAAU,EAAE,CAAC;QAC7B,MAAM,OAAO,GAA2B,EAAE,CAAC;QAC3C,IAAI,CAAC,CAAC,MAAM;YAAE,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC;QACxC,IAAI,CAAC,CAAC,aAAa;YAAE,OAAO,CAAC,aAAa,GAAG,CAAC,CAAC,aAAa,CAAC;QAC7D,OAAO,OAAO,CAAC;IACjB,CAAC,CAAC;IAEF,OAAO,IAAI,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC"}
|