@base44/functions-compiler 0.1.4 → 0.1.6
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/README.md +23 -9
- package/lib/src/assembly.js +2 -2
- package/lib/src/assembly.js.map +1 -1
- package/lib/src/assets.d.ts +7 -0
- package/lib/src/assets.js +15 -0
- package/lib/src/assets.js.map +1 -0
- package/lib/src/bundler.d.ts +3 -1
- package/lib/src/bundler.js +14 -1
- package/lib/src/bundler.js.map +1 -1
- package/lib/src/contracts.d.ts +2 -0
- package/lib/src/contracts.js +4 -0
- package/lib/src/contracts.js.map +1 -1
- package/lib/src/deno-bundle.js +2 -2
- package/lib/src/deno-bundle.js.map +1 -1
- package/lib/src/esbuild/deno-resolver.js +28 -24
- package/lib/src/esbuild/deno-resolver.js.map +1 -1
- package/lib/src/esbuild/private-data-sources-virtual.js +2 -3
- package/lib/src/esbuild/private-data-sources-virtual.js.map +1 -1
- package/lib/src/esbuild/runtime-context-specifier.d.ts +1 -0
- package/lib/src/esbuild/runtime-context-specifier.js +3 -0
- package/lib/src/esbuild/runtime-context-specifier.js.map +1 -0
- package/lib/src/esbuild/runtime-context-virtual.d.ts +2 -1
- package/lib/src/esbuild/runtime-context-virtual.js +4 -4
- package/lib/src/esbuild/runtime-context-virtual.js.map +1 -1
- package/lib/src/esbuild/runtime-virtual.js +4 -21
- package/lib/src/esbuild/runtime-virtual.js.map +1 -1
- package/lib/src/fetch-guard.js +4 -2
- package/lib/src/fetch-guard.js.map +1 -1
- package/lib/src/generated/assets.d.ts +1 -0
- package/lib/src/generated/assets.js +30 -0
- package/lib/src/generated/assets.js.map +1 -0
- package/lib/src/index.d.ts +1 -1
- package/lib/src/index.js.map +1 -1
- package/lib/src/lazy-deps.d.ts +2 -0
- package/lib/src/lazy-deps.js +11 -0
- package/lib/src/lazy-deps.js.map +1 -0
- package/lib/src/shards/build.d.ts +4 -1
- package/lib/src/shards/build.js +5 -4
- package/lib/src/shards/build.js.map +1 -1
- package/lib/src/version.d.ts +1 -1
- package/lib/src/version.js +1 -1
- package/lib/src/worker-entry.js +56 -40
- package/lib/src/worker-entry.js.map +1 -1
- package/package.json +4 -3
- package/lib/dist/activation-shim.mjs +0 -237
- package/lib/dist/actor.mjs +0 -1129
- package/lib/dist/deno-shim.mjs +0 -2347
- package/lib/src/private-data-sources/build-http.ts +0 -51
- package/lib/src/private-data-sources/elasticsearch.ts +0 -8
- package/lib/src/private-data-sources/http-url.ts +0 -23
- package/lib/src/private-data-sources/http.ts +0 -6
- package/lib/src/private-data-sources/hyperdrive.ts +0 -48
- package/lib/src/private-data-sources/ioredis-adapter.ts +0 -229
- package/lib/src/private-data-sources/manifest.ts +0 -150
- package/lib/src/private-data-sources/mariadb.ts +0 -8
- package/lib/src/private-data-sources/mongodb.ts +0 -70
- package/lib/src/private-data-sources/mysql.ts +0 -8
- package/lib/src/private-data-sources/node-net-adapter.ts +0 -247
- package/lib/src/private-data-sources/oracle.ts +0 -34
- package/lib/src/private-data-sources/postgres.ts +0 -8
- package/lib/src/private-data-sources/redis-auth.ts +0 -83
- package/lib/src/private-data-sources/redis.ts +0 -47
- package/lib/src/private-data-sources/runtime-environment.ts +0 -4
- package/lib/src/private-data-sources/runtime-manifest-store.ts +0 -32
- package/lib/src/private-data-sources/sqlserver.ts +0 -22
- package/lib/src/private-data-sources/tcp.ts +0 -86
- package/lib/src/private-data-sources/tedious-adapter.ts +0 -67
- package/lib/src/private-data-sources/types.ts +0 -29
- package/lib/src/runtime/index.ts +0 -30
- package/lib/src/runtime-context.ts +0 -29
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { resolveHttpPrivateDataSourceUrl } from "./http-url";
|
|
2
|
-
import {
|
|
3
|
-
currentPrivateDataSource,
|
|
4
|
-
privateDataSourceBinding,
|
|
5
|
-
} from "./manifest";
|
|
6
|
-
import type { FetchPrivateDataSourceBinding, PrivateDataSourceManifestEntry } from "./types";
|
|
7
|
-
|
|
8
|
-
// Internal, NOT a public virtual module: app code cannot import this builder to
|
|
9
|
-
// hand-forge a manifest entry. The only public entrypoints — http()/elasticsearch()
|
|
10
|
-
// — resolve their entry from the immutable runtime manifest via lookupPrivateDataSource.
|
|
11
|
-
|
|
12
|
-
function hasFetch(binding: unknown): binding is FetchPrivateDataSourceBinding {
|
|
13
|
-
return (
|
|
14
|
-
binding !== null &&
|
|
15
|
-
typeof binding === "object" &&
|
|
16
|
-
typeof (binding as { fetch?: unknown }).fetch === "function"
|
|
17
|
-
);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export function buildHttpPrivateDataSource(entry: PrivateDataSourceManifestEntry) {
|
|
21
|
-
return Object.freeze({
|
|
22
|
-
name: entry.name,
|
|
23
|
-
type: entry.type,
|
|
24
|
-
get bindingName() {
|
|
25
|
-
return currentPrivateDataSource(entry).bindingName;
|
|
26
|
-
},
|
|
27
|
-
fetch(resource: RequestInfo | URL, init?: RequestInit) {
|
|
28
|
-
const current = currentPrivateDataSource(entry);
|
|
29
|
-
const binding = privateDataSourceBinding(current);
|
|
30
|
-
if (!hasFetch(binding)) {
|
|
31
|
-
throw new Error(
|
|
32
|
-
`Private data source "${current.name}" does not expose fetch()`,
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
return binding.fetch(
|
|
36
|
-
resolveHttpPrivateDataSourceUrl(current, resource),
|
|
37
|
-
init,
|
|
38
|
-
);
|
|
39
|
-
},
|
|
40
|
-
// Fixed services expose the raw binding (Cloudflare pins their single
|
|
41
|
-
// target). Deferred Actor handles decide this from the request manifest,
|
|
42
|
-
// after their request environment exists.
|
|
43
|
-
get binding() {
|
|
44
|
-
const current = currentPrivateDataSource(entry);
|
|
45
|
-
return current.bindingName !== undefined
|
|
46
|
-
&& current.networkScope !== "network"
|
|
47
|
-
? privateDataSourceBinding(current)
|
|
48
|
-
: undefined;
|
|
49
|
-
},
|
|
50
|
-
});
|
|
51
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { buildHttpPrivateDataSource } from "./build-http";
|
|
2
|
-
import { privateDataSourceReference } from "./manifest";
|
|
3
|
-
|
|
4
|
-
export function elasticsearch(name: unknown) {
|
|
5
|
-
return buildHttpPrivateDataSource(
|
|
6
|
-
privateDataSourceReference(name, "elasticsearch"),
|
|
7
|
-
);
|
|
8
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { PrivateDataSourceManifestEntry } from "./types";
|
|
2
|
-
|
|
3
|
-
const FALLBACK_BASE_URL = "http://private-data-source.local";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Resolve a fetch target for an HTTP private data source. Relative paths resolve
|
|
7
|
-
* against the source's base URL; absolute URLs pass through. There is no host
|
|
8
|
-
* allowlist: for a fixed VPC service Cloudflare pins the single target, and for a
|
|
9
|
-
* network binding the reachable set is the customer's tunnel scope — the app can
|
|
10
|
-
* fetch any host behind it (e.g. Trino router + cluster nextUri hosts).
|
|
11
|
-
*/
|
|
12
|
-
export function resolveHttpPrivateDataSourceUrl(
|
|
13
|
-
entry: PrivateDataSourceManifestEntry,
|
|
14
|
-
resource: RequestInfo | URL,
|
|
15
|
-
): RequestInfo | URL {
|
|
16
|
-
if (resource instanceof Request) return resource;
|
|
17
|
-
const raw = resource instanceof URL ? resource.toString() : String(resource);
|
|
18
|
-
try {
|
|
19
|
-
return new URL(raw).toString();
|
|
20
|
-
} catch {
|
|
21
|
-
return new URL(raw || "/", entry.baseUrl || FALLBACK_BASE_URL).toString();
|
|
22
|
-
}
|
|
23
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
currentPrivateDataSource,
|
|
3
|
-
privateDataSourceReference,
|
|
4
|
-
privateDataSourceBinding,
|
|
5
|
-
} from "./manifest";
|
|
6
|
-
import type { PrivateDataSourceManifestEntry } from "./types";
|
|
7
|
-
|
|
8
|
-
export function buildHyperdrivePrivateDataSource(entry: PrivateDataSourceManifestEntry) {
|
|
9
|
-
const binding = () =>
|
|
10
|
-
privateDataSourceBinding(entry) as Record<string, unknown>;
|
|
11
|
-
return Object.freeze({
|
|
12
|
-
name: entry.name,
|
|
13
|
-
type: entry.type,
|
|
14
|
-
get bindingName() {
|
|
15
|
-
return currentPrivateDataSource(entry).bindingName;
|
|
16
|
-
},
|
|
17
|
-
get connectionString() {
|
|
18
|
-
return binding().connectionString;
|
|
19
|
-
},
|
|
20
|
-
get host() {
|
|
21
|
-
return binding().host;
|
|
22
|
-
},
|
|
23
|
-
get port() {
|
|
24
|
-
return binding().port;
|
|
25
|
-
},
|
|
26
|
-
get user() {
|
|
27
|
-
return binding().user;
|
|
28
|
-
},
|
|
29
|
-
get username() {
|
|
30
|
-
return binding().user;
|
|
31
|
-
},
|
|
32
|
-
get password() {
|
|
33
|
-
return binding().password;
|
|
34
|
-
},
|
|
35
|
-
get database() {
|
|
36
|
-
return binding().database;
|
|
37
|
-
},
|
|
38
|
-
get binding() {
|
|
39
|
-
return binding();
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function hyperdrive(name: unknown) {
|
|
45
|
-
return buildHyperdrivePrivateDataSource(
|
|
46
|
-
privateDataSourceReference(name, "hyperdrive"),
|
|
47
|
-
);
|
|
48
|
-
}
|
|
@@ -1,229 +0,0 @@
|
|
|
1
|
-
import { Buffer } from "node:buffer";
|
|
2
|
-
import { EventEmitter } from "node:events";
|
|
3
|
-
|
|
4
|
-
import { isCloudflareTcpSocket } from "./tcp";
|
|
5
|
-
|
|
6
|
-
type IoredisErrorEmitter = (type: string, err: Error) => void;
|
|
7
|
-
|
|
8
|
-
interface IoredisNetStream extends EventEmitter {
|
|
9
|
-
connecting: boolean;
|
|
10
|
-
destroyed: boolean;
|
|
11
|
-
readable: boolean;
|
|
12
|
-
writable: boolean;
|
|
13
|
-
remoteAddress?: string;
|
|
14
|
-
remotePort?: number;
|
|
15
|
-
_writableState: { ended: boolean };
|
|
16
|
-
write(data: string | Uint8Array, callback?: (err?: Error | null) => void): boolean;
|
|
17
|
-
end(data?: string | Uint8Array | (() => void), callback?: () => void): IoredisNetStream;
|
|
18
|
-
destroy(error?: Error): IoredisNetStream;
|
|
19
|
-
setNoDelay(noDelay?: boolean): IoredisNetStream;
|
|
20
|
-
setKeepAlive(enable?: boolean, initialDelay?: number): IoredisNetStream;
|
|
21
|
-
setTimeout(timeout: number, callback?: () => void): IoredisNetStream;
|
|
22
|
-
resume(): IoredisNetStream;
|
|
23
|
-
pause(): IoredisNetStream;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function exactNodeBuffer(value: Uint8Array): Buffer {
|
|
27
|
-
const bytes = new Uint8Array(value.byteLength);
|
|
28
|
-
bytes.set(value);
|
|
29
|
-
return Buffer.from(bytes.buffer);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function buildIoredisNetStream(
|
|
33
|
-
socket: unknown,
|
|
34
|
-
errorEmitter?: IoredisErrorEmitter,
|
|
35
|
-
): IoredisNetStream {
|
|
36
|
-
if (!isCloudflareTcpSocket(socket)) {
|
|
37
|
-
throw new Error("Redis private data source did not return a readable/writable TCP socket");
|
|
38
|
-
}
|
|
39
|
-
const cloudflareSocket = socket;
|
|
40
|
-
|
|
41
|
-
const encoder = new TextEncoder();
|
|
42
|
-
const stream = new EventEmitter() as IoredisNetStream;
|
|
43
|
-
let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
|
|
44
|
-
let writer: WritableStreamDefaultWriter<Uint8Array> | null = null;
|
|
45
|
-
let readStarted = false;
|
|
46
|
-
let connectEmitted = false;
|
|
47
|
-
let closed = false;
|
|
48
|
-
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
49
|
-
let writeChain = Promise.resolve();
|
|
50
|
-
|
|
51
|
-
function setClosed() {
|
|
52
|
-
if (closed) return;
|
|
53
|
-
closed = true;
|
|
54
|
-
stream.destroyed = true;
|
|
55
|
-
stream.readable = false;
|
|
56
|
-
stream.writable = false;
|
|
57
|
-
stream._writableState.ended = true;
|
|
58
|
-
if (timeout) {
|
|
59
|
-
clearTimeout(timeout);
|
|
60
|
-
timeout = null;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
reader?.releaseLock();
|
|
64
|
-
} catch {
|
|
65
|
-
// Ignore release errors when the stream is already closed.
|
|
66
|
-
}
|
|
67
|
-
reader = null;
|
|
68
|
-
try {
|
|
69
|
-
writer?.releaseLock();
|
|
70
|
-
} catch {
|
|
71
|
-
// Ignore release errors when the stream is already closed.
|
|
72
|
-
}
|
|
73
|
-
writer = null;
|
|
74
|
-
stream.emit("close");
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function emitError(error: unknown) {
|
|
78
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
79
|
-
errorEmitter?.("error", err);
|
|
80
|
-
stream.emit("error", err);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function markConnected() {
|
|
84
|
-
if (closed || connectEmitted) return;
|
|
85
|
-
connectEmitted = true;
|
|
86
|
-
stream.connecting = false;
|
|
87
|
-
stream.emit("connect");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async function readLoop() {
|
|
91
|
-
const activeReader = reader ?? cloudflareSocket.readable.getReader();
|
|
92
|
-
reader = activeReader;
|
|
93
|
-
try {
|
|
94
|
-
while (!closed) {
|
|
95
|
-
const { value, done } = await activeReader.read();
|
|
96
|
-
if (done) break;
|
|
97
|
-
if (value) stream.emit("data", exactNodeBuffer(value));
|
|
98
|
-
}
|
|
99
|
-
setClosed();
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (!closed) {
|
|
102
|
-
emitError(error);
|
|
103
|
-
setClosed();
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function writeBytes(bytes: Uint8Array) {
|
|
109
|
-
if (closed) return Promise.reject(new Error("Redis connection is closed"));
|
|
110
|
-
const activeWriter = writer ?? cloudflareSocket.writable.getWriter();
|
|
111
|
-
writer = activeWriter;
|
|
112
|
-
return activeWriter.write(bytes);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
stream.connecting = true;
|
|
116
|
-
stream.destroyed = false;
|
|
117
|
-
stream.readable = true;
|
|
118
|
-
stream.writable = true;
|
|
119
|
-
stream._writableState = { ended: false };
|
|
120
|
-
stream.write = (data, callback) => {
|
|
121
|
-
const bytes = typeof data === "string" ? encoder.encode(data) : data;
|
|
122
|
-
writeChain = writeChain
|
|
123
|
-
.then(() => writeBytes(bytes))
|
|
124
|
-
.then(
|
|
125
|
-
() => callback?.(),
|
|
126
|
-
(error) => {
|
|
127
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
128
|
-
callback?.(err);
|
|
129
|
-
emitError(err);
|
|
130
|
-
},
|
|
131
|
-
);
|
|
132
|
-
return true;
|
|
133
|
-
};
|
|
134
|
-
stream.end = (data?: string | Uint8Array | (() => void), callback?: () => void) => {
|
|
135
|
-
const done = typeof data === "function" ? data : callback;
|
|
136
|
-
if (typeof data === "string" || data instanceof Uint8Array) {
|
|
137
|
-
stream.write(data);
|
|
138
|
-
}
|
|
139
|
-
writeChain
|
|
140
|
-
.finally(() => {
|
|
141
|
-
done?.();
|
|
142
|
-
try {
|
|
143
|
-
cloudflareSocket.close?.();
|
|
144
|
-
} finally {
|
|
145
|
-
setClosed();
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
.catch(() => {});
|
|
149
|
-
return stream;
|
|
150
|
-
};
|
|
151
|
-
stream.destroy = (error?: Error) => {
|
|
152
|
-
if (error) emitError(error);
|
|
153
|
-
try {
|
|
154
|
-
cloudflareSocket.close?.();
|
|
155
|
-
} finally {
|
|
156
|
-
setClosed();
|
|
157
|
-
}
|
|
158
|
-
return stream;
|
|
159
|
-
};
|
|
160
|
-
stream.setNoDelay = () => stream;
|
|
161
|
-
stream.setKeepAlive = () => stream;
|
|
162
|
-
stream.setTimeout = (duration, callback) => {
|
|
163
|
-
if (timeout) clearTimeout(timeout);
|
|
164
|
-
timeout = null;
|
|
165
|
-
if (duration > 0) {
|
|
166
|
-
timeout = setTimeout(() => {
|
|
167
|
-
callback?.();
|
|
168
|
-
stream.emit("timeout");
|
|
169
|
-
}, duration);
|
|
170
|
-
}
|
|
171
|
-
return stream;
|
|
172
|
-
};
|
|
173
|
-
stream.resume = () => {
|
|
174
|
-
if (!readStarted) {
|
|
175
|
-
readStarted = true;
|
|
176
|
-
readLoop();
|
|
177
|
-
}
|
|
178
|
-
return stream;
|
|
179
|
-
};
|
|
180
|
-
stream.pause = () => stream;
|
|
181
|
-
|
|
182
|
-
if (cloudflareSocket.opened) {
|
|
183
|
-
cloudflareSocket.opened.then(markConnected, (error) => {
|
|
184
|
-
emitError(error);
|
|
185
|
-
stream.destroy(error instanceof Error ? error : new Error(String(error)));
|
|
186
|
-
});
|
|
187
|
-
} else {
|
|
188
|
-
queueMicrotask(markConnected);
|
|
189
|
-
}
|
|
190
|
-
cloudflareSocket.closed?.finally(setClosed).catch(() => {});
|
|
191
|
-
return stream;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
export function buildIoredisConnectorFactory(connect: () => unknown) {
|
|
195
|
-
return class Base44IoredisConnector {
|
|
196
|
-
firstError?: Error;
|
|
197
|
-
private stream?: IoredisNetStream;
|
|
198
|
-
private disconnectTimeout: number;
|
|
199
|
-
|
|
200
|
-
constructor(options?: { disconnectTimeout?: number }) {
|
|
201
|
-
this.disconnectTimeout =
|
|
202
|
-
typeof options?.disconnectTimeout === "number" ? options.disconnectTimeout : 2000;
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
check() {
|
|
206
|
-
return true;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
connect(errorEmitter?: IoredisErrorEmitter) {
|
|
210
|
-
return Promise.resolve(connect()).then((socket) => {
|
|
211
|
-
try {
|
|
212
|
-
this.stream = buildIoredisNetStream(socket, errorEmitter);
|
|
213
|
-
return this.stream;
|
|
214
|
-
} catch (error) {
|
|
215
|
-
this.firstError = error instanceof Error ? error : new Error(String(error));
|
|
216
|
-
throw this.firstError;
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
disconnect() {
|
|
222
|
-
const stream = this.stream;
|
|
223
|
-
if (!stream || stream.destroyed) return;
|
|
224
|
-
const timeout = setTimeout(() => stream.destroy(), this.disconnectTimeout);
|
|
225
|
-
stream.once("close", () => clearTimeout(timeout));
|
|
226
|
-
stream.end();
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
}
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
currentWorkerRuntimeContext,
|
|
3
|
-
workerEnvironment,
|
|
4
|
-
} from "./runtime-environment";
|
|
5
|
-
import { getRuntimeManifest } from "./runtime-manifest-store";
|
|
6
|
-
import type { PrivateDataSourceManifestEntry } from "./types";
|
|
7
|
-
|
|
8
|
-
const PRIVATE_DATA_SOURCES_MANIFEST_ENV = "BASE44_PRIVATE_DATA_SOURCES";
|
|
9
|
-
|
|
10
|
-
function readStringVar(key: string): string | undefined {
|
|
11
|
-
const value = workerEnvironment()[key];
|
|
12
|
-
return typeof value === "string" ? value : undefined;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Pin a HANDSHAKE-DELIVERED manifest to the deployed binding set: it is resolved
|
|
16
|
-
// fresh per activation, but the script's Hyperdrive/VPC bindings are frozen at
|
|
17
|
-
// upload — a source added/renamed since then must not surface an entry whose
|
|
18
|
-
// binding this script does not have (its lookup would pass and then fail deeper
|
|
19
|
-
// at privateDataSourceBinding). A manifest that exists as a Worker binding was
|
|
20
|
-
// frozen together with the live bindings, so it is served unfiltered.
|
|
21
|
-
function entryBindingIsDeployed(entry: PrivateDataSourceManifestEntry): boolean {
|
|
22
|
-
return typeof entry.bindingName === "string" && workerEnvironment()[entry.bindingName] !== undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function parsePrivateDataSourceManifest(): PrivateDataSourceManifestEntry[] {
|
|
26
|
-
// Runtime-secrets bundles receive the manifest through the private, single-
|
|
27
|
-
// instance store the activation shim writes (see runtime-manifest-store) — NOT
|
|
28
|
-
// the Worker binding, process.env, or any user-reachable global. Old mode has
|
|
29
|
-
// no runtime manifest and falls through to the immutable Worker binding.
|
|
30
|
-
const runtimeRaw = getRuntimeManifest();
|
|
31
|
-
const fromHandshake = runtimeRaw !== undefined;
|
|
32
|
-
const raw = fromHandshake ? runtimeRaw : readStringVar(PRIVATE_DATA_SOURCES_MANIFEST_ENV);
|
|
33
|
-
if (!raw) return [];
|
|
34
|
-
let parsed: unknown;
|
|
35
|
-
try {
|
|
36
|
-
parsed = JSON.parse(raw);
|
|
37
|
-
} catch {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
40
|
-
const entries = Array.isArray(parsed)
|
|
41
|
-
? parsed.filter(
|
|
42
|
-
(entry): entry is PrivateDataSourceManifestEntry =>
|
|
43
|
-
entry !== null && typeof entry === "object",
|
|
44
|
-
)
|
|
45
|
-
: [];
|
|
46
|
-
return fromHandshake ? entries.filter(entryBindingIsDeployed) : entries;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function normalizePrivateDataSourceKey(value: unknown): string {
|
|
50
|
-
return String(value || "")
|
|
51
|
-
.trim()
|
|
52
|
-
.toLowerCase()
|
|
53
|
-
.replace(/[^a-z0-9]+/g, "_")
|
|
54
|
-
.replace(/^_+|_+$/g, "");
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function privateDataSourceReference(
|
|
58
|
-
requestedName: unknown,
|
|
59
|
-
expectedType: string,
|
|
60
|
-
): PrivateDataSourceManifestEntry {
|
|
61
|
-
if (currentWorkerRuntimeContext()) {
|
|
62
|
-
return lookupPrivateDataSource(requestedName, expectedType);
|
|
63
|
-
}
|
|
64
|
-
return {
|
|
65
|
-
name: String(requestedName || ""),
|
|
66
|
-
type: expectedType,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function privateDataSourceMatchRank(
|
|
71
|
-
entry: PrivateDataSourceManifestEntry,
|
|
72
|
-
requestedName: unknown,
|
|
73
|
-
): number | null {
|
|
74
|
-
const requested = String(requestedName || "");
|
|
75
|
-
const normalizedRequested = normalizePrivateDataSourceKey(requested);
|
|
76
|
-
const candidates = [
|
|
77
|
-
{ value: entry.name, rank: 0 },
|
|
78
|
-
{ value: entry.bindingName, rank: 1 },
|
|
79
|
-
{ value: entry.name, rank: 2, normalized: true },
|
|
80
|
-
{ value: entry.bindingName, rank: 3, normalized: true },
|
|
81
|
-
];
|
|
82
|
-
let bestRank: number | null = null;
|
|
83
|
-
for (const candidate of candidates) {
|
|
84
|
-
if (typeof candidate.value !== "string") continue;
|
|
85
|
-
const matches = candidate.normalized
|
|
86
|
-
? normalizePrivateDataSourceKey(candidate.value) === normalizedRequested
|
|
87
|
-
: candidate.value === requested;
|
|
88
|
-
if (matches && (bestRank === null || candidate.rank < bestRank)) {
|
|
89
|
-
bestRank = candidate.rank;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
return bestRank;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function privateDataSourceMatchesExpectedType(
|
|
96
|
-
entry: PrivateDataSourceManifestEntry,
|
|
97
|
-
expectedType: string,
|
|
98
|
-
): boolean {
|
|
99
|
-
return entry.type === expectedType || (expectedType === "hyperdrive" && entry.bindingKind === "hyperdrive");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function lookupPrivateDataSource(
|
|
103
|
-
requestedName: unknown,
|
|
104
|
-
expectedType?: string,
|
|
105
|
-
): PrivateDataSourceManifestEntry {
|
|
106
|
-
const matches = parsePrivateDataSourceManifest()
|
|
107
|
-
.map((entry) => ({ entry, rank: privateDataSourceMatchRank(entry, requestedName) }))
|
|
108
|
-
.filter((match): match is { entry: PrivateDataSourceManifestEntry; rank: number } => match.rank !== null)
|
|
109
|
-
.sort((a, b) => a.rank - b.rank);
|
|
110
|
-
if (matches.length === 0) {
|
|
111
|
-
throw new Error(`Private data source "${requestedName}" is not bound to this backend function`);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const typeMatches = expectedType
|
|
115
|
-
? matches.filter((candidate) => privateDataSourceMatchesExpectedType(candidate.entry, expectedType))
|
|
116
|
-
: matches;
|
|
117
|
-
if (typeMatches.length === 0) {
|
|
118
|
-
throw new Error(`Private data source "${requestedName}" is not a ${expectedType} data source`);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const bestRank = typeMatches[0].rank;
|
|
122
|
-
const bestMatches = typeMatches.filter((candidate) => candidate.rank === bestRank);
|
|
123
|
-
if (bestMatches.length > 1) {
|
|
124
|
-
const kind = expectedType ? ` ${expectedType}` : "";
|
|
125
|
-
throw new Error(
|
|
126
|
-
`Private data source "${requestedName}" is ambiguous: ${bestMatches.length}${kind} data sources match this name`,
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
return bestMatches[0].entry;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
export function privateDataSourceBinding(entry: PrivateDataSourceManifestEntry): unknown {
|
|
133
|
-
const current = lookupPrivateDataSource(entry.name, entry.type);
|
|
134
|
-
if (!current.bindingName) {
|
|
135
|
-
throw new Error(`Private data source "${current.name}" has no Worker binding name`);
|
|
136
|
-
}
|
|
137
|
-
const binding = workerEnvironment()[current.bindingName];
|
|
138
|
-
if (!binding) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
`Private data source binding "${current.bindingName}" is missing from the Worker environment`,
|
|
141
|
-
);
|
|
142
|
-
}
|
|
143
|
-
return binding;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
export function currentPrivateDataSource(
|
|
147
|
-
entry: PrivateDataSourceManifestEntry,
|
|
148
|
-
): PrivateDataSourceManifestEntry {
|
|
149
|
-
return lookupPrivateDataSource(entry.name, entry.type);
|
|
150
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { buildHyperdrivePrivateDataSource } from "./hyperdrive";
|
|
2
|
-
import { privateDataSourceReference } from "./manifest";
|
|
3
|
-
|
|
4
|
-
export function mariadb(name: unknown) {
|
|
5
|
-
return buildHyperdrivePrivateDataSource(
|
|
6
|
-
privateDataSourceReference(name, "mariadb"),
|
|
7
|
-
);
|
|
8
|
-
}
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
currentPrivateDataSource,
|
|
3
|
-
normalizePrivateDataSourceKey,
|
|
4
|
-
privateDataSourceReference,
|
|
5
|
-
} from "./manifest";
|
|
6
|
-
import { buildTcpPrivateDataSource } from "./tcp";
|
|
7
|
-
import { installPrivateNodeNetAdapter, registerPrivateNodeNetRoute } from "./node-net-adapter";
|
|
8
|
-
import type { PrivateDataSourceManifestEntry } from "./types";
|
|
9
|
-
|
|
10
|
-
function encodeMongoConnectionPart(value: unknown): string {
|
|
11
|
-
return encodeURIComponent(String(value ?? ""));
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function buildMongoConnectionString(entry: PrivateDataSourceManifestEntry, hostname: string): string {
|
|
15
|
-
const username = entry.username ? encodeMongoConnectionPart(entry.username) : "";
|
|
16
|
-
const password = entry.password ? encodeMongoConnectionPart(entry.password) : "";
|
|
17
|
-
const auth = username ? `${username}${password ? `:${password}` : ""}@` : "";
|
|
18
|
-
const database = entry.database ? `/${encodeMongoConnectionPart(entry.database)}` : "";
|
|
19
|
-
const port = typeof entry.port === "number" ? entry.port : 27017;
|
|
20
|
-
return `mongodb://${auth}${hostname}:${port}${database}?directConnection=true`;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export function mongodb(name: unknown) {
|
|
24
|
-
installPrivateNodeNetAdapter();
|
|
25
|
-
const entry = privateDataSourceReference(name, "mongodb");
|
|
26
|
-
const source = buildTcpPrivateDataSource(entry) as ReturnType<typeof buildTcpPrivateDataSource> & {
|
|
27
|
-
connect: (options?: unknown) => unknown;
|
|
28
|
-
};
|
|
29
|
-
const driverHostname = `base44-private-${normalizePrivateDataSourceKey(
|
|
30
|
-
entry.bindingName || entry.name || "mongodb",
|
|
31
|
-
)}`;
|
|
32
|
-
|
|
33
|
-
function registerRoute() {
|
|
34
|
-
const current = currentPrivateDataSource(entry);
|
|
35
|
-
const port = typeof current.port === "number" ? current.port : 27017;
|
|
36
|
-
const label = `MongoDB private data source "${current.name || current.bindingName || driverHostname}"`;
|
|
37
|
-
registerPrivateNodeNetRoute(driverHostname, port, {
|
|
38
|
-
label,
|
|
39
|
-
connect: () => source.connect(),
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const extension = {
|
|
44
|
-
driverHost: driverHostname,
|
|
45
|
-
get connectionString() {
|
|
46
|
-
const current = currentPrivateDataSource(entry);
|
|
47
|
-
registerRoute();
|
|
48
|
-
return buildMongoConnectionString(current, driverHostname);
|
|
49
|
-
},
|
|
50
|
-
get uri() {
|
|
51
|
-
const current = currentPrivateDataSource(entry);
|
|
52
|
-
registerRoute();
|
|
53
|
-
return buildMongoConnectionString(current, driverHostname);
|
|
54
|
-
},
|
|
55
|
-
mongoClientOptions(options?: Record<string, unknown>) {
|
|
56
|
-
registerRoute();
|
|
57
|
-
return Object.freeze({
|
|
58
|
-
directConnection: true,
|
|
59
|
-
serverSelectionTimeoutMS: 5000,
|
|
60
|
-
...options,
|
|
61
|
-
});
|
|
62
|
-
},
|
|
63
|
-
};
|
|
64
|
-
return Object.freeze(
|
|
65
|
-
Object.defineProperties(
|
|
66
|
-
extension,
|
|
67
|
-
Object.getOwnPropertyDescriptors(source),
|
|
68
|
-
) as typeof source & typeof extension,
|
|
69
|
-
);
|
|
70
|
-
}
|