@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.
Files changed (299) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -2
  3. package/dist/agent/capabilities.d.ts +1 -3
  4. package/dist/agent/capabilities.d.ts.map +1 -1
  5. package/dist/agent/capabilities.js +10 -1
  6. package/dist/agent/capabilities.js.map +1 -1
  7. package/dist/agent/permissions.d.ts +43 -1
  8. package/dist/agent/permissions.d.ts.map +1 -1
  9. package/dist/agent/permissions.js +50 -2
  10. package/dist/agent/permissions.js.map +1 -1
  11. package/dist/cli.js +246 -97
  12. package/dist/cli.js.map +1 -1
  13. package/dist/codegen/dts.d.ts.map +1 -1
  14. package/dist/codegen/dts.js +1 -0
  15. package/dist/codegen/dts.js.map +1 -1
  16. package/dist/data/changeset.d.ts +12 -0
  17. package/dist/data/changeset.d.ts.map +1 -1
  18. package/dist/data/changeset.js +73 -17
  19. package/dist/data/changeset.js.map +1 -1
  20. package/dist/data/expr.d.ts +16 -0
  21. package/dist/data/expr.d.ts.map +1 -1
  22. package/dist/data/expr.js +33 -5
  23. package/dist/data/expr.js.map +1 -1
  24. package/dist/data/fleet.d.ts +41 -1
  25. package/dist/data/fleet.d.ts.map +1 -1
  26. package/dist/data/fleet.js +100 -11
  27. package/dist/data/fleet.js.map +1 -1
  28. package/dist/data/migrate.d.ts +20 -0
  29. package/dist/data/migrate.d.ts.map +1 -1
  30. package/dist/data/migrate.js +86 -3
  31. package/dist/data/migrate.js.map +1 -1
  32. package/dist/data/physical.d.ts +5 -0
  33. package/dist/data/physical.d.ts.map +1 -0
  34. package/dist/data/physical.js +196 -0
  35. package/dist/data/physical.js.map +1 -0
  36. package/dist/data/pool.d.ts +2 -1
  37. package/dist/data/pool.d.ts.map +1 -1
  38. package/dist/data/pool.js +159 -31
  39. package/dist/data/pool.js.map +1 -1
  40. package/dist/data/query.d.ts +10 -1
  41. package/dist/data/query.d.ts.map +1 -1
  42. package/dist/data/query.js +136 -27
  43. package/dist/data/query.js.map +1 -1
  44. package/dist/data/sqlite.d.ts.map +1 -1
  45. package/dist/data/sqlite.js +350 -52
  46. package/dist/data/sqlite.js.map +1 -1
  47. package/dist/data/time.d.ts +13 -0
  48. package/dist/data/time.d.ts.map +1 -1
  49. package/dist/data/time.js +61 -17
  50. package/dist/data/time.js.map +1 -1
  51. package/dist/index.d.ts +43 -22
  52. package/dist/index.d.ts.map +1 -1
  53. package/dist/index.js +32 -16
  54. package/dist/index.js.map +1 -1
  55. package/dist/kernel/audit.d.ts +27 -0
  56. package/dist/kernel/audit.d.ts.map +1 -0
  57. package/dist/kernel/audit.js +67 -0
  58. package/dist/kernel/audit.js.map +1 -0
  59. package/dist/kernel/classification.d.ts +26 -0
  60. package/dist/kernel/classification.d.ts.map +1 -0
  61. package/dist/kernel/classification.js +73 -0
  62. package/dist/kernel/classification.js.map +1 -0
  63. package/dist/kernel/compose.d.ts +6 -2
  64. package/dist/kernel/compose.d.ts.map +1 -1
  65. package/dist/kernel/compose.js +269 -15
  66. package/dist/kernel/compose.js.map +1 -1
  67. package/dist/kernel/define.d.ts +2 -3
  68. package/dist/kernel/define.d.ts.map +1 -1
  69. package/dist/kernel/define.js +22 -12
  70. package/dist/kernel/define.js.map +1 -1
  71. package/dist/kernel/diff.d.ts.map +1 -1
  72. package/dist/kernel/diff.js +28 -4
  73. package/dist/kernel/diff.js.map +1 -1
  74. package/dist/kernel/graph.js +1 -1
  75. package/dist/kernel/graph.js.map +1 -1
  76. package/dist/kernel/i18n.d.ts +10 -1
  77. package/dist/kernel/i18n.d.ts.map +1 -1
  78. package/dist/kernel/i18n.js +34 -1
  79. package/dist/kernel/i18n.js.map +1 -1
  80. package/dist/kernel/layout.d.ts +97 -0
  81. package/dist/kernel/layout.d.ts.map +1 -1
  82. package/dist/kernel/layout.js +294 -12
  83. package/dist/kernel/layout.js.map +1 -1
  84. package/dist/kernel/menu.d.ts +33 -2
  85. package/dist/kernel/menu.d.ts.map +1 -1
  86. package/dist/kernel/menu.js +99 -7
  87. package/dist/kernel/menu.js.map +1 -1
  88. package/dist/kernel/modules.d.ts +3 -3
  89. package/dist/kernel/modules.d.ts.map +1 -1
  90. package/dist/kernel/modules.js +21 -21
  91. package/dist/kernel/modules.js.map +1 -1
  92. package/dist/kernel/permissions.d.ts +10 -0
  93. package/dist/kernel/permissions.d.ts.map +1 -0
  94. package/dist/kernel/permissions.js +365 -0
  95. package/dist/kernel/permissions.js.map +1 -0
  96. package/dist/kernel/schedule.d.ts +23 -0
  97. package/dist/kernel/schedule.d.ts.map +1 -0
  98. package/dist/kernel/schedule.js +83 -0
  99. package/dist/kernel/schedule.js.map +1 -0
  100. package/dist/kernel/types.d.ts.map +1 -1
  101. package/dist/kernel/types.js +5 -4
  102. package/dist/kernel/types.js.map +1 -1
  103. package/dist/kernel/workspace.d.ts +92 -14
  104. package/dist/kernel/workspace.d.ts.map +1 -1
  105. package/dist/kernel/workspace.js +128 -49
  106. package/dist/kernel/workspace.js.map +1 -1
  107. package/dist/pdf/assets/Inter-Bold.ttf +0 -0
  108. package/dist/pdf/assets/Inter-Regular.ttf +0 -0
  109. package/dist/pdf/assets/Inter-SemiBold.ttf +0 -0
  110. package/dist/pdf/assets/LICENSE.txt +92 -0
  111. package/dist/pdf/font.d.ts +10 -0
  112. package/dist/pdf/font.d.ts.map +1 -0
  113. package/dist/pdf/font.js +83 -0
  114. package/dist/pdf/font.js.map +1 -0
  115. package/dist/pdf/image.d.ts +8 -0
  116. package/dist/pdf/image.d.ts.map +1 -0
  117. package/dist/pdf/image.js +94 -0
  118. package/dist/pdf/image.js.map +1 -0
  119. package/dist/pdf/index.d.ts +15 -0
  120. package/dist/pdf/index.d.ts.map +1 -0
  121. package/dist/pdf/index.js +17 -0
  122. package/dist/pdf/index.js.map +1 -0
  123. package/dist/pdf/markup.d.ts +19 -0
  124. package/dist/pdf/markup.d.ts.map +1 -0
  125. package/dist/pdf/markup.js +146 -0
  126. package/dist/pdf/markup.js.map +1 -0
  127. package/dist/pdf/render.d.ts +14 -0
  128. package/dist/pdf/render.d.ts.map +1 -0
  129. package/dist/pdf/render.js +387 -0
  130. package/dist/pdf/render.js.map +1 -0
  131. package/dist/scaffold/index.js +6 -6
  132. package/dist/scaffold/index.js.map +1 -1
  133. package/dist/scaffold/templates/{app.test.ts.tmpl → deployment.test.ts.tmpl} +3 -3
  134. package/dist/scaffold/templates/ket.workspace.ts.tmpl +4 -6
  135. package/dist/scaffold/templates/module.ts.tmpl +1 -4
  136. package/dist/server/assets.d.ts +23 -0
  137. package/dist/server/assets.d.ts.map +1 -0
  138. package/dist/server/assets.js +97 -0
  139. package/dist/server/assets.js.map +1 -0
  140. package/dist/server/boot.d.ts +157 -38
  141. package/dist/server/boot.d.ts.map +1 -1
  142. package/dist/server/boot.js +360 -138
  143. package/dist/server/boot.js.map +1 -1
  144. package/dist/server/config.d.ts +30 -10
  145. package/dist/server/config.d.ts.map +1 -1
  146. package/dist/server/config.js +73 -17
  147. package/dist/server/config.js.map +1 -1
  148. package/dist/server/ctx.d.ts +10 -0
  149. package/dist/server/ctx.d.ts.map +1 -1
  150. package/dist/server/ctx.js +266 -48
  151. package/dist/server/ctx.js.map +1 -1
  152. package/dist/server/development.d.ts +10 -0
  153. package/dist/server/development.d.ts.map +1 -0
  154. package/dist/server/development.js +15 -0
  155. package/dist/server/development.js.map +1 -0
  156. package/dist/server/fn.d.ts +22 -3
  157. package/dist/server/fn.d.ts.map +1 -1
  158. package/dist/server/fn.js +116 -32
  159. package/dist/server/fn.js.map +1 -1
  160. package/dist/server/form.d.ts +33 -0
  161. package/dist/server/form.d.ts.map +1 -0
  162. package/dist/server/form.js +47 -0
  163. package/dist/server/form.js.map +1 -0
  164. package/dist/server/http.d.ts +55 -3
  165. package/dist/server/http.d.ts.map +1 -1
  166. package/dist/server/http.js +306 -22
  167. package/dist/server/http.js.map +1 -1
  168. package/dist/server/idem.d.ts +3 -2
  169. package/dist/server/idem.d.ts.map +1 -1
  170. package/dist/server/idem.js +12 -4
  171. package/dist/server/idem.js.map +1 -1
  172. package/dist/server/jobs.js +1 -1
  173. package/dist/server/jobs.js.map +1 -1
  174. package/dist/server/log/combinators.d.ts +42 -0
  175. package/dist/server/log/combinators.d.ts.map +1 -0
  176. package/dist/server/log/combinators.js +270 -0
  177. package/dist/server/log/combinators.js.map +1 -0
  178. package/dist/server/log/console.d.ts +12 -0
  179. package/dist/server/log/console.d.ts.map +1 -0
  180. package/dist/server/log/console.js +96 -0
  181. package/dist/server/log/console.js.map +1 -0
  182. package/dist/server/log/file.d.ts +11 -0
  183. package/dist/server/log/file.d.ts.map +1 -0
  184. package/dist/server/log/file.js +61 -0
  185. package/dist/server/log/file.js.map +1 -0
  186. package/dist/server/log/index.d.ts +23 -0
  187. package/dist/server/log/index.d.ts.map +1 -0
  188. package/dist/server/log/index.js +45 -0
  189. package/dist/server/log/index.js.map +1 -0
  190. package/dist/server/log/logger.d.ts +53 -0
  191. package/dist/server/log/logger.d.ts.map +1 -0
  192. package/dist/server/log/logger.js +94 -0
  193. package/dist/server/log/logger.js.map +1 -0
  194. package/dist/server/log/memory.d.ts +11 -0
  195. package/dist/server/log/memory.d.ts.map +1 -0
  196. package/dist/server/log/memory.js +25 -0
  197. package/dist/server/log/memory.js.map +1 -0
  198. package/dist/server/log/types.d.ts +112 -0
  199. package/dist/server/log/types.d.ts.map +1 -0
  200. package/dist/server/log/types.js +58 -0
  201. package/dist/server/log/types.js.map +1 -0
  202. package/dist/server/policy.d.ts +29 -0
  203. package/dist/server/policy.d.ts.map +1 -0
  204. package/dist/server/policy.js +29 -0
  205. package/dist/server/policy.js.map +1 -0
  206. package/dist/server/queue.d.ts +1 -1
  207. package/dist/server/queue.d.ts.map +1 -1
  208. package/dist/server/queue.js.map +1 -1
  209. package/dist/server/ratelimit.d.ts +48 -0
  210. package/dist/server/ratelimit.d.ts.map +1 -0
  211. package/dist/server/ratelimit.js +102 -0
  212. package/dist/server/ratelimit.js.map +1 -0
  213. package/dist/server/respond.d.ts.map +1 -1
  214. package/dist/server/respond.js +16 -1
  215. package/dist/server/respond.js.map +1 -1
  216. package/dist/server/runtime.d.ts +18 -3
  217. package/dist/server/runtime.d.ts.map +1 -1
  218. package/dist/server/runtime.js +32 -2
  219. package/dist/server/runtime.js.map +1 -1
  220. package/dist/server/schedule.d.ts +18 -0
  221. package/dist/server/schedule.d.ts.map +1 -0
  222. package/dist/server/schedule.js +70 -0
  223. package/dist/server/schedule.js.map +1 -0
  224. package/dist/server/sequence.d.ts +42 -0
  225. package/dist/server/sequence.d.ts.map +1 -0
  226. package/dist/server/sequence.js +97 -0
  227. package/dist/server/sequence.js.map +1 -0
  228. package/dist/server/session.d.ts +21 -0
  229. package/dist/server/session.d.ts.map +1 -1
  230. package/dist/server/session.js +84 -10
  231. package/dist/server/session.js.map +1 -1
  232. package/dist/server/sessionstore.d.ts +3 -1
  233. package/dist/server/sessionstore.d.ts.map +1 -1
  234. package/dist/server/sessionstore.js +7 -3
  235. package/dist/server/sessionstore.js.map +1 -1
  236. package/dist/server/storage/index.d.ts +1 -1
  237. package/dist/server/storage/index.d.ts.map +1 -1
  238. package/dist/server/storage/index.js +61 -1
  239. package/dist/server/storage/index.js.map +1 -1
  240. package/dist/server/storage/types.d.ts +8 -0
  241. package/dist/server/storage/types.d.ts.map +1 -1
  242. package/dist/server/storage/types.js +58 -0
  243. package/dist/server/storage/types.js.map +1 -1
  244. package/dist/server/tenants.d.ts +20 -18
  245. package/dist/server/tenants.d.ts.map +1 -1
  246. package/dist/server/tenants.js +133 -84
  247. package/dist/server/tenants.js.map +1 -1
  248. package/dist/server/transport/types.js +2 -2
  249. package/dist/server/transport/types.js.map +1 -1
  250. package/dist/server/worker.d.ts +19 -3
  251. package/dist/server/worker.d.ts.map +1 -1
  252. package/dist/server/worker.js +168 -21
  253. package/dist/server/worker.js.map +1 -1
  254. package/dist/testing.d.ts +26 -10
  255. package/dist/testing.d.ts.map +1 -1
  256. package/dist/testing.js +41 -14
  257. package/dist/testing.js.map +1 -1
  258. package/dist/theme/contracts.d.ts +14 -1
  259. package/dist/theme/contracts.d.ts.map +1 -1
  260. package/dist/theme/contracts.js +35 -6
  261. package/dist/theme/contracts.js.map +1 -1
  262. package/dist/theme/index.d.ts +1 -1
  263. package/dist/theme/index.d.ts.map +1 -1
  264. package/dist/theme/index.js +1 -1
  265. package/dist/theme/index.js.map +1 -1
  266. package/dist/theme/joint-runtime.d.ts +33 -0
  267. package/dist/theme/joint-runtime.d.ts.map +1 -0
  268. package/dist/theme/joint-runtime.js +121 -0
  269. package/dist/theme/joint-runtime.js.map +1 -0
  270. package/dist/theme/joints.d.ts.map +1 -1
  271. package/dist/theme/joints.js +15 -97
  272. package/dist/theme/joints.js.map +1 -1
  273. package/dist/theme/ktl/compile.d.ts +4 -0
  274. package/dist/theme/ktl/compile.d.ts.map +1 -1
  275. package/dist/theme/ktl/compile.js +54 -1
  276. package/dist/theme/ktl/compile.js.map +1 -1
  277. package/dist/theme/ktl/parser.d.ts +4 -0
  278. package/dist/theme/ktl/parser.d.ts.map +1 -1
  279. package/dist/theme/ktl/parser.js +8 -0
  280. package/dist/theme/ktl/parser.js.map +1 -1
  281. package/dist/theme/render.d.ts +8 -0
  282. package/dist/theme/render.d.ts.map +1 -1
  283. package/dist/theme/render.js +88 -118
  284. package/dist/theme/render.js.map +1 -1
  285. package/dist/theme/tokens.d.ts +6 -0
  286. package/dist/theme/tokens.d.ts.map +1 -1
  287. package/dist/theme/tokens.js +6 -0
  288. package/dist/theme/tokens.js.map +1 -1
  289. package/dist/theme/viewmodel.d.ts +24 -0
  290. package/dist/theme/viewmodel.d.ts.map +1 -1
  291. package/dist/theme/viewmodel.js +75 -6
  292. package/dist/theme/viewmodel.js.map +1 -1
  293. package/dist/types.d.ts +304 -56
  294. package/dist/types.d.ts.map +1 -1
  295. package/package.json +6 -2
  296. package/dist/kernel/apps.d.ts +0 -43
  297. package/dist/kernel/apps.d.ts.map +0 -1
  298. package/dist/kernel/apps.js +0 -234
  299. 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 Odoo model hurts most in practice: a database per tenant means
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
- entries.delete(oldest[0]);
23
- await oldest[1].adapter.close();
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
- // Make room before opening, never after: the cap is on connections actually held.
30
- while (entries.size >= max) {
31
- if (!(await evictOne())) {
32
- throw new Error(`adapter pool is full (${entries.size}/${max}) and every database is in use`);
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
- const adapter = o.create(key);
36
- e = { adapter, leases: 0, lastUsed: now(), opening: null };
37
- entries.set(key, e);
38
- e.opening = adapter.open();
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 (e.opening) {
41
- await e.opening;
42
- e.opening = null;
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
- async evictIdle() {
68
- const cutoff = now() - idleMs;
69
- let n = 0;
70
- for (const [k, e] of [...entries]) {
71
- if (e.leases > 0 || e.lastUsed > cutoff)
72
- continue;
73
- entries.delete(k);
74
- await e.adapter.close();
75
- n++;
76
- }
77
- return n;
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
- async close() {
80
- for (const [, e] of entries)
81
- await e.adapter.close();
82
- entries.clear();
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()];
@@ -1 +1 @@
1
- {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/data/pool.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,EAAE;AACF,mFAAmF;AACnF,qFAAqF;AACrF,mFAAmF;AACnF,kEAAkE;AAyBlE,MAAM,UAAU,iBAAiB,CAAC,CAAc;IAC9C,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,CAAA;IACvB,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,GAAG,MAAM,CAAA;IACrC,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAA;IACvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAiB,CAAA;IAExC,MAAM,QAAQ,GAAG,KAAK,IAAsB,EAAE;QAC5C,IAAI,MAAM,GAA2B,IAAI,CAAA;QACzC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;gBAAE,SAAQ;YAC1B,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,QAAQ;gBAAE,MAAM,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACjE,CAAC;QACD,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAA;QACzB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QACzB,MAAM,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QAC/B,OAAO,IAAI,CAAA;IACb,CAAC,CAAA;IAED,MAAM,OAAO,GAAG,KAAK,EAAE,GAAW,EAAoB,EAAE;QACtD,IAAI,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACxB,IAAI,CAAC,CAAC,EAAE,CAAC;YACP,kFAAkF;YAClF,OAAO,OAAO,CAAC,IAAI,IAAI,GAAG,EAAE,CAAC;gBAC3B,IAAI,CAAC,CAAC,MAAM,QAAQ,EAAE,CAAC,EAAE,CAAC;oBACxB,MAAM,IAAI,KAAK,CAAC,yBAAyB,OAAO,CAAC,IAAI,IAAI,GAAG,gCAAgC,CAAC,CAAA;gBAC/F,CAAC;YACH,CAAC;YACD,MAAM,OAAO,GAAG,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YAC7B,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;YAC1D,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACnB,CAAC,CAAC,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;QAC5B,CAAC;QACD,IAAI,CAAC,CAAC,OAAO,EAAE,CAAC;YACd,MAAM,CAAC,CAAC,OAAO,CAAA;YACf,CAAC,CAAC,OAAO,GAAG,IAAI,CAAA;QAClB,CAAC;QACD,CAAC,CAAC,MAAM,EAAE,CAAA;QACV,CAAC,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAA;QAClB,OAAO,CAAC,CAAC,OAAO,CAAA;IAClB,CAAC,CAAA;IAED,MAAM,OAAO,GAAG,CAAC,GAAW,EAAQ,EAAE;QACpC,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QAC1B,IAAI,CAAC,CAAC;YAAE,OAAM;QACd,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QACpC,CAAC,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAA;IACpB,CAAC,CAAA;IAED,OAAO;QACL,OAAO;QACP,OAAO;QACP,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE;YAChB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAA;YAClC,IAAI,CAAC;gBACH,OAAO,MAAM,EAAE,CAAC,OAAO,CAAC,CAAA;YAC1B,CAAC;oBAAS,CAAC;gBACT,OAAO,CAAC,GAAG,CAAC,CAAA;YACd,CAAC;QACH,CAAC;QACD,KAAK,CAAC,SAAS;YACb,MAAM,MAAM,GAAG,GAAG,EAAE,GAAG,MAAM,CAAA;YAC7B,IAAI,CAAC,GAAG,CAAC,CAAA;YACT,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,EAAE,CAAC;gBAClC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,CAAC,QAAQ,GAAG,MAAM;oBAAE,SAAQ;gBACjD,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAA;gBACjB,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;gBACvB,CAAC,EAAE,CAAA;YACL,CAAC;YACD,OAAO,CAAC,CAAA;QACV,CAAC;QACD,KAAK,CAAC,KAAK;YACT,KAAK,MAAM,CAAC,EAAE,CAAC,CAAC,IAAI,OAAO;gBAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;YACpD,OAAO,CAAC,KAAK,EAAE,CAAA;QACjB,CAAC;QACD,IAAI,IAAI;YACN,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;QAC5B,CAAC;QACD,IAAI,IAAI;YACN,OAAO,OAAO,CAAC,IAAI,CAAA;QACrB,CAAC;KACF,CAAA;AACH,CAAC"}
1
+ {"version":3,"file":"pool.js","sourceRoot":"","sources":["../../../../../../packages/ketjs/src/data/pool.ts"],"names":[],"mappings":"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"}
@@ -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' | 'avg' | 'min' | 'max';
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;
@@ -1 +1 @@
1
- {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/data/query.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../../../../packages/ketjs/src/data/query.ts"],"names":[],"mappings":"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"}
@@ -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.keys(def.fields))
22
- t[name] = Object.freeze({ model, 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.fn !== 'count' && aggregate.col.model !== this.model)
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.fn !== 'count')
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 = (c) => `${q(tableNameFor(c.model))}.${q(c.name)}`;
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 = e.interval === 'quarter'
213
+ const format = interval === 'quarter'
166
214
  ? 'YYYY-"Q"Q'
167
- : e.interval === 'year'
215
+ : interval === 'year'
168
216
  ? 'YYYY'
169
- : e.interval === 'month'
217
+ : interval === 'month'
170
218
  ? 'YYYY-MM'
171
219
  : 'YYYY-MM-DD';
172
- return `TO_CHAR(DATE_TRUNC('${e.interval}', ${column} AT TIME ZONE ${bind(e.timezone)}), '${format}') = ${bind(e.value)}`;
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(e.interval)}, ${bind(e.timezone)}) = ${bind(e.value)}`;
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
- return `${colSql(e.col)} IN (${e.values.map(bind).join(', ')})`;
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} ${bind(e.value)}`;
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 (group.interval === 'quarter')
254
+ if (interval === 'quarter')
200
255
  return `TO_CHAR(DATE_TRUNC('quarter', ${column} AT TIME ZONE ${bind(timezone)}), 'YYYY-"Q"Q')`;
201
- const format = group.interval === 'year' ? 'YYYY' : group.interval === 'month' ? 'YYYY-MM' : 'YYYY-MM-DD';
202
- return `TO_CHAR(DATE_TRUNC('${group.interval}', ${column} AT TIME ZONE ${bind(timezone)}), '${format}')`;
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(group.interval)}, ${bind(timezone)})`;
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 Set(this.aggregates.map((a) => a.as));
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
- : aliases.has(order.by)
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
- return `${by} ${order.dir.toUpperCase()}`;
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.map((o) => `${colSql(o.col)} ${o.dir.toUpperCase()}`).join(', ')}`;
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