@lokalise/prisma-utils 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"cockroachdbError.js","sources":["../src/cockroachdbError.ts"],"sourcesContent":["import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * https://www.cockroachlabs.com/docs/stable/transaction-retry-error-reference#:~:text=To%20indicate%20that%20a%20transaction,the%20string%20%22restart%20transaction%22%20.\n *\n * All transaction retry errors use the SQLSTATE error code 40001\n */\nconst COCKROACHDB_RETRY_TRANSACTION_CODE = '40001'\n\n/**\n * Check if the error is a CockroachDB transaction retry error\n *\n * @param error\n */\nexport const isCockroachDBRetryTransaction = (error: PrismaClientKnownRequestError): boolean => {\n\tconst meta = error.meta\n\tif (!meta) return false\n\n\treturn meta.code === COCKROACHDB_RETRY_TRANSACTION_CODE\n}\n"],"names":["COCKROACHDB_RETRY_TRANSACTION_CODE","isCockroachDBRetryTransaction","error","meta"],"mappings":"AAOA,MAAMA,IAAqC,SAO9BC,IAAgC,CAACC,MAAkD;AAC/F,QAAMC,IAAOD,EAAM;AACnB,SAAKC,IAEEA,EAAK,SAASH,IAFH;AAGnB;"}
1
+ {"version":3,"file":"cockroachdbError.js","sources":["../src/cockroachdbError.ts"],"sourcesContent":["import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * https://www.cockroachlabs.com/docs/stable/transaction-retry-error-reference#:~:text=To%20indicate%20that%20a%20transaction,the%20string%20%22restart%20transaction%22%20.\n *\n * All transaction retry errors use the SQLSTATE error code 40001\n */\nconst COCKROACHDB_RETRY_TRANSACTION_CODE = '40001'\n\n/**\n * Check if the error is a CockroachDB transaction retry error\n *\n * @param error\n */\nexport const isCockroachDBRetryTransaction = (error: PrismaClientKnownRequestError): boolean => {\n\tconst meta = error.meta\n\tif (!meta) return false\n\n\treturn meta.code === COCKROACHDB_RETRY_TRANSACTION_CODE\n}\n"],"names":["COCKROACHDB_RETRY_TRANSACTION_CODE","isCockroachDBRetryTransaction","error","meta"],"mappings":"AAOA,MAAMA,IAAqC,SAO9BC,IAAgC,CAACC,MAAkD;AAC/F,QAAMC,IAAOD,EAAM;AACf,SAACC,IAEEA,EAAK,SAASH,IAFH;AAGnB;"}
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("@lokalise/node-core"),R=require("node:timers/promises"),a=e=>!!e&&i.isError(e)&&"code"in e&&typeof e.code=="string"&&e.code.startsWith("P"),l="P2025",c="P2034",u="P2002",D={COCKROACHDB:"CockroachDb"},O="40001",y=e=>{const r=e.meta;return r?r.code===O:!1},A={retriesAllowed:3,DbDriver:"CockroachDb",baseRetryDelayMs:100,maxRetryDelayMs:3e4},T=async(e,r,t)=>{const o={...A,...t};let s,n=0;for(;n<o.retriesAllowed&&(n>0&&await R.setTimeout(E(n,o.baseRetryDelayMs,o.maxRetryDelayMs)),s=await _(e,r,t),!(s.result||!I(s,o.DbDriver)));)n++;return s??{error:new Error("No transaction retry executed")}},_=async(e,r,t)=>{try{return{result:await e.$transaction(r,t)}}catch(o){return{error:o}}},E=(e,r,t)=>{const o=Math.pow(2,e-1)*r;return Math.min(o,t)},I=(e,r)=>{if(a(e.error)){const t=e.error;if(t.code===c||r==="CockroachDb"&&y(t))return!0}return!1};exports.DbDriverEnum=D;exports.PRISMA_NOT_FOUND_ERROR=l;exports.PRISMA_SERIALIZATION_ERROR=c;exports.PRISMA_UNIQUE_CONSTRAINT_ERROR=u;exports.isPrismaClientKnownRequestError=a;exports.prismaTransaction=T;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const i=require("@lokalise/node-core"),T=require("node:timers/promises"),c=e=>!!e&&i.isError(e)&&"code"in e&&typeof e.code=="string"&&e.code.startsWith("P"),R=e=>e.code===u&&e.message.includes("Transaction already closed"),m="P2025",l="P2034",A="P2002",u="P2028",O={COCKROACHDB:"CockroachDb"},D="40001",_=e=>{const t=e.meta;return t?t.code===D:!1},y={retriesAllowed:3,DbDriver:"CockroachDb",baseRetryDelayMs:100,maxRetryDelayMs:3e4,timeout:5e3,maxTimeout:3e4},E=async(e,t,o)=>{let r={...y,...o},s,n=0;for(;n<r.retriesAllowed&&(n>0&&await T.setTimeout(I(n,r.baseRetryDelayMs,r.maxRetryDelayMs)),s=await C(e,t,r),!s.result);){const a=d(s,r.DbDriver);if(!a)break;a==="increase-timeout"&&(r=i.deepClone(r),r.timeout=Math.min(r.timeout*=2,r.maxTimeout)),n++}return s??{error:new Error("No transaction retry executed")}},C=async(e,t,o)=>{try{return{result:await e.$transaction(t,o)}}catch(r){return{error:r}}},I=(e,t,o)=>{const r=Math.pow(2,e-1)*t;return Math.min(r,o)},d=(e,t)=>{if(c(e.error)){const o=e.error;if(o.code===l||t==="CockroachDb"&&_(o))return!0;if(R(o))return"increase-timeout"}return!1};exports.DbDriverEnum=O;exports.PRISMA_NOT_FOUND_ERROR=m;exports.PRISMA_SERIALIZATION_ERROR=l;exports.PRISMA_TRANSACTION_ERROR=u;exports.PRISMA_UNIQUE_CONSTRAINT_ERROR=A;exports.isPrismaClientKnownRequestError=c;exports.isPrismaTransactionClosedError=R;exports.prismaTransaction=E;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../src/prismaError.ts","../src/types.ts","../src/cockroachdbError.ts","../src/prismaTransaction.ts"],"sourcesContent":["import { isError } from '@lokalise/node-core'\nimport type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * What is checked?\n * \t1. error is defined and not null\n * \t2. error is an `Error`\n * \t3. error contains the field code which is a string\n * \t4. code starts by `P` ([doc](https://www.prisma.io/docs/reference/api-reference/error-reference#error-codes))\n */\nexport const isPrismaClientKnownRequestError = (\n\terror: unknown,\n): error is PrismaClientKnownRequestError =>\n\t!!error &&\n\tisError(error) &&\n\t'code' in error &&\n\ttypeof error.code === 'string' &&\n\terror.code.startsWith('P')\n\n/*\n * Prisma error code P2025 indicates that the operation failed because it depends on one or more\n * records that were required but not found\n */\nexport const PRISMA_NOT_FOUND_ERROR = 'P2025'\n\n/*\n * Prisma error code P2034 indicates a serialization error and that the transaction must be retried.\n * A different error code would indicate that an internal state error happened and that\n * the cluster itself is experiencing an issue which requires intervention\n */\nexport const PRISMA_SERIALIZATION_ERROR = 'P2034'\n\n/*\n * Prisma error code P2002 indicates that the operation failed because a unique constraint was\n * violated. This can happen if you try to create a record with a unique field that already exists\n */\nexport const PRISMA_UNIQUE_CONSTRAINT_ERROR = 'P2002'\n","import type * as runtime from '@prisma/client/runtime/library'\n\ntype ObjectValues<T> = T[keyof T]\n\nexport const DbDriverEnum = {\n\tCOCKROACHDB: 'CockroachDb',\n} as const\nexport type DbDriver = ObjectValues<typeof DbDriverEnum>\n\nexport type PrismaTransactionOptions = {\n\t// Prisma utils library custom options\n\tDbDriver?: DbDriver\n\tretriesAllowed?: number\n\tbaseRetryDelayMs?: number\n\tmaxRetryDelayMs?: number\n\n\t// Prisma $transaction options\n\tmaxWait?: number\n\ttimeout?: number\n\tisolationLevel?: string\n}\n\n// Prisma $transaction with array does not support maxWait and timeout options\nexport type PrismaTransactionBasicOptions = Omit<PrismaTransactionOptions, 'maxWait' | 'timeout'>\n\nexport type PrismaTransactionClient<P> = Omit<P, runtime.ITXClientDenyList>\n\nexport type PrismaTransactionFn<T, P> = (prisma: PrismaTransactionClient<P>) => Promise<T>\n","import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * https://www.cockroachlabs.com/docs/stable/transaction-retry-error-reference#:~:text=To%20indicate%20that%20a%20transaction,the%20string%20%22restart%20transaction%22%20.\n *\n * All transaction retry errors use the SQLSTATE error code 40001\n */\nconst COCKROACHDB_RETRY_TRANSACTION_CODE = '40001'\n\n/**\n * Check if the error is a CockroachDB transaction retry error\n *\n * @param error\n */\nexport const isCockroachDBRetryTransaction = (error: PrismaClientKnownRequestError): boolean => {\n\tconst meta = error.meta\n\tif (!meta) return false\n\n\treturn meta.code === COCKROACHDB_RETRY_TRANSACTION_CODE\n}\n","import { setTimeout } from 'node:timers/promises'\n\nimport type { Either } from '@lokalise/node-core'\nimport type { PrismaClient, Prisma } from '@prisma/client'\nimport type * as runtime from '@prisma/client/runtime/library'\n\nimport { isCockroachDBRetryTransaction } from './cockroachdbError'\nimport { isPrismaClientKnownRequestError, PRISMA_SERIALIZATION_ERROR } from './prismaError'\nimport type {\n\tDbDriver,\n\tPrismaTransactionBasicOptions,\n\tPrismaTransactionFn,\n\tPrismaTransactionOptions,\n} from './types'\n\ntype PrismaTransactionReturnType<T> = Either<\n\tunknown,\n\tT | runtime.Types.Utils.UnwrapTuple<Prisma.PrismaPromise<unknown>[]>\n>\n\nconst DEFAULT_OPTIONS = {\n\tretriesAllowed: 3,\n\tDbDriver: 'CockroachDb',\n\tbaseRetryDelayMs: 100,\n\tmaxRetryDelayMs: 30000, // 30s\n} satisfies Pick<\n\tPrismaTransactionOptions,\n\t'retriesAllowed' | 'DbDriver' | 'baseRetryDelayMs' | 'maxRetryDelayMs'\n>\n\n/**\n * Perform a Prisma DB transaction with automatic retries if needed.\n *\n * @template T | T extends Prisma.PrismaPromise<unknown>[]\n * @param {PrismaClient} prisma\n * @param {PrismaTransactionFn<T> | Prisma.PrismaPromise<unknown>[]} arg\t operation to perform into the transaction\n * @param {PrismaTransactionOptions | PrismaTransactionBasicOptions} options transaction configuration\n * @return {Promise<PrismaTransactionReturnType<T>>}\n */\nexport const prismaTransaction = (async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions | PrismaTransactionBasicOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\tconst optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options }\n\tlet result: PrismaTransactionReturnType<T> | undefined = undefined\n\n\tlet retries = 0\n\twhile (retries < optionsWithDefaults.retriesAllowed) {\n\t\tif (retries > 0) {\n\t\t\tawait setTimeout(\n\t\t\t\tcalculateRetryDelay(\n\t\t\t\t\tretries,\n\t\t\t\t\toptionsWithDefaults.baseRetryDelayMs,\n\t\t\t\t\toptionsWithDefaults.maxRetryDelayMs,\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\n\t\tresult = await executeTransactionTry(prisma, arg, options)\n\t\tif (result.result || !isRetryAllowed(result, optionsWithDefaults.DbDriver)) {\n\t\t\tbreak\n\t\t}\n\n\t\tretries++\n\t}\n\n\treturn result ?? { error: new Error('No transaction retry executed') }\n}) as {\n\t<T, P extends PrismaClient>(\n\t\tprisma: P,\n\t\tfn: PrismaTransactionFn<T, P>,\n\t\toptions?: PrismaTransactionOptions,\n\t): Promise<Either<unknown, T>>\n\t<T extends Prisma.PrismaPromise<unknown>[], P extends PrismaClient>(\n\t\tprisma: P,\n\t\targs: [...T],\n\t\toptions?: PrismaTransactionBasicOptions,\n\t): Promise<Either<unknown, runtime.Types.Utils.UnwrapTuple<T>>>\n}\n\nconst executeTransactionTry = async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\ttry {\n\t\treturn {\n\t\t\t// @ts-ignore\n\t\t\tresult: await prisma.$transaction<T>(arg, options),\n\t\t}\n\t} catch (error) {\n\t\treturn { error }\n\t}\n}\n\nconst calculateRetryDelay = (\n\tretries: number,\n\tbaseRetryDelayMs: number,\n\tmaxDelayMs: number,\n): number => {\n\t// exponential backoff -> 2^(retry-1) * baseRetryDelayMs\n\tconst expDelay = Math.pow(2, retries - 1) * baseRetryDelayMs\n\treturn Math.min(expDelay, maxDelayMs)\n}\n\nconst isRetryAllowed = <T>(result: PrismaTransactionReturnType<T>, dbDriver: DbDriver): boolean => {\n\tif (isPrismaClientKnownRequestError(result.error)) {\n\t\tconst error = result.error\n\t\tif (error.code === PRISMA_SERIALIZATION_ERROR) return true\n\t\tif (dbDriver === 'CockroachDb' && isCockroachDBRetryTransaction(error)) return true\n\t}\n\n\treturn false\n}\n"],"names":["isPrismaClientKnownRequestError","error","isError","PRISMA_NOT_FOUND_ERROR","PRISMA_SERIALIZATION_ERROR","PRISMA_UNIQUE_CONSTRAINT_ERROR","DbDriverEnum","COCKROACHDB_RETRY_TRANSACTION_CODE","isCockroachDBRetryTransaction","meta","DEFAULT_OPTIONS","prismaTransaction","prisma","arg","options","optionsWithDefaults","result","retries","setTimeout","calculateRetryDelay","executeTransactionTry","isRetryAllowed","baseRetryDelayMs","maxDelayMs","expDelay","dbDriver"],"mappings":"yJAUaA,EACZC,GAEA,CAAC,CAACA,GACFC,UAAQD,CAAK,GACb,SAAUA,GACV,OAAOA,EAAM,MAAS,UACtBA,EAAM,KAAK,WAAW,GAAG,EAMbE,EAAyB,QAOzBC,EAA6B,QAM7BC,EAAiC,QChCjCC,EAAe,CAC3B,YAAa,aACd,ECCMC,EAAqC,QAO9BC,EAAiCP,GAAkD,CAC/F,MAAMQ,EAAOR,EAAM,KACnB,OAAKQ,EAEEA,EAAK,OAASF,EAFH,EAGnB,ECCMG,EAAkB,CACvB,eAAgB,EAChB,SAAU,cACV,iBAAkB,IAClB,gBAAiB,GAClB,EAcaC,EAAqB,MACjCC,EACAC,EACAC,IAC6C,CAC7C,MAAMC,EAAsB,CAAE,GAAGL,EAAiB,GAAGI,CAAQ,EAC7D,IAAIE,EAEAC,EAAU,EACP,KAAAA,EAAUF,EAAoB,iBAChCE,EAAU,GACP,MAAAC,EAAA,WACLC,EACCF,EACAF,EAAoB,iBACpBA,EAAoB,eACrB,CAAA,EAIFC,EAAS,MAAMI,EAAsBR,EAAQC,EAAKC,CAAO,EACrD,EAAAE,EAAO,QAAU,CAACK,EAAeL,EAAQD,EAAoB,QAAQ,KAIzEE,IAGD,OAAOD,GAAU,CAAE,MAAO,IAAI,MAAM,+BAA+B,CAAE,CACtE,EAaMI,EAAwB,MAC7BR,EACAC,EACAC,IAC6C,CACzC,GAAA,CACI,MAAA,CAEN,OAAQ,MAAMF,EAAO,aAAgBC,EAAKC,CAAO,CAAA,QAE1Cb,EAAO,CACf,MAAO,CAAE,MAAAA,CAAM,CAChB,CACD,EAEMkB,EAAsB,CAC3BF,EACAK,EACAC,IACY,CAEZ,MAAMC,EAAW,KAAK,IAAI,EAAGP,EAAU,CAAC,EAAIK,EACrC,OAAA,KAAK,IAAIE,EAAUD,CAAU,CACrC,EAEMF,EAAiB,CAAIL,EAAwCS,IAAgC,CAC9F,GAAAzB,EAAgCgB,EAAO,KAAK,EAAG,CAClD,MAAMf,EAAQe,EAAO,MAEjB,GADAf,EAAM,OAASG,GACfqB,IAAa,eAAiBjB,EAA8BP,CAAK,EAAU,MAAA,EAChF,CAEO,MAAA,EACR"}
1
+ {"version":3,"file":"index.cjs","sources":["../src/prismaError.ts","../src/types.ts","../src/cockroachdbError.ts","../src/prismaTransaction.ts"],"sourcesContent":["import { isError } from '@lokalise/node-core'\nimport type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * What is checked?\n * \t1. error is defined and not null\n * \t2. error is an `Error`\n * \t3. error contains the field code which is a string\n * \t4. code starts by `P` ([doc](https://www.prisma.io/docs/reference/api-reference/error-reference#error-codes))\n */\nexport const isPrismaClientKnownRequestError = (\n\terror: unknown,\n): error is PrismaClientKnownRequestError =>\n\t!!error &&\n\tisError(error) &&\n\t'code' in error &&\n\ttypeof error.code === 'string' &&\n\terror.code.startsWith('P')\n\nexport const isPrismaTransactionClosedError = (error: PrismaClientKnownRequestError): boolean =>\n\terror.code === PRISMA_TRANSACTION_ERROR && error.message.includes('Transaction already closed')\n\n/*\n * Prisma error code P2025 indicates that the operation failed because it depends on one or more\n * records that were required but not found\n */\nexport const PRISMA_NOT_FOUND_ERROR = 'P2025'\n\n/*\n * Prisma error code P2034 indicates a serialization error and that the transaction must be retried.\n * A different error code would indicate that an internal state error happened and that\n * the cluster itself is experiencing an issue which requires intervention\n */\nexport const PRISMA_SERIALIZATION_ERROR = 'P2034'\n\n/*\n * Prisma error code P2002 indicates that the operation failed because a unique constraint was\n * violated. This can happen if you try to create a record with a unique field that already exists\n */\nexport const PRISMA_UNIQUE_CONSTRAINT_ERROR = 'P2002'\n\n/*\n * Prisma error code P2028 indicates a transaction API error, essentially a placeholder for errors that do not fit into\n * a more specific category. You should look into the error message for more details\n */\nexport const PRISMA_TRANSACTION_ERROR = 'P2028'\n","import type { Either } from '@lokalise/node-core'\nimport type { Prisma } from '@prisma/client'\nimport type * as runtime from '@prisma/client/runtime/library'\n\ntype ObjectValues<T> = T[keyof T]\n\nexport const DbDriverEnum = {\n\tCOCKROACHDB: 'CockroachDb',\n} as const\nexport type DbDriver = ObjectValues<typeof DbDriverEnum>\n\nexport type PrismaTransactionOptions = {\n\t// Prisma utils library custom options\n\tDbDriver?: DbDriver\n\tretriesAllowed?: number\n\tbaseRetryDelayMs?: number\n\tmaxRetryDelayMs?: number\n\tmaxTimeout?: number\n\n\t// Prisma $transaction options\n\tmaxWait?: number\n\ttimeout?: number\n\tisolationLevel?: string\n}\n\n// Prisma $transaction with array does not support maxWait and timeout options\nexport type PrismaTransactionBasicOptions = Omit<\n\tPrismaTransactionOptions,\n\t'maxWait' | 'timeout' | 'maxTimeout'\n>\n\nexport type PrismaTransactionClient<P> = Omit<P, runtime.ITXClientDenyList>\n\nexport type PrismaTransactionFn<T, P> = (prisma: PrismaTransactionClient<P>) => Promise<T>\n\nexport type PrismaTransactionReturnType<T> = Either<\n\tunknown,\n\tT | runtime.Types.Utils.UnwrapTuple<Prisma.PrismaPromise<unknown>[]>\n>\n","import type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * https://www.cockroachlabs.com/docs/stable/transaction-retry-error-reference#:~:text=To%20indicate%20that%20a%20transaction,the%20string%20%22restart%20transaction%22%20.\n *\n * All transaction retry errors use the SQLSTATE error code 40001\n */\nconst COCKROACHDB_RETRY_TRANSACTION_CODE = '40001'\n\n/**\n * Check if the error is a CockroachDB transaction retry error\n *\n * @param error\n */\nexport const isCockroachDBRetryTransaction = (error: PrismaClientKnownRequestError): boolean => {\n\tconst meta = error.meta\n\tif (!meta) return false\n\n\treturn meta.code === COCKROACHDB_RETRY_TRANSACTION_CODE\n}\n","import { setTimeout } from 'node:timers/promises'\n\nimport type { Either } from '@lokalise/node-core'\nimport { deepClone } from '@lokalise/node-core'\nimport type { PrismaClient, Prisma } from '@prisma/client'\nimport type * as runtime from '@prisma/client/runtime/library'\n\nimport { isCockroachDBRetryTransaction } from './cockroachdbError'\nimport {\n\tisPrismaClientKnownRequestError,\n\tisPrismaTransactionClosedError,\n\tPRISMA_SERIALIZATION_ERROR,\n} from './prismaError'\nimport type {\n\tDbDriver,\n\tPrismaTransactionBasicOptions,\n\tPrismaTransactionFn,\n\tPrismaTransactionOptions,\n\tPrismaTransactionReturnType,\n} from './types'\n\nconst DEFAULT_OPTIONS = {\n\tretriesAllowed: 3,\n\tDbDriver: 'CockroachDb',\n\tbaseRetryDelayMs: 100,\n\tmaxRetryDelayMs: 30000, // 30s\n\ttimeout: 5000, // 5s\n\tmaxTimeout: 30000, // 30s\n} satisfies Partial<PrismaTransactionOptions>\n\n/**\n * Perform a Prisma DB transaction with automatic retries if needed.\n *\n * @template T | T extends Prisma.PrismaPromise<unknown>[]\n * @param {PrismaClient} prisma\n * @param {PrismaTransactionFn<T> | Prisma.PrismaPromise<unknown>[]} arg\t operation to perform into the transaction\n * @param {PrismaTransactionOptions | PrismaTransactionBasicOptions} options transaction configuration\n * @return {Promise<PrismaTransactionReturnType<T>>}\n */\nexport const prismaTransaction = (async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions | PrismaTransactionBasicOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\tlet optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options }\n\tlet result: PrismaTransactionReturnType<T> | undefined = undefined\n\n\tlet retries = 0\n\twhile (retries < optionsWithDefaults.retriesAllowed) {\n\t\tif (retries > 0) {\n\t\t\tawait setTimeout(\n\t\t\t\tcalculateRetryDelay(\n\t\t\t\t\tretries,\n\t\t\t\t\toptionsWithDefaults.baseRetryDelayMs,\n\t\t\t\t\toptionsWithDefaults.maxRetryDelayMs,\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\n\t\tresult = await executeTransactionTry(prisma, arg, optionsWithDefaults)\n\t\tif (result.result) break\n\n\t\tconst retryAllowed = isRetryAllowed(result, optionsWithDefaults.DbDriver)\n\t\tif (!retryAllowed) break\n\n\t\tif (retryAllowed === 'increase-timeout') {\n\t\t\toptionsWithDefaults = deepClone(optionsWithDefaults)\n\t\t\toptionsWithDefaults.timeout = Math.min(\n\t\t\t\t(optionsWithDefaults.timeout *= 2),\n\t\t\t\toptionsWithDefaults.maxTimeout,\n\t\t\t)\n\t\t}\n\n\t\tretries++\n\t}\n\n\treturn result ?? { error: new Error('No transaction retry executed') }\n}) as {\n\t<T, P extends PrismaClient>(\n\t\tprisma: P,\n\t\tfn: PrismaTransactionFn<T, P>,\n\t\toptions?: PrismaTransactionOptions,\n\t): Promise<Either<unknown, T>>\n\t<T extends Prisma.PrismaPromise<unknown>[], P extends PrismaClient>(\n\t\tprisma: P,\n\t\targs: [...T],\n\t\toptions?: PrismaTransactionBasicOptions,\n\t): Promise<Either<unknown, runtime.Types.Utils.UnwrapTuple<T>>>\n}\n\nconst executeTransactionTry = async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\ttry {\n\t\treturn {\n\t\t\t// @ts-ignore\n\t\t\tresult: await prisma.$transaction<T>(arg, options),\n\t\t}\n\t} catch (error) {\n\t\treturn { error }\n\t}\n}\n\nconst calculateRetryDelay = (\n\tretries: number,\n\tbaseRetryDelayMs: number,\n\tmaxDelayMs: number,\n): number => {\n\t// exponential backoff -> 2^(retry-1) * baseRetryDelayMs\n\tconst expDelay = Math.pow(2, retries - 1) * baseRetryDelayMs\n\treturn Math.min(expDelay, maxDelayMs)\n}\n\ntype isRetryAllowedResult = boolean | 'increase-timeout'\n\nconst isRetryAllowed = <T>(\n\tresult: PrismaTransactionReturnType<T>,\n\tdbDriver: DbDriver,\n): isRetryAllowedResult => {\n\tif (isPrismaClientKnownRequestError(result.error)) {\n\t\tconst error = result.error\n\t\tif (error.code === PRISMA_SERIALIZATION_ERROR) return true\n\t\tif (dbDriver === 'CockroachDb' && isCockroachDBRetryTransaction(error)) return true\n\t\tif (isPrismaTransactionClosedError(error)) return 'increase-timeout'\n\t}\n\n\treturn false\n}\n"],"names":["isPrismaClientKnownRequestError","error","isError","isPrismaTransactionClosedError","PRISMA_TRANSACTION_ERROR","PRISMA_NOT_FOUND_ERROR","PRISMA_SERIALIZATION_ERROR","PRISMA_UNIQUE_CONSTRAINT_ERROR","DbDriverEnum","COCKROACHDB_RETRY_TRANSACTION_CODE","isCockroachDBRetryTransaction","meta","DEFAULT_OPTIONS","prismaTransaction","prisma","arg","options","optionsWithDefaults","result","retries","setTimeout","calculateRetryDelay","executeTransactionTry","retryAllowed","isRetryAllowed","deepClone","baseRetryDelayMs","maxDelayMs","expDelay","dbDriver"],"mappings":"yJAUaA,EACZC,GAEA,CAAC,CAACA,GACFC,UAAQD,CAAK,GACb,SAAUA,GACV,OAAOA,EAAM,MAAS,UACtBA,EAAM,KAAK,WAAW,GAAG,EAEbE,EAAkCF,GAC9CA,EAAM,OAASG,GAA4BH,EAAM,QAAQ,SAAS,4BAA4B,EAMlFI,EAAyB,QAOzBC,EAA6B,QAM7BC,EAAiC,QAMjCH,EAA2B,QCvC3BI,EAAe,CAC3B,YAAa,aACd,ECDMC,EAAqC,QAO9BC,EAAiCT,GAAkD,CAC/F,MAAMU,EAAOV,EAAM,KACf,OAACU,EAEEA,EAAK,OAASF,EAFH,EAGnB,ECEMG,EAAkB,CACvB,eAAgB,EAChB,SAAU,cACV,iBAAkB,IAClB,gBAAiB,IACjB,QAAS,IACT,WAAY,GACb,EAWaC,EAAqB,MACjCC,EACAC,EACAC,IAC6C,CAC7C,IAAIC,EAAsB,CAAE,GAAGL,EAAiB,GAAGI,CAAQ,EACvDE,EAEAC,EAAU,EACP,KAAAA,EAAUF,EAAoB,iBAChCE,EAAU,GACP,MAAAC,EAAA,WACLC,EACCF,EACAF,EAAoB,iBACpBA,EAAoB,eACrB,CAAA,EAIFC,EAAS,MAAMI,EAAsBR,EAAQC,EAAKE,CAAmB,EACjE,CAAAC,EAAO,SAZyC,CAcpD,MAAMK,EAAeC,EAAeN,EAAQD,EAAoB,QAAQ,EACxE,GAAI,CAACM,EAAc,MAEfA,IAAiB,qBACpBN,EAAsBQ,EAAAA,UAAUR,CAAmB,EACnDA,EAAoB,QAAU,KAAK,IACjCA,EAAoB,SAAW,EAChCA,EAAoB,UAAA,GAItBE,GACD,CAEA,OAAOD,GAAU,CAAE,MAAO,IAAI,MAAM,+BAA+B,CAAE,CACtE,EAaMI,EAAwB,MAC7BR,EACAC,EACAC,IAC6C,CACzC,GAAA,CACI,MAAA,CAEN,OAAQ,MAAMF,EAAO,aAAgBC,EAAKC,CAAO,CAAA,QAE1Cf,EAAO,CACf,MAAO,CAAE,MAAAA,CAAM,CAChB,CACD,EAEMoB,EAAsB,CAC3BF,EACAO,EACAC,IACY,CAEZ,MAAMC,EAAW,KAAK,IAAI,EAAGT,EAAU,CAAC,EAAIO,EACrC,OAAA,KAAK,IAAIE,EAAUD,CAAU,CACrC,EAIMH,EAAiB,CACtBN,EACAW,IAC0B,CACtB,GAAA7B,EAAgCkB,EAAO,KAAK,EAAG,CAClD,MAAMjB,EAAQiB,EAAO,MAErB,GADIjB,EAAM,OAASK,GACfuB,IAAa,eAAiBnB,EAA8BT,CAAK,EAAU,MAAA,GAC3E,GAAAE,EAA+BF,CAAK,EAAU,MAAA,kBACnD,CAEO,MAAA,EACR"}
package/dist/index.js CHANGED
@@ -1,12 +1,14 @@
1
- import { PRISMA_NOT_FOUND_ERROR as o, PRISMA_SERIALIZATION_ERROR as I, PRISMA_UNIQUE_CONSTRAINT_ERROR as _, isPrismaClientKnownRequestError as e } from "./prismaError.js";
1
+ import { PRISMA_NOT_FOUND_ERROR as o, PRISMA_SERIALIZATION_ERROR as I, PRISMA_TRANSACTION_ERROR as _, PRISMA_UNIQUE_CONSTRAINT_ERROR as i, isPrismaClientKnownRequestError as s, isPrismaTransactionClosedError as A } from "./prismaError.js";
2
2
  import { DbDriverEnum as O } from "./types.js";
3
- import { prismaTransaction as m } from "./prismaTransaction.js";
3
+ import { prismaTransaction as n } from "./prismaTransaction.js";
4
4
  export {
5
5
  O as DbDriverEnum,
6
6
  o as PRISMA_NOT_FOUND_ERROR,
7
7
  I as PRISMA_SERIALIZATION_ERROR,
8
- _ as PRISMA_UNIQUE_CONSTRAINT_ERROR,
9
- e as isPrismaClientKnownRequestError,
10
- m as prismaTransaction
8
+ _ as PRISMA_TRANSACTION_ERROR,
9
+ i as PRISMA_UNIQUE_CONSTRAINT_ERROR,
10
+ s as isPrismaClientKnownRequestError,
11
+ A as isPrismaTransactionClosedError,
12
+ n as prismaTransaction
11
13
  };
12
14
  //# sourceMappingURL=index.js.map
@@ -8,6 +8,8 @@ import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library';
8
8
  * 4. code starts by `P` ([doc](https://www.prisma.io/docs/reference/api-reference/error-reference#error-codes))
9
9
  */
10
10
  export declare const isPrismaClientKnownRequestError: (error: unknown) => error is PrismaClientKnownRequestError;
11
+ export declare const isPrismaTransactionClosedError: (error: PrismaClientKnownRequestError) => boolean;
11
12
  export declare const PRISMA_NOT_FOUND_ERROR = "P2025";
12
13
  export declare const PRISMA_SERIALIZATION_ERROR = "P2034";
13
14
  export declare const PRISMA_UNIQUE_CONSTRAINT_ERROR = "P2002";
15
+ export declare const PRISMA_TRANSACTION_ERROR = "P2028";
@@ -1,9 +1,11 @@
1
- import { isError as t } from "@lokalise/node-core";
2
- const o = (R) => !!R && t(R) && "code" in R && typeof R.code == "string" && R.code.startsWith("P"), n = "P2025", i = "P2034", I = "P2002";
1
+ import { isError as R } from "@lokalise/node-core";
2
+ const n = (s) => !!s && R(s) && "code" in s && typeof s.code == "string" && s.code.startsWith("P"), c = (s) => s.code === o && s.message.includes("Transaction already closed"), i = "P2025", P = "P2034", a = "P2002", o = "P2028";
3
3
  export {
4
- n as PRISMA_NOT_FOUND_ERROR,
5
- i as PRISMA_SERIALIZATION_ERROR,
6
- I as PRISMA_UNIQUE_CONSTRAINT_ERROR,
7
- o as isPrismaClientKnownRequestError
4
+ i as PRISMA_NOT_FOUND_ERROR,
5
+ P as PRISMA_SERIALIZATION_ERROR,
6
+ o as PRISMA_TRANSACTION_ERROR,
7
+ a as PRISMA_UNIQUE_CONSTRAINT_ERROR,
8
+ n as isPrismaClientKnownRequestError,
9
+ c as isPrismaTransactionClosedError
8
10
  };
9
11
  //# sourceMappingURL=prismaError.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prismaError.js","sources":["../src/prismaError.ts"],"sourcesContent":["import { isError } from '@lokalise/node-core'\nimport type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * What is checked?\n * \t1. error is defined and not null\n * \t2. error is an `Error`\n * \t3. error contains the field code which is a string\n * \t4. code starts by `P` ([doc](https://www.prisma.io/docs/reference/api-reference/error-reference#error-codes))\n */\nexport const isPrismaClientKnownRequestError = (\n\terror: unknown,\n): error is PrismaClientKnownRequestError =>\n\t!!error &&\n\tisError(error) &&\n\t'code' in error &&\n\ttypeof error.code === 'string' &&\n\terror.code.startsWith('P')\n\n/*\n * Prisma error code P2025 indicates that the operation failed because it depends on one or more\n * records that were required but not found\n */\nexport const PRISMA_NOT_FOUND_ERROR = 'P2025'\n\n/*\n * Prisma error code P2034 indicates a serialization error and that the transaction must be retried.\n * A different error code would indicate that an internal state error happened and that\n * the cluster itself is experiencing an issue which requires intervention\n */\nexport const PRISMA_SERIALIZATION_ERROR = 'P2034'\n\n/*\n * Prisma error code P2002 indicates that the operation failed because a unique constraint was\n * violated. This can happen if you try to create a record with a unique field that already exists\n */\nexport const PRISMA_UNIQUE_CONSTRAINT_ERROR = 'P2002'\n"],"names":["isPrismaClientKnownRequestError","error","isError","PRISMA_NOT_FOUND_ERROR","PRISMA_SERIALIZATION_ERROR","PRISMA_UNIQUE_CONSTRAINT_ERROR"],"mappings":";AAUO,MAAMA,IAAkC,CAC9CC,MAEA,CAAC,CAACA,KACFC,EAAQD,CAAK,KACb,UAAUA,KACV,OAAOA,EAAM,QAAS,YACtBA,EAAM,KAAK,WAAW,GAAG,GAMbE,IAAyB,SAOzBC,IAA6B,SAM7BC,IAAiC;"}
1
+ {"version":3,"file":"prismaError.js","sources":["../src/prismaError.ts"],"sourcesContent":["import { isError } from '@lokalise/node-core'\nimport type { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'\n\n/**\n * What is checked?\n * \t1. error is defined and not null\n * \t2. error is an `Error`\n * \t3. error contains the field code which is a string\n * \t4. code starts by `P` ([doc](https://www.prisma.io/docs/reference/api-reference/error-reference#error-codes))\n */\nexport const isPrismaClientKnownRequestError = (\n\terror: unknown,\n): error is PrismaClientKnownRequestError =>\n\t!!error &&\n\tisError(error) &&\n\t'code' in error &&\n\ttypeof error.code === 'string' &&\n\terror.code.startsWith('P')\n\nexport const isPrismaTransactionClosedError = (error: PrismaClientKnownRequestError): boolean =>\n\terror.code === PRISMA_TRANSACTION_ERROR && error.message.includes('Transaction already closed')\n\n/*\n * Prisma error code P2025 indicates that the operation failed because it depends on one or more\n * records that were required but not found\n */\nexport const PRISMA_NOT_FOUND_ERROR = 'P2025'\n\n/*\n * Prisma error code P2034 indicates a serialization error and that the transaction must be retried.\n * A different error code would indicate that an internal state error happened and that\n * the cluster itself is experiencing an issue which requires intervention\n */\nexport const PRISMA_SERIALIZATION_ERROR = 'P2034'\n\n/*\n * Prisma error code P2002 indicates that the operation failed because a unique constraint was\n * violated. This can happen if you try to create a record with a unique field that already exists\n */\nexport const PRISMA_UNIQUE_CONSTRAINT_ERROR = 'P2002'\n\n/*\n * Prisma error code P2028 indicates a transaction API error, essentially a placeholder for errors that do not fit into\n * a more specific category. You should look into the error message for more details\n */\nexport const PRISMA_TRANSACTION_ERROR = 'P2028'\n"],"names":["isPrismaClientKnownRequestError","error","isError","isPrismaTransactionClosedError","PRISMA_TRANSACTION_ERROR","PRISMA_NOT_FOUND_ERROR","PRISMA_SERIALIZATION_ERROR","PRISMA_UNIQUE_CONSTRAINT_ERROR"],"mappings":";AAUO,MAAMA,IAAkC,CAC9CC,MAEA,CAAC,CAACA,KACFC,EAAQD,CAAK,KACb,UAAUA,KACV,OAAOA,EAAM,QAAS,YACtBA,EAAM,KAAK,WAAW,GAAG,GAEbE,IAAiC,CAACF,MAC9CA,EAAM,SAASG,KAA4BH,EAAM,QAAQ,SAAS,4BAA4B,GAMlFI,IAAyB,SAOzBC,IAA6B,SAM7BC,IAAiC,SAMjCH,IAA2B;"}
@@ -1,45 +1,55 @@
1
- import { setTimeout as i } from "node:timers/promises";
2
- import { isCockroachDBRetryTransaction as n } from "./cockroachdbError.js";
3
- import { isPrismaClientKnownRequestError as c, PRISMA_SERIALIZATION_ERROR as l } from "./prismaError.js";
4
- const y = {
1
+ import { setTimeout as n } from "node:timers/promises";
2
+ import { deepClone as c } from "@lokalise/node-core";
3
+ import { isCockroachDBRetryTransaction as l } from "./cockroachdbError.js";
4
+ import { isPrismaClientKnownRequestError as m, PRISMA_SERIALIZATION_ERROR as u, isPrismaTransactionClosedError as y } from "./prismaError.js";
5
+ const D = {
5
6
  retriesAllowed: 3,
6
7
  DbDriver: "CockroachDb",
7
8
  baseRetryDelayMs: 100,
8
- maxRetryDelayMs: 3e4
9
+ maxRetryDelayMs: 3e4,
9
10
  // 30s
10
- }, h = async (e, o, r) => {
11
- const t = { ...y, ...r };
12
- let a, s = 0;
13
- for (; s < t.retriesAllowed && (s > 0 && await i(
14
- D(
15
- s,
16
- t.baseRetryDelayMs,
17
- t.maxRetryDelayMs
11
+ timeout: 5e3,
12
+ // 5s
13
+ maxTimeout: 3e4
14
+ // 30s
15
+ }, b = async (t, o, e) => {
16
+ let r = { ...D, ...e }, a, i = 0;
17
+ for (; i < r.retriesAllowed && (i > 0 && await n(
18
+ R(
19
+ i,
20
+ r.baseRetryDelayMs,
21
+ r.maxRetryDelayMs
18
22
  )
19
- ), a = await u(e, o, r), !(a.result || !R(a, t.DbDriver))); )
20
- s++;
23
+ ), a = await f(t, o, r), !a.result); ) {
24
+ const s = w(a, r.DbDriver);
25
+ if (!s) break;
26
+ s === "increase-timeout" && (r = c(r), r.timeout = Math.min(
27
+ r.timeout *= 2,
28
+ r.maxTimeout
29
+ )), i++;
30
+ }
21
31
  return a ?? { error: new Error("No transaction retry executed") };
22
- }, u = async (e, o, r) => {
32
+ }, f = async (t, o, e) => {
23
33
  try {
24
34
  return {
25
35
  // @ts-ignore
26
- result: await e.$transaction(o, r)
36
+ result: await t.$transaction(o, e)
27
37
  };
28
- } catch (t) {
29
- return { error: t };
38
+ } catch (r) {
39
+ return { error: r };
30
40
  }
31
- }, D = (e, o, r) => {
32
- const t = Math.pow(2, e - 1) * o;
33
- return Math.min(t, r);
34
- }, R = (e, o) => {
35
- if (c(e.error)) {
36
- const r = e.error;
37
- if (r.code === l || o === "CockroachDb" && n(r))
38
- return !0;
41
+ }, R = (t, o, e) => {
42
+ const r = Math.pow(2, t - 1) * o;
43
+ return Math.min(r, e);
44
+ }, w = (t, o) => {
45
+ if (m(t.error)) {
46
+ const e = t.error;
47
+ if (e.code === u || o === "CockroachDb" && l(e)) return !0;
48
+ if (y(e)) return "increase-timeout";
39
49
  }
40
50
  return !1;
41
51
  };
42
52
  export {
43
- h as prismaTransaction
53
+ b as prismaTransaction
44
54
  };
45
55
  //# sourceMappingURL=prismaTransaction.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"prismaTransaction.js","sources":["../src/prismaTransaction.ts"],"sourcesContent":["import { setTimeout } from 'node:timers/promises'\n\nimport type { Either } from '@lokalise/node-core'\nimport type { PrismaClient, Prisma } from '@prisma/client'\nimport type * as runtime from '@prisma/client/runtime/library'\n\nimport { isCockroachDBRetryTransaction } from './cockroachdbError'\nimport { isPrismaClientKnownRequestError, PRISMA_SERIALIZATION_ERROR } from './prismaError'\nimport type {\n\tDbDriver,\n\tPrismaTransactionBasicOptions,\n\tPrismaTransactionFn,\n\tPrismaTransactionOptions,\n} from './types'\n\ntype PrismaTransactionReturnType<T> = Either<\n\tunknown,\n\tT | runtime.Types.Utils.UnwrapTuple<Prisma.PrismaPromise<unknown>[]>\n>\n\nconst DEFAULT_OPTIONS = {\n\tretriesAllowed: 3,\n\tDbDriver: 'CockroachDb',\n\tbaseRetryDelayMs: 100,\n\tmaxRetryDelayMs: 30000, // 30s\n} satisfies Pick<\n\tPrismaTransactionOptions,\n\t'retriesAllowed' | 'DbDriver' | 'baseRetryDelayMs' | 'maxRetryDelayMs'\n>\n\n/**\n * Perform a Prisma DB transaction with automatic retries if needed.\n *\n * @template T | T extends Prisma.PrismaPromise<unknown>[]\n * @param {PrismaClient} prisma\n * @param {PrismaTransactionFn<T> | Prisma.PrismaPromise<unknown>[]} arg\t operation to perform into the transaction\n * @param {PrismaTransactionOptions | PrismaTransactionBasicOptions} options transaction configuration\n * @return {Promise<PrismaTransactionReturnType<T>>}\n */\nexport const prismaTransaction = (async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions | PrismaTransactionBasicOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\tconst optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options }\n\tlet result: PrismaTransactionReturnType<T> | undefined = undefined\n\n\tlet retries = 0\n\twhile (retries < optionsWithDefaults.retriesAllowed) {\n\t\tif (retries > 0) {\n\t\t\tawait setTimeout(\n\t\t\t\tcalculateRetryDelay(\n\t\t\t\t\tretries,\n\t\t\t\t\toptionsWithDefaults.baseRetryDelayMs,\n\t\t\t\t\toptionsWithDefaults.maxRetryDelayMs,\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\n\t\tresult = await executeTransactionTry(prisma, arg, options)\n\t\tif (result.result || !isRetryAllowed(result, optionsWithDefaults.DbDriver)) {\n\t\t\tbreak\n\t\t}\n\n\t\tretries++\n\t}\n\n\treturn result ?? { error: new Error('No transaction retry executed') }\n}) as {\n\t<T, P extends PrismaClient>(\n\t\tprisma: P,\n\t\tfn: PrismaTransactionFn<T, P>,\n\t\toptions?: PrismaTransactionOptions,\n\t): Promise<Either<unknown, T>>\n\t<T extends Prisma.PrismaPromise<unknown>[], P extends PrismaClient>(\n\t\tprisma: P,\n\t\targs: [...T],\n\t\toptions?: PrismaTransactionBasicOptions,\n\t): Promise<Either<unknown, runtime.Types.Utils.UnwrapTuple<T>>>\n}\n\nconst executeTransactionTry = async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\ttry {\n\t\treturn {\n\t\t\t// @ts-ignore\n\t\t\tresult: await prisma.$transaction<T>(arg, options),\n\t\t}\n\t} catch (error) {\n\t\treturn { error }\n\t}\n}\n\nconst calculateRetryDelay = (\n\tretries: number,\n\tbaseRetryDelayMs: number,\n\tmaxDelayMs: number,\n): number => {\n\t// exponential backoff -> 2^(retry-1) * baseRetryDelayMs\n\tconst expDelay = Math.pow(2, retries - 1) * baseRetryDelayMs\n\treturn Math.min(expDelay, maxDelayMs)\n}\n\nconst isRetryAllowed = <T>(result: PrismaTransactionReturnType<T>, dbDriver: DbDriver): boolean => {\n\tif (isPrismaClientKnownRequestError(result.error)) {\n\t\tconst error = result.error\n\t\tif (error.code === PRISMA_SERIALIZATION_ERROR) return true\n\t\tif (dbDriver === 'CockroachDb' && isCockroachDBRetryTransaction(error)) return true\n\t}\n\n\treturn false\n}\n"],"names":["DEFAULT_OPTIONS","prismaTransaction","prisma","arg","options","optionsWithDefaults","result","retries","setTimeout","calculateRetryDelay","executeTransactionTry","isRetryAllowed","error","baseRetryDelayMs","maxDelayMs","expDelay","dbDriver","isPrismaClientKnownRequestError","PRISMA_SERIALIZATION_ERROR","isCockroachDBRetryTransaction"],"mappings":";;;AAoBA,MAAMA,IAAkB;AAAA,EACvB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,iBAAiB;AAAA;AAClB,GAcaC,IAAqB,OACjCC,GACAC,GACAC,MAC6C;AAC7C,QAAMC,IAAsB,EAAE,GAAGL,GAAiB,GAAGI,EAAQ;AAC7D,MAAIE,GAEAC,IAAU;AACP,SAAAA,IAAUF,EAAoB,mBAChCE,IAAU,KACP,MAAAC;AAAA,IACLC;AAAA,MACCF;AAAA,MACAF,EAAoB;AAAA,MACpBA,EAAoB;AAAA,IACrB;AAAA,EAAA,GAIFC,IAAS,MAAMI,EAAsBR,GAAQC,GAAKC,CAAO,GACrD,EAAAE,EAAO,UAAU,CAACK,EAAeL,GAAQD,EAAoB,QAAQ;AAIzE,IAAAE;AAGD,SAAOD,KAAU,EAAE,OAAO,IAAI,MAAM,+BAA+B,EAAE;AACtE,GAaMI,IAAwB,OAC7BR,GACAC,GACAC,MAC6C;AACzC,MAAA;AACI,WAAA;AAAA;AAAA,MAEN,QAAQ,MAAMF,EAAO,aAAgBC,GAAKC,CAAO;AAAA,IAAA;AAAA,WAE1CQ,GAAO;AACf,WAAO,EAAE,OAAAA,EAAM;AAAA,EAChB;AACD,GAEMH,IAAsB,CAC3BF,GACAM,GACAC,MACY;AAEZ,QAAMC,IAAW,KAAK,IAAI,GAAGR,IAAU,CAAC,IAAIM;AACrC,SAAA,KAAK,IAAIE,GAAUD,CAAU;AACrC,GAEMH,IAAiB,CAAIL,GAAwCU,MAAgC;AAC9F,MAAAC,EAAgCX,EAAO,KAAK,GAAG;AAClD,UAAMM,IAAQN,EAAO;AAEjB,QADAM,EAAM,SAASM,KACfF,MAAa,iBAAiBG,EAA8BP,CAAK;AAAU,aAAA;AAAA,EAChF;AAEO,SAAA;AACR;"}
1
+ {"version":3,"file":"prismaTransaction.js","sources":["../src/prismaTransaction.ts"],"sourcesContent":["import { setTimeout } from 'node:timers/promises'\n\nimport type { Either } from '@lokalise/node-core'\nimport { deepClone } from '@lokalise/node-core'\nimport type { PrismaClient, Prisma } from '@prisma/client'\nimport type * as runtime from '@prisma/client/runtime/library'\n\nimport { isCockroachDBRetryTransaction } from './cockroachdbError'\nimport {\n\tisPrismaClientKnownRequestError,\n\tisPrismaTransactionClosedError,\n\tPRISMA_SERIALIZATION_ERROR,\n} from './prismaError'\nimport type {\n\tDbDriver,\n\tPrismaTransactionBasicOptions,\n\tPrismaTransactionFn,\n\tPrismaTransactionOptions,\n\tPrismaTransactionReturnType,\n} from './types'\n\nconst DEFAULT_OPTIONS = {\n\tretriesAllowed: 3,\n\tDbDriver: 'CockroachDb',\n\tbaseRetryDelayMs: 100,\n\tmaxRetryDelayMs: 30000, // 30s\n\ttimeout: 5000, // 5s\n\tmaxTimeout: 30000, // 30s\n} satisfies Partial<PrismaTransactionOptions>\n\n/**\n * Perform a Prisma DB transaction with automatic retries if needed.\n *\n * @template T | T extends Prisma.PrismaPromise<unknown>[]\n * @param {PrismaClient} prisma\n * @param {PrismaTransactionFn<T> | Prisma.PrismaPromise<unknown>[]} arg\t operation to perform into the transaction\n * @param {PrismaTransactionOptions | PrismaTransactionBasicOptions} options transaction configuration\n * @return {Promise<PrismaTransactionReturnType<T>>}\n */\nexport const prismaTransaction = (async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions | PrismaTransactionBasicOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\tlet optionsWithDefaults = { ...DEFAULT_OPTIONS, ...options }\n\tlet result: PrismaTransactionReturnType<T> | undefined = undefined\n\n\tlet retries = 0\n\twhile (retries < optionsWithDefaults.retriesAllowed) {\n\t\tif (retries > 0) {\n\t\t\tawait setTimeout(\n\t\t\t\tcalculateRetryDelay(\n\t\t\t\t\tretries,\n\t\t\t\t\toptionsWithDefaults.baseRetryDelayMs,\n\t\t\t\t\toptionsWithDefaults.maxRetryDelayMs,\n\t\t\t\t),\n\t\t\t)\n\t\t}\n\n\t\tresult = await executeTransactionTry(prisma, arg, optionsWithDefaults)\n\t\tif (result.result) break\n\n\t\tconst retryAllowed = isRetryAllowed(result, optionsWithDefaults.DbDriver)\n\t\tif (!retryAllowed) break\n\n\t\tif (retryAllowed === 'increase-timeout') {\n\t\t\toptionsWithDefaults = deepClone(optionsWithDefaults)\n\t\t\toptionsWithDefaults.timeout = Math.min(\n\t\t\t\t(optionsWithDefaults.timeout *= 2),\n\t\t\t\toptionsWithDefaults.maxTimeout,\n\t\t\t)\n\t\t}\n\n\t\tretries++\n\t}\n\n\treturn result ?? { error: new Error('No transaction retry executed') }\n}) as {\n\t<T, P extends PrismaClient>(\n\t\tprisma: P,\n\t\tfn: PrismaTransactionFn<T, P>,\n\t\toptions?: PrismaTransactionOptions,\n\t): Promise<Either<unknown, T>>\n\t<T extends Prisma.PrismaPromise<unknown>[], P extends PrismaClient>(\n\t\tprisma: P,\n\t\targs: [...T],\n\t\toptions?: PrismaTransactionBasicOptions,\n\t): Promise<Either<unknown, runtime.Types.Utils.UnwrapTuple<T>>>\n}\n\nconst executeTransactionTry = async <T, P extends PrismaClient>(\n\tprisma: P,\n\targ: PrismaTransactionFn<T, P> | Prisma.PrismaPromise<unknown>[],\n\toptions?: PrismaTransactionOptions,\n): Promise<PrismaTransactionReturnType<T>> => {\n\ttry {\n\t\treturn {\n\t\t\t// @ts-ignore\n\t\t\tresult: await prisma.$transaction<T>(arg, options),\n\t\t}\n\t} catch (error) {\n\t\treturn { error }\n\t}\n}\n\nconst calculateRetryDelay = (\n\tretries: number,\n\tbaseRetryDelayMs: number,\n\tmaxDelayMs: number,\n): number => {\n\t// exponential backoff -> 2^(retry-1) * baseRetryDelayMs\n\tconst expDelay = Math.pow(2, retries - 1) * baseRetryDelayMs\n\treturn Math.min(expDelay, maxDelayMs)\n}\n\ntype isRetryAllowedResult = boolean | 'increase-timeout'\n\nconst isRetryAllowed = <T>(\n\tresult: PrismaTransactionReturnType<T>,\n\tdbDriver: DbDriver,\n): isRetryAllowedResult => {\n\tif (isPrismaClientKnownRequestError(result.error)) {\n\t\tconst error = result.error\n\t\tif (error.code === PRISMA_SERIALIZATION_ERROR) return true\n\t\tif (dbDriver === 'CockroachDb' && isCockroachDBRetryTransaction(error)) return true\n\t\tif (isPrismaTransactionClosedError(error)) return 'increase-timeout'\n\t}\n\n\treturn false\n}\n"],"names":["DEFAULT_OPTIONS","prismaTransaction","prisma","arg","options","optionsWithDefaults","result","retries","setTimeout","calculateRetryDelay","executeTransactionTry","retryAllowed","isRetryAllowed","deepClone","error","baseRetryDelayMs","maxDelayMs","expDelay","dbDriver","isPrismaClientKnownRequestError","PRISMA_SERIALIZATION_ERROR","isCockroachDBRetryTransaction","isPrismaTransactionClosedError"],"mappings":";;;;AAqBA,MAAMA,IAAkB;AAAA,EACvB,gBAAgB;AAAA,EAChB,UAAU;AAAA,EACV,kBAAkB;AAAA,EAClB,iBAAiB;AAAA;AAAA,EACjB,SAAS;AAAA;AAAA,EACT,YAAY;AAAA;AACb,GAWaC,IAAqB,OACjCC,GACAC,GACAC,MAC6C;AAC7C,MAAIC,IAAsB,EAAE,GAAGL,GAAiB,GAAGI,EAAQ,GACvDE,GAEAC,IAAU;AACP,SAAAA,IAAUF,EAAoB,mBAChCE,IAAU,KACP,MAAAC;AAAA,IACLC;AAAA,MACCF;AAAA,MACAF,EAAoB;AAAA,MACpBA,EAAoB;AAAA,IACrB;AAAA,EAAA,GAIFC,IAAS,MAAMI,EAAsBR,GAAQC,GAAKE,CAAmB,GACjE,CAAAC,EAAO,WAZyC;AAcpD,UAAMK,IAAeC,EAAeN,GAAQD,EAAoB,QAAQ;AACxE,QAAI,CAACM,EAAc;AAEnB,IAAIA,MAAiB,uBACpBN,IAAsBQ,EAAUR,CAAmB,GACnDA,EAAoB,UAAU,KAAK;AAAA,MACjCA,EAAoB,WAAW;AAAA,MAChCA,EAAoB;AAAA,IAAA,IAItBE;AAAA,EACD;AAEA,SAAOD,KAAU,EAAE,OAAO,IAAI,MAAM,+BAA+B,EAAE;AACtE,GAaMI,IAAwB,OAC7BR,GACAC,GACAC,MAC6C;AACzC,MAAA;AACI,WAAA;AAAA;AAAA,MAEN,QAAQ,MAAMF,EAAO,aAAgBC,GAAKC,CAAO;AAAA,IAAA;AAAA,WAE1CU,GAAO;AACf,WAAO,EAAE,OAAAA,EAAM;AAAA,EAChB;AACD,GAEML,IAAsB,CAC3BF,GACAQ,GACAC,MACY;AAEZ,QAAMC,IAAW,KAAK,IAAI,GAAGV,IAAU,CAAC,IAAIQ;AACrC,SAAA,KAAK,IAAIE,GAAUD,CAAU;AACrC,GAIMJ,IAAiB,CACtBN,GACAY,MAC0B;AACtB,MAAAC,EAAgCb,EAAO,KAAK,GAAG;AAClD,UAAMQ,IAAQR,EAAO;AAErB,QADIQ,EAAM,SAASM,KACfF,MAAa,iBAAiBG,EAA8BP,CAAK,EAAU,QAAA;AAC3E,QAAAQ,EAA+BR,CAAK,EAAU,QAAA;AAAA,EACnD;AAEO,SAAA;AACR;"}
package/dist/types.d.ts CHANGED
@@ -1,3 +1,6 @@
1
+ import { Either } from '@lokalise/node-core';
2
+ import { Prisma } from '@prisma/client';
3
+
1
4
  import type * as runtime from '@prisma/client/runtime/library';
2
5
  type ObjectValues<T> = T[keyof T];
3
6
  export declare const DbDriverEnum: {
@@ -9,11 +12,13 @@ export type PrismaTransactionOptions = {
9
12
  retriesAllowed?: number;
10
13
  baseRetryDelayMs?: number;
11
14
  maxRetryDelayMs?: number;
15
+ maxTimeout?: number;
12
16
  maxWait?: number;
13
17
  timeout?: number;
14
18
  isolationLevel?: string;
15
19
  };
16
- export type PrismaTransactionBasicOptions = Omit<PrismaTransactionOptions, 'maxWait' | 'timeout'>;
20
+ export type PrismaTransactionBasicOptions = Omit<PrismaTransactionOptions, 'maxWait' | 'timeout' | 'maxTimeout'>;
17
21
  export type PrismaTransactionClient<P> = Omit<P, runtime.ITXClientDenyList>;
18
22
  export type PrismaTransactionFn<T, P> = (prisma: PrismaTransactionClient<P>) => Promise<T>;
23
+ export type PrismaTransactionReturnType<T> = Either<unknown, T | runtime.Types.Utils.UnwrapTuple<Prisma.PrismaPromise<unknown>[]>>;
19
24
  export {};
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sources":["../src/types.ts"],"sourcesContent":["import type * as runtime from '@prisma/client/runtime/library'\n\ntype ObjectValues<T> = T[keyof T]\n\nexport const DbDriverEnum = {\n\tCOCKROACHDB: 'CockroachDb',\n} as const\nexport type DbDriver = ObjectValues<typeof DbDriverEnum>\n\nexport type PrismaTransactionOptions = {\n\t// Prisma utils library custom options\n\tDbDriver?: DbDriver\n\tretriesAllowed?: number\n\tbaseRetryDelayMs?: number\n\tmaxRetryDelayMs?: number\n\n\t// Prisma $transaction options\n\tmaxWait?: number\n\ttimeout?: number\n\tisolationLevel?: string\n}\n\n// Prisma $transaction with array does not support maxWait and timeout options\nexport type PrismaTransactionBasicOptions = Omit<PrismaTransactionOptions, 'maxWait' | 'timeout'>\n\nexport type PrismaTransactionClient<P> = Omit<P, runtime.ITXClientDenyList>\n\nexport type PrismaTransactionFn<T, P> = (prisma: PrismaTransactionClient<P>) => Promise<T>\n"],"names":["DbDriverEnum"],"mappings":"AAIO,MAAMA,IAAe;AAAA,EAC3B,aAAa;AACd;"}
1
+ {"version":3,"file":"types.js","sources":["../src/types.ts"],"sourcesContent":["import type { Either } from '@lokalise/node-core'\nimport type { Prisma } from '@prisma/client'\nimport type * as runtime from '@prisma/client/runtime/library'\n\ntype ObjectValues<T> = T[keyof T]\n\nexport const DbDriverEnum = {\n\tCOCKROACHDB: 'CockroachDb',\n} as const\nexport type DbDriver = ObjectValues<typeof DbDriverEnum>\n\nexport type PrismaTransactionOptions = {\n\t// Prisma utils library custom options\n\tDbDriver?: DbDriver\n\tretriesAllowed?: number\n\tbaseRetryDelayMs?: number\n\tmaxRetryDelayMs?: number\n\tmaxTimeout?: number\n\n\t// Prisma $transaction options\n\tmaxWait?: number\n\ttimeout?: number\n\tisolationLevel?: string\n}\n\n// Prisma $transaction with array does not support maxWait and timeout options\nexport type PrismaTransactionBasicOptions = Omit<\n\tPrismaTransactionOptions,\n\t'maxWait' | 'timeout' | 'maxTimeout'\n>\n\nexport type PrismaTransactionClient<P> = Omit<P, runtime.ITXClientDenyList>\n\nexport type PrismaTransactionFn<T, P> = (prisma: PrismaTransactionClient<P>) => Promise<T>\n\nexport type PrismaTransactionReturnType<T> = Either<\n\tunknown,\n\tT | runtime.Types.Utils.UnwrapTuple<Prisma.PrismaPromise<unknown>[]>\n>\n"],"names":["DbDriverEnum"],"mappings":"AAMO,MAAMA,IAAe;AAAA,EAC3B,aAAa;AACd;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/prisma-utils",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "files": [
@@ -57,10 +57,10 @@
57
57
  "cross-env": "^7.0.3",
58
58
  "dotenv-cli": "^7.4.1",
59
59
  "prisma": "^5.14.0",
60
- "prettier": "^3.3.1",
60
+ "prettier": "^3.3.2",
61
61
  "rimraf": "^5.0.7",
62
62
  "typescript": "5.4.5",
63
- "vite": "^5.2.13",
63
+ "vite": "^5.3.3",
64
64
  "vitest": "^1.6.0"
65
65
  },
66
66
  "prettier": "@lokalise/prettier-config"