@kikko-land/kikko
Advanced tools
Comparing version 0.4.0 to 0.5.0
# @kikko-land/core | ||
## 0.5.0 | ||
### Minor Changes | ||
- 056a744: Add new fluent api | ||
## 0.4.0 | ||
@@ -4,0 +10,0 @@ |
@@ -1,42 +0,38 @@ | ||
import { sql as S } from "@kikko-land/sql"; | ||
const y = (t, e) => { | ||
if (!t.localState.transactionsState) | ||
import N from "lodash.isequal"; | ||
import { sql as S } from "@kikko-land/boono-sql"; | ||
export * from "@kikko-land/boono-sql"; | ||
const x = (e, r) => { | ||
if (!e.__state.localState.transactionsState.current) | ||
throw new Error("Not in transaction."); | ||
const r = [], n = (a) => (c, o) => { | ||
e(a, c, o); | ||
for (const i of r) | ||
i(); | ||
const t = e.__state.localState.transactionsState.current, i = [], a = (u) => (s, o) => { | ||
if (t.id === o.id) { | ||
r(u, s, t); | ||
for (const c of i) | ||
c(); | ||
} | ||
}; | ||
r.push( | ||
t.sharedState.eventsEmitter.on( | ||
i.push( | ||
e.__state.sharedState.eventsEmitter.on( | ||
"transactionCommitted", | ||
n("committed") | ||
a("committed") | ||
) | ||
), r.push( | ||
t.sharedState.eventsEmitter.on( | ||
), i.push( | ||
e.__state.sharedState.eventsEmitter.on( | ||
"transactionRollbacked", | ||
n("rollbacked") | ||
a("rollbacked") | ||
) | ||
); | ||
}, A = (t, e) => { | ||
y(t, (r, n, a) => { | ||
r === "committed" && e(n, a); | ||
}); | ||
}, L = (t, e) => { | ||
y(t, (r, n, a) => { | ||
r === "rollbacked" && e(n, a); | ||
}); | ||
}; | ||
function T() { | ||
const t = {}; | ||
function Q() { | ||
const e = {}; | ||
return { | ||
async emit(e, ...r) { | ||
const n = t[e] || []; | ||
for (const a of n) | ||
await a(...r); | ||
async emit(r, ...t) { | ||
const i = e[r] || []; | ||
for (const a of i) | ||
await a(...t); | ||
}, | ||
on(e, r) { | ||
return (t[e] = t[e] || []).push(r), () => { | ||
const n = t[e] || []; | ||
t[e] = n.filter((a) => a !== r); | ||
on(r, t) { | ||
return (e[r] = e[r] || []).push(t), () => { | ||
const i = e[r] || []; | ||
e[r] = i.filter((a) => a !== t); | ||
}; | ||
@@ -46,331 +42,504 @@ } | ||
} | ||
class h extends Error { | ||
class g extends Error { | ||
} | ||
class b extends Error { | ||
class E extends Error { | ||
} | ||
const v = (t, e) => ({ | ||
__state: { | ||
subscriptions: [], | ||
value: t, | ||
isStopped: !1, | ||
onStop: [] | ||
}, | ||
get isStopped() { | ||
return this.__state.isStopped; | ||
}, | ||
set value(r) { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${e} is stopped!`); | ||
this.__state.value = r; | ||
for (const n of this.__state.subscriptions) | ||
n(r); | ||
}, | ||
get value() { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${e} is stopped!`); | ||
return this.__state.value; | ||
}, | ||
subscribe(r, n = !0) { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${e} is stopped!`); | ||
return this.__state.subscriptions.push(r), n && r(this.__state.value), () => { | ||
this.__state.subscriptions = this.__state.subscriptions.filter((a) => a !== r); | ||
}; | ||
}, | ||
waitTill(r, n) { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${e} is stopped!`); | ||
return new Promise((c, o) => { | ||
var l; | ||
const i = [], s = () => { | ||
for (const u of i) | ||
u(); | ||
const A = (e, r) => { | ||
const t = r.deduplicate === void 0 ? !0 : r.deduplicate; | ||
return { | ||
__state: { | ||
subscriptions: [], | ||
value: e, | ||
isStopped: !1, | ||
onStop: [] | ||
}, | ||
get isStopped() { | ||
return this.__state.isStopped; | ||
}, | ||
set value(i) { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${r.label} is stopped!`); | ||
if (!(t && N(this.__state.value, i))) { | ||
this.__state.value = i; | ||
for (const a of this.__state.subscriptions) | ||
a(i); | ||
} | ||
}, | ||
get value() { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${r.label} is stopped!`); | ||
return this.__state.value; | ||
}, | ||
subscribe(i, a = !0) { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${r.label} is stopped!`); | ||
let u; | ||
const s = (o) => { | ||
u && u(), u = i(o); | ||
}; | ||
if (i.push( | ||
((l = n == null ? void 0 : n.stopIf) == null ? void 0 : l.subscribe((u) => { | ||
!u || (s(), o( | ||
new b( | ||
`waitUntil for reactiveVar ${e} is stopped due to stop signal` | ||
) | ||
)); | ||
}, !0)) || (() => { | ||
}) | ||
), i.push( | ||
this.subscribe((u) => { | ||
!r(u) || (s(), c()); | ||
}, !0) | ||
), (n == null ? void 0 : n.timeout) === void 0 || (n == null ? void 0 : n.timeout) !== "infinite") { | ||
const u = setTimeout( | ||
() => { | ||
s(), o( | ||
new h( | ||
`waitUntil for reactiveVar ${e} is timed out` | ||
) | ||
); | ||
}, | ||
(n == null ? void 0 : n.timeout) === void 0 ? 5e3 : n.timeout | ||
); | ||
i.push(() => { | ||
clearTimeout(u); | ||
return this.__state.subscriptions.push(s), a && s(this.__state.value), () => { | ||
this.__state.subscriptions = this.__state.subscriptions.filter((o) => o !== s); | ||
}; | ||
}, | ||
waitTill(i, a) { | ||
const u = new E( | ||
`waitUntil for reactiveVar ${r.label} is stopped due to stop signal` | ||
), s = new g( | ||
`waitUntil for reactiveVar ${r.label} is timed out` | ||
), o = new E( | ||
`waitUntil for reactiveVar ${r.label} is stopped due to reactive var stop` | ||
); | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${r.label} is stopped!`); | ||
return new Promise((h, l) => { | ||
var d; | ||
const n = [], m = () => { | ||
for (const f of n) | ||
f(); | ||
}; | ||
if (n.push( | ||
((d = a == null ? void 0 : a.stopIf) == null ? void 0 : d.subscribe((f) => { | ||
!f || (m(), l(u)); | ||
}, !0)) || (() => { | ||
}) | ||
), n.push( | ||
this.subscribe((f) => { | ||
!i(f) || (m(), h()); | ||
}, !0) | ||
), (a == null ? void 0 : a.timeout) === void 0 || typeof (a == null ? void 0 : a.timeout) == "number") { | ||
const f = setTimeout( | ||
() => { | ||
m(), l(s); | ||
}, | ||
(a == null ? void 0 : a.timeout) === void 0 ? 6e4 : a.timeout | ||
); | ||
n.push(() => { | ||
clearTimeout(f); | ||
}); | ||
} | ||
this.__state.onStop.push(() => { | ||
m(), l(o); | ||
}); | ||
} | ||
this.__state.onStop.push(() => { | ||
s(), o( | ||
new b( | ||
`waitUntil for reactiveVar ${e} is stopped due to reactive var stop` | ||
) | ||
); | ||
}); | ||
}); | ||
}, | ||
stop() { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${e} is already stopped!`); | ||
this.__state.subscriptions = []; | ||
for (const r of this.__state.onStop) | ||
r(); | ||
this.__state.isStopped = !0; | ||
} | ||
}), E = (t, e) => { | ||
}, | ||
stop() { | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${r.label} is already stopped!`); | ||
this.__state.subscriptions = []; | ||
for (const i of this.__state.onStop) | ||
i(); | ||
this.__state.isStopped = !0; | ||
} | ||
}; | ||
}, q = (e, r) => { | ||
const { | ||
sharedState: { runningState: r, dbName: n } | ||
} = t; | ||
if (r.value !== "running") | ||
throw new Error(`Failed to start ${e()}, db ${n} is stopping`); | ||
}, p = (t) => t.map((e) => e.preparedQuery); | ||
function w() { | ||
let t = ""; | ||
const e = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", r = e.length; | ||
for (let n = 0; n < 32; n++) | ||
t += e.charAt(Math.floor(Math.random() * r)); | ||
return t; | ||
__state: { | ||
sharedState: { runningState: t, dbName: i } | ||
} | ||
} = e; | ||
if (t.value !== "running") | ||
throw new Error(`Failed to start ${r()}, db ${i} is stopping`); | ||
}, R = (e) => e.map((r) => r.preparedQuery); | ||
function v() { | ||
let e = ""; | ||
const r = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789", t = r.length; | ||
for (let i = 0; i < 32; i++) | ||
e += r.charAt(Math.floor(Math.random() * t)); | ||
return e; | ||
} | ||
const _ = (t) => { | ||
const { current: e, queue: r } = t; | ||
const C = (e) => { | ||
const { current: r, queue: t } = e; | ||
return `current running job: ${JSON.stringify( | ||
e, | ||
r, | ||
null, | ||
2 | ||
)}, queue: ${JSON.stringify(r, null, 2)}`; | ||
}, f = async (t, e) => { | ||
const r = w(), n = { ...e, id: r }, { current: a, queue: c } = t.value; | ||
if (a || c.length > 0) { | ||
const o = t.waitTill((i) => { | ||
var s; | ||
return ((s = i.current) == null ? void 0 : s.id) === r; | ||
)}, queue: ${JSON.stringify(t, null, 2)}`; | ||
}, y = async (e, r) => { | ||
const t = v(), i = { ...r, id: t }, { current: a, queue: u } = e.value; | ||
if (a || u.length > 0) { | ||
const s = e.waitTill((o) => { | ||
var c; | ||
return ((c = o.current) == null ? void 0 : c.id) === t; | ||
}); | ||
t.value = { | ||
queue: [...c, n], | ||
e.value = { | ||
queue: [...u, i], | ||
current: a | ||
}; | ||
try { | ||
await o; | ||
} catch (i) { | ||
throw i instanceof h ? new h( | ||
`Timeout error while job acquire: '${i.message}'. Is it a dead lock? ${_( | ||
t.value | ||
)}, jobToAcquire: ${JSON.stringify(n, null, 2)}` | ||
) : i; | ||
await s; | ||
} catch (o) { | ||
throw o instanceof g ? new g( | ||
`Timeout error while job acquire: '${o.message}'. Is it a dead lock? ${C( | ||
e.value | ||
)}, jobToAcquire: ${JSON.stringify(i, null, 2)}` | ||
) : o; | ||
} | ||
} else | ||
t.value = { | ||
e.value = { | ||
queue: [], | ||
current: n | ||
current: i | ||
}; | ||
return n; | ||
}, m = (t, e) => { | ||
const { current: r, queue: n } = t.value; | ||
if ((r == null ? void 0 : r.id) !== e.id) | ||
return i; | ||
}, b = (e, r) => { | ||
const { current: t, queue: i } = e.value; | ||
if ((t == null ? void 0 : t.id) !== r.id) | ||
throw new Error( | ||
`Can't release job that is not currently running, ${_( | ||
t.value | ||
)}, toRelease: ${JSON.stringify(e, null, 2)}` | ||
`Can't release job that is not currently running, ${C( | ||
e.value | ||
)}, toRelease: ${JSON.stringify(r, null, 2)}` | ||
); | ||
t.value = { queue: n.slice(1), current: n[0] }; | ||
}, I = async (t) => { | ||
e.value = { queue: i.slice(1), current: i[0] }; | ||
}, F = async (e) => { | ||
try { | ||
return t.waitTill( | ||
({ queue: e, current: r }) => e.length === 0 && r === void 0, | ||
return e.waitTill( | ||
({ queue: r, current: t }) => r.length === 0 && t === void 0, | ||
{ timeout: 3e4 } | ||
); | ||
} catch (e) { | ||
throw e instanceof h ? new h( | ||
`Timeout error while awaiting all jobs done: '${e.message}'. Is it a dead lock?` | ||
) : e; | ||
} catch (r) { | ||
throw r instanceof g ? new g( | ||
`Timeout error while awaiting all jobs done: '${r.message}'. Is it a dead lock?` | ||
) : r; | ||
} | ||
}, R = async ({ | ||
dbName: t, | ||
plugins: e, | ||
queriesMiddlewares: r, | ||
dbBackend: n | ||
}) => { | ||
const a = v( | ||
"running", | ||
"runningState" | ||
), c = (await n)({ | ||
dbName: t | ||
}), o = v( | ||
{ | ||
queue: [], | ||
current: void 0 | ||
}, | ||
"jobsState" | ||
), i = { | ||
}, k = ["yellow", "cyan", "magenta"], M = async ({ db: e, queries: r }) => { | ||
var h, l, n; | ||
const { | ||
localState: { transactionsState: t }, | ||
sharedState: { | ||
clientId: w(), | ||
dbBackend: c, | ||
dbName: t, | ||
runningState: a, | ||
eventsEmitter: T(), | ||
jobsState: o, | ||
transactionsState: {} | ||
transactionsState: i, | ||
jobsState: a, | ||
dbBackend: u | ||
}, | ||
localState: { | ||
queriesMiddlewares: r || [], | ||
transactionsState: {} | ||
} | ||
}, s = await f(i.sharedState.jobsState, { | ||
type: "initDb", | ||
name: t | ||
}); | ||
let l = i; | ||
try { | ||
const u = () => i.sharedState.runningState.value; | ||
if (u() !== "running" || (await c.initialize(), u() !== "running")) | ||
return i; | ||
for (const d of e || []) | ||
l = d(l); | ||
} finally { | ||
m(o, s); | ||
} | ||
return await i.sharedState.eventsEmitter.emit("initialized", i), l; | ||
}, x = async (t) => { | ||
t.sharedState.runningState.value = "stopping", await I(t.sharedState.jobsState), await t.sharedState.dbBackend.stop(), t.sharedState.runningState.value = "stopped", queueMicrotask(() => { | ||
t.sharedState.runningState.stop(), t.sharedState.jobsState.stop(); | ||
}); | ||
}, $ = async ({ | ||
dbState: t, | ||
queries: e | ||
}) => { | ||
var l; | ||
const { | ||
localState: { transactionsState: r, suppressLog: n }, | ||
sharedState: { | ||
transactionsState: a, | ||
jobsState: c, | ||
dbBackend: o | ||
} | ||
} = t; | ||
if (r.current || E(t, () => JSON.stringify(e)), r.current && a.current && r.current.id !== a.current.id) | ||
sharedState: s | ||
} = e.__state; | ||
if (t.current || q(e, () => JSON.stringify(r)), t.current && (i == null ? void 0 : i.current) && t.current.id !== i.current.id) | ||
throw new Error( | ||
"Internal error: local running transaction is not the same as shared state transaction" | ||
); | ||
let i; | ||
r.current || (i = await f(c, { | ||
let o; | ||
const c = R(r.map((m) => m.toSql())); | ||
t.current || (o = await y(a, { | ||
type: "runQueries", | ||
queries: e.map((u) => u.toSql()) | ||
queries: r.map((m) => m.toSql()) | ||
})); | ||
const s = { | ||
log: { | ||
suppress: Boolean(n), | ||
transactionId: (l = r.current) == null ? void 0 : l.id | ||
try { | ||
const m = performance.now(), { result: d, performance: f } = await u.execQueries( | ||
c | ||
), I = performance.now(); | ||
if (!e.__state.localState.suppressLog) { | ||
t.current && t.current.id !== s.transactionLoggingState.id && (s.transactionLoggingState.id = t.current.id, s.transactionLoggingState.i++), (h = t == null ? void 0 : t.current) != null && h.id || (s.transactionLoggingState.id = void 0); | ||
const _ = d.map(({ performance: T }, w) => { | ||
const j = [ | ||
T.prepareTime !== void 0 ? `prepareTime=${(T.prepareTime / 1e3).toFixed(4)}` : "", | ||
T.execTime !== void 0 ? `execTime=${(T.execTime / 1e3).toFixed(4)}` : "", | ||
T.freeTime !== void 0 ? `freeTime=${(T.freeTime / 1e3).toFixed(4)}` : "" | ||
].filter(($) => $.length !== 0).join(" "); | ||
return "{" + [c[w].text.slice(0, 1e3), j].filter(($) => $.length !== 0).join(" ") + "}"; | ||
}).join(` | ||
`), p = `%c[${e.__state.sharedState.dbName}] ` + [ | ||
(l = t.current) != null && l.id ? `[tr_id=${(n = t.current) == null ? void 0 : n.id.substring(0, 6)}]` : "", | ||
_, | ||
(f == null ? void 0 : f.sendTime) !== void 0 ? `sendTime=${(f.sendTime / 1e3).toFixed(4)}` : "", | ||
(f == null ? void 0 : f.receiveTime) !== void 0 ? `receiveTime=${(f.receiveTime / 1e3).toFixed(4)}` : "", | ||
`totalTime=${((I - m) / 1e3).toFixed(4)}` | ||
].filter((T) => T.length !== 0).join(" "); | ||
console.log( | ||
p, | ||
`color: ${s.transactionLoggingState.id ? k[s.transactionLoggingState.i % k.length] : "white"}` | ||
); | ||
} | ||
}; | ||
try { | ||
const u = await o.execQueries( | ||
p(e.map((d) => d.toSql())), | ||
s | ||
); | ||
return { dbState: t, result: u, queries: e }; | ||
if (t.current && (i == null ? void 0 : i.current) && t.current.id === i.current.id) { | ||
const _ = i.performance; | ||
d.some((p) => p.performance.execTime !== void 0) && (_.execTime === void 0 && (_.execTime = 0), _.execTime += d.reduce( | ||
(p, T) => { | ||
var w; | ||
return p + ((w = T.performance.execTime) != null ? w : 0); | ||
}, | ||
0 | ||
)), d.some((p) => p.performance.freeTime !== void 0) && (_.freeTime === void 0 && (_.freeTime = 0), _.freeTime += d.reduce( | ||
(p, T) => { | ||
var w; | ||
return p + ((w = T.performance.freeTime) != null ? w : 0); | ||
}, | ||
0 | ||
)), d.some((p) => p.performance.prepareTime !== void 0) && (_.prepareTime === void 0 && (_.prepareTime = 0), _.prepareTime += d.reduce( | ||
(p, T) => { | ||
var w; | ||
return p + ((w = T.performance.prepareTime) != null ? w : 0); | ||
}, | ||
0 | ||
)), f.sendTime && (_.sendTime || (_.sendTime = 0), _.sendTime += f.sendTime), f.receiveTime && (_.receiveTime || (_.receiveTime = 0), _.receiveTime += f.receiveTime); | ||
} | ||
return { db: e, result: d, performance: f, queries: r }; | ||
} finally { | ||
i && m(c, i); | ||
o && b(a, o); | ||
} | ||
}, q = async (t, e) => { | ||
const r = [ | ||
...t.localState.queriesMiddlewares, | ||
$ | ||
}, V = async (e, r) => { | ||
const t = [ | ||
...e.__state.localState.queriesMiddlewares, | ||
M | ||
].reverse(); | ||
let n = (a) => Promise.resolve(a); | ||
for (const a of r) { | ||
const c = n; | ||
n = (o) => a({ ...o, next: c }); | ||
let i = (a) => Promise.resolve(a); | ||
for (const a of t) { | ||
const u = i; | ||
i = (s) => a({ ...s, next: u }); | ||
} | ||
return (await n({ | ||
dbState: t, | ||
return await i({ | ||
db: e, | ||
result: [], | ||
queries: e.map((a) => a.toSql()) | ||
})).result; | ||
}, D = async (t, e) => (await q(t, [e]))[0] || [], M = (t, e) => e({ | ||
...t, | ||
localState: { ...t.localState, suppressLog: !0 } | ||
}), O = (t) => ({ ...t, localState: { ...t.localState, suppressLog: !0 } }), g = async (t, e, r, n) => { | ||
performance: { | ||
sendTime: void 0, | ||
receiveTime: void 0, | ||
totalTime: 0 | ||
}, | ||
queries: r.map((a) => a.toSql()) | ||
}); | ||
}, L = (e, r, t) => { | ||
if (e.__state.localState.suppressLog) | ||
return; | ||
const i = [ | ||
t.prepareTime === void 0 ? "" : `prepareTime=${(t.prepareTime / 1e3).toFixed(4)}`, | ||
t.execTime === void 0 ? "" : `execTime=${(t.execTime / 1e3).toFixed(4)}`, | ||
t.freeTime === void 0 ? "" : `freeTime=${(t.freeTime / 1e3).toFixed(4)}`, | ||
t.sendTime === void 0 ? "" : `sendTime=${(t.sendTime / 1e3).toFixed(4)}`, | ||
t.receiveTime === void 0 ? "" : `receiveTime=${(t.receiveTime / 1e3).toFixed(4)}`, | ||
`totalTime=${(t.totalTime / 1e3).toFixed(4)}` | ||
].filter((a) => a.length !== 0).join(" "); | ||
console.log( | ||
`%c[${e.__state.sharedState.dbName}][tr_id=${r.slice( | ||
0, | ||
6 | ||
)}] Transaction finished with ${i}`, | ||
"color: #fff; background-color: #1da1f2; padding: 2px 4px; border-radius: 2px" | ||
); | ||
}, B = async (e, r, t, i) => { | ||
const { | ||
localState: { transactionsState: a }, | ||
sharedState: { | ||
transactionsState: c, | ||
eventsEmitter: o, | ||
dbBackend: i | ||
} | ||
} = t; | ||
if (a.current && c.current) { | ||
if (a.current.id !== c.current.id) | ||
sharedState: { transactionsState: u, eventsEmitter: s }, | ||
sharedState: o | ||
} = e.__state; | ||
if (a.current && (u == null ? void 0 : u.current)) { | ||
if (a.current.id !== (u == null ? void 0 : u.current.id)) | ||
throw new Error( | ||
"Internal error: local running transaction is not the same as shared state transaction" | ||
); | ||
return await r(t); | ||
return await t(e); | ||
} | ||
E(t, () => "transaction"); | ||
const s = { | ||
id: w() | ||
q(e, () => "transaction"); | ||
const c = { | ||
id: v(), | ||
type: "async" | ||
}; | ||
t = { | ||
...t, | ||
localState: { | ||
...t.localState, | ||
transactionsState: { current: s } | ||
e = { | ||
...e, | ||
__state: { | ||
...e.__state, | ||
localState: { | ||
...e.__state.localState, | ||
transactionsState: { current: c } | ||
} | ||
} | ||
}; | ||
const l = await f(t.sharedState.jobsState, { | ||
const h = await y(e.__state.sharedState.jobsState, { | ||
type: "runTransaction", | ||
transaction: s, | ||
label: n == null ? void 0 : n.label | ||
}), u = { | ||
log: { | ||
suppress: Boolean(t.localState.suppressLog), | ||
transactionId: s.id | ||
transaction: c, | ||
label: i == null ? void 0 : i.label | ||
}), l = performance.now(), n = { | ||
current: c, | ||
performance: { | ||
prepareTime: 0, | ||
execTime: 0, | ||
freeTime: 0, | ||
sendTime: 0, | ||
receiveTime: 0, | ||
totalTime: 0 | ||
} | ||
}; | ||
o.transactionsState = n; | ||
try { | ||
c.current = s, await o.emit("transactionWillStart", t, s), await i.execQueries( | ||
p([S`BEGIN ${S.raw(e)} TRANSACTION;`]), | ||
u | ||
), await o.emit("transactionStarted", t, s); | ||
await s.emit("transactionWillStart", e, c), await e.runQuery( | ||
S`BEGIN ${S.raw(r.toUpperCase())} TRANSACTION;` | ||
), await s.emit("transactionStarted", e, c); | ||
try { | ||
const d = await r(t); | ||
return await o.emit("transactionWillCommit", t, s), await i.execQueries(p([S`COMMIT`]), u), await o.emit("transactionCommitted", t, s), d; | ||
} catch (d) { | ||
throw console.error("Rollback transaction", d), await o.emit("transactionWillRollback", t, s), await i.execQueries(p([S`ROLLBACK`]), u), await o.emit("transactionRollbacked", t, s), d; | ||
const m = await t(e); | ||
return await s.emit("transactionWillCommit", e, c), await e.runQuery(S`COMMIT`), await s.emit("transactionCommitted", e, c), m; | ||
} catch (m) { | ||
throw console.error("Rollback transaction", m), await s.emit("transactionWillRollback", e, c), await e.runQuery(S`ROLLBACK`), await s.emit("transactionRollbacked", e, c), m; | ||
} | ||
} finally { | ||
m(t.sharedState.jobsState, l); | ||
n.performance.totalTime = performance.now() - l, L(e, c.id, n.performance), b(e.__state.sharedState.jobsState, h); | ||
} | ||
}, k = (t, e, r) => g(t, "DEFERRED", e, r), Q = (t, e, r) => g(t, "IMMEDIATE", e, r), V = (t, e, r) => g(t, "EXCLUSIVE", e, r), B = (t, e, r) => k(t, e, r); | ||
}, D = () => ({ | ||
__state: { | ||
queries: [] | ||
}, | ||
addQuery(e) { | ||
this.__state.queries.push(e); | ||
} | ||
}), J = async (e, r, t, i) => { | ||
const { | ||
localState: { transactionsState: a }, | ||
sharedState: { eventsEmitter: u }, | ||
sharedState: s | ||
} = e.__state; | ||
if (a.current) | ||
throw new Error( | ||
"You are running atomic transaction inside of a transaction. Consider moving atomic transaction call to runAfterTransaction callback." | ||
); | ||
const o = await (async () => { | ||
if (Array.isArray(t)) | ||
return t; | ||
{ | ||
const d = D(); | ||
return await t(d), d.__state.queries; | ||
} | ||
})(), c = { | ||
id: v(), | ||
type: "atomic" | ||
}, h = await y(e.__state.sharedState.jobsState, { | ||
type: "runAtomicTransaction", | ||
transaction: c, | ||
label: i == null ? void 0 : i.label | ||
}), l = { | ||
current: c, | ||
performance: { | ||
prepareTime: 0, | ||
execTime: 0, | ||
freeTime: 0, | ||
sendTime: 0, | ||
receiveTime: 0, | ||
totalTime: 0 | ||
} | ||
}; | ||
s.transactionsState = l, e = { | ||
...e, | ||
__state: { | ||
...e.__state, | ||
localState: { | ||
...e.__state.localState, | ||
transactionsState: { current: c } | ||
} | ||
} | ||
}; | ||
const n = performance.now(), m = [ | ||
S`BEGIN ${S.raw(r.toUpperCase())} TRANSACTION`, | ||
...o, | ||
S`COMMIT` | ||
]; | ||
try { | ||
await u.emit("transactionWillStart", e, c), await e.runQueries(m), await u.emit("transactionCommitted", e, c); | ||
} catch (d) { | ||
throw console.error("Rollback transaction", d), await u.emit("transactionWillRollback", e, c), await e.runQuery(S`ROLLBACK`), await u.emit("transactionRollbacked", e, c), d; | ||
} finally { | ||
l.performance.totalTime = performance.now() - n, L(e, c.id, l.performance), b(e.__state.sharedState.jobsState, h); | ||
} | ||
}, z = async ({ | ||
dbName: e, | ||
plugins: r, | ||
queriesMiddlewares: t, | ||
dbBackend: i | ||
}) => { | ||
const a = A( | ||
"running", | ||
{ label: "runningState" } | ||
), u = (await i)({ | ||
dbName: e | ||
}), s = A( | ||
{ | ||
queue: [], | ||
current: void 0 | ||
}, | ||
{ label: "jobsState" } | ||
), o = { | ||
__state: { | ||
sharedState: { | ||
clientId: v(), | ||
dbBackend: u, | ||
dbName: e, | ||
runningState: a, | ||
eventsEmitter: Q(), | ||
jobsState: s, | ||
transactionLoggingState: { | ||
id: void 0, | ||
i: 0 | ||
} | ||
}, | ||
localState: { | ||
queriesMiddlewares: t || [], | ||
transactionsState: {} | ||
} | ||
}, | ||
runInTransaction(l, n) { | ||
return B(this, (n == null ? void 0 : n.type) || "deferred", l, { | ||
label: n == null ? void 0 : n.label | ||
}); | ||
}, | ||
async runAtomicTransaction(l, n) { | ||
return await J( | ||
this, | ||
(n == null ? void 0 : n.type) || "deferred", | ||
l, | ||
n | ||
); | ||
}, | ||
async runQueries(l) { | ||
return (await V(this, l)).result.map(({ rows: m }) => m); | ||
}, | ||
async runQuery(l) { | ||
return (await this.runQueries([l]))[0]; | ||
}, | ||
runAfterTransactionCommitted(l) { | ||
return x(this, (n, m, d) => { | ||
n === "committed" && l(m, d); | ||
}); | ||
}, | ||
runAfterTransactionRollbacked(l) { | ||
x(o, (n, m, d) => { | ||
n === "rollbacked" && l(m, d); | ||
}); | ||
} | ||
}, c = await y(o.__state.sharedState.jobsState, { | ||
type: "initDb", | ||
name: e | ||
}); | ||
let h = o; | ||
try { | ||
const l = () => o.__state.sharedState.runningState.value; | ||
if (l() !== "running" || (await u.initialize(), l() !== "running")) | ||
return o; | ||
for (const n of r || []) | ||
h = n(h); | ||
} finally { | ||
b(s, c); | ||
} | ||
return await o.__state.sharedState.eventsEmitter.emit("initialized", o), h; | ||
}, G = async (e) => { | ||
e.__state.sharedState.runningState.value = "stopping", await F(e.__state.sharedState.jobsState), await e.__state.sharedState.dbBackend.stop(), e.__state.sharedState.runningState.value = "stopped", queueMicrotask(() => { | ||
e.__state.sharedState.runningState.stop(), e.__state.sharedState.jobsState.stop(); | ||
}); | ||
}, K = (e, r) => r({ | ||
...e, | ||
__state: { | ||
...e.__state, | ||
localState: { | ||
...e.__state.localState, | ||
suppressLog: !0 | ||
} | ||
} | ||
}), Y = (e) => ({ | ||
...e, | ||
__state: { | ||
...e.__state, | ||
localState: { ...e.__state.localState, suppressLog: !0 } | ||
} | ||
}); | ||
export { | ||
b as StoppedError, | ||
h as TimeoutError, | ||
R as initDbClient, | ||
w as makeId, | ||
v as reactiveVar, | ||
A as runAfterTransactionCommitted, | ||
L as runAfterTransactionRollbacked, | ||
k as runInDeferredTransaction, | ||
V as runInExclusiveTransaction, | ||
Q as runInImmediateTransaction, | ||
B as runInTransaction, | ||
q as runQueries, | ||
D as runQuery, | ||
x as stopDb, | ||
M as suppressLog, | ||
O as withSuppressedLog | ||
E as StoppedError, | ||
g as TimeoutError, | ||
z as initDbClient, | ||
v as makeId, | ||
A as reactiveVar, | ||
G as stopDb, | ||
K as suppressLog, | ||
Y as withSuppressedLog | ||
}; | ||
//# sourceMappingURL=index.es.js.map |
@@ -1,2 +0,3 @@ | ||
(function(s,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("@kikko-land/sql")):typeof define=="function"&&define.amd?define(["exports","@kikko-land/sql"],h):(s=typeof globalThis<"u"?globalThis:s||self,h(s.core={},s.sql))})(this,function(s,h){"use strict";const T=(t,e)=>{if(!t.localState.transactionsState)throw new Error("Not in transaction.");const n=[],r=a=>(u,c)=>{e(a,u,c);for(const i of n)i()};n.push(t.sharedState.eventsEmitter.on("transactionCommitted",r("committed"))),n.push(t.sharedState.eventsEmitter.on("transactionRollbacked",r("rollbacked")))},q=(t,e)=>{T(t,(n,r,a)=>{n==="committed"&&e(r,a)})},$=(t,e)=>{T(t,(n,r,a)=>{n==="rollbacked"&&e(r,a)})};function C(){const t={};return{async emit(e,...n){const r=t[e]||[];for(const a of r)await a(...n)},on(e,n){return(t[e]=t[e]||[]).push(n),()=>{const r=t[e]||[];t[e]=r.filter(a=>a!==n)}}}}class f extends Error{}class m extends Error{}const g=(t,e)=>({__state:{subscriptions:[],value:t,isStopped:!1,onStop:[]},get isStopped(){return this.__state.isStopped},set value(n){if(this.isStopped)throw new Error(`reactiveVar ${e} is stopped!`);this.__state.value=n;for(const r of this.__state.subscriptions)r(n)},get value(){if(this.isStopped)throw new Error(`reactiveVar ${e} is stopped!`);return this.__state.value},subscribe(n,r=!0){if(this.isStopped)throw new Error(`reactiveVar ${e} is stopped!`);return this.__state.subscriptions.push(n),r&&n(this.__state.value),()=>{this.__state.subscriptions=this.__state.subscriptions.filter(a=>a!==n)}},waitTill(n,r){if(this.isStopped)throw new Error(`reactiveVar ${e} is stopped!`);return new Promise((u,c)=>{var d;const i=[],o=()=>{for(const l of i)l()};if(i.push(((d=r==null?void 0:r.stopIf)==null?void 0:d.subscribe(l=>{!l||(o(),c(new m(`waitUntil for reactiveVar ${e} is stopped due to stop signal`)))},!0))||(()=>{})),i.push(this.subscribe(l=>{!n(l)||(o(),u())},!0)),(r==null?void 0:r.timeout)===void 0||(r==null?void 0:r.timeout)!=="infinite"){const l=setTimeout(()=>{o(),c(new f(`waitUntil for reactiveVar ${e} is timed out`))},(r==null?void 0:r.timeout)===void 0?5e3:r.timeout);i.push(()=>{clearTimeout(l)})}this.__state.onStop.push(()=>{o(),c(new m(`waitUntil for reactiveVar ${e} is stopped due to reactive var stop`))})})},stop(){if(this.isStopped)throw new Error(`reactiveVar ${e} is already stopped!`);this.__state.subscriptions=[];for(const n of this.__state.onStop)n();this.__state.isStopped=!0}}),E=(t,e)=>{const{sharedState:{runningState:n,dbName:r}}=t;if(n.value!=="running")throw new Error(`Failed to start ${e()}, db ${r} is stopping`)},w=t=>t.map(e=>e.preparedQuery);function p(){let t="";const e="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",n=e.length;for(let r=0;r<32;r++)t+=e.charAt(Math.floor(Math.random()*n));return t}const I=t=>{const{current:e,queue:n}=t;return`current running job: ${JSON.stringify(e,null,2)}, queue: ${JSON.stringify(n,null,2)}`},b=async(t,e)=>{const n=p(),r={...e,id:n},{current:a,queue:u}=t.value;if(a||u.length>0){const c=t.waitTill(i=>{var o;return((o=i.current)==null?void 0:o.id)===n});t.value={queue:[...u,r],current:a};try{await c}catch(i){throw i instanceof f?new f(`Timeout error while job acquire: '${i.message}'. Is it a dead lock? ${I(t.value)}, jobToAcquire: ${JSON.stringify(r,null,2)}`):i}}else t.value={queue:[],current:r};return r},v=(t,e)=>{const{current:n,queue:r}=t.value;if((n==null?void 0:n.id)!==e.id)throw new Error(`Can't release job that is not currently running, ${I(t.value)}, toRelease: ${JSON.stringify(e,null,2)}`);t.value={queue:r.slice(1),current:r[0]}},A=async t=>{try{return t.waitTill(({queue:e,current:n})=>e.length===0&&n===void 0,{timeout:3e4})}catch(e){throw e instanceof f?new f(`Timeout error while awaiting all jobs done: '${e.message}'. Is it a dead lock?`):e}},L=async({dbName:t,plugins:e,queriesMiddlewares:n,dbBackend:r})=>{const a=g("running","runningState"),u=(await r)({dbName:t}),c=g({queue:[],current:void 0},"jobsState"),i={sharedState:{clientId:p(),dbBackend:u,dbName:t,runningState:a,eventsEmitter:C(),jobsState:c,transactionsState:{}},localState:{queriesMiddlewares:n||[],transactionsState:{}}},o=await b(i.sharedState.jobsState,{type:"initDb",name:t});let d=i;try{const l=()=>i.sharedState.runningState.value;if(l()!=="running"||(await u.initialize(),l()!=="running"))return i;for(const S of e||[])d=S(d)}finally{v(c,o)}return await i.sharedState.eventsEmitter.emit("initialized",i),d},D=async t=>{t.sharedState.runningState.value="stopping",await A(t.sharedState.jobsState),await t.sharedState.dbBackend.stop(),t.sharedState.runningState.value="stopped",queueMicrotask(()=>{t.sharedState.runningState.stop(),t.sharedState.jobsState.stop()})},R=async({dbState:t,queries:e})=>{var d;const{localState:{transactionsState:n,suppressLog:r},sharedState:{transactionsState:a,jobsState:u,dbBackend:c}}=t;if(n.current||E(t,()=>JSON.stringify(e)),n.current&&a.current&&n.current.id!==a.current.id)throw new Error("Internal error: local running transaction is not the same as shared state transaction");let i;n.current||(i=await b(u,{type:"runQueries",queries:e.map(l=>l.toSql())}));const o={log:{suppress:Boolean(r),transactionId:(d=n.current)==null?void 0:d.id}};try{const l=await c.execQueries(w(e.map(S=>S.toSql())),o);return{dbState:t,result:l,queries:e}}finally{i&&v(u,i)}},_=async(t,e)=>{const n=[...t.localState.queriesMiddlewares,R].reverse();let r=a=>Promise.resolve(a);for(const a of n){const u=r;r=c=>a({...c,next:u})}return(await r({dbState:t,result:[],queries:e.map(a=>a.toSql())})).result},M=async(t,e)=>(await _(t,[e]))[0]||[],Q=(t,e)=>e({...t,localState:{...t.localState,suppressLog:!0}}),O=t=>({...t,localState:{...t.localState,suppressLog:!0}}),y=async(t,e,n,r)=>{const{localState:{transactionsState:a},sharedState:{transactionsState:u,eventsEmitter:c,dbBackend:i}}=t;if(a.current&&u.current){if(a.current.id!==u.current.id)throw new Error("Internal error: local running transaction is not the same as shared state transaction");return await n(t)}E(t,()=>"transaction");const o={id:p()};t={...t,localState:{...t.localState,transactionsState:{current:o}}};const d=await b(t.sharedState.jobsState,{type:"runTransaction",transaction:o,label:r==null?void 0:r.label}),l={log:{suppress:Boolean(t.localState.suppressLog),transactionId:o.id}};try{u.current=o,await c.emit("transactionWillStart",t,o),await i.execQueries(w([h.sql`BEGIN ${h.sql.raw(e)} TRANSACTION;`]),l),await c.emit("transactionStarted",t,o);try{const S=await n(t);return await c.emit("transactionWillCommit",t,o),await i.execQueries(w([h.sql`COMMIT`]),l),await c.emit("transactionCommitted",t,o),S}catch(S){throw console.error("Rollback transaction",S),await c.emit("transactionWillRollback",t,o),await i.execQueries(w([h.sql`ROLLBACK`]),l),await c.emit("transactionRollbacked",t,o),S}}finally{v(t.sharedState.jobsState,d)}},k=(t,e,n)=>y(t,"DEFERRED",e,n),V=(t,e,n)=>y(t,"IMMEDIATE",e,n),j=(t,e,n)=>y(t,"EXCLUSIVE",e,n),B=(t,e,n)=>k(t,e,n);s.StoppedError=m,s.TimeoutError=f,s.initDbClient=L,s.makeId=p,s.reactiveVar=g,s.runAfterTransactionCommitted=q,s.runAfterTransactionRollbacked=$,s.runInDeferredTransaction=k,s.runInExclusiveTransaction=j,s.runInImmediateTransaction=V,s.runInTransaction=B,s.runQueries=_,s.runQuery=M,s.stopDb=D,s.suppressLog=Q,s.withSuppressedLog=O,Object.defineProperties(s,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
(function(_,y){typeof exports=="object"&&typeof module<"u"?y(exports,require("lodash.isequal"),require("@kikko-land/boono-sql")):typeof define=="function"&&define.amd?define(["exports","lodash.isequal","@kikko-land/boono-sql"],y):(_=typeof globalThis<"u"?globalThis:_||self,y(_.core={},_.isEqual,_.boonoSql))})(this,function(_,y,S){"use strict";const N=(e=>e&&typeof e=="object"&&"default"in e?e:{default:e})(y),A=(e,i)=>{if(!e.__state.localState.transactionsState.current)throw new Error("Not in transaction.");const t=e.__state.localState.transactionsState.current,r=[],a=u=>(s,o)=>{if(t.id===o.id){i(u,s,t);for(const c of r)c()}};r.push(e.__state.sharedState.eventsEmitter.on("transactionCommitted",a("committed"))),r.push(e.__state.sharedState.eventsEmitter.on("transactionRollbacked",a("rollbacked")))};function Q(){const e={};return{async emit(i,...t){const r=e[i]||[];for(const a of r)await a(...t)},on(i,t){return(e[i]=e[i]||[]).push(t),()=>{const r=e[i]||[];e[i]=r.filter(a=>a!==t)}}}}class v extends Error{}class k extends Error{}const x=(e,i)=>{const t=i.deduplicate===void 0?!0:i.deduplicate;return{__state:{subscriptions:[],value:e,isStopped:!1,onStop:[]},get isStopped(){return this.__state.isStopped},set value(r){if(this.isStopped)throw new Error(`reactiveVar ${i.label} is stopped!`);if(!(t&&N.default(this.__state.value,r))){this.__state.value=r;for(const a of this.__state.subscriptions)a(r)}},get value(){if(this.isStopped)throw new Error(`reactiveVar ${i.label} is stopped!`);return this.__state.value},subscribe(r,a=!0){if(this.isStopped)throw new Error(`reactiveVar ${i.label} is stopped!`);let u;const s=o=>{u&&u(),u=r(o)};return this.__state.subscriptions.push(s),a&&s(this.__state.value),()=>{this.__state.subscriptions=this.__state.subscriptions.filter(o=>o!==s)}},waitTill(r,a){const u=new k(`waitUntil for reactiveVar ${i.label} is stopped due to stop signal`),s=new v(`waitUntil for reactiveVar ${i.label} is timed out`),o=new k(`waitUntil for reactiveVar ${i.label} is stopped due to reactive var stop`);if(this.isStopped)throw new Error(`reactiveVar ${i.label} is stopped!`);return new Promise((w,l)=>{var m;const n=[],d=()=>{for(const f of n)f()};if(n.push(((m=a==null?void 0:a.stopIf)==null?void 0:m.subscribe(f=>{!f||(d(),l(u))},!0))||(()=>{})),n.push(this.subscribe(f=>{!r(f)||(d(),w())},!0)),(a==null?void 0:a.timeout)===void 0||typeof(a==null?void 0:a.timeout)=="number"){const f=setTimeout(()=>{d(),l(s)},(a==null?void 0:a.timeout)===void 0?6e4:a.timeout);n.push(()=>{clearTimeout(f)})}this.__state.onStop.push(()=>{d(),l(o)})})},stop(){if(this.isStopped)throw new Error(`reactiveVar ${i.label} is already stopped!`);this.__state.subscriptions=[];for(const r of this.__state.onStop)r();this.__state.isStopped=!0}}},L=(e,i)=>{const{__state:{sharedState:{runningState:t,dbName:r}}}=e;if(t.value!=="running")throw new Error(`Failed to start ${i()}, db ${r} is stopping`)},R=e=>e.map(i=>i.preparedQuery);function b(){let e="";const i="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",t=i.length;for(let r=0;r<32;r++)e+=i.charAt(Math.floor(Math.random()*t));return e}const C=e=>{const{current:i,queue:t}=e;return`current running job: ${JSON.stringify(i,null,2)}, queue: ${JSON.stringify(t,null,2)}`},$=async(e,i)=>{const t=b(),r={...i,id:t},{current:a,queue:u}=e.value;if(a||u.length>0){const s=e.waitTill(o=>{var c;return((c=o.current)==null?void 0:c.id)===t});e.value={queue:[...u,r],current:a};try{await s}catch(o){throw o instanceof v?new v(`Timeout error while job acquire: '${o.message}'. Is it a dead lock? ${C(e.value)}, jobToAcquire: ${JSON.stringify(r,null,2)}`):o}}else e.value={queue:[],current:r};return r},E=(e,i)=>{const{current:t,queue:r}=e.value;if((t==null?void 0:t.id)!==i.id)throw new Error(`Can't release job that is not currently running, ${C(e.value)}, toRelease: ${JSON.stringify(i,null,2)}`);e.value={queue:r.slice(1),current:r[0]}},F=async e=>{try{return e.waitTill(({queue:i,current:t})=>i.length===0&&t===void 0,{timeout:3e4})}catch(i){throw i instanceof v?new v(`Timeout error while awaiting all jobs done: '${i.message}'. Is it a dead lock?`):i}},j=["yellow","cyan","magenta"],D=async({db:e,queries:i})=>{var w,l,n;const{localState:{transactionsState:t},sharedState:{transactionsState:r,jobsState:a,dbBackend:u},sharedState:s}=e.__state;if(t.current||L(e,()=>JSON.stringify(i)),t.current&&(r==null?void 0:r.current)&&t.current.id!==r.current.id)throw new Error("Internal error: local running transaction is not the same as shared state transaction");let o;const c=R(i.map(d=>d.toSql()));t.current||(o=await $(a,{type:"runQueries",queries:i.map(d=>d.toSql())}));try{const d=performance.now(),{result:m,performance:f}=await u.execQueries(c),G=performance.now();if(!e.__state.localState.suppressLog){t.current&&t.current.id!==s.transactionLoggingState.id&&(s.transactionLoggingState.id=t.current.id,s.transactionLoggingState.i++),(w=t==null?void 0:t.current)!=null&&w.id||(s.transactionLoggingState.id=void 0);const T=m.map(({performance:p},g)=>{const K=[p.prepareTime!==void 0?`prepareTime=${(p.prepareTime/1e3).toFixed(4)}`:"",p.execTime!==void 0?`execTime=${(p.execTime/1e3).toFixed(4)}`:"",p.freeTime!==void 0?`freeTime=${(p.freeTime/1e3).toFixed(4)}`:""].filter(q=>q.length!==0).join(" ");return"{"+[c[g].text.slice(0,1e3),K].filter(q=>q.length!==0).join(" ")+"}"}).join(` | ||
`),h=`%c[${e.__state.sharedState.dbName}] `+[(l=t.current)!=null&&l.id?`[tr_id=${(n=t.current)==null?void 0:n.id.substring(0,6)}]`:"",T,(f==null?void 0:f.sendTime)!==void 0?`sendTime=${(f.sendTime/1e3).toFixed(4)}`:"",(f==null?void 0:f.receiveTime)!==void 0?`receiveTime=${(f.receiveTime/1e3).toFixed(4)}`:"",`totalTime=${((G-d)/1e3).toFixed(4)}`].filter(p=>p.length!==0).join(" ");console.log(h,`color: ${s.transactionLoggingState.id?j[s.transactionLoggingState.i%j.length]:"white"}`)}if(t.current&&(r==null?void 0:r.current)&&t.current.id===r.current.id){const T=r.performance;m.some(h=>h.performance.execTime!==void 0)&&(T.execTime===void 0&&(T.execTime=0),T.execTime+=m.reduce((h,p)=>{var g;return h+((g=p.performance.execTime)!=null?g:0)},0)),m.some(h=>h.performance.freeTime!==void 0)&&(T.freeTime===void 0&&(T.freeTime=0),T.freeTime+=m.reduce((h,p)=>{var g;return h+((g=p.performance.freeTime)!=null?g:0)},0)),m.some(h=>h.performance.prepareTime!==void 0)&&(T.prepareTime===void 0&&(T.prepareTime=0),T.prepareTime+=m.reduce((h,p)=>{var g;return h+((g=p.performance.prepareTime)!=null?g:0)},0)),f.sendTime&&(T.sendTime||(T.sendTime=0),T.sendTime+=f.sendTime),f.receiveTime&&(T.receiveTime||(T.receiveTime=0),T.receiveTime+=f.receiveTime)}return{db:e,result:m,performance:f,queries:i}}finally{o&&E(a,o)}},M=async(e,i)=>{const t=[...e.__state.localState.queriesMiddlewares,D].reverse();let r=a=>Promise.resolve(a);for(const a of t){const u=r;r=s=>a({...s,next:u})}return await r({db:e,result:[],performance:{sendTime:void 0,receiveTime:void 0,totalTime:0},queries:i.map(a=>a.toSql())})},I=(e,i,t)=>{if(e.__state.localState.suppressLog)return;const r=[t.prepareTime===void 0?"":`prepareTime=${(t.prepareTime/1e3).toFixed(4)}`,t.execTime===void 0?"":`execTime=${(t.execTime/1e3).toFixed(4)}`,t.freeTime===void 0?"":`freeTime=${(t.freeTime/1e3).toFixed(4)}`,t.sendTime===void 0?"":`sendTime=${(t.sendTime/1e3).toFixed(4)}`,t.receiveTime===void 0?"":`receiveTime=${(t.receiveTime/1e3).toFixed(4)}`,`totalTime=${(t.totalTime/1e3).toFixed(4)}`].filter(a=>a.length!==0).join(" ");console.log(`%c[${e.__state.sharedState.dbName}][tr_id=${i.slice(0,6)}] Transaction finished with ${r}`,"color: #fff; background-color: #1da1f2; padding: 2px 4px; border-radius: 2px")},V=async(e,i,t,r)=>{const{localState:{transactionsState:a},sharedState:{transactionsState:u,eventsEmitter:s},sharedState:o}=e.__state;if(a.current&&(u==null?void 0:u.current)){if(a.current.id!==(u==null?void 0:u.current.id))throw new Error("Internal error: local running transaction is not the same as shared state transaction");return await t(e)}L(e,()=>"transaction");const c={id:b(),type:"async"};e={...e,__state:{...e.__state,localState:{...e.__state.localState,transactionsState:{current:c}}}};const w=await $(e.__state.sharedState.jobsState,{type:"runTransaction",transaction:c,label:r==null?void 0:r.label}),l=performance.now(),n={current:c,performance:{prepareTime:0,execTime:0,freeTime:0,sendTime:0,receiveTime:0,totalTime:0}};o.transactionsState=n;try{await s.emit("transactionWillStart",e,c),await e.runQuery(S.sql`BEGIN ${S.sql.raw(i.toUpperCase())} TRANSACTION;`),await s.emit("transactionStarted",e,c);try{const d=await t(e);return await s.emit("transactionWillCommit",e,c),await e.runQuery(S.sql`COMMIT`),await s.emit("transactionCommitted",e,c),d}catch(d){throw console.error("Rollback transaction",d),await s.emit("transactionWillRollback",e,c),await e.runQuery(S.sql`ROLLBACK`),await s.emit("transactionRollbacked",e,c),d}}finally{n.performance.totalTime=performance.now()-l,I(e,c.id,n.performance),E(e.__state.sharedState.jobsState,w)}},B=()=>({__state:{queries:[]},addQuery(e){this.__state.queries.push(e)}}),J=async(e,i,t,r)=>{const{localState:{transactionsState:a},sharedState:{eventsEmitter:u},sharedState:s}=e.__state;if(a.current)throw new Error("You are running atomic transaction inside of a transaction. Consider moving atomic transaction call to runAfterTransaction callback.");const o=await(async()=>{if(Array.isArray(t))return t;{const m=B();return await t(m),m.__state.queries}})(),c={id:b(),type:"atomic"},w=await $(e.__state.sharedState.jobsState,{type:"runAtomicTransaction",transaction:c,label:r==null?void 0:r.label}),l={current:c,performance:{prepareTime:0,execTime:0,freeTime:0,sendTime:0,receiveTime:0,totalTime:0}};s.transactionsState=l,e={...e,__state:{...e.__state,localState:{...e.__state.localState,transactionsState:{current:c}}}};const n=performance.now(),d=[S.sql`BEGIN ${S.sql.raw(i.toUpperCase())} TRANSACTION`,...o,S.sql`COMMIT`];try{await u.emit("transactionWillStart",e,c),await e.runQueries(d),await u.emit("transactionCommitted",e,c)}catch(m){throw console.error("Rollback transaction",m),await u.emit("transactionWillRollback",e,c),await e.runQuery(S.sql`ROLLBACK`),await u.emit("transactionRollbacked",e,c),m}finally{l.performance.totalTime=performance.now()-n,I(e,c.id,l.performance),E(e.__state.sharedState.jobsState,w)}},W=async({dbName:e,plugins:i,queriesMiddlewares:t,dbBackend:r})=>{const a=x("running",{label:"runningState"}),u=(await r)({dbName:e}),s=x({queue:[],current:void 0},{label:"jobsState"}),o={__state:{sharedState:{clientId:b(),dbBackend:u,dbName:e,runningState:a,eventsEmitter:Q(),jobsState:s,transactionLoggingState:{id:void 0,i:0}},localState:{queriesMiddlewares:t||[],transactionsState:{}}},runInTransaction(l,n){return V(this,(n==null?void 0:n.type)||"deferred",l,{label:n==null?void 0:n.label})},async runAtomicTransaction(l,n){return await J(this,(n==null?void 0:n.type)||"deferred",l,n)},async runQueries(l){return(await M(this,l)).result.map(({rows:d})=>d)},async runQuery(l){return(await this.runQueries([l]))[0]},runAfterTransactionCommitted(l){return A(this,(n,d,m)=>{n==="committed"&&l(d,m)})},runAfterTransactionRollbacked(l){A(o,(n,d,m)=>{n==="rollbacked"&&l(d,m)})}},c=await $(o.__state.sharedState.jobsState,{type:"initDb",name:e});let w=o;try{const l=()=>o.__state.sharedState.runningState.value;if(l()!=="running"||(await u.initialize(),l()!=="running"))return o;for(const n of i||[])w=n(w)}finally{E(s,c)}return await o.__state.sharedState.eventsEmitter.emit("initialized",o),w},U=async e=>{e.__state.sharedState.runningState.value="stopping",await F(e.__state.sharedState.jobsState),await e.__state.sharedState.dbBackend.stop(),e.__state.sharedState.runningState.value="stopped",queueMicrotask(()=>{e.__state.sharedState.runningState.stop(),e.__state.sharedState.jobsState.stop()})},O=(e,i)=>i({...e,__state:{...e.__state,localState:{...e.__state.localState,suppressLog:!0}}}),z=e=>({...e,__state:{...e.__state,localState:{...e.__state.localState,suppressLog:!0}}});_.StoppedError=k,_.TimeoutError=v,_.initDbClient=W,_.makeId=b,_.reactiveVar=x,_.stopDb=U,_.suppressLog=O,_.withSuppressedLog=z;for(const e in S)e!=="default"&&!_.hasOwnProperty(e)&&Object.defineProperty(_,e,{enumerable:!0,get:()=>S[e]});Object.defineProperties(_,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}); | ||
//# sourceMappingURL=index.umd.js.map |
{ | ||
"name": "@kikko-land/kikko", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"author": "Sergey Popov", | ||
@@ -38,10 +38,11 @@ "license": "MIT", | ||
"dependencies": { | ||
"@kikko-land/query-builder": "^0.2.0", | ||
"@kikko-land/sql": "^0.2.0", | ||
"@kikko-land/boono-sql": "^0.3.0", | ||
"lodash.isequal": "^4.5.0", | ||
"ts-essentials": "^9.3.0" | ||
}, | ||
"devDependencies": { | ||
"@kikko-land/common-scripts": "^0.3.0", | ||
"@types/emscripten": "^1.39.6" | ||
"@kikko-land/common-scripts": "^0.4.0", | ||
"@types/emscripten": "^1.39.6", | ||
"@types/lodash.isequal": "^4.5.6" | ||
} | ||
} |
@@ -1,14 +0,15 @@ | ||
import { IDbState, ITransaction } from "./types"; | ||
import { IDb, ITransaction } from "./types"; | ||
const runAfterTransaction = ( | ||
db: IDbState, | ||
export const runAfterTransaction = ( | ||
db: IDb, | ||
func: ( | ||
event: "committed" | "rollbacked", | ||
db: IDbState, | ||
db: IDb, | ||
transaction: ITransaction | ||
) => void | ||
) => { | ||
if (!db.localState.transactionsState) { | ||
if (!db.__state.localState.transactionsState.current) { | ||
throw new Error("Not in transaction."); | ||
} | ||
const transaction = db.__state.localState.transactionsState.current; | ||
@@ -19,3 +20,5 @@ const unsubscribes: (() => void)[] = []; | ||
(event: "committed" | "rollbacked") => | ||
(db: IDbState, transaction: ITransaction) => { | ||
(db: IDb, evTransaction: ITransaction) => { | ||
if (transaction.id !== evTransaction.id) return; | ||
func(event, db, transaction); | ||
@@ -29,3 +32,3 @@ | ||
unsubscribes.push( | ||
db.sharedState.eventsEmitter.on( | ||
db.__state.sharedState.eventsEmitter.on( | ||
"transactionCommitted", | ||
@@ -37,3 +40,3 @@ listener("committed") | ||
unsubscribes.push( | ||
db.sharedState.eventsEmitter.on( | ||
db.__state.sharedState.eventsEmitter.on( | ||
"transactionRollbacked", | ||
@@ -44,23 +47,1 @@ listener("rollbacked") | ||
}; | ||
export const runAfterTransactionCommitted = ( | ||
db: IDbState, | ||
func: (db: IDbState, transaction: ITransaction) => void | ||
) => { | ||
runAfterTransaction(db, (ev, db, transaction) => { | ||
if (ev === "committed") { | ||
func(db, transaction); | ||
} | ||
}); | ||
}; | ||
export const runAfterTransactionRollbacked = ( | ||
db: IDbState, | ||
func: (db: IDbState, transaction: ITransaction) => void | ||
) => { | ||
runAfterTransaction(db, (ev, db, transaction) => { | ||
if (ev === "rollbacked") { | ||
func(db, transaction); | ||
} | ||
}); | ||
}; |
@@ -1,4 +0,4 @@ | ||
import { IPrimitiveValue, ISql } from "@kikko-land/sql"; | ||
import { IPrimitiveValue, ISql } from "@kikko-land/boono-sql"; | ||
import { IDbState } from "./types"; | ||
import { IDb } from "./types"; | ||
import { makeId } from "./utils"; | ||
@@ -42,3 +42,3 @@ | ||
export const buildRunQueriesCommand = ( | ||
state: IDbState, | ||
state: IDb, | ||
queries: ISql[] | ||
@@ -54,4 +54,3 @@ ): IExecQueriesCommand => { | ||
commandId: makeId(), | ||
suppressLog: state.localState.suppressLog, | ||
}; | ||
}; |
@@ -19,5 +19,5 @@ // Adopted from https://github.com/ai/nanoevents/blob/main/index.js | ||
export function createNanoEvents<Events extends EventsMap>(): INanoEmitter< | ||
Events | ||
> { | ||
export function createNanoEvents< | ||
Events extends EventsMap | ||
>(): INanoEmitter<Events> { | ||
const events: Partial<{ [E in keyof Events]: Events[E][] }> = {}; | ||
@@ -24,0 +24,0 @@ |
@@ -1,8 +0,6 @@ | ||
export * from "./afterTransaction"; | ||
export * from "./initDb"; | ||
export * from "./reactiveVar"; | ||
export * from "./runQueries"; | ||
export * from "./suppressLog"; | ||
export * from "./transaction"; | ||
export * from "./types"; | ||
export { makeId } from "./utils"; | ||
export * from "@kikko-land/boono-sql"; |
@@ -0,13 +1,20 @@ | ||
import { ISqlAdapter } from "@kikko-land/boono-sql"; | ||
import { runAfterTransaction } from "./afterTransaction"; | ||
import { createNanoEvents } from "./createNanoEvents"; | ||
import { acquireJob, IJobsState, releaseJob, whenAllJobsDone } from "./job"; | ||
import { reactiveVar } from "./reactiveVar"; | ||
import { runQueries } from "./runQueries"; | ||
import { execAtomicTransaction, runInTransactionFunc } from "./transaction"; | ||
import { | ||
IAtomicTransactionScope, | ||
IDb, | ||
IDbBackend, | ||
IDbState, | ||
IKikkoEvents, | ||
IQueriesMiddleware, | ||
ITransaction, | ||
} from "./types"; | ||
import { makeId } from "./utils"; | ||
export type IDbClientPlugin = (state: IDbState) => IDbState; | ||
export type IDbClientPlugin = (state: IDb) => IDb; | ||
@@ -26,6 +33,6 @@ export type IInitDbClientConfig = { | ||
dbBackend, | ||
}: IInitDbClientConfig): Promise<IDbState> => { | ||
}: IInitDbClientConfig): Promise<IDb> => { | ||
const runningState = reactiveVar<"running" | "stopping" | "stopped">( | ||
"running", | ||
"runningState" | ||
{ label: "runningState" } | ||
); | ||
@@ -41,25 +48,81 @@ const dbBackendCalled = (await dbBackend)({ | ||
} as IJobsState, | ||
"jobsState" | ||
{ label: "jobsState" } | ||
); | ||
const state: IDbState = { | ||
sharedState: { | ||
clientId: makeId(), | ||
dbBackend: dbBackendCalled, | ||
dbName, | ||
const db: IDb = { | ||
__state: { | ||
sharedState: { | ||
clientId: makeId(), | ||
dbBackend: dbBackendCalled, | ||
dbName, | ||
runningState: runningState, | ||
runningState: runningState, | ||
eventsEmitter: createNanoEvents<IKikkoEvents>(), | ||
eventsEmitter: createNanoEvents<IKikkoEvents>(), | ||
jobsState: jobsState, | ||
transactionsState: {}, | ||
jobsState: jobsState, | ||
transactionLoggingState: { | ||
id: undefined, | ||
i: 0, | ||
}, | ||
}, | ||
localState: { | ||
queriesMiddlewares: queriesMiddlewares || [], | ||
transactionsState: {}, | ||
}, | ||
}, | ||
localState: { | ||
queriesMiddlewares: queriesMiddlewares || [], | ||
transactionsState: {}, | ||
runInTransaction<T>( | ||
func: (state: IDb) => Promise<T>, | ||
opts?: { label?: string; type?: "deferred" | "immediate" | "exclusive" } | ||
): Promise<T> { | ||
return runInTransactionFunc<T>(this, opts?.type || "deferred", func, { | ||
label: opts?.label, | ||
}); | ||
}, | ||
async runAtomicTransaction( | ||
func: | ||
| ((scope: IAtomicTransactionScope) => Promise<void> | void) | ||
| ISqlAdapter[], | ||
opts?: { label?: string; type?: "deferred" | "immediate" | "exclusive" } | ||
): Promise<void> { | ||
return await execAtomicTransaction( | ||
this, | ||
opts?.type || "deferred", | ||
func, | ||
opts | ||
); | ||
}, | ||
async runQueries<D extends Record<string, unknown>>( | ||
queries: ISqlAdapter[] | ||
): Promise<D[][]> { | ||
const res = await runQueries(this, queries); | ||
return res.result.map(({ rows }) => rows) as D[][]; | ||
}, | ||
async runQuery<D extends Record<string, unknown>>( | ||
query: ISqlAdapter | ||
): Promise<D[]> { | ||
return (await this.runQueries<D>([query]))[0]; | ||
}, | ||
runAfterTransactionCommitted( | ||
func: (db: IDb, transaction: ITransaction) => void | ||
) { | ||
return runAfterTransaction(this, (ev, db, transaction) => { | ||
if (ev === "committed") { | ||
func(db, transaction); | ||
} | ||
}); | ||
}, | ||
runAfterTransactionRollbacked( | ||
func: (db: IDb, transaction: ITransaction) => void | ||
) { | ||
runAfterTransaction(db, (ev, db, transaction) => { | ||
if (ev === "rollbacked") { | ||
func(db, transaction); | ||
} | ||
}); | ||
}, | ||
}; | ||
const job = await acquireJob(state.sharedState.jobsState, { | ||
const job = await acquireJob(db.__state.sharedState.jobsState, { | ||
type: "initDb", | ||
@@ -69,12 +132,12 @@ name: dbName, | ||
let currentState = state; | ||
let currentState = db; | ||
try { | ||
const getRunningState = () => state.sharedState.runningState.value; | ||
const getRunningState = () => db.__state.sharedState.runningState.value; | ||
if (getRunningState() !== "running") return state; | ||
if (getRunningState() !== "running") return db; | ||
await dbBackendCalled.initialize(); | ||
if (getRunningState() !== "running") return state; | ||
if (getRunningState() !== "running") return db; | ||
@@ -88,3 +151,3 @@ for (const plugin of plugins || []) { | ||
await state.sharedState.eventsEmitter.emit("initialized", state); | ||
await db.__state.sharedState.eventsEmitter.emit("initialized", db); | ||
@@ -94,14 +157,14 @@ return currentState; | ||
export const stopDb = async (state: IDbState) => { | ||
state.sharedState.runningState.value = "stopping"; | ||
export const stopDb = async (state: IDb) => { | ||
state.__state.sharedState.runningState.value = "stopping"; | ||
await whenAllJobsDone(state.sharedState.jobsState); | ||
await state.sharedState.dbBackend.stop(); | ||
await whenAllJobsDone(state.__state.sharedState.jobsState); | ||
await state.__state.sharedState.dbBackend.stop(); | ||
state.sharedState.runningState.value = "stopped"; | ||
state.__state.sharedState.runningState.value = "stopped"; | ||
queueMicrotask(() => { | ||
state.sharedState.runningState.stop(); | ||
state.sharedState.jobsState.stop(); | ||
state.__state.sharedState.runningState.stop(); | ||
state.__state.sharedState.jobsState.stop(); | ||
}); | ||
}; |
@@ -1,2 +0,2 @@ | ||
import { ISql } from "@kikko-land/sql"; | ||
import { ISql } from "@kikko-land/boono-sql"; | ||
import { DeepReadonly } from "ts-essentials"; | ||
@@ -15,3 +15,3 @@ | ||
| { | ||
type: "runTransaction"; | ||
type: "runTransaction" | "runAtomicTransaction"; | ||
id: string; | ||
@@ -18,0 +18,0 @@ transaction: ITransaction; |
@@ -0,1 +1,3 @@ | ||
import isEqual from "lodash.isequal"; | ||
export interface ReactiveVar<T> { | ||
@@ -9,6 +11,3 @@ __state: { | ||
value: T; | ||
subscribe( | ||
sub: (val: T) => void | (() => void), | ||
emitValueOnSubscribe?: boolean | ||
): () => void; | ||
subscribe(sub: (val: T) => void, emitValueOnSubscribe?: boolean): () => void; | ||
waitTill( | ||
@@ -29,3 +28,9 @@ filter: (val: T) => boolean, | ||
export const reactiveVar = <T>(val: T, label: string): ReactiveVar<T> => { | ||
export const reactiveVar = <T>( | ||
val: T, | ||
rOpts: { label: string; deduplicate?: boolean } | ||
): ReactiveVar<T> => { | ||
const shouldDeduplicate = | ||
rOpts.deduplicate === undefined ? true : rOpts.deduplicate; | ||
return { | ||
@@ -42,4 +47,9 @@ __state: { | ||
set value(val: T) { | ||
if (this.isStopped) throw new Error(`reactiveVar ${label} is stopped!`); | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${rOpts.label} is stopped!`); | ||
if (shouldDeduplicate && isEqual(this.__state.value, val)) { | ||
return; | ||
} | ||
this.__state.value = val; | ||
@@ -52,3 +62,4 @@ | ||
get value() { | ||
if (this.isStopped) throw new Error(`reactiveVar ${label} is stopped!`); | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${rOpts.label} is stopped!`); | ||
@@ -58,11 +69,21 @@ return this.__state.value; | ||
subscribe( | ||
sub: (val: T) => void | (() => void), | ||
emitValueOnSubscribe: boolean = true | ||
func: (val: T) => void | (() => void), | ||
emitValueOnSubscribe = true | ||
) { | ||
if (this.isStopped) throw new Error(`reactiveVar ${label} is stopped!`); | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${rOpts.label} is stopped!`); | ||
this.__state.subscriptions.push(sub); | ||
let currentUnsubscribe: undefined | void | (() => void); | ||
const subscriber = (val: T) => { | ||
if (currentUnsubscribe) { | ||
currentUnsubscribe(); | ||
} | ||
currentUnsubscribe = func(val); | ||
}; | ||
this.__state.subscriptions.push(subscriber); | ||
if (emitValueOnSubscribe) { | ||
sub(this.__state.value); | ||
subscriber(this.__state.value); | ||
} | ||
@@ -72,3 +93,3 @@ | ||
this.__state.subscriptions = this.__state.subscriptions.filter((s) => { | ||
return s !== sub; | ||
return s !== subscriber; | ||
}); | ||
@@ -84,4 +105,15 @@ }; | ||
) { | ||
if (this.isStopped) throw new Error(`reactiveVar ${label} is stopped!`); | ||
const stopError = new StoppedError( | ||
`waitUntil for reactiveVar ${rOpts.label} is stopped due to stop signal` | ||
); | ||
const timeoutError = new TimeoutError( | ||
`waitUntil for reactiveVar ${rOpts.label} is timed out` | ||
); | ||
const stoppedError = new StoppedError( | ||
`waitUntil for reactiveVar ${rOpts.label} is stopped due to reactive var stop` | ||
); | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${rOpts.label} is stopped!`); | ||
const toWait = new Promise<void>((resolve, reject) => { | ||
@@ -102,8 +134,7 @@ const unsubscriptions: (() => void)[] = []; | ||
reject( | ||
new StoppedError( | ||
`waitUntil for reactiveVar ${label} is stopped due to stop signal` | ||
) | ||
); | ||
}, true) || (() => {}) | ||
reject(stopError); | ||
}, true) || | ||
(() => { | ||
return undefined; | ||
}) | ||
); | ||
@@ -121,3 +152,3 @@ | ||
if (opts?.timeout === undefined || opts?.timeout !== "infinite") { | ||
if (opts?.timeout === undefined || typeof opts?.timeout === "number") { | ||
const id = setTimeout( | ||
@@ -127,9 +158,5 @@ () => { | ||
reject( | ||
new TimeoutError( | ||
`waitUntil for reactiveVar ${label} is timed out` | ||
) | ||
); | ||
reject(timeoutError); | ||
}, | ||
opts?.timeout === undefined ? 5000 : opts.timeout | ||
opts?.timeout === undefined ? 60_000 : opts.timeout | ||
); | ||
@@ -145,7 +172,3 @@ | ||
reject( | ||
new StoppedError( | ||
`waitUntil for reactiveVar ${label} is stopped due to reactive var stop` | ||
) | ||
); | ||
reject(stoppedError); | ||
}); | ||
@@ -158,3 +181,3 @@ }); | ||
if (this.isStopped) | ||
throw new Error(`reactiveVar ${label} is already stopped!`); | ||
throw new Error(`reactiveVar ${rOpts.label} is already stopped!`); | ||
@@ -161,0 +184,0 @@ this.__state.subscriptions = []; |
@@ -1,6 +0,6 @@ | ||
import { ISqlAdapter } from "@kikko-land/sql"; | ||
import { ISqlAdapter } from "@kikko-land/boono-sql"; | ||
import { acquireJob, IJob, releaseJob } from "./job"; | ||
import { | ||
IDbState, | ||
IDb, | ||
INextQueriesMiddleware, | ||
@@ -12,8 +12,7 @@ IQueriesMiddleware, | ||
const runQueriesMiddleware: IQueriesMiddleware = async ({ | ||
dbState, | ||
queries, | ||
}) => { | ||
const colors = ["yellow", "cyan", "magenta"]; | ||
const runQueriesMiddleware: IQueriesMiddleware = async ({ db, queries }) => { | ||
const { | ||
localState: { transactionsState: transactionsLocalState, suppressLog }, | ||
localState: { transactionsState: transactionsLocalState }, | ||
sharedState: { | ||
@@ -24,9 +23,10 @@ transactionsState: transactionsSharedState, | ||
}, | ||
} = dbState; | ||
sharedState, | ||
} = db.__state; | ||
if (!transactionsLocalState.current) { | ||
assureDbIsRunning(dbState, () => JSON.stringify(queries)); | ||
assureDbIsRunning(db, () => JSON.stringify(queries)); | ||
} | ||
if (transactionsLocalState.current && transactionsSharedState.current) { | ||
if (transactionsLocalState.current && transactionsSharedState?.current) { | ||
if ( | ||
@@ -44,2 +44,4 @@ transactionsLocalState.current.id !== transactionsSharedState.current.id | ||
const unwrappedQueries = unwrapQueries(queries.map((q) => q.toSql())); | ||
if (!transactionsLocalState.current) { | ||
@@ -52,16 +54,136 @@ job = await acquireJob(jobsState, { | ||
const execOpts = { | ||
log: { | ||
suppress: Boolean(suppressLog), | ||
transactionId: transactionsLocalState.current?.id, | ||
}, | ||
}; | ||
try { | ||
const result = await dbBackend.execQueries( | ||
unwrapQueries(queries.map((q) => q.toSql())), | ||
execOpts | ||
const startedAt = performance.now(); | ||
const { result, performance: qPerformance } = await dbBackend.execQueries( | ||
unwrappedQueries | ||
); | ||
const endedAt = performance.now(); | ||
return { dbState, result, queries }; | ||
if (!db.__state.localState.suppressLog) { | ||
if ( | ||
transactionsLocalState.current && | ||
transactionsLocalState.current.id !== | ||
sharedState.transactionLoggingState.id | ||
) { | ||
sharedState.transactionLoggingState.id = | ||
transactionsLocalState.current.id; | ||
sharedState.transactionLoggingState.i++; | ||
} | ||
if (!transactionsLocalState?.current?.id) { | ||
sharedState.transactionLoggingState.id = undefined; | ||
} | ||
const queriesTimings = result | ||
.map(({ performance }, i) => { | ||
const times = [ | ||
performance.prepareTime !== undefined | ||
? `prepareTime=${(performance.prepareTime / 1000).toFixed(4)}` | ||
: "", | ||
performance.execTime !== undefined | ||
? `execTime=${(performance.execTime / 1000).toFixed(4)}` | ||
: "", | ||
performance.freeTime !== undefined | ||
? `freeTime=${(performance.freeTime / 1000).toFixed(4)}` | ||
: "", | ||
] | ||
.filter((t) => t.length !== 0) | ||
.join(" "); | ||
return ( | ||
"{" + | ||
[unwrappedQueries[i].text.slice(0, 1000), times] | ||
.filter((v) => v.length !== 0) | ||
.join(" ") + | ||
"}" | ||
); | ||
}) | ||
.join("\n"); | ||
const totalTiming = | ||
`%c[${db.__state.sharedState.dbName}] ` + | ||
[ | ||
transactionsLocalState.current?.id | ||
? `[tr_id=${transactionsLocalState.current?.id.substring(0, 6)}]` | ||
: "", | ||
queriesTimings, | ||
qPerformance?.sendTime !== undefined | ||
? `sendTime=${(qPerformance.sendTime / 1000).toFixed(4)}` | ||
: "", | ||
qPerformance?.receiveTime !== undefined | ||
? `receiveTime=${(qPerformance.receiveTime / 1000).toFixed(4)}` | ||
: "", | ||
`totalTime=${((endedAt - startedAt) / 1000).toFixed(4)}`, | ||
] | ||
.filter((t) => t.length !== 0) | ||
.join(" "); | ||
console.log( | ||
totalTiming, | ||
`color: ${ | ||
sharedState.transactionLoggingState.id | ||
? colors[sharedState.transactionLoggingState.i % colors.length] | ||
: "white" | ||
}` | ||
); | ||
} | ||
if (transactionsLocalState.current && transactionsSharedState?.current) { | ||
if ( | ||
transactionsLocalState.current.id === transactionsSharedState.current.id | ||
) { | ||
const perfData = transactionsSharedState.performance; | ||
if (result.some((d) => d.performance.execTime !== undefined)) { | ||
if (perfData.execTime === undefined) { | ||
perfData.execTime = 0; | ||
} | ||
perfData.execTime += result.reduce( | ||
(partialSum, a) => partialSum + (a.performance.execTime ?? 0), | ||
0 | ||
); | ||
} | ||
if (result.some((d) => d.performance.freeTime !== undefined)) { | ||
if (perfData.freeTime === undefined) { | ||
perfData.freeTime = 0; | ||
} | ||
perfData.freeTime += result.reduce( | ||
(partialSum, a) => partialSum + (a.performance.freeTime ?? 0), | ||
0 | ||
); | ||
} | ||
if (result.some((d) => d.performance.prepareTime !== undefined)) { | ||
if (perfData.prepareTime === undefined) { | ||
perfData.prepareTime = 0; | ||
} | ||
perfData.prepareTime += result.reduce( | ||
(partialSum, a) => partialSum + (a.performance.prepareTime ?? 0), | ||
0 | ||
); | ||
} | ||
if (qPerformance.sendTime) { | ||
if (!perfData.sendTime) { | ||
perfData.sendTime = 0; | ||
} | ||
perfData.sendTime += qPerformance.sendTime; | ||
} | ||
if (qPerformance.receiveTime) { | ||
if (!perfData.receiveTime) { | ||
perfData.receiveTime = 0; | ||
} | ||
perfData.receiveTime += qPerformance.receiveTime; | ||
} | ||
} | ||
} | ||
return { db: db, result, performance: qPerformance, queries }; | ||
} finally { | ||
@@ -74,8 +196,5 @@ if (job) { | ||
export const runQueries = async <D extends Record<string, unknown>>( | ||
state: IDbState, | ||
queries: ISqlAdapter[] | ||
): Promise<D[][]> => { | ||
export const runQueries = async (db: IDb, queries: ISqlAdapter[]) => { | ||
const middlewares: IQueriesMiddleware[] = [ | ||
...state.localState.queriesMiddlewares, | ||
...db.__state.localState.queriesMiddlewares, | ||
runQueriesMiddleware, | ||
@@ -93,16 +212,12 @@ ].reverse(); | ||
return ( | ||
await toCall({ | ||
dbState: state, | ||
result: [], | ||
queries: queries.map((q) => q.toSql()), | ||
}) | ||
).result as D[][]; | ||
return await toCall({ | ||
db: db, | ||
result: [], | ||
performance: { | ||
sendTime: undefined, | ||
receiveTime: undefined, | ||
totalTime: 0, | ||
}, | ||
queries: queries.map((q) => q.toSql()), | ||
}); | ||
}; | ||
export const runQuery = async <D extends Record<string, unknown>>( | ||
state: IDbState, | ||
query: ISqlAdapter | ||
) => { | ||
return (await runQueries<D>(state, [query]))[0] || []; | ||
}; |
@@ -1,15 +0,24 @@ | ||
import { IDbState } from "./types"; | ||
import { IDb } from "./types"; | ||
export const suppressLog = <T>( | ||
state: IDbState, | ||
func: (state: IDbState) => T | ||
): T => { | ||
export const suppressLog = <T>(db: IDb, func: (state: IDb) => T): T => { | ||
return func({ | ||
...state, | ||
localState: { ...state.localState, suppressLog: true }, | ||
...db, | ||
__state: { | ||
...db.__state, | ||
localState: { | ||
...db.__state.localState, | ||
suppressLog: true, | ||
}, | ||
}, | ||
}); | ||
}; | ||
export const withSuppressedLog = (state: IDbState): IDbState => { | ||
return { ...state, localState: { ...state.localState, suppressLog: true } }; | ||
export const withSuppressedLog = (db: IDb): IDb => { | ||
return { | ||
...db, | ||
__state: { | ||
...db.__state, | ||
localState: { ...db.__state.localState, suppressLog: true }, | ||
}, | ||
}; | ||
}; |
@@ -1,11 +0,53 @@ | ||
import { sql } from "@kikko-land/sql"; | ||
import { ISqlAdapter, sql } from "@kikko-land/boono-sql"; | ||
import { acquireJob, releaseJob } from "./job"; | ||
import { IDbState, ITransaction } from "./types"; | ||
import { assureDbIsRunning, makeId, unwrapQueries } from "./utils"; | ||
import { | ||
IAtomicTransactionScope, | ||
IDb, | ||
ITransaction, | ||
ITransactionPerformance, | ||
} from "./types"; | ||
import { assureDbIsRunning, makeId } from "./utils"; | ||
const runInTransactionFunc = async <T>( | ||
state: IDbState, | ||
transactionType: "DEFERRED" | "IMMEDIATE" | "EXCLUSIVE", | ||
func: (state: IDbState) => Promise<T>, | ||
const logTimeIfNeeded = ( | ||
db: IDb, | ||
transactionId: string, | ||
performance: ITransactionPerformance | ||
) => { | ||
if (db.__state.localState.suppressLog) return; | ||
const data = [ | ||
performance.prepareTime === undefined | ||
? "" | ||
: `prepareTime=${(performance.prepareTime / 1000).toFixed(4)}`, | ||
performance.execTime === undefined | ||
? "" | ||
: `execTime=${(performance.execTime / 1000).toFixed(4)}`, | ||
performance.freeTime === undefined | ||
? "" | ||
: `freeTime=${(performance.freeTime / 1000).toFixed(4)}`, | ||
performance.sendTime === undefined | ||
? "" | ||
: `sendTime=${(performance.sendTime / 1000).toFixed(4)}`, | ||
performance.receiveTime === undefined | ||
? "" | ||
: `receiveTime=${(performance.receiveTime / 1000).toFixed(4)}`, | ||
`totalTime=${(performance.totalTime / 1000).toFixed(4)}`, | ||
] | ||
.filter((v) => v.length !== 0) | ||
.join(" "); | ||
console.log( | ||
`%c[${db.__state.sharedState.dbName}][tr_id=${transactionId.slice( | ||
0, | ||
6 | ||
)}] Transaction finished with ${data}`, | ||
`color: #fff; background-color: #1da1f2; padding: 2px 4px; border-radius: 2px` | ||
); | ||
}; | ||
export const runInTransactionFunc = async <T>( | ||
db: IDb, | ||
transactionType: "deferred" | "immediate" | "exclusive", | ||
func: (state: IDb) => Promise<T>, | ||
opts?: { label?: string } | ||
@@ -15,14 +57,11 @@ ) => { | ||
localState: { transactionsState: transactionsLocalState }, | ||
sharedState: { | ||
transactionsState: transactionsSharedState, | ||
eventsEmitter, | ||
dbBackend, | ||
}, | ||
} = state; | ||
sharedState: { transactionsState: transactionsSharedState, eventsEmitter }, | ||
sharedState, | ||
} = db.__state; | ||
// It's indeed that function in same transaction don't need to check db is running | ||
// Cause all transaction will await to execute on DB before stop | ||
if (transactionsLocalState.current && transactionsSharedState.current) { | ||
if (transactionsLocalState.current && transactionsSharedState?.current) { | ||
if ( | ||
transactionsLocalState.current.id !== transactionsSharedState.current.id | ||
transactionsLocalState.current.id !== transactionsSharedState?.current.id | ||
) { | ||
@@ -36,20 +75,24 @@ // Is it possible? | ||
// we already in same transaction | ||
return await func(state); | ||
return await func(db); | ||
} | ||
assureDbIsRunning(state, () => "transaction"); | ||
assureDbIsRunning(db, () => "transaction"); | ||
const transaction: ITransaction = { | ||
id: makeId(), | ||
type: "async", | ||
}; | ||
state = { | ||
...state, | ||
localState: { | ||
...state.localState, | ||
transactionsState: { current: transaction }, | ||
db = { | ||
...db, | ||
__state: { | ||
...db.__state, | ||
localState: { | ||
...db.__state.localState, | ||
transactionsState: { current: transaction }, | ||
}, | ||
}, | ||
}; | ||
const job = await acquireJob(state.sharedState.jobsState, { | ||
const job = await acquireJob(db.__state.sharedState.jobsState, { | ||
type: "runTransaction", | ||
@@ -60,39 +103,43 @@ transaction, | ||
const execOpts = { | ||
log: { | ||
suppress: Boolean(state.localState.suppressLog), | ||
transactionId: transaction.id, | ||
const startTime = performance.now(); | ||
const transactionState = { | ||
current: transaction, | ||
performance: { | ||
prepareTime: 0, | ||
execTime: 0, | ||
freeTime: 0, | ||
sendTime: 0, | ||
receiveTime: 0, | ||
totalTime: 0, | ||
}, | ||
}; | ||
sharedState.transactionsState = transactionState; | ||
try { | ||
transactionsSharedState.current = transaction; | ||
await eventsEmitter.emit("transactionWillStart", db, transaction); | ||
await eventsEmitter.emit("transactionWillStart", state, transaction); | ||
await dbBackend.execQueries( | ||
unwrapQueries([sql`BEGIN ${sql.raw(transactionType)} TRANSACTION;`]), | ||
execOpts | ||
await db.runQuery( | ||
sql`BEGIN ${sql.raw(transactionType.toUpperCase())} TRANSACTION;` | ||
); | ||
await eventsEmitter.emit("transactionStarted", state, transaction); | ||
await eventsEmitter.emit("transactionStarted", db, transaction); | ||
try { | ||
const res = await func(state); | ||
const result = await func(db); | ||
await eventsEmitter.emit("transactionWillCommit", state, transaction); | ||
await eventsEmitter.emit("transactionWillCommit", db, transaction); | ||
await dbBackend.execQueries(unwrapQueries([sql`COMMIT`]), execOpts); | ||
await db.runQuery(sql`COMMIT`); | ||
await eventsEmitter.emit("transactionCommitted", state, transaction); | ||
await eventsEmitter.emit("transactionCommitted", db, transaction); | ||
return res; | ||
return result; | ||
} catch (e) { | ||
console.error("Rollback transaction", e); | ||
await eventsEmitter.emit("transactionWillRollback", state, transaction); | ||
await eventsEmitter.emit("transactionWillRollback", db, transaction); | ||
await dbBackend.execQueries(unwrapQueries([sql`ROLLBACK`]), execOpts); | ||
await db.runQuery(sql`ROLLBACK`); | ||
await eventsEmitter.emit("transactionRollbacked", state, transaction); | ||
await eventsEmitter.emit("transactionRollbacked", db, transaction); | ||
@@ -102,27 +149,116 @@ throw e; | ||
} finally { | ||
releaseJob(state.sharedState.jobsState, job); | ||
transactionState.performance.totalTime = performance.now() - startTime; | ||
logTimeIfNeeded(db, transaction.id, transactionState.performance); | ||
releaseJob(db.__state.sharedState.jobsState, job); | ||
} | ||
}; | ||
// By default it is deferred | ||
export const runInDeferredTransaction = <T>( | ||
state: IDbState, | ||
func: (state: IDbState) => Promise<T>, | ||
const initAtomicTransaction = (): IAtomicTransactionScope => { | ||
return { | ||
__state: { | ||
queries: [], | ||
}, | ||
addQuery(q: ISqlAdapter): void { | ||
this.__state.queries.push(q); | ||
}, | ||
}; | ||
}; | ||
export const execAtomicTransaction = async ( | ||
db: IDb, | ||
transactionType: "deferred" | "immediate" | "exclusive", | ||
funcOrQueries: | ||
| ((scope: IAtomicTransactionScope) => Promise<void> | void) | ||
| ISqlAdapter[], | ||
opts?: { label?: string } | ||
) => runInTransactionFunc(state, "DEFERRED", func, opts); | ||
export const runInImmediateTransaction = <T>( | ||
state: IDbState, | ||
func: (state: IDbState) => Promise<T>, | ||
opts?: { label?: string } | ||
) => runInTransactionFunc(state, "IMMEDIATE", func, opts); | ||
export const runInExclusiveTransaction = <T>( | ||
state: IDbState, | ||
func: (state: IDbState) => Promise<T>, | ||
opts?: { label?: string } | ||
) => runInTransactionFunc(state, "EXCLUSIVE", func, opts); | ||
): Promise<void> => { | ||
const { | ||
localState: { transactionsState: transactionsLocalState }, | ||
sharedState: { eventsEmitter }, | ||
sharedState, | ||
} = db.__state; | ||
if (transactionsLocalState.current) { | ||
throw new Error( | ||
"You are running atomic transaction inside of a transaction. Consider moving atomic transaction call to runAfterTransaction callback." | ||
); | ||
} | ||
export const runInTransaction = <T>( | ||
state: IDbState, | ||
func: (state: IDbState) => Promise<T>, | ||
opts?: { label?: string } | ||
) => runInDeferredTransaction(state, func, opts); | ||
const inputQueries = await (async () => { | ||
if (Array.isArray(funcOrQueries)) { | ||
return funcOrQueries; | ||
} else { | ||
const atomicTransaction = initAtomicTransaction(); | ||
await funcOrQueries(atomicTransaction); | ||
return atomicTransaction.__state.queries; | ||
} | ||
})(); | ||
const transaction: ITransaction = { | ||
id: makeId(), | ||
type: "atomic", | ||
}; | ||
const job = await acquireJob(db.__state.sharedState.jobsState, { | ||
type: "runAtomicTransaction", | ||
transaction, | ||
label: opts?.label, | ||
}); | ||
const transactionState = { | ||
current: transaction, | ||
performance: { | ||
prepareTime: 0, | ||
execTime: 0, | ||
freeTime: 0, | ||
sendTime: 0, | ||
receiveTime: 0, | ||
totalTime: 0, | ||
}, | ||
}; | ||
sharedState.transactionsState = transactionState; | ||
db = { | ||
...db, | ||
__state: { | ||
...db.__state, | ||
localState: { | ||
...db.__state.localState, | ||
transactionsState: { current: transaction }, | ||
}, | ||
}, | ||
}; | ||
const startTime = performance.now(); | ||
const queries = [ | ||
sql`BEGIN ${sql.raw(transactionType.toUpperCase())} TRANSACTION`, | ||
...inputQueries, | ||
sql`COMMIT`, | ||
]; | ||
try { | ||
await eventsEmitter.emit("transactionWillStart", db, transaction); | ||
await db.runQueries(queries); | ||
await eventsEmitter.emit("transactionCommitted", db, transaction); | ||
} catch (e) { | ||
console.error("Rollback transaction", e); | ||
await eventsEmitter.emit("transactionWillRollback", db, transaction); | ||
await db.runQuery(sql`ROLLBACK`); | ||
await eventsEmitter.emit("transactionRollbacked", db, transaction); | ||
throw e; | ||
} finally { | ||
transactionState.performance.totalTime = performance.now() - startTime; | ||
logTimeIfNeeded(db, transaction.id, transactionState.performance); | ||
releaseJob(db.__state.sharedState.jobsState, job); | ||
} | ||
}; |
116
src/types.ts
@@ -1,3 +0,2 @@ | ||
import { IBaseToken } from "@kikko-land/query-builder/src/types"; | ||
import { ISqlAdapter } from "@kikko-land/sql"; | ||
import { ISqlAdapter } from "@kikko-land/boono-sql"; | ||
import { DeepReadonly } from "ts-essentials"; | ||
@@ -10,25 +9,25 @@ | ||
export type IKikkoEvents = { | ||
initialized: (db: IDbState) => Promise<void> | void; | ||
initialized: (db: IDb) => Promise<void> | void; | ||
transactionWillStart: ( | ||
db: IDbState, | ||
db: IDb, | ||
transaction: ITransaction | ||
) => Promise<void> | void; | ||
transactionStarted: ( | ||
db: IDbState, | ||
db: IDb, | ||
transaction: ITransaction | ||
) => Promise<void> | void; | ||
transactionWillCommit: ( | ||
db: IDbState, | ||
db: IDb, | ||
transaction: ITransaction | ||
) => Promise<void> | void; | ||
transactionCommitted: ( | ||
db: IDbState, | ||
db: IDb, | ||
transaction: ITransaction | ||
) => Promise<void> | void; | ||
transactionWillRollback: ( | ||
db: IDbState, | ||
db: IDb, | ||
transaction: ITransaction | ||
) => Promise<void> | void; | ||
transactionRollbacked: ( | ||
db: IDbState, | ||
db: IDb, | ||
transaction: ITransaction | ||
@@ -40,8 +39,21 @@ ) => Promise<void> | void; | ||
id: string; | ||
type: "atomic" | "async"; | ||
} | ||
export type IQueriesMiddlewareState = { | ||
dbState: IDbState; | ||
result: IQueryResult[]; | ||
queries: (IBaseToken | ISqlAdapter)[]; | ||
db: IDb; | ||
result: { | ||
rows: IQueryResult; | ||
performance: { | ||
execTime?: number; | ||
prepareTime?: number; | ||
freeTime?: number; | ||
}; | ||
}[]; | ||
performance: { | ||
sendTime?: number; | ||
receiveTime?: number; | ||
totalTime: number; | ||
}; | ||
queries: ISqlAdapter[]; | ||
}; | ||
@@ -59,19 +71,63 @@ | ||
export interface IDbState { | ||
// mutable object | ||
sharedState: ISharedDbState; | ||
// immutable object | ||
localState: DeepReadonly<ILocalDbState>; | ||
export interface IAtomicTransactionScope { | ||
__state: { | ||
queries: ISqlAdapter[]; | ||
}; | ||
addQuery(q: ISqlAdapter): void; | ||
} | ||
export interface IDb { | ||
__state: { | ||
// mutable object | ||
sharedState: ISharedDbState; | ||
// immutable object | ||
localState: DeepReadonly<ILocalDbState>; | ||
}; | ||
runInTransaction<T>( | ||
func: (state: IDb) => Promise<T>, | ||
opts?: { label?: string; type?: "deferred" | "immediate" | "exclusive" } | ||
): Promise<T>; | ||
runAtomicTransaction( | ||
func: | ||
| ((scope: IAtomicTransactionScope) => Promise<void> | void) | ||
| ISqlAdapter[], | ||
opts?: { label?: string; type?: "deferred" | "immediate" | "exclusive" } | ||
): Promise<void>; | ||
runQueries<D extends Record<string, unknown>>( | ||
queries: ISqlAdapter[] | ||
): Promise<D[][]>; | ||
runQuery<D extends Record<string, unknown>>(query: ISqlAdapter): Promise<D[]>; | ||
runAfterTransactionCommitted( | ||
func: (db: IDb, transaction: ITransaction) => void | ||
): void; | ||
runAfterTransactionRollbacked( | ||
func: (db: IDb, transaction: ITransaction) => void | ||
): void; | ||
} | ||
export type IQueryValue = number | string | Uint8Array | null; | ||
export type IQuery = { values: IQueryValue[]; text: string }; | ||
export type IQueryResult = Record<string, IQueryValue>[]; | ||
export type IExecQueriesResult = { | ||
result: { | ||
rows: IQueryResult; | ||
performance: { | ||
prepareTime?: number; | ||
freeTime?: number; | ||
execTime?: number; | ||
}; | ||
}[]; | ||
performance: { | ||
sendTime?: number; | ||
receiveTime?: number; | ||
totalTime: number; | ||
}; | ||
}; | ||
type IDbInstance = { | ||
initialize(): Promise<void>; | ||
execQueries( | ||
queries: IQuery[], | ||
opts: { log: { suppress: boolean; transactionId?: string } } | ||
): Promise<IQueryResult[]>; | ||
execQueries(queries: IQuery[]): Promise<IExecQueriesResult>; | ||
stop(): Promise<void>; | ||
@@ -81,2 +137,11 @@ }; | ||
export type ITransactionPerformance = { | ||
freeTime?: number; | ||
sendTime?: number; | ||
receiveTime?: number; | ||
prepareTime?: number; | ||
execTime?: number; | ||
totalTime: number; | ||
}; | ||
export interface ISharedDbState { | ||
@@ -95,5 +160,10 @@ dbName: string; | ||
transactionsState: { | ||
current?: ITransaction; | ||
transactionsState?: { | ||
current: ITransaction; | ||
performance: ITransactionPerformance; | ||
}; | ||
transactionLoggingState: { | ||
i: number; | ||
id: string | undefined; | ||
}; | ||
} | ||
@@ -100,0 +170,0 @@ |
@@ -1,8 +0,10 @@ | ||
import { ISql } from "@kikko-land/sql"; | ||
import { ISql } from "@kikko-land/boono-sql"; | ||
import { IDbState, IQuery } from "./types"; | ||
import { IDb, IQuery } from "./types"; | ||
export const assureDbIsRunning = (state: IDbState, toStart: () => string) => { | ||
export const assureDbIsRunning = (state: IDb, toStart: () => string) => { | ||
const { | ||
sharedState: { runningState, dbName }, | ||
__state: { | ||
sharedState: { runningState, dbName }, | ||
}, | ||
} = state; | ||
@@ -9,0 +11,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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
164072
1922
3
1
+ Added@kikko-land/boono-sql@^0.3.0
+ Addedlodash.isequal@^4.5.0
+ Added@kikko-land/boono-sql@0.3.0(transitive)
+ Addedlodash.isequal@4.5.0(transitive)
- Removed@kikko-land/query-builder@^0.2.0
- Removed@kikko-land/sql@^0.2.0
- Removed@kikko-land/query-builder@0.2.0(transitive)
- Removed@kikko-land/sql@0.2.0(transitive)