@geekapps/auth-nextjs 0.22.0 → 0.25.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 +257 -210
- package/dist/actions.js +1 -10
- package/dist/actions.js.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/connections.d.ts +102 -9
- package/dist/connections.js +321 -36
- package/dist/connections.js.map +1 -1
- package/dist/middleware.js +47 -26
- package/dist/middleware.js.map +1 -1
- package/dist/server.d.ts +20 -2
- package/dist/server.js +65 -18
- package/dist/server.js.map +1 -1
- package/dist/typegen.d.ts +50 -11
- package/dist/typegen.js +126 -19
- package/dist/typegen.js.map +1 -1
- package/package.json +69 -68
package/dist/connections.js
CHANGED
|
@@ -14,27 +14,117 @@ __export(typegen_exports, {
|
|
|
14
14
|
DEFAULT_ISSUER: () => DEFAULT_ISSUER,
|
|
15
15
|
DEFAULT_OUT_DIR: () => DEFAULT_OUT_DIR,
|
|
16
16
|
fetchCatalog: () => fetchCatalog,
|
|
17
|
-
|
|
17
|
+
fetchManifest: () => fetchManifest,
|
|
18
|
+
generateConnectionTypes: () => generateConnectionTypes,
|
|
19
|
+
generateManifestTypes: () => generateManifestTypes
|
|
18
20
|
});
|
|
19
21
|
import { mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync, existsSync } from "fs";
|
|
20
22
|
import { join, resolve } from "path";
|
|
21
|
-
async function fetchCatalog(
|
|
22
|
-
const
|
|
23
|
-
|
|
23
|
+
async function fetchCatalog(credentials) {
|
|
24
|
+
const issuer = credentials.issuer.replace(/\/$/, "");
|
|
25
|
+
const asUser = "accessToken" in credentials;
|
|
26
|
+
const response = await fetch(`${issuer}${asUser ? "/me/connections/catalog" : "/connections/catalog"}`, {
|
|
27
|
+
headers: {
|
|
28
|
+
authorization: asUser ? `Bearer ${credentials.accessToken}` : `Basic ${Buffer.from(`${credentials.clientId}:${credentials.clientSecret}`).toString("base64")}`
|
|
29
|
+
},
|
|
24
30
|
cache: "no-store"
|
|
25
31
|
});
|
|
26
32
|
if (!response.ok) {
|
|
27
|
-
throw new Error(
|
|
33
|
+
throw new Error(
|
|
34
|
+
asUser ? `cat\xE1logo indispon\xEDvel (${response.status}) \u2014 rode \`geekapps login\` de novo` : `cat\xE1logo indispon\xEDvel (${response.status}) \u2014 confira GEEKAPPS_AUTH_CLIENT_ID e GEEKAPPS_AUTH_CLIENT_SECRET`
|
|
35
|
+
);
|
|
28
36
|
}
|
|
29
37
|
return await response.json();
|
|
30
38
|
}
|
|
39
|
+
async function fetchManifest(manifestUrl) {
|
|
40
|
+
const response = await fetch(manifestUrl, { headers: { accept: "application/json" }, cache: "no-store" });
|
|
41
|
+
if (!response.ok) throw new Error(`manifesto indispon\xEDvel (${response.status})`);
|
|
42
|
+
const manifest = await response.json();
|
|
43
|
+
if (manifest?.version !== 1 || !manifest.scopes || !Array.isArray(manifest.routes)) {
|
|
44
|
+
throw new Error("manifesto em formato inv\xE1lido");
|
|
45
|
+
}
|
|
46
|
+
return manifest;
|
|
47
|
+
}
|
|
48
|
+
function pascal(name) {
|
|
49
|
+
return name.split(/[^a-zA-Z0-9]+/).filter(Boolean).map((part) => part[0].toUpperCase() + part.slice(1)).join("");
|
|
50
|
+
}
|
|
51
|
+
function properties(schema) {
|
|
52
|
+
return schema?.properties ?? {};
|
|
53
|
+
}
|
|
54
|
+
function required(schema) {
|
|
55
|
+
return Array.isArray(schema?.required) ? schema.required : [];
|
|
56
|
+
}
|
|
57
|
+
function mergeInputSchema(route) {
|
|
58
|
+
const parts = [route.params, route.query, route.body];
|
|
59
|
+
const pathParams = [...route.path.matchAll(/:([A-Za-z0-9_]+)/g)].map((m) => m[1]);
|
|
60
|
+
return {
|
|
61
|
+
type: "object",
|
|
62
|
+
properties: Object.assign({}, ...parts.map(properties)),
|
|
63
|
+
required: [.../* @__PURE__ */ new Set([...pathParams, ...parts.flatMap(required)])],
|
|
64
|
+
additionalProperties: false
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
async function generateManifestTypes(app, manifest) {
|
|
68
|
+
const { compile } = await import("json-schema-to-typescript");
|
|
69
|
+
const options = { bannerComment: "", additionalProperties: false, format: false, declareExternallyReferenced: true };
|
|
70
|
+
const blocks = [];
|
|
71
|
+
const routeEntries = [];
|
|
72
|
+
for (const route of manifest.routes) {
|
|
73
|
+
const base = pascal(route.route);
|
|
74
|
+
const input = `${base}Input`;
|
|
75
|
+
const output = `${base}Output`;
|
|
76
|
+
blocks.push(await compile(mergeInputSchema(route), input, options));
|
|
77
|
+
blocks.push(
|
|
78
|
+
route.response ? await compile({ ...route.response, title: void 0 }, output, options) : `export type ${output} = unknown;
|
|
79
|
+
`
|
|
80
|
+
);
|
|
81
|
+
routeEntries.push(
|
|
82
|
+
` ${JSON.stringify(route.route)}: {
|
|
83
|
+
scope: ${JSON.stringify(route.scope)};
|
|
84
|
+
method: ${JSON.stringify(route.method)};
|
|
85
|
+
path: ${JSON.stringify(route.path)};
|
|
86
|
+
input: ${input};
|
|
87
|
+
output: ${output};
|
|
88
|
+
};`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
const scopeIds = Object.keys(manifest.scopes);
|
|
92
|
+
const scopeDocs = scopeIds.map((id) => ` ${JSON.stringify(id)}: ${JSON.stringify(manifest.scopes[id].description)};`).join("\n");
|
|
93
|
+
return `${HEADER}/** Escopos que esta app aceita em conex\xF5es. */
|
|
94
|
+
export type Scope = ${scopeIds.length ? scopeIds.map((id) => JSON.stringify(id)).join(" | ") : "never"};
|
|
95
|
+
|
|
96
|
+
/** Descri\xE7\xE3o de cada escopo, como aparece no consent. */
|
|
97
|
+
export interface ScopeDescriptions {
|
|
98
|
+
${scopeDocs}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Rotas liberadas por escopo, pelo nome usado no cliente (\`post.messages.send(...)\`). */
|
|
102
|
+
export interface Routes {
|
|
103
|
+
${routeEntries.join("\n")}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export interface App {
|
|
107
|
+
slug: ${JSON.stringify(app.slug)};
|
|
108
|
+
targetAppId: ${JSON.stringify(app.targetAppId)};
|
|
109
|
+
scopes: Scope;
|
|
110
|
+
routes: Routes;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
${blocks.join("\n")}`;
|
|
114
|
+
}
|
|
31
115
|
async function generateOpenApiTypes(openApiUrl) {
|
|
32
116
|
const { default: openapiTS, astToString } = await import("openapi-typescript");
|
|
33
117
|
return HEADER + astToString(await openapiTS(new URL(openApiUrl)));
|
|
34
118
|
}
|
|
35
119
|
function renderIndex(apps, augment) {
|
|
36
|
-
const imports = apps.
|
|
37
|
-
|
|
120
|
+
const imports = apps.flatMap(({ app, manifest, openApi }, i) => [
|
|
121
|
+
manifest ? `import type { App as App${i} } from "./${app.slug}";` : null,
|
|
122
|
+
openApi ? `import type { paths as paths${i} } from "./${app.slug}.openapi";` : null
|
|
123
|
+
]).filter(Boolean).join("\n");
|
|
124
|
+
const members = apps.map(({ app, manifest, openApi }, i) => {
|
|
125
|
+
const fields = [manifest ? `app: App${i};` : null, openApi ? `paths: paths${i};` : null].filter(Boolean).join(" ");
|
|
126
|
+
return ` ${JSON.stringify(app.slug)}: { ${fields} };`;
|
|
127
|
+
}).join("\n");
|
|
38
128
|
return `${HEADER}${imports}
|
|
39
129
|
|
|
40
130
|
declare module ${JSON.stringify(augment)} {
|
|
@@ -52,22 +142,38 @@ async function generateConnectionTypes(params) {
|
|
|
52
142
|
const files = /* @__PURE__ */ new Map();
|
|
53
143
|
const result = { apps: [], failed: [], stale: [] };
|
|
54
144
|
const generated = [];
|
|
145
|
+
const keepExisting = (file) => {
|
|
146
|
+
const existing = join(outDir, file);
|
|
147
|
+
if (!existsSync(existing)) return false;
|
|
148
|
+
files.set(file, readFileSync(existing, "utf-8"));
|
|
149
|
+
return true;
|
|
150
|
+
};
|
|
55
151
|
for (const app of catalog) {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
generated.push(app);
|
|
152
|
+
const entry = { app, manifest: false, openApi: false };
|
|
153
|
+
if (app.manifestUrl) {
|
|
154
|
+
const file = `${app.slug}.d.ts`;
|
|
155
|
+
try {
|
|
156
|
+
files.set(file, await generateManifestTypes(app, await fetchManifest(app.manifestUrl)));
|
|
157
|
+
entry.manifest = true;
|
|
158
|
+
} catch (error) {
|
|
159
|
+
result.failed.push({ name: app.name, reason: `manifesto: ${error.message}` });
|
|
160
|
+
entry.manifest = keepExisting(file);
|
|
66
161
|
}
|
|
67
162
|
}
|
|
163
|
+
if (app.openApiUrl) {
|
|
164
|
+
const file = `${app.slug}.openapi.d.ts`;
|
|
165
|
+
try {
|
|
166
|
+
files.set(file, await generateOpenApiTypes(app.openApiUrl));
|
|
167
|
+
entry.openApi = true;
|
|
168
|
+
} catch (error) {
|
|
169
|
+
result.failed.push({ name: app.name, reason: `openapi: ${error.message}` });
|
|
170
|
+
entry.openApi = keepExisting(file);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (entry.manifest || entry.openApi) generated.push(entry);
|
|
68
174
|
}
|
|
69
|
-
result.apps = generated.map((app) => app.name);
|
|
70
|
-
files.set("index.d.ts", renderIndex(generated, params.augment ??
|
|
175
|
+
result.apps = generated.map(({ app }) => app.name);
|
|
176
|
+
files.set("index.d.ts", renderIndex(generated, params.augment ?? DEFAULT_AUGMENT));
|
|
71
177
|
const current = existsSync(outDir) ? new Set(readdirSync(outDir).filter((f) => f.endsWith(".d.ts"))) : /* @__PURE__ */ new Set();
|
|
72
178
|
if (params.check) {
|
|
73
179
|
for (const [file, content] of files) {
|
|
@@ -85,12 +191,13 @@ async function generateConnectionTypes(params) {
|
|
|
85
191
|
for (const file of current) if (!files.has(file)) rmSync(join(outDir, file));
|
|
86
192
|
return result;
|
|
87
193
|
}
|
|
88
|
-
var DEFAULT_ISSUER, DEFAULT_OUT_DIR, HEADER;
|
|
194
|
+
var DEFAULT_ISSUER, DEFAULT_OUT_DIR, DEFAULT_AUGMENT, HEADER;
|
|
89
195
|
var init_typegen = __esm({
|
|
90
196
|
"src/typegen.ts"() {
|
|
91
197
|
"use strict";
|
|
92
198
|
DEFAULT_ISSUER = "https://accounts-api.geekapps.com.br";
|
|
93
199
|
DEFAULT_OUT_DIR = "geekapps-connections";
|
|
200
|
+
DEFAULT_AUGMENT = "@geekapps/auth-nextjs/connections";
|
|
94
201
|
HEADER = "// Gerado pelo SDK do Geekapps Auth. N\xE3o edite \xE0 m\xE3o.\n\n";
|
|
95
202
|
}
|
|
96
203
|
});
|
|
@@ -112,15 +219,41 @@ var GEEKAPPS_AUTH_SERVER_URL = "https://accounts-api.geekapps.com.br";
|
|
|
112
219
|
var AppConnectionError = class extends Error {
|
|
113
220
|
status;
|
|
114
221
|
code;
|
|
222
|
+
/** Código de erro como o servidor mandou (ex: "connection_inactive", "invalid_state"). */
|
|
223
|
+
reason;
|
|
224
|
+
/** Escopo que faltou, quando `code` é "missing_scope". */
|
|
225
|
+
scope;
|
|
115
226
|
body;
|
|
116
|
-
constructor(message, status,
|
|
227
|
+
constructor(message, status, reason, body) {
|
|
117
228
|
super(message);
|
|
118
229
|
this.name = "AppConnectionError";
|
|
119
230
|
this.status = status;
|
|
120
|
-
this.
|
|
231
|
+
this.reason = reason;
|
|
232
|
+
this.code = toErrorCode(status, reason);
|
|
121
233
|
this.body = body;
|
|
234
|
+
this.scope = body && typeof body === "object" && typeof body.scope === "string" ? body.scope : void 0;
|
|
122
235
|
}
|
|
123
236
|
};
|
|
237
|
+
function toErrorCode(status, reason) {
|
|
238
|
+
switch (reason) {
|
|
239
|
+
case "missing_scope":
|
|
240
|
+
case "app_connection_not_allowed":
|
|
241
|
+
return "missing_scope";
|
|
242
|
+
case "connection_expired":
|
|
243
|
+
return "connection_expired";
|
|
244
|
+
case "app_not_allowed":
|
|
245
|
+
return "app_not_allowed";
|
|
246
|
+
case "connection_inactive":
|
|
247
|
+
case "connection_revoked":
|
|
248
|
+
case "invalid_token":
|
|
249
|
+
case "membership_required":
|
|
250
|
+
return "connection_revoked";
|
|
251
|
+
case "network_error":
|
|
252
|
+
return "network_error";
|
|
253
|
+
}
|
|
254
|
+
if (status === 400 || status === 422) return "validation_error";
|
|
255
|
+
return "unknown";
|
|
256
|
+
}
|
|
124
257
|
function env(name) {
|
|
125
258
|
const value = process.env[name];
|
|
126
259
|
return value && value.trim().length > 0 ? value : void 0;
|
|
@@ -154,6 +287,7 @@ async function connectApp(params) {
|
|
|
154
287
|
if (!clientId) {
|
|
155
288
|
throw new Error("App Connections: defina GEEKAPPS_AUTH_CLIENT_ID \u2014 \xE9 o client_id desta app, a que pede a conex\xE3o.");
|
|
156
289
|
}
|
|
290
|
+
const targetAppId = params.targetAppId ?? await resolveAppSlug(params.app, params.config ?? {});
|
|
157
291
|
const codeVerifier = randomBytes2(64).toString("base64url");
|
|
158
292
|
const state = randomBytes2(24).toString("base64url");
|
|
159
293
|
const store = await cookies2();
|
|
@@ -170,7 +304,10 @@ async function connectApp(params) {
|
|
|
170
304
|
url.searchParams.set("state", state);
|
|
171
305
|
url.searchParams.set("code_challenge", createHash2("sha256").update(codeVerifier).digest("base64url"));
|
|
172
306
|
url.searchParams.set("code_challenge_method", "S256");
|
|
173
|
-
url.searchParams.set("target_app_id",
|
|
307
|
+
url.searchParams.set("target_app_id", targetAppId);
|
|
308
|
+
if (params.scopes && params.scopes.length > 0) {
|
|
309
|
+
url.searchParams.set("connection_scopes", [...new Set(params.scopes)].join(" "));
|
|
310
|
+
}
|
|
174
311
|
url.searchParams.set("prompt", "select_account consent");
|
|
175
312
|
if (params.displayName) url.searchParams.set("connection_display_name", params.displayName);
|
|
176
313
|
redirect2(url.toString());
|
|
@@ -222,6 +359,21 @@ async function completeAppConnection(params) {
|
|
|
222
359
|
}
|
|
223
360
|
};
|
|
224
361
|
}
|
|
362
|
+
var _catalogCache = {
|
|
363
|
+
value: null,
|
|
364
|
+
fetchedAt: 0
|
|
365
|
+
};
|
|
366
|
+
async function resolveAppSlug(slug, config) {
|
|
367
|
+
if (!_catalogCache.value || Date.now() - _catalogCache.fetchedAt > 5 * 6e4) {
|
|
368
|
+
const { issuer, clientId, clientSecret } = requireCredentials(config);
|
|
369
|
+
const { fetchCatalog: fetchCatalog2 } = await Promise.resolve().then(() => (init_typegen(), typegen_exports));
|
|
370
|
+
_catalogCache.value = await fetchCatalog2({ issuer, clientId, clientSecret });
|
|
371
|
+
_catalogCache.fetchedAt = Date.now();
|
|
372
|
+
}
|
|
373
|
+
const app = _catalogCache.value.find((item) => item.slug === slug);
|
|
374
|
+
if (!app) throw new Error(`App Connections: a app "${slug}" n\xE3o est\xE1 no cat\xE1logo do Geekapps Auth.`);
|
|
375
|
+
return app.targetAppId;
|
|
376
|
+
}
|
|
225
377
|
var _jwtCache = /* @__PURE__ */ new Map();
|
|
226
378
|
async function postToAuthServer(path, connectionToken, config) {
|
|
227
379
|
const { issuer, clientId, clientSecret } = requireCredentials(config);
|
|
@@ -309,29 +461,162 @@ async function rotateConnectionToken(params) {
|
|
|
309
461
|
_jwtCache.delete(params.connectionToken);
|
|
310
462
|
return { token: data.connection_token, expiresAt: data.expires_at };
|
|
311
463
|
}
|
|
312
|
-
var
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
464
|
+
var MANIFEST_PATH = "/.well-known/geekapps-app.json";
|
|
465
|
+
var MANIFEST_TTL_MS = 5 * 6e4;
|
|
466
|
+
var _manifestCache = /* @__PURE__ */ new Map();
|
|
467
|
+
async function getRuntimeManifest(baseUrl, force = false) {
|
|
468
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${MANIFEST_PATH}`;
|
|
469
|
+
const cached = _manifestCache.get(url);
|
|
470
|
+
if (cached && !force && Date.now() - cached.fetchedAt < MANIFEST_TTL_MS) return cached.value;
|
|
316
471
|
try {
|
|
317
|
-
const {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
});
|
|
322
|
-
|
|
472
|
+
const response = await fetch(url, { headers: { accept: "application/json" }, cache: "no-store" });
|
|
473
|
+
if (!response.ok) throw new Error(`status ${response.status}`);
|
|
474
|
+
const value = await response.json();
|
|
475
|
+
if (value?.version !== 1 || !Array.isArray(value.routes)) throw new Error("formato inv\xE1lido");
|
|
476
|
+
_manifestCache.set(url, { value, fetchedAt: Date.now() });
|
|
477
|
+
return value;
|
|
478
|
+
} catch (error) {
|
|
479
|
+
if (cached) return cached.value;
|
|
480
|
+
throw new AppConnectionError(
|
|
481
|
+
`App Connections: manifesto da app alvo indispon\xEDvel em ${url} (${error.message})`,
|
|
482
|
+
0,
|
|
483
|
+
"network_error",
|
|
484
|
+
null
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
function propertyNames(schema) {
|
|
489
|
+
const properties2 = schema?.properties;
|
|
490
|
+
return properties2 && typeof properties2 === "object" ? Object.keys(properties2) : [];
|
|
491
|
+
}
|
|
492
|
+
function appendQuery(url, key, value) {
|
|
493
|
+
if (value === void 0) return;
|
|
494
|
+
if (Array.isArray(value)) {
|
|
495
|
+
for (const item of value) appendQuery(url, key, item);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
url.searchParams.append(key, value !== null && typeof value === "object" ? JSON.stringify(value) : String(value));
|
|
499
|
+
}
|
|
500
|
+
function buildRequest(baseUrl, route, input) {
|
|
501
|
+
const rest = { ...input };
|
|
502
|
+
const path = route.path.replace(/:([A-Za-z0-9_]+)/g, (_, name) => {
|
|
503
|
+
const value = rest[name];
|
|
504
|
+
delete rest[name];
|
|
505
|
+
if (value === void 0 || value === null || value === "") {
|
|
506
|
+
throw new AppConnectionError(`App Connections: falta o par\xE2metro "${name}" em ${route.route}.`, 0, "validation_error", null);
|
|
507
|
+
}
|
|
508
|
+
return encodeURIComponent(String(value));
|
|
509
|
+
});
|
|
510
|
+
const url = new URL(path.replace(/^\//, ""), `${baseUrl.replace(/\/+$/, "")}/`);
|
|
511
|
+
const method = route.method.toUpperCase();
|
|
512
|
+
const bodyless = method === "GET" || method === "DELETE" || method === "HEAD";
|
|
513
|
+
const queryFields = new Set(propertyNames(route.query));
|
|
514
|
+
const body = {};
|
|
515
|
+
for (const [key, value] of Object.entries(rest)) {
|
|
516
|
+
if (bodyless || queryFields.has(key)) appendQuery(url, key, value);
|
|
517
|
+
else if (value !== void 0) body[key] = value;
|
|
518
|
+
}
|
|
519
|
+
const hasBody = !bodyless && (route.body !== null || Object.keys(body).length > 0);
|
|
520
|
+
return { url, method, body: hasBody ? JSON.stringify(body) : void 0 };
|
|
521
|
+
}
|
|
522
|
+
async function accessTokenFor(connectionToken, config) {
|
|
523
|
+
try {
|
|
524
|
+
return await getConnectionAccessToken({ connectionToken, config });
|
|
525
|
+
} catch (error) {
|
|
526
|
+
if (error instanceof AppConnectionError) throw error;
|
|
527
|
+
throw new AppConnectionError(`App Connections: auth-server inacess\xEDvel (${error.message})`, 0, "network_error", null);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
async function callRoute(connectionToken, options, routeName, input = {}) {
|
|
531
|
+
const config = options.config ?? {};
|
|
532
|
+
let token = await accessTokenFor(connectionToken, config);
|
|
533
|
+
const baseUrl = options.baseUrl ?? token.targetApiBaseUrl;
|
|
534
|
+
if (!baseUrl) {
|
|
535
|
+
throw new AppConnectionError("App Connections: a app alvo n\xE3o declarou apiBaseUrl \u2014 passe `baseUrl`.", 0, "unknown", null);
|
|
536
|
+
}
|
|
537
|
+
let manifest = await getRuntimeManifest(baseUrl);
|
|
538
|
+
let route = manifest.routes.find((item) => item.route === routeName);
|
|
539
|
+
if (!route) {
|
|
540
|
+
manifest = await getRuntimeManifest(baseUrl, true);
|
|
541
|
+
route = manifest.routes.find((item) => item.route === routeName);
|
|
323
542
|
}
|
|
543
|
+
if (!route) {
|
|
544
|
+
throw new AppConnectionError(`App Connections: a app alvo n\xE3o tem a rota "${routeName}".`, 404, "route_not_found", null);
|
|
545
|
+
}
|
|
546
|
+
const request = buildRequest(baseUrl, route, input);
|
|
547
|
+
for (let attempt = 0; ; attempt++) {
|
|
548
|
+
let response;
|
|
549
|
+
try {
|
|
550
|
+
response = await fetch(request.url, {
|
|
551
|
+
method: request.method,
|
|
552
|
+
headers: {
|
|
553
|
+
authorization: `Bearer ${token.accessToken}`,
|
|
554
|
+
accept: "application/json",
|
|
555
|
+
...request.body !== void 0 && { "content-type": "application/json" }
|
|
556
|
+
},
|
|
557
|
+
body: request.body,
|
|
558
|
+
cache: "no-store"
|
|
559
|
+
});
|
|
560
|
+
} catch (error) {
|
|
561
|
+
throw new AppConnectionError(`App Connections: app alvo inacess\xEDvel (${error.message})`, 0, "network_error", null);
|
|
562
|
+
}
|
|
563
|
+
if (response.status === 401 && attempt === 0) {
|
|
564
|
+
_jwtCache.delete(connectionToken);
|
|
565
|
+
token = await accessTokenFor(connectionToken, config);
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
const text = await response.text();
|
|
569
|
+
let body = null;
|
|
570
|
+
try {
|
|
571
|
+
body = text ? JSON.parse(text) : null;
|
|
572
|
+
} catch {
|
|
573
|
+
body = text;
|
|
574
|
+
}
|
|
575
|
+
if (!response.ok) {
|
|
576
|
+
const reason = errorCode(body) ?? (response.status === 401 ? "connection_revoked" : void 0);
|
|
577
|
+
throw new AppConnectionError(
|
|
578
|
+
`App Connections: ${route.route} falhou (${response.status}${reason ? ` ${reason}` : ""})`,
|
|
579
|
+
response.status,
|
|
580
|
+
reason,
|
|
581
|
+
body
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
return body ?? void 0;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
function routeProxy(call, path) {
|
|
588
|
+
return new Proxy(function() {
|
|
589
|
+
}, {
|
|
590
|
+
get(_target, property) {
|
|
591
|
+
if (typeof property !== "string" || property === "then") return void 0;
|
|
592
|
+
return routeProxy(call, [...path, property]);
|
|
593
|
+
},
|
|
594
|
+
apply(_target, _this, args) {
|
|
595
|
+
if (path.length === 0) throw new Error("App Connections: chame uma rota, ex: post.messages.send({ ... }).");
|
|
596
|
+
return call(path.join("."), args[0]);
|
|
597
|
+
}
|
|
598
|
+
});
|
|
324
599
|
}
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
600
|
+
function createConnections(config = {}) {
|
|
601
|
+
return new Proxy({}, {
|
|
602
|
+
get(_target, slug) {
|
|
603
|
+
if (typeof slug !== "string" || slug === "then") return void 0;
|
|
604
|
+
return (connectionToken, options = {}) => routeProxy(
|
|
605
|
+
(route, input) => callRoute(connectionToken, { config, ...options }, route, input),
|
|
606
|
+
[]
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
328
610
|
}
|
|
611
|
+
var connections = createConnections();
|
|
329
612
|
export {
|
|
330
613
|
AppConnectionError,
|
|
331
614
|
completeAppConnection,
|
|
332
615
|
connectApp,
|
|
333
616
|
connectionFetch,
|
|
617
|
+
connections,
|
|
334
618
|
createConnectionClient,
|
|
619
|
+
createConnections,
|
|
335
620
|
getConnectionAccessToken,
|
|
336
621
|
rotateConnectionToken
|
|
337
622
|
};
|