Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

kmore

Package Overview
Dependencies
Maintainers
1
Versions
272
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

kmore - npm Package Compare versions

Comparing version 33.3.0 to 34.0.0

1

dist/lib/config.js

@@ -10,2 +10,3 @@ export const defaultPropDescriptor = {

identifier: void 0,
kmoreQueryId: void 0,
kUid: '',

@@ -12,0 +13,0 @@ queryUid: '',

33

dist/lib/event.d.ts

@@ -1,8 +0,27 @@

import type { Knex } from 'knex';
import { EventCallbacks, OnQueryData, OnQueryErrorData, OnQueryErrorErr, OnQueryRespRaw, QueryResponse } from './types.js';
export declare function callCbOnStart<Ctx = unknown>(ctx: Ctx | undefined, dbId: string, cbs: EventCallbacks<Ctx> | undefined, identifier: unknown, builder: Knex.QueryBuilder): Promise<void>;
export declare function callCbOnQuery<Ctx = unknown>(ctx: Ctx | undefined, dbId: string, cbs: EventCallbacks<Ctx> | undefined, identifier: unknown, data: OnQueryData): Promise<void>;
export declare function callCbOnQueryResp<Ctx = unknown>(ctx: Ctx | undefined, dbId: string, cbs: EventCallbacks<Ctx> | undefined, identifier: unknown, _resp: QueryResponse, // not used
respRaw: OnQueryRespRaw): Promise<void>;
export declare function callCbOnQueryError<Ctx = unknown>(ctx: Ctx | undefined, dbId: string, cbs: EventCallbacks<Ctx> | undefined, identifier: unknown, err: OnQueryErrorErr, data: OnQueryErrorData): Promise<void>;
import { EventCallbacks, KmoreQueryBuilder, OnQueryData, OnQueryErrorData, OnQueryErrorErr, OnQueryRespRaw, QueryResponse } from './types.js';
export interface CallCbOptionsBase<Ctx = any> {
ctx: Ctx | undefined;
dbId: string;
cbs: EventCallbacks<Ctx> | undefined;
identifier: unknown;
kmoreQueryId: symbol;
}
export interface CallCbOnStartOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
builder: KmoreQueryBuilder;
}
export declare function callCbOnStart(options: CallCbOnStartOptions): Promise<void>;
export interface CallCbOnQueryOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
data: OnQueryData;
}
export declare function callCbOnQuery(options: CallCbOnQueryOptions): Promise<void>;
export interface CallCbOnQueryRespOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
_resp: QueryResponse;
respRaw: OnQueryRespRaw;
}
export declare function callCbOnQueryResp(options: CallCbOnQueryRespOptions): Promise<void>;
export interface CallCbOnQueryErrorOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
err: OnQueryErrorErr;
data: OnQueryErrorData;
}
export declare function callCbOnQueryError(options: CallCbOnQueryErrorOptions): Promise<void>;
//# sourceMappingURL=event.d.ts.map

@@ -0,61 +1,61 @@

/* eslint-disable @typescript-eslint/no-explicit-any */
import { initKmoreEvent } from './config.js';
export async function callCbOnStart(ctx, dbId, cbs, identifier, builder) {
const cb = cbs?.start;
export async function callCbOnStart(options) {
const cb = options.cbs?.start;
if (typeof cb === 'function') {
const event = processKnexOnEvent({
type: 'start',
identifier,
identifier: options.identifier,
data: void 0,
queryUid: '',
queryBuilder: builder,
queryBuilder: options.builder,
});
event.dbId = dbId;
await cb(event, ctx);
event.dbId = options.dbId;
await cb(event, options.ctx);
}
}
export async function callCbOnQuery(ctx, dbId, cbs, identifier, data) {
const cb = cbs?.query;
export async function callCbOnQuery(options) {
const cb = options.cbs?.query;
if (typeof cb === 'function') {
const queryUid = pickQueryUidFrom(data);
const queryUid = pickQueryUidFrom(options.data);
const event = processKnexOnEvent({
type: 'query',
identifier,
data,
identifier: options.identifier,
data: options.data,
queryUid,
queryBuilder: void 0,
});
event.dbId = dbId;
await cb(event, ctx);
event.dbId = options.dbId;
await cb(event, options.ctx);
}
}
export async function callCbOnQueryResp(ctx, dbId, cbs, identifier, _resp, // not used
respRaw) {
const cb = cbs?.queryResponse;
export async function callCbOnQueryResp(options) {
const cb = options.cbs?.queryResponse;
if (typeof cb === 'function') {
const queryUid = pickQueryUidFrom(respRaw);
const queryUid = pickQueryUidFrom(options.respRaw);
const event = processKnexOnEvent({
type: 'queryResponse',
identifier,
respRaw,
identifier: options.identifier,
respRaw: options.respRaw,
queryUid,
queryBuilder: void 0,
});
event.dbId = dbId;
await cb(event, ctx);
event.dbId = options.dbId;
await cb(event, options.ctx);
}
}
export async function callCbOnQueryError(ctx, dbId, cbs, identifier, err, data) {
const cb = cbs?.queryError;
export async function callCbOnQueryError(options) {
const cb = options.cbs?.queryError;
if (typeof cb === 'function') {
const queryUid = pickQueryUidFrom(data);
const queryUid = pickQueryUidFrom(options.data);
const event = processKnexOnEvent({
type: 'queryError',
identifier,
exError: err,
exData: data,
identifier: options.identifier,
exError: options.err,
exData: options.data,
queryUid,
queryBuilder: void 0,
});
event.dbId = dbId;
await cb(event, ctx);
event.dbId = options.dbId;
await cb(event, options.ctx);
}

@@ -62,0 +62,0 @@ }

import type { DbDict } from 'kmore-types';
import type { Knex } from 'knex';
import { postProcessResponse } from './helper.js';
import { CaseType, DbQueryBuilder, EventCallbacks, KnexConfig, QueryContext, QuerySpanInfo } from './types.js';
import { CaseType, DbQueryBuilder, EventCallbacks, KmoreQueryBuilder, KmoreTransaction, KmoreTransactionConfig, KnexConfig, QueryContext, QuerySpanInfo } from './types.js';
export declare class Kmore<D = any, Context = any> {

@@ -42,2 +42,15 @@ /**

readonly postProcessResponseSet: Set<typeof postProcessResponse>;
readonly trxActionOnError: KmoreTransactionConfig['trxActionOnError'];
/**
* kmoreTrxid => trx
*/
readonly trxMap: Map<symbol, KmoreTransaction>;
/**
* kmoreTrxId => Set<kmoreQueryId>
*/
readonly trxIdQueryMap: TrxIdQueryMap;
/**
* context => Set<kmoreTrxId>
*/
readonly ctxTrxIdMap: WeakMap<object, Set<symbol>>;
readonly config: KnexConfig;

@@ -54,7 +67,14 @@ readonly dict: unknown extends D ? undefined : DbDict<D>;

*/
transaction(id?: PropertyKey, config?: Knex.TransactionConfig): Promise<Knex.Transaction & {
kmoreTrxId: symbol;
}>;
transaction(id?: PropertyKey, config?: KmoreTransactionConfig): Promise<KmoreTransaction>;
getTrxByKmoreQueryId(kmoreQueryId: symbol): KmoreTransaction | undefined;
getTrxByKmoreTrxId(id: symbol): KmoreTransaction | undefined;
setCtxTrxIdMap(ctx: unknown, kmoreTrxId: symbol): void;
getTrxSetByCtx(ctx: unknown): Set<KmoreTransaction>;
doTrxActionOnError(trx: KmoreTransaction | undefined): Promise<void>;
protected createTrxProxy(trx: KmoreTransaction): KmoreTransaction;
protected createRefTables<P extends string>(prefix: P, caseConvert: CaseType): DbQueryBuilder<Context, D, P, CaseType>;
protected extRefTableFnProperty(refName: string, caseConvert: CaseType, ctx: Context | undefined): Knex.QueryBuilder;
protected extRefTableFnProperty(refName: string, caseConvert: CaseType, ctx: Context | undefined): KmoreQueryBuilder;
protected extRefTableFnPropertyCallback(refTable: KmoreQueryBuilder, caseConvert: CaseType, ctx: Context | undefined, kmoreQueryId: symbol): KmoreQueryBuilder;
protected extRefTableFnPropertyTransacting(refTable: KmoreQueryBuilder, ctx: Context | undefined): KmoreQueryBuilder;
protected extRefTableFnPropertyThen(refTable: KmoreQueryBuilder): KmoreQueryBuilder;
protected postProcessResponse(result: any, queryContext?: QueryContext): unknown;

@@ -81,5 +101,16 @@ }

wrapIdentifierCaseConvert?: CaseType | undefined;
/**
* Atuo trsaction action (rollback|commit|none) on error (Rejection or Exception),
* @CAUTION **Will always rollback if query error in database even though this value set to 'commit'**
* @default rollback
*/
trxActionOnError?: KmoreTransactionConfig['trxActionOnError'];
}
export declare function KmoreFactory<D, Ctx = unknown>(options: KmoreFactoryOpts<D, Ctx>): Kmore<D, Ctx>;
export declare function createDbh(knexConfig: KnexConfig): Knex;
/**
* kmoreTrxId => Set<kmoreQueryId>
*/
declare type TrxIdQueryMap = Map<symbol, Set<symbol>>;
export {};
//# sourceMappingURL=kmore.d.ts.map

@@ -0,1 +1,2 @@

/* eslint-disable max-lines-per-function */
/* eslint-disable @typescript-eslint/no-explicit-any */

@@ -7,3 +8,3 @@ /* eslint-disable import/no-extraneous-dependencies */

import { defaultPropDescriptor, initialConfig } from './config.js';
import { callCbOnQuery, callCbOnQueryError, callCbOnQueryResp, callCbOnStart } from './event.js';
import { callCbOnQuery, callCbOnQueryError, callCbOnQueryResp, callCbOnStart, } from './event.js';
import { postProcessResponse, wrapIdentifier } from './helper.js';

@@ -49,2 +50,15 @@ import { CaseType, } from './types.js';

postProcessResponseSet = new Set();
trxActionOnError = 'rollback';
/**
* kmoreTrxid => trx
*/
trxMap = new Map();
/**
* kmoreTrxId => Set<kmoreQueryId>
*/
trxIdQueryMap = new Map();
/**
* context => Set<kmoreTrxId>
*/
ctxTrxIdMap = new WeakMap();
config;

@@ -93,2 +107,5 @@ dict;

this.config.postProcessResponse = (result, queryContext) => this.postProcessResponse(result, queryContext);
if (options.trxActionOnError) {
this.trxActionOnError = options.trxActionOnError;
}
this.refTables = this.createRefTables('ref_', CaseType.none);

@@ -107,4 +124,4 @@ this.camelTables = this.createRefTables('ref_', CaseType.camel);

: id ? Symbol(id) : Symbol(Date.now());
const trx = await this.dbh.transaction(void 0, config);
Object.defineProperty(trx, 'kmoreTrxId', {
const tmp = await this.dbh.transaction(void 0, config);
Object.defineProperty(tmp, 'kmoreTrxId', {
...defaultPropDescriptor,

@@ -114,4 +131,108 @@ enumerable: false,

});
const trxActionOnError = config?.trxActionOnError
?? this.trxActionOnError ?? 'rollback';
Object.defineProperty(tmp, 'trxActionOnError', {
...defaultPropDescriptor,
enumerable: false,
value: trxActionOnError,
});
if (trxActionOnError === 'none') {
return tmp;
}
const trx = this.createTrxProxy(tmp);
this.trxMap.set(kmoreTrxId, trx);
this.trxIdQueryMap.set(kmoreTrxId, new Set());
return trx;
}
getTrxByKmoreQueryId(kmoreQueryId) {
for (const [trxId, queryIdSet] of this.trxIdQueryMap.entries()) {
if (queryIdSet.has(kmoreQueryId)) {
const trx = this.trxMap.get(trxId);
if (trx) {
return trx;
}
}
}
}
getTrxByKmoreTrxId(id) {
return this.trxMap.get(id);
}
setCtxTrxIdMap(ctx, kmoreTrxId) {
if (!ctx || !kmoreTrxId || typeof ctx !== 'object') {
return;
}
if (!this.ctxTrxIdMap.get(ctx)) {
this.ctxTrxIdMap.set(ctx, new Set());
}
this.ctxTrxIdMap.get(ctx)?.add(kmoreTrxId);
}
getTrxSetByCtx(ctx) {
const ret = new Set();
if (!ctx || typeof ctx !== 'object') {
return ret;
}
const trxIdMap = this.ctxTrxIdMap.get(ctx);
trxIdMap?.forEach((kmoreTrxId) => {
const trx = this.trxMap.get(kmoreTrxId);
if (trx) {
ret.add(trx);
}
});
return ret;
}
async doTrxActionOnError(trx) {
if (!trx) {
return;
}
if (!trx.isCompleted()) {
this.trxMap.delete(trx.kmoreTrxId);
}
switch (trx.trxActionOnError) {
case 'rollback': {
await trx.rollback();
this.trxMap.delete(trx.kmoreTrxId);
this.trxIdQueryMap.delete(trx.kmoreTrxId);
break;
}
case 'commit': {
await trx.commit();
this.trxMap.delete(trx.kmoreTrxId);
this.trxIdQueryMap.delete(trx.kmoreTrxId);
break;
}
default:
break;
}
}
createTrxProxy(trx) {
// eslint-disable-next-line @typescript-eslint/unbound-method
const commit = new Proxy(trx.commit, {
apply: (target, ctx, args) => {
this.trxIdQueryMap.delete(ctx.kmoreTrxId);
this.trxMap.delete(ctx.kmoreTrxId);
return Reflect.apply(target, ctx, args);
},
});
Object.defineProperty(trx, 'commit', {
enumerable: true,
writable: true,
configurable: false,
value: commit,
});
// eslint-disable-next-line @typescript-eslint/unbound-method
const rollback = new Proxy(trx.rollback, {
apply: (target, ctx, args) => {
this.trxIdQueryMap.delete(ctx.kmoreTrxId);
this.trxMap.delete(ctx.kmoreTrxId);
return Reflect.apply(target, ctx, args);
},
});
Object.defineProperty(trx, 'rollback', {
enumerable: true,
writable: true,
configurable: false,
value: rollback,
});
return trx;
}
createRefTables(prefix, caseConvert) {

@@ -141,14 +262,98 @@ const rb = {};

assert(caseConvert, 'caseConvert must be defined');
const opts = {
const kmoreQueryId = Symbol(`${this.dbId}-${Date.now()}`);
let refTable = this.dbh(refName);
refTable = this.extRefTableFnPropertyCallback(refTable, caseConvert, ctx, kmoreQueryId);
refTable = this.extRefTableFnPropertyTransacting(refTable, ctx);
refTable = this.extRefTableFnPropertyThen(refTable);
void Object.defineProperty(refTable, 'kmoreQueryId', {
...defaultPropDescriptor,
value: kmoreQueryId,
});
return refTable;
}
extRefTableFnPropertyCallback(refTable, caseConvert, ctx, kmoreQueryId) {
assert(caseConvert, 'caseConvert must be defined');
const queryCtxOpts = {
wrapIdentifierCaseConvert: this.wrapIdentifierCaseConvert,
postProcessResponseCaseConvert: caseConvert,
kmoreQueryId,
};
const refTable = this.dbh(refName)
.queryContext(opts)
.on('start', async (builder) => callCbOnStart(ctx, this.dbId, this.eventCallbacks, this.instanceId, builder))
.on('query', async (data) => callCbOnQuery(ctx, this.dbId, this.eventCallbacks, this.instanceId, data))
.on('query-response', async (resp, respRaw) => callCbOnQueryResp(ctx, this.dbId, this.eventCallbacks, this.instanceId, resp, respRaw))
.on('query-error', async (err, data) => callCbOnQueryError(ctx, this.dbId, this.eventCallbacks, this.instanceId, err, data));
const opts = {
ctx,
dbId: this.dbId,
cbs: this.eventCallbacks,
identifier: this.instanceId,
kmoreQueryId,
};
const refTable2 = refTable
.queryContext(queryCtxOpts)
.on('start', async (builder) => callCbOnStart({
...opts,
builder,
}))
.on('query', async (data) => callCbOnQuery({
...opts,
data,
}))
.on('query-response', async (resp, respRaw) => callCbOnQueryResp({
...opts,
_resp: resp,
respRaw,
}))
.on('query-error', async (err, data) => {
const trx = this.getTrxByKmoreQueryId(kmoreQueryId);
await this.doTrxActionOnError(trx);
return callCbOnQueryError({
...opts,
err,
data,
});
});
return refTable2;
}
extRefTableFnPropertyTransacting(refTable, ctx) {
// eslint-disable-next-line @typescript-eslint/unbound-method
const ts = new Proxy(refTable.transacting, {
apply: (target, ctx2, args) => {
const [trx] = args;
assert(trx?.isTransaction === true, 'trx must be a transaction');
const { kmoreTrxId } = trx;
const qid = ctx2.kmoreQueryId;
if (qid && kmoreTrxId) {
this.trxIdQueryMap.get(kmoreTrxId)?.add(qid);
this.setCtxTrxIdMap(ctx, kmoreTrxId);
}
return Reflect.apply(target, ctx2, args);
},
});
void Object.defineProperty(refTable, 'transacting', {
...defaultPropDescriptor,
value: ts,
});
return refTable;
}
extRefTableFnPropertyThen(refTable) {
// eslint-disable-next-line @typescript-eslint/unbound-method
const pm = new Proxy(refTable.then, {
apply: (target, ctx2, args) => {
const qid = ctx2.kmoreQueryId;
const trx = this.getTrxByKmoreQueryId(qid);
if (!trx) {
return Reflect.apply(target, ctx2, args);
}
return Reflect.apply(target, ctx2, args)
.catch(async (err) => {
const trx2 = this.getTrxByKmoreQueryId(qid);
await this.doTrxActionOnError(trx2);
return Promise.reject(err);
});
},
});
void Object.defineProperty(refTable, 'then', {
...defaultPropDescriptor,
configurable: true,
value: pm,
});
return refTable;
}
postProcessResponse(result, queryContext) {

@@ -155,0 +360,0 @@ let ret = result;

@@ -5,2 +5,22 @@ import { RecordCamelKeys, RecordPascalKeys, RecordSnakeKeys } from '@waiting/shared-types';

export declare type KnexConfig = Knex.Config;
export declare type KmoreTransaction = Knex.Transaction & {
kmoreTrxId: symbol;
/**
* Atuo trsaction action (rollback|commit|none) on error (Rejection or Exception),
* @CAUTION **Will always rollback if query error in database even though this value set to 'commit'**
* @default rollback
*/
trxActionOnError: NonNullable<KmoreTransactionConfig['trxActionOnError']>;
};
export declare type KmoreTransactionConfig = Knex.TransactionConfig & {
/**
* Atuo trsaction action (rollback|commit|none) on error (Rejection or Exception),
* @CAUTION **Will always rollback if query error in database even though this value set to 'commit'**
* @default rollback
*/
trxActionOnError?: 'commit' | 'rollback' | 'none';
};
export declare type KmoreQueryBuilder<TRecord extends {} = any, TResult = any> = Knex.QueryBuilder<TRecord, TResult> & {
kmoreQueryId: symbol;
};
export declare enum EnumClient {

@@ -27,3 +47,3 @@ pg = "pg",

}
export declare type TbQueryBuilder<TRecord, Context> = (ctx?: Context) => Knex.QueryBuilder<TRecord, TRecord[]>;
export declare type TbQueryBuilder<TRecord, Context> = (ctx?: Context) => KmoreQueryBuilder<TRecord, TRecord[]>;
export declare type EventType = 'query' | 'queryError' | 'queryResponse' | 'start' | 'unknown';

@@ -39,2 +59,3 @@ export interface KmoreEvent<T = unknown> {

queryUid: string;
kmoreQueryId: symbol | undefined;
/**

@@ -61,2 +82,3 @@ * @description Note: may keep value of the latest transaction id,

postProcessResponseCaseConvert: CaseType;
kmoreQueryId: symbol;
}

@@ -63,0 +85,0 @@ export interface OnQueryData {

{
"name": "kmore",
"author": "waiting",
"version": "33.3.0",
"version": "34.0.0",
"description": "A SQL query builder based on knex with powerful TypeScript type support",

@@ -45,3 +45,3 @@ "keywords": [

"cross-env": "7",
"kmore-types": "^33.1.1",
"kmore-types": "^34.0.0",
"knex": "^2.2.0",

@@ -77,3 +77,3 @@ "pg": "^8.7.3"

},
"gitHead": "a435ff4a6d65356f44f034b68895225bd421f498"
"gitHead": "29051a2cc393ff5d25a4faadf67b90889b404121"
}

@@ -15,2 +15,3 @@ import type { KmoreEvent, KnexConfig } from './types.js'

identifier: void 0,
kmoreQueryId: void 0,
kUid: '',

@@ -17,0 +18,0 @@ queryUid: '',

@@ -1,3 +0,2 @@

import type { Knex } from 'knex'
/* eslint-disable @typescript-eslint/no-explicit-any */
import { initKmoreEvent } from './config.js'

@@ -7,2 +6,3 @@ import {

KmoreEvent,
KmoreQueryBuilder,
OnQueryData,

@@ -16,93 +16,90 @@ OnQueryErrorData,

export async function callCbOnStart<Ctx = unknown>(
ctx: Ctx | undefined,
dbId: string,
cbs: EventCallbacks<Ctx> | undefined,
identifier: unknown,
builder: Knex.QueryBuilder,
): Promise<void> {
export interface CallCbOptionsBase<Ctx = any> {
ctx: Ctx | undefined
dbId: string
cbs: EventCallbacks<Ctx> | undefined
identifier: unknown
kmoreQueryId: symbol
}
const cb: EventCallbacks<Ctx>['start'] = cbs?.start
export interface CallCbOnStartOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
builder: KmoreQueryBuilder
}
export async function callCbOnStart(options: CallCbOnStartOptions): Promise<void> {
const cb: EventCallbacks['start'] = options.cbs?.start
if (typeof cb === 'function') {
const event = processKnexOnEvent({
type: 'start',
identifier,
identifier: options.identifier,
data: void 0,
queryUid: '',
queryBuilder: builder,
queryBuilder: options.builder,
})
event.dbId = dbId
await cb(event, ctx)
event.dbId = options.dbId
await cb(event, options.ctx)
}
}
export async function callCbOnQuery<Ctx = unknown>(
ctx: Ctx | undefined,
dbId: string,
cbs: EventCallbacks<Ctx> | undefined,
identifier: unknown,
data: OnQueryData,
): Promise<void> {
export interface CallCbOnQueryOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
data: OnQueryData
}
const cb: EventCallbacks<Ctx>['query'] = cbs?.query
export async function callCbOnQuery(options: CallCbOnQueryOptions): Promise<void> {
const cb: EventCallbacks['query'] = options.cbs?.query
if (typeof cb === 'function') {
const queryUid = pickQueryUidFrom(data)
const queryUid = pickQueryUidFrom(options.data)
const event = processKnexOnEvent({
type: 'query',
identifier,
data,
identifier: options.identifier,
data: options.data,
queryUid,
queryBuilder: void 0,
})
event.dbId = dbId
await cb(event, ctx)
event.dbId = options.dbId
await cb(event, options.ctx)
}
}
export async function callCbOnQueryResp<Ctx = unknown>(
ctx: Ctx | undefined,
dbId: string,
cbs: EventCallbacks<Ctx> | undefined,
identifier: unknown,
_resp: QueryResponse, // not used
respRaw: OnQueryRespRaw,
): Promise<void> {
export interface CallCbOnQueryRespOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
_resp: QueryResponse
respRaw: OnQueryRespRaw
}
const cb: EventCallbacks<Ctx>['queryResponse'] = cbs?.queryResponse
export async function callCbOnQueryResp(options: CallCbOnQueryRespOptions): Promise<void> {
const cb: EventCallbacks['queryResponse'] = options.cbs?.queryResponse
if (typeof cb === 'function') {
const queryUid = pickQueryUidFrom(respRaw)
const queryUid = pickQueryUidFrom(options.respRaw)
const event = processKnexOnEvent({
type: 'queryResponse',
identifier,
respRaw,
identifier: options.identifier,
respRaw: options.respRaw,
queryUid,
queryBuilder: void 0,
})
event.dbId = dbId
await cb(event, ctx)
event.dbId = options.dbId
await cb(event, options.ctx)
}
}
export async function callCbOnQueryError<Ctx = unknown>(
ctx: Ctx | undefined,
dbId: string,
cbs: EventCallbacks<Ctx> | undefined,
identifier: unknown,
err: OnQueryErrorErr,
data: OnQueryErrorData,
): Promise<void> {
export interface CallCbOnQueryErrorOptions<Ctx = any> extends CallCbOptionsBase<Ctx> {
err: OnQueryErrorErr
data: OnQueryErrorData
}
const cb: EventCallbacks<Ctx>['queryError'] = cbs?.queryError
export async function callCbOnQueryError(options: CallCbOnQueryErrorOptions): Promise<void> {
const cb: EventCallbacks['queryError'] = options.cbs?.queryError
if (typeof cb === 'function') {
const queryUid = pickQueryUidFrom(data)
const queryUid = pickQueryUidFrom(options.data)
const event = processKnexOnEvent({
type: 'queryError',
identifier,
exError: err,
exData: data,
identifier: options.identifier,
exError: options.err,
exData: options.data,
queryUid,
queryBuilder: void 0,
})
event.dbId = dbId
await cb(event, ctx)
event.dbId = options.dbId
await cb(event, options.ctx)
}

@@ -109,0 +106,0 @@ }

@@ -0,1 +1,2 @@

/* eslint-disable max-lines-per-function */
/* eslint-disable @typescript-eslint/no-explicit-any */

@@ -11,3 +12,9 @@ /* eslint-disable import/no-extraneous-dependencies */

import { defaultPropDescriptor, initialConfig } from './config.js'
import { callCbOnQuery, callCbOnQueryError, callCbOnQueryResp, callCbOnStart } from './event.js'
import {
callCbOnQuery,
callCbOnQueryError,
callCbOnQueryResp,
callCbOnStart,
CallCbOptionsBase,
} from './event.js'
import { PostProcessInput, postProcessResponse, wrapIdentifier } from './helper.js'

@@ -18,2 +25,5 @@ import {

EventCallbacks,
KmoreQueryBuilder,
KmoreTransaction,
KmoreTransactionConfig,
KnexConfig,

@@ -75,10 +85,23 @@ OnQueryData,

readonly postProcessResponseSet = new Set<typeof postProcessResponse>()
readonly trxActionOnError: KmoreTransactionConfig['trxActionOnError'] = 'rollback'
/**
* kmoreTrxid => trx
*/
readonly trxMap = new Map<symbol, KmoreTransaction>()
/**
* kmoreTrxId => Set<kmoreQueryId>
*/
readonly trxIdQueryMap: TrxIdQueryMap = new Map()
/**
* context => Set<kmoreTrxId>
*/
readonly ctxTrxIdMap = new WeakMap<object, Set<symbol>>()
public readonly config: KnexConfig
public readonly dict: unknown extends D ? undefined : DbDict<D>
public readonly dbId: string
public readonly dbh: Knex
public readonly instanceId: string | symbol
public readonly eventCallbacks: EventCallbacks<Context> | undefined
public readonly wrapIdentifierCaseConvert: CaseType
readonly config: KnexConfig
readonly dict: unknown extends D ? undefined : DbDict<D>
readonly dbId: string
readonly dbh: Knex
readonly instanceId: string | symbol
readonly eventCallbacks: EventCallbacks<Context> | undefined
readonly wrapIdentifierCaseConvert: CaseType

@@ -131,2 +154,6 @@

if (options.trxActionOnError) {
this.trxActionOnError = options.trxActionOnError
}
this.refTables = this.createRefTables<'ref_'>('ref_', CaseType.none)

@@ -146,4 +173,4 @@ this.camelTables = this.createRefTables<'ref_'>('ref_', CaseType.camel)

id?: PropertyKey,
config?: Knex.TransactionConfig,
): Promise<Knex.Transaction & { kmoreTrxId: symbol }> {
config?: KmoreTransactionConfig,
): Promise<KmoreTransaction> {

@@ -154,5 +181,5 @@ const kmoreTrxId = typeof id === 'symbol'

const trx = await this.dbh.transaction(void 0, config)
const tmp = await this.dbh.transaction(void 0, config)
Object.defineProperty(trx, 'kmoreTrxId', {
Object.defineProperty(tmp, 'kmoreTrxId', {
...defaultPropDescriptor,

@@ -162,6 +189,129 @@ enumerable: false,

})
return trx as Knex.Transaction & { kmoreTrxId: symbol }
const trxActionOnError: KmoreTransactionConfig['trxActionOnError'] = config?.trxActionOnError
?? this.trxActionOnError ?? 'rollback'
Object.defineProperty(tmp, 'trxActionOnError', {
...defaultPropDescriptor,
enumerable: false,
value: trxActionOnError,
})
if (trxActionOnError === 'none') {
return tmp as KmoreTransaction
}
const trx = this.createTrxProxy(tmp as KmoreTransaction)
this.trxMap.set(kmoreTrxId, trx)
this.trxIdQueryMap.set(kmoreTrxId, new Set())
return trx
}
getTrxByKmoreQueryId(kmoreQueryId: symbol): KmoreTransaction | undefined {
for (const [trxId, queryIdSet] of this.trxIdQueryMap.entries()) {
if (queryIdSet.has(kmoreQueryId)) {
const trx = this.trxMap.get(trxId)
if (trx) {
return trx
}
}
}
}
getTrxByKmoreTrxId(id: symbol): KmoreTransaction | undefined {
return this.trxMap.get(id)
}
setCtxTrxIdMap(
ctx: unknown,
kmoreTrxId: symbol,
): void {
if (! ctx || ! kmoreTrxId || typeof ctx !== 'object') { return }
if (! this.ctxTrxIdMap.get(ctx)) {
this.ctxTrxIdMap.set(ctx, new Set())
}
this.ctxTrxIdMap.get(ctx)?.add(kmoreTrxId)
}
getTrxSetByCtx(
ctx: unknown,
): Set<KmoreTransaction> {
const ret = new Set<KmoreTransaction>()
if (! ctx || typeof ctx !== 'object') {
return ret
}
const trxIdMap = this.ctxTrxIdMap.get(ctx)
trxIdMap?.forEach((kmoreTrxId) => {
const trx = this.trxMap.get(kmoreTrxId)
if (trx) {
ret.add(trx)
}
})
return ret
}
async doTrxActionOnError(trx: KmoreTransaction | undefined): Promise<void> {
if (! trx) { return }
if (! trx.isCompleted()) {
this.trxMap.delete(trx.kmoreTrxId)
}
switch (trx.trxActionOnError) {
case 'rollback': {
await trx.rollback()
this.trxMap.delete(trx.kmoreTrxId)
this.trxIdQueryMap.delete(trx.kmoreTrxId)
break
}
case 'commit': {
await trx.commit()
this.trxMap.delete(trx.kmoreTrxId)
this.trxIdQueryMap.delete(trx.kmoreTrxId)
break
}
default:
break
}
}
protected createTrxProxy(trx: KmoreTransaction): KmoreTransaction {
// eslint-disable-next-line @typescript-eslint/unbound-method
const commit = new Proxy(trx.commit, {
apply: (target: typeof trx.commit, ctx: KmoreTransaction, args: unknown[]) => {
this.trxIdQueryMap.delete(ctx.kmoreTrxId)
this.trxMap.delete(ctx.kmoreTrxId)
return Reflect.apply(target, ctx, args)
},
})
Object.defineProperty(trx, 'commit', {
enumerable: true,
writable: true,
configurable: false,
value: commit,
})
// eslint-disable-next-line @typescript-eslint/unbound-method
const rollback = new Proxy(trx.rollback, {
apply: (target: typeof trx.rollback, ctx: KmoreTransaction, args: unknown[]) => {
this.trxIdQueryMap.delete(ctx.kmoreTrxId)
this.trxMap.delete(ctx.kmoreTrxId)
return Reflect.apply(target, ctx, args)
},
})
Object.defineProperty(trx, 'rollback', {
enumerable: true,
writable: true,
configurable: false,
value: rollback,
})
return trx
}
protected createRefTables<P extends string>(

@@ -203,51 +353,144 @@ prefix: P,

ctx: Context | undefined,
): Knex.QueryBuilder {
): KmoreQueryBuilder {
assert(caseConvert, 'caseConvert must be defined')
const opts: QueryContext = {
const kmoreQueryId = Symbol(`${this.dbId}-${Date.now()}`)
let refTable = this.dbh(refName)
refTable = this.extRefTableFnPropertyCallback(
refTable as KmoreQueryBuilder,
caseConvert,
ctx,
kmoreQueryId,
)
refTable = this.extRefTableFnPropertyTransacting(refTable as KmoreQueryBuilder, ctx)
refTable = this.extRefTableFnPropertyThen(refTable as KmoreQueryBuilder)
void Object.defineProperty(refTable, 'kmoreQueryId', {
...defaultPropDescriptor,
value: kmoreQueryId,
})
return refTable as KmoreQueryBuilder
}
protected extRefTableFnPropertyCallback(
refTable: KmoreQueryBuilder,
caseConvert: CaseType,
ctx: Context | undefined,
kmoreQueryId: symbol,
): KmoreQueryBuilder {
assert(caseConvert, 'caseConvert must be defined')
const queryCtxOpts: QueryContext = {
wrapIdentifierCaseConvert: this.wrapIdentifierCaseConvert,
postProcessResponseCaseConvert: caseConvert,
kmoreQueryId,
}
const refTable = this.dbh(refName)
.queryContext(opts)
.on('start', async (builder: Knex.QueryBuilder) => callCbOnStart<Context>(
ctx,
this.dbId,
this.eventCallbacks,
this.instanceId,
builder,
))
.on('query', async (data: OnQueryData) => callCbOnQuery<Context>(
ctx,
this.dbId,
this.eventCallbacks,
this.instanceId,
data,
))
const opts: CallCbOptionsBase = {
ctx,
dbId: this.dbId,
cbs: this.eventCallbacks,
identifier: this.instanceId,
kmoreQueryId,
}
const refTable2 = refTable
.queryContext(queryCtxOpts)
.on(
'start',
async (builder: KmoreQueryBuilder) => callCbOnStart({
...opts,
builder,
}),
)
.on(
'query',
async (data: OnQueryData) => callCbOnQuery({
...opts,
data,
}),
)
.on(
'query-response',
async (resp: QueryResponse, respRaw: OnQueryRespRaw) => callCbOnQueryResp<Context>(
ctx,
this.dbId,
this.eventCallbacks,
this.instanceId,
resp,
async (resp: QueryResponse, respRaw: OnQueryRespRaw) => callCbOnQueryResp({
...opts,
_resp: resp,
respRaw,
),
}),
)
.on(
'query-error',
async (err: OnQueryErrorErr, data: OnQueryErrorData) => callCbOnQueryError<Context>(
ctx,
this.dbId,
this.eventCallbacks,
this.instanceId,
err,
data,
),
async (err: OnQueryErrorErr, data: OnQueryErrorData) => {
const trx = this.getTrxByKmoreQueryId(kmoreQueryId)
await this.doTrxActionOnError(trx)
return callCbOnQueryError({
...opts,
err,
data,
})
},
)
return refTable
return refTable2 as KmoreQueryBuilder
}
protected extRefTableFnPropertyTransacting(
refTable: KmoreQueryBuilder,
ctx: Context | undefined,
): KmoreQueryBuilder {
// eslint-disable-next-line @typescript-eslint/unbound-method
const ts = new Proxy(refTable.transacting, {
apply: (target: KmoreQueryBuilder['transacting'], ctx2: KmoreQueryBuilder, args: [KmoreTransaction]) => {
const [trx] = args
assert(trx?.isTransaction === true, 'trx must be a transaction')
const { kmoreTrxId } = trx
const qid = ctx2.kmoreQueryId as symbol | undefined
if (qid && kmoreTrxId) {
this.trxIdQueryMap.get(kmoreTrxId)?.add(qid)
this.setCtxTrxIdMap(ctx, kmoreTrxId)
}
return Reflect.apply(target, ctx2, args)
},
})
void Object.defineProperty(refTable, 'transacting', {
...defaultPropDescriptor,
value: ts,
})
return refTable as KmoreQueryBuilder
}
protected extRefTableFnPropertyThen(refTable: KmoreQueryBuilder): KmoreQueryBuilder {
// eslint-disable-next-line @typescript-eslint/unbound-method
const pm = new Proxy(refTable.then, {
apply: (target: () => Promise<unknown>, ctx2: KmoreQueryBuilder, args: unknown[]) => {
const qid = ctx2.kmoreQueryId
const trx = this.getTrxByKmoreQueryId(qid)
if (! trx) {
return Reflect.apply(target, ctx2, args)
}
return (Reflect.apply(target, ctx2, args) as Promise<unknown>)
.catch(async (err: unknown) => {
const trx2 = this.getTrxByKmoreQueryId(qid)
await this.doTrxActionOnError(trx2)
return Promise.reject(err)
})
},
})
void Object.defineProperty(refTable, 'then', {
...defaultPropDescriptor,
configurable: true,
value: pm,
})
return refTable as KmoreQueryBuilder
}
protected postProcessResponse(

@@ -264,2 +507,3 @@ result: any,

}
}

@@ -286,2 +530,8 @@

wrapIdentifierCaseConvert?: CaseType | undefined
/**
* Atuo trsaction action (rollback|commit|none) on error (Rejection or Exception),
* @CAUTION **Will always rollback if query error in database even though this value set to 'commit'**
* @default rollback
*/
trxActionOnError?: KmoreTransactionConfig['trxActionOnError']
}

@@ -299,1 +549,7 @@

/**
* kmoreTrxId => Set<kmoreQueryId>
*/
type TrxIdQueryMap = Map<symbol, Set<symbol>>

@@ -9,2 +9,22 @@ /* eslint-disable @typescript-eslint/no-explicit-any */

export type KnexConfig = Knex.Config
export type KmoreTransaction = Knex.Transaction & {
kmoreTrxId: symbol,
/**
* Atuo trsaction action (rollback|commit|none) on error (Rejection or Exception),
* @CAUTION **Will always rollback if query error in database even though this value set to 'commit'**
* @default rollback
*/
trxActionOnError: NonNullable<KmoreTransactionConfig['trxActionOnError']>,
}
export type KmoreTransactionConfig = Knex.TransactionConfig & {
/**
* Atuo trsaction action (rollback|commit|none) on error (Rejection or Exception),
* @CAUTION **Will always rollback if query error in database even though this value set to 'commit'**
* @default rollback
*/
trxActionOnError?: 'commit' | 'rollback' | 'none',
}
// eslint-disable-next-line @typescript-eslint/ban-types
export type KmoreQueryBuilder<TRecord extends {} = any, TResult = any> =
Knex.QueryBuilder<TRecord, TResult> & { kmoreQueryId: symbol }

@@ -49,3 +69,3 @@ export enum EnumClient {

export type TbQueryBuilder<TRecord, Context> = (ctx?: Context) => Knex.QueryBuilder<TRecord, TRecord[]>
export type TbQueryBuilder<TRecord, Context> = (ctx?: Context) => KmoreQueryBuilder<TRecord, TRecord[]>
// export type TbQueryBuilder<TRecord>

@@ -81,2 +101,3 @@ // = <CaseConvert extends CaseType = CaseType.none>(caseConvert?: CaseConvert)

queryUid: string // 'mXxtvuJLHkZI816UZic57'
kmoreQueryId: symbol | undefined
/**

@@ -104,2 +125,3 @@ * @description Note: may keep value of the latest transaction id,

postProcessResponseCaseConvert: CaseType
kmoreQueryId: symbol
}

@@ -106,0 +128,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc