@aigne/afs-dns 1.11.0-beta.11 → 1.11.0-beta.13

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/index.mjs CHANGED
@@ -1,4 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
+ import { AFSNotFoundError } from "@aigne/afs";
3
+ import { AFSBaseProvider, Actions, Delete, Explain, List, Meta, Read, Search, Stat, Write } from "@aigne/afs/provider";
2
4
  import { minimatch } from "minimatch";
3
5
  import { z } from "zod";
4
6
  import { ChangeResourceRecordSetsCommand, GetHostedZoneCommand, ListHostedZonesCommand, ListResourceRecordSetsCommand, Route53Client } from "@aws-sdk/client-route-53";
@@ -1011,9 +1013,23 @@ function validateRecord(record) {
1011
1013
  };
1012
1014
  }
1013
1015
 
1016
+ //#endregion
1017
+ //#region \0@oxc-project+runtime@0.108.0/helpers/decorate.js
1018
+ function __decorate(decorators, target, key, desc) {
1019
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
1020
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
1021
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
1022
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
1023
+ }
1024
+
1014
1025
  //#endregion
1015
1026
  //#region src/provider/dns-provider.ts
1016
- var DNSProvider = class {
1027
+ /**
1028
+ * DNS Provider
1029
+ *
1030
+ * AFS module for DNS zone management via various providers.
1031
+ */
1032
+ var DNSProvider = class extends AFSBaseProvider {
1017
1033
  name;
1018
1034
  description;
1019
1035
  accessMode;
@@ -1028,6 +1044,48 @@ var DNSProvider = class {
1028
1044
  static schema() {
1029
1045
  return z.object({ zone: z.string() });
1030
1046
  }
1047
+ static treeSchema() {
1048
+ return {
1049
+ operations: [
1050
+ "list",
1051
+ "read",
1052
+ "write",
1053
+ "delete",
1054
+ "search",
1055
+ "stat",
1056
+ "explain"
1057
+ ],
1058
+ tree: {
1059
+ "/": {
1060
+ kind: "dns:zone",
1061
+ operations: [
1062
+ "list",
1063
+ "read",
1064
+ "search"
1065
+ ]
1066
+ },
1067
+ "/_zone": {
1068
+ kind: "dns:zone-meta",
1069
+ operations: ["read"]
1070
+ },
1071
+ "/{record-name}": {
1072
+ kind: "dns:record",
1073
+ operations: [
1074
+ "read",
1075
+ "write",
1076
+ "delete"
1077
+ ],
1078
+ destructive: ["delete"]
1079
+ }
1080
+ },
1081
+ auth: {
1082
+ type: "aws",
1083
+ env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
1084
+ },
1085
+ bestFor: ["DNS zone management", "record CRUD"],
1086
+ notFor: ["domain registration"]
1087
+ };
1088
+ }
1031
1089
  /**
1032
1090
  * Provider manifest for URI-based discovery
1033
1091
  */
@@ -1036,12 +1094,40 @@ var DNSProvider = class {
1036
1094
  name: "dns",
1037
1095
  description: "DNS zone management — Route53 and Google Cloud DNS.\n- List, create, update, and delete DNS records (A, AAAA, CNAME, MX, TXT, etc.)\n- Audit logging and rate limiting for safe zone modifications\n- Path structure: `/{zone}/{record-name}`",
1038
1096
  uriTemplate: "dns://{zone}",
1039
- category: "cloud-dns",
1097
+ category: "network",
1040
1098
  schema: z.object({ zone: z.string() }),
1041
- tags: ["dns", "cloud"]
1099
+ tags: ["dns", "cloud"],
1100
+ capabilityTags: [
1101
+ "read-write",
1102
+ "crud",
1103
+ "destructive",
1104
+ "auth:aws",
1105
+ "remote",
1106
+ "cloud",
1107
+ "http"
1108
+ ],
1109
+ security: {
1110
+ riskLevel: "external",
1111
+ resourceAccess: ["cloud-api"],
1112
+ requires: ["cloud-credentials"],
1113
+ dataSensitivity: ["system-config"],
1114
+ notes: ["Can modify DNS records — misconfiguration may cause service outages"]
1115
+ },
1116
+ capabilities: {
1117
+ network: {
1118
+ egress: true,
1119
+ allowedDomains: ["*.amazonaws.com", "*.googleapis.com"]
1120
+ },
1121
+ secrets: [
1122
+ "aws/access-key-id",
1123
+ "aws/secret-access-key",
1124
+ "gcp/credentials"
1125
+ ]
1126
+ }
1042
1127
  };
1043
1128
  }
1044
1129
  constructor(options) {
1130
+ super();
1045
1131
  this.zone = options.zone;
1046
1132
  this.adapter = options.adapter;
1047
1133
  this.accessMode = options.accessMode ?? "readonly";
@@ -1051,70 +1137,163 @@ var DNSProvider = class {
1051
1137
  this.name = `dns:${this.zone}`;
1052
1138
  this.description = `DNS zone: ${this.zone}`;
1053
1139
  }
1054
- async list(path, options) {
1055
- const normalizedPath = this.normalizePath(path);
1056
- if ((options?.maxDepth ?? 1) === 0) return { data: [] };
1057
- if (normalizedPath === "/") {
1058
- const entries = [];
1059
- const names = await this.adapter.listRecords(this.zone);
1060
- for (const name of names) entries.push({
1061
- id: name,
1062
- path: `/${name}`,
1063
- summary: name === "_zone" ? "Zone metadata" : `DNS record: ${name}`,
1064
- meta: {
1065
- kind: name === "_zone" ? "dns:zone-meta" : "dns:record",
1066
- childrenCount: 0
1067
- }
1068
- });
1069
- return { data: entries };
1070
- }
1071
- const recordName = this.extractRecordName(normalizedPath);
1072
- if ((await this.adapter.getRecord(this.zone, recordName)).records.length === 0 && recordName !== "_zone") return { data: [] };
1073
- return { data: [{
1074
- id: recordName,
1075
- path: normalizedPath,
1076
- summary: recordName === "_zone" ? "Zone metadata" : `DNS record: ${recordName}`,
1140
+ async listRoot(_ctx) {
1141
+ const entries = [];
1142
+ const names = await this.adapter.listRecords(this.zone);
1143
+ for (const name of names) entries.push({
1144
+ id: name,
1145
+ path: `/${name}`,
1146
+ summary: name === "_zone" ? "Zone metadata" : `DNS record: ${name}`,
1077
1147
  meta: {
1078
- kind: recordName === "_zone" ? "dns:zone-meta" : "dns:record",
1148
+ kind: name === "_zone" ? "dns:zone-meta" : "dns:record",
1079
1149
  childrenCount: 0
1080
1150
  }
1081
- }] };
1151
+ });
1152
+ return { data: entries };
1082
1153
  }
1083
- async read(path, _options) {
1084
- const normalizedPath = this.normalizePath(path);
1085
- if (normalizedPath === "/.meta/.capabilities") return this.readCapabilities();
1086
- const { recordName, type } = this.parsePathWithQuery(normalizedPath);
1087
- if (recordName === "_zone") {
1088
- const metadata = await this.adapter.getZoneMetadata(this.zone);
1089
- return { data: {
1090
- id: "_zone",
1091
- path: "/_zone",
1092
- summary: `Zone metadata for ${this.zone}`,
1093
- meta: { kind: "dns:zone-meta" },
1094
- content: metadata
1095
- } };
1096
- }
1154
+ async listRecord(ctx) {
1155
+ const recordName = ctx.params.recordName;
1156
+ if (recordName === "_zone") return { data: [] };
1157
+ if ((await this.adapter.getRecord(this.zone, recordName)).records.length === 0) throw new AFSNotFoundError(`/${recordName}`);
1158
+ return { data: [] };
1159
+ }
1160
+ async readRoot(_ctx) {
1161
+ const names = await this.adapter.listRecords(this.zone);
1162
+ return {
1163
+ id: this.zone,
1164
+ path: "/",
1165
+ summary: `DNS zone: ${this.zone}`,
1166
+ meta: {
1167
+ kind: "dns:zone",
1168
+ childrenCount: names.length
1169
+ }
1170
+ };
1171
+ }
1172
+ async readRootMeta(_ctx) {
1173
+ const names = await this.adapter.listRecords(this.zone);
1174
+ return {
1175
+ id: "meta",
1176
+ path: "/.meta",
1177
+ summary: `DNS zone: ${this.zone}`,
1178
+ meta: {
1179
+ kind: "dns:zone",
1180
+ childrenCount: names.length
1181
+ }
1182
+ };
1183
+ }
1184
+ readCapabilities(_ctx) {
1185
+ const actionCatalogs = [];
1186
+ actionCatalogs.push({
1187
+ kind: "dns:zone",
1188
+ description: "Zone-level operations",
1189
+ catalog: [{
1190
+ name: "validate-zone",
1191
+ description: "Validate zone integrity (SOA, NS records)",
1192
+ inputSchema: {
1193
+ type: "object",
1194
+ properties: {},
1195
+ description: "No parameters required"
1196
+ }
1197
+ }, {
1198
+ name: "export-zone",
1199
+ description: "Export zone as BIND zone file format",
1200
+ inputSchema: {
1201
+ type: "object",
1202
+ properties: {},
1203
+ description: "No parameters required"
1204
+ }
1205
+ }],
1206
+ discovery: {
1207
+ pathTemplate: "/.actions/{action}",
1208
+ note: "Exec /.actions/validate-zone or /.actions/export-zone to execute"
1209
+ }
1210
+ });
1211
+ const operations = {
1212
+ read: true,
1213
+ list: true,
1214
+ write: this.accessMode === "readwrite",
1215
+ delete: this.accessMode === "readwrite",
1216
+ search: true,
1217
+ exec: true,
1218
+ stat: true,
1219
+ explain: true
1220
+ };
1221
+ return {
1222
+ id: "/.meta/.capabilities",
1223
+ path: "/.meta/.capabilities",
1224
+ content: {
1225
+ schemaVersion: 1,
1226
+ provider: this.name,
1227
+ version: "1.0.0",
1228
+ description: this.description,
1229
+ tools: [],
1230
+ actions: actionCatalogs,
1231
+ operations
1232
+ },
1233
+ meta: { kind: "afs:capabilities" }
1234
+ };
1235
+ }
1236
+ async readZone(_ctx) {
1237
+ const metadata = await this.adapter.getZoneMetadata(this.zone);
1238
+ return {
1239
+ id: "_zone",
1240
+ path: "/_zone",
1241
+ summary: `Zone metadata for ${this.zone}`,
1242
+ meta: {
1243
+ kind: "dns:zone-meta",
1244
+ childrenCount: 0
1245
+ },
1246
+ content: metadata
1247
+ };
1248
+ }
1249
+ async readRecord(ctx) {
1250
+ const { name: recordName, type } = this.parseRecordParam(ctx.params.recordName);
1097
1251
  const recordSet = await this.adapter.getRecord(this.zone, recordName);
1098
1252
  let records = recordSet.records;
1099
1253
  if (type) records = records.filter((r) => r.type === type);
1100
- if (records.length === 0) return { data: void 0 };
1101
- return { data: {
1254
+ if (records.length === 0) throw new AFSNotFoundError(`/${recordName}`);
1255
+ return {
1102
1256
  id: recordName,
1103
- path: normalizedPath.split("?")[0] ?? normalizedPath,
1257
+ path: ctx.path.split("?")[0] ?? ctx.path,
1104
1258
  summary: `DNS records for ${recordSet.fqdn}`,
1105
- meta: { kind: "dns:record" },
1259
+ meta: {
1260
+ kind: "dns:record",
1261
+ childrenCount: 0
1262
+ },
1106
1263
  content: {
1107
1264
  fqdn: recordSet.fqdn,
1108
1265
  records
1109
1266
  }
1110
- } };
1267
+ };
1111
1268
  }
1112
- async write(path, content, _options) {
1113
- if (this.accessMode !== "readwrite") throw new Error(`DNS provider is readonly, cannot write to ${path}`);
1114
- const normalizedPath = this.normalizePath(path);
1115
- const recordName = this.extractRecordName(normalizedPath);
1116
- if (recordName === "validate-zone") return this.handleValidateZone();
1117
- if (recordName === "export-zone") return this.handleExportZone();
1269
+ async readRecordMeta(ctx) {
1270
+ const recordName = ctx.params.recordName;
1271
+ if (recordName === "_zone") return {
1272
+ id: "_zone",
1273
+ path: "/_zone/.meta",
1274
+ summary: "Zone metadata",
1275
+ meta: {
1276
+ kind: "dns:zone-meta",
1277
+ childrenCount: 0
1278
+ }
1279
+ };
1280
+ const recordSet = await this.adapter.getRecord(this.zone, recordName);
1281
+ if (recordSet.records.length === 0) throw new AFSNotFoundError(`/${recordName}`);
1282
+ const firstRecord = recordSet.records[0];
1283
+ return {
1284
+ id: recordName,
1285
+ path: `/${recordName}/.meta`,
1286
+ summary: `DNS record: ${recordName}`,
1287
+ meta: {
1288
+ kind: "dns:record",
1289
+ childrenCount: 0,
1290
+ recordType: firstRecord?.type,
1291
+ ttl: firstRecord?.ttl
1292
+ }
1293
+ };
1294
+ }
1295
+ async writeRecord(ctx, content) {
1296
+ const recordName = ctx.params.recordName;
1118
1297
  if (recordName === "_zone") throw new Error("Cannot write to _zone metadata");
1119
1298
  const record = content.content;
1120
1299
  if (!record || !record.type) throw new Error("Invalid record content: must include type");
@@ -1147,16 +1326,14 @@ var DNSProvider = class {
1147
1326
  });
1148
1327
  return { data: {
1149
1328
  id: recordName,
1150
- path: normalizedPath,
1329
+ path: ctx.path,
1151
1330
  summary: `Updated DNS record: ${recordName}`,
1152
1331
  meta: { kind: "dns:record" },
1153
1332
  content: record
1154
1333
  } };
1155
1334
  }
1156
- async delete(path, _options) {
1157
- if (this.accessMode !== "readwrite") throw new Error(`DNS provider is readonly, cannot delete ${path}`);
1158
- const normalizedPath = this.normalizePath(path);
1159
- const { recordName, type } = this.parsePathWithQuery(normalizedPath);
1335
+ async deleteRecord(ctx) {
1336
+ const { name: recordName, type } = this.parseRecordParam(ctx.params.recordName);
1160
1337
  if (recordName === "_zone") throw new Error("Cannot delete _zone metadata");
1161
1338
  const permResult = checkPermission({
1162
1339
  name: recordName,
@@ -1172,6 +1349,9 @@ var DNSProvider = class {
1172
1349
  });
1173
1350
  throw new Error(formatPermissionError(permResult));
1174
1351
  }
1352
+ const existing = await this.adapter.getRecord(this.zone, recordName);
1353
+ if (existing.records.length === 0) throw new AFSNotFoundError(`/${recordName}`);
1354
+ if (type && !existing.records.some((r) => r.type === type)) throw new AFSNotFoundError(`/${recordName}?type=${type}`, `No ${type} record found for ${recordName}`);
1175
1355
  await this.acquireRateLimit();
1176
1356
  await this.adapter.deleteRecord(this.zone, recordName, type);
1177
1357
  this.auditLogger.logDelete({
@@ -1181,68 +1361,34 @@ var DNSProvider = class {
1181
1361
  });
1182
1362
  return { message: `Record ${recordName}${type ? ` (${type})` : ""} deleted` };
1183
1363
  }
1184
- normalizePath(path) {
1185
- if (!path.startsWith("/")) return `/${path}`;
1186
- return path;
1187
- }
1188
- extractRecordName(path) {
1189
- return (path.replace(/^\//, "").split("?")[0] ?? "") || "@";
1190
- }
1191
- parsePathWithQuery(path) {
1192
- const [pathPart, queryPart] = path.split("?");
1193
- const recordName = this.extractRecordName(pathPart ?? path);
1194
- let type;
1195
- if (queryPart) type = new URLSearchParams(queryPart).get("type") ?? void 0;
1196
- return {
1197
- recordName,
1198
- type
1199
- };
1200
- }
1201
- /**
1202
- * Sanitize record values to prevent injection attacks
1203
- */
1204
- sanitizeRecordValues(record) {
1205
- const { values } = record;
1206
- if (typeof values === "string") {
1207
- rejectNullBytes(values);
1208
- const result = sanitizeRecordValue(values, record.type);
1209
- if (!result.valid) throw new Error(`Invalid record value: ${result.error}`);
1210
- } else if (Array.isArray(values)) {
1211
- for (const value of values) if (typeof value === "string") {
1212
- rejectNullBytes(value);
1213
- const result = sanitizeRecordValue(value, record.type);
1214
- if (!result.valid) throw new Error(`Invalid record value: ${result.error}`);
1364
+ async statRoot(_ctx) {
1365
+ const names = await this.adapter.listRecords(this.zone);
1366
+ return { data: {
1367
+ id: this.zone,
1368
+ path: "/",
1369
+ meta: {
1370
+ kind: "dns:zone",
1371
+ childrenCount: names.length
1215
1372
  }
1216
- }
1217
- }
1218
- /**
1219
- * Acquire rate limit token (waits if necessary)
1220
- */
1221
- async acquireRateLimit() {
1222
- if (this.rateLimiter) {
1223
- if (!await this.rateLimiter.tryAcquire()) throw new Error("Rate limit exceeded. Please retry later.");
1224
- }
1373
+ } };
1225
1374
  }
1226
- /**
1227
- * Extract values from a record for audit logging
1228
- */
1229
- extractValues(record) {
1230
- const { values } = record;
1231
- if (typeof values === "string") return [values];
1232
- if (Array.isArray(values)) return values;
1233
- return [values];
1375
+ async statZone(_ctx) {
1376
+ return { data: {
1377
+ id: "_zone",
1378
+ path: "/_zone",
1379
+ meta: { kind: "dns:zone-meta" }
1380
+ } };
1234
1381
  }
1235
- async stat(path) {
1236
- const normalizedPath = this.normalizePath(path);
1237
- const recordName = this.extractRecordName(normalizedPath);
1382
+ async statRecord(ctx) {
1383
+ const recordName = ctx.params.recordName;
1238
1384
  const recordSet = await this.adapter.getRecord(this.zone, recordName);
1239
- if (recordSet.records.length === 0 && recordName !== "_zone") throw new Error(`Record not found: ${recordName}`);
1385
+ if (recordSet.records.length === 0) throw new AFSNotFoundError(`/${recordName}`);
1240
1386
  const firstRecord = recordSet.records[0];
1241
1387
  return { data: {
1242
1388
  id: recordName,
1243
- path: normalizedPath,
1389
+ path: ctx.path,
1244
1390
  meta: {
1245
- kind: recordName === "_zone" ? "dns:zone-meta" : "dns:record",
1391
+ kind: "dns:record",
1246
1392
  recordType: firstRecord?.type,
1247
1393
  ttl: firstRecord?.ttl,
1248
1394
  valueCount: recordSet.records.reduce((count, r) => {
@@ -1253,14 +1399,7 @@ var DNSProvider = class {
1253
1399
  }
1254
1400
  } };
1255
1401
  }
1256
- async explain(path) {
1257
- const normalizedPath = this.normalizePath(path);
1258
- const recordName = this.extractRecordName(normalizedPath);
1259
- if (normalizedPath === "/" || recordName === "@") return this.explainRoot();
1260
- if (recordName === "_zone") return this.explainZone();
1261
- throw new Error(`Cannot explain path: ${path}`);
1262
- }
1263
- async explainRoot() {
1402
+ async explainRoot(_ctx) {
1264
1403
  const metadata = await this.adapter.getZoneMetadata(this.zone);
1265
1404
  const names = await this.adapter.listRecords(this.zone);
1266
1405
  return {
@@ -1274,7 +1413,7 @@ var DNSProvider = class {
1274
1413
  `
1275
1414
  };
1276
1415
  }
1277
- async explainZone() {
1416
+ async explainZone(_ctx) {
1278
1417
  const metadata = await this.adapter.getZoneMetadata(this.zone);
1279
1418
  const apex = await this.adapter.getRecord(this.zone, "@");
1280
1419
  const soaRecord = apex.records.find((r) => r.type === "SOA");
@@ -1306,7 +1445,7 @@ var DNSProvider = class {
1306
1445
  content: lines.join("\n")
1307
1446
  };
1308
1447
  }
1309
- async search(_path, query) {
1448
+ async searchRecords(_ctx, query) {
1310
1449
  const names = await this.adapter.listRecords(this.zone);
1311
1450
  const results = [];
1312
1451
  for (const name of names) {
@@ -1333,59 +1472,28 @@ var DNSProvider = class {
1333
1472
  }
1334
1473
  return { data: results };
1335
1474
  }
1336
- readCapabilities() {
1337
- const actionCatalogs = [];
1338
- actionCatalogs.push({
1339
- kind: "dns:zone",
1340
- description: "Zone-level operations",
1341
- catalog: [{
1475
+ listActions(_ctx) {
1476
+ return { data: [{
1477
+ id: "validate-zone",
1478
+ path: "/.actions/validate-zone",
1479
+ summary: "validate-zone",
1480
+ meta: {
1481
+ kind: "afs:action",
1342
1482
  name: "validate-zone",
1343
- description: "Validate zone integrity (SOA, NS records)",
1344
- inputSchema: {
1345
- type: "object",
1346
- properties: {},
1347
- description: "No parameters required"
1348
- }
1349
- }, {
1483
+ description: "Validate zone integrity (SOA, NS records)"
1484
+ }
1485
+ }, {
1486
+ id: "export-zone",
1487
+ path: "/.actions/export-zone",
1488
+ summary: "export-zone",
1489
+ meta: {
1490
+ kind: "afs:action",
1350
1491
  name: "export-zone",
1351
- description: "Export zone as BIND zone file format",
1352
- inputSchema: {
1353
- type: "object",
1354
- properties: {},
1355
- description: "No parameters required"
1356
- }
1357
- }],
1358
- discovery: {
1359
- pathTemplate: "/{action}",
1360
- note: "Write to /validate-zone or /export-zone to execute"
1492
+ description: "Export zone as BIND zone file format"
1361
1493
  }
1362
- });
1363
- const operations = {
1364
- read: true,
1365
- list: true,
1366
- write: this.accessMode === "readwrite",
1367
- delete: this.accessMode === "readwrite",
1368
- search: true,
1369
- exec: false,
1370
- stat: true,
1371
- explain: true
1372
- };
1373
- return { data: {
1374
- id: "/.meta/.capabilities",
1375
- path: "/.meta/.capabilities",
1376
- content: {
1377
- schemaVersion: 1,
1378
- provider: this.name,
1379
- version: "1.0.0",
1380
- description: this.description,
1381
- tools: [],
1382
- actions: actionCatalogs,
1383
- operations
1384
- },
1385
- meta: { kind: "afs:capabilities" }
1386
- } };
1494
+ }] };
1387
1495
  }
1388
- async handleValidateZone() {
1496
+ async execValidateZone(_ctx, _args) {
1389
1497
  const apex = await this.adapter.getRecord(this.zone, "@");
1390
1498
  const checks = [];
1391
1499
  const soaRecord = apex.records.find((r) => r.type === "SOA");
@@ -1404,18 +1512,13 @@ var DNSProvider = class {
1404
1512
  });
1405
1513
  const allValid = checks.every((c) => c.valid);
1406
1514
  return { data: {
1407
- id: "validate-zone",
1408
- path: "/validate-zone",
1409
- summary: `Zone validation: ${allValid ? "PASSED" : "FAILED"}`,
1410
- meta: { kind: "dns:action-result" },
1411
- content: {
1412
- valid: allValid,
1413
- checks,
1414
- zone: this.zone
1415
- }
1515
+ valid: allValid,
1516
+ checks,
1517
+ zone: this.zone,
1518
+ summary: `Zone validation: ${allValid ? "PASSED" : "FAILED"}`
1416
1519
  } };
1417
1520
  }
1418
- async handleExportZone() {
1521
+ async execExportZone(_ctx, _args) {
1419
1522
  const names = await this.adapter.listRecords(this.zone);
1420
1523
  const lines = [];
1421
1524
  lines.push(`; Zone file for ${this.zone}`);
@@ -1433,14 +1536,77 @@ var DNSProvider = class {
1433
1536
  }
1434
1537
  lines.push("");
1435
1538
  return { data: {
1436
- id: "export-zone",
1437
- path: "/export-zone",
1438
- summary: `BIND zone file for ${this.zone}`,
1439
- meta: { kind: "dns:action-result" },
1440
- content: lines.join("\n")
1539
+ content: lines.join("\n"),
1540
+ summary: `BIND zone file for ${this.zone}`
1441
1541
  } };
1442
1542
  }
1543
+ /**
1544
+ * Parse a record param that may contain a query string (e.g. "www?type=A")
1545
+ */
1546
+ parseRecordParam(param) {
1547
+ const queryIndex = param.indexOf("?");
1548
+ if (queryIndex === -1) return { name: param };
1549
+ const name = param.slice(0, queryIndex);
1550
+ const queryPart = param.slice(queryIndex + 1);
1551
+ return {
1552
+ name,
1553
+ type: new URLSearchParams(queryPart).get("type") ?? void 0
1554
+ };
1555
+ }
1556
+ /**
1557
+ * Sanitize record values to prevent injection attacks
1558
+ */
1559
+ sanitizeRecordValues(record) {
1560
+ const { values } = record;
1561
+ if (typeof values === "string") {
1562
+ rejectNullBytes(values);
1563
+ const result = sanitizeRecordValue(values, record.type);
1564
+ if (!result.valid) throw new Error(`Invalid record value: ${result.error}`);
1565
+ } else if (Array.isArray(values)) {
1566
+ for (const value of values) if (typeof value === "string") {
1567
+ rejectNullBytes(value);
1568
+ const result = sanitizeRecordValue(value, record.type);
1569
+ if (!result.valid) throw new Error(`Invalid record value: ${result.error}`);
1570
+ }
1571
+ }
1572
+ }
1573
+ /**
1574
+ * Acquire rate limit token (waits if necessary)
1575
+ */
1576
+ async acquireRateLimit() {
1577
+ if (this.rateLimiter) {
1578
+ if (!await this.rateLimiter.tryAcquire()) throw new Error("Rate limit exceeded. Please retry later.");
1579
+ }
1580
+ }
1581
+ /**
1582
+ * Extract values from a record for audit logging
1583
+ */
1584
+ extractValues(record) {
1585
+ const { values } = record;
1586
+ if (typeof values === "string") return [values];
1587
+ if (Array.isArray(values)) return values;
1588
+ return [values];
1589
+ }
1443
1590
  };
1591
+ __decorate([List("/")], DNSProvider.prototype, "listRoot", null);
1592
+ __decorate([List("/:recordName")], DNSProvider.prototype, "listRecord", null);
1593
+ __decorate([Read("/")], DNSProvider.prototype, "readRoot", null);
1594
+ __decorate([Meta("/")], DNSProvider.prototype, "readRootMeta", null);
1595
+ __decorate([Read("/.meta/.capabilities")], DNSProvider.prototype, "readCapabilities", null);
1596
+ __decorate([Read("/_zone")], DNSProvider.prototype, "readZone", null);
1597
+ __decorate([Read("/:recordName")], DNSProvider.prototype, "readRecord", null);
1598
+ __decorate([Meta("/:recordName")], DNSProvider.prototype, "readRecordMeta", null);
1599
+ __decorate([Write("/:recordName")], DNSProvider.prototype, "writeRecord", null);
1600
+ __decorate([Delete("/:recordName")], DNSProvider.prototype, "deleteRecord", null);
1601
+ __decorate([Stat("/")], DNSProvider.prototype, "statRoot", null);
1602
+ __decorate([Stat("/_zone")], DNSProvider.prototype, "statZone", null);
1603
+ __decorate([Stat("/:recordName")], DNSProvider.prototype, "statRecord", null);
1604
+ __decorate([Explain("/")], DNSProvider.prototype, "explainRoot", null);
1605
+ __decorate([Explain("/_zone")], DNSProvider.prototype, "explainZone", null);
1606
+ __decorate([Search("/")], DNSProvider.prototype, "searchRecords", null);
1607
+ __decorate([Actions("/")], DNSProvider.prototype, "listActions", null);
1608
+ __decorate([Actions.Exec("/", "validate-zone")], DNSProvider.prototype, "execValidateZone", null);
1609
+ __decorate([Actions.Exec("/", "export-zone")], DNSProvider.prototype, "execExportZone", null);
1444
1610
 
1445
1611
  //#endregion
1446
1612
  //#region src/route53/parser.ts