runline 0.29.0 → 0.30.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/auth/errors.d.ts +22 -0
- package/dist/auth/errors.js +25 -0
- package/dist/auth/oauth2.d.ts +21 -0
- package/dist/auth/oauth2.js +154 -0
- package/dist/auth/token.d.ts +11 -0
- package/dist/auth/token.js +231 -0
- package/dist/auth/types.d.ts +93 -0
- package/dist/auth/types.js +1 -0
- package/dist/commands/auth.js +26 -13
- package/dist/commands/connection.js +3 -6
- package/dist/commands/exec.js +7 -2
- package/dist/commands/init.js +9 -2
- package/dist/config/loader.d.ts +7 -13
- package/dist/config/loader.js +58 -88
- package/dist/config/store.d.ts +17 -0
- package/dist/config/store.js +85 -0
- package/dist/connections/file.d.ts +13 -0
- package/dist/connections/file.js +53 -0
- package/dist/connections/memory.d.ts +15 -0
- package/dist/connections/memory.js +70 -0
- package/dist/connections/types.d.ts +47 -0
- package/dist/connections/types.js +10 -0
- package/dist/core/engine.d.ts +5 -2
- package/dist/core/engine.js +48 -55
- package/dist/core/oauth.d.ts +20 -15
- package/dist/core/oauth.js +195 -98
- package/dist/credentials/http.d.ts +19 -0
- package/dist/credentials/http.js +109 -0
- package/dist/credentials/policy.d.ts +5 -0
- package/dist/credentials/policy.js +92 -0
- package/dist/credentials/registry.d.ts +31 -0
- package/dist/credentials/registry.js +171 -0
- package/dist/credentials/transport.d.ts +38 -0
- package/dist/credentials/transport.js +283 -0
- package/dist/credentials/types.d.ts +80 -0
- package/dist/credentials/types.js +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +8 -0
- package/dist/plugin/types.d.ts +30 -17
- package/dist/plugins/_shared/credentialAdapter.js +88 -0
- package/dist/plugins/_shared/googleAuth.js +89 -103
- package/dist/plugins/_shared/googleCredentials.js +160 -0
- package/dist/plugins/_shared/microsoftAuth.js +81 -74
- package/dist/plugins/_shared/microsoftCredentials.js +90 -0
- package/dist/plugins/_shared/tokenRefresh.js +39 -0
- package/dist/plugins/bitwarden/src/index.js +13 -31
- package/dist/plugins/gmail/src/index.js +8 -44
- package/dist/plugins/googleAppsScript/src/index.js +3 -25
- package/dist/plugins/googleCalendar/src/index.js +3 -41
- package/dist/plugins/googleContacts/src/index.js +3 -36
- package/dist/plugins/googleDocs/src/index.js +5 -0
- package/dist/plugins/googleDocs/src/shared.js +2 -30
- package/dist/plugins/googleDrive/src/index.js +52 -98
- package/dist/plugins/googleSheets/src/index.js +3 -40
- package/dist/plugins/googleSlides/src/index.js +5 -39
- package/dist/plugins/googleTasks/src/index.js +3 -30
- package/dist/plugins/halopsa/src/index.js +16 -31
- package/dist/plugins/microsoftCalendar/src/index.js +2 -1
- package/dist/plugins/microsoftFiles/src/index.js +10 -24
- package/dist/plugins/microsoftMail/src/index.js +2 -1
- package/dist/plugins/paypal/src/index.js +14 -29
- package/dist/plugins/plaud/src/index.js +210 -0
- package/dist/plugins/plaud/src/shared.js +221 -0
- package/dist/plugins/salesforce/src/shared.js +17 -32
- package/dist/sdk.d.ts +13 -5
- package/dist/sdk.js +35 -29
- package/dist/utils/observer.d.ts +2 -0
- package/dist/utils/observer.js +11 -0
- package/package.json +1 -1
|
@@ -1,29 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
async function getAccessToken(clientId, clientSecret, tokenUrl) {
|
|
3
|
-
if (cachedToken && Date.now() < cachedToken.expiresAt)
|
|
4
|
-
return cachedToken.value;
|
|
5
|
-
const res = await fetch(tokenUrl, {
|
|
6
|
-
method: "POST",
|
|
7
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
8
|
-
body: new URLSearchParams({
|
|
9
|
-
client_id: clientId,
|
|
10
|
-
client_secret: clientSecret,
|
|
11
|
-
grant_type: "client_credentials",
|
|
12
|
-
scope: "api.organization",
|
|
13
|
-
deviceName: "runline",
|
|
14
|
-
deviceType: "2",
|
|
15
|
-
deviceIdentifier: "runline",
|
|
16
|
-
}),
|
|
17
|
-
});
|
|
18
|
-
if (!res.ok)
|
|
19
|
-
throw new Error(`Bitwarden token error ${res.status}: ${await res.text()}`);
|
|
20
|
-
const data = (await res.json());
|
|
21
|
-
cachedToken = {
|
|
22
|
-
value: data.access_token,
|
|
23
|
-
expiresAt: Date.now() + (data.expires_in - 60) * 1000,
|
|
24
|
-
};
|
|
25
|
-
return cachedToken.value;
|
|
26
|
-
}
|
|
1
|
+
import { coordinatedAccessToken, requestToken, } from "../../_shared/tokenRefresh.js";
|
|
27
2
|
async function apiRequest(token, baseUrl, method, endpoint, body, qs) {
|
|
28
3
|
const url = new URL(`${baseUrl}${endpoint}`);
|
|
29
4
|
if (qs) {
|
|
@@ -57,8 +32,7 @@ async function apiRequest(token, baseUrl, method, endpoint, body, qs) {
|
|
|
57
32
|
return res.json();
|
|
58
33
|
return { success: true };
|
|
59
34
|
}
|
|
60
|
-
function getConn(
|
|
61
|
-
const cfg = ctx.connection.config;
|
|
35
|
+
function getConn(cfg) {
|
|
62
36
|
const domain = cfg.domain?.replace(/\/$/, "");
|
|
63
37
|
const env = cfg.environment;
|
|
64
38
|
const baseUrl = env === "selfHosted" && domain
|
|
@@ -75,9 +49,17 @@ function getConn(ctx) {
|
|
|
75
49
|
};
|
|
76
50
|
}
|
|
77
51
|
async function authedRequest(ctx, method, endpoint, body, qs) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
52
|
+
const token = await coordinatedAccessToken(ctx, (current) => {
|
|
53
|
+
const { clientId, clientSecret, tokenUrl } = getConn(current);
|
|
54
|
+
return requestToken({ url: tokenUrl, clientAuthentication: "client_secret_post" }, {
|
|
55
|
+
grant_type: "client_credentials",
|
|
56
|
+
scope: "api.organization",
|
|
57
|
+
deviceName: "runline",
|
|
58
|
+
deviceType: "2",
|
|
59
|
+
deviceIdentifier: "runline",
|
|
60
|
+
}, { clientId, clientSecret });
|
|
61
|
+
});
|
|
62
|
+
return apiRequest(token, getConn(ctx.connection.config).baseUrl, method, endpoint, body, qs);
|
|
81
63
|
}
|
|
82
64
|
export default function bitwarden(rl) {
|
|
83
65
|
rl.setName("bitwarden");
|
|
@@ -22,12 +22,8 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import * as t from "typebox";
|
|
24
24
|
import { renderEmailJsx } from "../../_shared/emailJsx.js";
|
|
25
|
-
import {
|
|
25
|
+
import { googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
26
26
|
import { GoogleTimestamp, Id, NonEmptyString, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
27
|
-
// ─── Auth ────────────────────────────────────────────────────────
|
|
28
|
-
async function accessToken(ctx) {
|
|
29
|
-
return googleAccessToken(ctx, "gmail", SCOPES);
|
|
30
|
-
}
|
|
31
27
|
// ─── Request ─────────────────────────────────────────────────────
|
|
32
28
|
const API_BASE = "https://gmail.googleapis.com/gmail/v1/users/me";
|
|
33
29
|
function sendFailureRetryHint(method, path) {
|
|
@@ -36,48 +32,15 @@ function sendFailureRetryHint(method, path) {
|
|
|
36
32
|
return " Before retrying, check whether the email was already sent using gmail.message.list({ q: 'in:sent to:<recipient> subject:\"<subject>\"', maxResults: 10 }) and inspect likely matches with gmail.message.get({ id, format: 'metadata' }).";
|
|
37
33
|
}
|
|
38
34
|
async function gmailRequest(ctx, method, path, body, qs) {
|
|
39
|
-
const token = await accessToken(ctx);
|
|
40
|
-
const url = new URL(`${API_BASE}${path}`);
|
|
41
|
-
if (qs) {
|
|
42
|
-
for (const [k, v] of Object.entries(qs)) {
|
|
43
|
-
if (v === undefined || v === null)
|
|
44
|
-
continue;
|
|
45
|
-
if (Array.isArray(v)) {
|
|
46
|
-
for (const entry of v)
|
|
47
|
-
url.searchParams.append(k, String(entry));
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
url.searchParams.set(k, String(v));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
const init = {
|
|
55
|
-
method,
|
|
56
|
-
headers: {
|
|
57
|
-
Authorization: `Bearer ${token}`,
|
|
58
|
-
Accept: "application/json",
|
|
59
|
-
},
|
|
60
|
-
};
|
|
61
|
-
if (body && Object.keys(body).length > 0) {
|
|
62
|
-
init.headers["Content-Type"] =
|
|
63
|
-
"application/json";
|
|
64
|
-
init.body = JSON.stringify(body);
|
|
65
|
-
}
|
|
66
|
-
let res;
|
|
67
35
|
try {
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
catch (err) {
|
|
71
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
72
|
-
throw new Error(`gmail: ${method} ${path} failed: ${msg}.${sendFailureRetryHint(method, path)}`);
|
|
36
|
+
return await googleJsonRequest(ctx, "gmail", SCOPES, method, `${API_BASE}${path}`, body, qs);
|
|
73
37
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
throw new Error(
|
|
38
|
+
catch (error) {
|
|
39
|
+
const hint = sendFailureRetryHint(method, path);
|
|
40
|
+
if (!hint)
|
|
41
|
+
throw error;
|
|
42
|
+
throw new Error(`${error instanceof Error ? error.message : "gmail: request failed"}.${hint}`);
|
|
79
43
|
}
|
|
80
|
-
return text ? JSON.parse(text) : { success: true };
|
|
81
44
|
}
|
|
82
45
|
async function paginateAll(ctx, path, key, qs) {
|
|
83
46
|
const out = [];
|
|
@@ -672,6 +635,7 @@ export default function gmail(rl) {
|
|
|
672
635
|
],
|
|
673
636
|
});
|
|
674
637
|
rl.setConnectionSchema({
|
|
638
|
+
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
675
639
|
clientId: {
|
|
676
640
|
type: "string",
|
|
677
641
|
required: false,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as t from "typebox";
|
|
2
|
-
import {
|
|
2
|
+
import { googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
3
3
|
import { Id, NonEmptyString, PositiveInteger, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
4
4
|
const SCRIPT_API = "https://script.googleapis.com/v1";
|
|
5
5
|
const DRIVE_API = "https://www.googleapis.com/drive/v3";
|
|
@@ -33,36 +33,14 @@ const scriptFileSchema = t.Object({
|
|
|
33
33
|
type: scriptFileTypeSchema,
|
|
34
34
|
source: t.String(),
|
|
35
35
|
}, { additionalProperties: false });
|
|
36
|
-
function accessToken(ctx) {
|
|
37
|
-
return googleAccessToken(ctx, "googleAppsScript", SCOPES);
|
|
38
|
-
}
|
|
39
36
|
async function call(ctx, method, url, payload) {
|
|
40
|
-
|
|
41
|
-
if (payload !== undefined)
|
|
42
|
-
headers["Content-Type"] = "application/json";
|
|
43
|
-
const res = await fetch(url, {
|
|
44
|
-
method,
|
|
45
|
-
headers,
|
|
46
|
-
body: payload !== undefined ? JSON.stringify(payload) : undefined,
|
|
47
|
-
});
|
|
48
|
-
const text = await res.text();
|
|
49
|
-
let json;
|
|
50
|
-
try {
|
|
51
|
-
json = text ? JSON.parse(text) : {};
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
json = { raw: text };
|
|
55
|
-
}
|
|
56
|
-
if (!res.ok) {
|
|
57
|
-
const msg = json?.error?.message || json?.error_description || text.slice(0, 300) || res.status;
|
|
58
|
-
throw new Error(`googleAppsScript: ${method} ${res.status}: ${msg}`);
|
|
59
|
-
}
|
|
60
|
-
return json;
|
|
37
|
+
return googleJsonRequest(ctx, "googleAppsScript", SCOPES, method, url, payload);
|
|
61
38
|
}
|
|
62
39
|
export default function googleAppsScript(rl) {
|
|
63
40
|
rl.setName("googleAppsScript");
|
|
64
41
|
rl.setVersion("1.0.0");
|
|
65
42
|
rl.setConnectionSchema({
|
|
43
|
+
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
66
44
|
clientId: { type: "string", required: false, description: "OAuth client ID", env: "GOOGLE_APPS_SCRIPT_CLIENT_ID" },
|
|
67
45
|
clientSecret: { type: "string", required: false, description: "OAuth client secret", env: "GOOGLE_APPS_SCRIPT_CLIENT_SECRET" },
|
|
68
46
|
refreshToken: { type: "string", required: false, description: "OAuth refresh token", env: "GOOGLE_APPS_SCRIPT_REFRESH_TOKEN" },
|
|
@@ -29,53 +29,14 @@
|
|
|
29
29
|
*/
|
|
30
30
|
import rrulePkg from "rrule";
|
|
31
31
|
import * as t from "typebox";
|
|
32
|
-
import {
|
|
32
|
+
import { googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
33
33
|
import { Id, NonEmptyString, NonEmptyStringArray, PositiveInteger, StringOrStringArray, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
34
34
|
// `rrule` ships as CJS; named imports fail under Node ESM.
|
|
35
35
|
const { RRule } = rrulePkg;
|
|
36
|
-
// ─── Auth ────────────────────────────────────────────────────────
|
|
37
|
-
async function accessToken(ctx) {
|
|
38
|
-
return googleAccessToken(ctx, "googleCalendar", SCOPES);
|
|
39
|
-
}
|
|
40
36
|
// ─── Request ─────────────────────────────────────────────────────
|
|
41
37
|
const API_BASE = "https://www.googleapis.com/calendar/v3";
|
|
42
38
|
async function calRequest(ctx, method, path, body, qs) {
|
|
43
|
-
|
|
44
|
-
const url = new URL(`${API_BASE}${path}`);
|
|
45
|
-
if (qs) {
|
|
46
|
-
for (const [k, v] of Object.entries(qs)) {
|
|
47
|
-
if (v === undefined || v === null)
|
|
48
|
-
continue;
|
|
49
|
-
if (Array.isArray(v)) {
|
|
50
|
-
for (const entry of v)
|
|
51
|
-
url.searchParams.append(k, String(entry));
|
|
52
|
-
}
|
|
53
|
-
else {
|
|
54
|
-
url.searchParams.set(k, String(v));
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const init = {
|
|
59
|
-
method,
|
|
60
|
-
headers: {
|
|
61
|
-
Authorization: `Bearer ${token}`,
|
|
62
|
-
Accept: "application/json",
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
if (body && Object.keys(body).length > 0) {
|
|
66
|
-
init.headers["Content-Type"] = "application/json";
|
|
67
|
-
init.body = JSON.stringify(body);
|
|
68
|
-
}
|
|
69
|
-
const res = await fetch(url.toString(), init);
|
|
70
|
-
if (res.status === 204)
|
|
71
|
-
return { success: true };
|
|
72
|
-
const text = await res.text();
|
|
73
|
-
if (!res.ok) {
|
|
74
|
-
// 403/429 deserve a retry with exponential backoff — Calendar
|
|
75
|
-
// hands these out freely when you hit per-user quota.
|
|
76
|
-
throw new Error(`googleCalendar: ${method} ${path} → ${res.status} ${text}`);
|
|
77
|
-
}
|
|
78
|
-
return text ? JSON.parse(text) : { success: true };
|
|
39
|
+
return googleJsonRequest(ctx, "googleCalendar", SCOPES, method, `${API_BASE}${path}`, body, qs);
|
|
79
40
|
}
|
|
80
41
|
async function paginateAll(ctx, path, key, qs) {
|
|
81
42
|
const out = [];
|
|
@@ -572,6 +533,7 @@ export default function googleCalendar(rl) {
|
|
|
572
533
|
],
|
|
573
534
|
});
|
|
574
535
|
rl.setConnectionSchema({
|
|
536
|
+
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
575
537
|
clientId: {
|
|
576
538
|
type: "string",
|
|
577
539
|
required: false,
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* convenience `contactId` field stripped from `resourceName`.
|
|
24
24
|
*/
|
|
25
25
|
import * as t from "typebox";
|
|
26
|
-
import {
|
|
26
|
+
import { googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
27
27
|
import { Id, NonEmptyString, PositiveInteger, stringEnum, } from "../../_shared/googleSchemas.js";
|
|
28
28
|
// ─── Fields ─────────────────────────────────────────────────────
|
|
29
29
|
/**
|
|
@@ -81,44 +81,10 @@ const UPDATABLE_PERSON_FIELDS = new Set([
|
|
|
81
81
|
"urls",
|
|
82
82
|
"userDefined",
|
|
83
83
|
]);
|
|
84
|
-
// ─── Auth ────────────────────────────────────────────────────────
|
|
85
|
-
async function accessToken(ctx) {
|
|
86
|
-
return googleAccessToken(ctx, "googleContacts", SCOPES);
|
|
87
|
-
}
|
|
88
84
|
// ─── Request ─────────────────────────────────────────────────────
|
|
89
85
|
const API_BASE = "https://people.googleapis.com/v1";
|
|
90
86
|
async function peopleRequest(ctx, method, path, body, qs) {
|
|
91
|
-
|
|
92
|
-
const url = new URL(`${API_BASE}${path}`);
|
|
93
|
-
if (qs) {
|
|
94
|
-
for (const [k, v] of Object.entries(qs)) {
|
|
95
|
-
if (v === undefined || v === null)
|
|
96
|
-
continue;
|
|
97
|
-
if (Array.isArray(v)) {
|
|
98
|
-
for (const entry of v)
|
|
99
|
-
url.searchParams.append(k, String(entry));
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
url.searchParams.set(k, String(v));
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
const init = {
|
|
107
|
-
method,
|
|
108
|
-
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
109
|
-
};
|
|
110
|
-
if (body && Object.keys(body).length > 0) {
|
|
111
|
-
init.headers["Content-Type"] = "application/json";
|
|
112
|
-
init.body = JSON.stringify(body);
|
|
113
|
-
}
|
|
114
|
-
const res = await fetch(url.toString(), init);
|
|
115
|
-
if (res.status === 204)
|
|
116
|
-
return { success: true };
|
|
117
|
-
const text = await res.text();
|
|
118
|
-
if (!res.ok) {
|
|
119
|
-
throw new Error(`googleContacts: ${method} ${path} → ${res.status} ${text}`);
|
|
120
|
-
}
|
|
121
|
-
return text ? JSON.parse(text) : { success: true };
|
|
87
|
+
return googleJsonRequest(ctx, "googleContacts", SCOPES, method, `${API_BASE}${path}`, body, qs);
|
|
122
88
|
}
|
|
123
89
|
async function paginateAll(ctx, path, key, qs) {
|
|
124
90
|
const out = [];
|
|
@@ -438,6 +404,7 @@ export default function googleContacts(rl) {
|
|
|
438
404
|
],
|
|
439
405
|
});
|
|
440
406
|
rl.setConnectionSchema({
|
|
407
|
+
authMethod: { type: "string", required: false, description: "delegated or serviceAccount (legacy configs infer the method)" },
|
|
441
408
|
clientId: { type: "string", required: false, env: "GOOGLE_CONTACTS_CLIENT_ID" },
|
|
442
409
|
clientSecret: { type: "string", required: false, env: "GOOGLE_CONTACTS_CLIENT_SECRET" },
|
|
443
410
|
refreshToken: { type: "string", required: false, env: "GOOGLE_CONTACTS_REFRESH_TOKEN" },
|
|
@@ -51,6 +51,11 @@ export default function googleDocs(rl) {
|
|
|
51
51
|
],
|
|
52
52
|
});
|
|
53
53
|
rl.setConnectionSchema({
|
|
54
|
+
authMethod: {
|
|
55
|
+
type: "string",
|
|
56
|
+
required: false,
|
|
57
|
+
description: "delegated or serviceAccount (legacy configs infer the method)",
|
|
58
|
+
},
|
|
54
59
|
clientId: { type: "string", required: false, env: "GOOGLE_DOCS_CLIENT_ID" },
|
|
55
60
|
clientSecret: {
|
|
56
61
|
type: "string",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as t from "typebox";
|
|
2
|
-
import {
|
|
2
|
+
import { googleJsonRequest } from "../../_shared/googleAuth.js";
|
|
3
3
|
import { RawGoogleObject } from "../../_shared/googleSchemas.js";
|
|
4
4
|
export const SCOPES = [
|
|
5
5
|
"https://www.googleapis.com/auth/documents",
|
|
@@ -60,36 +60,8 @@ export const WriteControl = t.Object({
|
|
|
60
60
|
],
|
|
61
61
|
});
|
|
62
62
|
export { RawGoogleObject };
|
|
63
|
-
export async function accessToken(ctx) {
|
|
64
|
-
return googleAccessToken(ctx, "googleDocs", SCOPES);
|
|
65
|
-
}
|
|
66
63
|
export async function docsRequest(ctx, method, path, body, qs, baseOverride) {
|
|
67
|
-
|
|
68
|
-
const url = new URL(`${baseOverride ?? DOCS_BASE}${path}`);
|
|
69
|
-
if (qs) {
|
|
70
|
-
for (const [k, v] of Object.entries(qs)) {
|
|
71
|
-
if (v === undefined || v === null)
|
|
72
|
-
continue;
|
|
73
|
-
url.searchParams.set(k, String(v));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
const init = {
|
|
77
|
-
method,
|
|
78
|
-
headers: { Authorization: `Bearer ${token}`, Accept: "application/json" },
|
|
79
|
-
};
|
|
80
|
-
if (body && Object.keys(body).length > 0) {
|
|
81
|
-
init.headers["Content-Type"] =
|
|
82
|
-
"application/json";
|
|
83
|
-
init.body = JSON.stringify(body);
|
|
84
|
-
}
|
|
85
|
-
const res = await fetch(url.toString(), init);
|
|
86
|
-
if (res.status === 204)
|
|
87
|
-
return { success: true };
|
|
88
|
-
const text = await res.text();
|
|
89
|
-
if (!res.ok) {
|
|
90
|
-
throw new Error(`googleDocs: ${method} ${path} → ${res.status} ${text}`);
|
|
91
|
-
}
|
|
92
|
-
return text ? JSON.parse(text) : { success: true };
|
|
64
|
+
return googleJsonRequest(ctx, "googleDocs", SCOPES, method, `${baseOverride ?? DOCS_BASE}${path}`, body, qs);
|
|
93
65
|
}
|
|
94
66
|
const DOC_URL_REGEX = /https:\/\/docs\.google\.com\/document\/d\/([a-zA-Z0-9-_]+)/;
|
|
95
67
|
export function extractDocumentId(input) {
|