@ketvietlab/ketjs 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +8 -2
- package/dist/agent/capabilities.d.ts +1 -3
- package/dist/agent/capabilities.d.ts.map +1 -1
- package/dist/agent/capabilities.js +10 -1
- package/dist/agent/capabilities.js.map +1 -1
- package/dist/agent/permissions.d.ts +43 -1
- package/dist/agent/permissions.d.ts.map +1 -1
- package/dist/agent/permissions.js +50 -2
- package/dist/agent/permissions.js.map +1 -1
- package/dist/cli.js +246 -97
- package/dist/cli.js.map +1 -1
- package/dist/codegen/dts.d.ts.map +1 -1
- package/dist/codegen/dts.js +1 -0
- package/dist/codegen/dts.js.map +1 -1
- package/dist/data/changeset.d.ts +12 -0
- package/dist/data/changeset.d.ts.map +1 -1
- package/dist/data/changeset.js +73 -17
- package/dist/data/changeset.js.map +1 -1
- package/dist/data/expr.d.ts +16 -0
- package/dist/data/expr.d.ts.map +1 -1
- package/dist/data/expr.js +33 -5
- package/dist/data/expr.js.map +1 -1
- package/dist/data/fleet.d.ts +41 -1
- package/dist/data/fleet.d.ts.map +1 -1
- package/dist/data/fleet.js +100 -11
- package/dist/data/fleet.js.map +1 -1
- package/dist/data/migrate.d.ts +20 -0
- package/dist/data/migrate.d.ts.map +1 -1
- package/dist/data/migrate.js +86 -3
- package/dist/data/migrate.js.map +1 -1
- package/dist/data/physical.d.ts +5 -0
- package/dist/data/physical.d.ts.map +1 -0
- package/dist/data/physical.js +196 -0
- package/dist/data/physical.js.map +1 -0
- package/dist/data/pool.d.ts +2 -1
- package/dist/data/pool.d.ts.map +1 -1
- package/dist/data/pool.js +159 -31
- package/dist/data/pool.js.map +1 -1
- package/dist/data/query.d.ts +10 -1
- package/dist/data/query.d.ts.map +1 -1
- package/dist/data/query.js +136 -27
- package/dist/data/query.js.map +1 -1
- package/dist/data/sqlite.d.ts.map +1 -1
- package/dist/data/sqlite.js +350 -52
- package/dist/data/sqlite.js.map +1 -1
- package/dist/data/time.d.ts +13 -0
- package/dist/data/time.d.ts.map +1 -1
- package/dist/data/time.js +61 -17
- package/dist/data/time.js.map +1 -1
- package/dist/index.d.ts +43 -22
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -16
- package/dist/index.js.map +1 -1
- package/dist/kernel/audit.d.ts +27 -0
- package/dist/kernel/audit.d.ts.map +1 -0
- package/dist/kernel/audit.js +67 -0
- package/dist/kernel/audit.js.map +1 -0
- package/dist/kernel/classification.d.ts +26 -0
- package/dist/kernel/classification.d.ts.map +1 -0
- package/dist/kernel/classification.js +73 -0
- package/dist/kernel/classification.js.map +1 -0
- package/dist/kernel/compose.d.ts +6 -2
- package/dist/kernel/compose.d.ts.map +1 -1
- package/dist/kernel/compose.js +269 -15
- package/dist/kernel/compose.js.map +1 -1
- package/dist/kernel/define.d.ts +2 -3
- package/dist/kernel/define.d.ts.map +1 -1
- package/dist/kernel/define.js +22 -12
- package/dist/kernel/define.js.map +1 -1
- package/dist/kernel/diff.d.ts.map +1 -1
- package/dist/kernel/diff.js +28 -4
- package/dist/kernel/diff.js.map +1 -1
- package/dist/kernel/graph.js +1 -1
- package/dist/kernel/graph.js.map +1 -1
- package/dist/kernel/i18n.d.ts +10 -1
- package/dist/kernel/i18n.d.ts.map +1 -1
- package/dist/kernel/i18n.js +34 -1
- package/dist/kernel/i18n.js.map +1 -1
- package/dist/kernel/layout.d.ts +97 -0
- package/dist/kernel/layout.d.ts.map +1 -1
- package/dist/kernel/layout.js +294 -12
- package/dist/kernel/layout.js.map +1 -1
- package/dist/kernel/menu.d.ts +33 -2
- package/dist/kernel/menu.d.ts.map +1 -1
- package/dist/kernel/menu.js +99 -7
- package/dist/kernel/menu.js.map +1 -1
- package/dist/kernel/modules.d.ts +3 -3
- package/dist/kernel/modules.d.ts.map +1 -1
- package/dist/kernel/modules.js +21 -21
- package/dist/kernel/modules.js.map +1 -1
- package/dist/kernel/permissions.d.ts +10 -0
- package/dist/kernel/permissions.d.ts.map +1 -0
- package/dist/kernel/permissions.js +365 -0
- package/dist/kernel/permissions.js.map +1 -0
- package/dist/kernel/schedule.d.ts +23 -0
- package/dist/kernel/schedule.d.ts.map +1 -0
- package/dist/kernel/schedule.js +83 -0
- package/dist/kernel/schedule.js.map +1 -0
- package/dist/kernel/types.d.ts.map +1 -1
- package/dist/kernel/types.js +5 -4
- package/dist/kernel/types.js.map +1 -1
- package/dist/kernel/workspace.d.ts +92 -14
- package/dist/kernel/workspace.d.ts.map +1 -1
- package/dist/kernel/workspace.js +128 -49
- package/dist/kernel/workspace.js.map +1 -1
- package/dist/pdf/assets/Inter-Bold.ttf +0 -0
- package/dist/pdf/assets/Inter-Regular.ttf +0 -0
- package/dist/pdf/assets/Inter-SemiBold.ttf +0 -0
- package/dist/pdf/assets/LICENSE.txt +92 -0
- package/dist/pdf/font.d.ts +10 -0
- package/dist/pdf/font.d.ts.map +1 -0
- package/dist/pdf/font.js +83 -0
- package/dist/pdf/font.js.map +1 -0
- package/dist/pdf/image.d.ts +8 -0
- package/dist/pdf/image.d.ts.map +1 -0
- package/dist/pdf/image.js +94 -0
- package/dist/pdf/image.js.map +1 -0
- package/dist/pdf/index.d.ts +15 -0
- package/dist/pdf/index.d.ts.map +1 -0
- package/dist/pdf/index.js +17 -0
- package/dist/pdf/index.js.map +1 -0
- package/dist/pdf/markup.d.ts +19 -0
- package/dist/pdf/markup.d.ts.map +1 -0
- package/dist/pdf/markup.js +146 -0
- package/dist/pdf/markup.js.map +1 -0
- package/dist/pdf/render.d.ts +14 -0
- package/dist/pdf/render.d.ts.map +1 -0
- package/dist/pdf/render.js +387 -0
- package/dist/pdf/render.js.map +1 -0
- package/dist/scaffold/index.js +6 -6
- package/dist/scaffold/index.js.map +1 -1
- package/dist/scaffold/templates/{app.test.ts.tmpl → deployment.test.ts.tmpl} +3 -3
- package/dist/scaffold/templates/ket.workspace.ts.tmpl +4 -6
- package/dist/scaffold/templates/module.ts.tmpl +1 -4
- package/dist/server/assets.d.ts +23 -0
- package/dist/server/assets.d.ts.map +1 -0
- package/dist/server/assets.js +97 -0
- package/dist/server/assets.js.map +1 -0
- package/dist/server/boot.d.ts +157 -38
- package/dist/server/boot.d.ts.map +1 -1
- package/dist/server/boot.js +360 -138
- package/dist/server/boot.js.map +1 -1
- package/dist/server/config.d.ts +30 -10
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +73 -17
- package/dist/server/config.js.map +1 -1
- package/dist/server/ctx.d.ts +10 -0
- package/dist/server/ctx.d.ts.map +1 -1
- package/dist/server/ctx.js +266 -48
- package/dist/server/ctx.js.map +1 -1
- package/dist/server/development.d.ts +10 -0
- package/dist/server/development.d.ts.map +1 -0
- package/dist/server/development.js +15 -0
- package/dist/server/development.js.map +1 -0
- package/dist/server/fn.d.ts +22 -3
- package/dist/server/fn.d.ts.map +1 -1
- package/dist/server/fn.js +116 -32
- package/dist/server/fn.js.map +1 -1
- package/dist/server/form.d.ts +33 -0
- package/dist/server/form.d.ts.map +1 -0
- package/dist/server/form.js +47 -0
- package/dist/server/form.js.map +1 -0
- package/dist/server/http.d.ts +55 -3
- package/dist/server/http.d.ts.map +1 -1
- package/dist/server/http.js +306 -22
- package/dist/server/http.js.map +1 -1
- package/dist/server/idem.d.ts +3 -2
- package/dist/server/idem.d.ts.map +1 -1
- package/dist/server/idem.js +12 -4
- package/dist/server/idem.js.map +1 -1
- package/dist/server/jobs.js +1 -1
- package/dist/server/jobs.js.map +1 -1
- package/dist/server/log/combinators.d.ts +42 -0
- package/dist/server/log/combinators.d.ts.map +1 -0
- package/dist/server/log/combinators.js +270 -0
- package/dist/server/log/combinators.js.map +1 -0
- package/dist/server/log/console.d.ts +12 -0
- package/dist/server/log/console.d.ts.map +1 -0
- package/dist/server/log/console.js +96 -0
- package/dist/server/log/console.js.map +1 -0
- package/dist/server/log/file.d.ts +11 -0
- package/dist/server/log/file.d.ts.map +1 -0
- package/dist/server/log/file.js +61 -0
- package/dist/server/log/file.js.map +1 -0
- package/dist/server/log/index.d.ts +23 -0
- package/dist/server/log/index.d.ts.map +1 -0
- package/dist/server/log/index.js +45 -0
- package/dist/server/log/index.js.map +1 -0
- package/dist/server/log/logger.d.ts +53 -0
- package/dist/server/log/logger.d.ts.map +1 -0
- package/dist/server/log/logger.js +94 -0
- package/dist/server/log/logger.js.map +1 -0
- package/dist/server/log/memory.d.ts +11 -0
- package/dist/server/log/memory.d.ts.map +1 -0
- package/dist/server/log/memory.js +25 -0
- package/dist/server/log/memory.js.map +1 -0
- package/dist/server/log/types.d.ts +112 -0
- package/dist/server/log/types.d.ts.map +1 -0
- package/dist/server/log/types.js +58 -0
- package/dist/server/log/types.js.map +1 -0
- package/dist/server/policy.d.ts +29 -0
- package/dist/server/policy.d.ts.map +1 -0
- package/dist/server/policy.js +29 -0
- package/dist/server/policy.js.map +1 -0
- package/dist/server/queue.d.ts +1 -1
- package/dist/server/queue.d.ts.map +1 -1
- package/dist/server/queue.js.map +1 -1
- package/dist/server/ratelimit.d.ts +48 -0
- package/dist/server/ratelimit.d.ts.map +1 -0
- package/dist/server/ratelimit.js +102 -0
- package/dist/server/ratelimit.js.map +1 -0
- package/dist/server/respond.d.ts.map +1 -1
- package/dist/server/respond.js +16 -1
- package/dist/server/respond.js.map +1 -1
- package/dist/server/runtime.d.ts +18 -3
- package/dist/server/runtime.d.ts.map +1 -1
- package/dist/server/runtime.js +32 -2
- package/dist/server/runtime.js.map +1 -1
- package/dist/server/schedule.d.ts +18 -0
- package/dist/server/schedule.d.ts.map +1 -0
- package/dist/server/schedule.js +70 -0
- package/dist/server/schedule.js.map +1 -0
- package/dist/server/sequence.d.ts +42 -0
- package/dist/server/sequence.d.ts.map +1 -0
- package/dist/server/sequence.js +97 -0
- package/dist/server/sequence.js.map +1 -0
- package/dist/server/session.d.ts +21 -0
- package/dist/server/session.d.ts.map +1 -1
- package/dist/server/session.js +84 -10
- package/dist/server/session.js.map +1 -1
- package/dist/server/sessionstore.d.ts +3 -1
- package/dist/server/sessionstore.d.ts.map +1 -1
- package/dist/server/sessionstore.js +7 -3
- package/dist/server/sessionstore.js.map +1 -1
- package/dist/server/storage/index.d.ts +1 -1
- package/dist/server/storage/index.d.ts.map +1 -1
- package/dist/server/storage/index.js +61 -1
- package/dist/server/storage/index.js.map +1 -1
- package/dist/server/storage/types.d.ts +8 -0
- package/dist/server/storage/types.d.ts.map +1 -1
- package/dist/server/storage/types.js +58 -0
- package/dist/server/storage/types.js.map +1 -1
- package/dist/server/tenants.d.ts +20 -18
- package/dist/server/tenants.d.ts.map +1 -1
- package/dist/server/tenants.js +133 -84
- package/dist/server/tenants.js.map +1 -1
- package/dist/server/transport/types.js +2 -2
- package/dist/server/transport/types.js.map +1 -1
- package/dist/server/worker.d.ts +19 -3
- package/dist/server/worker.d.ts.map +1 -1
- package/dist/server/worker.js +168 -21
- package/dist/server/worker.js.map +1 -1
- package/dist/testing.d.ts +26 -10
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +41 -14
- package/dist/testing.js.map +1 -1
- package/dist/theme/contracts.d.ts +14 -1
- package/dist/theme/contracts.d.ts.map +1 -1
- package/dist/theme/contracts.js +35 -6
- package/dist/theme/contracts.js.map +1 -1
- package/dist/theme/index.d.ts +1 -1
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +1 -1
- package/dist/theme/index.js.map +1 -1
- package/dist/theme/joint-runtime.d.ts +33 -0
- package/dist/theme/joint-runtime.d.ts.map +1 -0
- package/dist/theme/joint-runtime.js +121 -0
- package/dist/theme/joint-runtime.js.map +1 -0
- package/dist/theme/joints.d.ts.map +1 -1
- package/dist/theme/joints.js +15 -97
- package/dist/theme/joints.js.map +1 -1
- package/dist/theme/ktl/compile.d.ts +4 -0
- package/dist/theme/ktl/compile.d.ts.map +1 -1
- package/dist/theme/ktl/compile.js +54 -1
- package/dist/theme/ktl/compile.js.map +1 -1
- package/dist/theme/ktl/parser.d.ts +4 -0
- package/dist/theme/ktl/parser.d.ts.map +1 -1
- package/dist/theme/ktl/parser.js +8 -0
- package/dist/theme/ktl/parser.js.map +1 -1
- package/dist/theme/render.d.ts +8 -0
- package/dist/theme/render.d.ts.map +1 -1
- package/dist/theme/render.js +88 -118
- package/dist/theme/render.js.map +1 -1
- package/dist/theme/tokens.d.ts +6 -0
- package/dist/theme/tokens.d.ts.map +1 -1
- package/dist/theme/tokens.js +6 -0
- package/dist/theme/tokens.js.map +1 -1
- package/dist/theme/viewmodel.d.ts +24 -0
- package/dist/theme/viewmodel.d.ts.map +1 -1
- package/dist/theme/viewmodel.js +75 -6
- package/dist/theme/viewmodel.js.map +1 -1
- package/dist/types.d.ts +304 -56
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
- package/dist/kernel/apps.d.ts +0 -43
- package/dist/kernel/apps.d.ts.map +0 -1
- package/dist/kernel/apps.js +0 -234
- package/dist/kernel/apps.js.map +0 -1
package/dist/data/pool.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// One adapter per database, kept warm and bounded.
|
|
2
2
|
//
|
|
3
|
-
// This is where the
|
|
3
|
+
// This is where the domain contract model hurts most in practice: a database per tenant means
|
|
4
4
|
// connections multiply by tenant, and a Postgres cluster has a hard ceiling on them.
|
|
5
5
|
// So the pool caps how many databases stay open at once, evicts the least recently
|
|
6
6
|
// used, and refuses to close one that a request is still holding.
|
|
@@ -9,40 +9,128 @@ export function createAdapterPool(o) {
|
|
|
9
9
|
const idleMs = o.idleMs ?? 5 * 60_000;
|
|
10
10
|
const now = o.now ?? (() => Date.now());
|
|
11
11
|
const entries = new Map();
|
|
12
|
+
const closing = new Set();
|
|
13
|
+
let state = 'open';
|
|
14
|
+
let closePromise = null;
|
|
15
|
+
const assertOpen = () => {
|
|
16
|
+
if (state !== 'open')
|
|
17
|
+
throw new Error('adapter pool is closed');
|
|
18
|
+
};
|
|
19
|
+
// Admission is separate from leasing an existing entry. Opening another
|
|
20
|
+
// datastore may have to close an idle one first, and that closing connection
|
|
21
|
+
// still consumes capacity until close() settles. Serializing this small path
|
|
22
|
+
// keeps a later acquire from slipping into the temporarily empty Map slot.
|
|
23
|
+
let admissionTail = Promise.resolve();
|
|
24
|
+
const withAdmission = async (fn) => {
|
|
25
|
+
const previous = admissionTail;
|
|
26
|
+
let release;
|
|
27
|
+
admissionTail = new Promise((resolve) => {
|
|
28
|
+
release = resolve;
|
|
29
|
+
});
|
|
30
|
+
await previous;
|
|
31
|
+
try {
|
|
32
|
+
return await fn();
|
|
33
|
+
}
|
|
34
|
+
finally {
|
|
35
|
+
release();
|
|
36
|
+
}
|
|
37
|
+
};
|
|
12
38
|
const evictOne = async () => {
|
|
13
39
|
let oldest = null;
|
|
14
40
|
for (const [k, e] of entries) {
|
|
15
|
-
if (e.leases > 0)
|
|
41
|
+
if (e.leases > 0 || e.opening || closing.has(e))
|
|
16
42
|
continue;
|
|
17
43
|
if (!oldest || e.lastUsed < oldest[1].lastUsed)
|
|
18
44
|
oldest = [k, e];
|
|
19
45
|
}
|
|
20
46
|
if (!oldest)
|
|
21
47
|
return false;
|
|
22
|
-
|
|
23
|
-
|
|
48
|
+
const [key, entry] = oldest;
|
|
49
|
+
closing.add(entry);
|
|
50
|
+
try {
|
|
51
|
+
await entry.adapter?.close();
|
|
52
|
+
}
|
|
53
|
+
finally {
|
|
54
|
+
// Keep the tombstone visible to size/open until close has settled. The
|
|
55
|
+
// admission lock keeps another datastore from claiming it meanwhile.
|
|
56
|
+
if (entries.get(key) === entry)
|
|
57
|
+
entries.delete(key);
|
|
58
|
+
closing.delete(entry);
|
|
59
|
+
}
|
|
24
60
|
return true;
|
|
25
61
|
};
|
|
26
62
|
const acquire = async (key) => {
|
|
63
|
+
assertOpen();
|
|
27
64
|
let e = entries.get(key);
|
|
65
|
+
if (e && closing.has(e))
|
|
66
|
+
e = undefined;
|
|
28
67
|
if (!e) {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
68
|
+
e = await withAdmission(async () => {
|
|
69
|
+
assertOpen();
|
|
70
|
+
// Another acquire for this key may have won admission while we waited.
|
|
71
|
+
const admitted = entries.get(key);
|
|
72
|
+
if (admitted) {
|
|
73
|
+
// Eviction normally owns the admission lock for the whole close, so a
|
|
74
|
+
// closing entry here would mean that lifecycle invariant was broken.
|
|
75
|
+
// Never lease it even if a future close path forgets that invariant.
|
|
76
|
+
if (closing.has(admitted))
|
|
77
|
+
throw new Error(`adapter pool entry "${key}" is closing during admission`);
|
|
78
|
+
return admitted;
|
|
33
79
|
}
|
|
80
|
+
// Make room before opening, never after: the cap is on connections
|
|
81
|
+
// actually held, including a victim whose close() is still in flight.
|
|
82
|
+
while (entries.size >= max) {
|
|
83
|
+
if (!(await evictOne())) {
|
|
84
|
+
throw new Error(`adapter pool is full (${entries.size}/${max}) and every database is in use`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// close() marks the pool terminal before waiting for admission. An
|
|
88
|
+
// acquire already queued here must not create an adapter after shutdown.
|
|
89
|
+
assertOpen();
|
|
90
|
+
const entry = { adapter: null, leases: 0, lastUsed: now(), opening: null };
|
|
91
|
+
entries.set(key, entry);
|
|
92
|
+
entry.opening = (async () => {
|
|
93
|
+
const adapter = await o.create(key);
|
|
94
|
+
entry.adapter = adapter;
|
|
95
|
+
try {
|
|
96
|
+
await adapter.open();
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
await adapter.close().catch(() => { });
|
|
100
|
+
entry.adapter = null;
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
})().catch((error) => {
|
|
104
|
+
if (entries.get(key) === entry)
|
|
105
|
+
entries.delete(key);
|
|
106
|
+
throw error;
|
|
107
|
+
});
|
|
108
|
+
return entry;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
if (closing.has(e))
|
|
112
|
+
throw new Error(`adapter pool entry "${key}" is closing`);
|
|
113
|
+
assertOpen();
|
|
114
|
+
e.leases++;
|
|
115
|
+
const opening = e.opening;
|
|
116
|
+
if (opening) {
|
|
117
|
+
try {
|
|
118
|
+
await opening;
|
|
34
119
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
120
|
+
catch (error) {
|
|
121
|
+
e.leases = Math.max(0, e.leases - 1);
|
|
122
|
+
throw error;
|
|
123
|
+
}
|
|
124
|
+
if (e.opening === opening)
|
|
125
|
+
e.opening = null;
|
|
39
126
|
}
|
|
40
|
-
if (
|
|
41
|
-
|
|
42
|
-
|
|
127
|
+
if (state !== 'open') {
|
|
128
|
+
e.leases = Math.max(0, e.leases - 1);
|
|
129
|
+
throw new Error('adapter pool is closed');
|
|
43
130
|
}
|
|
44
|
-
e.leases++;
|
|
45
131
|
e.lastUsed = now();
|
|
132
|
+
if (!e.adapter)
|
|
133
|
+
throw new Error(`adapter pool failed to open "${key}"`);
|
|
46
134
|
return e.adapter;
|
|
47
135
|
};
|
|
48
136
|
const release = (key) => {
|
|
@@ -64,22 +152,62 @@ export function createAdapterPool(o) {
|
|
|
64
152
|
release(key);
|
|
65
153
|
}
|
|
66
154
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
155
|
+
evictIdle() {
|
|
156
|
+
return withAdmission(async () => {
|
|
157
|
+
const cutoff = now() - idleMs;
|
|
158
|
+
let n = 0;
|
|
159
|
+
for (const [k, e] of [...entries]) {
|
|
160
|
+
if (e.leases > 0 || e.opening || closing.has(e) || e.lastUsed > cutoff)
|
|
161
|
+
continue;
|
|
162
|
+
closing.add(e);
|
|
163
|
+
try {
|
|
164
|
+
await e.adapter?.close();
|
|
165
|
+
n++;
|
|
166
|
+
}
|
|
167
|
+
finally {
|
|
168
|
+
if (entries.get(k) === e)
|
|
169
|
+
entries.delete(k);
|
|
170
|
+
closing.delete(e);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return n;
|
|
174
|
+
});
|
|
78
175
|
},
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
176
|
+
close() {
|
|
177
|
+
if (closePromise)
|
|
178
|
+
return closePromise;
|
|
179
|
+
// Terminal state is visible synchronously. The actual drain is queued
|
|
180
|
+
// behind admission so it cannot race an eviction or a new datastore.
|
|
181
|
+
state = 'closing';
|
|
182
|
+
closePromise = withAdmission(async () => {
|
|
183
|
+
const errors = [];
|
|
184
|
+
for (const [key, e] of [...entries]) {
|
|
185
|
+
closing.add(e);
|
|
186
|
+
try {
|
|
187
|
+
if (e.opening)
|
|
188
|
+
await e.opening.catch(() => { });
|
|
189
|
+
await e.adapter?.close();
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
errors.push(error);
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
e.adapter = null;
|
|
196
|
+
e.opening = null;
|
|
197
|
+
if (entries.get(key) === e)
|
|
198
|
+
entries.delete(key);
|
|
199
|
+
closing.delete(e);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
entries.clear();
|
|
203
|
+
if (errors.length === 1)
|
|
204
|
+
throw errors[0];
|
|
205
|
+
if (errors.length > 1)
|
|
206
|
+
throw new AggregateError(errors, 'adapter pool failed to close');
|
|
207
|
+
}).finally(() => {
|
|
208
|
+
state = 'closed';
|
|
209
|
+
});
|
|
210
|
+
return closePromise;
|
|
83
211
|
},
|
|
84
212
|
get open() {
|
|
85
213
|
return [...entries.keys()];
|
package/dist/data/pool.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/data/pool.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,
|
|
1
|
+
{"version":3,"file":"pool.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/data/pool.ts"],"names":[],"mappings":"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"}
|
package/dist/data/query.d.ts
CHANGED
|
@@ -18,11 +18,20 @@ export type GroupSpec = {
|
|
|
18
18
|
};
|
|
19
19
|
export type AggregateSpec = {
|
|
20
20
|
fn: 'count';
|
|
21
|
+
col?: Col;
|
|
21
22
|
as: string;
|
|
22
23
|
} | {
|
|
23
|
-
fn: 'countDistinct' | 'sum' | '
|
|
24
|
+
fn: 'countDistinct' | 'sum' | 'min' | 'max';
|
|
24
25
|
col: Col;
|
|
25
26
|
as: string;
|
|
27
|
+
} | {
|
|
28
|
+
fn: 'avg';
|
|
29
|
+
col: Col;
|
|
30
|
+
as: string;
|
|
31
|
+
/** Required for decimal columns so both adapters return the same finite value. */
|
|
32
|
+
scale?: number;
|
|
33
|
+
/** Decimal averages currently support PostgreSQL-compatible half-away-from-zero rounding. */
|
|
34
|
+
rounding?: 'half-away-from-zero';
|
|
26
35
|
};
|
|
27
36
|
export type GroupOrder = {
|
|
28
37
|
by: 'key' | 'count' | string;
|
package/dist/data/query.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/data/query.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAG1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/data/query.ts"],"names":[],"mappings":"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"}
|
package/dist/data/query.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
// mutating one, so a query can be passed through several modules and refined by
|
|
3
3
|
// each without any of them stepping on the others — Ecto's composability with a
|
|
4
4
|
// chainable surface.
|
|
5
|
-
import { exprTouches, and } from './expr.js';
|
|
5
|
+
import { assertCol, exprTouches, and, makeCol } from './expr.js';
|
|
6
6
|
import { tableNameFor } from './migrate.js';
|
|
7
7
|
import { KetError } from '../kernel/errors.js';
|
|
8
|
-
import { assertTimezone } from './time.js';
|
|
8
|
+
import { assertGroupInterval, assertTimezone } from './time.js';
|
|
9
|
+
import { DECIMAL_MAX_CHARS, parseDecimal } from './changeset.js';
|
|
9
10
|
// Column handles are built once from the manifest. No proxies, no magic: a column
|
|
10
11
|
// that does not exist simply is not there, and the expression helpers reject it.
|
|
11
12
|
export function table(manifest, model) {
|
|
@@ -18,8 +19,8 @@ export function table(manifest, model) {
|
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
const t = Object.create(null);
|
|
21
|
-
for (const name of Object.
|
|
22
|
-
t[name] =
|
|
22
|
+
for (const [name, field] of Object.entries(def.fields))
|
|
23
|
+
t[name] = makeCol(model, name, field.base);
|
|
23
24
|
t['$model'] = model;
|
|
24
25
|
t['$columns'] = Object.keys(def.fields);
|
|
25
26
|
return Object.freeze(t);
|
|
@@ -67,7 +68,7 @@ export class Query {
|
|
|
67
68
|
});
|
|
68
69
|
}
|
|
69
70
|
select(...cols) {
|
|
70
|
-
return this.with({ columns: cols.map((c) => c.name) });
|
|
71
|
+
return this.with({ columns: cols.map((c) => assertCol(c).name) });
|
|
71
72
|
}
|
|
72
73
|
/** Additional conditions are ANDed, so a query can be narrowed by several callers. */
|
|
73
74
|
where(...parts) {
|
|
@@ -75,6 +76,8 @@ export class Query {
|
|
|
75
76
|
return this.with({ condition: this.condition ? and(this.condition, next) : next });
|
|
76
77
|
}
|
|
77
78
|
orderBy(...order) {
|
|
79
|
+
for (const item of order)
|
|
80
|
+
assertCol(item.col);
|
|
78
81
|
return this.with({ order: [...this.order, ...order] });
|
|
79
82
|
}
|
|
80
83
|
limit(n) {
|
|
@@ -90,11 +93,14 @@ export class Query {
|
|
|
90
93
|
if (!groups.length)
|
|
91
94
|
throw new KetError({ code: 'E_GROUP_EMPTY', message: 'groupBy requires a field' });
|
|
92
95
|
for (const group of groups) {
|
|
96
|
+
assertCol(group.col);
|
|
93
97
|
if (group.col.model !== this.model)
|
|
94
98
|
throw new KetError({
|
|
95
99
|
code: 'E_GROUP_MODEL',
|
|
96
100
|
message: `cannot group ${this.model} by ${group.col.model}`,
|
|
97
101
|
});
|
|
102
|
+
if (group.interval)
|
|
103
|
+
assertGroupInterval(group.interval);
|
|
98
104
|
if (group.interval && group.timezone)
|
|
99
105
|
assertTimezone(group.timezone);
|
|
100
106
|
}
|
|
@@ -102,7 +108,9 @@ export class Query {
|
|
|
102
108
|
}
|
|
103
109
|
aggregate(...aggregates) {
|
|
104
110
|
for (const aggregate of aggregates) {
|
|
105
|
-
if (aggregate.
|
|
111
|
+
if (aggregate.col)
|
|
112
|
+
assertCol(aggregate.col);
|
|
113
|
+
if (aggregate.col && aggregate.col.model !== this.model)
|
|
106
114
|
throw new KetError({
|
|
107
115
|
code: 'E_GROUP_MODEL',
|
|
108
116
|
message: `cannot aggregate ${aggregate.col.model} on ${this.model}`,
|
|
@@ -133,7 +141,7 @@ export class Query {
|
|
|
133
141
|
for (const group of this.groups)
|
|
134
142
|
s.add(group.col.model);
|
|
135
143
|
for (const aggregate of this.aggregates)
|
|
136
|
-
if (aggregate.
|
|
144
|
+
if (aggregate.col)
|
|
137
145
|
s.add(aggregate.col.model);
|
|
138
146
|
return [...s].sort();
|
|
139
147
|
}
|
|
@@ -148,7 +156,44 @@ export class Query {
|
|
|
148
156
|
params.push(v);
|
|
149
157
|
return ph();
|
|
150
158
|
};
|
|
151
|
-
const colSql = (
|
|
159
|
+
const colSql = (candidate) => {
|
|
160
|
+
const c = assertCol(candidate);
|
|
161
|
+
return `${q(tableNameFor(c.model))}.${q(c.name)}`;
|
|
162
|
+
};
|
|
163
|
+
const isDecimal = (c) => assertCol(c).base === 'decimal';
|
|
164
|
+
const isSqliteDecimal = (c) => dialect === 'sqlite' && isDecimal(c);
|
|
165
|
+
const nulls = (dir) => (dir === 'asc' ? 'LAST' : 'FIRST');
|
|
166
|
+
const orderTerm = (sql, dir) => `${sql} ${dir.toUpperCase()} NULLS ${nulls(dir)}`;
|
|
167
|
+
const bindDecimal = (c, value) => {
|
|
168
|
+
if (!isDecimal(c) || value === null)
|
|
169
|
+
return bind(value);
|
|
170
|
+
const parsed = parseDecimal(value);
|
|
171
|
+
if (!parsed.ok)
|
|
172
|
+
throw new KetError({
|
|
173
|
+
code: parsed.reason === 'size' ? 'E_DECIMAL_TOO_LONG' : 'E_INVALID_DECIMAL',
|
|
174
|
+
message: parsed.reason === 'size'
|
|
175
|
+
? `${c.model}.${c.name} query value exceeds the ${DECIMAL_MAX_CHARS}-character decimal limit`
|
|
176
|
+
: `${c.model}.${c.name} query value must be a finite number or plain decimal string`,
|
|
177
|
+
});
|
|
178
|
+
return bind(parsed.value);
|
|
179
|
+
};
|
|
180
|
+
const decimalOrderSql = (column, dir) => {
|
|
181
|
+
const sign = `ket_decimal_sign(${column})`;
|
|
182
|
+
const exponent = `ket_decimal_exponent(${column})`;
|
|
183
|
+
const digits = `ket_decimal_digits(${column})`;
|
|
184
|
+
const reverse = dir === 'asc' ? 'desc' : 'asc';
|
|
185
|
+
// SQLite keeps decimal columns as TEXT for exact decoding. These components
|
|
186
|
+
// sort normalized scientific parts instead of coercing the value through
|
|
187
|
+
// REAL, including magnitudes beyond Number.MAX_SAFE_INTEGER.
|
|
188
|
+
return [
|
|
189
|
+
orderTerm(sign, dir),
|
|
190
|
+
orderTerm(`CASE WHEN ${sign} < 0 THEN ${exponent} END`, reverse),
|
|
191
|
+
orderTerm(`CASE WHEN ${sign} < 0 THEN ${digits} END`, reverse),
|
|
192
|
+
orderTerm(`CASE WHEN ${sign} > 0 THEN ${exponent} END`, dir),
|
|
193
|
+
orderTerm(`CASE WHEN ${sign} > 0 THEN ${digits} END`, dir),
|
|
194
|
+
].join(', ');
|
|
195
|
+
};
|
|
196
|
+
const decimalOrder = (c, dir) => decimalOrderSql(colSql(c), dir);
|
|
152
197
|
const render = (e) => {
|
|
153
198
|
if (e.op === 'and' || e.op === 'or') {
|
|
154
199
|
if (!e.parts.length)
|
|
@@ -161,27 +206,34 @@ export class Query {
|
|
|
161
206
|
return `${colSql(e.col)} IS ${e.negated ? 'NOT ' : ''}NULL`;
|
|
162
207
|
if (e.op === 'bucket') {
|
|
163
208
|
const column = colSql(e.col);
|
|
209
|
+
// Interpolated into DATE_TRUNC below, so it must be a member of the closed
|
|
210
|
+
// set and nothing else — the value reaches here straight from JSON.
|
|
211
|
+
const interval = assertGroupInterval(e.interval);
|
|
164
212
|
if (dialect === 'postgres') {
|
|
165
|
-
const format =
|
|
213
|
+
const format = interval === 'quarter'
|
|
166
214
|
? 'YYYY-"Q"Q'
|
|
167
|
-
:
|
|
215
|
+
: interval === 'year'
|
|
168
216
|
? 'YYYY'
|
|
169
|
-
:
|
|
217
|
+
: interval === 'month'
|
|
170
218
|
? 'YYYY-MM'
|
|
171
219
|
: 'YYYY-MM-DD';
|
|
172
|
-
return `TO_CHAR(DATE_TRUNC('${
|
|
220
|
+
return `TO_CHAR(DATE_TRUNC('${interval}', ${column} AT TIME ZONE ${bind(e.timezone)}), '${format}') = ${bind(e.value)}`;
|
|
173
221
|
}
|
|
174
|
-
return `ket_date_bucket(${column}, ${bind(
|
|
222
|
+
return `ket_date_bucket(${column}, ${bind(interval)}, ${bind(e.timezone)}) = ${bind(e.value)}`;
|
|
175
223
|
}
|
|
176
224
|
if (e.op === 'like')
|
|
177
225
|
return `${colSql(e.col)} ${e.insensitive && dialect === 'postgres' ? 'ILIKE' : 'LIKE'} ${bind(e.value)}${e.escape ? ` ESCAPE '\\'` : ''}`;
|
|
178
226
|
if (e.op === 'in') {
|
|
179
227
|
if (!e.values.length)
|
|
180
228
|
return '1 = 0';
|
|
181
|
-
|
|
229
|
+
if (isSqliteDecimal(e.col))
|
|
230
|
+
return `(${e.values.map((value) => `ket_decimal_cmp(${colSql(e.col)}, ${bindDecimal(e.col, value)}) = 0`).join(' OR ')})`;
|
|
231
|
+
return `${colSql(e.col)} IN (${e.values.map((value) => bindDecimal(e.col, value)).join(', ')})`;
|
|
182
232
|
}
|
|
233
|
+
if (isSqliteDecimal(e.col))
|
|
234
|
+
return `ket_decimal_cmp(${colSql(e.col)}, ${bindDecimal(e.col, e.value)}) ${e.cmp} 0`;
|
|
183
235
|
const compared = e.numeric && dialect === 'sqlite' ? `CAST(${colSql(e.col)} AS REAL)` : colSql(e.col);
|
|
184
|
-
return `${compared} ${e.cmp} ${
|
|
236
|
+
return `${compared} ${e.cmp} ${bindDecimal(e.col, e.value)}`;
|
|
185
237
|
};
|
|
186
238
|
const t = q(tableNameFor(this.model));
|
|
187
239
|
let text;
|
|
@@ -193,19 +245,66 @@ export class Query {
|
|
|
193
245
|
const groupSql = this.groups.map((group) => {
|
|
194
246
|
const column = colSql(group.col);
|
|
195
247
|
if (!group.interval)
|
|
196
|
-
return column;
|
|
248
|
+
return isSqliteDecimal(group.col) ? `ket_decimal_key(${column})` : column;
|
|
249
|
+
// Same boundary as the bucket expression: interpolated into DATE_TRUNC, so
|
|
250
|
+
// it is validated here rather than trusted from the caller.
|
|
251
|
+
const interval = assertGroupInterval(group.interval);
|
|
197
252
|
const timezone = group.timezone ?? 'UTC';
|
|
198
253
|
if (dialect === 'postgres') {
|
|
199
|
-
if (
|
|
254
|
+
if (interval === 'quarter')
|
|
200
255
|
return `TO_CHAR(DATE_TRUNC('quarter', ${column} AT TIME ZONE ${bind(timezone)}), 'YYYY-"Q"Q')`;
|
|
201
|
-
const format =
|
|
202
|
-
return `TO_CHAR(DATE_TRUNC('${
|
|
256
|
+
const format = interval === 'year' ? 'YYYY' : interval === 'month' ? 'YYYY-MM' : 'YYYY-MM-DD';
|
|
257
|
+
return `TO_CHAR(DATE_TRUNC('${interval}', ${column} AT TIME ZONE ${bind(timezone)}), '${format}')`;
|
|
203
258
|
}
|
|
204
|
-
return `ket_date_bucket(${column}, ${bind(
|
|
259
|
+
return `ket_date_bucket(${column}, ${bind(interval)}, ${bind(timezone)})`;
|
|
205
260
|
});
|
|
206
261
|
const aggregateSql = this.aggregates.map((aggregate) => {
|
|
207
262
|
if (aggregate.fn === 'count')
|
|
208
|
-
return `COUNT(*) AS ${q(aggregate.as)}`;
|
|
263
|
+
return `COUNT(${aggregate.col ? colSql(aggregate.col) : '*'}) AS ${q(aggregate.as)}`;
|
|
264
|
+
if (aggregate.fn === 'avg' && isDecimal(aggregate.col)) {
|
|
265
|
+
if (aggregate.rounding !== 'half-away-from-zero' || aggregate.scale === undefined)
|
|
266
|
+
throw new KetError({
|
|
267
|
+
code: 'E_DECIMAL_AVG_ROUNDING_REQUIRED',
|
|
268
|
+
message: 'a decimal average requires an explicit finite scale and rounding rule',
|
|
269
|
+
hint: `add scale and rounding: 'half-away-from-zero', or request sum and count and divide in the domain`,
|
|
270
|
+
});
|
|
271
|
+
if (!Number.isSafeInteger(aggregate.scale) ||
|
|
272
|
+
aggregate.scale < 0 ||
|
|
273
|
+
aggregate.scale > DECIMAL_MAX_CHARS)
|
|
274
|
+
throw new KetError({
|
|
275
|
+
code: 'E_DECIMAL_AVG_SCALE',
|
|
276
|
+
message: `decimal average scale must be an integer from 0 to ${DECIMAL_MAX_CHARS}`,
|
|
277
|
+
});
|
|
278
|
+
const column = colSql(aggregate.col);
|
|
279
|
+
let average;
|
|
280
|
+
if (dialect === 'sqlite')
|
|
281
|
+
average = `ket_decimal_avg(${column}, ${bind(aggregate.scale)})`;
|
|
282
|
+
else {
|
|
283
|
+
// PostgreSQL's numeric AVG divides before ROUND and chooses a finite
|
|
284
|
+
// internal result scale, so high requested scales can no longer
|
|
285
|
+
// recover the discarded digits. Scale the exact SUM first, then use
|
|
286
|
+
// integer quotient/remainder arithmetic for half-away-from-zero.
|
|
287
|
+
const scale = bind(aggregate.scale);
|
|
288
|
+
const factor = `CAST(('1e' || CAST(${scale} AS TEXT)) AS NUMERIC)`;
|
|
289
|
+
const sum = `SUM(${column})`;
|
|
290
|
+
const count = `COUNT(${column})`;
|
|
291
|
+
const shifted = `ABS(${sum}) * ${factor}`;
|
|
292
|
+
const rounded = `DIV(${shifted}, ${count}) + CASE WHEN MOD(${shifted}, ${count}) * 2 >= ${count} THEN 1 ELSE 0 END`;
|
|
293
|
+
// Multiplication by an exact negative-power factor preserves the
|
|
294
|
+
// requested display scale. PostgreSQL numeric division derives a
|
|
295
|
+
// result scale from operand precision and can truncate this back
|
|
296
|
+
// to 20 digits before the client receives it.
|
|
297
|
+
const inverseFactor = `CAST(('1e-' || CAST(${scale} AS TEXT)) AS NUMERIC)`;
|
|
298
|
+
average = `CASE WHEN ${count} = 0 THEN NULL ELSE SIGN(${sum}) * (${rounded}) * ${inverseFactor} END`;
|
|
299
|
+
}
|
|
300
|
+
return `${average} AS ${q(aggregate.as)}`;
|
|
301
|
+
}
|
|
302
|
+
if (isSqliteDecimal(aggregate.col)) {
|
|
303
|
+
const column = colSql(aggregate.col);
|
|
304
|
+
if (aggregate.fn === 'countDistinct')
|
|
305
|
+
return `COUNT(DISTINCT ket_decimal_key(${column})) AS ${q(aggregate.as)}`;
|
|
306
|
+
return `ket_decimal_${aggregate.fn}(${column}) AS ${q(aggregate.as)}`;
|
|
307
|
+
}
|
|
209
308
|
const fn = aggregate.fn === 'countDistinct' ? 'COUNT' : aggregate.fn.toUpperCase();
|
|
210
309
|
const body = aggregate.fn === 'countDistinct' ? `DISTINCT ${colSql(aggregate.col)}` : colSql(aggregate.col);
|
|
211
310
|
return `${fn}(${body}) AS ${q(aggregate.as)}`;
|
|
@@ -219,25 +318,35 @@ export class Query {
|
|
|
219
318
|
if (this.kind === 'group') {
|
|
220
319
|
text += ` GROUP BY ${this.groups.map((_, i) => String(i + 1)).join(', ')}`;
|
|
221
320
|
if (this.groupOrder.length) {
|
|
222
|
-
const aliases = new
|
|
321
|
+
const aliases = new Map(this.aggregates.map((aggregate) => [aggregate.as, aggregate]));
|
|
223
322
|
text += ` ORDER BY ${this.groupOrder
|
|
224
323
|
.map((order) => {
|
|
324
|
+
const aggregate = aliases.get(order.by);
|
|
225
325
|
const by = order.by === 'key'
|
|
226
326
|
? q('__group0')
|
|
227
327
|
: order.by === 'count'
|
|
228
328
|
? q('__count')
|
|
229
|
-
:
|
|
329
|
+
: aggregate
|
|
230
330
|
? q(order.by)
|
|
231
331
|
: null;
|
|
232
332
|
if (!by)
|
|
233
333
|
throw new KetError({ code: 'E_GROUP_ORDER', message: `unknown group order "${order.by}"` });
|
|
234
|
-
|
|
334
|
+
const decimal = (order.by === 'key' && !this.groups[0].interval && isSqliteDecimal(this.groups[0].col)) ||
|
|
335
|
+
(aggregate !== undefined &&
|
|
336
|
+
aggregate.fn !== 'count' &&
|
|
337
|
+
aggregate.fn !== 'countDistinct' &&
|
|
338
|
+
isSqliteDecimal(aggregate.col));
|
|
339
|
+
if (decimal)
|
|
340
|
+
return decimalOrderSql(by, order.dir);
|
|
341
|
+
return orderTerm(by, order.dir);
|
|
235
342
|
})
|
|
236
343
|
.join(', ')}`;
|
|
237
344
|
}
|
|
238
345
|
}
|
|
239
346
|
else if (this.order.length)
|
|
240
|
-
text += ` ORDER BY ${this.order
|
|
347
|
+
text += ` ORDER BY ${this.order
|
|
348
|
+
.map((o) => (isSqliteDecimal(o.col) ? decimalOrder(o.col, o.dir) : orderTerm(colSql(o.col), o.dir)))
|
|
349
|
+
.join(', ')}`;
|
|
241
350
|
if (this.limitN != null)
|
|
242
351
|
text += ` LIMIT ${bind(this.limitN)}`;
|
|
243
352
|
if (this.offsetN != null)
|
|
@@ -263,6 +372,6 @@ export class Query {
|
|
|
263
372
|
}
|
|
264
373
|
export const from = (t) => new Query({ kind: 'select', model: t.$model });
|
|
265
374
|
export const deleteFrom = (t) => new Query({ kind: 'delete', model: t.$model });
|
|
266
|
-
export const asc = (col) => ({ col, dir: 'asc' });
|
|
267
|
-
export const desc = (col) => ({ col, dir: 'desc' });
|
|
375
|
+
export const asc = (col) => ({ col: assertCol(col), dir: 'asc' });
|
|
376
|
+
export const desc = (col) => ({ col: assertCol(col), dir: 'desc' });
|
|
268
377
|
//# sourceMappingURL=query.js.map
|