From fd0821b2e7f34524db435168aee067dc78126911 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Thu, 2 Jun 2022 23:06:30 +0200 Subject: [PATCH 01/33] remove 3.4.1 fallback from data proxy client --- .../src/data-proxy/utils/getClientVersion.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts index 66e022a51c78..5b5362f2ccb7 100644 --- a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts +++ b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts @@ -1,4 +1,7 @@ import type { EngineConfig } from '../../common/Engine' +import { NotImplementedYetError } from '../errors/NotImplementedYetError' + +const semverRegex = /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/ /** * Determine the client version to be sent to the DataProxy @@ -6,14 +9,26 @@ import type { EngineConfig } from '../../common/Engine' * @returns */ export function getClientVersion(config: EngineConfig) { + // internal override for testing and manual version overrides + if (process.env.PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION) { + return process.env.PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION + } + const [version, suffix] = config.clientVersion?.split('-') ?? [] // we expect the version to match the pattern major.minor.patch - if (!suffix && /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/.test(version)) { + if (suffix === undefined && semverRegex.test(version)) { return version } - // TODO: we should have a Data Proxy deployment which accepts any - // arbitrary version for testing purposes. - return '3.4.1' // and we default it to the latest stable + // then it must be an integration version, so we use its parent + if (suffix === 'integration' && semverRegex.test(version)) { + const [major, minor] = version.split('.') + return `${major}.${parseInt(minor) - 1}.${0}` + } + + // nothing matched, meaning that the provided version is invalid + throw new NotImplementedYetError('Only `major.minor.patch` versions are supported by Prisma Data Proxy.', { + clientVersion: config.clientVersion ?? 'undefined', + }) } From 5aadcaf52c3832ad52d7f4749edc698217aa8db5 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Thu, 2 Jun 2022 23:49:40 +0200 Subject: [PATCH 02/33] only create an edge directory if dataProxy is true --- packages/client/src/generation/generateClient.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/client/src/generation/generateClient.ts b/packages/client/src/generation/generateClient.ts index 80ae62ef06a0..3167e799c18b 100644 --- a/packages/client/src/generation/generateClient.ts +++ b/packages/client/src/generation/generateClient.ts @@ -222,8 +222,12 @@ export async function generateClient(options: GenerateClientOptions): Promise Date: Fri, 3 Jun 2022 02:13:19 +0200 Subject: [PATCH 03/33] infer the data proxy version from the engine version --- .../src/data-proxy/DataProxyEngine.ts | 12 +++---- .../src/data-proxy/utils/getClientVersion.ts | 31 ++++++++++++++++--- tsconfig.build.regular.json | 3 +- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/packages/engine-core/src/data-proxy/DataProxyEngine.ts b/packages/engine-core/src/data-proxy/DataProxyEngine.ts index 44424a500a42..c088e8fb4805 100644 --- a/packages/engine-core/src/data-proxy/DataProxyEngine.ts +++ b/packages/engine-core/src/data-proxy/DataProxyEngine.ts @@ -26,7 +26,7 @@ export class DataProxyEngine extends Engine { private env: { [k: string]: string } private clientVersion: string - private remoteClientVersion: string + private remoteClientVersion: string | Promise private headers: { Authorization: string } private host: string @@ -68,8 +68,8 @@ export class DataProxyEngine extends Engine { } } - private url(s: string) { - return `https://${this.host}/${this.remoteClientVersion}/${this.inlineSchemaHash}/${s}` + private async url(s: string) { + return `https://${this.host}/${await this.remoteClientVersion}/${this.inlineSchemaHash}/${s}` } // TODO: looks like activeProvider is the only thing @@ -85,7 +85,7 @@ export class DataProxyEngine extends Engine { } private async uploadSchema() { - const response = await request(this.url('schema'), { + const response = await request(await this.url('schema'), { method: 'PUT', headers: this.headers, body: this.inlineSchema, @@ -128,10 +128,10 @@ export class DataProxyEngine extends Engine { private async requestInternal(body: Record, headers: Record, attempt: number) { try { this.logEmitter.emit('info', { - message: `Calling ${this.url('graphql')} (n=${attempt})`, + message: `Calling ${await this.url('graphql')} (n=${attempt})`, }) - const response = await request(this.url('graphql'), { + const response = await request(await this.url('graphql'), { method: 'POST', headers: { ...headers, ...this.headers }, body: JSON.stringify(body), diff --git a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts index 5b5362f2ccb7..6b68ffff18f5 100644 --- a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts +++ b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts @@ -1,7 +1,11 @@ +import { version as engineVersion } from '@prisma/engines/package.json' + import type { EngineConfig } from '../../common/Engine' import { NotImplementedYetError } from '../errors/NotImplementedYetError' +import { request } from './request' const semverRegex = /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/ +const prismaNpm = 'https://registry.npmjs.org/prisma' /** * Determine the client version to be sent to the DataProxy @@ -9,26 +13,43 @@ const semverRegex = /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/ * @returns */ export function getClientVersion(config: EngineConfig) { + const clientVersion = config.clientVersion ?? 'unknown' + // internal override for testing and manual version overrides if (process.env.PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION) { return process.env.PRISMA_CLIENT_DATA_PROXY_CLIENT_VERSION } - const [version, suffix] = config.clientVersion?.split('-') ?? [] + const [version, suffix] = clientVersion?.split('-') ?? [] // we expect the version to match the pattern major.minor.patch if (suffix === undefined && semverRegex.test(version)) { return version } - // then it must be an integration version, so we use its parent + // then it's an integration version, we resolve its data proxy if (suffix === 'integration' && semverRegex.test(version)) { - const [major, minor] = version.split('.') - return `${major}.${parseInt(minor) - 1}.${0}` + return (async () => { + // we infer the data proxy version from the engine version + const [version] = engineVersion.split('-') ?? [] + const [major, minor, patch] = version.split('.') + + // if a patch has happened, then we return that version + if (patch !== '0') return `${major}.${minor}.${patch}` + + // if not, we know that the minor must be minus with 1 + const published = `${major}.${parseInt(minor) - 1}.x` + + // we don't know what `x` is, so we query the registry + const resolvedPublishedVersion = `${prismaNpm}/${published}` + const data = (await request(resolvedPublishedVersion, { clientVersion })).json() + + return ((await data)['version'] as string) ?? 'undefined' + })() // <-- this is just creating a promise via an iife } // nothing matched, meaning that the provided version is invalid throw new NotImplementedYetError('Only `major.minor.patch` versions are supported by Prisma Data Proxy.', { - clientVersion: config.clientVersion ?? 'undefined', + clientVersion, }) } diff --git a/tsconfig.build.regular.json b/tsconfig.build.regular.json index 51a02abe19a2..6dfc2740656a 100644 --- a/tsconfig.build.regular.json +++ b/tsconfig.build.regular.json @@ -17,7 +17,8 @@ "skipDefaultLibCheck": true, "skipLibCheck": true, - "emitDeclarationOnly": true + "emitDeclarationOnly": true, + "resolveJsonModule": true }, "exclude": ["**/dist", "**/node_modules", "**/src/__tests__"] } From 089304c37cdd978a7180fdc7767b42a00d4576bd Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 13:36:30 +0200 Subject: [PATCH 04/33] apply review --- .../engine-core/src/data-proxy/utils/getClientVersion.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts index 6b68ffff18f5..b43f0cd3ebd6 100644 --- a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts +++ b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts @@ -27,7 +27,7 @@ export function getClientVersion(config: EngineConfig) { return version } - // then it's an integration version, we resolve its data proxy + // if it's an integration version, we resolve its data proxy if (suffix === 'integration' && semverRegex.test(version)) { return (async () => { // we infer the data proxy version from the engine version @@ -41,10 +41,9 @@ export function getClientVersion(config: EngineConfig) { const published = `${major}.${parseInt(minor) - 1}.x` // we don't know what `x` is, so we query the registry - const resolvedPublishedVersion = `${prismaNpm}/${published}` - const data = (await request(resolvedPublishedVersion, { clientVersion })).json() + const res = await request(`${prismaNpm}/${published}`, { clientVersion }) - return ((await data)['version'] as string) ?? 'undefined' + return ((await res.json())['version'] as string) ?? 'undefined' })() // <-- this is just creating a promise via an iife } From dceabd099c8a3a71d0964f2d413c2f392dc77644 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 15:46:39 +0200 Subject: [PATCH 05/33] restore fix for umd lz-string via esbuild injection --- packages/client/helpers/build.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/client/helpers/build.ts b/packages/client/helpers/build.ts index f8939797b162..2d08302485a4 100644 --- a/packages/client/helpers/build.ts +++ b/packages/client/helpers/build.ts @@ -13,6 +13,8 @@ const nodeRuntimeBuildConfig: BuildOptions = { bundle: true, define: { NODE_CLIENT: 'true', + // that fixes an issue with lz-string umd builds + 'define.amd': 'false', }, } @@ -36,6 +38,8 @@ const edgeRuntimeBuildConfig: BuildOptions = { define: { // that helps us to tree-shake unused things out NODE_CLIENT: 'false', + // that fixes an issue with lz-string umd builds + 'define.amd': 'false', }, plugins: [ fillPlugin({ From 4d0b7412ab1065102221dcd1827bc98f7f1358f4 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 18:48:30 +0200 Subject: [PATCH 06/33] add missing data proxy url check to prisma init --- packages/cli/src/Init.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/Init.ts b/packages/cli/src/Init.ts index 2cad34973935..111876c53137 100644 --- a/packages/cli/src/Init.ts +++ b/packages/cli/src/Init.ts @@ -1,8 +1,9 @@ import type { ConnectorType } from '@prisma/generator-helper' -import type { Command } from '@prisma/sdk' import { arg, canConnectToDatabase, + checkUnsupportedDataProxy, + Command, format, getCommandWithExecutor, HelpError, @@ -134,6 +135,8 @@ export class Init implements Command { return this.help() } + await checkUnsupportedDataProxy('init', args, false) + /** * Validation */ From 752d757cf30c6e3e283818145df6f5e474c9775f Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 19:33:47 +0200 Subject: [PATCH 07/33] aggregate the ambient env to the loaded one --- packages/engine-core/src/common/Engine.ts | 2 +- packages/engine-core/src/data-proxy/DataProxyEngine.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/engine-core/src/common/Engine.ts b/packages/engine-core/src/common/Engine.ts index a802767668b5..50a74780cbbb 100644 --- a/packages/engine-core/src/common/Engine.ts +++ b/packages/engine-core/src/common/Engine.ts @@ -56,7 +56,7 @@ export interface EngineConfig { showColors?: boolean logQueries?: boolean logLevel?: 'info' | 'warn' - env?: Record + env: Record flags?: string[] clientVersion?: string previewFeatures?: string[] diff --git a/packages/engine-core/src/data-proxy/DataProxyEngine.ts b/packages/engine-core/src/data-proxy/DataProxyEngine.ts index bc39b7737ab1..81c9f03dc12c 100644 --- a/packages/engine-core/src/data-proxy/DataProxyEngine.ts +++ b/packages/engine-core/src/data-proxy/DataProxyEngine.ts @@ -24,7 +24,7 @@ export class DataProxyEngine extends Engine { private inlineDatasources: any private config: EngineConfig private logEmitter: EventEmitter - private env: { [k: string]: string } + private env: { [k in string]?: string } private clientVersion: string private remoteClientVersion: string | Promise @@ -35,7 +35,7 @@ export class DataProxyEngine extends Engine { super() this.config = config - this.env = this.config.env ?? {} + this.env = { ...process.env, ...this.config.env } this.inlineSchema = config.inlineSchema ?? '' this.inlineDatasources = config.inlineDatasources ?? {} this.inlineSchemaHash = config.inlineSchemaHash ?? '' From 5cf13748eacf5d46adc8c69c8ee20b46a21399e2 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 19:51:53 +0200 Subject: [PATCH 08/33] log the select data proxy client version in debug --- .../src/data-proxy/utils/getClientVersion.ts | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts index b43f0cd3ebd6..94f39e0dd664 100644 --- a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts +++ b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts @@ -1,3 +1,4 @@ +import Debug from '@prisma/debug' import { version as engineVersion } from '@prisma/engines/package.json' import type { EngineConfig } from '../../common/Engine' @@ -6,13 +7,9 @@ import { request } from './request' const semverRegex = /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/ const prismaNpm = 'https://registry.npmjs.org/prisma' +const debug = Debug('prisma:client:dataproxyEngine') -/** - * Determine the client version to be sent to the DataProxy - * @param config - * @returns - */ -export function getClientVersion(config: EngineConfig) { +function _getClientVersion(config: EngineConfig) { const clientVersion = config.clientVersion ?? 'unknown' // internal override for testing and manual version overrides @@ -28,7 +25,7 @@ export function getClientVersion(config: EngineConfig) { } // if it's an integration version, we resolve its data proxy - if (suffix === 'integration' && semverRegex.test(version)) { + if (suffix === 'integration' || clientVersion === '0.0.0') { return (async () => { // we infer the data proxy version from the engine version const [version] = engineVersion.split('-') ?? [] @@ -52,3 +49,16 @@ export function getClientVersion(config: EngineConfig) { clientVersion, }) } + +/** + * Determine the client version to be sent to the DataProxy + * @param config + * @returns + */ +export async function getClientVersion(config: EngineConfig) { + const version = await _getClientVersion(config) + + debug('version', version) + + return version +} From 21294c617815cf8af9859a61ea15d259e92294c0 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 20:11:42 +0200 Subject: [PATCH 09/33] make buildInjectableEdgeEnv more digestible --- .../utils/buildInjectableEdgeEnv.ts | 84 +++++++------------ 1 file changed, 32 insertions(+), 52 deletions(-) diff --git a/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts b/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts index 8beb0d1b1781..b69a0c183489 100644 --- a/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts +++ b/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts @@ -1,6 +1,6 @@ import type { InternalDatasource } from '../../runtime/utils/printDatasources' -type LoadedEnv = { +type InjectableEnv = { parsed: { [x: string]: string | undefined } @@ -9,8 +9,9 @@ type LoadedEnv = { /** * Builds an injectable environment for the data proxy edge client. It's useful * because it is designed to run in browser-like environments where things like - * `fs`, `process.env`, and .env file loading are not available. The injectable - * env is the default fallback when `tryLoadEnvs` wasn't called by the client. + * `fs`, `process.env`, and .env file loading are not available. It is the place + * where we make collect the env vars for the edge client. To understand this + * better, take a look at the generated code in the edge client. * @see {@link declareInjectableEdgeEnv} * @param edge * @param datasources @@ -18,77 +19,40 @@ type LoadedEnv = { */ export function buildInjectableEdgeEnv(edge: boolean, datasources: InternalDatasource[]) { if (edge === true) { - const envVarNames = getSelectedEnvVarNames(datasources) - const loadedEnv = getEmptyEnvObjectForVars(envVarNames) - - return declareInjectableEdgeEnv(loadedEnv) + return declareInjectableEdgeEnv(datasources) } return `` } /** - * Determines which env vars we are interested in in the case of the data proxy - * client. Right now, we are only interested in env vars from the `datasource`. + * Creates the necessary declarations to embed env vars directly inside of the + * generated client. We abuse a custom `JSON.stringify` to generate the code. * @param datasources to find env vars in - * @returns */ -function getSelectedEnvVarNames(datasources: InternalDatasource[]) { - return datasources.reduce((acc, datasource) => { - if (datasource.url.fromEnvVar) { - return [...acc, datasource.url.fromEnvVar] - } +function declareInjectableEdgeEnv(datasources: InternalDatasource[]) { + const envVarNames = getSelectedEnvVarNames(datasources) - return acc - }, [] as string[]) -} - -/** - * Like `tryLoadEnvs` but we only retain a subset of the env vars that will be - * used in the case of the data proxy client. And we discard the `message` prop. - * @param envVarNames to be selected from the load - * @returns - */ -function getEmptyEnvObjectForVars(envVarNames: string[]) { - const selectedEnv: LoadedEnv = { parsed: {} } + const injectableEdgeEnv: InjectableEnv = { parsed: {} } + // we create a base env with empty values for env names for (const envVarName of envVarNames) { - /** note that we willingly keep `undefined` values because - * we need that later in {@link declareInjectableEdgeEnv} **/ - selectedEnv.parsed[envVarName] = undefined + injectableEdgeEnv.parsed[envVarName] = undefined } - return selectedEnv -} - -/** - * Creates the necessary declarations to embed env vars directly inside of the - * generated client. We abuse a custom `JSON.stringify` replacer to transform - * {@link loadedEnv} into a piece of code. The goal of this is to take that and - * generate a new object which re-prioritizes the loading of the environment. - * - * By generating an output with `${key} || process.env.${key} || '${value}'` it - * would yield something like `DB_URL || process.env.DB_URL || undefined`: - * - For Cloudflare Workers `DB_URL` will be the first to be found (runtime) - * - `process.env.DB_URL` will be undefined - * - For Vercel `process.env.DB_URL` is replaced by webpack by a value - * - `DB_URL` will be undefined at anytime - * - If none of them were provided, we fallback to the default `undefined` - * @param loadedEnv - */ -function declareInjectableEdgeEnv(loadedEnv: LoadedEnv) { // abuse a custom replacer to create the injectable env const injectableEdgeEnvDeclaration = JSON.stringify( - loadedEnv, + injectableEdgeEnv, (key, value) => { if (key === '') return value if (key === 'parsed') return value + // for cloudflare workers, an env var is a global js variable const cfwEnv = `typeof global !== 'undefined' && global['${key}']` + // for vercel edge functions, it's injected statically at build const vercelEnv = `process.env['${key}']` - const dotEnv = value ? `'${value}'` : 'undefined' - return `${cfwEnv} || ${vercelEnv} || ${dotEnv}` + return `${cfwEnv} || ${vercelEnv} || undefined` }, 2, ).replace(/"/g, '') // remove quotes to make code @@ -96,3 +60,19 @@ function declareInjectableEdgeEnv(loadedEnv: LoadedEnv) { return ` config.injectableEdgeEnv = ${injectableEdgeEnvDeclaration}` } + +/** + * Determines which env vars we are interested in in the case of the data proxy + * client. Right now, we are only interested in env vars from the `datasource`. + * @param datasources to find env vars in + * @returns + */ +function getSelectedEnvVarNames(datasources: InternalDatasource[]) { + return datasources.reduce((acc, datasource) => { + if (datasource.url.fromEnvVar) { + return [...acc, datasource.url.fromEnvVar] + } + + return acc + }, [] as string[]) +} From b951a42a8cb938e0a19cf7de56bf8d0d389b7062 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 20:45:27 +0200 Subject: [PATCH 10/33] include "edge" in the publish of the package --- packages/client/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/client/package.json b/packages/client/package.json index 78dc54bc16bf..fd8307f30cb9 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -56,6 +56,7 @@ "runtime", "scripts", "generator-build", + "edge", "index.js", "index.d.ts", "index-browser.js" From e94e596f759c52af0f282ce0b3943ed398917354 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 20:47:41 +0200 Subject: [PATCH 11/33] update comment about default throw files --- packages/client/scripts/postinstall.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/client/scripts/postinstall.js b/packages/client/scripts/postinstall.js index 9155eeee0181..d5de17d54d34 100644 --- a/packages/client/scripts/postinstall.js +++ b/packages/client/scripts/postinstall.js @@ -197,7 +197,9 @@ function run(cmd, params, cwd = process.cwd()) { /** * Copies our default "throw" files into the default generation folder. These * files are dummy and informative because they just throw an error to let the - * user know that they have forgotten to run `prisma generate`. + * user know that they have forgotten to run `prisma generate` or that they + * don't have a a schema file yet. We only add these files at the default + * location `node_modules/.prisma/client`. */ async function createDefaultGeneratedThrowFiles() { try { From 1f33f60cdc035e675a14868044f0962493afdc2a Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 21:08:21 +0200 Subject: [PATCH 12/33] check that metrics cannot be used with --data-proxy --- .../check-feature-flags/checkFeatureFlags.ts | 20 ++++++++++--------- .../forbiddenItxWithProxyFlagMessage.ts | 9 ++++----- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/packages/sdk/src/get-generators/utils/check-feature-flags/checkFeatureFlags.ts b/packages/sdk/src/get-generators/utils/check-feature-flags/checkFeatureFlags.ts index d33a2bd316ef..dfcf32fb042c 100644 --- a/packages/sdk/src/get-generators/utils/check-feature-flags/checkFeatureFlags.ts +++ b/packages/sdk/src/get-generators/utils/check-feature-flags/checkFeatureFlags.ts @@ -1,6 +1,6 @@ import type { ConfigMetaFormat } from '../../../engine-commands' import { GetGeneratorOptions } from '../../getGenerators' -import { forbiddenItxWithDataProxyFlagMessage } from './forbiddenItxWithProxyFlagMessage' +import { forbiddenPreviewFeatureWithDataProxyFlagMessage } from './forbiddenItxWithProxyFlagMessage' /** * Check feature flags and preview features @@ -12,16 +12,18 @@ export function checkFeatureFlags(config: ConfigMetaFormat, options: GetGenerato } function checkForbiddenItxWithDataProxyFlag(config: ConfigMetaFormat, options: GetGeneratorOptions) { - if ( - options.dataProxy === true && + options.dataProxy === true && config.generators.some((generatorConfig) => { - return generatorConfig.previewFeatures.some( - (feature) => feature.toLocaleLowerCase() === 'interactiveTransactions'.toLocaleLowerCase(), - ) + return generatorConfig.previewFeatures.some((feature) => { + if (feature.toLocaleLowerCase() === 'metrics'.toLocaleLowerCase()) { + throw new Error(forbiddenPreviewFeatureWithDataProxyFlagMessage('metrics')) + } + + if (feature.toLocaleLowerCase() === 'interactiveTransactions'.toLocaleLowerCase()) { + throw new Error(forbiddenPreviewFeatureWithDataProxyFlagMessage('interactiveTransactions')) + } + }) }) - ) { - throw new Error(forbiddenItxWithDataProxyFlagMessage) - } } /* Example diff --git a/packages/sdk/src/get-generators/utils/check-feature-flags/forbiddenItxWithProxyFlagMessage.ts b/packages/sdk/src/get-generators/utils/check-feature-flags/forbiddenItxWithProxyFlagMessage.ts index 8f889081b805..f006c3fc5e06 100644 --- a/packages/sdk/src/get-generators/utils/check-feature-flags/forbiddenItxWithProxyFlagMessage.ts +++ b/packages/sdk/src/get-generators/utils/check-feature-flags/forbiddenItxWithProxyFlagMessage.ts @@ -2,10 +2,9 @@ import chalk from 'chalk' import { link } from '../../../utils/link' -export const forbiddenItxWithDataProxyFlagMessage = ` -${chalk.green('interactiveTransactions')} preview feature is not yet available with ${chalk.green('--data-proxy')}. -Please remove ${chalk.red('interactiveTransactions')} from the ${chalk.green('previewFeatures')} in your schema. +export const forbiddenPreviewFeatureWithDataProxyFlagMessage = (previewFeatureName: string) => ` +${chalk.green(previewFeatureName)} preview feature is not yet available with ${chalk.green('--data-proxy')}. +Please remove ${chalk.red(previewFeatureName)} from the ${chalk.green('previewFeatures')} in your schema. -More information in our documentation: -${link('https://pris.ly/d/data-proxy')} +More information about Data Proxy: ${link('https://pris.ly/d/data-proxy')} ` From 227da0a748759982ebaa6952b6c4ecab58ff660f Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 21:10:14 +0200 Subject: [PATCH 13/33] throw error instead of exiting the process (for testing) --- .../sdk/src/cli/checkUnsupportedDataProxy.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/sdk/src/cli/checkUnsupportedDataProxy.ts b/packages/sdk/src/cli/checkUnsupportedDataProxy.ts index e609696a8522..c01616ff75f1 100644 --- a/packages/sdk/src/cli/checkUnsupportedDataProxy.ts +++ b/packages/sdk/src/cli/checkUnsupportedDataProxy.ts @@ -43,7 +43,7 @@ More information about Data Proxy: ${link('https://pris.ly/d/data-proxy-cli')} * @param args the cli command arguments * @param implicitSchema if this command implicitly loads a schema */ -async function _checkUnsupportedDataProxy(command: string, args: Args, implicitSchema: boolean) { +async function checkUnsupportedDataProxyMessage(command: string, args: Args, implicitSchema: boolean) { // when the schema can be implicit, we use its default location if (implicitSchema === true) { args['--schema'] = (await getSchemaPath(args['--schema'])) ?? undefined @@ -53,8 +53,7 @@ async function _checkUnsupportedDataProxy(command: string, args: Args, implicitS for (const [argName, argValue] of argList) { // for all the args that represent an url ensure data proxy isn't used if (argName.includes('url') && argValue.includes('prisma://')) { - console.error(forbiddenCmdWithDataProxyFlagMessage(command)) - process.exit(1) + return forbiddenCmdWithDataProxyFlagMessage(command) } // for all the args that represent a schema path ensure data proxy isn't used @@ -68,15 +67,16 @@ async function _checkUnsupportedDataProxy(command: string, args: Args, implicitS const urlEnvVarValue = urlEnvVarName ? process.env[urlEnvVarName] : undefined if ((urlFromValue ?? urlEnvVarValue)?.startsWith('prisma://')) { - console.error(forbiddenCmdWithDataProxyFlagMessage(command)) - process.exit(1) + return forbiddenCmdWithDataProxyFlagMessage(command) } } } + + return undefined } export async function checkUnsupportedDataProxy(command: string, args: Args, implicitSchema: boolean) { - try { - await _checkUnsupportedDataProxy(command, args, implicitSchema) - } catch {} + const message = await checkUnsupportedDataProxyMessage(command, args, implicitSchema).catch(() => undefined) + + if (message) throw new Error(message) } From dfc6fb79ec4a57565bcbec7ee0a074af51de5f5c Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 23:06:58 +0200 Subject: [PATCH 14/33] fix the default import location via module not the path --- packages/client/edge/index.d.ts | 2 +- packages/client/edge/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/edge/index.d.ts b/packages/client/edge/index.d.ts index e1051b2162f3..483980844cea 100644 --- a/packages/client/edge/index.d.ts +++ b/packages/client/edge/index.d.ts @@ -1 +1 @@ -export * from '../.prisma/client' +export * from '.prisma/client' diff --git a/packages/client/edge/index.js b/packages/client/edge/index.js index ffb4b3b9cd94..8d790165589b 100644 --- a/packages/client/edge/index.js +++ b/packages/client/edge/index.js @@ -1,3 +1,3 @@ module.exports = { - ...require('../.prisma/client/edge'), + ...require('.prisma/client/edge'), } From cfff964f159a0fa2f0a2a04986311a41f33ccd15 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 23:21:41 +0200 Subject: [PATCH 15/33] remove unnecessary logic for building NTF annotations --- .../generation/utils/buildNFTAnnotations.ts | 34 ++----------------- 1 file changed, 3 insertions(+), 31 deletions(-) diff --git a/packages/client/src/generation/utils/buildNFTAnnotations.ts b/packages/client/src/generation/utils/buildNFTAnnotations.ts index 94423154eb7f..26e1cde24672 100644 --- a/packages/client/src/generation/utils/buildNFTAnnotations.ts +++ b/packages/client/src/generation/utils/buildNFTAnnotations.ts @@ -35,10 +35,11 @@ export function buildNFTAnnotations( } const engineAnnotations = map(platforms, (platform) => { - return buildNFTEngineAnnotation(engineType, platform, relativeOutdir) + const engineFilename = getQueryEngineFilename(engineType, platform) + return engineFilename ? buildNFTAnnotation(engineFilename, relativeOutdir) : '' }).join('\n') - const schemaAnnotations = buildNFTSchemaAnnotation(engineType, relativeOutdir) + const schemaAnnotations = buildNFTAnnotation('schema.prisma', relativeOutdir) return `${engineAnnotations}${schemaAnnotations}` } @@ -76,32 +77,3 @@ function buildNFTAnnotation(fileName: string, relativeOutdir: string) { path.join(__dirname, ${JSON.stringify(fileName)}); path.join(process.cwd(), ${JSON.stringify(relativeFilePath)})` } - -/** - * Build an annotation for the prisma client engine files - * @param engineType - * @param platform - * @param relativeOutdir - * @returns - */ -function buildNFTEngineAnnotation(engineType: ClientEngineType, platform: Platform, relativeOutdir: string) { - const engineFilename = getQueryEngineFilename(engineType, platform) - - if (engineFilename === undefined) return '' - - return buildNFTAnnotation(engineFilename, relativeOutdir) -} - -/** - * Build an annotation for the prisma schema files - * @param engineType - * @param relativeOutdir - * @returns - */ -function buildNFTSchemaAnnotation(engineType: ClientEngineType, relativeOutdir: string) { - if (engineType === ClientEngineType.Library || engineType === ClientEngineType.Binary) { - return buildNFTAnnotation('schema.prisma', relativeOutdir) - } - - return '' -} From 3476eb9cdf82c1afae7eaecd06a51ea6b2f65852 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 23:30:45 +0200 Subject: [PATCH 16/33] rename network to request error and append info --- .../engine-core/src/data-proxy/errors/NetworkError.ts | 10 +++++----- packages/engine-core/src/data-proxy/utils/request.ts | 6 ++++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/packages/engine-core/src/data-proxy/errors/NetworkError.ts b/packages/engine-core/src/data-proxy/errors/NetworkError.ts index 8113d347b1d4..540b90102cfc 100644 --- a/packages/engine-core/src/data-proxy/errors/NetworkError.ts +++ b/packages/engine-core/src/data-proxy/errors/NetworkError.ts @@ -2,13 +2,13 @@ import type { DataProxyErrorInfo } from './DataProxyError' import { DataProxyError } from './DataProxyError' import { setRetryable } from './utils/setRetryable' -export interface NetworkErrorInfo extends DataProxyErrorInfo {} +export interface RequestErrorInfo extends DataProxyErrorInfo {} -export class NetworkError extends DataProxyError { - public name = 'NetworkError' +export class RequestError extends DataProxyError { + public name = 'RequestError' public code = 'P5010' - constructor(info: NetworkErrorInfo) { - super('Cannot fetch data from service', setRetryable(info, true)) + constructor(message: string, info: RequestErrorInfo) { + super(`Cannot fetch data from service:\n${message}`, setRetryable(info, true)) } } diff --git a/packages/engine-core/src/data-proxy/utils/request.ts b/packages/engine-core/src/data-proxy/utils/request.ts index 856a157b488c..9fbf7f5131a8 100644 --- a/packages/engine-core/src/data-proxy/utils/request.ts +++ b/packages/engine-core/src/data-proxy/utils/request.ts @@ -3,7 +3,7 @@ import type Https from 'https' import type { RequestInit, Response } from 'node-fetch' import type { O } from 'ts-toolbelt' -import { NetworkError } from '../errors/NetworkError' +import { RequestError } from '../errors/NetworkError' import { getJSRuntimeName } from './getJSRuntimeName' // our implementation handles less @@ -23,6 +23,7 @@ export async function request( url: string, options: RequestOptions & { clientVersion: string }, ): Promise { + const clientVersion = options.clientVersion const jsRuntimeName = getJSRuntimeName() try { @@ -32,7 +33,8 @@ export async function request( return await nodeFetch(url, options) } } catch (e) { - throw new NetworkError({ clientVersion: options.clientVersion }) + const message = e.message ?? 'Unknown error' + throw new RequestError(message, { clientVersion }) } } From 779a3db8bb85cfb5fc6533d114dc481f7f4f45fc Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Fri, 3 Jun 2022 23:40:22 +0200 Subject: [PATCH 17/33] update snapshots --- packages/sdk/src/__tests__/getGenerators/getGenerators.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/sdk/src/__tests__/getGenerators/getGenerators.test.ts b/packages/sdk/src/__tests__/getGenerators/getGenerators.test.ts index b5a96d8172c1..09239dda67d2 100644 --- a/packages/sdk/src/__tests__/getGenerators/getGenerators.test.ts +++ b/packages/sdk/src/__tests__/getGenerators/getGenerators.test.ts @@ -781,8 +781,7 @@ describe('getGenerators', () => { interactiveTransactions preview feature is not yet available with --data-proxy. Please remove interactiveTransactions from the previewFeatures in your schema. - More information in our documentation: - https://pris.ly/d/data-proxy + More information about Data Proxy: https://pris.ly/d/data-proxy " `) } From 1de120d6a9339c084e84f0f9e0c3676dfa0e093a Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 00:18:42 +0200 Subject: [PATCH 18/33] make the prisma client edge point to browser field --- packages/client/src/generation/generateClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/generation/generateClient.ts b/packages/client/src/generation/generateClient.ts index 3167e799c18b..ceb2d0079011 100644 --- a/packages/client/src/generation/generateClient.ts +++ b/packages/client/src/generation/generateClient.ts @@ -129,7 +129,7 @@ export async function buildClient({ name: '.prisma/client/edge', main: 'index.js', types: '../index.d.ts', - browser: '../index-browser.js', + browser: 'index.js', }, null, 2, From f4eac3677a01753a450a9004d6398629dde8f707 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 01:16:29 +0200 Subject: [PATCH 19/33] obfuscate require in a way that does not break jest --- packages/engine-core/src/data-proxy/utils/request.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/engine-core/src/data-proxy/utils/request.ts b/packages/engine-core/src/data-proxy/utils/request.ts index 9fbf7f5131a8..53d432d90b85 100644 --- a/packages/engine-core/src/data-proxy/utils/request.ts +++ b/packages/engine-core/src/data-proxy/utils/request.ts @@ -86,7 +86,7 @@ function buildResponse(incomingData: Buffer[], response: IncomingMessage): Reque * @returns */ async function nodeFetch(url: string, options: RequestOptions = {}): Promise { - const https: typeof Https = await globalThis[['e', 'v', 'a', 'l'].join('')](`import('https')`) + const https: typeof Https = include('https') const httpsOptions = buildOptions(options) const incomingData = [] as Buffer[] @@ -103,3 +103,6 @@ async function nodeFetch(url: string, options: RequestOptions = {}): Promise {} From b87e2405438f85fb9b7d4b431128bbe3ba151eb4 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 01:33:11 +0200 Subject: [PATCH 20/33] only assign to globalThis if it actually exists --- packages/client/src/runtime/getPrismaClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/runtime/getPrismaClient.ts b/packages/client/src/runtime/getPrismaClient.ts index 941bf53c1ca3..82ee214a7cd8 100644 --- a/packages/client/src/runtime/getPrismaClient.ts +++ b/packages/client/src/runtime/getPrismaClient.ts @@ -47,7 +47,7 @@ declare global { } // used by esbuild for tree-shaking -globalThis.NODE_CLIENT = true +typeof globalThis === 'object' ? (globalThis.NODE_CLIENT = true) : 0 function isReadonlyArray(arg: any): arg is ReadonlyArray { return Array.isArray(arg) From 961b7f6bd532f04a5a9b048303f8ea8e158e0044 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 02:33:19 +0200 Subject: [PATCH 21/33] fix env priority, env var injection, and constructor promises --- .../client/src/generation/utils/buildInjectableEdgeEnv.ts | 2 +- packages/engine-core/src/data-proxy/DataProxyEngine.ts | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts b/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts index b69a0c183489..8bebfb9ee932 100644 --- a/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts +++ b/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts @@ -50,7 +50,7 @@ function declareInjectableEdgeEnv(datasources: InternalDatasource[]) { // for cloudflare workers, an env var is a global js variable const cfwEnv = `typeof global !== 'undefined' && global['${key}']` // for vercel edge functions, it's injected statically at build - const vercelEnv = `process.env['${key}']` + const vercelEnv = `process.env.${key}` return `${cfwEnv} || ${vercelEnv} || undefined` }, diff --git a/packages/engine-core/src/data-proxy/DataProxyEngine.ts b/packages/engine-core/src/data-proxy/DataProxyEngine.ts index 81c9f03dc12c..a10eff963bad 100644 --- a/packages/engine-core/src/data-proxy/DataProxyEngine.ts +++ b/packages/engine-core/src/data-proxy/DataProxyEngine.ts @@ -18,6 +18,9 @@ import { request } from './utils/request' const MAX_RETRIES = 10 +// to defer the execution of promises in the constructor +const P = Promise.resolve() + export class DataProxyEngine extends Engine { private inlineSchema: string private inlineSchemaHash: string @@ -35,7 +38,7 @@ export class DataProxyEngine extends Engine { super() this.config = config - this.env = { ...process.env, ...this.config.env } + this.env = { ...this.config.env, ...process.env } this.inlineSchema = config.inlineSchema ?? '' this.inlineDatasources = config.inlineDatasources ?? {} this.inlineSchemaHash = config.inlineSchemaHash ?? '' @@ -45,7 +48,7 @@ export class DataProxyEngine extends Engine { this.logEmitter.on('error', () => {}) const [host, apiKey] = this.extractHostAndApiKey() - this.remoteClientVersion = getClientVersion(this.config) + this.remoteClientVersion = P.then(() => getClientVersion(this.config)) this.headers = { Authorization: `Bearer ${apiKey}` } this.host = host } From 4a4d8efd3943b9e488e1323208af54053a0c0b53 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 15:08:34 +0200 Subject: [PATCH 22/33] do not create edge folder but just the file --- packages/client/{edge/index.d.ts => edge.d.ts} | 0 packages/client/{edge/index.js => edge.js} | 0 packages/client/package.json | 3 ++- packages/client/scripts/postinstall.js | 7 ++----- .../client/src/generation/generateClient.ts | 18 ++---------------- 5 files changed, 6 insertions(+), 22 deletions(-) rename packages/client/{edge/index.d.ts => edge.d.ts} (100%) rename packages/client/{edge/index.js => edge.js} (100%) diff --git a/packages/client/edge/index.d.ts b/packages/client/edge.d.ts similarity index 100% rename from packages/client/edge/index.d.ts rename to packages/client/edge.d.ts diff --git a/packages/client/edge/index.js b/packages/client/edge.js similarity index 100% rename from packages/client/edge/index.js rename to packages/client/edge.js diff --git a/packages/client/package.json b/packages/client/package.json index fd8307f30cb9..d16132a44561 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -56,7 +56,8 @@ "runtime", "scripts", "generator-build", - "edge", + "index.js", + "edge.d.ts", "index.js", "index.d.ts", "index-browser.js" diff --git a/packages/client/scripts/postinstall.js b/packages/client/scripts/postinstall.js index d5de17d54d34..1f903cb97eb2 100644 --- a/packages/client/scripts/postinstall.js +++ b/packages/client/scripts/postinstall.js @@ -207,13 +207,10 @@ async function createDefaultGeneratedThrowFiles() { const defaultNodeIndexPath = path.join(dotPrismaClientDir, 'index.js') const defaultNodeIndexDtsPath = path.join(dotPrismaClientDir, 'index.d.ts') const defaultBrowserIndexPath = path.join(dotPrismaClientDir, 'index-browser.js') + const defaultEdgeIndexPath = path.join(dotPrismaClientDir, 'edge.js') + const defaultEdgeIndexDtsPath = path.join(dotPrismaClientDir, 'edge.d.ts') await makeDir(dotPrismaClientDir) - const dotPrismaClientEdgeDir = path.join(dotPrismaClientDir, 'edge') - const defaultEdgeIndexPath = path.join(dotPrismaClientEdgeDir, 'index.js') - const defaultEdgeIndexDtsPath = path.join(dotPrismaClientEdgeDir, 'index.d.ts') - await makeDir(dotPrismaClientEdgeDir) - if (!fs.existsSync(defaultNodeIndexPath)) { await copyFile(path.join(__dirname, 'default-index.js'), defaultNodeIndexPath) } diff --git a/packages/client/src/generation/generateClient.ts b/packages/client/src/generation/generateClient.ts index ceb2d0079011..0863049a8bcd 100644 --- a/packages/client/src/generation/generateClient.ts +++ b/packages/client/src/generation/generateClient.ts @@ -123,17 +123,8 @@ export async function buildClient({ // we only generate the edge client if `--data-proxy` is passed if (dataProxy === true) { - fileMap[path.join('edge', 'index.js')] = await JS(edgeTsClient, true) - fileMap[path.join('edge', 'package.json')] = JSON.stringify( - { - name: '.prisma/client/edge', - main: 'index.js', - types: '../index.d.ts', - browser: 'index.js', - }, - null, - 2, - ) + fileMap[path.join('edge.js')] = await JS(edgeTsClient, true) + fileMap[path.join('edge.d.ts')] = await TS(edgeTsClient) } return { @@ -223,11 +214,6 @@ export async function generateClient(options: GenerateClientOptions): Promise Date: Sat, 4 Jun 2022 15:09:34 +0200 Subject: [PATCH 23/33] debug vercel edge functions via logging --- packages/engine-core/src/data-proxy/DataProxyEngine.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/engine-core/src/data-proxy/DataProxyEngine.ts b/packages/engine-core/src/data-proxy/DataProxyEngine.ts index a10eff963bad..6ccac63ff102 100644 --- a/packages/engine-core/src/data-proxy/DataProxyEngine.ts +++ b/packages/engine-core/src/data-proxy/DataProxyEngine.ts @@ -201,6 +201,13 @@ export class DataProxyEngine extends Engine { const loadedEnvURL = this.env[mainDatasourceEnv] const dataProxyURL = mainDatasourceURL ?? loadedEnvURL + console.log(`[${process.env.VERCEL_DATA_PROXY_URL}]`) + console.log(this.inlineDatasources) + console.log(mainDatasourceName) + console.log(this.env) + console.log(this.config.env) + console.log(process.env) + let url: URL try { url = new URL(dataProxyURL ?? '') From 317077651ae89dd2e31513286c3589f2def0fe28 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 15:19:11 +0200 Subject: [PATCH 24/33] fix wrong path to runtime after removal of edge folder --- packages/client/src/generation/generateClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/src/generation/generateClient.ts b/packages/client/src/generation/generateClient.ts index 0863049a8bcd..f0c2b53c46a7 100644 --- a/packages/client/src/generation/generateClient.ts +++ b/packages/client/src/generation/generateClient.ts @@ -425,7 +425,7 @@ async function getGenerationDirs({ testMode, runtimeDirs, generator, outputDir } const _runtimeDirs = { // if we have an override, we use it, but if not then use the defaults node: runtimeDirs?.node || (useDefaultOutdir ? '@prisma/client/runtime' : './runtime'), - edge: runtimeDirs?.edge || (useDefaultOutdir ? '@prisma/client/runtime' : '../runtime'), + edge: runtimeDirs?.edge || (useDefaultOutdir ? '@prisma/client/runtime' : './runtime'), } const finalOutputDir = useDefaultOutdir ? await getDefaultOutdir(outputDir) : outputDir From 2aaf6e066c71e772cff235e1b174968873707d22 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 16:09:32 +0200 Subject: [PATCH 25/33] include edge.js in the package.json --- packages/client/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/client/package.json b/packages/client/package.json index d16132a44561..31603d54143f 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -56,7 +56,7 @@ "runtime", "scripts", "generator-build", - "index.js", + "edge.js", "edge.d.ts", "index.js", "index.d.ts", From 347765ead6c9a4f00ed835ce15edcd0b1c0ef9a9 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sat, 4 Jun 2022 17:19:36 +0200 Subject: [PATCH 26/33] make esbuild discard all evals and new Function --- packages/client/helpers/build.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/client/helpers/build.ts b/packages/client/helpers/build.ts index 9fabb8ea148a..34f2b1ae6725 100644 --- a/packages/client/helpers/build.ts +++ b/packages/client/helpers/build.ts @@ -43,6 +43,10 @@ const edgeRuntimeBuildConfig: BuildOptions = { }, plugins: [ fillPlugin({ + // these would fail at runtime anyways + eval: { define: 'undefined' }, + Function: { define: 'undefined' }, + // TODO no tree shaking on wrapper pkgs '@prisma/get-platform': { contents: '' }, // removes un-needed code out of `chalk` From 3d2b34927564284f54ac8ac2cccf651f579cbc17 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Sun, 5 Jun 2022 00:04:05 +0200 Subject: [PATCH 27/33] properly remove and polyfill calls to Function --- .../compile/plugins/fill-plugin/fillers/function.ts | 3 +++ packages/client/helpers/build.ts | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 helpers/compile/plugins/fill-plugin/fillers/function.ts diff --git a/helpers/compile/plugins/fill-plugin/fillers/function.ts b/helpers/compile/plugins/fill-plugin/fillers/function.ts new file mode 100644 index 000000000000..8581d768bbee --- /dev/null +++ b/helpers/compile/plugins/fill-plugin/fillers/function.ts @@ -0,0 +1,3 @@ +export const fn = () => {} + +fn.prototype = fn diff --git a/packages/client/helpers/build.ts b/packages/client/helpers/build.ts index 34f2b1ae6725..67c303edf7c5 100644 --- a/packages/client/helpers/build.ts +++ b/packages/client/helpers/build.ts @@ -5,6 +5,9 @@ import type { BuildOptions } from '../../../helpers/compile/build' import { build } from '../../../helpers/compile/build' import { fillPlugin } from '../../../helpers/compile/plugins/fill-plugin/fillPlugin' +const fillPluginPath = path.join('..', '..', 'helpers', 'compile', 'plugins', 'fill-plugin') +const functionPolyfillPath = path.join(fillPluginPath, 'fillers', 'function.ts') + // we define the config for runtime const nodeRuntimeBuildConfig: BuildOptions = { name: 'runtime', @@ -43,9 +46,12 @@ const edgeRuntimeBuildConfig: BuildOptions = { }, plugins: [ fillPlugin({ - // these would fail at runtime anyways + // we remove eval and Function for vercel eval: { define: 'undefined' }, - Function: { define: 'undefined' }, + Function: { + define: 'fn', + inject: functionPolyfillPath, + }, // TODO no tree shaking on wrapper pkgs '@prisma/get-platform': { contents: '' }, From 091f80789856c066cc975ab92d9b4c7e9eb5cb90 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Mon, 6 Jun 2022 14:47:59 +0200 Subject: [PATCH 28/33] cleanup --- packages/engine-core/src/data-proxy/DataProxyEngine.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/packages/engine-core/src/data-proxy/DataProxyEngine.ts b/packages/engine-core/src/data-proxy/DataProxyEngine.ts index 6ccac63ff102..a10eff963bad 100644 --- a/packages/engine-core/src/data-proxy/DataProxyEngine.ts +++ b/packages/engine-core/src/data-proxy/DataProxyEngine.ts @@ -201,13 +201,6 @@ export class DataProxyEngine extends Engine { const loadedEnvURL = this.env[mainDatasourceEnv] const dataProxyURL = mainDatasourceURL ?? loadedEnvURL - console.log(`[${process.env.VERCEL_DATA_PROXY_URL}]`) - console.log(this.inlineDatasources) - console.log(mainDatasourceName) - console.log(this.env) - console.log(this.config.env) - console.log(process.env) - let url: URL try { url = new URL(dataProxyURL ?? '') From 736ce044769797b0962f17ae13bfb8e8da258c38 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Mon, 6 Jun 2022 14:49:02 +0200 Subject: [PATCH 29/33] fix engine mode display for generate --- packages/sdk/src/cli/getGeneratorSuccessMessage.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/sdk/src/cli/getGeneratorSuccessMessage.ts b/packages/sdk/src/cli/getGeneratorSuccessMessage.ts index b823c27991c5..023cc6dd2f72 100644 --- a/packages/sdk/src/cli/getGeneratorSuccessMessage.ts +++ b/packages/sdk/src/cli/getGeneratorSuccessMessage.ts @@ -23,7 +23,8 @@ function formatVersion(generator: Generator): string | undefined { if (generator.getProvider() === 'prisma-client-js') { const engineType = getClientEngineType(generator.config) - return version ? `${version} | ${engineType}` : engineType + const engineMode = generator.options?.dataProxy ? 'dataproxy' : engineType + return version ? `${version} | ${engineMode}` : engineMode } return version From 985c6ae8efc5d8632153b21a7dc82c1b5e72c0b8 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Mon, 6 Jun 2022 22:52:29 +0200 Subject: [PATCH 30/33] create a dts redirect for custom outdir --- packages/client/src/generation/TSClient/Generatable.ts | 6 +++--- packages/client/src/generation/TSClient/TSClient.ts | 5 ++++- packages/client/src/generation/generateClient.ts | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/packages/client/src/generation/TSClient/Generatable.ts b/packages/client/src/generation/TSClient/Generatable.ts index 20de764a94e2..6ab59925d4a5 100644 --- a/packages/client/src/generation/TSClient/Generatable.ts +++ b/packages/client/src/generation/TSClient/Generatable.ts @@ -1,6 +1,6 @@ export interface Generatable { toJS?(edge?: boolean): string | Promise - toTS(): string | Promise + toTS(edge?: boolean): string | Promise toBrowserJS?(): string | Promise toTSWithoutNamespace?(): string | Promise } @@ -13,6 +13,6 @@ export function BrowserJS(gen: Generatable): string | Promise { return gen.toBrowserJS?.() ?? '' } -export function TS(gen: Generatable): string | Promise { - return gen.toTS() +export function TS(gen: Generatable, edge = false): string | Promise { + return gen.toTS(edge) } diff --git a/packages/client/src/generation/TSClient/TSClient.ts b/packages/client/src/generation/TSClient/TSClient.ts index c6909a97dcb7..3678149511fe 100644 --- a/packages/client/src/generation/TSClient/TSClient.ts +++ b/packages/client/src/generation/TSClient/TSClient.ts @@ -133,7 +133,10 @@ ${buildNFTAnnotations(dataProxy, engineType, platforms, relativeOutdir)} ` return code } - public toTS(): string { + public toTS(edge = false): string { + // edge exports the same ts definitions as the index + if (edge === true) return `export * from './index'` + const prismaClientClass = new PrismaClientClass( this.dmmf, this.options.datasources, diff --git a/packages/client/src/generation/generateClient.ts b/packages/client/src/generation/generateClient.ts index f0c2b53c46a7..9105a6d67f4c 100644 --- a/packages/client/src/generation/generateClient.ts +++ b/packages/client/src/generation/generateClient.ts @@ -124,7 +124,7 @@ export async function buildClient({ // we only generate the edge client if `--data-proxy` is passed if (dataProxy === true) { fileMap[path.join('edge.js')] = await JS(edgeTsClient, true) - fileMap[path.join('edge.d.ts')] = await TS(edgeTsClient) + fileMap[path.join('edge.d.ts')] = await TS(edgeTsClient, true) } return { From 727b2904211a51e746291e424b8379de58ed240e Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Tue, 7 Jun 2022 00:28:49 +0200 Subject: [PATCH 31/33] further simplify logic for injectable edge env --- .../utils/buildInjectableEdgeEnv.ts | 41 ++++++++----------- 1 file changed, 17 insertions(+), 24 deletions(-) diff --git a/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts b/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts index 8bebfb9ee932..c52fbe7a5fd4 100644 --- a/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts +++ b/packages/client/src/generation/utils/buildInjectableEdgeEnv.ts @@ -9,9 +9,11 @@ type InjectableEnv = { /** * Builds an injectable environment for the data proxy edge client. It's useful * because it is designed to run in browser-like environments where things like - * `fs`, `process.env`, and .env file loading are not available. It is the place - * where we make collect the env vars for the edge client. To understand this - * better, take a look at the generated code in the edge client. + * `fs`, `process.env`, and .env file loading are not available. That means env + * vars are represented as a global variable or injected at build time. This is + * the glue code to make this work with our existing env var loading logic. It + * is the place where we make collect the env vars for the edge client. To + * understand this better, take a look at the generated code in the edge client. * @see {@link declareInjectableEdgeEnv} * @param edge * @param datasources @@ -31,34 +33,25 @@ export function buildInjectableEdgeEnv(edge: boolean, datasources: InternalDatas * @param datasources to find env vars in */ function declareInjectableEdgeEnv(datasources: InternalDatasource[]) { - const envVarNames = getSelectedEnvVarNames(datasources) - + // we create a base env with empty values for env names const injectableEdgeEnv: InjectableEnv = { parsed: {} } + const envVarNames = getSelectedEnvVarNames(datasources) - // we create a base env with empty values for env names for (const envVarName of envVarNames) { - injectableEdgeEnv.parsed[envVarName] = undefined - } + // for cloudflare workers, an env var is a global js variable + const cfwEnv = `typeof global !== 'undefined' && global['${envVarName}']` + // for vercel edge functions, it's injected statically at build + const vercelEnv = `process.env.${envVarName}` - // abuse a custom replacer to create the injectable env - const injectableEdgeEnvDeclaration = JSON.stringify( - injectableEdgeEnv, - (key, value) => { - if (key === '') return value - if (key === 'parsed') return value - - // for cloudflare workers, an env var is a global js variable - const cfwEnv = `typeof global !== 'undefined' && global['${key}']` - // for vercel edge functions, it's injected statically at build - const vercelEnv = `process.env.${key}` + injectableEdgeEnv.parsed[envVarName] = `${cfwEnv} || ${vercelEnv} || undefined` + } - return `${cfwEnv} || ${vercelEnv} || undefined` - }, - 2, - ).replace(/"/g, '') // remove quotes to make code + // we make it json then remove the quotes to turn it into "code" + const injectableEdgeEnvJson = JSON.stringify(injectableEdgeEnv, null, 2) + const injectableEdgeEnvCode = injectableEdgeEnvJson.replace(/"/g, '') return ` -config.injectableEdgeEnv = ${injectableEdgeEnvDeclaration}` +config.injectableEdgeEnv = ${injectableEdgeEnvCode}` } /** From 0755229415bef7cb60f518e30b5d503d74650839 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Tue, 7 Jun 2022 00:32:08 +0200 Subject: [PATCH 32/33] remove unnecessary path.join --- packages/client/src/generation/generateClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/client/src/generation/generateClient.ts b/packages/client/src/generation/generateClient.ts index 9105a6d67f4c..6bebaf8825b4 100644 --- a/packages/client/src/generation/generateClient.ts +++ b/packages/client/src/generation/generateClient.ts @@ -123,8 +123,8 @@ export async function buildClient({ // we only generate the edge client if `--data-proxy` is passed if (dataProxy === true) { - fileMap[path.join('edge.js')] = await JS(edgeTsClient, true) - fileMap[path.join('edge.d.ts')] = await TS(edgeTsClient, true) + fileMap['edge.js'] = await JS(edgeTsClient, true) + fileMap['edge.d.ts'] = await TS(edgeTsClient, true) } return { From fa0b01143cd20ae2a0e5e2ed9454800078020829 Mon Sep 17 00:00:00 2001 From: Pierre-Antoine Mills Date: Tue, 7 Jun 2022 11:36:57 +0200 Subject: [PATCH 33/33] make getClientVersion async --- .../src/data-proxy/DataProxyEngine.ts | 2 +- .../src/data-proxy/utils/getClientVersion.ts | 24 +++++++++---------- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/engine-core/src/data-proxy/DataProxyEngine.ts b/packages/engine-core/src/data-proxy/DataProxyEngine.ts index a10eff963bad..f1bad4f5ccd4 100644 --- a/packages/engine-core/src/data-proxy/DataProxyEngine.ts +++ b/packages/engine-core/src/data-proxy/DataProxyEngine.ts @@ -30,7 +30,7 @@ export class DataProxyEngine extends Engine { private env: { [k in string]?: string } private clientVersion: string - private remoteClientVersion: string | Promise + private remoteClientVersion: Promise private headers: { Authorization: string } private host: string diff --git a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts index 94f39e0dd664..3ac61a5538cf 100644 --- a/packages/engine-core/src/data-proxy/utils/getClientVersion.ts +++ b/packages/engine-core/src/data-proxy/utils/getClientVersion.ts @@ -9,7 +9,7 @@ const semverRegex = /^[1-9][0-9]*\.[0-9]+\.[0-9]+$/ const prismaNpm = 'https://registry.npmjs.org/prisma' const debug = Debug('prisma:client:dataproxyEngine') -function _getClientVersion(config: EngineConfig) { +async function _getClientVersion(config: EngineConfig) { const clientVersion = config.clientVersion ?? 'unknown' // internal override for testing and manual version overrides @@ -26,22 +26,20 @@ function _getClientVersion(config: EngineConfig) { // if it's an integration version, we resolve its data proxy if (suffix === 'integration' || clientVersion === '0.0.0') { - return (async () => { - // we infer the data proxy version from the engine version - const [version] = engineVersion.split('-') ?? [] - const [major, minor, patch] = version.split('.') + // we infer the data proxy version from the engine version + const [version] = engineVersion.split('-') ?? [] + const [major, minor, patch] = version.split('.') - // if a patch has happened, then we return that version - if (patch !== '0') return `${major}.${minor}.${patch}` + // if a patch has happened, then we return that version + if (patch !== '0') return `${major}.${minor}.${patch}` - // if not, we know that the minor must be minus with 1 - const published = `${major}.${parseInt(minor) - 1}.x` + // if not, we know that the minor must be minus with 1 + const published = `${major}.${parseInt(minor) - 1}.x` - // we don't know what `x` is, so we query the registry - const res = await request(`${prismaNpm}/${published}`, { clientVersion }) + // we don't know what `x` is, so we query the registry + const res = await request(`${prismaNpm}/${published}`, { clientVersion }) - return ((await res.json())['version'] as string) ?? 'undefined' - })() // <-- this is just creating a promise via an iife + return ((await res.json())['version'] as string) ?? 'undefined' } // nothing matched, meaning that the provided version is invalid