vite-plugin-caddy-multiple-tls 1.8.0 → 1.9.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.
Files changed (3) hide show
  1. package/README.md +8 -5
  2. package/dist/index.js +299 -146
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -130,7 +130,11 @@ export default config;
130
130
 
131
131
  This derives a host like `<repo>.<branch>.web-1.localhost`.
132
132
 
133
- The plugin now treats hostname ownership as explicit. If another live Vite server already owns the resolved domain, it will refuse takeover instead of deleting the other server's route. Use `instanceLabel`, `domain`, or stop the other server first.
133
+ The plugin treats hostname ownership as latest-started-wins. If another live Vite server already owns a resolved hostname, the newly started server replaces that hostname's Caddy route so the domain points at the server you just started.
134
+
135
+ When a plugin instance has multiple domains, each hostname is managed independently. Reusing `app.localhost` from another server will not remove a sibling route like `api.localhost`.
136
+
137
+ If the plugin replaces one hostname from an older combined multi-domain route, it preserves the remaining sibling hostnames before removing the old combined route.
134
138
 
135
139
  If a previous Vite process crashed and left stale ownership behind, the plugin will reclaim it automatically and clean up the stale Caddy route before continuing.
136
140
 
@@ -179,12 +183,11 @@ export default config;
179
183
 
180
184
  ## Troubleshooting
181
185
 
182
- ### `Cannot claim ... another Vite server already owns this domain`
186
+ ### A hostname points to the newest matching server
183
187
 
184
- This means another live dev server is already using the resolved hostname.
188
+ If two dev servers resolve to the same hostname, the most recently started server owns that hostname.
185
189
 
186
- - Stop the other server if you want this one to use the same host.
187
- - Add `instanceLabel` if both servers should run at the same time.
190
+ - Add `instanceLabel` if both servers should stay reachable at the same time.
188
191
  - Pass an explicit `domain` if you want total control over the hostname.
189
192
 
190
193
  ### `client is not allowed to access from origin ''`
package/dist/index.js CHANGED
@@ -14,7 +14,6 @@ var ROUTE_ID_PREFIX = "vite-proxy-";
14
14
  var LOCK_TIMEOUT_MS = 5e3;
15
15
  var LOCK_RETRY_MS = 50;
16
16
  var ROUTE_OWNERSHIP_VERSION = 1;
17
- var ROUTE_OWNERSHIP_STALE_AFTER_MS = 3e4;
18
17
  var ROUTE_OWNERSHIP_HEARTBEAT_INTERVAL_MS = 1e4;
19
18
  var CONNECTIVITY_ERROR_CODES = /* @__PURE__ */ new Set([
20
19
  "ECONNREFUSED",
@@ -215,7 +214,14 @@ async function withFileLock(lockPath, fn) {
215
214
  }
216
215
  }
217
216
  async function withApiLock(apiUrl, fn) {
218
- await withFileLock(getLockPath(apiUrl), fn);
217
+ let result;
218
+ await withFileLock(getLockPath(apiUrl), async () => {
219
+ result = await fn();
220
+ });
221
+ return result;
222
+ }
223
+ async function withCaddyApiLock(apiUrl, fn) {
224
+ return withApiLock(apiUrl, fn);
219
225
  }
220
226
  async function readRouteOwnershipByPath(recordPath) {
221
227
  try {
@@ -261,20 +267,6 @@ async function listRouteOwnershipRecords(scope) {
261
267
  );
262
268
  });
263
269
  }
264
- function isProcessAlive(pid) {
265
- try {
266
- process.kill(pid, 0);
267
- return true;
268
- } catch (e) {
269
- return isNodeError(e) && e.code === "EPERM";
270
- }
271
- }
272
- function isRouteOwnershipActive(record, now = Date.now()) {
273
- if (isProcessAlive(record.pid)) {
274
- return true;
275
- }
276
- return record.pid <= 0 && now - record.lastSeenAt <= ROUTE_OWNERSHIP_STALE_AFTER_MS;
277
- }
278
270
  async function claimRouteOwnership(record) {
279
271
  const normalizedRecord = normalizeRouteOwnershipRecord(record);
280
272
  const { lockPath, recordPath } = getRouteOwnershipPaths(normalizedRecord);
@@ -294,21 +286,10 @@ async function claimRouteOwnership(record) {
294
286
  return candidate.ownerId !== normalizedRecord.ownerId && intersectsDomains(candidate.domains, normalizedRecord.domains);
295
287
  }
296
288
  );
297
- const activeConflict = overlappingRecords.find((candidate) => {
298
- return isRouteOwnershipActive(candidate);
299
- });
300
- if (activeConflict) {
301
- claimResult = {
302
- status: "active-conflict",
303
- currentRecord: normalizedRecord,
304
- existingRecord: activeConflict
305
- };
306
- return;
307
- }
308
289
  await writeRouteOwnership(normalizedRecord);
309
290
  if (overlappingRecords.length > 0) {
310
291
  claimResult = {
311
- status: "reclaimed",
292
+ status: "replaced",
312
293
  currentRecord: normalizedRecord,
313
294
  previousRecords: overlappingRecords
314
295
  };
@@ -609,6 +590,106 @@ function intersectsDomains(targetDomains, routeDomains) {
609
590
  const targetSet = new Set(targetDomains);
610
591
  return routeDomains.some((domain) => targetSet.has(domain));
611
592
  }
593
+ async function readManagedConfigById(id, apiUrl, adminOrigin) {
594
+ const res = await caddyFetch(`${getApiUrl(apiUrl)}/id/${id}`, void 0, apiUrl, adminOrigin);
595
+ if (res.status === 404) return null;
596
+ await assertCaddyResponse(res, `Failed to read managed Caddy resource ${id}`);
597
+ const text = await res.text();
598
+ const parsed = parseConfig(text);
599
+ return isRecord(parsed) ? parsed : null;
600
+ }
601
+ function createPreservedOwnershipRecord(record, domains) {
602
+ const normalizedDomains = normalizeRouteOwnershipDomains(domains);
603
+ const hash = createHash("sha1").update(`${record.routeId}:${normalizedDomains.join(",")}:${process.pid}:${Date.now()}`).digest("hex").slice(0, 12);
604
+ const ownerId = `${record.ownerId}-preserved-${hash}`;
605
+ const routeId = `${ROUTE_ID_PREFIX}preserved-${hash}`;
606
+ const now = Date.now();
607
+ return {
608
+ ...record,
609
+ ownerId,
610
+ domains: normalizedDomains,
611
+ routeId,
612
+ tlsPolicyId: record.tlsPolicyId ? `${routeId}-tls` : null,
613
+ startedAt: now,
614
+ lastSeenAt: now
615
+ };
616
+ }
617
+ async function postManagedRouteConfig(route, serverName = DEFAULT_SERVER_NAME, apiUrl, adminOrigin) {
618
+ const res = await caddyFetch(
619
+ `${getApiUrl(apiUrl)}/config/apps/http/servers/${serverName}/routes`,
620
+ {
621
+ method: "POST",
622
+ headers: { "Content-Type": "application/json" },
623
+ body: JSON.stringify(route)
624
+ },
625
+ apiUrl,
626
+ adminOrigin
627
+ );
628
+ await assertCaddyResponse(res, "Failed to preserve managed Caddy route");
629
+ }
630
+ async function postManagedTlsPolicyConfig(policy, apiUrl, adminOrigin) {
631
+ const res = await caddyFetch(
632
+ `${getApiUrl(apiUrl)}/config/apps/tls/automation/policies`,
633
+ {
634
+ method: "POST",
635
+ headers: { "Content-Type": "application/json" },
636
+ body: JSON.stringify(policy)
637
+ },
638
+ apiUrl,
639
+ adminOrigin
640
+ );
641
+ if (!res.ok) {
642
+ const text = await res.text();
643
+ if (isTlsPolicyOverlapError(text)) {
644
+ return;
645
+ }
646
+ throw buildCaddyRequestError("Failed to preserve managed Caddy TLS policy", res.status, text);
647
+ }
648
+ }
649
+ async function createManagedResourcePreservation(record, domains, serverName = DEFAULT_SERVER_NAME, apiUrl, adminOrigin) {
650
+ const remainingDomains = normalizeRouteOwnershipDomains(
651
+ domains.filter((domain) => record.domains.includes(domain))
652
+ );
653
+ if (remainingDomains.length === 0) return null;
654
+ const routeConfig = await readManagedConfigById(record.routeId, apiUrl, adminOrigin);
655
+ if (!routeConfig) return null;
656
+ const preservedRecord = createPreservedOwnershipRecord(record, remainingDomains);
657
+ const preservedRoute = {
658
+ ...routeConfig,
659
+ "@id": preservedRecord.routeId,
660
+ match: [{ host: preservedRecord.domains }]
661
+ };
662
+ const sourceTlsPolicy = record.tlsPolicyId && preservedRecord.tlsPolicyId ? await readManagedConfigById(record.tlsPolicyId, apiUrl, adminOrigin) : null;
663
+ const preservedTlsPolicy = preservedRecord.tlsPolicyId ? {
664
+ ...sourceTlsPolicy ?? {
665
+ issuers: [
666
+ {
667
+ module: "internal"
668
+ }
669
+ ]
670
+ },
671
+ "@id": preservedRecord.tlsPolicyId,
672
+ subjects: preservedRecord.domains
673
+ } : null;
674
+ await postManagedRouteConfig(preservedRoute, serverName, apiUrl, adminOrigin);
675
+ return {
676
+ record: preservedRecord,
677
+ tlsPolicy: preservedTlsPolicy
678
+ };
679
+ }
680
+ async function commitManagedResourcePreservation(preservation, apiUrl, adminOrigin) {
681
+ if (preservation.tlsPolicy) {
682
+ await postManagedTlsPolicyConfig(preservation.tlsPolicy, apiUrl, adminOrigin);
683
+ }
684
+ await writeRouteOwnership(preservation.record);
685
+ }
686
+ async function discardManagedResourcePreservation(preservation, apiUrl, adminOrigin) {
687
+ let discarded = await removeRoute(preservation.record.routeId, apiUrl, adminOrigin);
688
+ if (preservation.record.tlsPolicyId) {
689
+ discarded = await removeTlsPolicy(preservation.record.tlsPolicyId, apiUrl, adminOrigin) && discarded;
690
+ }
691
+ return discarded;
692
+ }
612
693
  async function findManagedRoutesForDomains(domains, serverName = DEFAULT_SERVER_NAME, apiUrl, adminOrigin) {
613
694
  if (domains.length === 0) return [];
614
695
  const res = await caddyFetch(
@@ -983,15 +1064,6 @@ function viteCaddyTlsPlugin({
983
1064
  lastSeenAt: now
984
1065
  };
985
1066
  }
986
- function buildOwnershipConflictMessage(domains, existingRecord) {
987
- const ownerLocation = existingRecord.configRoot ?? existingRecord.cwd;
988
- const domainLabel = domains.join(", ");
989
- return [
990
- `Cannot claim ${domainLabel}: another Vite server already owns ${domains.length > 1 ? "these domains" : "this domain"}.`,
991
- `Existing owner pid ${existingRecord.pid} from ${ownerLocation}.`,
992
- "Stop the other server first, or use `instanceLabel` or `domain` to make the hostname unique."
993
- ].join(" ");
994
- }
995
1067
  async function releaseOwnershipRecord(record) {
996
1068
  if (!record) return;
997
1069
  await releaseRouteOwnership(record);
@@ -999,6 +1071,10 @@ function viteCaddyTlsPlugin({
999
1071
  async function releaseOwnershipRecords(records) {
1000
1072
  await Promise.all(records.map((record) => releaseOwnershipRecord(record)));
1001
1073
  }
1074
+ function getPreservedDomains(record, replacedDomains) {
1075
+ const replacedDomainSet = new Set(replacedDomains);
1076
+ return record.domains.filter((domain2) => !replacedDomainSet.has(domain2));
1077
+ }
1002
1078
  async function cleanupClaimedResources(record, removeWithRetry) {
1003
1079
  let cleaned = true;
1004
1080
  if (record.tlsPolicyId) {
@@ -1030,6 +1106,17 @@ function viteCaddyTlsPlugin({
1030
1106
  }
1031
1107
  return cleaned;
1032
1108
  }
1109
+ async function discardManagedResourcePreservations(preservations) {
1110
+ await Promise.all(
1111
+ preservations.map((preservation) => {
1112
+ return discardManagedResourcePreservation(
1113
+ preservation,
1114
+ pluginCaddyApiUrl,
1115
+ pluginCaddyAdminOrigin
1116
+ );
1117
+ })
1118
+ );
1119
+ }
1033
1120
  function isPreviewServer(server) {
1034
1121
  return server.config.isProduction;
1035
1122
  }
@@ -1061,15 +1148,14 @@ function viteCaddyTlsPlugin({
1061
1148
  instanceLabel
1062
1149
  });
1063
1150
  const domainArray = resolvedDomains ?? [];
1064
- const ownerId = createOwnerId();
1065
- const ownershipRecord = createRouteOwnershipRecord(ownerId, domainArray, config.root);
1066
- const routeId = ownershipRecord.routeId;
1067
- const tlsPolicyId = ownershipRecord.tlsPolicyId;
1151
+ const ownershipRecords = domainArray.map((resolvedDomain) => {
1152
+ return createRouteOwnershipRecord(createOwnerId(), [resolvedDomain], config.root);
1153
+ });
1068
1154
  let cleanupStarted = false;
1069
1155
  let resolvedPort = null;
1070
1156
  let resolvedHost = null;
1071
1157
  let setupStarted = false;
1072
- let activeOwnershipRecord = null;
1158
+ let activeOwnershipRecords = [];
1073
1159
  let ownershipHeartbeat = null;
1074
1160
  function buildDomainResolutionMessage() {
1075
1161
  const issues = [];
@@ -1100,7 +1186,6 @@ function viteCaddyTlsPlugin({
1100
1186
  console.error(buildDomainResolutionMessage());
1101
1187
  return;
1102
1188
  }
1103
- let tlsPolicyAdded = false;
1104
1189
  function getPortFromAddress(address) {
1105
1190
  if (address && typeof address === "object" && "port" in address) {
1106
1191
  const port = address.port;
@@ -1180,12 +1265,21 @@ function viteCaddyTlsPlugin({
1180
1265
  clearInterval(ownershipHeartbeat);
1181
1266
  ownershipHeartbeat = null;
1182
1267
  }
1183
- if (!activeOwnershipRecord) return;
1184
- const cleaned = await cleanupClaimedResources(activeOwnershipRecord, removeWithRetry);
1185
- if (cleaned) {
1186
- await releaseOwnershipRecord(activeOwnershipRecord);
1187
- activeOwnershipRecord = null;
1268
+ if (activeOwnershipRecords.length === 0) return;
1269
+ await cleanupOwnershipRecords(activeOwnershipRecords);
1270
+ }
1271
+ async function cleanupOwnershipRecords(records) {
1272
+ const cleanedOwnerIds = /* @__PURE__ */ new Set();
1273
+ for (const activeOwnershipRecord of records) {
1274
+ const cleaned = await cleanupClaimedResources(activeOwnershipRecord, removeWithRetry);
1275
+ if (cleaned) {
1276
+ await releaseOwnershipRecord(activeOwnershipRecord);
1277
+ cleanedOwnerIds.add(activeOwnershipRecord.ownerId);
1278
+ }
1188
1279
  }
1280
+ activeOwnershipRecords = activeOwnershipRecords.filter((record) => {
1281
+ return !cleanedOwnerIds.has(record.ownerId);
1282
+ });
1189
1283
  }
1190
1284
  function onServerClose() {
1191
1285
  void cleanupRoute();
@@ -1230,16 +1324,28 @@ function viteCaddyTlsPlugin({
1230
1324
  console.error(`Failed to remove ${label} after ${maxAttempts} attempts.`);
1231
1325
  return false;
1232
1326
  }
1233
- function startOwnershipHeartbeat(record) {
1327
+ function startOwnershipHeartbeat() {
1234
1328
  ownershipHeartbeat = setInterval(() => {
1235
- void touchRouteOwnership(record).then((touched) => {
1236
- if (touched || !ownershipHeartbeat) {
1329
+ const records = [...activeOwnershipRecords];
1330
+ if (records.length === 0) {
1331
+ if (ownershipHeartbeat) {
1332
+ clearInterval(ownershipHeartbeat);
1333
+ ownershipHeartbeat = null;
1334
+ }
1335
+ return;
1336
+ }
1337
+ void Promise.all(records.map((record) => touchRouteOwnership(record))).then((touchResults) => {
1338
+ if (touchResults.every(Boolean) || !ownershipHeartbeat) {
1237
1339
  return;
1238
1340
  }
1341
+ const lostRecords = records.filter((_, index) => {
1342
+ return !touchResults[index];
1343
+ });
1344
+ const lostDomains = lostRecords.flatMap((record) => record.domains);
1239
1345
  console.error(
1240
- `Lost route ownership for ${domainArray.join(", ")}. Cleaning up managed Caddy resources.`
1346
+ `Lost route ownership for ${lostDomains.join(", ")}. Cleaning up managed Caddy resources.`
1241
1347
  );
1242
- void cleanupRoute();
1348
+ void cleanupOwnershipRecords(lostRecords);
1243
1349
  }).catch((error) => {
1244
1350
  console.error(
1245
1351
  `Failed to refresh route ownership for ${domainArray.join(", ")}.`,
@@ -1264,105 +1370,152 @@ function viteCaddyTlsPlugin({
1264
1370
  }
1265
1371
  const port = getServerPort();
1266
1372
  const upstreamHost = getUpstreamHost();
1267
- let claimResult;
1373
+ let configuredRoutes = false;
1268
1374
  try {
1269
- claimResult = await claimRouteOwnership(ownershipRecord);
1375
+ configuredRoutes = await withCaddyApiLock(pluginCaddyApiUrl, async () => {
1376
+ for (const ownershipRecord of ownershipRecords) {
1377
+ const routeDomains = ownershipRecord.domains;
1378
+ const routeId = ownershipRecord.routeId;
1379
+ const tlsPolicyId = ownershipRecord.tlsPolicyId;
1380
+ try {
1381
+ const claimResult = await claimRouteOwnership(ownershipRecord);
1382
+ activeOwnershipRecords.push(claimResult.currentRecord);
1383
+ if (claimResult.status === "replaced") {
1384
+ const preservations = [];
1385
+ let replaced = true;
1386
+ for (const previousRecord of claimResult.previousRecords) {
1387
+ const preservedDomains = getPreservedDomains(previousRecord, routeDomains);
1388
+ if (preservedDomains.length > 0) {
1389
+ const preservation = await createManagedResourcePreservation(
1390
+ previousRecord,
1391
+ preservedDomains,
1392
+ serverName,
1393
+ pluginCaddyApiUrl,
1394
+ pluginCaddyAdminOrigin
1395
+ );
1396
+ if (preservation) {
1397
+ preservations.push(preservation);
1398
+ }
1399
+ }
1400
+ replaced = await cleanupClaimedResources(previousRecord, removeWithRetry) && replaced;
1401
+ }
1402
+ if (!replaced) {
1403
+ console.error(
1404
+ `Failed to replace previous ownership for ${routeDomains.join(", ")}. Try stopping the other server or removing stale Caddy state manually.`
1405
+ );
1406
+ await discardManagedResourcePreservations(preservations);
1407
+ await cleanupRoute();
1408
+ return false;
1409
+ }
1410
+ try {
1411
+ await Promise.all(
1412
+ preservations.map((preservation) => {
1413
+ return commitManagedResourcePreservation(
1414
+ preservation,
1415
+ pluginCaddyApiUrl,
1416
+ pluginCaddyAdminOrigin
1417
+ );
1418
+ })
1419
+ );
1420
+ } catch (e) {
1421
+ console.error(
1422
+ `Failed to preserve sibling domains while replacing ${routeDomains.join(", ")}.`,
1423
+ e
1424
+ );
1425
+ await discardManagedResourcePreservations(preservations);
1426
+ await cleanupRoute();
1427
+ return false;
1428
+ }
1429
+ await releaseOwnershipRecords(claimResult.previousRecords);
1430
+ }
1431
+ } catch (e) {
1432
+ console.error("Failed to claim route ownership for the resolved domains.", e);
1433
+ await cleanupRoute();
1434
+ return false;
1435
+ }
1436
+ const conflictingRouteIds = (await findManagedRoutesForDomains(
1437
+ routeDomains,
1438
+ serverName,
1439
+ pluginCaddyApiUrl,
1440
+ pluginCaddyAdminOrigin
1441
+ )).filter((existingRouteId) => {
1442
+ return existingRouteId !== routeId;
1443
+ });
1444
+ const conflictingTlsPolicyIds = (await findManagedTlsPoliciesForDomains(
1445
+ routeDomains,
1446
+ pluginCaddyApiUrl,
1447
+ pluginCaddyAdminOrigin
1448
+ )).filter((existingTlsPolicyId) => {
1449
+ return existingTlsPolicyId !== tlsPolicyId;
1450
+ });
1451
+ if (conflictingRouteIds.length > 0 || conflictingTlsPolicyIds.length > 0) {
1452
+ const cleanedOrphans = await cleanupManagedResources(
1453
+ conflictingRouteIds,
1454
+ conflictingTlsPolicyIds,
1455
+ removeWithRetry
1456
+ );
1457
+ if (cleanedOrphans) {
1458
+ console.warn(
1459
+ `Reclaimed orphaned managed Caddy resources for ${routeDomains.join(", ")}.`
1460
+ );
1461
+ } else {
1462
+ console.error(
1463
+ `Cannot point ${routeDomains.join(", ")} to this dev server because Caddy still has orphaned managed resources. Remove the stale Caddy state manually.`
1464
+ );
1465
+ await cleanupRoute();
1466
+ return false;
1467
+ }
1468
+ }
1469
+ if (tlsPolicyId) {
1470
+ try {
1471
+ await addTlsPolicy(
1472
+ tlsPolicyId,
1473
+ routeDomains,
1474
+ pluginCaddyApiUrl,
1475
+ pluginCaddyAdminOrigin
1476
+ );
1477
+ } catch (e) {
1478
+ console.error(
1479
+ `Failed to add TLS policy to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
1480
+ e
1481
+ );
1482
+ await cleanupRoute();
1483
+ return false;
1484
+ }
1485
+ }
1486
+ try {
1487
+ await addRoute(
1488
+ routeId,
1489
+ routeDomains,
1490
+ port,
1491
+ cors,
1492
+ serverName,
1493
+ upstreamHost,
1494
+ upstreamHostHeader,
1495
+ pluginCaddyApiUrl,
1496
+ pluginCaddyAdminOrigin
1497
+ );
1498
+ } catch (e) {
1499
+ console.error(
1500
+ `Failed to add route to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
1501
+ e
1502
+ );
1503
+ await cleanupRoute();
1504
+ return false;
1505
+ }
1506
+ }
1507
+ return true;
1508
+ });
1270
1509
  } catch (e) {
1271
- console.error("Failed to claim route ownership for the resolved domains.", e);
1272
- return;
1273
- }
1274
- if (claimResult.status === "active-conflict") {
1275
- console.error(buildOwnershipConflictMessage(domainArray, claimResult.existingRecord));
1510
+ console.error("Failed to update Caddy routes.", e);
1511
+ await cleanupRoute();
1276
1512
  return;
1277
1513
  }
1278
- activeOwnershipRecord = claimResult.currentRecord;
1279
- if (claimResult.status === "reclaimed") {
1280
- let reclaimed = true;
1281
- for (const previousRecord of claimResult.previousRecords) {
1282
- reclaimed = await cleanupClaimedResources(previousRecord, removeWithRetry) && reclaimed;
1283
- }
1284
- if (!reclaimed) {
1285
- console.error(
1286
- `Failed to reclaim stale ownership for ${domainArray.join(", ")}. Try stopping the other server or removing stale Caddy state manually.`
1287
- );
1288
- await releaseOwnershipRecord(activeOwnershipRecord);
1289
- activeOwnershipRecord = null;
1290
- return;
1291
- }
1292
- await releaseOwnershipRecords(claimResult.previousRecords);
1293
- }
1294
- const conflictingRouteIds = (await findManagedRoutesForDomains(
1295
- domainArray,
1296
- serverName,
1297
- pluginCaddyApiUrl,
1298
- pluginCaddyAdminOrigin
1299
- )).filter((existingRouteId) => {
1300
- return existingRouteId !== routeId;
1301
- });
1302
- const conflictingTlsPolicyIds = (await findManagedTlsPoliciesForDomains(
1303
- domainArray,
1304
- pluginCaddyApiUrl,
1305
- pluginCaddyAdminOrigin
1306
- )).filter((existingTlsPolicyId) => {
1307
- return existingTlsPolicyId !== tlsPolicyId;
1308
- });
1309
- if (conflictingRouteIds.length > 0 || conflictingTlsPolicyIds.length > 0) {
1310
- const reclaimedOrphans = await cleanupManagedResources(
1311
- conflictingRouteIds,
1312
- conflictingTlsPolicyIds,
1313
- removeWithRetry
1314
- );
1315
- if (reclaimedOrphans) {
1316
- console.warn(`Reclaimed orphaned managed Caddy resources for ${domainArray.join(", ")}.`);
1317
- } else {
1318
- console.error(
1319
- `Cannot claim ${domainArray.join(", ")} because Caddy still has orphaned managed resources. Remove the stale Caddy state or use \`instanceLabel\` or \`domain\` to make the hostname unique.`
1320
- );
1321
- await releaseOwnershipRecord(activeOwnershipRecord);
1322
- activeOwnershipRecord = null;
1323
- return;
1324
- }
1325
- }
1326
- if (tlsPolicyId) {
1327
- try {
1328
- await addTlsPolicy(tlsPolicyId, domainArray, pluginCaddyApiUrl, pluginCaddyAdminOrigin);
1329
- tlsPolicyAdded = true;
1330
- } catch (e) {
1331
- console.error(
1332
- `Failed to add TLS policy to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
1333
- e
1334
- );
1335
- await releaseOwnershipRecord(activeOwnershipRecord);
1336
- activeOwnershipRecord = null;
1337
- return;
1338
- }
1339
- }
1340
- try {
1341
- await addRoute(
1342
- routeId,
1343
- domainArray,
1344
- port,
1345
- cors,
1346
- serverName,
1347
- upstreamHost,
1348
- upstreamHostHeader,
1349
- pluginCaddyApiUrl,
1350
- pluginCaddyAdminOrigin
1351
- );
1352
- } catch (e) {
1353
- if (tlsPolicyAdded && tlsPolicyId) {
1354
- await removeTlsPolicy(tlsPolicyId, pluginCaddyApiUrl, pluginCaddyAdminOrigin);
1355
- }
1356
- await releaseOwnershipRecord(activeOwnershipRecord);
1357
- activeOwnershipRecord = null;
1358
- console.error(
1359
- `Failed to add route to Caddy. Is the Caddy Admin API reachable at ${pluginCaddyApiUrl}?`,
1360
- e
1361
- );
1514
+ if (!configuredRoutes) {
1362
1515
  return;
1363
1516
  }
1364
- if (activeOwnershipRecord) {
1365
- startOwnershipHeartbeat(activeOwnershipRecord);
1517
+ if (activeOwnershipRecords.length > 0) {
1518
+ startOwnershipHeartbeat();
1366
1519
  }
1367
1520
  console.log("\n\u{1F512} Caddy is proxying your traffic on https");
1368
1521
  console.log(`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vite-plugin-caddy-multiple-tls",
3
- "version": "1.8.0",
3
+ "version": "1.9.0",
4
4
  "description": "Vite plugin that uses Caddy to provide local HTTPS with derived domains.",
5
5
  "keywords": [
6
6
  "caddy",