Skip to content

Commit

Permalink
Add support for Cloudflare Workers
Browse files Browse the repository at this point in the history
  • Loading branch information
scotttrinh committed Feb 15, 2024
1 parent fa3ceaf commit 5c152c5
Show file tree
Hide file tree
Showing 16 changed files with 189 additions and 165 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module.exports = {
// aren't imported as 'import type' in other parts of the generated
// querybuilder, so set this option to ensure we always do that
"@typescript-eslint/consistent-type-imports": "error",
"@typescript-eslint/no-namespace": "off"
"@typescript-eslint/no-namespace": "off",
"@typescript-eslint/no-inferrable-types": "off",
}
};
2 changes: 1 addition & 1 deletion compileForDeno.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ export async function run({

let resolvedImportPath = resolveImportPath(importPath, sourcePath);

for (const name of ["adapter", "adapter.shared", "adapter.crypto"]) {
for (const name of ["adapter", "adapter.crypto"]) {
if (resolvedImportPath.endsWith(`/${name}.node.ts`)) {
resolvedImportPath = resolvedImportPath.replace(
`/${name}.node.ts`,
Expand Down
22 changes: 11 additions & 11 deletions packages/driver/src/adapter.node.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as crypto from "crypto";
import { promises as fs } from "fs";
import * as net from "net";
import * as os from "os";
import * as path from "path";
import * as tls from "tls";
import * as crypto from "node:crypto";
import { promises as fs } from "node:fs";
import * as net from "node:net";
import * as os from "node:os";
import * as path from "node:path";
import * as tls from "node:tls";

import process from "process";
import * as readline from "readline";
import { Writable } from "stream";
import process from "node:process";
import * as readline from "node:readline";
import { Writable } from "node:stream";

export { path, net, fs, tls, process };

Expand Down Expand Up @@ -84,7 +84,7 @@ export function input(
): Promise<string> {
let silent = false;

const output = !!params?.silent
const output = params?.silent
? new Writable({
write(
chunk: any,
Expand All @@ -101,7 +101,7 @@ export function input(
output,
});

return new Promise((resolve, rej) => {
return new Promise((resolve) => {
rl.question(message, (val) => {
rl.close();
resolve(val);
Expand Down
12 changes: 0 additions & 12 deletions packages/driver/src/adapter.shared.deno.ts

This file was deleted.

6 changes: 0 additions & 6 deletions packages/driver/src/adapter.shared.node.ts

This file was deleted.

10 changes: 6 additions & 4 deletions packages/driver/src/browserClient.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { BaseClientPool, Client, ConnectOptions } from "./baseClient";
import { BaseClientPool, Client, type ConnectOptions } from "./baseClient";
import { getConnectArgumentsParser } from "./conUtils";
import cryptoUtils from "./browserCrypto";
import { EdgeDBError } from "./errors";
import { FetchConnection } from "./fetchConn";
import { getHTTPSCRAMAuth } from "./httpScram";
import { Options } from "./options";

const parseConnectArguments = getConnectArgumentsParser(null);
const makeConnectArgumentsParser = (env: Record<string, string | undefined>) =>
getConnectArgumentsParser(null, env);
const httpSCRAMAuth = getHTTPSCRAMAuth(cryptoUtils);

class FetchClientPool extends BaseClientPool {
Expand All @@ -22,11 +23,12 @@ export function createClient(): Client {
}

export function createHttpClient(
options?: string | ConnectOptions | null
options?: string | ConnectOptions | null,
env: Record<string, string | undefined> = {}
): Client {
return new Client(
new FetchClientPool(
parseConnectArguments,
makeConnectArgumentsParser(env),
typeof options === "string" ? { dsn: options } : options ?? {}
),
Options.defaults()
Expand Down
3 changes: 2 additions & 1 deletion packages/driver/src/conUtils.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ export const serverUtils = {
searchConfigDir: platform.searchConfigDir,
};

export const parseConnectArguments = getConnectArgumentsParser(serverUtils);
export const makeConnectArgumentsParser = () =>
getConnectArgumentsParser(serverUtils, process.env);
80 changes: 45 additions & 35 deletions packages/driver/src/conUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,11 @@

import * as errors from "./errors";
import {
Credentials,
type Credentials,
getCredentialsPath,
readCredentialsFile,
validateCredentials,
} from "./credentials";
import { getEnv } from "./adapter.shared.node";
import { Duration, parseHumanDurationString } from "./datatypes/datetime";
import { checkValidEdgeDBDuration } from "./codecs/datetime";
import { InterfaceError } from "./errors";
Expand Down Expand Up @@ -101,11 +100,12 @@ export type ConnectArgumentsParser = (
) => Promise<NormalizedConnectConfig>;

export function getConnectArgumentsParser(
utils: ServerUtils | null
utils: ServerUtils | null,
env: { [key: string]: string | undefined }
): ConnectArgumentsParser {
return async (opts: ConnectConfig) => {
return {
...(await parseConnectDsnAndArgs(opts, utils)),
...(await parseConnectDsnAndArgs(opts, utils, env)),
connectTimeout: opts.timeout,
logging: opts.logging ?? true,
};
Expand Down Expand Up @@ -156,8 +156,11 @@ export class ResolvedConnectConfig {
_waitUntilAvailableSource: string | null = null;

serverSettings: { [key: string]: string } = {};
env: { [key: string]: string | undefined } = {};

constructor(env: { [key: string]: string | undefined }) {
this.env = env;

constructor() {
this.setHost = this.setHost.bind(this);
this.setPort = this.setPort.bind(this);
this.setDatabase = this.setDatabase.bind(this);
Expand Down Expand Up @@ -271,7 +274,7 @@ export class ResolvedConnectConfig {
.join(", ")}`
);
}
const clientSecurity = getEnv("EDGEDB_CLIENT_SECURITY");
const clientSecurity = this.env["EDGEDB_CLIENT_SECURITY"];
if (clientSecurity !== undefined) {
if (
!["default", "insecure_dev_mode", "strict"].includes(clientSecurity)
Expand Down Expand Up @@ -483,9 +486,10 @@ export function parseDuration(duration: string | number | Duration): number {

async function parseConnectDsnAndArgs(
config: ConnectConfig,
serverUtils: ServerUtils | null
serverUtils: ServerUtils | null,
env: { [key: string]: string | undefined }
): Promise<PartiallyNormalizedConfig> {
const resolvedConfig = new ResolvedConnectConfig();
const resolvedConfig = new ResolvedConnectConfig(env);
let fromEnv = false;
let fromProject = false;

Expand All @@ -510,7 +514,7 @@ async function parseConnectDsnAndArgs(
user: config.user,
password: config.password,
secretKey: config.secretKey,
cloudProfile: getEnv("EDGEDB_CLOUD_PROFILE"),
cloudProfile: env["EDGEDB_CLOUD_PROFILE"],
tlsCA: config.tlsCA,
tlsCAFile: config.tlsCAFile,
tlsSecurity: config.tlsSecurity,
Expand Down Expand Up @@ -540,13 +544,14 @@ async function parseConnectDsnAndArgs(
},
`Cannot have more than one of the following connection options: ` +
`'dsn', 'instanceName', 'credentials', 'credentialsFile' or 'host'/'port'`,
serverUtils
serverUtils,
env
);

if (!hasCompoundOptions) {
// resolve config from env vars

let port: string | undefined = getEnv("EDGEDB_PORT");
let port: string | undefined = env["EDGEDB_PORT"];
if (resolvedConfig._port === null && port?.startsWith("tcp://")) {
// EDGEDB_PORT is set by 'docker --link' so ignore and warn
// tslint:disable-next-line: no-console
Expand All @@ -560,20 +565,20 @@ async function parseConnectDsnAndArgs(
await resolveConfigOptions(
resolvedConfig,
{
dsn: getEnv("EDGEDB_DSN"),
instanceName: getEnv("EDGEDB_INSTANCE"),
credentials: getEnv("EDGEDB_CREDENTIALS"),
credentialsFile: getEnv("EDGEDB_CREDENTIALS_FILE"),
host: getEnv("EDGEDB_HOST"),
dsn: env["EDGEDB_DSN"],
instanceName: env["EDGEDB_INSTANCE"],
credentials: env["EDGEDB_CREDENTIALS"],
credentialsFile: env["EDGEDB_CREDENTIALS_FILE"],
host: env["EDGEDB_HOST"],
port,
database: getEnv("EDGEDB_DATABASE"),
user: getEnv("EDGEDB_USER"),
password: getEnv("EDGEDB_PASSWORD"),
secretKey: getEnv("EDGEDB_SECRET_KEY"),
tlsCA: getEnv("EDGEDB_TLS_CA"),
tlsCAFile: getEnv("EDGEDB_TLS_CA_FILE"),
tlsSecurity: getEnv("EDGEDB_CLIENT_TLS_SECURITY"),
waitUntilAvailable: getEnv("EDGEDB_WAIT_UNTIL_AVAILABLE"),
database: env["EDGEDB_DATABASE"],
user: env["EDGEDB_USER"],
password: env["EDGEDB_PASSWORD"],
secretKey: env["EDGEDB_SECRET_KEY"],
tlsCA: env["EDGEDB_TLS_CA"],
tlsCAFile: env["EDGEDB_TLS_CA_FILE"],
tlsSecurity: env["EDGEDB_CLIENT_TLS_SECURITY"],
waitUntilAvailable: env["EDGEDB_WAIT_UNTIL_AVAILABLE"],
},
{
dsn: `'EDGEDB_DSN' environment variable`,
Expand All @@ -594,7 +599,8 @@ async function parseConnectDsnAndArgs(
`Cannot have more than one of the following connection environment variables: ` +
`'EDGEDB_DSN', 'EDGEDB_INSTANCE', 'EDGEDB_CREDENTIALS', ` +
`'EDGEDB_CREDENTIALS_FILE' or 'EDGEDB_HOST'`,
serverUtils
serverUtils,
env
));
}

Expand Down Expand Up @@ -643,7 +649,8 @@ async function parseConnectDsnAndArgs(
database: `project default database`,
},
"",
serverUtils
serverUtils,
env
);
fromProject = true;
} else {
Expand Down Expand Up @@ -690,7 +697,8 @@ async function resolveConfigOptions<
config: Config,
sources: { [key in keyof Config]: string },
compoundParamsError: string,
serverUtils: ServerUtils | null
serverUtils: ServerUtils | null,
env: Record<string, string | undefined>
): Promise<{ hasCompoundOptions: boolean; anyOptionsUsed: boolean }> {
let anyOptionsUsed = false;

Expand Down Expand Up @@ -780,7 +788,8 @@ async function resolveConfigOptions<
: config.host !== undefined
? sources.host!
: sources.port!,
readFile
readFile,
env
);
} else {
let creds: Credentials;
Expand Down Expand Up @@ -848,13 +857,14 @@ async function parseDSNIntoConfig(
_dsnString: string,
config: ResolvedConnectConfig,
source: string,
readFile: (fn: string) => Promise<string>
readFile: (fn: string) => Promise<string>,
env: Record<string, string | undefined>
): Promise<void> {
// URL api does not support ipv6 zone ids, so extract zone id before parsing
// https://url.spec.whatwg.org/#host-representation
let dsnString = _dsnString;
let regexHostname: string | null = null;
let zoneId: string = "";
let zoneId = "";
const regexResult = /\[(.*?)(%25.+?)\]/.exec(_dsnString);
if (regexResult) {
regexHostname = regexResult[1];
Expand Down Expand Up @@ -920,15 +930,15 @@ async function parseDSNIntoConfig(
let param = value || (searchParams.get(paramName) ?? null);
let paramSource = source;
if (param === null) {
const env = searchParams.get(`${paramName}_env`);
if (env != null) {
param = getEnv(env, true) ?? null;
const key = searchParams.get(`${paramName}_env`);
if (key != null) {
param = env[key] ?? null;
if (param === null) {
throw new InterfaceError(
`'${paramName}_env' environment variable '${env}' doesn't exist`
`'${paramName}_env' environment variable '${key}' doesn't exist`
);
}
paramSource += ` (${paramName}_env: ${env})`;
paramSource += ` (${paramName}_env: ${key})`;
}
}
if (param === null) {
Expand Down
6 changes: 4 additions & 2 deletions packages/driver/src/nodeClient.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { BaseClientPool, Client, ConnectOptions } from "./baseClient";
import { parseConnectArguments } from "./conUtils.server";
import { BaseClientPool, Client, type ConnectOptions } from "./baseClient";
import { makeConnectArgumentsParser } from "./conUtils.server";
import cryptoUtils from "./adapter.crypto.node";
import { Options } from "./options";
import { RawConnection } from "./rawConn";
import { FetchConnection } from "./fetchConn";
import { getHTTPSCRAMAuth } from "./httpScram";

const parseConnectArguments = makeConnectArgumentsParser();

class ClientPool extends BaseClientPool {
isStateless = false;
_connectWithTimeout = RawConnection.connectWithTimeout.bind(RawConnection);
Expand Down
11 changes: 7 additions & 4 deletions packages/driver/src/primitives/buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,19 @@ let decodeB64: (b64: string) => Uint8Array;
let encodeB64: (data: Uint8Array) => string;

// @ts-ignore: Buffer is not defined in Deno
if (Buffer === "function") {
globalThis.Buffer = globalThis.Buffer || undefined;

// @ts-ignore: Buffer is not defined in Deno
if (globalThis.Buffer && globalThis.Buffer === "function") {
decodeB64 = (b64: string): Uint8Array => {
// @ts-ignore: Buffer is not defined in Deno
return Buffer.from(b64, "base64");
return globalThis.Buffer.from(b64, "base64");
};
encodeB64 = (data: Uint8Array): string => {
// @ts-ignore: Buffer is not defined in Deno
const buf = !Buffer.isBuffer(data)
const buf = !globalThis.Buffer.isBuffer(data)
? // @ts-ignore: Buffer is not defined in Deno
Buffer.from(data.buffer, data.byteOffset, data.byteLength)
globalThis.Buffer.from(data.buffer, data.byteOffset, data.byteLength)
: data;
return buf.toString("base64");
};
Expand Down

0 comments on commit 5c152c5

Please sign in to comment.