@zackbart/connecta 0.30.0 → 0.31.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/CHANGELOG.md +32 -0
- package/README.md +6 -3
- package/bin/version.mjs +1 -1
- package/dist/call-recovery.js +25 -0
- package/dist/config-schema.d.ts +14 -7
- package/dist/config-schema.js +29 -10
- package/dist/connectors/guarded-fetch.d.ts +9 -1
- package/dist/connectors/guarded-fetch.js +14 -2
- package/dist/errors.d.ts +2 -0
- package/dist/invocation.js +32 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/operator-ui/view.d.ts +9 -0
- package/dist/operator-ui/view.js +11 -0
- package/dist/program-writes.js +2 -47
- package/dist/provider.d.ts +7 -5
- package/dist/provider.js +3 -4
- package/dist/providers/_shared/rest/dispatch.js +41 -0
- package/dist/providers/_shared/rest/operation-index.d.ts +133 -0
- package/dist/providers/_shared/rest/operation-index.js +527 -0
- package/dist/providers/_shared/rest/tools.d.ts +141 -0
- package/dist/providers/_shared/rest/tools.js +569 -0
- package/dist/providers/_shared/rest/value-safety.d.ts +151 -0
- package/dist/providers/_shared/rest/value-safety.js +580 -0
- package/dist/providers/cloudflare/graphql.js +385 -0
- package/dist/providers/cloudflare/index.d.ts +47 -51
- package/dist/providers/cloudflare/index.js +188 -1997
- package/dist/providers/cloudflare/openapi.generated.js +3347 -0
- package/dist/providers/cloudflare/rest.d.ts +37 -0
- package/dist/providers/cloudflare/rest.js +1056 -0
- package/dist/providers/cloudflare/skill.generated.js +7 -4
- package/dist/providers/cloudflare/value-safety.js +1247 -0
- package/dist/providers/notion/index.d.ts +35 -31
- package/dist/providers/notion/index.js +60 -1499
- package/dist/providers/notion/openapi.generated.js +75 -0
- package/dist/providers/notion/rest.d.ts +24 -0
- package/dist/providers/notion/rest.js +1067 -0
- package/dist/providers/notion/skill.generated.js +5 -4
- package/dist/providers/notion/value-safety.js +31 -0
- package/dist/providers/stripe/index.d.ts +26 -20
- package/dist/providers/stripe/index.js +103 -130
- package/dist/providers/stripe/openapi.generated.js +649 -0
- package/dist/providers/stripe/rest.d.ts +14 -0
- package/dist/providers/stripe/rest.js +395 -0
- package/dist/providers/stripe/skill.generated.js +7 -5
- package/dist/providers/stripe/value-safety.js +705 -0
- package/dist/providers/vercel/index.d.ts +31 -22
- package/dist/providers/vercel/index.js +96 -853
- package/dist/providers/vercel/openapi.generated.js +456 -0
- package/dist/providers/vercel/rest.d.ts +20 -0
- package/dist/providers/vercel/rest.js +1002 -0
- package/dist/providers/vercel/skill.generated.js +4 -5
- package/dist/providers/vercel/value-safety.js +328 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/write-outcome.js +47 -0
- package/documentation/README.md +1 -0
- package/documentation/architecture.md +120 -8
- package/documentation/integrating.md +16 -5
- package/documentation/meta-tools.md +9 -2
- package/documentation/provider-auth-migration.md +307 -0
- package/documentation/provider-migration-0.29.md +17 -5
- package/examples/worker/README.md +2 -2
- package/package.json +3 -2
- package/templates/node/README.md +2 -2
- package/templates/node/package.json +1 -1
- package/dist/providers/vercel/mcp-ownership.js +0 -212
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
// Read-only admission for Cloudflare's GraphQL Analytics API. A document is
|
|
2
|
+
// tokenized by the GraphQL lexical grammar (strings, block strings, comments
|
|
3
|
+
// and commas are skipped as the spec skips them) and its top-level
|
|
4
|
+
// definitions are walked, so a `mutation` hidden in a string, a comment, or
|
|
5
|
+
// after an anonymous query is still found, and anything that is not a query
|
|
6
|
+
// or a fragment is refused. Nothing here does I/O.
|
|
7
|
+
import { ConnectorCallError } from "../../errors.js";
|
|
8
|
+
const SCOPED_FIELDS = new Set(["accounts", "zones"]);
|
|
9
|
+
function refuse(message) {
|
|
10
|
+
throw new ConnectorCallError("invalid_args", message);
|
|
11
|
+
}
|
|
12
|
+
function tokenize(source) {
|
|
13
|
+
const tokens = [];
|
|
14
|
+
let at = 0;
|
|
15
|
+
const length = source.length;
|
|
16
|
+
while (at < length) {
|
|
17
|
+
const character = source[at];
|
|
18
|
+
if (character === " " || character === "\t" || character === "\n" || character === "\r" || character === ",") {
|
|
19
|
+
at += 1;
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
if (character === "") {
|
|
23
|
+
at += 1;
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (character === "#") {
|
|
27
|
+
while (at < length && source[at] !== "\n" && source[at] !== "\r")
|
|
28
|
+
at += 1;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
if (source.startsWith("...", at)) {
|
|
32
|
+
tokens.push({ kind: "punct", value: "..." });
|
|
33
|
+
at += 3;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if ("!$&()[]{}:=@|".includes(character)) {
|
|
37
|
+
tokens.push({ kind: "punct", value: character });
|
|
38
|
+
at += 1;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (/[_A-Za-z]/.test(character)) {
|
|
42
|
+
const start = at;
|
|
43
|
+
while (at < length && /[_0-9A-Za-z]/.test(source[at]))
|
|
44
|
+
at += 1;
|
|
45
|
+
tokens.push({ kind: "name", value: source.slice(start, at) });
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
if (character === "-" || /[0-9]/.test(character)) {
|
|
49
|
+
const match = /^-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?/.exec(source.slice(at));
|
|
50
|
+
if (!match)
|
|
51
|
+
refuse("The GraphQL document has a malformed number.");
|
|
52
|
+
tokens.push({ kind: "number", value: match[0] });
|
|
53
|
+
at += match[0].length;
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (source.startsWith('"""', at)) {
|
|
57
|
+
let end = at + 3;
|
|
58
|
+
let text = "";
|
|
59
|
+
for (;;) {
|
|
60
|
+
if (end >= length)
|
|
61
|
+
refuse("The GraphQL document has an unterminated block string.");
|
|
62
|
+
if (source.startsWith('\\"""', end)) {
|
|
63
|
+
text += '"""';
|
|
64
|
+
end += 4;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (source.startsWith('"""', end))
|
|
68
|
+
break;
|
|
69
|
+
text += source[end];
|
|
70
|
+
end += 1;
|
|
71
|
+
}
|
|
72
|
+
tokens.push({ kind: "string", value: text });
|
|
73
|
+
at = end + 3;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (character === '"') {
|
|
77
|
+
let end = at + 1;
|
|
78
|
+
let text = "";
|
|
79
|
+
for (;;) {
|
|
80
|
+
const next = source[end];
|
|
81
|
+
if (next === undefined || next === "\n" || next === "\r")
|
|
82
|
+
refuse("The GraphQL document has an unterminated string.");
|
|
83
|
+
if (next === '"')
|
|
84
|
+
break;
|
|
85
|
+
if (next === "\\") {
|
|
86
|
+
const escape = source[end + 1];
|
|
87
|
+
if (escape === "u") {
|
|
88
|
+
const hex = source.slice(end + 2, end + 6);
|
|
89
|
+
if (!/^[0-9A-Fa-f]{4}$/.test(hex))
|
|
90
|
+
refuse("The GraphQL document has a malformed string escape.");
|
|
91
|
+
text += String.fromCharCode(parseInt(hex, 16));
|
|
92
|
+
end += 6;
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const simple = {
|
|
96
|
+
'"': '"',
|
|
97
|
+
"\\": "\\",
|
|
98
|
+
"/": "/",
|
|
99
|
+
b: "\b",
|
|
100
|
+
f: "\f",
|
|
101
|
+
n: "\n",
|
|
102
|
+
r: "\r",
|
|
103
|
+
t: "\t",
|
|
104
|
+
};
|
|
105
|
+
if (escape === undefined || !(escape in simple))
|
|
106
|
+
refuse("The GraphQL document has a malformed string escape.");
|
|
107
|
+
text += simple[escape];
|
|
108
|
+
end += 2;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
text += next;
|
|
112
|
+
end += 1;
|
|
113
|
+
}
|
|
114
|
+
tokens.push({ kind: "string", value: text });
|
|
115
|
+
at = end + 1;
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
refuse(`The GraphQL document has an unexpected character at offset ${at}.`);
|
|
119
|
+
}
|
|
120
|
+
return tokens;
|
|
121
|
+
}
|
|
122
|
+
const OPENERS = { "(": ")", "[": "]", "{": "}" };
|
|
123
|
+
/**
|
|
124
|
+
* Refuse anything but queries and fragments, and report the account and zone
|
|
125
|
+
* tags the document names. `variables` resolves `$name` tag values.
|
|
126
|
+
*/
|
|
127
|
+
export function inspectGraphqlQuery(source, variables, options = {}) {
|
|
128
|
+
const tokens = tokenize(source);
|
|
129
|
+
if (tokens.length === 0)
|
|
130
|
+
refuse("The GraphQL document is empty.");
|
|
131
|
+
let index = 0;
|
|
132
|
+
let operations = 0;
|
|
133
|
+
// Skip one balanced group starting at `index` (an opener); returns the index after its closer.
|
|
134
|
+
const skipGroup = (start) => {
|
|
135
|
+
const stack = [];
|
|
136
|
+
for (let at = start; at < tokens.length; at += 1) {
|
|
137
|
+
const token = tokens[at];
|
|
138
|
+
if (token.kind !== "punct")
|
|
139
|
+
continue;
|
|
140
|
+
if (OPENERS[token.value])
|
|
141
|
+
stack.push(OPENERS[token.value]);
|
|
142
|
+
else if (token.value === ")" || token.value === "]" || token.value === "}") {
|
|
143
|
+
if (stack.pop() !== token.value)
|
|
144
|
+
refuse("The GraphQL document has unbalanced brackets.");
|
|
145
|
+
if (stack.length === 0)
|
|
146
|
+
return at + 1;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return refuse("The GraphQL document has unbalanced brackets.");
|
|
150
|
+
};
|
|
151
|
+
// From a definition keyword, consume the header (name, variables,
|
|
152
|
+
// directives) and its selection set.
|
|
153
|
+
const skipDefinition = (start) => {
|
|
154
|
+
for (let at = start; at < tokens.length; at += 1) {
|
|
155
|
+
const token = tokens[at];
|
|
156
|
+
if (token.kind !== "punct")
|
|
157
|
+
continue;
|
|
158
|
+
if (token.value === "{")
|
|
159
|
+
return skipGroup(at);
|
|
160
|
+
if (token.value === "(" || token.value === "[")
|
|
161
|
+
at = skipGroup(at) - 1;
|
|
162
|
+
else if (token.value === "}" || token.value === ")" || token.value === "]") {
|
|
163
|
+
refuse("The GraphQL document has unbalanced brackets.");
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return refuse("A GraphQL definition has no selection set.");
|
|
167
|
+
};
|
|
168
|
+
// Each top-level definition's token range, for the strict checks below.
|
|
169
|
+
const definitions = [];
|
|
170
|
+
const nameAfter = (at) => {
|
|
171
|
+
const token = tokens[at];
|
|
172
|
+
return token?.kind === "name" ? token.value : undefined;
|
|
173
|
+
};
|
|
174
|
+
while (index < tokens.length) {
|
|
175
|
+
const token = tokens[index];
|
|
176
|
+
if (token.kind === "punct" && token.value === "{") {
|
|
177
|
+
operations += 1;
|
|
178
|
+
const end = skipGroup(index);
|
|
179
|
+
definitions.push({ kind: "query", name: undefined, start: index, end });
|
|
180
|
+
index = end;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
if (token.kind === "name" && token.value === "query") {
|
|
184
|
+
operations += 1;
|
|
185
|
+
const end = skipDefinition(index + 1);
|
|
186
|
+
definitions.push({ kind: "query", name: nameAfter(index + 1), start: index, end });
|
|
187
|
+
index = end;
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (token.kind === "name" && token.value === "fragment") {
|
|
191
|
+
const end = skipDefinition(index + 1);
|
|
192
|
+
definitions.push({ kind: "fragment", name: nameAfter(index + 1), start: index, end });
|
|
193
|
+
index = end;
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
if (token.kind === "name" && (token.value === "mutation" || token.value === "subscription")) {
|
|
197
|
+
refuse(`graphql_query runs read-only queries; this document contains a ${token.value}, which it never sends.`);
|
|
198
|
+
}
|
|
199
|
+
refuse("graphql_query accepts only query operations and fragments.");
|
|
200
|
+
}
|
|
201
|
+
if (operations === 0)
|
|
202
|
+
refuse("The GraphQL document has no query operation.");
|
|
203
|
+
if (options.strict) {
|
|
204
|
+
// One operation, so every variable default belongs to the query that
|
|
205
|
+
// runs; and every fragment reachable from it, defined, used, and acyclic,
|
|
206
|
+
// so the fields checked below are exactly the fields that execute.
|
|
207
|
+
if (operations !== 1)
|
|
208
|
+
refuse("A pinned connector sends one query operation per GraphQL document.");
|
|
209
|
+
const operation = definitions.find((definition) => definition.kind === "query");
|
|
210
|
+
if (options.operationName !== undefined && options.operationName !== operation.name) {
|
|
211
|
+
refuse(`operationName ${options.operationName} names no query in this document.`);
|
|
212
|
+
}
|
|
213
|
+
const fragments = new Map();
|
|
214
|
+
for (const definition of definitions) {
|
|
215
|
+
if (definition.kind !== "fragment")
|
|
216
|
+
continue;
|
|
217
|
+
if (!definition.name || definition.name === "on" || fragments.has(definition.name)) {
|
|
218
|
+
refuse("A GraphQL fragment must have one unique name.");
|
|
219
|
+
}
|
|
220
|
+
fragments.set(definition.name, definition);
|
|
221
|
+
}
|
|
222
|
+
const spreads = (range) => {
|
|
223
|
+
const names = [];
|
|
224
|
+
for (let at = range.start; at < range.end; at += 1) {
|
|
225
|
+
if (tokens[at].value === "..." && tokens[at + 1]?.kind === "name" && tokens[at + 1].value !== "on") {
|
|
226
|
+
names.push(tokens[at + 1].value);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
return names;
|
|
230
|
+
};
|
|
231
|
+
const used = new Set();
|
|
232
|
+
const visit = (range, path) => {
|
|
233
|
+
for (const name of spreads(range)) {
|
|
234
|
+
const fragment = fragments.get(name);
|
|
235
|
+
if (!fragment)
|
|
236
|
+
refuse(`The GraphQL document spreads an undefined fragment ${name}.`);
|
|
237
|
+
if (path.includes(name))
|
|
238
|
+
refuse(`The GraphQL fragment ${name} spreads itself.`);
|
|
239
|
+
used.add(name);
|
|
240
|
+
visit(fragment, [...path, name]);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
visit(operation, []);
|
|
244
|
+
const unused = [...fragments.keys()].filter((name) => !used.has(name));
|
|
245
|
+
if (unused.length > 0)
|
|
246
|
+
refuse(`The GraphQL document defines unused fragments: ${unused.join(", ")}.`);
|
|
247
|
+
}
|
|
248
|
+
const accountTags = [];
|
|
249
|
+
const zoneTags = [];
|
|
250
|
+
const openTags = [];
|
|
251
|
+
const unscopedFields = [];
|
|
252
|
+
const UNRESOLVED = Symbol("unresolved");
|
|
253
|
+
// Variable declarations (`$zoneTag: String! = "…"`) are not filters; record
|
|
254
|
+
// their defaults so a variable resolves as the server would resolve it.
|
|
255
|
+
const defaults = new Map();
|
|
256
|
+
const declared = new Set();
|
|
257
|
+
const resolveVariable = (name) => Object.hasOwn(variables, name) ? variables[name] : defaults.has(name) ? defaults.get(name) : UNRESOLVED;
|
|
258
|
+
/** Parse one GraphQL input value at `at`, resolving variables; returns the value and the index after it. */
|
|
259
|
+
const parseValue = (at) => {
|
|
260
|
+
const token = tokens[at];
|
|
261
|
+
if (token === undefined)
|
|
262
|
+
return { value: UNRESOLVED, end: at };
|
|
263
|
+
if (token.kind === "string")
|
|
264
|
+
return { value: token.value, end: at + 1 };
|
|
265
|
+
if (token.kind === "number")
|
|
266
|
+
return { value: Number(token.value), end: at + 1 };
|
|
267
|
+
if (token.kind === "name") {
|
|
268
|
+
const literal = { true: true, false: false, null: null };
|
|
269
|
+
return { value: Object.hasOwn(literal, token.value) ? literal[token.value] : { enum: token.value }, end: at + 1 };
|
|
270
|
+
}
|
|
271
|
+
if (token.value === "$" && tokens[at + 1]?.kind === "name") {
|
|
272
|
+
return { value: resolveVariable(tokens[at + 1].value), end: at + 2 };
|
|
273
|
+
}
|
|
274
|
+
if (token.value === "[") {
|
|
275
|
+
const items = [];
|
|
276
|
+
let next = at + 1;
|
|
277
|
+
while (next < tokens.length && tokens[next].value !== "]") {
|
|
278
|
+
const item = parseValue(next);
|
|
279
|
+
if (item.end === next)
|
|
280
|
+
return { value: UNRESOLVED, end: skipGroup(at) };
|
|
281
|
+
items.push(item.value);
|
|
282
|
+
next = item.end;
|
|
283
|
+
}
|
|
284
|
+
return { value: items, end: next + 1 };
|
|
285
|
+
}
|
|
286
|
+
if (token.value === "{") {
|
|
287
|
+
const object = {};
|
|
288
|
+
let next = at + 1;
|
|
289
|
+
while (next < tokens.length && tokens[next].value !== "}") {
|
|
290
|
+
const key = tokens[next];
|
|
291
|
+
if (key?.kind !== "name" || tokens[next + 1]?.value !== ":")
|
|
292
|
+
return { value: UNRESOLVED, end: skipGroup(at) };
|
|
293
|
+
const item = parseValue(next + 2);
|
|
294
|
+
if (item.end === next + 2)
|
|
295
|
+
return { value: UNRESOLVED, end: skipGroup(at) };
|
|
296
|
+
object[key.value] = item.value;
|
|
297
|
+
next = item.end;
|
|
298
|
+
}
|
|
299
|
+
return { value: object, end: next + 1 };
|
|
300
|
+
}
|
|
301
|
+
return { value: UNRESOLVED, end: at };
|
|
302
|
+
};
|
|
303
|
+
for (let at = 0; at + 2 < tokens.length; at += 1) {
|
|
304
|
+
const [dollar, name, colon] = [tokens[at], tokens[at + 1], tokens[at + 2]];
|
|
305
|
+
if (dollar.value !== "$" || name.kind !== "name" || colon.value !== ":")
|
|
306
|
+
continue;
|
|
307
|
+
declared.add(at + 1);
|
|
308
|
+
for (let scan = at + 3; scan < tokens.length; scan += 1) {
|
|
309
|
+
const token = tokens[scan];
|
|
310
|
+
if (token.kind === "punct" && (token.value === "$" || token.value === ")"))
|
|
311
|
+
break;
|
|
312
|
+
if (token.kind === "punct" && token.value === "=") {
|
|
313
|
+
const parsed = parseValue(scan + 1);
|
|
314
|
+
if (parsed.value !== UNRESOLVED)
|
|
315
|
+
defaults.set(name.value, parsed.value);
|
|
316
|
+
break;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Validate one `zones` or `accounts` field's resolved `filter`: an object
|
|
322
|
+
* whose only keys are the tag equality or `_in`, with string values. Each
|
|
323
|
+
* field is checked on its own, so a valid alias cannot carry an unpinned one.
|
|
324
|
+
*/
|
|
325
|
+
const checkField = (field, filter) => {
|
|
326
|
+
const prefix = field === "zones" ? "zone" : "account";
|
|
327
|
+
if (filter === UNRESOLVED || typeof filter !== "object" || filter === null || Array.isArray(filter)) {
|
|
328
|
+
unscopedFields.push(field);
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
const target = prefix === "zone" ? zoneTags : accountTags;
|
|
332
|
+
let tagged = false;
|
|
333
|
+
for (const [key, value] of Object.entries(filter)) {
|
|
334
|
+
if (key === `${prefix}Tag` && typeof value === "string") {
|
|
335
|
+
target.push(value);
|
|
336
|
+
tagged = true;
|
|
337
|
+
}
|
|
338
|
+
else if (key === `${prefix}Tag_in` &&
|
|
339
|
+
Array.isArray(value) &&
|
|
340
|
+
value.length > 0 &&
|
|
341
|
+
value.every((item) => typeof item === "string")) {
|
|
342
|
+
target.push(...value);
|
|
343
|
+
tagged = true;
|
|
344
|
+
}
|
|
345
|
+
else {
|
|
346
|
+
openTags.push(`${field}.${key}`);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
if (!tagged)
|
|
350
|
+
unscopedFields.push(field);
|
|
351
|
+
};
|
|
352
|
+
for (let at = 0; at < tokens.length; at += 1) {
|
|
353
|
+
const token = tokens[at];
|
|
354
|
+
if (token.kind !== "name" || !SCOPED_FIELDS.has(token.value) || declared.has(at))
|
|
355
|
+
continue;
|
|
356
|
+
const previous = tokens[at - 1]?.value;
|
|
357
|
+
const next = tokens[at + 1];
|
|
358
|
+
if (previous === "$" || previous === "on" || previous === "...")
|
|
359
|
+
continue;
|
|
360
|
+
// `zones: accounts(…)` aliases, and object keys inside arguments.
|
|
361
|
+
if (next?.kind === "punct" && next.value === ":")
|
|
362
|
+
continue;
|
|
363
|
+
if (next?.kind !== "punct" || next.value !== "(") {
|
|
364
|
+
unscopedFields.push(token.value);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
let filter = UNRESOLVED;
|
|
368
|
+
let cursor = at + 2;
|
|
369
|
+
while (cursor < tokens.length && tokens[cursor].value !== ")") {
|
|
370
|
+
const key = tokens[cursor];
|
|
371
|
+
if (key?.kind !== "name" || tokens[cursor + 1]?.value !== ":") {
|
|
372
|
+
filter = UNRESOLVED;
|
|
373
|
+
break;
|
|
374
|
+
}
|
|
375
|
+
const parsed = parseValue(cursor + 2);
|
|
376
|
+
if (parsed.end === cursor + 2)
|
|
377
|
+
break;
|
|
378
|
+
if (key.value === "filter")
|
|
379
|
+
filter = parsed.value;
|
|
380
|
+
cursor = parsed.end;
|
|
381
|
+
}
|
|
382
|
+
checkField(token.value, filter);
|
|
383
|
+
}
|
|
384
|
+
return { accountTags, zoneTags, openTags, unscopedFields };
|
|
385
|
+
}
|
|
@@ -1,29 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
|
|
4
|
-
export
|
|
1
|
+
import type { ConnectorCallAdmissionPolicy } from "../../types.js";
|
|
2
|
+
import { type CloudflarePin } from "./rest.js";
|
|
3
|
+
export { CLOUDFLARE_API_BASE } from "./rest.js";
|
|
4
|
+
export type { CloudflarePin } from "./rest.js";
|
|
5
5
|
/** Cloudflare's official whole-API hosted MCP endpoint. */
|
|
6
6
|
export declare const CLOUDFLARE_MCP_ENDPOINT = "https://mcp.cloudflare.com/mcp";
|
|
7
|
-
/** Authentication schemes accepted by Cloudflare's v4 API. */
|
|
8
|
-
export type CloudflareAuthentication = "apiToken" | "globalApiKey";
|
|
9
|
-
/**
|
|
10
|
-
* The 21 record types Cloudflare accepts. Eight carry a single `content`
|
|
11
|
-
* string; the other thirteen carry a per-type structured `data` object.
|
|
12
|
-
* `list_dns_records` filters on all 21 because reading them costs nothing.
|
|
13
|
-
*/
|
|
14
|
-
export declare const CLOUDFLARE_DNS_RECORD_TYPES: readonly ["A", "AAAA", "CAA", "CERT", "CNAME", "DNSKEY", "DS", "HTTPS", "LOC", "MX", "NAPTR", "NS", "OPENPGPKEY", "PTR", "SMIMEA", "SRV", "SSHFP", "SVCB", "TLSA", "TXT", "URI"];
|
|
15
|
-
/**
|
|
16
|
-
* Content-valued types only, and the only types `create_dns_record` and
|
|
17
|
-
* `update_dns_record` accept. Covering the thirteen structured-`data` types
|
|
18
|
-
* would mean either a free-form `data` passthrough — the untyped body this
|
|
19
|
-
* connection exists to avoid — or thirteen more hand-written schemas for record
|
|
20
|
-
* types that are rare in day-to-day zone administration. They stay fully
|
|
21
|
-
* readable; writing one goes through the approval-gated raw mutation tool with
|
|
22
|
-
* Cloudflare's documented per-type `data` body.
|
|
23
|
-
*/
|
|
24
|
-
export declare const CLOUDFLARE_CONTENT_DNS_RECORD_TYPES: readonly ["A", "AAAA", "CNAME", "MX", "NS", "OPENPGPKEY", "PTR", "TXT"];
|
|
25
7
|
interface CloudflareCommonOptions {
|
|
26
|
-
/** Human-readable display name; defaults identify the
|
|
8
|
+
/** Human-readable display name; defaults identify the implementation. */
|
|
27
9
|
title?: string;
|
|
28
10
|
/** Downstream auth ownership. Defaults to one shared deployment grant. */
|
|
29
11
|
authScope?: "shared" | "personal";
|
|
@@ -34,44 +16,58 @@ interface CloudflareCommonOptions {
|
|
|
34
16
|
/** Connector-specific inline result limit; omit to inherit the deployment. */
|
|
35
17
|
maxResultBytes?: number;
|
|
36
18
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
19
|
+
/** Cloudflare's hosted MCP server (`search` and `execute`) over OAuth. */
|
|
20
|
+
export interface CloudflareOAuthOptions extends CloudflareCommonOptions {
|
|
21
|
+
auth: {
|
|
22
|
+
type: "oauth";
|
|
23
|
+
};
|
|
24
|
+
/** Optional per-runtime downstream call-admission policy. */
|
|
25
|
+
callAdmission?: ConnectorCallAdmissionPolicy;
|
|
26
|
+
}
|
|
27
|
+
interface CloudflareKeyOptions extends CloudflareCommonOptions {
|
|
40
28
|
/**
|
|
41
|
-
* Default account id
|
|
42
|
-
*
|
|
43
|
-
* with `list_accounts`.
|
|
29
|
+
* Default account id: fills `{account_id}` in paths and verifies an
|
|
30
|
+
* account-owned token. Routing only; `pin` is the boundary.
|
|
44
31
|
*/
|
|
45
32
|
accountId?: string;
|
|
46
|
-
/**
|
|
47
|
-
* Default zone id for zone-scoped tools. When set, `zoneId` becomes an
|
|
48
|
-
* optional argument; when omitted, agents must pass one and can find it with
|
|
49
|
-
* `list_zones`.
|
|
50
|
-
*/
|
|
33
|
+
/** Default zone id: fills `{zone_id}` in paths. Routing only; `pin` is the boundary. */
|
|
51
34
|
zoneId?: string;
|
|
52
35
|
/** API base override for a proxy or a test double. Defaults to the v4 API. */
|
|
53
36
|
baseUrl?: string;
|
|
54
|
-
/** Authentication scheme. Defaults to the recommended scoped API token. */
|
|
55
|
-
authentication?: CloudflareAuthentication;
|
|
56
|
-
/** Credential presentation override; credentials are always operator-managed. */
|
|
57
|
-
credential?: ConnectorCredentialConfig;
|
|
58
37
|
/** Simultaneous downstream calls. Defaults to 6. */
|
|
59
38
|
maxConcurrency?: number;
|
|
60
39
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Connecta's REST connector with one operator-managed, scoped API token. A
|
|
42
|
+
* `pin` restricts it further to named accounts and zones.
|
|
43
|
+
*/
|
|
44
|
+
export interface CloudflareApiTokenOptions extends CloudflareKeyOptions {
|
|
45
|
+
auth: {
|
|
46
|
+
type: "apiToken";
|
|
47
|
+
};
|
|
48
|
+
/** Accounts and zones this connector may touch, enforced on every call. */
|
|
49
|
+
pin?: CloudflarePin;
|
|
67
50
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Connecta's REST connector with a legacy Global API Key and its user's
|
|
53
|
+
* email. The key acts as the user everywhere, so the connector requires a
|
|
54
|
+
* `pin` unless `unpinned: true` states the whole estate is intended.
|
|
55
|
+
*/
|
|
56
|
+
export interface CloudflareGlobalApiKeyOptions extends CloudflareKeyOptions {
|
|
57
|
+
auth: {
|
|
58
|
+
type: "globalApiKey";
|
|
59
|
+
};
|
|
60
|
+
/** Accounts and zones this connector may touch; required unless `unpinned`. */
|
|
61
|
+
pin?: CloudflarePin;
|
|
62
|
+
/** Accept every account and zone the key's user can reach. */
|
|
63
|
+
unpinned?: true;
|
|
70
64
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
65
|
+
export type CloudflareOptions = CloudflareOAuthOptions | CloudflareApiTokenOptions | CloudflareGlobalApiKeyOptions;
|
|
66
|
+
/**
|
|
67
|
+
* A maintained Cloudflare connection. `auth` selects the implementation:
|
|
68
|
+
* OAuth reaches Cloudflare's hosted MCP server; an API token or a Global API
|
|
69
|
+
* Key reaches Connecta's REST connector to the v4 API.
|
|
70
|
+
*/
|
|
71
|
+
export declare const cloudflare: import("../../provider.js").ProviderFactory<CloudflareOptions>;
|
|
75
72
|
/** @deprecated Read `cloudflare.definition.classify` instead. Kept for existing imports. */
|
|
76
73
|
export declare const CLOUDFLARE_MCP_VETTED_CATALOG: import("../../catalog-drift.js").VettedCatalog;
|
|
77
|
-
export {};
|