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 +21 -0
- package/README.md +151 -0
- package/dist/connection-manager.d.ts +5 -0
- package/dist/connection-manager.d.ts.map +1 -0
- package/dist/connection-manager.js +163 -0
- package/dist/connection-manager.js.map +1 -0
- package/dist/doh-resolver.d.ts +12 -0
- package/dist/doh-resolver.d.ts.map +1 -0
- package/dist/doh-resolver.js +139 -0
- package/dist/doh-resolver.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/smart-agent.d.ts +8 -0
- package/dist/smart-agent.d.ts.map +1 -0
- package/dist/smart-agent.js +80 -0
- package/dist/smart-agent.js.map +1 -0
- package/dist/supabase-wrapper.d.ts +6 -0
- package/dist/supabase-wrapper.d.ts.map +1 -0
- package/dist/supabase-wrapper.js +21 -0
- package/dist/supabase-wrapper.js.map +1 -0
- package/package.json +66 -0
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 @@
|
|
|
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":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|