@framers/agentos-ext-cloud-cloudflare-pages 0.1.0 → 0.1.2

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/manifest.json CHANGED
@@ -2,16 +2,16 @@
2
2
  "$schema": "https://agentos.sh/schemas/extension-manifest-v1.json",
3
3
  "id": "com.framers.cloud.cloudflare-pages",
4
4
  "name": "Cloudflare Pages & Workers",
5
- "version": "0.1.0",
5
+ "version": "0.1.2",
6
6
  "description": "Deploy Cloudflare Pages projects, manage DNS records, and deploy Workers. Supports static sites, full-stack frameworks, and edge functions via the Cloudflare API v4.",
7
7
  "author": {
8
8
  "name": "Frame.dev",
9
9
  "email": "support@frame.dev",
10
- "url": "https://github.com/framersai"
10
+ "url": "https://github.com/framerslab"
11
11
  },
12
- "license": "MIT",
12
+ "license": "Apache-2.0",
13
13
  "keywords": ["cloud", "cloudflare", "pages", "workers", "dns", "deploy", "edge"],
14
- "agentosVersion": "^2.0.0",
14
+ "agentosVersion": ">=0.10.40",
15
15
  "categories": ["tool", "cloud"],
16
16
  "platforms": [],
17
17
  "sdk": "none",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@framers/agentos-ext-cloud-cloudflare-pages",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Cloudflare Pages cloud extension — deploy Pages projects, manage DNS records, and deploy Workers via the Cloudflare API v4.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -37,29 +37,37 @@
37
37
  "url": "https://github.com/jddunn"
38
38
  }
39
39
  ],
40
- "license": "MIT",
40
+ "license": "Apache-2.0",
41
41
  "repository": {
42
42
  "type": "git",
43
- "url": "https://github.com/framersai/agentos-extensions.git",
43
+ "url": "git+https://github.com/framerslab/agentos-extensions.git",
44
44
  "directory": "registry/curated/cloud/cloudflare-pages"
45
45
  },
46
46
  "publishConfig": {
47
47
  "access": "public"
48
48
  },
49
49
  "peerDependencies": {
50
- "@framers/agentos": "^0.5.2"
50
+ "@framers/agentos": ">=0.10.40"
51
51
  },
52
52
  "devDependencies": {
53
- "@framers/agentos": "^0.6.6",
54
53
  "@types/node": "^20.12.12",
55
54
  "rimraf": "^5.0.7",
56
55
  "typescript": "^5.4.5",
57
- "vitest": "^1.6.0"
56
+ "vitest": "^3.2.7",
57
+ "@framers/agentos": "^0.13.40"
58
58
  },
59
+ "homepage": "https://github.com/framerslab/agentos-extensions/tree/master/registry/curated/cloud/cloudflare-pages#readme",
60
+ "bugs": {
61
+ "url": "https://github.com/framerslab/agentos-extensions/issues"
62
+ },
63
+ "files": [
64
+ "dist",
65
+ "manifest.json"
66
+ ],
59
67
  "scripts": {
60
68
  "build": "tsc",
61
69
  "dev": "tsc --watch",
62
- "test": "vitest run",
70
+ "test": "vitest run --passWithNoTests",
63
71
  "test:watch": "vitest",
64
72
  "typecheck": "tsc --noEmit",
65
73
  "clean": "rimraf dist"
@@ -1,606 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * @fileoverview Cloudflare REST API service layer.
4
- *
5
- * Wraps the Cloudflare API v4 for Pages project deployment, DNS record
6
- * management, and Worker script deployment.
7
- */
8
-
9
- // ---------------------------------------------------------------------------
10
- // Types
11
- // ---------------------------------------------------------------------------
12
-
13
- export interface CloudflareConfig {
14
- /** Cloudflare API token (from https://dash.cloudflare.com/profile/api-tokens) */
15
- apiToken: string;
16
- /** Cloudflare Account ID */
17
- accountId: string;
18
- /** API base URL (defaults to https://api.cloudflare.com/client/v4) */
19
- baseUrl?: string;
20
- }
21
-
22
- export interface CloudflarePagesProject {
23
- id: string;
24
- name: string;
25
- subdomain: string;
26
- productionBranch: string;
27
- createdOn: string;
28
- domains: string[];
29
- source?: { type: string; config?: { owner: string; repo_name: string; production_branch: string } };
30
- latestDeployment?: CloudflareDeployment;
31
- }
32
-
33
- export interface CloudflareDeployment {
34
- id: string;
35
- projectId: string;
36
- projectName: string;
37
- url: string;
38
- environment: 'production' | 'preview';
39
- createdOn: string;
40
- modifiedOn: string;
41
- latestStage: { name: string; status: 'idle' | 'active' | 'success' | 'failure' };
42
- deploymentTrigger?: { type: string; metadata?: { branch: string; commitHash: string; commitMessage: string } };
43
- stages: Array<{ name: string; status: string; startedOn?: string; endedOn?: string }>;
44
- }
45
-
46
- export interface CloudflareDnsRecord {
47
- id: string;
48
- type: 'A' | 'AAAA' | 'CNAME' | 'TXT' | 'MX' | 'NS' | 'SRV' | 'CAA';
49
- name: string;
50
- content: string;
51
- ttl: number;
52
- proxied?: boolean;
53
- priority?: number;
54
- comment?: string;
55
- }
56
-
57
- export interface CloudflareZone {
58
- id: string;
59
- name: string;
60
- status: string;
61
- nameServers: string[];
62
- }
63
-
64
- export interface CloudflareWorker {
65
- id: string;
66
- tag: string;
67
- etag: string;
68
- size: number;
69
- createdOn: string;
70
- modifiedOn: string;
71
- }
72
-
73
- export interface DeployPagesResult {
74
- id: string;
75
- url: string;
76
- environment: string;
77
- projectName: string;
78
- latestStage: { name: string; status: string };
79
- }
80
-
81
- export interface CreatePagesProjectOptions {
82
- /** Git repository URL (e.g. "https://github.com/user/repo") */
83
- gitUrl: string;
84
- /** Production branch (defaults to "main") */
85
- productionBranch?: string;
86
- /** Build command (e.g. "npm run build") */
87
- buildCommand?: string;
88
- /** Build output directory (e.g. "dist", "build", ".next") */
89
- buildOutputDirectory?: string;
90
- /** Environment variables for the build */
91
- envVars?: Record<string, string>;
92
- }
93
-
94
- export interface DeployWorkerOptions {
95
- /** Worker script name */
96
- name: string;
97
- /** JavaScript/TypeScript source code */
98
- script: string;
99
- /** Route patterns (e.g. ["example.com/*"]) */
100
- routes?: string[];
101
- /** Compatibility date (e.g. "2024-01-01") */
102
- compatibilityDate?: string;
103
- /** Environment variables / secrets bindings */
104
- bindings?: Record<string, string>;
105
- }
106
-
107
- // ---------------------------------------------------------------------------
108
- // Service
109
- // ---------------------------------------------------------------------------
110
-
111
- export class CloudflareService {
112
- private config: CloudflareConfig;
113
- private running = false;
114
-
115
- constructor(config: CloudflareConfig) {
116
- this.config = {
117
- ...config,
118
- baseUrl: config.baseUrl ?? 'https://api.cloudflare.com/client/v4',
119
- };
120
- }
121
-
122
- async initialize(): Promise<void> {
123
- // Validate token by verifying the token
124
- const res = await this.fetch('/user/tokens/verify');
125
- if (!res.ok) {
126
- const body = await res.text();
127
- throw new Error(`Cloudflare auth failed: ${res.status} ${body}`);
128
- }
129
- this.running = true;
130
- }
131
-
132
- async shutdown(): Promise<void> {
133
- this.running = false;
134
- }
135
-
136
- get isRunning(): boolean {
137
- return this.running;
138
- }
139
-
140
- // ── Pages Projects ──────────────────────────────────────────────────────
141
-
142
- /** List all Pages projects. */
143
- async listProjects(limit = 25): Promise<CloudflarePagesProject[]> {
144
- const res = await this.fetch(
145
- `/accounts/${this.config.accountId}/pages/projects?per_page=${limit}`,
146
- );
147
- const data = await this.json(res);
148
- return (data.result ?? []).map((p: any) => this.mapProject(p));
149
- }
150
-
151
- /** Get a Pages project by name. */
152
- async getProject(projectName: string): Promise<CloudflarePagesProject> {
153
- const res = await this.fetch(
154
- `/accounts/${this.config.accountId}/pages/projects/${encodeURIComponent(projectName)}`,
155
- );
156
- const data = await this.json(res);
157
- return this.mapProject(data.result);
158
- }
159
-
160
- /** Create a new Pages project. */
161
- async createProject(
162
- name: string,
163
- opts?: {
164
- productionBranch?: string;
165
- buildCommand?: string;
166
- buildOutputDirectory?: string;
167
- gitUrl?: string;
168
- },
169
- ): Promise<CloudflarePagesProject> {
170
- const body: Record<string, unknown> = {
171
- name,
172
- production_branch: opts?.productionBranch ?? 'main',
173
- };
174
-
175
- if (opts?.buildCommand || opts?.buildOutputDirectory) {
176
- body.build_config = {
177
- ...(opts?.buildCommand ? { build_command: opts.buildCommand } : {}),
178
- ...(opts?.buildOutputDirectory ? { destination_dir: opts.buildOutputDirectory } : {}),
179
- };
180
- }
181
-
182
- if (opts?.gitUrl) {
183
- const { owner, repo } = this.parseGitUrl(opts.gitUrl);
184
- body.source = {
185
- type: 'github',
186
- config: {
187
- owner,
188
- repo_name: repo,
189
- production_branch: opts?.productionBranch ?? 'main',
190
- },
191
- };
192
- }
193
-
194
- const res = await this.fetch(
195
- `/accounts/${this.config.accountId}/pages/projects`,
196
- { method: 'POST', body: JSON.stringify(body) },
197
- );
198
- const data = await this.json(res);
199
- return this.mapProject(data.result);
200
- }
201
-
202
- // ── Deployments ─────────────────────────────────────────────────────────
203
-
204
- /** Create a deployment for a Pages project (triggers build from latest commit). */
205
- async createDeployment(
206
- projectName: string,
207
- opts?: { branch?: string },
208
- ): Promise<DeployPagesResult> {
209
- const formData = new FormData();
210
- if (opts?.branch) {
211
- formData.append('branch', opts.branch);
212
- }
213
-
214
- const res = await this.fetch(
215
- `/accounts/${this.config.accountId}/pages/projects/${encodeURIComponent(projectName)}/deployments`,
216
- {
217
- method: 'POST',
218
- body: formData,
219
- headers: {}, // Let fetch set content-type for FormData
220
- },
221
- );
222
- const data = await this.json(res);
223
- const d = data.result;
224
- return {
225
- id: d.id,
226
- url: d.url ? (d.url.startsWith('http') ? d.url : `https://${d.url}`) : '',
227
- environment: d.environment ?? 'production',
228
- projectName: d.project_name ?? projectName,
229
- latestStage: d.latest_stage ?? { name: 'queued', status: 'idle' },
230
- };
231
- }
232
-
233
- /** Deploy from a Git URL — creates project if needed, then triggers deployment. */
234
- async deployFromGit(opts: CreatePagesProjectOptions): Promise<DeployPagesResult> {
235
- const projectName = this.inferProjectName(opts.gitUrl);
236
-
237
- // Try to get existing project
238
- let _project: CloudflarePagesProject | null = null;
239
- try {
240
- _project = await this.getProject(projectName);
241
- } catch {
242
- // Project doesn't exist — create it
243
- _project = await this.createProject(projectName, {
244
- gitUrl: opts.gitUrl,
245
- productionBranch: opts.productionBranch,
246
- buildCommand: opts.buildCommand,
247
- buildOutputDirectory: opts.buildOutputDirectory,
248
- });
249
- }
250
-
251
- // Set env vars if provided
252
- if (opts.envVars && Object.keys(opts.envVars).length > 0) {
253
- await this.setProjectEnvVars(projectName, opts.envVars);
254
- }
255
-
256
- // Trigger deployment
257
- return this.createDeployment(projectName, { branch: opts.productionBranch });
258
- }
259
-
260
- /** Get deployment status. */
261
- async getDeployment(
262
- projectName: string,
263
- deploymentId: string,
264
- ): Promise<CloudflareDeployment> {
265
- const res = await this.fetch(
266
- `/accounts/${this.config.accountId}/pages/projects/${encodeURIComponent(projectName)}/deployments/${deploymentId}`,
267
- );
268
- const data = await this.json(res);
269
- return this.mapDeployment(data.result);
270
- }
271
-
272
- /** List recent deployments for a project. */
273
- async listDeployments(
274
- projectName: string,
275
- limit = 10,
276
- ): Promise<CloudflareDeployment[]> {
277
- const res = await this.fetch(
278
- `/accounts/${this.config.accountId}/pages/projects/${encodeURIComponent(projectName)}/deployments?per_page=${limit}`,
279
- );
280
- const data = await this.json(res);
281
- return (data.result ?? []).map((d: any) => this.mapDeployment(d));
282
- }
283
-
284
- /** Set environment variables on a Pages project. */
285
- async setProjectEnvVars(
286
- projectName: string,
287
- vars: Record<string, string>,
288
- environment: 'production' | 'preview' = 'production',
289
- ): Promise<void> {
290
- const envVarMap: Record<string, { value: string; type: string }> = {};
291
- for (const [key, value] of Object.entries(vars)) {
292
- envVarMap[key] = { value, type: 'plain_text' };
293
- }
294
-
295
- const body = {
296
- deployment_configs: {
297
- [environment]: {
298
- env_vars: envVarMap,
299
- },
300
- },
301
- };
302
-
303
- await this.fetch(
304
- `/accounts/${this.config.accountId}/pages/projects/${encodeURIComponent(projectName)}`,
305
- { method: 'PATCH', body: JSON.stringify(body) },
306
- );
307
- }
308
-
309
- // ── DNS ─────────────────────────────────────────────────────────────────
310
-
311
- /** Look up a zone by domain name. */
312
- async getZoneByDomain(domain: string): Promise<CloudflareZone> {
313
- const res = await this.fetch(`/zones?name=${encodeURIComponent(domain)}`);
314
- const data = await this.json(res);
315
- const zones = data.result ?? [];
316
- if (zones.length === 0) {
317
- throw new Error(`No Cloudflare zone found for domain: ${domain}`);
318
- }
319
- return this.mapZone(zones[0]);
320
- }
321
-
322
- /** List DNS records for a zone. */
323
- async listDnsRecords(
324
- zoneId: string,
325
- opts?: { type?: string; name?: string },
326
- ): Promise<CloudflareDnsRecord[]> {
327
- let path = `/zones/${zoneId}/dns_records?per_page=100`;
328
- if (opts?.type) path += `&type=${opts.type}`;
329
- if (opts?.name) path += `&name=${encodeURIComponent(opts.name)}`;
330
-
331
- const res = await this.fetch(path);
332
- const data = await this.json(res);
333
- return (data.result ?? []).map((r: any) => this.mapDnsRecord(r));
334
- }
335
-
336
- /** Create a DNS record. */
337
- async createDnsRecord(
338
- zoneId: string,
339
- record: {
340
- type: string;
341
- name: string;
342
- content: string;
343
- ttl?: number;
344
- proxied?: boolean;
345
- priority?: number;
346
- comment?: string;
347
- },
348
- ): Promise<CloudflareDnsRecord> {
349
- const body: Record<string, unknown> = {
350
- type: record.type,
351
- name: record.name,
352
- content: record.content,
353
- ttl: record.ttl ?? 1, // 1 = auto
354
- proxied: record.proxied ?? false,
355
- };
356
- if (record.priority !== undefined) body.priority = record.priority;
357
- if (record.comment) body.comment = record.comment;
358
-
359
- const res = await this.fetch(`/zones/${zoneId}/dns_records`, {
360
- method: 'POST',
361
- body: JSON.stringify(body),
362
- });
363
- const data = await this.json(res);
364
- return this.mapDnsRecord(data.result);
365
- }
366
-
367
- /** Update a DNS record. */
368
- async updateDnsRecord(
369
- zoneId: string,
370
- recordId: string,
371
- record: {
372
- type?: string;
373
- name?: string;
374
- content?: string;
375
- ttl?: number;
376
- proxied?: boolean;
377
- priority?: number;
378
- comment?: string;
379
- },
380
- ): Promise<CloudflareDnsRecord> {
381
- const res = await this.fetch(`/zones/${zoneId}/dns_records/${recordId}`, {
382
- method: 'PUT',
383
- body: JSON.stringify(record),
384
- });
385
- const data = await this.json(res);
386
- return this.mapDnsRecord(data.result);
387
- }
388
-
389
- /** Delete a DNS record. */
390
- async deleteDnsRecord(zoneId: string, recordId: string): Promise<void> {
391
- await this.fetch(`/zones/${zoneId}/dns_records/${recordId}`, {
392
- method: 'DELETE',
393
- });
394
- }
395
-
396
- // ── Workers ─────────────────────────────────────────────────────────────
397
-
398
- /** Deploy a Worker script. */
399
- async deployWorker(opts: DeployWorkerOptions): Promise<CloudflareWorker> {
400
- // Upload the script
401
- const formData = new FormData();
402
-
403
- const metadata: Record<string, unknown> = {
404
- main_module: 'worker.js',
405
- compatibility_date: opts.compatibilityDate ?? '2024-01-01',
406
- };
407
-
408
- // Add bindings for env vars / secrets
409
- if (opts.bindings && Object.keys(opts.bindings).length > 0) {
410
- metadata.bindings = Object.entries(opts.bindings).map(([name, text]) => ({
411
- type: 'plain_text',
412
- name,
413
- text,
414
- }));
415
- }
416
-
417
- formData.append(
418
- 'metadata',
419
- new Blob([JSON.stringify(metadata)], { type: 'application/json' }),
420
- );
421
- formData.append(
422
- 'worker.js',
423
- new Blob([opts.script], { type: 'application/javascript+module' }),
424
- 'worker.js',
425
- );
426
-
427
- const res = await this.fetch(
428
- `/accounts/${this.config.accountId}/workers/scripts/${encodeURIComponent(opts.name)}`,
429
- {
430
- method: 'PUT',
431
- body: formData,
432
- headers: {}, // Let fetch set content-type for FormData
433
- },
434
- );
435
- const data = await this.json(res);
436
- const result = data.result;
437
-
438
- // Set up routes if provided
439
- if (opts.routes && opts.routes.length > 0) {
440
- for (const pattern of opts.routes) {
441
- try {
442
- // Determine zone from route pattern
443
- const routeDomain = pattern.replace(/\/\*$/, '').replace(/^\*\./, '');
444
- const zone = await this.getZoneByDomain(routeDomain);
445
- await this.fetch(`/zones/${zone.id}/workers/routes`, {
446
- method: 'POST',
447
- body: JSON.stringify({ pattern, script: opts.name }),
448
- });
449
- } catch {
450
- // Route setup is best-effort — zone may not be managed
451
- }
452
- }
453
- }
454
-
455
- return {
456
- id: result?.id ?? opts.name,
457
- tag: result?.tag ?? '',
458
- etag: result?.etag ?? '',
459
- size: result?.size ?? 0,
460
- createdOn: result?.created_on ?? new Date().toISOString(),
461
- modifiedOn: result?.modified_on ?? new Date().toISOString(),
462
- };
463
- }
464
-
465
- /** List Worker scripts. */
466
- async listWorkers(): Promise<CloudflareWorker[]> {
467
- const res = await this.fetch(
468
- `/accounts/${this.config.accountId}/workers/scripts`,
469
- );
470
- const data = await this.json(res);
471
- return (data.result ?? []).map((w: any) => ({
472
- id: w.id,
473
- tag: w.tag ?? '',
474
- etag: w.etag ?? '',
475
- size: w.size ?? 0,
476
- createdOn: w.created_on ?? '',
477
- modifiedOn: w.modified_on ?? '',
478
- }));
479
- }
480
-
481
- /** Delete a Worker script. */
482
- async deleteWorker(name: string): Promise<void> {
483
- await this.fetch(
484
- `/accounts/${this.config.accountId}/workers/scripts/${encodeURIComponent(name)}`,
485
- { method: 'DELETE' },
486
- );
487
- }
488
-
489
- // ── Helpers ─────────────────────────────────────────────────────────────
490
-
491
- private async fetch(path: string, init?: RequestInit & { headers?: Record<string, string> }): Promise<Response> {
492
- const url = `${this.config.baseUrl}${path}`;
493
- const headers: Record<string, string> = {
494
- 'Authorization': `Bearer ${this.config.apiToken}`,
495
- ...(init?.headers ?? {}),
496
- };
497
-
498
- // Only set Content-Type for non-FormData requests
499
- if (!(init?.body instanceof FormData) && !headers['Content-Type']) {
500
- headers['Content-Type'] = 'application/json';
501
- }
502
-
503
- return globalThis.fetch(url, {
504
- ...init,
505
- headers,
506
- });
507
- }
508
-
509
- private async json(res: Response): Promise<any> {
510
- if (!res.ok) {
511
- const text = await res.text();
512
- throw new Error(`Cloudflare API ${res.status}: ${text.slice(0, 500)}`);
513
- }
514
- return res.json();
515
- }
516
-
517
- private parseGitUrl(url: string): { owner: string; repo: string } {
518
- const match = url.match(/(?:github\.com|gitlab\.com|bitbucket\.org)[/:]([^/]+)\/([^/.]+)/);
519
- if (!match) throw new Error(`Cannot parse git URL: ${url}`);
520
- return { owner: match[1], repo: match[2] };
521
- }
522
-
523
- private inferProjectName(gitUrl: string): string {
524
- const { repo } = this.parseGitUrl(gitUrl);
525
- return repo.toLowerCase().replace(/[^a-z0-9-]/g, '-');
526
- }
527
-
528
- private mapProject(data: any): CloudflarePagesProject {
529
- return {
530
- id: data.id,
531
- name: data.name,
532
- subdomain: data.subdomain ?? `${data.name}.pages.dev`,
533
- productionBranch: data.production_branch ?? 'main',
534
- createdOn: data.created_on ?? '',
535
- domains: data.domains ?? [],
536
- source: data.source
537
- ? {
538
- type: data.source.type,
539
- config: data.source.config
540
- ? {
541
- owner: data.source.config.owner,
542
- repo_name: data.source.config.repo_name,
543
- production_branch: data.source.config.production_branch,
544
- }
545
- : undefined,
546
- }
547
- : undefined,
548
- latestDeployment: data.latest_deployment
549
- ? this.mapDeployment(data.latest_deployment)
550
- : undefined,
551
- };
552
- }
553
-
554
- private mapDeployment(data: any): CloudflareDeployment {
555
- return {
556
- id: data.id,
557
- projectId: data.project_id ?? '',
558
- projectName: data.project_name ?? '',
559
- url: data.url ? (data.url.startsWith('http') ? data.url : `https://${data.url}`) : '',
560
- environment: data.environment ?? 'production',
561
- createdOn: data.created_on ?? '',
562
- modifiedOn: data.modified_on ?? '',
563
- latestStage: data.latest_stage ?? { name: 'queued', status: 'idle' },
564
- deploymentTrigger: data.deployment_trigger
565
- ? {
566
- type: data.deployment_trigger.type,
567
- metadata: data.deployment_trigger.metadata
568
- ? {
569
- branch: data.deployment_trigger.metadata.branch ?? '',
570
- commitHash: data.deployment_trigger.metadata.commit_hash ?? '',
571
- commitMessage: data.deployment_trigger.metadata.commit_message ?? '',
572
- }
573
- : undefined,
574
- }
575
- : undefined,
576
- stages: (data.stages ?? []).map((s: any) => ({
577
- name: s.name,
578
- status: s.status,
579
- startedOn: s.started_on,
580
- endedOn: s.ended_on,
581
- })),
582
- };
583
- }
584
-
585
- private mapDnsRecord(data: any): CloudflareDnsRecord {
586
- return {
587
- id: data.id,
588
- type: data.type,
589
- name: data.name,
590
- content: data.content,
591
- ttl: data.ttl ?? 1,
592
- proxied: data.proxied,
593
- priority: data.priority,
594
- comment: data.comment,
595
- };
596
- }
597
-
598
- private mapZone(data: any): CloudflareZone {
599
- return {
600
- id: data.id,
601
- name: data.name,
602
- status: data.status,
603
- nameServers: data.name_servers ?? [],
604
- };
605
- }
606
- }