Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Cloudflare Workers #867

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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