supadns-js 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 SupaDNS Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,151 @@
1
+ # SupaDNS
2
+
3
+ **Bypass blocked DNS for Supabase** — a Node.js/TypeScript library that resolves `*.supabase.co` via DNS-over-HTTPS (DoH) when your ISP's DNS is blocking access.
4
+
5
+ ## The Problem
6
+
7
+ In some regions (e.g. India), ISP-level DNS blocking prevents applications from resolving `*.supabase.co` domains. This means `createClient()` from `@supabase/supabase-js` silently fails with DNS errors — even though Supabase itself is not down.
8
+
9
+ ## The Solution
10
+
11
+ SupaDNS is a **drop-in compatibility layer** that:
12
+
13
+ 1. Tries normal system DNS first (zero overhead when DNS works).
14
+ 2. On DNS failure, automatically resolves via **DNS-over-HTTPS** using trusted resolvers.
15
+ 3. Connects to the resolved IP while preserving TLS certificate validation.
16
+
17
+ **No Supabase API behavior changes. No API key storage. Fully stateless.**
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ npm install supadns-js @supabase/supabase-js
23
+ ```
24
+
25
+ ### Before (standard Supabase)
26
+
27
+ ```typescript
28
+ import { createClient } from "supabase-js";
29
+
30
+ const supabase = createClient(
31
+ "https://myproject.supabase.co",
32
+ "your-anon-key"
33
+ );
34
+ ```
35
+
36
+ ### After (with DNS bypass)
37
+
38
+ ```typescript
39
+ import { createSmartClient } from "supadns-js";
40
+
41
+ const supabase = createSmartClient(
42
+ "https://myproject.supabase.co",
43
+ "your-anon-key"
44
+ );
45
+ ```
46
+
47
+ **That's it.** One import change. Everything else works the same — Auth, REST, Storage, Functions.
48
+
49
+ ## How It Works
50
+
51
+ ```
52
+ ┌─────────────────────────────────────────────────────────┐
53
+ │ Your App │
54
+ │ createSmartClient(url, key) │
55
+ │ │ │
56
+ │ ▼ │
57
+ │ ┌──────────────────┐ │
58
+ │ │ supabase-wrapper │ ◄── injects smartFetch │
59
+ │ └────────┬─────────┘ │
60
+ │ ▼ │
61
+ │ ┌──────────────────────┐ │
62
+ │ │ connection-manager │ │
63
+ │ │ │ │
64
+ │ │ 1. Try system DNS │──── success ──► response │
65
+ │ │ 2. DNS failed? │ │
66
+ │ │ ▼ │ │
67
+ │ │ ┌────────────────┐ │ │
68
+ │ │ │ doh-resolver │ │ Quad9 (primary) │
69
+ │ │ │ │ │ Cloudflare (fallback) │
70
+ │ │ │ DNS-over-HTTPS│ │ TTL-aware cache │
71
+ │ │ └───────┬────────┘ │ │
72
+ │ │ ▼ │ │
73
+ │ │ Connect via IP │──── success ──► response │
74
+ │ │ (preserve TLS SNI) │ │
75
+ │ └──────────────────────┘ │
76
+ └─────────────────────────────────────────────────────────┘
77
+ ```
78
+
79
+ ## API Reference
80
+
81
+ ### `createSmartClient(url, key, options?)`
82
+
83
+ Drop-in replacement for `createClient`. Returns a standard `SupabaseClient`.
84
+
85
+ | Parameter | Type | Description |
86
+ | :--------------------- | :------- | :------------------------------------------ |
87
+ | `url` | `string` | Your Supabase project URL |
88
+ | `key` | `string` | Your Supabase anon/service key |
89
+ | `options` | `object` | Standard Supabase options + SupaDNS options |
90
+ | `options.initialDnsTimeoutMs` | `number` | Timeout for initial DNS attempt (default: 5000ms) |
91
+
92
+ ### `smartFetch(input, init?, options?)`
93
+
94
+ A fetch-compatible function with automatic DoH fallback. Useful if you want DNS bypass outside of Supabase.
95
+
96
+ ```typescript
97
+ import { smartFetch } from "supadns-js";
98
+
99
+ const res = await smartFetch("https://myproject.supabase.co/rest/v1/todos");
100
+ ```
101
+
102
+ ### `resolveDoH(hostname)`
103
+
104
+ Directly resolve a hostname via DNS-over-HTTPS. Returns an IPv4 address.
105
+
106
+ ```typescript
107
+ import { resolveDoH } from "supadns-js";
108
+
109
+ const ip = await resolveDoH("myproject.supabase.co");
110
+ // → "104.18.xxx.xxx"
111
+ ```
112
+
113
+ ### `SmartAgent`
114
+
115
+ Custom `https.Agent` for use with libraries that accept an agent option (e.g. axios, got).
116
+
117
+ ```typescript
118
+ import { SmartAgent } from "supadns-js";
119
+ import axios from "axios";
120
+
121
+ const agent = new SmartAgent();
122
+ const res = await axios.get("https://myproject.supabase.co/rest/v1/todos", {
123
+ httpsAgent: agent,
124
+ });
125
+ ```
126
+
127
+ ## Resolver Priority
128
+
129
+ | Priority | Resolver | Protocol |
130
+ | :------: | :-------------------------------- | :------- |
131
+ | 1 | System DNS | UDP/53 |
132
+ | 2 | Quad9 (`dns.quad9.net`) | DoH |
133
+ | 3 | Cloudflare (`cloudflare-dns.com`) | DoH |
134
+
135
+ ## Requirements
136
+
137
+ - **Node.js** ≥ 18.0.0 (uses built-in `fetch`)
138
+ - **@supabase/supabase-js** ≥ 2.0.0
139
+
140
+ ## Safety & Privacy
141
+
142
+ - ✅ API keys are never stored or logged
143
+ - ✅ Authorization headers are never logged
144
+ - ✅ All request handling is stateless
145
+ - ✅ TLS certificate validation is always enforced
146
+ - ✅ DNS queries use encrypted HTTPS transport
147
+
148
+ ## License
149
+
150
+ MIT
151
+
@@ -0,0 +1,5 @@
1
+ export interface SmartFetchOptions {
2
+ initialTimeoutMs?: number;
3
+ }
4
+ export declare function smartFetch(input: string | URL | Request, init?: RequestInit, options?: SmartFetchOptions): Promise<Response>;
5
+ //# sourceMappingURL=connection-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-manager.d.ts","sourceRoot":"","sources":["../src/connection-manager.ts"],"names":[],"mappings":"AAyBA,MAAM,WAAW,iBAAiB;IAC9B,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAID,wBAAsB,UAAU,CAC5B,KAAK,EAAE,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,IAAI,CAAC,EAAE,WAAW,EAClB,OAAO,CAAC,EAAE,iBAAiB,GAC5B,OAAO,CAAC,QAAQ,CAAC,CA4BnB"}
@@ -0,0 +1,163 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.smartFetch = smartFetch;
37
+ const https = __importStar(require("node:https"));
38
+ const doh_resolver_js_1 = require("./doh-resolver.js");
39
+ const smart_agent_js_1 = require("./smart-agent.js");
40
+ const INITIAL_ATTEMPT_TIMEOUT_MS = 10_000;
41
+ const DNS_FAILURE_PATTERNS = [
42
+ "ENOTFOUND",
43
+ "EAI_AGAIN",
44
+ "ECONNREFUSED",
45
+ "ETIMEDOUT",
46
+ "ECONNRESET",
47
+ "UND_ERR_CONNECT_TIMEOUT",
48
+ "getaddrinfo",
49
+ "dns",
50
+ "fetch failed",
51
+ "network error",
52
+ ];
53
+ function isDnsOrConnectionError(err) {
54
+ if (!(err instanceof Error))
55
+ return false;
56
+ const msg = `${err.message} ${"code" in err ? err.code : ""}`.toLowerCase();
57
+ return DNS_FAILURE_PATTERNS.some((pat) => msg.includes(pat.toLowerCase()));
58
+ }
59
+ // Drop-in fetch() replacement. Tries system DNS first,
60
+ // falls back to DoH for Supabase domains on DNS failure.
61
+ async function smartFetch(input, init, options) {
62
+ const timeoutMs = options?.initialTimeoutMs ?? INITIAL_ATTEMPT_TIMEOUT_MS;
63
+ try {
64
+ const controller = new AbortController();
65
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
66
+ const callerSignal = init?.signal;
67
+ const mergedInit = { ...init, signal: controller.signal };
68
+ if (callerSignal) {
69
+ callerSignal.addEventListener("abort", () => controller.abort(callerSignal.reason), { once: true });
70
+ }
71
+ const res = await fetch(input, mergedInit);
72
+ clearTimeout(timer);
73
+ return res;
74
+ }
75
+ catch (err) {
76
+ const url = parseUrl(input);
77
+ if (!url || !(0, doh_resolver_js_1.isSupabaseDomain)(url.hostname) || !isDnsOrConnectionError(err)) {
78
+ throw err;
79
+ }
80
+ return await fetchViaDoH(url, init);
81
+ }
82
+ }
83
+ function parseUrl(input) {
84
+ try {
85
+ if (input instanceof Request)
86
+ return new URL(input.url);
87
+ return new URL(input.toString());
88
+ }
89
+ catch {
90
+ return null;
91
+ }
92
+ }
93
+ let _fallbackAgent = null;
94
+ function getFallbackAgent() {
95
+ if (!_fallbackAgent)
96
+ _fallbackAgent = new smart_agent_js_1.SmartAgent();
97
+ return _fallbackAgent;
98
+ }
99
+ // DoH fallback: resolve via DoH, connect with node:https + SmartAgent for correct TLS SNI
100
+ async function fetchViaDoH(url, init) {
101
+ const resolvedIp = await (0, doh_resolver_js_1.resolveDoH)(url.hostname);
102
+ const agent = getFallbackAgent();
103
+ return new Promise((resolve, reject) => {
104
+ const headers = {};
105
+ if (init?.headers) {
106
+ const h = new Headers(init.headers);
107
+ h.forEach((value, key) => { headers[key] = value; });
108
+ }
109
+ if (!headers["host"] && !headers["Host"]) {
110
+ headers["Host"] = url.host;
111
+ }
112
+ const reqOptions = {
113
+ hostname: resolvedIp,
114
+ port: url.port ? parseInt(url.port) : 443,
115
+ path: url.pathname + url.search,
116
+ method: (init?.method || "GET").toUpperCase(),
117
+ headers,
118
+ agent,
119
+ servername: url.hostname,
120
+ };
121
+ const req = https.request(reqOptions, (res) => {
122
+ const chunks = [];
123
+ res.on("data", (chunk) => chunks.push(chunk));
124
+ res.on("end", () => {
125
+ const body = Buffer.concat(chunks);
126
+ const responseHeaders = new Headers();
127
+ for (const [key, value] of Object.entries(res.headers)) {
128
+ if (value) {
129
+ if (Array.isArray(value)) {
130
+ value.forEach((v) => responseHeaders.append(key, v));
131
+ }
132
+ else {
133
+ responseHeaders.set(key, value);
134
+ }
135
+ }
136
+ }
137
+ resolve(new Response(body, {
138
+ status: res.statusCode || 200,
139
+ statusText: res.statusMessage || "",
140
+ headers: responseHeaders,
141
+ }));
142
+ });
143
+ res.on("error", reject);
144
+ });
145
+ req.on("error", reject);
146
+ if (init?.body) {
147
+ if (typeof init.body === "string") {
148
+ req.write(init.body);
149
+ }
150
+ else if (init.body instanceof ArrayBuffer) {
151
+ req.write(Buffer.from(init.body));
152
+ }
153
+ else if (init.body instanceof Uint8Array) {
154
+ req.write(init.body);
155
+ }
156
+ else {
157
+ req.write(String(init.body));
158
+ }
159
+ }
160
+ req.end();
161
+ });
162
+ }
163
+ //# sourceMappingURL=connection-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"connection-manager.js","sourceRoot":"","sources":["../src/connection-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { Buffer } from "node:buffer";
2
+ export declare function getCached(hostname: string): string | null;
3
+ export declare function setCache(hostname: string, ip: string, ttl: number): void;
4
+ export declare function clearCache(): void;
5
+ export declare function encodeDnsQuery(hostname: string): Buffer;
6
+ export declare function decodeDnsResponse(buf: Buffer): {
7
+ ip: string;
8
+ ttl: number;
9
+ } | null;
10
+ export declare function resolveDoH(hostname: string): Promise<string>;
11
+ export declare function isSupabaseDomain(hostname: string): boolean;
12
+ //# sourceMappingURL=doh-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doh-resolver.d.ts","sourceRoot":"","sources":["../src/doh-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAiBrC,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAQzD;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAGxE;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAGD,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAuBvD;AAGD,wBAAgB,iBAAiB,CAC7B,GAAG,EAAE,MAAM,GACZ;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAmCpC;AAID,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyClE;AAED,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE1D"}
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getCached = getCached;
4
+ exports.setCache = setCache;
5
+ exports.clearCache = clearCache;
6
+ exports.encodeDnsQuery = encodeDnsQuery;
7
+ exports.decodeDnsResponse = decodeDnsResponse;
8
+ exports.resolveDoH = resolveDoH;
9
+ exports.isSupabaseDomain = isSupabaseDomain;
10
+ const node_buffer_1 = require("node:buffer");
11
+ const DOH_ENDPOINTS = [
12
+ "https://dns.quad9.net/dns-query",
13
+ "https://cloudflare-dns.com/dns-query",
14
+ ];
15
+ const DOH_TIMEOUT_MS = 5_000;
16
+ const MIN_TTL_SECONDS = 30;
17
+ const cache = new Map();
18
+ function getCached(hostname) {
19
+ const entry = cache.get(hostname);
20
+ if (!entry)
21
+ return null;
22
+ if (Date.now() > entry.expiresAt) {
23
+ cache.delete(hostname);
24
+ return null;
25
+ }
26
+ return entry.ip;
27
+ }
28
+ function setCache(hostname, ip, ttl) {
29
+ const effectiveTtl = Math.max(ttl, MIN_TTL_SECONDS);
30
+ cache.set(hostname, { ip, expiresAt: Date.now() + effectiveTtl * 1000 });
31
+ }
32
+ function clearCache() {
33
+ cache.clear();
34
+ }
35
+ // DNS wire-format encoding (RFC 1035) for a single A record query
36
+ function encodeDnsQuery(hostname) {
37
+ const id = node_buffer_1.Buffer.alloc(2);
38
+ id.writeUInt16BE(0x0001);
39
+ const header = node_buffer_1.Buffer.from([
40
+ ...id,
41
+ 0x01, 0x00, // RD=1
42
+ 0x00, 0x01, // QDCOUNT=1
43
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
44
+ ]);
45
+ const labels = hostname.split(".");
46
+ const questionParts = [];
47
+ for (const label of labels) {
48
+ const len = node_buffer_1.Buffer.alloc(1);
49
+ len.writeUInt8(label.length);
50
+ questionParts.push(len, node_buffer_1.Buffer.from(label, "ascii"));
51
+ }
52
+ questionParts.push(node_buffer_1.Buffer.from([0x00])); // root
53
+ questionParts.push(node_buffer_1.Buffer.from([0x00, 0x01])); // QTYPE=A
54
+ questionParts.push(node_buffer_1.Buffer.from([0x00, 0x01])); // QCLASS=IN
55
+ return node_buffer_1.Buffer.concat([header, ...questionParts]);
56
+ }
57
+ // Decode a DNS response, return first A record or null
58
+ function decodeDnsResponse(buf) {
59
+ let offset = 12; // skip header
60
+ const qdcount = buf.readUInt16BE(4);
61
+ for (let i = 0; i < qdcount; i++) {
62
+ while (buf[offset] !== 0x00) {
63
+ if ((buf[offset] & 0xc0) === 0xc0) {
64
+ offset += 2;
65
+ break;
66
+ }
67
+ offset += buf[offset] + 1;
68
+ }
69
+ if (buf[offset] === 0x00)
70
+ offset += 1;
71
+ offset += 4; // QTYPE + QCLASS
72
+ }
73
+ const ancount = buf.readUInt16BE(6);
74
+ for (let i = 0; i < ancount; i++) {
75
+ if ((buf[offset] & 0xc0) === 0xc0) {
76
+ offset += 2;
77
+ }
78
+ else {
79
+ while (buf[offset] !== 0x00) {
80
+ offset += buf[offset] + 1;
81
+ }
82
+ offset += 1;
83
+ }
84
+ const rtype = buf.readUInt16BE(offset);
85
+ offset += 2;
86
+ offset += 2; // rclass
87
+ const ttl = buf.readUInt32BE(offset);
88
+ offset += 4;
89
+ const rdlength = buf.readUInt16BE(offset);
90
+ offset += 2;
91
+ if (rtype === 1 && rdlength === 4) {
92
+ const ip = `${buf[offset]}.${buf[offset + 1]}.${buf[offset + 2]}.${buf[offset + 3]}`;
93
+ return { ip, ttl };
94
+ }
95
+ offset += rdlength;
96
+ }
97
+ return null;
98
+ }
99
+ // Resolve hostname to IPv4 via DoH. Tries Quad9 first, then Cloudflare.
100
+ // Results are cached by TTL.
101
+ async function resolveDoH(hostname) {
102
+ const cached = getCached(hostname);
103
+ if (cached)
104
+ return cached;
105
+ const query = encodeDnsQuery(hostname);
106
+ const errors = [];
107
+ for (const endpoint of DOH_ENDPOINTS) {
108
+ try {
109
+ const controller = new AbortController();
110
+ const timer = setTimeout(() => controller.abort(), DOH_TIMEOUT_MS);
111
+ const res = await fetch(endpoint, {
112
+ method: "POST",
113
+ headers: {
114
+ "Content-Type": "application/dns-message",
115
+ Accept: "application/dns-message",
116
+ },
117
+ body: new Uint8Array(query),
118
+ signal: controller.signal,
119
+ });
120
+ clearTimeout(timer);
121
+ if (!res.ok)
122
+ throw new Error(`DoH HTTP ${res.status} from ${endpoint}`);
123
+ const arrayBuf = await res.arrayBuffer();
124
+ const answer = decodeDnsResponse(node_buffer_1.Buffer.from(arrayBuf));
125
+ if (!answer)
126
+ throw new Error(`No A record from ${endpoint}`);
127
+ setCache(hostname, answer.ip, answer.ttl);
128
+ return answer.ip;
129
+ }
130
+ catch (err) {
131
+ errors.push(err instanceof Error ? err : new Error(String(err)));
132
+ }
133
+ }
134
+ throw new Error(`All DoH endpoints failed for ${hostname}: ${errors.map((e) => e.message).join("; ")}`);
135
+ }
136
+ function isSupabaseDomain(hostname) {
137
+ return hostname.endsWith(".supabase.co") || hostname === "supabase.co";
138
+ }
139
+ //# sourceMappingURL=doh-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doh-resolver.js","sourceRoot":"","sources":["../src/doh-resolver.ts"],"names":[],"mappings":";;AAiBA,8BAQC;AAED,4BAGC;AAED,gCAEC;AAGD,wCAuBC;AAGD,8CAqCC;AAID,gCAyCC;AAED,4CAEC;AArJD,6CAAqC;AAErC,MAAM,aAAa,GAAG;IAClB,iCAAiC;IACjC,sCAAsC;CAChC,CAAC;AAEX,MAAM,cAAc,GAAG,KAAK,CAAC;AAC7B,MAAM,eAAe,GAAG,EAAE,CAAC;AAO3B,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;AAE5C,SAAgB,SAAS,CAAC,QAAgB;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;QAC/B,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QACvB,OAAO,IAAI,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC,EAAE,CAAC;AACpB,CAAC;AAED,SAAgB,QAAQ,CAAC,QAAgB,EAAE,EAAU,EAAE,GAAW;IAC9D,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,eAAe,CAAC,CAAC;IACpD,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,GAAG,IAAI,EAAE,CAAC,CAAC;AAC7E,CAAC;AAED,SAAgB,UAAU;IACtB,KAAK,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC;AAED,kEAAkE;AAClE,SAAgB,cAAc,CAAC,QAAgB;IAC3C,MAAM,EAAE,GAAG,oBAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IAEzB,MAAM,MAAM,GAAG,oBAAM,CAAC,IAAI,CAAC;QACvB,GAAG,EAAE;QACL,IAAI,EAAE,IAAI,EAAE,OAAO;QACnB,IAAI,EAAE,IAAI,EAAE,YAAY;QACxB,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI;KACrC,CAAC,CAAC;IAEH,MAAM,MAAM,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnC,MAAM,aAAa,GAAa,EAAE,CAAC;IACnC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACzB,MAAM,GAAG,GAAG,oBAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAC5B,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QAC7B,aAAa,CAAC,IAAI,CAAC,GAAG,EAAE,oBAAM,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC;IACzD,CAAC;IACD,aAAa,CAAC,IAAI,CAAC,oBAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAO,OAAO;IACtD,aAAa,CAAC,IAAI,CAAC,oBAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;IACzD,aAAa,CAAC,IAAI,CAAC,oBAAM,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,YAAY;IAE3D,OAAO,oBAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,GAAG,aAAa,CAAC,CAAC,CAAC;AACrD,CAAC;AAED,uDAAuD;AACvD,SAAgB,iBAAiB,CAC7B,GAAW;IAEX,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC,cAAc;IAE/B,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;YAC1B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAE,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;gBAAC,MAAM,IAAI,CAAC,CAAC;gBAAC,MAAM;YAAC,CAAC;YAC3D,MAAM,IAAI,GAAG,CAAC,MAAM,CAAE,GAAG,CAAC,CAAC;QAC/B,CAAC;QACD,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI;YAAE,MAAM,IAAI,CAAC,CAAC;QACtC,MAAM,IAAI,CAAC,CAAC,CAAC,iBAAiB;IAClC,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IACpC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,EAAE,CAAC,EAAE,EAAE,CAAC;QAC/B,IAAI,CAAC,GAAG,CAAC,MAAM,CAAE,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YACjC,MAAM,IAAI,CAAC,CAAC;QAChB,CAAC;aAAM,CAAC;YACJ,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;gBAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAE,GAAG,CAAC,CAAC;YAAC,CAAC;YAC5D,MAAM,IAAI,CAAC,CAAC;QAChB,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,IAAI,CAAC,CAAC;QACpD,MAAM,IAAI,CAAC,CAAC,CAAC,SAAS;QACtB,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,IAAI,CAAC,CAAC;QAClD,MAAM,QAAQ,GAAG,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAAC,MAAM,IAAI,CAAC,CAAC;QAEvD,IAAI,KAAK,KAAK,CAAC,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YAChC,MAAM,EAAE,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC;YACrF,OAAO,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;QACvB,CAAC;QACD,MAAM,IAAI,QAAQ,CAAC;IACvB,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,wEAAwE;AACxE,6BAA6B;AACtB,KAAK,UAAU,UAAU,CAAC,QAAgB;IAC7C,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,KAAK,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACvC,MAAM,MAAM,GAAY,EAAE,CAAC;IAE3B,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;QACnC,IAAI,CAAC;YACD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;YACzC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,cAAc,CAAC,CAAC;YAEnE,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,QAAQ,EAAE;gBAC9B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACL,cAAc,EAAE,yBAAyB;oBACzC,MAAM,EAAE,yBAAyB;iBACpC;gBACD,IAAI,EAAE,IAAI,UAAU,CAAC,KAAK,CAAC;gBAC3B,MAAM,EAAE,UAAU,CAAC,MAAM;aAC5B,CAAC,CAAC;YAEH,YAAY,CAAC,KAAK,CAAC,CAAC;YAEpB,IAAI,CAAC,GAAG,CAAC,EAAE;gBAAE,MAAM,IAAI,KAAK,CAAC,YAAY,GAAG,CAAC,MAAM,SAAS,QAAQ,EAAE,CAAC,CAAC;YAExE,MAAM,QAAQ,GAAG,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC;YACzC,MAAM,MAAM,GAAG,iBAAiB,CAAC,oBAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;YAExD,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,EAAE,CAAC,CAAC;YAE7D,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;YAC1C,OAAO,MAAM,CAAC,EAAE,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACX,MAAM,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACrE,CAAC;IACL,CAAC;IAED,MAAM,IAAI,KAAK,CACX,gCAAgC,QAAQ,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACzF,CAAC;AACN,CAAC;AAED,SAAgB,gBAAgB,CAAC,QAAgB;IAC7C,OAAO,QAAQ,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,QAAQ,KAAK,aAAa,CAAC;AAC3E,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createSmartClient, type SmartClientOptions } from "./supabase-wrapper.js";
2
+ export { smartFetch } from "./connection-manager.js";
3
+ export { resolveDoH, isSupabaseDomain, clearCache, getCached, setCache, } from "./doh-resolver.js";
4
+ export { SmartAgent, getSharedAgent } from "./smart-agent.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,KAAK,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AACrD,OAAO,EACH,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,SAAS,EACT,QAAQ,GACX,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSharedAgent = exports.SmartAgent = exports.setCache = exports.getCached = exports.clearCache = exports.isSupabaseDomain = exports.resolveDoH = exports.smartFetch = exports.createSmartClient = void 0;
4
+ var supabase_wrapper_js_1 = require("./supabase-wrapper.js");
5
+ Object.defineProperty(exports, "createSmartClient", { enumerable: true, get: function () { return supabase_wrapper_js_1.createSmartClient; } });
6
+ var connection_manager_js_1 = require("./connection-manager.js");
7
+ Object.defineProperty(exports, "smartFetch", { enumerable: true, get: function () { return connection_manager_js_1.smartFetch; } });
8
+ var doh_resolver_js_1 = require("./doh-resolver.js");
9
+ Object.defineProperty(exports, "resolveDoH", { enumerable: true, get: function () { return doh_resolver_js_1.resolveDoH; } });
10
+ Object.defineProperty(exports, "isSupabaseDomain", { enumerable: true, get: function () { return doh_resolver_js_1.isSupabaseDomain; } });
11
+ Object.defineProperty(exports, "clearCache", { enumerable: true, get: function () { return doh_resolver_js_1.clearCache; } });
12
+ Object.defineProperty(exports, "getCached", { enumerable: true, get: function () { return doh_resolver_js_1.getCached; } });
13
+ Object.defineProperty(exports, "setCache", { enumerable: true, get: function () { return doh_resolver_js_1.setCache; } });
14
+ var smart_agent_js_1 = require("./smart-agent.js");
15
+ Object.defineProperty(exports, "SmartAgent", { enumerable: true, get: function () { return smart_agent_js_1.SmartAgent; } });
16
+ Object.defineProperty(exports, "getSharedAgent", { enumerable: true, get: function () { return smart_agent_js_1.getSharedAgent; } });
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6DAAmF;AAA1E,wHAAA,iBAAiB,OAAA;AAC1B,iEAAqD;AAA5C,mHAAA,UAAU,OAAA;AACnB,qDAM2B;AALvB,6GAAA,UAAU,OAAA;AACV,mHAAA,gBAAgB,OAAA;AAChB,6GAAA,UAAU,OAAA;AACV,4GAAA,SAAS,OAAA;AACT,2GAAA,QAAQ,OAAA;AAEZ,mDAA8D;AAArD,4GAAA,UAAU,OAAA;AAAE,gHAAA,cAAc,OAAA"}
@@ -0,0 +1,8 @@
1
+ import * as https from "node:https";
2
+ import type { Duplex } from "node:stream";
3
+ export declare class SmartAgent extends https.Agent {
4
+ constructor(options?: https.AgentOptions);
5
+ createConnection(options: Record<string, any>, callback?: (err: Error | null, stream: Duplex) => void): Duplex | null | undefined;
6
+ }
7
+ export declare function getSharedAgent(): SmartAgent;
8
+ //# sourceMappingURL=smart-agent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-agent.d.ts","sourceRoot":"","sources":["../src/smart-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAK1C,qBAAa,UAAW,SAAQ,KAAK,CAAC,KAAK;gBAC3B,OAAO,CAAC,EAAE,KAAK,CAAC,YAAY;IAIxC,gBAAgB,CACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5B,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GACvD,MAAM,GAAG,IAAI,GAAG,SAAS;CA+B/B;AAID,wBAAgB,cAAc,IAAI,UAAU,CAG3C"}
@@ -0,0 +1,80 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SmartAgent = void 0;
37
+ exports.getSharedAgent = getSharedAgent;
38
+ const https = __importStar(require("node:https"));
39
+ const tls = __importStar(require("node:tls"));
40
+ const doh_resolver_js_1 = require("./doh-resolver.js");
41
+ // Custom HTTPS Agent that resolves Supabase domains via DoH
42
+ // and connects to the resolved IP with correct TLS SNI.
43
+ class SmartAgent extends https.Agent {
44
+ constructor(options) {
45
+ super({ ...options, keepAlive: true });
46
+ }
47
+ createConnection(options, callback) {
48
+ const hostname = options.host || options.servername || "";
49
+ const port = options.port || 443;
50
+ if (!(0, doh_resolver_js_1.isSupabaseDomain)(hostname)) {
51
+ return super.createConnection(options, callback);
52
+ }
53
+ (0, doh_resolver_js_1.resolveDoH)(hostname)
54
+ .then((resolvedIp) => {
55
+ const socket = tls.connect({
56
+ host: resolvedIp,
57
+ port,
58
+ servername: hostname, // SNI must match the real hostname
59
+ rejectUnauthorized: options.rejectUnauthorized ?? true,
60
+ ALPNProtocols: options.ALPNProtocols,
61
+ ca: options.ca,
62
+ cert: options.cert,
63
+ key: options.key,
64
+ }, () => callback?.(null, socket));
65
+ socket.on("error", (err) => callback?.(err, socket));
66
+ })
67
+ .catch((err) => {
68
+ callback?.(err instanceof Error ? err : new Error(String(err)), null);
69
+ });
70
+ return undefined;
71
+ }
72
+ }
73
+ exports.SmartAgent = SmartAgent;
74
+ let _sharedAgent = null;
75
+ function getSharedAgent() {
76
+ if (!_sharedAgent)
77
+ _sharedAgent = new SmartAgent();
78
+ return _sharedAgent;
79
+ }
80
+ //# sourceMappingURL=smart-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smart-agent.js","sourceRoot":"","sources":["../src/smart-agent.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,wCAGC;AArDD,kDAAoC;AACpC,8CAAgC;AAEhC,uDAAiE;AAEjE,4DAA4D;AAC5D,wDAAwD;AACxD,MAAa,UAAW,SAAQ,KAAK,CAAC,KAAK;IACvC,YAAY,OAA4B;QACpC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,gBAAgB,CACZ,OAA4B,EAC5B,QAAsD;QAEtD,MAAM,QAAQ,GAAW,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,UAAU,IAAI,EAAE,CAAC;QAClE,MAAM,IAAI,GAAW,OAAO,CAAC,IAAI,IAAI,GAAG,CAAC;QAEzC,IAAI,CAAC,IAAA,kCAAgB,EAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,OAAO,KAAK,CAAC,gBAAgB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QACrD,CAAC;QAED,IAAA,4BAAU,EAAC,QAAQ,CAAC;aACf,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE;YACjB,MAAM,MAAM,GAAG,GAAG,CAAC,OAAO,CACtB;gBACI,IAAI,EAAE,UAAU;gBAChB,IAAI;gBACJ,UAAU,EAAE,QAAQ,EAAE,mCAAmC;gBACzD,kBAAkB,EAAE,OAAO,CAAC,kBAAkB,IAAI,IAAI;gBACtD,aAAa,EAAE,OAAO,CAAC,aAAa;gBACpC,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,GAAG,EAAE,OAAO,CAAC,GAAG;aACnB,EACD,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,CACjC,CAAC;YACF,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAU,EAAE,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC;QAChE,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;YACpB,QAAQ,EAAE,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,IAAW,CAAC,CAAC;QACjF,CAAC,CAAC,CAAC;QAEP,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ;AAvCD,gCAuCC;AAED,IAAI,YAAY,GAAsB,IAAI,CAAC;AAE3C,SAAgB,cAAc;IAC1B,IAAI,CAAC,YAAY;QAAE,YAAY,GAAG,IAAI,UAAU,EAAE,CAAC;IACnD,OAAO,YAAY,CAAC;AACxB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { type SupabaseClient, type SupabaseClientOptions } from "@supabase/supabase-js";
2
+ export interface SmartClientOptions extends SupabaseClientOptions<string> {
3
+ initialDnsTimeoutMs?: number;
4
+ }
5
+ export declare function createSmartClient(supabaseUrl: string, supabaseKey: string, options?: SmartClientOptions): SupabaseClient;
6
+ //# sourceMappingURL=supabase-wrapper.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supabase-wrapper.d.ts","sourceRoot":"","sources":["../src/supabase-wrapper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,cAAc,EAAE,KAAK,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAGtG,MAAM,WAAW,kBAAmB,SAAQ,qBAAqB,CAAC,MAAM,CAAC;IACrE,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAChC;AAGD,wBAAgB,iBAAiB,CAC7B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,kBAAkB,GAC7B,cAAc,CAgBhB"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createSmartClient = createSmartClient;
4
+ const supabase_js_1 = require("@supabase/supabase-js");
5
+ const connection_manager_js_1 = require("./connection-manager.js");
6
+ // Drop-in replacement for createClient with automatic DoH fallback.
7
+ function createSmartClient(supabaseUrl, supabaseKey, options) {
8
+ const { initialDnsTimeoutMs, ...supabaseOptions } = options ?? {};
9
+ const wrappedFetch = (input, init) => {
10
+ return (0, connection_manager_js_1.smartFetch)(input, init, { initialTimeoutMs: initialDnsTimeoutMs });
11
+ };
12
+ const mergedOptions = {
13
+ ...supabaseOptions,
14
+ global: {
15
+ ...supabaseOptions.global,
16
+ fetch: wrappedFetch,
17
+ },
18
+ };
19
+ return (0, supabase_js_1.createClient)(supabaseUrl, supabaseKey, mergedOptions);
20
+ }
21
+ //# sourceMappingURL=supabase-wrapper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"supabase-wrapper.js","sourceRoot":"","sources":["../src/supabase-wrapper.ts"],"names":[],"mappings":";;AAQA,8CAoBC;AA5BD,uDAAsG;AACtG,mEAAqD;AAMrD,oEAAoE;AACpE,SAAgB,iBAAiB,CAC7B,WAAmB,EACnB,WAAmB,EACnB,OAA4B;IAE5B,MAAM,EAAE,mBAAmB,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,IAAK,EAAyB,CAAC;IAE1F,MAAM,YAAY,GAA4B,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC1D,OAAO,IAAA,kCAAU,EAAC,KAAK,EAAE,IAAI,EAAE,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,CAAC,CAAC;IAC9E,CAAC,CAAC;IAEF,MAAM,aAAa,GAAkC;QACjD,GAAG,eAAe;QAClB,MAAM,EAAE;YACJ,GAAG,eAAe,CAAC,MAAM;YACzB,KAAK,EAAE,YAAY;SACtB;KACJ,CAAC;IAEF,OAAO,IAAA,0BAAY,EAAC,WAAW,EAAE,WAAW,EAAE,aAAa,CAAmB,CAAC;AACnF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "supadns-js",
3
+ "version": "1.0.0",
4
+ "description": "Bypass blocked DNS for Supabase using DNS-over-HTTPS. Drop-in replacement for createClient.",
5
+
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/supadns/supadns-js.git"
20
+ },
21
+
22
+ "homepage": "https://github.com/supadns/supadns-js#readme",
23
+
24
+ "bugs": {
25
+ "url": "https://github.com/supadns/supadns-js/issues"
26
+ },
27
+
28
+ "scripts": {
29
+ "build": "tsc",
30
+ "test": "vitest run",
31
+ "test:watch": "vitest",
32
+ "prepublishOnly": "npm run build"
33
+ },
34
+
35
+ "keywords": [
36
+ "supabase",
37
+ "dns",
38
+ "doh",
39
+ "dns-over-https",
40
+ "bypass",
41
+ "india",
42
+ "blocked",
43
+ "resolver"
44
+ ],
45
+
46
+ "license": "MIT",
47
+
48
+ "peerDependencies": {
49
+ "@supabase/supabase-js": ">=2.0.0"
50
+ },
51
+
52
+ "devDependencies": {
53
+ "@supabase/supabase-js": "^2.49.1",
54
+ "typescript": "^5.7.3",
55
+ "vitest": "^3.0.5"
56
+ },
57
+
58
+ "engines": {
59
+ "node": ">=18.0.0"
60
+ },
61
+
62
+ "files": [
63
+ "dist",
64
+ "README.md"
65
+ ]
66
+ }