@camstack/addon-cloudflare 1.0.3 → 1.0.5

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.
@@ -1,7 +1,38 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-FQAxmoZY.js");
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
24
+ const require_dist = require("../dist-Bj5XzuE6.js");
25
+ let node_path = require("node:path");
26
+ node_path = __toESM(node_path);
3
27
  let node_crypto = require("node:crypto");
4
28
  let node_child_process = require("node:child_process");
29
+ let node_fs = require("node:fs");
30
+ node_fs = __toESM(node_fs);
31
+ let node_fs_promises = require("node:fs/promises");
32
+ node_fs_promises = __toESM(node_fs_promises);
33
+ let node_os = require("node:os");
34
+ node_os = __toESM(node_os);
35
+ let node_stream_promises = require("node:stream/promises");
5
36
  //#region src/tunnel/cloudflare-tunnel.ts
6
37
  /**
7
38
  * Direct child_process.spawn() driver for the `cloudflared` binary.
@@ -23,19 +54,23 @@ var CloudflareTunnelService = class CloudflareTunnelService {
23
54
  config;
24
55
  logger;
25
56
  eventBus;
57
+ resolveCloudflaredBin;
26
58
  id = "cloudflare-tunnel";
27
59
  type = "cloudflare";
28
60
  endpoint = null;
29
61
  lastError;
30
62
  child = null;
63
+ /** Resolved cloudflared path/command (set on first start, reused on restart). */
64
+ cloudflaredBin = "cloudflared";
31
65
  restartCount = 0;
32
66
  intentionalStop = false;
33
67
  static MAX_RESTARTS = 5;
34
68
  static STOP_GRACE_MS = 5e3;
35
- constructor(config, logger, eventBus) {
69
+ constructor(config, logger, eventBus, resolveCloudflaredBin = async () => "cloudflared") {
36
70
  this.config = config;
37
71
  this.logger = logger;
38
72
  this.eventBus = eventBus;
73
+ this.resolveCloudflaredBin = resolveCloudflaredBin;
39
74
  }
40
75
  async start() {
41
76
  this.logger.info("Starting Cloudflare tunnel", {
@@ -76,6 +111,18 @@ var CloudflareTunnelService = class CloudflareTunnelService {
76
111
  this.intentionalStop = false;
77
112
  this.restartCount = 0;
78
113
  this.lastError = void 0;
114
+ try {
115
+ this.cloudflaredBin = await this.resolveCloudflaredBin();
116
+ } catch (err) {
117
+ this.logger.error("Failed to provision cloudflared binary", {
118
+ meta: { error: err instanceof Error ? err.message : String(err) },
119
+ tags: {
120
+ topic: "tunnel",
121
+ phase: "provision-error"
122
+ }
123
+ });
124
+ throw err;
125
+ }
79
126
  this.spawnChild();
80
127
  const placeholderHost = this.config.mode === "custom" ? this.config.customHostname : "pending.trycloudflare.com";
81
128
  this.endpoint = {
@@ -192,7 +239,7 @@ var CloudflareTunnelService = class CloudflareTunnelService {
192
239
  const args = [...debugFlags, ...tunnelArgs];
193
240
  let child;
194
241
  try {
195
- child = (0, node_child_process.spawn)("cloudflared", args, { stdio: [
242
+ child = (0, node_child_process.spawn)(this.cloudflaredBin, args, { stdio: [
196
243
  "ignore",
197
244
  "pipe",
198
245
  "pipe"
@@ -340,6 +387,130 @@ var CloudflareTunnelService = class CloudflareTunnelService {
340
387
  });
341
388
  }
342
389
  };
390
+ var RELEASE_BASE = "https://github.com/cloudflare/cloudflared/releases/download";
391
+ /**
392
+ * Map a Node `platform`/`arch` to the matching cloudflared release asset.
393
+ * Pure + exported for testing. Throws on an unsupported combination.
394
+ */
395
+ function cloudflaredAsset(platform, arch) {
396
+ const a = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : arch === "arm" ? "arm" : null;
397
+ if (a === null) throw new Error(`cloudflared: unsupported arch '${arch}'`);
398
+ switch (platform) {
399
+ case "linux": return {
400
+ fileName: `cloudflared-linux-${a}`,
401
+ isArchive: false
402
+ };
403
+ case "darwin": return {
404
+ fileName: `cloudflared-darwin-${a === "arm64" ? "amd64" : a}.tgz`,
405
+ isArchive: true
406
+ };
407
+ case "win32": return {
408
+ fileName: `cloudflared-windows-${a}.exe`,
409
+ isArchive: false
410
+ };
411
+ default: throw new Error(`cloudflared: unsupported platform '${platform}'`);
412
+ }
413
+ }
414
+ /** Resolve a working `cloudflared` from PATH, or null if not runnable. */
415
+ async function cloudflaredOnPath() {
416
+ return await runsOk("cloudflared") ? "cloudflared" : null;
417
+ }
418
+ /** True when `<bin> --version` exits 0 (binary present + executable). */
419
+ function runsOk(bin) {
420
+ return new Promise((resolve) => {
421
+ let done = false;
422
+ const finish = (ok) => {
423
+ if (!done) {
424
+ done = true;
425
+ resolve(ok);
426
+ }
427
+ };
428
+ try {
429
+ const child = (0, node_child_process.spawn)(bin, ["--version"], { stdio: "ignore" });
430
+ child.on("error", () => finish(false));
431
+ child.on("exit", (code) => finish(code === 0));
432
+ setTimeout(() => {
433
+ child.kill("SIGKILL");
434
+ finish(false);
435
+ }, 5e3);
436
+ } catch {
437
+ finish(false);
438
+ }
439
+ });
440
+ }
441
+ async function downloadTo(url, dest) {
442
+ const res = await fetch(url, { redirect: "follow" });
443
+ if (!res.ok || !res.body) throw new Error(`cloudflared download failed: HTTP ${res.status} for ${url}`);
444
+ const tmp = `${dest}.partial`;
445
+ await (0, node_stream_promises.pipeline)(res.body, node_fs.createWriteStream(tmp));
446
+ await node_fs_promises.rename(tmp, dest);
447
+ }
448
+ /** Extract the `cloudflared` entry from a downloaded .tgz into `destBin`. */
449
+ async function extractArchive(archivePath, destBin, logger) {
450
+ const dir = node_path.dirname(destBin);
451
+ await new Promise((resolve, reject) => {
452
+ const child = (0, node_child_process.spawn)("tar", [
453
+ "-xzf",
454
+ archivePath,
455
+ "-C",
456
+ dir
457
+ ], { stdio: "inherit" });
458
+ child.on("error", reject);
459
+ child.on("exit", (code) => code === 0 ? resolve() : reject(/* @__PURE__ */ new Error(`tar exited ${code}`)));
460
+ });
461
+ if (!node_fs.existsSync(destBin)) logger.warn("cloudflared archive did not yield expected binary name", {
462
+ meta: { destBin },
463
+ tags: {
464
+ topic: "tunnel",
465
+ phase: "extract"
466
+ }
467
+ });
468
+ await node_fs_promises.rm(archivePath, { force: true });
469
+ }
470
+ /**
471
+ * Return a path/command for a runnable `cloudflared`, downloading + caching it
472
+ * on first use. Prefers an existing PATH install; otherwise caches under
473
+ * `binDir`. Throws (with an actionable message) if it cannot be provisioned.
474
+ */
475
+ async function ensureCloudflared(opts) {
476
+ const onPath = await cloudflaredOnPath();
477
+ if (onPath) return onPath;
478
+ const cached = node_path.join(opts.binDir, node_os.platform() === "win32" ? "cloudflared.exe" : "cloudflared");
479
+ if (node_fs.existsSync(cached) && await runsOk(cached)) return cached;
480
+ const version = opts.version ?? "2025.4.2";
481
+ const asset = cloudflaredAsset(node_os.platform(), node_os.arch());
482
+ const url = `${RELEASE_BASE}/${version}/${asset.fileName}`;
483
+ await node_fs_promises.mkdir(opts.binDir, { recursive: true });
484
+ opts.logger.info("Provisioning cloudflared (not on PATH) — downloading", {
485
+ meta: {
486
+ version,
487
+ asset: asset.fileName,
488
+ dest: cached
489
+ },
490
+ tags: {
491
+ topic: "tunnel",
492
+ phase: "provision"
493
+ }
494
+ });
495
+ if (asset.isArchive) {
496
+ const archive = node_path.join(opts.binDir, asset.fileName);
497
+ await downloadTo(url, archive);
498
+ await extractArchive(archive, cached, opts.logger);
499
+ } else await downloadTo(url, cached);
500
+ await node_fs_promises.chmod(cached, 493);
501
+ if (!await runsOk(cached)) throw new Error(`cloudflared downloaded to ${cached} but is not runnable`);
502
+ opts.logger.info("cloudflared ready", {
503
+ meta: {
504
+ path: cached,
505
+ version
506
+ },
507
+ tags: {
508
+ topic: "tunnel",
509
+ phase: "provision-done"
510
+ }
511
+ });
512
+ return cached;
513
+ }
343
514
  //#endregion
344
515
  //#region src/tunnel/cloudflare-api.ts
345
516
  /**
@@ -666,6 +837,19 @@ var CloudflareTunnelAddon = class extends require_dist.BaseAddon {
666
837
  }
667
838
  return "127.0.0.1";
668
839
  }
840
+ /**
841
+ * Resolve the `cloudflared` binary path once per addon lifetime (PATH install,
842
+ * or a copy downloaded + cached under `<dataDir>/bin`). Memoized so the
843
+ * (possible) download happens at most once, on the first tunnel start.
844
+ */
845
+ cloudflaredBinPromise;
846
+ resolveCloudflared() {
847
+ this.cloudflaredBinPromise ??= ensureCloudflared({
848
+ binDir: node_path.join(this.ctx.dataDir, "bin"),
849
+ logger: this.ctx.logger
850
+ });
851
+ return this.cloudflaredBinPromise;
852
+ }
669
853
  async onInitialize() {
670
854
  if (this.config.localPort === 3001) {
671
855
  this.ctx.logger.warn("Resetting stale localPort=3001 (Vite admin-ui dev port) → 0 (auto)", { tags: {
@@ -679,7 +863,7 @@ var CloudflareTunnelAddon = class extends require_dist.BaseAddon {
679
863
  ...this.config,
680
864
  localPort: this.resolveLocalPort(),
681
865
  localHost
682
- }, this.ctx.logger, this.ctx.eventBus);
866
+ }, this.ctx.logger, this.ctx.eventBus, () => this.resolveCloudflared());
683
867
  this.ctx.logger.info("Cloudflare Tunnel addon initialized", { meta: {
684
868
  mode: this.config.mode,
685
869
  hasCustomToken: !!this.config.customTunnelToken
@@ -735,7 +919,7 @@ var CloudflareTunnelAddon = class extends require_dist.BaseAddon {
735
919
  ...this.config,
736
920
  localPort: this.resolveLocalPort(),
737
921
  localHost
738
- }, this.ctx.logger, this.ctx.eventBus);
922
+ }, this.ctx.logger, this.ctx.eventBus, () => this.resolveCloudflared());
739
923
  if (this.isRunCapable()) {
740
924
  this.ctx.logger.info("config changed — (re-)spawning tunnel", {
741
925
  meta: {
@@ -1,6 +1,11 @@
1
- import { a as networkAccessCapability, c as array, d as literal, f as number, i as defineCustomActions, l as boolean, m as string, n as EventCategory, p as object, r as customAction, s as _enum, t as BaseAddon } from "../dist-gS41k7Cx.mjs";
1
+ import { a as networkAccessCapability, c as array, d as literal, f as number, i as defineCustomActions, l as boolean, m as string, n as EventCategory, p as object, r as customAction, s as _enum, t as BaseAddon } from "../dist-D4obfhYq.mjs";
2
+ import * as path from "node:path";
2
3
  import { randomUUID } from "node:crypto";
3
4
  import { spawn } from "node:child_process";
5
+ import * as fs from "node:fs";
6
+ import * as fsp from "node:fs/promises";
7
+ import * as os from "node:os";
8
+ import { pipeline } from "node:stream/promises";
4
9
  //#region src/tunnel/cloudflare-tunnel.ts
5
10
  /**
6
11
  * Direct child_process.spawn() driver for the `cloudflared` binary.
@@ -22,19 +27,23 @@ var CloudflareTunnelService = class CloudflareTunnelService {
22
27
  config;
23
28
  logger;
24
29
  eventBus;
30
+ resolveCloudflaredBin;
25
31
  id = "cloudflare-tunnel";
26
32
  type = "cloudflare";
27
33
  endpoint = null;
28
34
  lastError;
29
35
  child = null;
36
+ /** Resolved cloudflared path/command (set on first start, reused on restart). */
37
+ cloudflaredBin = "cloudflared";
30
38
  restartCount = 0;
31
39
  intentionalStop = false;
32
40
  static MAX_RESTARTS = 5;
33
41
  static STOP_GRACE_MS = 5e3;
34
- constructor(config, logger, eventBus) {
42
+ constructor(config, logger, eventBus, resolveCloudflaredBin = async () => "cloudflared") {
35
43
  this.config = config;
36
44
  this.logger = logger;
37
45
  this.eventBus = eventBus;
46
+ this.resolveCloudflaredBin = resolveCloudflaredBin;
38
47
  }
39
48
  async start() {
40
49
  this.logger.info("Starting Cloudflare tunnel", {
@@ -75,6 +84,18 @@ var CloudflareTunnelService = class CloudflareTunnelService {
75
84
  this.intentionalStop = false;
76
85
  this.restartCount = 0;
77
86
  this.lastError = void 0;
87
+ try {
88
+ this.cloudflaredBin = await this.resolveCloudflaredBin();
89
+ } catch (err) {
90
+ this.logger.error("Failed to provision cloudflared binary", {
91
+ meta: { error: err instanceof Error ? err.message : String(err) },
92
+ tags: {
93
+ topic: "tunnel",
94
+ phase: "provision-error"
95
+ }
96
+ });
97
+ throw err;
98
+ }
78
99
  this.spawnChild();
79
100
  const placeholderHost = this.config.mode === "custom" ? this.config.customHostname : "pending.trycloudflare.com";
80
101
  this.endpoint = {
@@ -191,7 +212,7 @@ var CloudflareTunnelService = class CloudflareTunnelService {
191
212
  const args = [...debugFlags, ...tunnelArgs];
192
213
  let child;
193
214
  try {
194
- child = spawn("cloudflared", args, { stdio: [
215
+ child = spawn(this.cloudflaredBin, args, { stdio: [
195
216
  "ignore",
196
217
  "pipe",
197
218
  "pipe"
@@ -339,6 +360,130 @@ var CloudflareTunnelService = class CloudflareTunnelService {
339
360
  });
340
361
  }
341
362
  };
363
+ var RELEASE_BASE = "https://github.com/cloudflare/cloudflared/releases/download";
364
+ /**
365
+ * Map a Node `platform`/`arch` to the matching cloudflared release asset.
366
+ * Pure + exported for testing. Throws on an unsupported combination.
367
+ */
368
+ function cloudflaredAsset(platform, arch) {
369
+ const a = arch === "x64" ? "amd64" : arch === "arm64" ? "arm64" : arch === "arm" ? "arm" : null;
370
+ if (a === null) throw new Error(`cloudflared: unsupported arch '${arch}'`);
371
+ switch (platform) {
372
+ case "linux": return {
373
+ fileName: `cloudflared-linux-${a}`,
374
+ isArchive: false
375
+ };
376
+ case "darwin": return {
377
+ fileName: `cloudflared-darwin-${a === "arm64" ? "amd64" : a}.tgz`,
378
+ isArchive: true
379
+ };
380
+ case "win32": return {
381
+ fileName: `cloudflared-windows-${a}.exe`,
382
+ isArchive: false
383
+ };
384
+ default: throw new Error(`cloudflared: unsupported platform '${platform}'`);
385
+ }
386
+ }
387
+ /** Resolve a working `cloudflared` from PATH, or null if not runnable. */
388
+ async function cloudflaredOnPath() {
389
+ return await runsOk("cloudflared") ? "cloudflared" : null;
390
+ }
391
+ /** True when `<bin> --version` exits 0 (binary present + executable). */
392
+ function runsOk(bin) {
393
+ return new Promise((resolve) => {
394
+ let done = false;
395
+ const finish = (ok) => {
396
+ if (!done) {
397
+ done = true;
398
+ resolve(ok);
399
+ }
400
+ };
401
+ try {
402
+ const child = spawn(bin, ["--version"], { stdio: "ignore" });
403
+ child.on("error", () => finish(false));
404
+ child.on("exit", (code) => finish(code === 0));
405
+ setTimeout(() => {
406
+ child.kill("SIGKILL");
407
+ finish(false);
408
+ }, 5e3);
409
+ } catch {
410
+ finish(false);
411
+ }
412
+ });
413
+ }
414
+ async function downloadTo(url, dest) {
415
+ const res = await fetch(url, { redirect: "follow" });
416
+ if (!res.ok || !res.body) throw new Error(`cloudflared download failed: HTTP ${res.status} for ${url}`);
417
+ const tmp = `${dest}.partial`;
418
+ await pipeline(res.body, fs.createWriteStream(tmp));
419
+ await fsp.rename(tmp, dest);
420
+ }
421
+ /** Extract the `cloudflared` entry from a downloaded .tgz into `destBin`. */
422
+ async function extractArchive(archivePath, destBin, logger) {
423
+ const dir = path.dirname(destBin);
424
+ await new Promise((resolve, reject) => {
425
+ const child = spawn("tar", [
426
+ "-xzf",
427
+ archivePath,
428
+ "-C",
429
+ dir
430
+ ], { stdio: "inherit" });
431
+ child.on("error", reject);
432
+ child.on("exit", (code) => code === 0 ? resolve() : reject(/* @__PURE__ */ new Error(`tar exited ${code}`)));
433
+ });
434
+ if (!fs.existsSync(destBin)) logger.warn("cloudflared archive did not yield expected binary name", {
435
+ meta: { destBin },
436
+ tags: {
437
+ topic: "tunnel",
438
+ phase: "extract"
439
+ }
440
+ });
441
+ await fsp.rm(archivePath, { force: true });
442
+ }
443
+ /**
444
+ * Return a path/command for a runnable `cloudflared`, downloading + caching it
445
+ * on first use. Prefers an existing PATH install; otherwise caches under
446
+ * `binDir`. Throws (with an actionable message) if it cannot be provisioned.
447
+ */
448
+ async function ensureCloudflared(opts) {
449
+ const onPath = await cloudflaredOnPath();
450
+ if (onPath) return onPath;
451
+ const cached = path.join(opts.binDir, os.platform() === "win32" ? "cloudflared.exe" : "cloudflared");
452
+ if (fs.existsSync(cached) && await runsOk(cached)) return cached;
453
+ const version = opts.version ?? "2025.4.2";
454
+ const asset = cloudflaredAsset(os.platform(), os.arch());
455
+ const url = `${RELEASE_BASE}/${version}/${asset.fileName}`;
456
+ await fsp.mkdir(opts.binDir, { recursive: true });
457
+ opts.logger.info("Provisioning cloudflared (not on PATH) — downloading", {
458
+ meta: {
459
+ version,
460
+ asset: asset.fileName,
461
+ dest: cached
462
+ },
463
+ tags: {
464
+ topic: "tunnel",
465
+ phase: "provision"
466
+ }
467
+ });
468
+ if (asset.isArchive) {
469
+ const archive = path.join(opts.binDir, asset.fileName);
470
+ await downloadTo(url, archive);
471
+ await extractArchive(archive, cached, opts.logger);
472
+ } else await downloadTo(url, cached);
473
+ await fsp.chmod(cached, 493);
474
+ if (!await runsOk(cached)) throw new Error(`cloudflared downloaded to ${cached} but is not runnable`);
475
+ opts.logger.info("cloudflared ready", {
476
+ meta: {
477
+ path: cached,
478
+ version
479
+ },
480
+ tags: {
481
+ topic: "tunnel",
482
+ phase: "provision-done"
483
+ }
484
+ });
485
+ return cached;
486
+ }
342
487
  //#endregion
343
488
  //#region src/tunnel/cloudflare-api.ts
344
489
  /**
@@ -665,6 +810,19 @@ var CloudflareTunnelAddon = class extends BaseAddon {
665
810
  }
666
811
  return "127.0.0.1";
667
812
  }
813
+ /**
814
+ * Resolve the `cloudflared` binary path once per addon lifetime (PATH install,
815
+ * or a copy downloaded + cached under `<dataDir>/bin`). Memoized so the
816
+ * (possible) download happens at most once, on the first tunnel start.
817
+ */
818
+ cloudflaredBinPromise;
819
+ resolveCloudflared() {
820
+ this.cloudflaredBinPromise ??= ensureCloudflared({
821
+ binDir: path.join(this.ctx.dataDir, "bin"),
822
+ logger: this.ctx.logger
823
+ });
824
+ return this.cloudflaredBinPromise;
825
+ }
668
826
  async onInitialize() {
669
827
  if (this.config.localPort === 3001) {
670
828
  this.ctx.logger.warn("Resetting stale localPort=3001 (Vite admin-ui dev port) → 0 (auto)", { tags: {
@@ -678,7 +836,7 @@ var CloudflareTunnelAddon = class extends BaseAddon {
678
836
  ...this.config,
679
837
  localPort: this.resolveLocalPort(),
680
838
  localHost
681
- }, this.ctx.logger, this.ctx.eventBus);
839
+ }, this.ctx.logger, this.ctx.eventBus, () => this.resolveCloudflared());
682
840
  this.ctx.logger.info("Cloudflare Tunnel addon initialized", { meta: {
683
841
  mode: this.config.mode,
684
842
  hasCustomToken: !!this.config.customTunnelToken
@@ -734,7 +892,7 @@ var CloudflareTunnelAddon = class extends BaseAddon {
734
892
  ...this.config,
735
893
  localPort: this.resolveLocalPort(),
736
894
  localHost
737
- }, this.ctx.logger, this.ctx.eventBus);
895
+ }, this.ctx.logger, this.ctx.eventBus, () => this.resolveCloudflared());
738
896
  if (this.isRunCapable()) {
739
897
  this.ctx.logger.info("config changed — (re-)spawning tunnel", {
740
898
  meta: {
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dist = require("../dist-FQAxmoZY.js");
2
+ const require_dist = require("../dist-Bj5XzuE6.js");
3
3
  //#region src/turn/cloudflare-turn.ts
4
4
  /**
5
5
  * Cloudflare returns ICE servers in several flavours depending on which
@@ -1,4 +1,4 @@
1
- import { d as literal, f as number, i as defineCustomActions, l as boolean, m as string, o as turnProviderCapability, p as object, r as customAction, t as BaseAddon, u as discriminatedUnion } from "../dist-gS41k7Cx.mjs";
1
+ import { d as literal, f as number, i as defineCustomActions, l as boolean, m as string, o as turnProviderCapability, p as object, r as customAction, t as BaseAddon, u as discriminatedUnion } from "../dist-D4obfhYq.mjs";
2
2
  //#region src/turn/cloudflare-turn.ts
3
3
  /**
4
4
  * Cloudflare returns ICE servers in several flavours depending on which
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-cloudflare",
3
- "version": "1.0.3",
3
+ "version": "1.0.5",
4
4
  "description": "Cloudflare bundle — Tunnel (network-access) + TURN relay (turn-provider). Multi-entry npm package shipping 2 addons under a single bundle.",
5
5
  "keywords": [
6
6
  "camstack",