@slopware/sloppy-linux-x64 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (434) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +5 -0
  3. package/bin/sloppy +0 -0
  4. package/bin/sloppyc +0 -0
  5. package/docs/KNOWN_LIMITATIONS.md +16 -0
  6. package/docs/LICENSES.md +6 -0
  7. package/docs/NOTICE.md +8 -0
  8. package/examples/README.md +140 -0
  9. package/examples/auth-api/README.md +20 -0
  10. package/examples/auth-api/app.js +61 -0
  11. package/examples/auth-api/appsettings.json +7 -0
  12. package/examples/auth-api/sloppy.json +5 -0
  13. package/examples/cache-basic/README.md +9 -0
  14. package/examples/cache-basic/app.js +32 -0
  15. package/examples/cache-hybrid-postgres/README.md +10 -0
  16. package/examples/cache-hybrid-postgres/app.js +27 -0
  17. package/examples/cache-output-api/README.md +10 -0
  18. package/examples/cache-output-api/app.js +35 -0
  19. package/examples/codec-base64-hex/README.md +14 -0
  20. package/examples/codec-base64-hex/app.js +15 -0
  21. package/examples/codec-checksums/README.md +15 -0
  22. package/examples/codec-checksums/app.js +8 -0
  23. package/examples/codec-compression/README.md +13 -0
  24. package/examples/codec-compression/app.js +9 -0
  25. package/examples/codec-streaming-compression/README.md +19 -0
  26. package/examples/codec-streaming-compression/app.js +16 -0
  27. package/examples/codec-text-binary/README.md +16 -0
  28. package/examples/codec-text-binary/app.js +17 -0
  29. package/examples/compiler-hello/README.md +71 -0
  30. package/examples/compiler-hello/app.js +7 -0
  31. package/examples/compiler-hello/expected/app.js +8 -0
  32. package/examples/compiler-hello/expected/app.js.map +53 -0
  33. package/examples/compiler-hello/expected/app.plan.json +229 -0
  34. package/examples/compiler-hello/expected/routes.slrt +0 -0
  35. package/examples/config-basic/README.md +13 -0
  36. package/examples/config-basic/app.js +13 -0
  37. package/examples/config-basic/appsettings.json +7 -0
  38. package/examples/config-secrets-redaction/README.md +9 -0
  39. package/examples/config-secrets-redaction/app.js +9 -0
  40. package/examples/config-secrets-redaction/appsettings.json +5 -0
  41. package/examples/config-strict-mode/README.md +7 -0
  42. package/examples/config-strict-mode/app.js +10 -0
  43. package/examples/config-strict-mode/appsettings.json +7 -0
  44. package/examples/configured-api/README.md +38 -0
  45. package/examples/configured-api/app.js +12 -0
  46. package/examples/configured-api/appsettings.Development.json +5 -0
  47. package/examples/configured-api/appsettings.json +6 -0
  48. package/examples/configured-api/sloppy.json +5 -0
  49. package/examples/core-config-secrets/README.md +10 -0
  50. package/examples/core-config-secrets/app.js +15 -0
  51. package/examples/core-fs-time-codec/README.md +9 -0
  52. package/examples/core-fs-time-codec/app.js +8 -0
  53. package/examples/core-network-time-codec/README.md +11 -0
  54. package/examples/core-network-time-codec/app.js +20 -0
  55. package/examples/core-policy-audit/README.md +7 -0
  56. package/examples/core-policy-audit/app.js +22 -0
  57. package/examples/core-process-time-codec/README.md +8 -0
  58. package/examples/core-process-time-codec/app.js +28 -0
  59. package/examples/core-worker-time/README.md +8 -0
  60. package/examples/core-worker-time/app.js +17 -0
  61. package/examples/crypto-hash-hmac/README.md +17 -0
  62. package/examples/crypto-hash-hmac/app.js +29 -0
  63. package/examples/crypto-password/README.md +21 -0
  64. package/examples/crypto-password/app.js +12 -0
  65. package/examples/crypto-random-token/README.md +16 -0
  66. package/examples/crypto-random-token/app.js +12 -0
  67. package/examples/crypto-secret-constant-time/README.md +21 -0
  68. package/examples/crypto-secret-constant-time/app.js +15 -0
  69. package/examples/data-foundation/README.md +39 -0
  70. package/examples/data-foundation/app.js +63 -0
  71. package/examples/dependency-graph/README.md +19 -0
  72. package/examples/dependency-graph/fixtures/graph-helper/index.js +3 -0
  73. package/examples/dependency-graph/fixtures/graph-helper/package.json +6 -0
  74. package/examples/dependency-graph/package.json +7 -0
  75. package/examples/dependency-graph/public/message.txt +1 -0
  76. package/examples/dependency-graph/sloppy.json +9 -0
  77. package/examples/dependency-graph/src/main.ts +8 -0
  78. package/examples/dogfood/README.md +23 -0
  79. package/examples/dogfood/dogfood.json +136 -0
  80. package/examples/dynamic-module-include/README.md +20 -0
  81. package/examples/dynamic-module-include/public/readme.txt +1 -0
  82. package/examples/dynamic-module-include/sloppy.json +12 -0
  83. package/examples/dynamic-module-include/src/main.ts +6 -0
  84. package/examples/dynamic-module-include/src/plugins/alpha.js +3 -0
  85. package/examples/dynamic-module-include/src/plugins/beta.js +3 -0
  86. package/examples/ergonomics/README.md +42 -0
  87. package/examples/ergonomics/app.js +38 -0
  88. package/examples/framework-controller/README.md +12 -0
  89. package/examples/framework-controller/app.js +31 -0
  90. package/examples/framework-di-services/README.md +17 -0
  91. package/examples/framework-di-services/app.ts +40 -0
  92. package/examples/framework-explicit-binding/README.md +12 -0
  93. package/examples/framework-explicit-binding/app.ts +34 -0
  94. package/examples/framework-hello/README.md +16 -0
  95. package/examples/framework-hello/app.ts +16 -0
  96. package/examples/framework-postgres-crud/README.md +73 -0
  97. package/examples/framework-postgres-crud/app.ts +64 -0
  98. package/examples/framework-sqlite-crud/README.md +52 -0
  99. package/examples/framework-sqlite-crud/app.ts +90 -0
  100. package/examples/framework-sqlite-crud/appsettings.json +11 -0
  101. package/examples/framework-sqlserver-crud/README.md +73 -0
  102. package/examples/framework-sqlserver-crud/app.ts +64 -0
  103. package/examples/framework-validation-errors/README.md +12 -0
  104. package/examples/framework-validation-errors/app.ts +16 -0
  105. package/examples/fs-basic/README.md +24 -0
  106. package/examples/fs-basic/app.js +12 -0
  107. package/examples/fs-roots-policy/README.md +14 -0
  108. package/examples/fs-roots-policy/app.js +4 -0
  109. package/examples/fs-streams/README.md +18 -0
  110. package/examples/fs-streams/app.js +11 -0
  111. package/examples/fs-watch/README.md +19 -0
  112. package/examples/fs-watch/app.js +11 -0
  113. package/examples/hello/README.md +63 -0
  114. package/examples/hello/app.js +19 -0
  115. package/examples/hello-minimal/README.md +51 -0
  116. package/examples/hello-minimal/sloppy.json +5 -0
  117. package/examples/hello-minimal/src/main.ts +9 -0
  118. package/examples/http-client-basic/README.md +11 -0
  119. package/examples/http-client-basic/app.js +46 -0
  120. package/examples/http-client-generated/README.md +22 -0
  121. package/examples/http-client-generated/openapi.json +45 -0
  122. package/examples/http-client-resilience/README.md +4 -0
  123. package/examples/http-client-resilience/app.js +38 -0
  124. package/examples/http-client-runtime-loopback/README.md +24 -0
  125. package/examples/http-client-testhost/README.md +4 -0
  126. package/examples/http-client-testhost/app.js +27 -0
  127. package/examples/http-client-testhost-package-mock/README.md +26 -0
  128. package/examples/http-client-typed/README.md +5 -0
  129. package/examples/http-client-typed/app.js +33 -0
  130. package/examples/modules-api/README.md +30 -0
  131. package/examples/modules-api/app.js +9 -0
  132. package/examples/modules-api/modules/routes.js +16 -0
  133. package/examples/modules-api/sloppy.json +5 -0
  134. package/examples/modules-basic/README.md +32 -0
  135. package/examples/modules-basic/app.js +41 -0
  136. package/examples/net-deadline-cancel/README.md +13 -0
  137. package/examples/net-deadline-cancel/app.js +34 -0
  138. package/examples/net-local-ipc/README.md +12 -0
  139. package/examples/net-local-ipc/app.js +46 -0
  140. package/examples/net-policy-strict/README.md +12 -0
  141. package/examples/net-policy-strict/app.js +34 -0
  142. package/examples/net-tcp-client/README.md +10 -0
  143. package/examples/net-tcp-client/app.js +23 -0
  144. package/examples/net-tcp-echo/README.md +11 -0
  145. package/examples/net-tcp-echo/app.js +45 -0
  146. package/examples/net-tcp-server/README.md +10 -0
  147. package/examples/net-tcp-server/app.js +28 -0
  148. package/examples/node-compat-path-events/README.md +15 -0
  149. package/examples/node-compat-path-events/sloppy.json +6 -0
  150. package/examples/node-compat-path-events/src/main.ts +15 -0
  151. package/examples/ops-compiler/README.md +9 -0
  152. package/examples/ops-compiler/app.js +26 -0
  153. package/examples/ops-health-metrics-management/README.md +14 -0
  154. package/examples/ops-health-metrics-management/app.js +24 -0
  155. package/examples/orm-basic/README.md +17 -0
  156. package/examples/orm-basic/app.js +82 -0
  157. package/examples/orm-cursor-export/README.md +16 -0
  158. package/examples/orm-cursor-export/app.js +28 -0
  159. package/examples/orm-migrations/README.md +14 -0
  160. package/examples/orm-migrations/migrations/.gitkeep +1 -0
  161. package/examples/orm-migrations/sloppy.json +9 -0
  162. package/examples/orm-migrations/src/app.ts +34 -0
  163. package/examples/orm-relations-includes/README.md +10 -0
  164. package/examples/orm-relations-includes/app.js +47 -0
  165. package/examples/orm-testservices/README.md +37 -0
  166. package/examples/orm-testservices/test.mjs +32 -0
  167. package/examples/os-runtime-api/README.md +11 -0
  168. package/examples/os-runtime-api/app.js +44 -0
  169. package/examples/package-zod-like/README.md +28 -0
  170. package/examples/package-zod-like/fixtures/zod-like/index.js +48 -0
  171. package/examples/package-zod-like/fixtures/zod-like/package.json +12 -0
  172. package/examples/package-zod-like/package.json +7 -0
  173. package/examples/package-zod-like/sloppy.json +6 -0
  174. package/examples/package-zod-like/src/main.ts +16 -0
  175. package/examples/postgres-basic/README.md +31 -0
  176. package/examples/postgres-basic/app.js +50 -0
  177. package/examples/prealpha-control-plane/README.md +50 -0
  178. package/examples/prealpha-control-plane/appsettings.Development.json +11 -0
  179. package/examples/prealpha-control-plane/appsettings.json +15 -0
  180. package/examples/prealpha-control-plane/sloppy.json +5 -0
  181. package/examples/prealpha-control-plane/src/db/schema.js +7 -0
  182. package/examples/prealpha-control-plane/src/db/seed.js +6 -0
  183. package/examples/prealpha-control-plane/src/main.js +21 -0
  184. package/examples/prealpha-control-plane/src/routes/apps.js +34 -0
  185. package/examples/prealpha-control-plane/src/routes/builds.js +25 -0
  186. package/examples/prealpha-control-plane/src/routes/deployments.js +19 -0
  187. package/examples/prealpha-control-plane/src/routes/diagnostics.js +11 -0
  188. package/examples/prealpha-control-plane/src/routes/health.js +27 -0
  189. package/examples/prealpha-control-plane/src/routes/projects.js +38 -0
  190. package/examples/prealpha-control-plane/src/services/diagnosticsSink.js +11 -0
  191. package/examples/prealpha-control-plane/src/services/repositories.js +9 -0
  192. package/examples/prealpha-control-plane/src/validation/schemas.js +6 -0
  193. package/examples/program-fs-process/README.md +31 -0
  194. package/examples/program-fs-process/sloppy.json +9 -0
  195. package/examples/program-fs-process/src/main.ts +27 -0
  196. package/examples/program-hello/README.md +32 -0
  197. package/examples/program-hello/main.ts +8 -0
  198. package/examples/program-hello/message.ts +1 -0
  199. package/examples/program-hello/sloppy.json +5 -0
  200. package/examples/rate-limit-auth/README.md +3 -0
  201. package/examples/rate-limit-auth/app.js +14 -0
  202. package/examples/rate-limit-basic/README.md +3 -0
  203. package/examples/rate-limit-basic/app.js +13 -0
  204. package/examples/rate-limit-redis/README.md +5 -0
  205. package/examples/rate-limit-redis/app.js +20 -0
  206. package/examples/rate-limit-testhost/README.md +4 -0
  207. package/examples/rate-limit-testhost/app.js +13 -0
  208. package/examples/rate-limit-websocket/README.md +3 -0
  209. package/examples/rate-limit-websocket/app.js +16 -0
  210. package/examples/realtime-auth/README.md +8 -0
  211. package/examples/realtime-auth/app.js +25 -0
  212. package/examples/realtime-auth/test.mjs +43 -0
  213. package/examples/realtime-chat/README.md +8 -0
  214. package/examples/realtime-chat/app.js +32 -0
  215. package/examples/realtime-chat/test.mjs +52 -0
  216. package/examples/realtime-dashboard/README.md +20 -0
  217. package/examples/realtime-dashboard/app.js +37 -0
  218. package/examples/realtime-presence/README.md +8 -0
  219. package/examples/realtime-presence/app.js +32 -0
  220. package/examples/realtime-presence/test.mjs +50 -0
  221. package/examples/realtime-testhost/README.md +8 -0
  222. package/examples/realtime-testhost/test.mjs +31 -0
  223. package/examples/redis-basic/README.md +17 -0
  224. package/examples/redis-basic/app.js +39 -0
  225. package/examples/redis-cache/README.md +14 -0
  226. package/examples/redis-cache/app.js +36 -0
  227. package/examples/redis-locks/README.md +13 -0
  228. package/examples/redis-locks/app.js +49 -0
  229. package/examples/request-context/README.md +32 -0
  230. package/examples/request-context/app.js +15 -0
  231. package/examples/sqlite-basic/README.md +52 -0
  232. package/examples/sqlite-basic/app.js +56 -0
  233. package/examples/sqlserver-basic/README.md +36 -0
  234. package/examples/sqlserver-basic/app.js +59 -0
  235. package/examples/static-files-basic/README.md +11 -0
  236. package/examples/static-files-basic/app.js +12 -0
  237. package/examples/static-files-basic/public/app.js +1 -0
  238. package/examples/static-files-basic/public/site.css +3 -0
  239. package/examples/static-files-package/README.md +12 -0
  240. package/examples/static-files-package/app.js +10 -0
  241. package/examples/static-files-package/public/index.html +2 -0
  242. package/examples/static-files-precompressed/README.md +12 -0
  243. package/examples/static-files-precompressed/app.js +11 -0
  244. package/examples/static-files-precompressed/public/app.js +1 -0
  245. package/examples/static-files-precompressed/public/app.js.br +0 -0
  246. package/examples/static-files-precompressed/public/app.js.gz +0 -0
  247. package/examples/static-files-spa/README.md +12 -0
  248. package/examples/static-files-spa/app.js +16 -0
  249. package/examples/static-files-spa/dist/assets/app.js +1 -0
  250. package/examples/static-files-spa/dist/index.html +4 -0
  251. package/examples/static-files-testhost/README.md +8 -0
  252. package/examples/static-files-testhost/app.js +13 -0
  253. package/examples/static-files-testhost/public/app.js +1 -0
  254. package/examples/static-files-testhost/public/app.js.gz +0 -0
  255. package/examples/static-files-testhost/test.mjs +38 -0
  256. package/examples/testhost-basic/README.md +26 -0
  257. package/examples/testhost-db/README.md +31 -0
  258. package/examples/testservices-postgres/README.md +68 -0
  259. package/examples/testservices-redis/README.md +71 -0
  260. package/examples/testservices-sqlserver/README.md +75 -0
  261. package/examples/time-basic/README.md +18 -0
  262. package/examples/time-basic/app.js +12 -0
  263. package/examples/time-deadline-cancellation/README.md +11 -0
  264. package/examples/time-deadline-cancellation/app.js +27 -0
  265. package/examples/time-fake-clock/README.md +14 -0
  266. package/examples/time-fake-clock/app.js +25 -0
  267. package/examples/time-interval-schedule/README.md +13 -0
  268. package/examples/time-interval-schedule/app.js +60 -0
  269. package/examples/users-api-sqlite/README.md +74 -0
  270. package/examples/users-api-sqlite/app.js +11 -0
  271. package/examples/users-api-sqlite/appsettings.Development.json +11 -0
  272. package/examples/users-api-sqlite/appsettings.json +11 -0
  273. package/examples/users-api-sqlite/modules/users.js +40 -0
  274. package/examples/users-api-sqlite/sloppy.json +5 -0
  275. package/examples/validation-errors/README.md +36 -0
  276. package/examples/validation-errors/app.js +14 -0
  277. package/examples/validation-errors/invalid-user.http +6 -0
  278. package/examples/validation-errors/sloppy.json +5 -0
  279. package/examples/web-dynamic-routes/README.md +17 -0
  280. package/examples/web-dynamic-routes/app.ts +27 -0
  281. package/examples/webhooks-basic/README.md +11 -0
  282. package/examples/webhooks-basic/app.js +48 -0
  283. package/examples/websocket-auth/README.md +8 -0
  284. package/examples/websocket-auth/app.js +16 -0
  285. package/examples/websocket-echo/README.md +9 -0
  286. package/examples/websocket-echo/app.js +36 -0
  287. package/examples/websocket-json-schema/README.md +5 -0
  288. package/examples/websocket-json-schema/app.js +25 -0
  289. package/examples/websocket-testhost/README.md +11 -0
  290. package/examples/websocket-testhost/test.mjs +49 -0
  291. package/examples/workers-background-service/README.md +7 -0
  292. package/examples/workers-background-service/app.js +16 -0
  293. package/examples/workers-js-isolate/README.md +8 -0
  294. package/examples/workers-js-isolate/app.js +19 -0
  295. package/examples/workers-js-isolate/workers/parser.ts +11 -0
  296. package/examples/workers-shutdown/README.md +6 -0
  297. package/examples/workers-shutdown/app.js +26 -0
  298. package/examples/workers-workerpool/README.md +6 -0
  299. package/examples/workers-workerpool/app.js +23 -0
  300. package/examples/workers-workqueue/README.md +8 -0
  301. package/examples/workers-workqueue/app.js +24 -0
  302. package/manifest.json +59 -0
  303. package/package.json +34 -0
  304. package/stdlib/sloppy/README.md +177 -0
  305. package/stdlib/sloppy/app.js +2142 -0
  306. package/stdlib/sloppy/auth.js +1813 -0
  307. package/stdlib/sloppy/bootstrap.manifest.json +83 -0
  308. package/stdlib/sloppy/cache.js +1542 -0
  309. package/stdlib/sloppy/codec.js +1153 -0
  310. package/stdlib/sloppy/config.js +61 -0
  311. package/stdlib/sloppy/crypto.js +312 -0
  312. package/stdlib/sloppy/data.js +2945 -0
  313. package/stdlib/sloppy/ffi.js +185 -0
  314. package/stdlib/sloppy/fs.js +795 -0
  315. package/stdlib/sloppy/health.js +603 -0
  316. package/stdlib/sloppy/http.js +1595 -0
  317. package/stdlib/sloppy/index.js +59 -0
  318. package/stdlib/sloppy/internal/bytes.js +31 -0
  319. package/stdlib/sloppy/internal/capabilities.js +155 -0
  320. package/stdlib/sloppy/internal/config.js +640 -0
  321. package/stdlib/sloppy/internal/disposable.js +31 -0
  322. package/stdlib/sloppy/internal/headers.js +63 -0
  323. package/stdlib/sloppy/internal/intrinsics.js +2 -0
  324. package/stdlib/sloppy/internal/json.js +20 -0
  325. package/stdlib/sloppy/internal/logging.js +278 -0
  326. package/stdlib/sloppy/internal/modules.js +405 -0
  327. package/stdlib/sloppy/internal/redaction.js +87 -0
  328. package/stdlib/sloppy/internal/routes.js +2279 -0
  329. package/stdlib/sloppy/internal/runtime-classic.js +19837 -0
  330. package/stdlib/sloppy/internal/services.js +690 -0
  331. package/stdlib/sloppy/internal/shared.js +32 -0
  332. package/stdlib/sloppy/internal/testhost-diagnostics.js +88 -0
  333. package/stdlib/sloppy/internal/testhost-http-server.js +238 -0
  334. package/stdlib/sloppy/internal/testhost-http.js +118 -0
  335. package/stdlib/sloppy/internal/testhost-loopback.js +50 -0
  336. package/stdlib/sloppy/internal/testservices-docker.js +154 -0
  337. package/stdlib/sloppy/internal/validation.js +117 -0
  338. package/stdlib/sloppy/metrics.js +427 -0
  339. package/stdlib/sloppy/net.js +5208 -0
  340. package/stdlib/sloppy/node/assert/strict.js +39 -0
  341. package/stdlib/sloppy/node/assert.js +228 -0
  342. package/stdlib/sloppy/node/buffer.js +247 -0
  343. package/stdlib/sloppy/node/console.js +33 -0
  344. package/stdlib/sloppy/node/constants.js +9 -0
  345. package/stdlib/sloppy/node/crypto.js +89 -0
  346. package/stdlib/sloppy/node/diagnostics_channel.js +41 -0
  347. package/stdlib/sloppy/node/events.js +113 -0
  348. package/stdlib/sloppy/node/fs/promises.js +27 -0
  349. package/stdlib/sloppy/node/fs.js +280 -0
  350. package/stdlib/sloppy/node/http.js +11 -0
  351. package/stdlib/sloppy/node/https.js +11 -0
  352. package/stdlib/sloppy/node/module.js +40 -0
  353. package/stdlib/sloppy/node/os.js +22 -0
  354. package/stdlib/sloppy/node/path.js +78 -0
  355. package/stdlib/sloppy/node/perf_hooks.js +12 -0
  356. package/stdlib/sloppy/node/process.js +129 -0
  357. package/stdlib/sloppy/node/querystring.js +21 -0
  358. package/stdlib/sloppy/node/stream/promises.js +3 -0
  359. package/stdlib/sloppy/node/stream.js +132 -0
  360. package/stdlib/sloppy/node/string_decoder.js +23 -0
  361. package/stdlib/sloppy/node/timers.js +26 -0
  362. package/stdlib/sloppy/node/tty.js +18 -0
  363. package/stdlib/sloppy/node/url.js +17 -0
  364. package/stdlib/sloppy/node/util.js +95 -0
  365. package/stdlib/sloppy/node/zlib.js +72 -0
  366. package/stdlib/sloppy/orm.js +2188 -0
  367. package/stdlib/sloppy/os.js +580 -0
  368. package/stdlib/sloppy/problem-details.js +29 -0
  369. package/stdlib/sloppy/providers/sqlite.js +26 -0
  370. package/stdlib/sloppy/rate-limit.js +856 -0
  371. package/stdlib/sloppy/realtime.js +1508 -0
  372. package/stdlib/sloppy/redis.js +1272 -0
  373. package/stdlib/sloppy/request-id.js +184 -0
  374. package/stdlib/sloppy/request-logging.js +101 -0
  375. package/stdlib/sloppy/results.js +933 -0
  376. package/stdlib/sloppy/schema.js +546 -0
  377. package/stdlib/sloppy/testing.js +4081 -0
  378. package/stdlib/sloppy/testservices.js +1041 -0
  379. package/stdlib/sloppy/time.js +894 -0
  380. package/stdlib/sloppy/webhooks.js +1330 -0
  381. package/stdlib/sloppy/workers.js +986 -0
  382. package/templates/api/README.md +82 -0
  383. package/templates/api/appsettings.Development.json +14 -0
  384. package/templates/api/appsettings.json +13 -0
  385. package/templates/api/data/.gitkeep +1 -0
  386. package/templates/api/gitignore +4 -0
  387. package/templates/api/migrations/0001_create_users.sql +1 -0
  388. package/templates/api/package.json +16 -0
  389. package/templates/api/public/hello.txt +1 -0
  390. package/templates/api/sloppy.json +14 -0
  391. package/templates/api/src/config.ts +1 -0
  392. package/templates/api/src/db/migrate.ts +14 -0
  393. package/templates/api/src/db/schema.ts +4 -0
  394. package/templates/api/src/db/usersRepository.ts +23 -0
  395. package/templates/api/src/main.ts +18 -0
  396. package/templates/api/src/models/user.ts +7 -0
  397. package/templates/api/src/routes/health.ts +20 -0
  398. package/templates/api/src/routes/users.ts +40 -0
  399. package/templates/api/src/services/usersService.ts +21 -0
  400. package/templates/api/tsconfig.json +15 -0
  401. package/templates/cli/README.md +16 -0
  402. package/templates/cli/gitignore +2 -0
  403. package/templates/cli/package.json +13 -0
  404. package/templates/cli/sloppy.json +6 -0
  405. package/templates/cli/src/commands/echo.ts +9 -0
  406. package/templates/cli/src/commands/inspect.ts +20 -0
  407. package/templates/cli/src/main.ts +50 -0
  408. package/templates/cli/tsconfig.json +15 -0
  409. package/templates/minimal-api/README.md +14 -0
  410. package/templates/minimal-api/gitignore +3 -0
  411. package/templates/minimal-api/package.json +14 -0
  412. package/templates/minimal-api/sloppy.json +5 -0
  413. package/templates/minimal-api/src/main.ts +9 -0
  414. package/templates/minimal-api/tsconfig.json +15 -0
  415. package/templates/node-compat/README.md +40 -0
  416. package/templates/node-compat/gitignore +2 -0
  417. package/templates/node-compat/package.json +11 -0
  418. package/templates/node-compat/sloppy.json +6 -0
  419. package/templates/node-compat/src/main.ts +40 -0
  420. package/templates/package-api/README.md +44 -0
  421. package/templates/package-api/fixtures/validator-lite/index.js +7 -0
  422. package/templates/package-api/fixtures/validator-lite/package.json +6 -0
  423. package/templates/package-api/gitignore +3 -0
  424. package/templates/package-api/package.json +17 -0
  425. package/templates/package-api/sloppy.json +5 -0
  426. package/templates/package-api/src/main.ts +10 -0
  427. package/templates/package-api/src/routes/health.ts +5 -0
  428. package/templates/package-api/src/routes/users.ts +12 -0
  429. package/templates/package-api/tsconfig.json +15 -0
  430. package/templates/program/README.md +12 -0
  431. package/templates/program/gitignore +1 -0
  432. package/templates/program/package.json +10 -0
  433. package/templates/program/sloppy.json +6 -0
  434. package/templates/program/src/main.ts +9 -0
@@ -0,0 +1,2188 @@
1
+ import { isPlainObject } from "./internal/validation.js";
2
+ import { Migrations, sql as dataSql } from "./data.js";
3
+ import { schema as Schema, isSchema } from "./schema.js";
4
+
5
+ const IDENTIFIER_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/u;
6
+ const ORM_TABLE = Symbol("SloppyOrmTable");
7
+ const ORM_COLUMN = Symbol("SloppyOrmColumn");
8
+ const ORM_EXPR = Symbol("SloppyOrmExpression");
9
+ const ORM_RAW = Symbol("SloppyOrmRawSql");
10
+ const ORM_RELATIONS = Symbol("SloppyOrmRelations");
11
+
12
+ const COLUMN_TYPES = Object.freeze({
13
+ text: { schema: () => Schema.string() },
14
+ int: { schema: () => Schema.int() },
15
+ bigint: { schema: () => Schema.string() },
16
+ number: { schema: () => Schema.number() },
17
+ decimal: { schema: () => Schema.string() },
18
+ bool: { schema: () => Schema.boolean() },
19
+ uuid: { schema: () => Schema.string().uuid() },
20
+ instant: { schema: () => Schema.string() },
21
+ date: { schema: () => Schema.string() },
22
+ json: { schema: () => createJsonValueSchema() },
23
+ blob: { schema: () => Schema.array(Schema.int()) },
24
+ enum: { schema: (column) => Schema.enum(column.enumValues) },
25
+ });
26
+
27
+ const DIALECTS = Object.freeze({
28
+ sqlite: Object.freeze({
29
+ provider: "sqlite",
30
+ placeholderStyle: "question",
31
+ quote: quoteIdentifier,
32
+ placeholder: () => "?",
33
+ limitOffset(limit, offset) {
34
+ const parts = [];
35
+ if (limit !== null) {
36
+ parts.push(`limit ${limit}`);
37
+ }
38
+ if (offset !== null) {
39
+ parts.push(`offset ${offset}`);
40
+ }
41
+ return parts.join(" ");
42
+ },
43
+ returning(columns) {
44
+ return columns.length === 0 ? "" : ` returning ${columns.map((col) => quoteIdentifier(col.name)).join(", ")}`;
45
+ },
46
+ defaultNow: "CURRENT_TIMESTAMP",
47
+ types: Object.freeze({
48
+ text: "text",
49
+ int: "integer",
50
+ bigint: "integer",
51
+ number: "real",
52
+ decimal: "text",
53
+ bool: "integer",
54
+ uuid: "text",
55
+ instant: "text",
56
+ date: "text",
57
+ json: "text",
58
+ blob: "blob",
59
+ enum: "text",
60
+ }),
61
+ }),
62
+ postgres: Object.freeze({
63
+ provider: "postgres",
64
+ placeholderStyle: "postgres",
65
+ quote: quoteIdentifier,
66
+ placeholder: (index) => `$${index}`,
67
+ limitOffset(limit, offset) {
68
+ const parts = [];
69
+ if (limit !== null) {
70
+ parts.push(`limit ${limit}`);
71
+ }
72
+ if (offset !== null) {
73
+ parts.push(`offset ${offset}`);
74
+ }
75
+ return parts.join(" ");
76
+ },
77
+ returning(columns) {
78
+ return columns.length === 0 ? "" : ` returning ${columns.map((col) => quoteIdentifier(col.name)).join(", ")}`;
79
+ },
80
+ defaultNow: "CURRENT_TIMESTAMP",
81
+ types: Object.freeze({
82
+ text: "text",
83
+ int: "integer",
84
+ bigint: "bigint",
85
+ number: "double precision",
86
+ decimal: "numeric",
87
+ bool: "boolean",
88
+ uuid: "uuid",
89
+ instant: "timestamptz",
90
+ date: "date",
91
+ json: "jsonb",
92
+ blob: "bytea",
93
+ enum: "text",
94
+ }),
95
+ }),
96
+ sqlserver: Object.freeze({
97
+ provider: "sqlserver",
98
+ placeholderStyle: "question",
99
+ quote(name) {
100
+ assertIdentifier(name, "SQL Server identifier");
101
+ return `[${name.replaceAll("]", "]]")}]`;
102
+ },
103
+ placeholder: () => "?",
104
+ limitOffset(limit, offset) {
105
+ if (limit === null && offset === null) {
106
+ return "";
107
+ }
108
+ return `offset ${offset ?? 0} rows${limit === null ? "" : ` fetch next ${limit} rows only`}`;
109
+ },
110
+ returning(columns) {
111
+ return columns.length === 0 ? "" : ` output ${columns.map((col) => `inserted.${this.quote(col.name)}`).join(", ")}`;
112
+ },
113
+ defaultNow: "SYSUTCDATETIME()",
114
+ types: Object.freeze({
115
+ text: "nvarchar(max)",
116
+ int: "int",
117
+ bigint: "bigint",
118
+ number: "float",
119
+ decimal: "decimal(38, 18)",
120
+ bool: "bit",
121
+ uuid: "uniqueidentifier",
122
+ instant: "datetimeoffset",
123
+ date: "date",
124
+ json: "nvarchar(max)",
125
+ blob: "varbinary(max)",
126
+ enum: "nvarchar(255)",
127
+ }),
128
+ }),
129
+ });
130
+
131
+ const ORM_ERROR_HINTS = Object.freeze({
132
+ SLOPPY_ORM_CONCURRENCY_CONFLICT: "Reload the row, compare the concurrency token, and retry the update or delete with the latest token.",
133
+ SLOPPY_ORM_CURSOR_INCLUDE_UNSUPPORTED: "Run the cursor query without include(), or materialize the query with toList() before loading relations.",
134
+ SLOPPY_ORM_DESTRUCTIVE_MIGRATION: "Review the destructive changes explicitly and pass allowDestructive: true only for an intentional migration draft.",
135
+ SLOPPY_ORM_DUPLICATE_COLUMN: "Declare each table column once inside table(\"name\", { ... }).",
136
+ SLOPPY_ORM_EMPTY_INSERT: "Pass at least one row object to insert() or insertMany().",
137
+ SLOPPY_ORM_FOREIGN_KEY_VIOLATION: "Insert or update the referenced parent row first, or verify references(() => Parent.id) points at the intended table.",
138
+ SLOPPY_ORM_GENERATED_PATCH: "Do not patch generated columns; let the database or provider produce the value.",
139
+ SLOPPY_ORM_INVALID_CONCURRENCY_TOKEN: "Use exactly one int or bigint column marked concurrencyToken() for optimistic concurrency.",
140
+ SLOPPY_ORM_INVALID_DEFAULT: "Use a default value that matches the column type, or use defaultNow() only on instant/date columns.",
141
+ SLOPPY_ORM_INVALID_ENUM: "Declare enums with a non-empty array of string values, for example column.enum([\"active\", \"archived\"]).",
142
+ SLOPPY_ORM_INVALID_EXPRESSION: "Build predicates from ORM column expressions, orm.and(), orm.or(), orm.not(), or orm.sql fragments.",
143
+ SLOPPY_ORM_INVALID_IDENTIFIER: "Use simple SQL identifiers with letters, digits, and underscores, starting with a letter or underscore.",
144
+ SLOPPY_ORM_INVALID_INCLUDE: "Return a relation from include(), for example include(u => u.team) or include(t => t.users.take(100)).",
145
+ SLOPPY_ORM_INVALID_INCLUDE_STRATEGY: "Use include strategy \"join\" or \"split\".",
146
+ SLOPPY_ORM_INVALID_LIST_EXPRESSION: "Pass a non-empty array to in() or notIn().",
147
+ SLOPPY_ORM_INVALID_MIGRATION_SNAPSHOT: "Pass a snapshot created by orm.migrations.snapshot(...).",
148
+ SLOPPY_ORM_INVALID_ORDER: "Return column order expressions such as u.createdAt.desc() from orderBy().",
149
+ SLOPPY_ORM_INVALID_PATCH_OPERATION: "Use increment()/decrement() on numeric columns and setNow() on instant/date columns.",
150
+ SLOPPY_ORM_INVALID_PROJECTION: "Return a non-empty object of columns or expressions from select().",
151
+ SLOPPY_ORM_INVALID_REFERENCE: "Use references(() => OtherTable.id) after both tables are in scope.",
152
+ SLOPPY_ORM_INVALID_RELATION: "Use relation(Table, ({ one, many }) => ({ name: one(Other, { local: Table.otherId, foreign: Other.id }) })).",
153
+ SLOPPY_ORM_INVALID_SOFT_DELETE: "Use one nullable instant/date column marked softDelete().",
154
+ SLOPPY_ORM_INVALID_TABLE: "Use table(\"users\", { id: column.uuid().primaryKey(), ... }) with at least one column.",
155
+ SLOPPY_ORM_MULTIPLE_CONCURRENCY_TOKENS: "Keep a single concurrencyToken() column per table.",
156
+ SLOPPY_ORM_MULTIPLE_SOFT_DELETE_COLUMNS: "Keep a single softDelete() column per table.",
157
+ SLOPPY_ORM_NOT_NULL_PATCH: "Patch nullable columns with null, or omit non-nullable fields you do not want to change.",
158
+ SLOPPY_ORM_NOT_NULL_VIOLATION: "Provide values for required columns or declare a default/defaultNow() in the model and database migration.",
159
+ SLOPPY_ORM_PRIMARY_KEY_PATCH: "Primary keys are immutable; update a different column or insert a new row.",
160
+ SLOPPY_ORM_PRIMARY_KEY_REQUIRED: "Define exactly one primaryKey() column before using by-id helpers.",
161
+ SLOPPY_ORM_PRIVATE_COLUMN: "Use public columns in publicSchema(), public(), pick(), and projections; private columns stay server-side.",
162
+ SLOPPY_ORM_PRIVATE_PATCH: "Do not patch private columns through public patch paths; handle sensitive writes explicitly.",
163
+ SLOPPY_ORM_PROVIDER_ERROR: "Check provider availability, connection configuration, generated SQL, and the original provider message in details.cause.",
164
+ SLOPPY_ORM_PROVIDER_SQL_MISMATCH: "Run provider-specific raw SQL only against the matching provider, or use provider-neutral orm.sql fragments.",
165
+ SLOPPY_ORM_RAW_SQL_PLACEHOLDER_MISMATCH: "Keep raw SQL placeholders and interpolated parameter count aligned.",
166
+ SLOPPY_ORM_SCHEMA_PICK_UNKNOWN_FIELD: "Pick only fields that exist on the table schema.",
167
+ SLOPPY_ORM_SEQUENCE_EMPTY: "Use firstOrDefault() or singleOrDefault() when an empty result is valid.",
168
+ SLOPPY_ORM_SEQUENCE_MULTIPLE: "Add a unique predicate, use first(), or call toList() when multiple rows are valid.",
169
+ SLOPPY_ORM_SOFT_DELETE_UNAVAILABLE: "Mark a nullable instant/date column with softDelete(), or call deleteById() for hard deletes.",
170
+ SLOPPY_ORM_TRANSACTION_UNAVAILABLE: "Use a database provider that implements transaction(callback).",
171
+ SLOPPY_ORM_UNDEFINED_PATCH_VALUE: "Omit unchanged fields from patches; use null only for nullable columns.",
172
+ SLOPPY_ORM_UNIQUE_VIOLATION: "Use a unique value, or query for the existing row before inserting/updating.",
173
+ SLOPPY_ORM_UNKNOWN_COLUMN: "Check the table declaration and use one of its declared column names.",
174
+ SLOPPY_ORM_UNSUPPORTED_COLUMN_TYPE: "Use a supported ORM column type such as text, int, bool, uuid, instant, json, blob, or enum([...]).",
175
+ SLOPPY_ORM_UNSUPPORTED_PROVIDER: "Use provider \"sqlite\", \"postgres\", or \"sqlserver\".",
176
+ SLOPPY_ORM_VALIDATION_FAILED: "Read details.issues and correct the row or patch before calling the provider.",
177
+ });
178
+
179
+ function ormErrorHint(code) {
180
+ return ORM_ERROR_HINTS[code] ?? "Check the ORM table, query, provider, or migration input against docs/api/orm.md.";
181
+ }
182
+
183
+ class SloppyOrmError extends Error {
184
+ constructor(code, message, details = undefined, hint = undefined) {
185
+ super(message);
186
+ this.name = "SloppyOrmError";
187
+ this.code = code;
188
+ this.details = details === undefined ? undefined : Object.freeze({ ...details });
189
+ this.hint = hint ?? ormErrorHint(code);
190
+ }
191
+ }
192
+
193
+ class SloppyOrmConcurrencyError extends SloppyOrmError {
194
+ constructor(message, details = undefined, hint = undefined) {
195
+ super("SLOPPY_ORM_CONCURRENCY_CONFLICT", message, details, hint);
196
+ this.name = "SloppyOrmConcurrencyError";
197
+ }
198
+ }
199
+
200
+ function ormError(code, message, details = undefined, hint = undefined) {
201
+ return new SloppyOrmError(code, message, details, hint);
202
+ }
203
+
204
+ function classifyProviderError(error) {
205
+ if (error instanceof SloppyOrmError) {
206
+ return null;
207
+ }
208
+ const code = String(error?.code ?? error?.sqlState ?? error?.sqlstate ?? "");
209
+ const number = error?.number ?? error?.errno;
210
+ const message = String(error?.message ?? error ?? "");
211
+ const lower = message.toLowerCase();
212
+ if (
213
+ code === "23505"
214
+ || code === "SQLITE_CONSTRAINT_UNIQUE"
215
+ || number === 2601
216
+ || number === 2627
217
+ || lower.includes("unique constraint failed")
218
+ || lower.includes("duplicate key")
219
+ || lower.includes("unique index")
220
+ ) {
221
+ return "SLOPPY_ORM_UNIQUE_VIOLATION";
222
+ }
223
+ if (
224
+ code === "23503"
225
+ || code === "SQLITE_CONSTRAINT_FOREIGNKEY"
226
+ || number === 547
227
+ || lower.includes("foreign key constraint failed")
228
+ || lower.includes("foreign key")
229
+ || lower.includes("reference constraint")
230
+ ) {
231
+ return "SLOPPY_ORM_FOREIGN_KEY_VIOLATION";
232
+ }
233
+ if (
234
+ code === "23502"
235
+ || code === "SQLITE_CONSTRAINT_NOTNULL"
236
+ || number === 515
237
+ || lower.includes("not null constraint failed")
238
+ || lower.includes("cannot insert the value null")
239
+ || lower.includes("null value in column")
240
+ ) {
241
+ return "SLOPPY_ORM_NOT_NULL_VIOLATION";
242
+ }
243
+ return null;
244
+ }
245
+
246
+ function wrapProviderError(error, operation, tableObject = undefined) {
247
+ if (error instanceof SloppyOrmError) {
248
+ return error;
249
+ }
250
+ const code = classifyProviderError(error) ?? "SLOPPY_ORM_PROVIDER_ERROR";
251
+ const details = {
252
+ operation,
253
+ cause: String(error?.message ?? error),
254
+ };
255
+ if (tableObject !== undefined) {
256
+ details.table = tableName(tableObject);
257
+ }
258
+ if (error?.code !== undefined) {
259
+ details.providerCode = String(error.code);
260
+ }
261
+ if (error?.sqlState !== undefined || error?.sqlstate !== undefined) {
262
+ details.sqlState = String(error.sqlState ?? error.sqlstate);
263
+ }
264
+ if (error?.number !== undefined || error?.errno !== undefined) {
265
+ details.providerNumber = Number(error.number ?? error.errno);
266
+ }
267
+ return ormError(code, `Sloppy ORM ${operation} failed with a provider error.`, details);
268
+ }
269
+
270
+ async function withProviderErrors(operation, tableObject, callback) {
271
+ try {
272
+ return await callback();
273
+ } catch (error) {
274
+ throw wrapProviderError(error, operation, tableObject);
275
+ }
276
+ }
277
+
278
+ function assertIdentifier(name, subject) {
279
+ if (typeof name !== "string" || !IDENTIFIER_PATTERN.test(name)) {
280
+ throw ormError("SLOPPY_ORM_INVALID_IDENTIFIER", `Sloppy ORM ${subject} must be a safe SQL identifier.`, { name });
281
+ }
282
+ }
283
+
284
+ function quoteIdentifier(name) {
285
+ assertIdentifier(name, "identifier");
286
+ return `"${name.replaceAll("\"", "\"\"")}"`;
287
+ }
288
+
289
+ function freezeDeep(value) {
290
+ if (value === null || typeof value !== "object") {
291
+ return value;
292
+ }
293
+ if (Object.isFrozen(value)) {
294
+ return value;
295
+ }
296
+ if (Array.isArray(value)) {
297
+ for (const item of value) {
298
+ freezeDeep(item);
299
+ }
300
+ return Object.freeze(value);
301
+ }
302
+ for (const item of Object.values(value)) {
303
+ freezeDeep(item);
304
+ }
305
+ return Object.freeze(value);
306
+ }
307
+
308
+ function schemaIssue(path, code, message) {
309
+ return Object.freeze({ path: Object.freeze([...path]), code, message });
310
+ }
311
+
312
+ function schemaSuccess(value) {
313
+ return Object.freeze({ ok: true, value });
314
+ }
315
+
316
+ function schemaFailure(path, message) {
317
+ return Object.freeze({
318
+ ok: false,
319
+ issues: Object.freeze([schemaIssue(path, "type", message)]),
320
+ });
321
+ }
322
+
323
+ function createJsonValueSchema() {
324
+ function validateJsonValue(value, path) {
325
+ if (value === undefined) {
326
+ return schemaFailure(path, "Expected a JSON value.");
327
+ }
328
+ if (typeof value === "function" || typeof value === "symbol" || typeof value === "bigint") {
329
+ return schemaFailure(path, "Expected a JSON value.");
330
+ }
331
+ try {
332
+ JSON.stringify(value);
333
+ } catch {
334
+ return schemaFailure(path, "Expected a JSON-serializable value.");
335
+ }
336
+ return schemaSuccess(value);
337
+ }
338
+ const current = {
339
+ kind: "json",
340
+ metadata: Object.freeze({ kind: "json" }),
341
+ validate(value) {
342
+ return validateJsonValue(value, []);
343
+ },
344
+ __validateAtPath: validateJsonValue,
345
+ optional() {
346
+ return createOptionalSchema(current);
347
+ },
348
+ nullable() {
349
+ return createNullableSchema(current);
350
+ },
351
+ };
352
+ return Object.freeze(current);
353
+ }
354
+
355
+ function createOptionalSchema(inner) {
356
+ const current = {
357
+ ...inner,
358
+ metadata: Object.freeze({ ...inner.metadata, optional: true }),
359
+ validate(value) {
360
+ return value === undefined ? schemaSuccess(undefined) : inner.validate(value);
361
+ },
362
+ __validateAtPath(value, path) {
363
+ return value === undefined ? schemaSuccess(undefined) : inner.__validateAtPath(value, path);
364
+ },
365
+ optional() {
366
+ return current;
367
+ },
368
+ nullable() {
369
+ return createNullableSchema(current);
370
+ },
371
+ };
372
+ return Object.freeze(current);
373
+ }
374
+
375
+ function createNullableSchema(inner) {
376
+ const current = {
377
+ ...inner,
378
+ metadata: Object.freeze({ ...inner.metadata, nullable: true }),
379
+ validate(value) {
380
+ return value === null ? schemaSuccess(null) : inner.validate(value);
381
+ },
382
+ __validateAtPath(value, path) {
383
+ return value === null ? schemaSuccess(null) : inner.__validateAtPath(value, path);
384
+ },
385
+ optional() {
386
+ return createOptionalSchema(current);
387
+ },
388
+ nullable() {
389
+ return current;
390
+ },
391
+ };
392
+ return Object.freeze(current);
393
+ }
394
+
395
+ function immutableRow(row) {
396
+ if (!isPlainObject(row)) {
397
+ return row;
398
+ }
399
+ return freezeDeep({ ...row });
400
+ }
401
+
402
+ function immutableRows(rows) {
403
+ return Object.freeze(rows.map((row) => immutableRow(row)));
404
+ }
405
+
406
+ function assertTable(value, subject = "table") {
407
+ if (value === null || typeof value !== "object" || value[ORM_TABLE] !== true) {
408
+ throw new TypeError(`Sloppy ORM ${subject} must be a table.`);
409
+ }
410
+ }
411
+
412
+ function tableName(tableObject) {
413
+ return tableObject.metadata?.name ?? tableObject.name;
414
+ }
415
+
416
+ function assertColumn(value, subject = "column") {
417
+ if (value === null || typeof value !== "object" || value[ORM_COLUMN] !== true) {
418
+ throw new TypeError(`Sloppy ORM ${subject} must be a column.`);
419
+ }
420
+ }
421
+
422
+ function columnMetadata(builder, tableName, name) {
423
+ const reference = builder._reference === null ? null : resolveReference(builder._reference, name);
424
+ return freezeDeep({
425
+ name,
426
+ table: tableName,
427
+ type: builder.type,
428
+ enumValues: builder.enumValues,
429
+ primaryKey: builder._primaryKey,
430
+ nullable: builder._nullable,
431
+ notNull: builder._notNull,
432
+ unique: builder._unique,
433
+ index: builder._index,
434
+ default: builder._default,
435
+ defaultNow: builder._defaultNow,
436
+ generated: builder._generated,
437
+ reference,
438
+ concurrencyToken: builder._concurrencyToken,
439
+ softDelete: builder._softDelete,
440
+ private: builder._private,
441
+ });
442
+ }
443
+
444
+ function resolveReference(reference, columnName) {
445
+ let target;
446
+ try {
447
+ target = reference();
448
+ } catch (error) {
449
+ throw ormError("SLOPPY_ORM_INVALID_REFERENCE", `Sloppy ORM column '${columnName}' reference could not be resolved.`, { cause: String(error?.message ?? error) });
450
+ }
451
+ assertColumn(target, `column '${columnName}' reference target`);
452
+ return {
453
+ table: tableName(target.table),
454
+ column: target.name,
455
+ };
456
+ }
457
+
458
+ function makeColumnBuilder(type, enumValues = null, state = {}) {
459
+ const builder = {
460
+ type,
461
+ enumValues,
462
+ _primaryKey: state._primaryKey === true,
463
+ _nullable: state._nullable === true,
464
+ _notNull: state._notNull === true,
465
+ _unique: state._unique === true,
466
+ _index: state._index === true,
467
+ _default: state._default,
468
+ _defaultNow: state._defaultNow === true,
469
+ _generated: state._generated === true,
470
+ _reference: state._reference ?? null,
471
+ _concurrencyToken: state._concurrencyToken === true,
472
+ _softDelete: state._softDelete === true,
473
+ _private: state._private === true,
474
+ };
475
+ function next(patch) {
476
+ return makeColumnBuilder(type, enumValues, { ...builder, ...patch });
477
+ }
478
+ return Object.freeze({
479
+ __sloppyOrmColumnBuilder: true,
480
+ ...builder,
481
+ primaryKey() {
482
+ return next({ _primaryKey: true, _notNull: true, _nullable: false });
483
+ },
484
+ notNull() {
485
+ return next({ _notNull: true, _nullable: false });
486
+ },
487
+ nullable() {
488
+ return next({ _nullable: true, _notNull: false });
489
+ },
490
+ unique() {
491
+ return next({ _unique: true });
492
+ },
493
+ index() {
494
+ return next({ _index: true });
495
+ },
496
+ default(value) {
497
+ validateDefaultValue(type, enumValues, value);
498
+ return next({ _default: value });
499
+ },
500
+ defaultNow() {
501
+ if (type !== "instant" && type !== "date") {
502
+ throw ormError("SLOPPY_ORM_INVALID_DEFAULT", "Sloppy ORM defaultNow() is only valid on instant/date columns.");
503
+ }
504
+ return next({ _defaultNow: true });
505
+ },
506
+ generated() {
507
+ return next({ _generated: true });
508
+ },
509
+ references(callback) {
510
+ if (typeof callback !== "function") {
511
+ throw new TypeError("Sloppy ORM references() expects a callback returning a column.");
512
+ }
513
+ return next({ _reference: callback });
514
+ },
515
+ concurrencyToken() {
516
+ if (type !== "int" && type !== "bigint") {
517
+ throw ormError("SLOPPY_ORM_INVALID_CONCURRENCY_TOKEN", "Sloppy ORM concurrencyToken() requires an int or bigint column.");
518
+ }
519
+ return next({ _concurrencyToken: true, _notNull: true, _nullable: false });
520
+ },
521
+ softDelete() {
522
+ if (type !== "instant" && type !== "date") {
523
+ throw ormError("SLOPPY_ORM_INVALID_SOFT_DELETE", "Sloppy ORM softDelete() requires a nullable instant/date column.");
524
+ }
525
+ if (builder._nullable !== true || builder._notNull === true) {
526
+ throw ormError("SLOPPY_ORM_INVALID_SOFT_DELETE", "Sloppy ORM soft-delete column must be nullable instant/date.");
527
+ }
528
+ return next({ _softDelete: true, _nullable: true, _notNull: false, _index: true });
529
+ },
530
+ private() {
531
+ return next({ _private: true });
532
+ },
533
+ });
534
+ }
535
+
536
+ function validateDefaultValue(type, enumValues, value) {
537
+ if (value === undefined) {
538
+ throw ormError("SLOPPY_ORM_INVALID_DEFAULT", "Sloppy ORM default(undefined) is not allowed.");
539
+ }
540
+ if (value === null) {
541
+ return;
542
+ }
543
+ if ((type === "text" || type === "uuid" || type === "instant" || type === "date" || type === "decimal" || type === "bigint") && typeof value !== "string") {
544
+ throw ormError("SLOPPY_ORM_INVALID_DEFAULT", `Sloppy ORM ${type} default must be a string.`);
545
+ }
546
+ if ((type === "int" || type === "number") && typeof value !== "number") {
547
+ throw ormError("SLOPPY_ORM_INVALID_DEFAULT", `Sloppy ORM ${type} default must be a number.`);
548
+ }
549
+ if (type === "bool" && typeof value !== "boolean") {
550
+ throw ormError("SLOPPY_ORM_INVALID_DEFAULT", "Sloppy ORM bool default must be a boolean.");
551
+ }
552
+ if (type === "enum" && !enumValues.includes(value)) {
553
+ throw ormError("SLOPPY_ORM_INVALID_DEFAULT", "Sloppy ORM enum default must be one of the enum values.");
554
+ }
555
+ }
556
+
557
+ function createColumn(tableObject, metadata) {
558
+ const columnObject = {
559
+ [ORM_COLUMN]: true,
560
+ table: tableObject,
561
+ name: metadata.name,
562
+ metadata,
563
+ eq(value) {
564
+ return binaryExpr("=", columnExpr(columnObject), valueExpr(value));
565
+ },
566
+ ne(value) {
567
+ return binaryExpr("<>", columnExpr(columnObject), valueExpr(value));
568
+ },
569
+ gt(value) {
570
+ return binaryExpr(">", columnExpr(columnObject), valueExpr(value));
571
+ },
572
+ gte(value) {
573
+ return binaryExpr(">=", columnExpr(columnObject), valueExpr(value));
574
+ },
575
+ lt(value) {
576
+ return binaryExpr("<", columnExpr(columnObject), valueExpr(value));
577
+ },
578
+ lte(value) {
579
+ return binaryExpr("<=", columnExpr(columnObject), valueExpr(value));
580
+ },
581
+ isNull() {
582
+ return unarySqlExpr(columnExpr(columnObject), "is null");
583
+ },
584
+ isNotNull() {
585
+ return unarySqlExpr(columnExpr(columnObject), "is not null");
586
+ },
587
+ like(value) {
588
+ return binaryExpr("like", columnExpr(columnObject), valueExpr(value));
589
+ },
590
+ ilike(value) {
591
+ return binaryExpr("ilike", columnExpr(columnObject), valueExpr(value));
592
+ },
593
+ in(values) {
594
+ return listExpr("in", columnExpr(columnObject), values);
595
+ },
596
+ notIn(values) {
597
+ return listExpr("not in", columnExpr(columnObject), values);
598
+ },
599
+ startsWith(value) {
600
+ return binaryExpr("like", columnExpr(columnObject), valueExpr(`${value}%`));
601
+ },
602
+ contains(value) {
603
+ return binaryExpr("like", columnExpr(columnObject), valueExpr(`%${value}%`));
604
+ },
605
+ endsWith(value) {
606
+ return binaryExpr("like", columnExpr(columnObject), valueExpr(`%${value}`));
607
+ },
608
+ asc() {
609
+ return orderExpr(columnExpr(columnObject), "asc");
610
+ },
611
+ desc() {
612
+ return orderExpr(columnExpr(columnObject), "desc");
613
+ },
614
+ };
615
+ return Object.freeze(columnObject);
616
+ }
617
+
618
+ function schemaForColumn(columnMeta, { optional = false, patch = false } = {}) {
619
+ const factory = COLUMN_TYPES[columnMeta.type]?.schema;
620
+ if (factory === undefined) {
621
+ throw ormError("SLOPPY_ORM_UNSUPPORTED_COLUMN_TYPE", `Sloppy ORM column type '${columnMeta.type}' is not supported.`);
622
+ }
623
+ let current = factory(columnMeta);
624
+ if (columnMeta.nullable) {
625
+ current = current.nullable();
626
+ }
627
+ if (optional || patch || columnMeta.generated || columnMeta.default !== undefined || columnMeta.defaultNow) {
628
+ current = current.optional();
629
+ }
630
+ return current;
631
+ }
632
+
633
+ function makeObjectSchema(shape) {
634
+ return Schema.object(shape);
635
+ }
636
+
637
+ function validateInsertValue(tableObject, value) {
638
+ const result = tableObject.insertSchema.validate(value);
639
+ if (!result.ok) {
640
+ throwValidation("insert", result.issues);
641
+ }
642
+ return result.value;
643
+ }
644
+
645
+ function validatePatchValue(tableObject, patch, options = {}) {
646
+ if (!isPlainObject(patch)) {
647
+ throw new TypeError("Sloppy ORM patch must be a plain object.");
648
+ }
649
+ const schemaPatch = {};
650
+ for (const [key, value] of Object.entries(patch)) {
651
+ if (value === undefined) {
652
+ throw ormError("SLOPPY_ORM_UNDEFINED_PATCH_VALUE", `Sloppy ORM patch field '${key}' is undefined. Omit the field or set null explicitly.`);
653
+ }
654
+ const columnMeta = tableObject.metadata.columns[key];
655
+ if (columnMeta === undefined) {
656
+ throw ormError("SLOPPY_ORM_UNKNOWN_COLUMN", `Sloppy ORM patch field '${key}' is not a column on '${tableName(tableObject)}'.`);
657
+ }
658
+ if (columnMeta.primaryKey && options.allowPrimaryKey !== true) {
659
+ throw ormError("SLOPPY_ORM_PRIMARY_KEY_PATCH", `Sloppy ORM patch field '${key}' is a primary key.`);
660
+ }
661
+ if (columnMeta.generated) {
662
+ throw ormError("SLOPPY_ORM_GENERATED_PATCH", `Sloppy ORM patch field '${key}' is generated.`);
663
+ }
664
+ if (columnMeta.private && options.allowPrivate !== true) {
665
+ throw ormError("SLOPPY_ORM_PRIVATE_PATCH", `Sloppy ORM patch field '${key}' is private.`);
666
+ }
667
+ if (value === null && !columnMeta.nullable) {
668
+ throw ormError("SLOPPY_ORM_NOT_NULL_PATCH", `Sloppy ORM patch field '${key}' is not nullable.`);
669
+ }
670
+ if (isPatchOperation(value)) {
671
+ validatePatchOperation(columnMeta, value);
672
+ continue;
673
+ }
674
+ schemaPatch[key] = value;
675
+ }
676
+ const result = tableObject.patchSchema.validate(schemaPatch);
677
+ if (!result.ok) {
678
+ throwValidation("patch", result.issues);
679
+ }
680
+ return freezeDeep({ ...result.value, ...Object.fromEntries(Object.entries(patch).filter(([, value]) => isPatchOperation(value))) });
681
+ }
682
+
683
+ function isPatchOperation(value) {
684
+ return value !== null && typeof value === "object" && value.__sloppyOrmOperation === true;
685
+ }
686
+
687
+ function validatePatchOperation(columnMeta, operationValue) {
688
+ if ((operationValue.kind === "increment" || operationValue.kind === "decrement") && columnMeta.type !== "int" && columnMeta.type !== "bigint" && columnMeta.type !== "number" && columnMeta.type !== "decimal") {
689
+ throw ormError("SLOPPY_ORM_INVALID_PATCH_OPERATION", `Sloppy ORM ${operationValue.kind}() requires a numeric column.`);
690
+ }
691
+ if (operationValue.kind === "setNow" && columnMeta.type !== "instant" && columnMeta.type !== "date") {
692
+ throw ormError("SLOPPY_ORM_INVALID_PATCH_OPERATION", "Sloppy ORM setNow() requires an instant/date column.");
693
+ }
694
+ }
695
+
696
+ function throwValidation(operation, issues) {
697
+ throw ormError("SLOPPY_ORM_VALIDATION_FAILED", `Sloppy ORM ${operation} validation failed.`, { issues });
698
+ }
699
+
700
+ function table(name, definition) {
701
+ assertIdentifier(name, "table name");
702
+ if (!isPlainObject(definition) || Object.keys(definition).length === 0) {
703
+ throw ormError("SLOPPY_ORM_INVALID_TABLE", "Sloppy ORM table definition must be a non-empty plain object.");
704
+ }
705
+ const columns = {};
706
+ const columnMetadataEntries = {};
707
+ const tableObject = {
708
+ [ORM_TABLE]: true,
709
+ name,
710
+ };
711
+ Object.defineProperty(tableObject, ORM_RELATIONS, {
712
+ value: [],
713
+ enumerable: false,
714
+ });
715
+ const seenColumns = new Set();
716
+ for (const [columnName, builder] of Object.entries(definition)) {
717
+ assertIdentifier(columnName, "column name");
718
+ if (seenColumns.has(columnName)) {
719
+ throw ormError("SLOPPY_ORM_DUPLICATE_COLUMN", `Sloppy ORM column '${columnName}' is duplicated.`);
720
+ }
721
+ if (builder?.__sloppyOrmColumnBuilder !== true) {
722
+ throw new TypeError(`Sloppy ORM table column '${columnName}' must be created with column.*().`);
723
+ }
724
+ seenColumns.add(columnName);
725
+ const meta = columnMetadata(builder, name, columnName);
726
+ columnMetadataEntries[columnName] = meta;
727
+ columns[columnName] = createColumn(tableObject, meta);
728
+ }
729
+ const primaryKeys = Object.values(columnMetadataEntries).filter((current) => current.primaryKey);
730
+ const concurrencyTokens = Object.values(columnMetadataEntries).filter((current) => current.concurrencyToken);
731
+ const softDeletes = Object.values(columnMetadataEntries).filter((current) => current.softDelete);
732
+ if (concurrencyTokens.length > 1) {
733
+ throw ormError("SLOPPY_ORM_MULTIPLE_CONCURRENCY_TOKENS", `Sloppy ORM table '${name}' has multiple concurrency token columns.`);
734
+ }
735
+ if (softDeletes.length > 1) {
736
+ throw ormError("SLOPPY_ORM_MULTIPLE_SOFT_DELETE_COLUMNS", `Sloppy ORM table '${name}' has multiple soft-delete columns.`);
737
+ }
738
+ for (const meta of softDeletes) {
739
+ if (!meta.nullable || (meta.type !== "instant" && meta.type !== "date")) {
740
+ throw ormError("SLOPPY_ORM_INVALID_SOFT_DELETE", "Sloppy ORM soft-delete column must be nullable instant/date.");
741
+ }
742
+ }
743
+
744
+ const frozenColumns = freezeDeep(columnMetadataEntries);
745
+ const metadata = freezeDeep({
746
+ name,
747
+ columns: frozenColumns,
748
+ primaryKey: primaryKeys.map((current) => current.name),
749
+ unique: Object.values(frozenColumns).filter((current) => current.unique).map((current) => current.name),
750
+ indexes: Object.values(frozenColumns).filter((current) => current.index).map((current) => current.name),
751
+ foreignKeys: Object.values(frozenColumns).filter((current) => current.reference !== null).map((current) => ({
752
+ column: current.name,
753
+ foreignTable: current.reference.table,
754
+ foreignColumn: current.reference.column,
755
+ })),
756
+ privateColumns: Object.values(frozenColumns).filter((current) => current.private).map((current) => current.name),
757
+ softDeleteColumn: softDeletes[0]?.name ?? null,
758
+ concurrencyTokenColumn: concurrencyTokens[0]?.name ?? null,
759
+ });
760
+ const rowShape = {};
761
+ const insertShape = {};
762
+ const patchShape = {};
763
+ for (const [columnName, meta] of Object.entries(frozenColumns)) {
764
+ rowShape[columnName] = schemaForColumn(meta);
765
+ if (!meta.generated && !meta.primaryKey) {
766
+ insertShape[columnName] = schemaForColumn(meta, {
767
+ optional: meta.default !== undefined || meta.defaultNow || meta.nullable,
768
+ });
769
+ } else if (meta.primaryKey && !meta.generated) {
770
+ insertShape[columnName] = schemaForColumn(meta, { optional: false });
771
+ }
772
+ if (!meta.generated && !meta.primaryKey && !meta.private) {
773
+ patchShape[columnName] = schemaForColumn(meta, { patch: true });
774
+ }
775
+ }
776
+ Object.assign(tableObject, columns, {
777
+ metadata,
778
+ rowSchema: makeObjectSchema(rowShape),
779
+ insertSchema: addPick(makeObjectSchema(insertShape)),
780
+ patchSchema: addPick(makeObjectSchema(patchShape)),
781
+ get primaryKey() {
782
+ return Object.freeze(primaryKeys.map((current) => columns[current.name]));
783
+ },
784
+ get privateColumns() {
785
+ return metadata.privateColumns;
786
+ },
787
+ publicSchema(names = undefined) {
788
+ const selected = normalizeColumnNames(tableObject, names, { includePrivate: false });
789
+ const shape = {};
790
+ for (const columnName of selected) {
791
+ shape[columnName] = rowShape[columnName];
792
+ }
793
+ return addPick(makeObjectSchema(shape));
794
+ },
795
+ public(row, names = undefined) {
796
+ const selected = normalizeColumnNames(tableObject, names, { includePrivate: false });
797
+ const output = {};
798
+ for (const columnName of selected) {
799
+ if (Object.prototype.hasOwnProperty.call(row, columnName)) {
800
+ output[columnName] = row[columnName];
801
+ }
802
+ }
803
+ return immutableRow(output);
804
+ },
805
+ pick(...names) {
806
+ return tableObject.publicSchema(names);
807
+ },
808
+ mapper() {
809
+ return (rowValue) => immutableRow(rowValue);
810
+ },
811
+ insert(db, values) {
812
+ return createInsertCommand(tableObject, db, values);
813
+ },
814
+ insertMany(db, rows) {
815
+ return insertMany(tableObject, db, rows);
816
+ },
817
+ updateById(db, id, patch, options = {}) {
818
+ return updateById(tableObject, db, id, patch, options);
819
+ },
820
+ deleteById(db, id, options = {}) {
821
+ return deleteById(tableObject, db, id, options);
822
+ },
823
+ softDeleteById(db, id, options = {}) {
824
+ return softDeleteById(tableObject, db, id, options);
825
+ },
826
+ findById(db, id) {
827
+ return orm.from(tableObject).where((t) => pkPredicate(tableObject, t, id)).singleOrDefault(db);
828
+ },
829
+ findOne(db, predicate) {
830
+ return orm.from(tableObject).where(predicate).singleOrDefault(db);
831
+ },
832
+ exists(db, predicate = undefined) {
833
+ const query = predicate === undefined ? orm.from(tableObject) : orm.from(tableObject).where(predicate);
834
+ return query.any(db);
835
+ },
836
+ count(db, predicate = undefined) {
837
+ const query = predicate === undefined ? orm.from(tableObject) : orm.from(tableObject).where(predicate);
838
+ return query.count(db);
839
+ },
840
+ edit(row) {
841
+ return createEditor(tableObject, row);
842
+ },
843
+ });
844
+ Object.freeze(tableObject);
845
+ return tableObject;
846
+ }
847
+
848
+ function addPick(objectSchema) {
849
+ if (!isSchema(objectSchema) || objectSchema.kind !== "object") {
850
+ return objectSchema;
851
+ }
852
+ return Object.freeze({
853
+ ...objectSchema,
854
+ pick(...names) {
855
+ const flatNames = names.flat();
856
+ const shape = {};
857
+ for (const name of flatNames) {
858
+ if (!Object.prototype.hasOwnProperty.call(objectSchema.shape, name)) {
859
+ throw ormError("SLOPPY_ORM_SCHEMA_PICK_UNKNOWN_FIELD", `Sloppy ORM schema pick field '${name}' does not exist.`);
860
+ }
861
+ shape[name] = objectSchema.shape[name];
862
+ }
863
+ return addPick(Schema.object(shape));
864
+ },
865
+ });
866
+ }
867
+
868
+ function normalizeColumnNames(tableObject, names, options = {}) {
869
+ assertTable(tableObject);
870
+ const allNames = Object.keys(tableObject.metadata.columns);
871
+ const selected = names === undefined && options.includePrivate !== true
872
+ ? allNames.filter((name) => !tableObject.metadata.columns[name].private)
873
+ : (names === undefined ? allNames : names.flat());
874
+ for (const name of selected) {
875
+ if (!Object.prototype.hasOwnProperty.call(tableObject.metadata.columns, name)) {
876
+ throw ormError("SLOPPY_ORM_UNKNOWN_COLUMN", `Sloppy ORM column '${name}' does not exist on '${tableName(tableObject)}'.`);
877
+ }
878
+ if (options.includePrivate !== true && tableObject.metadata.columns[name].private) {
879
+ throw ormError("SLOPPY_ORM_PRIVATE_COLUMN", `Sloppy ORM column '${name}' is private.`);
880
+ }
881
+ }
882
+ return Object.freeze([...selected]);
883
+ }
884
+
885
+ function pkPredicate(tableObject, proxy, id) {
886
+ const keys = tableObject.primaryKey;
887
+ if (keys.length !== 1) {
888
+ throw ormError("SLOPPY_ORM_PRIMARY_KEY_REQUIRED", `Sloppy ORM table '${tableName(tableObject)}' requires exactly one primary key for this operation.`);
889
+ }
890
+ return proxy[keys[0].name].eq(id);
891
+ }
892
+
893
+ function columnTypeFactory(type) {
894
+ return () => makeColumnBuilder(type);
895
+ }
896
+
897
+ const column = Object.freeze({
898
+ text: columnTypeFactory("text"),
899
+ string: columnTypeFactory("text"),
900
+ int: columnTypeFactory("int"),
901
+ integer: columnTypeFactory("int"),
902
+ bigint: columnTypeFactory("bigint"),
903
+ number: columnTypeFactory("number"),
904
+ float: columnTypeFactory("number"),
905
+ decimal: columnTypeFactory("decimal"),
906
+ bool: columnTypeFactory("bool"),
907
+ boolean: columnTypeFactory("bool"),
908
+ uuid: columnTypeFactory("uuid"),
909
+ instant: columnTypeFactory("instant"),
910
+ timestamp: columnTypeFactory("instant"),
911
+ date: columnTypeFactory("date"),
912
+ json: columnTypeFactory("json"),
913
+ blob: columnTypeFactory("blob"),
914
+ bytes: columnTypeFactory("blob"),
915
+ enum(values) {
916
+ if (!Array.isArray(values) || values.length === 0 || !values.every((value) => typeof value === "string" && value.length > 0)) {
917
+ throw ormError("SLOPPY_ORM_INVALID_ENUM", "Sloppy ORM enum values must be a non-empty array of strings.");
918
+ }
919
+ return makeColumnBuilder("enum", Object.freeze([...values]));
920
+ },
921
+ });
922
+
923
+ function expr(kind, payload) {
924
+ return Object.freeze({ [ORM_EXPR]: true, kind, ...payload });
925
+ }
926
+
927
+ function isExpr(value) {
928
+ return value !== null && typeof value === "object" && value[ORM_EXPR] === true;
929
+ }
930
+
931
+ function isRawSql(value) {
932
+ return value !== null && typeof value === "object" && value[ORM_RAW] === true;
933
+ }
934
+
935
+ function expressionArg(value, subject) {
936
+ if (isExpr(value)) {
937
+ return value;
938
+ }
939
+ if (isRawSql(value)) {
940
+ return expr("raw", { raw: value });
941
+ }
942
+ throw ormError("SLOPPY_ORM_INVALID_EXPRESSION", `Sloppy ORM ${subject} expects expressions.`);
943
+ }
944
+
945
+ function columnExpr(columnObject) {
946
+ assertColumn(columnObject);
947
+ return expr("column", { column: columnObject });
948
+ }
949
+
950
+ function valueExpr(value) {
951
+ if (isExpr(value)) {
952
+ return value;
953
+ }
954
+ if (value !== null && typeof value === "object" && value[ORM_COLUMN] === true) {
955
+ return columnExpr(value);
956
+ }
957
+ if (value !== null && typeof value === "object" && value[ORM_RAW] === true) {
958
+ return expr("raw", { raw: value });
959
+ }
960
+ return expr("value", { value });
961
+ }
962
+
963
+ function binaryExpr(operator, left, right) {
964
+ return expr("binary", { operator, left, right });
965
+ }
966
+
967
+ function unarySqlExpr(inner, suffix) {
968
+ return expr("unary-suffix", { inner, suffix });
969
+ }
970
+
971
+ function listExpr(operator, left, values) {
972
+ if (!Array.isArray(values) || values.length === 0) {
973
+ throw ormError("SLOPPY_ORM_INVALID_LIST_EXPRESSION", `Sloppy ORM ${operator} expression requires a non-empty array.`);
974
+ }
975
+ return expr("list", { operator, left, values: values.map(valueExpr) });
976
+ }
977
+
978
+ function orderExpr(inner, direction) {
979
+ return expr("order", { inner, direction });
980
+ }
981
+
982
+ function and(...items) {
983
+ const expressions = items.flat().filter(Boolean).map((item) => expressionArg(item, "and()"));
984
+ if (expressions.length === 0) {
985
+ throw ormError("SLOPPY_ORM_INVALID_EXPRESSION", "Sloppy ORM and() expects expressions.");
986
+ }
987
+ return expr("logical", { operator: "and", expressions });
988
+ }
989
+
990
+ function or(...items) {
991
+ const expressions = items.flat().filter(Boolean).map((item) => expressionArg(item, "or()"));
992
+ if (expressions.length === 0) {
993
+ throw ormError("SLOPPY_ORM_INVALID_EXPRESSION", "Sloppy ORM or() expects expressions.");
994
+ }
995
+ return expr("logical", { operator: "or", expressions });
996
+ }
997
+
998
+ function not(item) {
999
+ return expr("not", { inner: expressionArg(item, "not()") });
1000
+ }
1001
+
1002
+ function rawSql(strings, ...values) {
1003
+ const lowered = dataSql(strings, ...values);
1004
+ return Object.freeze({ [ORM_RAW]: true, provider: "any", query: lowered });
1005
+ }
1006
+
1007
+ rawSql.sqlite = function sqliteRaw(strings, ...values) {
1008
+ return Object.freeze({ [ORM_RAW]: true, provider: "sqlite", query: dataSql(strings, ...values) });
1009
+ };
1010
+
1011
+ rawSql.postgres = function postgresRaw(strings, ...values) {
1012
+ return Object.freeze({ [ORM_RAW]: true, provider: "postgres", query: dataSql.lower(strings, values, { placeholderStyle: "postgres" }) });
1013
+ };
1014
+
1015
+ rawSql.sqlserver = function sqlserverRaw(strings, ...values) {
1016
+ return Object.freeze({ [ORM_RAW]: true, provider: "sqlserver", query: dataSql(strings, ...values) });
1017
+ };
1018
+
1019
+ Object.freeze(rawSql);
1020
+
1021
+ function providerKind(db, fallback = "sqlite") {
1022
+ const debug = typeof db?.__debug === "function" ? db.__debug() : undefined;
1023
+ if (debug?.kind === "sqlite-connection") {
1024
+ return "sqlite";
1025
+ }
1026
+ if (debug?.kind === "postgres-connection") {
1027
+ return "postgres";
1028
+ }
1029
+ if (debug?.kind === "sqlserver-connection") {
1030
+ return "sqlserver";
1031
+ }
1032
+ if (typeof debug?.provider === "string" && DIALECTS[debug.provider] !== undefined) {
1033
+ return debug.provider;
1034
+ }
1035
+ if (debug?.placeholderStyle === "postgres") {
1036
+ return "postgres";
1037
+ }
1038
+ return fallback;
1039
+ }
1040
+
1041
+ function dialectFor(db, options = {}) {
1042
+ const provider = options.provider ?? providerKind(db, options.fallbackProvider ?? "sqlite");
1043
+ const dialect = DIALECTS[provider];
1044
+ if (dialect === undefined) {
1045
+ throw ormError("SLOPPY_ORM_UNSUPPORTED_PROVIDER", `Sloppy ORM provider '${provider}' is not supported.`);
1046
+ }
1047
+ return dialect;
1048
+ }
1049
+
1050
+ function compileExpression(expression, dialect, params, aliases = new Map()) {
1051
+ if (!isExpr(expression)) {
1052
+ throw ormError("SLOPPY_ORM_INVALID_EXPRESSION", "Sloppy ORM expected a query expression.");
1053
+ }
1054
+ switch (expression.kind) {
1055
+ case "column": {
1056
+ const alias = aliases.get(expression.column.table) ?? tableName(expression.column.table);
1057
+ return `${dialect.quote(alias)}.${dialect.quote(expression.column.name)}`;
1058
+ }
1059
+ case "value":
1060
+ params.push(expression.value);
1061
+ return dialect.placeholder(params.length);
1062
+ case "binary":
1063
+ if (expression.operator === "ilike" && dialect.provider !== "postgres") {
1064
+ return `(lower(${compileExpression(expression.left, dialect, params, aliases)}) like lower(${compileExpression(expression.right, dialect, params, aliases)}))`;
1065
+ }
1066
+ return `(${compileExpression(expression.left, dialect, params, aliases)} ${expression.operator} ${compileExpression(expression.right, dialect, params, aliases)})`;
1067
+ case "unary-suffix":
1068
+ return `(${compileExpression(expression.inner, dialect, params, aliases)} ${expression.suffix})`;
1069
+ case "list":
1070
+ return `(${compileExpression(expression.left, dialect, params, aliases)} ${expression.operator} (${expression.values.map((value) => compileExpression(value, dialect, params, aliases)).join(", ")}))`;
1071
+ case "logical":
1072
+ return `(${expression.expressions.map((item) => compileExpression(item, dialect, params, aliases)).join(` ${expression.operator} `)})`;
1073
+ case "not":
1074
+ return `(not ${compileExpression(expression.inner, dialect, params, aliases)})`;
1075
+ case "raw":
1076
+ if (expression.raw.provider !== "any" && expression.raw.provider !== dialect.provider) {
1077
+ throw ormError("SLOPPY_ORM_PROVIDER_SQL_MISMATCH", `Sloppy ORM raw SQL fragment for '${expression.raw.provider}' cannot run on '${dialect.provider}'.`);
1078
+ }
1079
+ {
1080
+ const base = params.length;
1081
+ let index = 0;
1082
+ const placeholderPattern = expression.raw.query.placeholderStyle === "postgres" ? /\$\d+/gu : /\?/gu;
1083
+ const text = expression.raw.query.text.replace(placeholderPattern, () => dialect.placeholder(base + ++index));
1084
+ if (index !== expression.raw.query.parameters.length) {
1085
+ throw ormError("SLOPPY_ORM_RAW_SQL_PLACEHOLDER_MISMATCH", "Sloppy ORM raw SQL fragment placeholder count does not match its parameters.");
1086
+ }
1087
+ for (const value of expression.raw.query.parameters) {
1088
+ params.push(value);
1089
+ }
1090
+ return text;
1091
+ }
1092
+ default:
1093
+ throw ormError("SLOPPY_ORM_INVALID_EXPRESSION", `Sloppy ORM expression kind '${expression.kind}' is not supported.`);
1094
+ }
1095
+ }
1096
+
1097
+ function createTableProxy(tableObject) {
1098
+ const proxy = {};
1099
+ for (const [name, value] of Object.entries(tableObject.metadata.columns)) {
1100
+ void value;
1101
+ proxy[name] = tableObject[name];
1102
+ }
1103
+ return Object.freeze(proxy);
1104
+ }
1105
+
1106
+ function createRelationProxy(tableObject) {
1107
+ const proxy = {};
1108
+ for (const relationEntry of relationsFor(tableObject)) {
1109
+ Object.defineProperty(proxy, relationEntry.name, {
1110
+ enumerable: true,
1111
+ get() {
1112
+ return createIncludeBuilder(relationEntry);
1113
+ },
1114
+ });
1115
+ }
1116
+ return Object.freeze(proxy);
1117
+ }
1118
+
1119
+ function joinedIncludesFor(state) {
1120
+ return Object.freeze(state.includes.filter((include) => {
1121
+ if (include.relation.kind !== "one" || include.__state.options.strategy === "split") {
1122
+ return false;
1123
+ }
1124
+ return include.__state.where === null && include.__state.limit === null;
1125
+ }));
1126
+ }
1127
+
1128
+ function splitIncludesFor(state, joinIncludes) {
1129
+ if (joinIncludes.length === 0) {
1130
+ return state.includes;
1131
+ }
1132
+ const joined = new Set(joinIncludes);
1133
+ return Object.freeze(state.includes.filter((include) => !joined.has(include)));
1134
+ }
1135
+
1136
+ function joinColumnAlias(relationEntry, columnName) {
1137
+ return `${relationEntry.name}__${columnName}`;
1138
+ }
1139
+
1140
+ function applyJoinedIncludes(rows, joinIncludes) {
1141
+ if (joinIncludes.length === 0) {
1142
+ return rows;
1143
+ }
1144
+ return rows.map((row) => {
1145
+ const output = {};
1146
+ for (const [key, value] of Object.entries(row)) {
1147
+ if (!joinIncludes.some((include) => key.startsWith(`${include.relation.name}__`))) {
1148
+ output[key] = value;
1149
+ }
1150
+ }
1151
+ for (const include of joinIncludes) {
1152
+ const relationEntry = include.relation;
1153
+ const related = {};
1154
+ let hasValue = false;
1155
+ for (const columnName of Object.keys(relationEntry.target.metadata.columns)) {
1156
+ const alias = joinColumnAlias(relationEntry, columnName);
1157
+ if (!Object.prototype.hasOwnProperty.call(row, alias)) {
1158
+ continue;
1159
+ }
1160
+ const value = row[alias];
1161
+ related[columnName] = value;
1162
+ if (value !== null && value !== undefined) {
1163
+ hasValue = true;
1164
+ }
1165
+ }
1166
+ output[relationEntry.name] = hasValue ? immutableRow(related) : null;
1167
+ }
1168
+ return output;
1169
+ });
1170
+ }
1171
+
1172
+ function projectionColumns(projection, tableObject) {
1173
+ if (projection === null || typeof projection !== "object" || Array.isArray(projection)) {
1174
+ throw ormError("SLOPPY_ORM_INVALID_PROJECTION", "Sloppy ORM select() callback must return an object.");
1175
+ }
1176
+ const entries = [];
1177
+ for (const [alias, value] of Object.entries(projection)) {
1178
+ if (value !== null && typeof value === "object" && value[ORM_COLUMN] === true) {
1179
+ entries.push({ alias, expression: columnExpr(value), column: value });
1180
+ } else if (isExpr(value)) {
1181
+ entries.push({ alias, expression: value, column: null });
1182
+ } else {
1183
+ throw ormError("SLOPPY_ORM_INVALID_PROJECTION", `Sloppy ORM projection field '${alias}' must be a column or expression.`);
1184
+ }
1185
+ }
1186
+ if (entries.length === 0) {
1187
+ throw ormError("SLOPPY_ORM_INVALID_PROJECTION", "Sloppy ORM projection cannot be empty.");
1188
+ }
1189
+ void tableObject;
1190
+ return Object.freeze(entries.map((entry) => Object.freeze(entry)));
1191
+ }
1192
+
1193
+ function buildSelectSql(state, db, options = {}) {
1194
+ const dialect = dialectFor(db, options);
1195
+ const params = [];
1196
+ const alias = "t0";
1197
+ const aliases = new Map([[state.table, alias]]);
1198
+ const joinIncludes = joinedIncludesFor(state);
1199
+ const selectParts = state.projection === null
1200
+ ? Object.keys(state.table.metadata.columns).map((name) => `${dialect.quote(alias)}.${dialect.quote(name)} as ${dialect.quote(name)}`)
1201
+ : state.projection.map((item) => `${compileExpression(item.expression, dialect, params, aliases)} as ${dialect.quote(item.alias)}`);
1202
+ const joinParts = [];
1203
+ joinIncludes.forEach((include, index) => {
1204
+ const relationEntry = include.relation;
1205
+ const joinAlias = `t${index + 1}`;
1206
+ aliases.set(relationEntry.target, joinAlias);
1207
+ joinParts.push(
1208
+ `left join ${dialect.quote(tableName(relationEntry.target))} ${dialect.quote(joinAlias)} on ${dialect.quote(alias)}.${dialect.quote(relationEntry.local.name)} = ${dialect.quote(joinAlias)}.${dialect.quote(relationEntry.foreign.name)}`,
1209
+ );
1210
+ for (const columnName of Object.keys(relationEntry.target.metadata.columns)) {
1211
+ selectParts.push(`${dialect.quote(joinAlias)}.${dialect.quote(columnName)} as ${dialect.quote(joinColumnAlias(relationEntry, columnName))}`);
1212
+ }
1213
+ });
1214
+ const parts = [
1215
+ `select ${selectParts.join(", ")}`,
1216
+ `from ${dialect.quote(tableName(state.table))} ${dialect.quote(alias)}`,
1217
+ ];
1218
+ parts.push(...joinParts);
1219
+ if (state.where !== null) {
1220
+ parts.push(`where ${compileExpression(state.where, dialect, params, aliases)}`);
1221
+ }
1222
+ if (state.order.length !== 0) {
1223
+ parts.push(`order by ${state.order.map((item) => {
1224
+ const current = item.kind === "order" ? item : orderExpr(item, "asc");
1225
+ return `${compileExpression(current.inner, dialect, params, aliases)} ${current.direction}`;
1226
+ }).join(", ")}`);
1227
+ }
1228
+ const limitOffset = dialect.limitOffset(state.limit, state.offset);
1229
+ if (limitOffset.length !== 0) {
1230
+ if (dialect.provider === "sqlserver" && state.order.length === 0) {
1231
+ parts.push(`order by ${dialect.quote(alias)}.${dialect.quote(primaryKeyColumn(state.table).name)}`);
1232
+ }
1233
+ parts.push(limitOffset);
1234
+ }
1235
+ return { text: parts.join(" "), params, dialect, joinIncludes };
1236
+ }
1237
+
1238
+ function lowerQuery(text, params, dialect) {
1239
+ if (params.length === 0) {
1240
+ return dataSql.lower([text], [], { placeholderStyle: dialect.placeholderStyle });
1241
+ }
1242
+ const strings = [];
1243
+ let last = 0;
1244
+ const placeholderPattern = dialect.provider === "postgres" ? /\$\d+/gu : /\?/gu;
1245
+ for (const match of text.matchAll(placeholderPattern)) {
1246
+ strings.push(text.slice(last, match.index));
1247
+ last = match.index + match[0].length;
1248
+ }
1249
+ strings.push(text.slice(last));
1250
+ return dataSql.lower(strings, params, { placeholderStyle: dialect.placeholderStyle });
1251
+ }
1252
+
1253
+ function callProvider(db, method, query, options) {
1254
+ const forwarded = providerOperationOptions(options);
1255
+ return forwarded === undefined
1256
+ ? db[method](query.text, [...query.parameters])
1257
+ : db[method](query.text, [...query.parameters], forwarded);
1258
+ }
1259
+
1260
+ function providerOperationOptions(options) {
1261
+ if (options === undefined || options === null || typeof options !== "object") {
1262
+ return undefined;
1263
+ }
1264
+ const forwarded = {};
1265
+ for (const key of ["batchSize", "maxRows", "mode", "timeoutMs"]) {
1266
+ if (options[key] !== undefined) {
1267
+ forwarded[key] = options[key];
1268
+ }
1269
+ }
1270
+ return Object.keys(forwarded).length === 0 ? undefined : Object.freeze(forwarded);
1271
+ }
1272
+
1273
+ function createQueryBuilder(tableObject, state = undefined) {
1274
+ assertTable(tableObject);
1275
+ const current = state ?? {
1276
+ table: tableObject,
1277
+ where: null,
1278
+ projection: null,
1279
+ order: [],
1280
+ offset: null,
1281
+ limit: null,
1282
+ includes: [],
1283
+ };
1284
+ function next(patch) {
1285
+ return createQueryBuilder(tableObject, { ...current, ...patch });
1286
+ }
1287
+ async function toList(db, options = {}) {
1288
+ const compiled = buildSelectSql(current, db, options);
1289
+ const rows = await withProviderErrors("select", current.table, () =>
1290
+ callProvider(db, "query", lowerQuery(compiled.text, compiled.params, compiled.dialect), options));
1291
+ const baseRows = immutableRows(applyJoinedIncludes(rows, compiled.joinIncludes));
1292
+ const splitIncludes = splitIncludesFor(current, compiled.joinIncludes);
1293
+ return loadIncludes(baseRows, { ...current, includes: splitIncludes }, db, options);
1294
+ }
1295
+ async function cursor(db, options = {}) {
1296
+ if (current.includes.length !== 0) {
1297
+ throw ormError("SLOPPY_ORM_CURSOR_INCLUDE_UNSUPPORTED", "Sloppy ORM cursor() does not support includes because cursors must stay incremental.");
1298
+ }
1299
+ validateCursorOptions(options);
1300
+ const compiled = buildSelectSql(current, db, options);
1301
+ const cursorValue = await withProviderErrors("cursor", current.table, () =>
1302
+ callProvider(db, "queryCursor", lowerQuery(compiled.text, compiled.params, compiled.dialect), options));
1303
+ return wrapOrmCursor(cursorValue, current, options);
1304
+ }
1305
+ const builder = {
1306
+ where(predicate) {
1307
+ if (typeof predicate !== "function") {
1308
+ throw new TypeError("Sloppy ORM where() expects a predicate callback.");
1309
+ }
1310
+ const expression = expressionArg(predicate(createTableProxy(tableObject), { and, or, not, sql: rawSql }), "where()");
1311
+ return next({ where: current.where === null ? expression : and(current.where, expression) });
1312
+ },
1313
+ select(callback) {
1314
+ if (typeof callback !== "function") {
1315
+ throw new TypeError("Sloppy ORM select() expects a projection callback.");
1316
+ }
1317
+ return next({ projection: projectionColumns(callback(createTableProxy(tableObject)), tableObject) });
1318
+ },
1319
+ orderBy(...callbacks) {
1320
+ return next({ order: normalizeOrderCallbacks(tableObject, callbacks) });
1321
+ },
1322
+ thenBy(...callbacks) {
1323
+ return next({ order: Object.freeze([...current.order, ...normalizeOrderCallbacks(tableObject, callbacks)]) });
1324
+ },
1325
+ skip(count) {
1326
+ assertNonNegativeInteger(count, "skip");
1327
+ return next({ offset: count });
1328
+ },
1329
+ take(count) {
1330
+ assertNonNegativeInteger(count, "take");
1331
+ return next({ limit: count });
1332
+ },
1333
+ include(callback, options = {}) {
1334
+ if (typeof callback !== "function") {
1335
+ throw new TypeError("Sloppy ORM include() expects a relation callback.");
1336
+ }
1337
+ const include = callback(createRelationProxy(tableObject));
1338
+ if (include?.__sloppyOrmInclude !== true) {
1339
+ throw ormError("SLOPPY_ORM_INVALID_INCLUDE", "Sloppy ORM include() callback must return a relation include.");
1340
+ }
1341
+ return next({ includes: Object.freeze([...current.includes, include.withOptions(options)]) });
1342
+ },
1343
+ async first(db, options = {}) {
1344
+ const rows = await this.take(1).toList(db, options);
1345
+ if (rows.length === 0) {
1346
+ throw ormError("SLOPPY_ORM_SEQUENCE_EMPTY", "Sloppy ORM first() expected at least one row.");
1347
+ }
1348
+ return rows[0];
1349
+ },
1350
+ async firstOrDefault(db, options = {}) {
1351
+ const rows = await this.take(1).toList(db, options);
1352
+ return rows[0] ?? null;
1353
+ },
1354
+ async single(db, options = {}) {
1355
+ const rows = await this.take(2).toList(db, options);
1356
+ if (rows.length === 0) {
1357
+ throw ormError("SLOPPY_ORM_SEQUENCE_EMPTY", "Sloppy ORM single() expected one row.");
1358
+ }
1359
+ if (rows.length > 1) {
1360
+ throw ormError("SLOPPY_ORM_SEQUENCE_MULTIPLE", "Sloppy ORM single() found more than one row.");
1361
+ }
1362
+ return rows[0];
1363
+ },
1364
+ async singleOrDefault(db, options = {}) {
1365
+ const rows = await this.take(2).toList(db, options);
1366
+ if (rows.length > 1) {
1367
+ throw ormError("SLOPPY_ORM_SEQUENCE_MULTIPLE", "Sloppy ORM singleOrDefault() found more than one row.");
1368
+ }
1369
+ return rows[0] ?? null;
1370
+ },
1371
+ toList,
1372
+ async any(db, predicate = undefined, options = {}) {
1373
+ const query = predicate === undefined ? builder : builder.where(predicate);
1374
+ const rows = await query.take(1).select((t) => ({ one: primaryKeyColumn(tableObject, t) })).toList(db, options);
1375
+ return rows.length !== 0;
1376
+ },
1377
+ async count(db, options = {}) {
1378
+ const dialect = dialectFor(db, options);
1379
+ const params = [];
1380
+ const alias = "t0";
1381
+ const aliases = new Map([[tableObject, alias]]);
1382
+ const parts = [
1383
+ `select count(*) as ${dialect.quote("count")}`,
1384
+ `from ${dialect.quote(tableName(tableObject))} ${dialect.quote(alias)}`,
1385
+ ];
1386
+ if (current.where !== null) {
1387
+ parts.push(`where ${compileExpression(current.where, dialect, params, aliases)}`);
1388
+ }
1389
+ const row = await withProviderErrors("count", tableObject, () =>
1390
+ callProvider(db, "queryOne", lowerQuery(parts.join(" "), params, dialect), options));
1391
+ return Number(row?.count ?? 0);
1392
+ },
1393
+ cursor,
1394
+ __debug() {
1395
+ return Object.freeze({ ...current });
1396
+ },
1397
+ };
1398
+ return Object.freeze(builder);
1399
+ }
1400
+
1401
+ function assertNonNegativeInteger(value, subject) {
1402
+ if (!Number.isInteger(value) || value < 0) {
1403
+ throw new TypeError(`Sloppy ORM ${subject} value must be a non-negative integer.`);
1404
+ }
1405
+ }
1406
+
1407
+ function validateCursorOptions(options) {
1408
+ if (options.batchSize !== undefined && (!Number.isInteger(options.batchSize) || options.batchSize <= 0)) {
1409
+ throw new TypeError("Sloppy ORM cursor batchSize must be a positive integer.");
1410
+ }
1411
+ if (options.maxRows !== undefined && (!Number.isInteger(options.maxRows) || options.maxRows < 0)) {
1412
+ throw new TypeError("Sloppy ORM cursor maxRows must be a non-negative integer.");
1413
+ }
1414
+ }
1415
+
1416
+ function normalizeOrderCallbacks(tableObject, callbacks) {
1417
+ const items = callbacks.flat().map((callback) => {
1418
+ const value = typeof callback === "function" ? callback(createTableProxy(tableObject)) : callback;
1419
+ if (value !== null && typeof value === "object" && value[ORM_COLUMN] === true) {
1420
+ return orderExpr(columnExpr(value), "asc");
1421
+ }
1422
+ if (!isExpr(value)) {
1423
+ throw ormError("SLOPPY_ORM_INVALID_ORDER", "Sloppy ORM orderBy() expects column/order expressions.");
1424
+ }
1425
+ return value;
1426
+ });
1427
+ return Object.freeze(items);
1428
+ }
1429
+
1430
+ function primaryKeyColumn(tableObject, proxy = tableObject) {
1431
+ const keys = tableObject.primaryKey;
1432
+ if (keys.length !== 1) {
1433
+ throw ormError("SLOPPY_ORM_PRIMARY_KEY_REQUIRED", `Sloppy ORM table '${tableName(tableObject)}' requires exactly one primary key for this operation.`);
1434
+ }
1435
+ return proxy[keys[0].name];
1436
+ }
1437
+
1438
+ function createInsertCommand(tableObject, db, values) {
1439
+ const input = validateInsertValue(tableObject, values);
1440
+ const command = {
1441
+ async execute(options = {}) {
1442
+ const result = await executeInsert(tableObject, db, input, false, options);
1443
+ return result;
1444
+ },
1445
+ async returning(options = {}) {
1446
+ const rows = await executeInsert(tableObject, db, input, true, options);
1447
+ return rows[0] ?? null;
1448
+ },
1449
+ };
1450
+ return Object.freeze(command);
1451
+ }
1452
+
1453
+ async function executeInsert(tableObject, db, values, returning, options = {}) {
1454
+ const dialect = dialectFor(db, options);
1455
+ const params = [];
1456
+ const columns = Object.keys(values).filter((name) => values[name] !== undefined);
1457
+ if (columns.length === 0) {
1458
+ throw ormError("SLOPPY_ORM_EMPTY_INSERT", "Sloppy ORM insert requires at least one value.");
1459
+ }
1460
+ const placeholders = columns.map((name) => {
1461
+ params.push(values[name]);
1462
+ return dialect.placeholder(params.length);
1463
+ });
1464
+ const returningColumns = returning ? Object.values(tableObject.metadata.columns).filter((col) => !col.private) : [];
1465
+ const returningSql = returning ? dialect.returning(returningColumns) : "";
1466
+ const text = dialect.provider === "sqlserver" && returningSql.length !== 0
1467
+ ? `insert into ${dialect.quote(tableName(tableObject))} (${columns.map((name) => dialect.quote(name)).join(", ")})${returningSql} values (${placeholders.join(", ")})`
1468
+ : `insert into ${dialect.quote(tableName(tableObject))} (${columns.map((name) => dialect.quote(name)).join(", ")}) values (${placeholders.join(", ")})${returningSql}`;
1469
+ if (returning) {
1470
+ const rows = await withProviderErrors("insert returning", tableObject, () =>
1471
+ callProvider(db, "query", lowerQuery(text, params, dialect), options));
1472
+ return immutableRows(rows);
1473
+ }
1474
+ return withProviderErrors("insert", tableObject, () =>
1475
+ callProvider(db, "exec", lowerQuery(text, params, dialect), options));
1476
+ }
1477
+
1478
+ async function insertMany(tableObject, db, rows, options = {}) {
1479
+ if (!Array.isArray(rows) || rows.length === 0) {
1480
+ throw new TypeError("Sloppy ORM insertMany rows must be a non-empty array.");
1481
+ }
1482
+ return orm.transaction(db, async (tx) => {
1483
+ let affectedRows = 0;
1484
+ for (const row of rows) {
1485
+ const result = await tableObject.insert(tx, row).execute(options);
1486
+ affectedRows += Number(result?.affectedRows ?? 0);
1487
+ }
1488
+ return Object.freeze({ affectedRows });
1489
+ });
1490
+ }
1491
+
1492
+ function updateExpressionsForPatch(tableObject, patch, dialect, params) {
1493
+ const sets = [];
1494
+ for (const [name, value] of Object.entries(patch)) {
1495
+ const expression = value !== null && typeof value === "object" && value.__sloppyOrmOperation === true
1496
+ ? value
1497
+ : null;
1498
+ if (expression?.kind === "increment") {
1499
+ sets.push(`${dialect.quote(name)} = ${dialect.quote(name)} + ${dialect.placeholder(params.push(expression.value))}`);
1500
+ continue;
1501
+ }
1502
+ if (expression?.kind === "decrement") {
1503
+ sets.push(`${dialect.quote(name)} = ${dialect.quote(name)} - ${dialect.placeholder(params.push(expression.value))}`);
1504
+ continue;
1505
+ }
1506
+ if (expression?.kind === "setNow") {
1507
+ sets.push(`${dialect.quote(name)} = ${dialect.defaultNow}`);
1508
+ continue;
1509
+ }
1510
+ if (expression?.kind === "raw") {
1511
+ sets.push(`${dialect.quote(name)} = ${compileExpression(valueExpr(expression.value), dialect, params)}`);
1512
+ continue;
1513
+ }
1514
+ params.push(value);
1515
+ sets.push(`${dialect.quote(name)} = ${dialect.placeholder(params.length)}`);
1516
+ }
1517
+ const tokenName = tableObject.metadata.concurrencyTokenColumn;
1518
+ if (tokenName !== null && !Object.prototype.hasOwnProperty.call(patch, tokenName)) {
1519
+ sets.push(`${dialect.quote(tokenName)} = ${dialect.quote(tokenName)} + 1`);
1520
+ }
1521
+ return sets;
1522
+ }
1523
+
1524
+ async function updateById(tableObject, db, id, patch, options = {}) {
1525
+ const checked = validatePatchValue(tableObject, patch, options);
1526
+ const dialect = dialectFor(db, options);
1527
+ const params = [];
1528
+ const sets = updateExpressionsForPatch(tableObject, checked, dialect, params);
1529
+ if (sets.length === 0) {
1530
+ return Object.freeze({ affectedRows: 0 });
1531
+ }
1532
+ const pk = primaryKeyColumn(tableObject);
1533
+ params.push(id);
1534
+ const where = [`${dialect.quote(pk.name)} = ${dialect.placeholder(params.length)}`];
1535
+ const tokenName = tableObject.metadata.concurrencyTokenColumn;
1536
+ const expected = options.expected ?? {};
1537
+ if (tokenName !== null && expected[tokenName] !== undefined) {
1538
+ params.push(expected[tokenName]);
1539
+ where.push(`${dialect.quote(tokenName)} = ${dialect.placeholder(params.length)}`);
1540
+ }
1541
+ const text = `update ${dialect.quote(tableName(tableObject))} set ${sets.join(", ")} where ${where.join(" and ")}`;
1542
+ const result = await withProviderErrors("update", tableObject, () =>
1543
+ callProvider(db, "exec", lowerQuery(text, params, dialect), options));
1544
+ if (tokenName !== null && expected[tokenName] !== undefined && Number(result?.affectedRows ?? 0) === 0) {
1545
+ throw new SloppyOrmConcurrencyError(`Sloppy ORM update on '${tableName(tableObject)}' did not match the expected concurrency token.`);
1546
+ }
1547
+ return result;
1548
+ }
1549
+
1550
+ async function deleteById(tableObject, db, id, options = {}) {
1551
+ const dialect = dialectFor(db, options);
1552
+ const pk = primaryKeyColumn(tableObject);
1553
+ const params = [id];
1554
+ const where = [`${dialect.quote(pk.name)} = ${dialect.placeholder(1)}`];
1555
+ const tokenName = tableObject.metadata.concurrencyTokenColumn;
1556
+ const expected = options.expected ?? {};
1557
+ if (tokenName !== null && expected[tokenName] !== undefined) {
1558
+ params.push(expected[tokenName]);
1559
+ where.push(`${dialect.quote(tokenName)} = ${dialect.placeholder(params.length)}`);
1560
+ }
1561
+ const result = await withProviderErrors("delete", tableObject, () =>
1562
+ callProvider(db, "exec", lowerQuery(`delete from ${dialect.quote(tableName(tableObject))} where ${where.join(" and ")}`, params, dialect), options));
1563
+ if (tokenName !== null && expected[tokenName] !== undefined && Number(result?.affectedRows ?? 0) === 0) {
1564
+ throw new SloppyOrmConcurrencyError(`Sloppy ORM delete on '${tableName(tableObject)}' did not match the expected concurrency token.`);
1565
+ }
1566
+ return result;
1567
+ }
1568
+
1569
+ function softDeleteById(tableObject, db, id, options = {}) {
1570
+ const softDeleteColumn = tableObject.metadata.softDeleteColumn;
1571
+ if (softDeleteColumn === null) {
1572
+ throw ormError("SLOPPY_ORM_SOFT_DELETE_UNAVAILABLE", `Sloppy ORM table '${tableName(tableObject)}' has no soft-delete column.`);
1573
+ }
1574
+ return updateById(tableObject, db, id, {
1575
+ [softDeleteColumn]: operation.setNow(),
1576
+ }, { ...options, allowPrivate: true });
1577
+ }
1578
+
1579
+ const operation = Object.freeze({
1580
+ increment(value = 1) {
1581
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1582
+ throw new TypeError("Sloppy ORM increment() value must be a finite number.");
1583
+ }
1584
+ return Object.freeze({ __sloppyOrmOperation: true, kind: "increment", value });
1585
+ },
1586
+ decrement(value = 1) {
1587
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1588
+ throw new TypeError("Sloppy ORM decrement() value must be a finite number.");
1589
+ }
1590
+ return Object.freeze({ __sloppyOrmOperation: true, kind: "decrement", value });
1591
+ },
1592
+ setNow() {
1593
+ return Object.freeze({ __sloppyOrmOperation: true, kind: "setNow" });
1594
+ },
1595
+ raw(value) {
1596
+ return Object.freeze({ __sloppyOrmOperation: true, kind: "raw", value });
1597
+ },
1598
+ });
1599
+
1600
+ function createEditor(tableObject, row) {
1601
+ if (!isPlainObject(row)) {
1602
+ throw new TypeError("Sloppy ORM edit() row must be a plain object.");
1603
+ }
1604
+ const patch = {};
1605
+ const editor = {
1606
+ set(name, value) {
1607
+ if (value === undefined) {
1608
+ throw ormError("SLOPPY_ORM_UNDEFINED_PATCH_VALUE", `Sloppy ORM editor field '${name}' cannot be undefined.`);
1609
+ }
1610
+ validatePatchValue(tableObject, { [name]: value });
1611
+ patch[name] = value;
1612
+ return editor;
1613
+ },
1614
+ patch() {
1615
+ return freezeDeep({ ...patch });
1616
+ },
1617
+ async save(db, options = {}) {
1618
+ const pk = primaryKeyColumn(tableObject);
1619
+ return tableObject.updateById(db, row[pk.name], patch, options);
1620
+ },
1621
+ };
1622
+ return Object.freeze(editor);
1623
+ }
1624
+
1625
+ function relation(tableObject, callback) {
1626
+ assertTable(tableObject);
1627
+ if (typeof callback !== "function") {
1628
+ throw new TypeError("Sloppy ORM relation() expects a callback.");
1629
+ }
1630
+ const helpers = Object.freeze({
1631
+ one(target, options) {
1632
+ return relationDefinition("one", tableObject, target, options);
1633
+ },
1634
+ many(target, options) {
1635
+ return relationDefinition("many", tableObject, target, options);
1636
+ },
1637
+ });
1638
+ const definitions = callback(helpers);
1639
+ if (!isPlainObject(definitions) || Object.keys(definitions).length === 0) {
1640
+ throw ormError("SLOPPY_ORM_INVALID_RELATION", "Sloppy ORM relation() must return a non-empty object.");
1641
+ }
1642
+ const entries = tableObject[ORM_RELATIONS];
1643
+ for (const [name, definition] of Object.entries(definitions)) {
1644
+ assertIdentifier(name, "relation name");
1645
+ const next = freezeDeep({ name, ...definition });
1646
+ const existing = entries.findIndex((entry) => entry.name === name);
1647
+ if (existing >= 0) {
1648
+ entries[existing] = next;
1649
+ } else {
1650
+ entries.push(next);
1651
+ }
1652
+ }
1653
+ return tableObject;
1654
+ }
1655
+
1656
+ function relationDefinition(kind, source, target, options) {
1657
+ assertTable(source, "relation source");
1658
+ assertTable(target, "relation target");
1659
+ if (!isPlainObject(options)) {
1660
+ throw new TypeError("Sloppy ORM relation options must be a plain object.");
1661
+ }
1662
+ assertColumn(options.local, "relation local");
1663
+ assertColumn(options.foreign, "relation foreign");
1664
+ if (options.local.table !== source) {
1665
+ throw new TypeError("Sloppy ORM relation local column must belong to the source table.");
1666
+ }
1667
+ if (options.foreign.table !== target) {
1668
+ throw new TypeError("Sloppy ORM relation foreign column must belong to the target table.");
1669
+ }
1670
+ return {
1671
+ kind,
1672
+ target,
1673
+ local: options.local,
1674
+ foreign: options.foreign,
1675
+ };
1676
+ }
1677
+
1678
+ function relationsFor(tableObject) {
1679
+ return Object.freeze([...(tableObject[ORM_RELATIONS] ?? [])]);
1680
+ }
1681
+
1682
+ function createIncludeBuilder(relationEntry, state = {}) {
1683
+ const current = {
1684
+ where: state.where ?? null,
1685
+ limit: state.limit ?? null,
1686
+ options: state.options ?? {},
1687
+ };
1688
+ const include = {
1689
+ __sloppyOrmInclude: true,
1690
+ relation: relationEntry,
1691
+ where(predicate) {
1692
+ if (typeof predicate !== "function") {
1693
+ throw new TypeError("Sloppy ORM include.where() expects a predicate callback.");
1694
+ }
1695
+ const expression = predicate(createTableProxy(relationEntry.target), { and, or, not, sql: rawSql });
1696
+ const checked = expressionArg(expression, "include.where()");
1697
+ return createIncludeBuilder(relationEntry, { ...current, where: checked });
1698
+ },
1699
+ take(count) {
1700
+ assertNonNegativeInteger(count, "include.take");
1701
+ return createIncludeBuilder(relationEntry, { ...current, limit: count });
1702
+ },
1703
+ withOptions(options) {
1704
+ return createIncludeBuilder(relationEntry, { ...current, options: normalizeIncludeOptions(options) });
1705
+ },
1706
+ __state: current,
1707
+ };
1708
+ return Object.freeze(include);
1709
+ }
1710
+
1711
+ function normalizeIncludeOptions(options) {
1712
+ if (options === undefined) {
1713
+ return Object.freeze({});
1714
+ }
1715
+ if (!isPlainObject(options)) {
1716
+ throw new TypeError("Sloppy ORM include options must be a plain object.");
1717
+ }
1718
+ if (options.strategy !== undefined && options.strategy !== "join" && options.strategy !== "split") {
1719
+ throw ormError("SLOPPY_ORM_INVALID_INCLUDE_STRATEGY", "Sloppy ORM include strategy must be 'join' or 'split'.");
1720
+ }
1721
+ return Object.freeze({ ...options });
1722
+ }
1723
+
1724
+ async function loadIncludes(parentRows, queryState, db, options) {
1725
+ if (queryState.includes.length === 0 || parentRows.length === 0) {
1726
+ return parentRows;
1727
+ }
1728
+ let rows = parentRows.map((row) => ({ ...row }));
1729
+ for (const include of queryState.includes) {
1730
+ rows = await loadInclude(rows, include, db, options);
1731
+ }
1732
+ return immutableRows(rows);
1733
+ }
1734
+
1735
+ async function loadInclude(parentRows, include, db, options) {
1736
+ const relationEntry = include.relation;
1737
+ const localName = relationEntry.local.name;
1738
+ const foreignName = relationEntry.foreign.name;
1739
+ const ids = [...new Set(parentRows.map((row) => row[localName]).filter((value) => value !== null && value !== undefined))];
1740
+ if (ids.length === 0) {
1741
+ return parentRows.map((row) => ({ ...row, [relationEntry.name]: relationEntry.kind === "many" ? Object.freeze([]) : null }));
1742
+ }
1743
+ let childQuery = orm.from(relationEntry.target).where((t) => t[foreignName].in(ids));
1744
+ if (include.__state.where !== null) {
1745
+ childQuery = childQuery.where(() => include.__state.where);
1746
+ }
1747
+ if (include.__state.limit !== null) {
1748
+ childQuery = childQuery.take(include.__state.limit);
1749
+ }
1750
+ const children = await childQuery.toList(db, options);
1751
+ const grouped = new Map();
1752
+ for (const child of children) {
1753
+ const key = child[foreignName];
1754
+ const bucket = grouped.get(key) ?? [];
1755
+ bucket.push(child);
1756
+ grouped.set(key, bucket);
1757
+ }
1758
+ return parentRows.map((row) => {
1759
+ const bucket = grouped.get(row[localName]) ?? [];
1760
+ return {
1761
+ ...row,
1762
+ [relationEntry.name]: relationEntry.kind === "many"
1763
+ ? Object.freeze(bucket.map((item) => immutableRow(item)))
1764
+ : (bucket[0] === undefined ? null : immutableRow(bucket[0])),
1765
+ };
1766
+ });
1767
+ }
1768
+
1769
+ function wrapOrmCursor(cursorValue, state, options = {}) {
1770
+ let closed = false;
1771
+ let rowsSeen = 0;
1772
+ const maxRows = options.maxRows ?? null;
1773
+ const cursor = {
1774
+ provider: cursorValue.provider,
1775
+ mode: cursorValue.mode,
1776
+ columns: cursorValue.columns,
1777
+ columnNames: cursorValue.columnNames,
1778
+ selected: state.projection === null
1779
+ ? Object.keys(state.table.metadata.columns)
1780
+ : state.projection.map((item) => item.alias),
1781
+ get closed() {
1782
+ return closed || cursorValue.closed === true;
1783
+ },
1784
+ async close() {
1785
+ closed = true;
1786
+ if (typeof cursorValue.close === "function") {
1787
+ await cursorValue.close();
1788
+ }
1789
+ },
1790
+ [Symbol.asyncIterator]() {
1791
+ const iterator = cursorValue[Symbol.asyncIterator]();
1792
+ return {
1793
+ async next() {
1794
+ try {
1795
+ if (maxRows !== null && rowsSeen >= maxRows) {
1796
+ closed = true;
1797
+ await cursor.close();
1798
+ return { done: true };
1799
+ }
1800
+ const item = await iterator.next();
1801
+ if (item.done) {
1802
+ closed = true;
1803
+ return item;
1804
+ }
1805
+ rowsSeen += 1;
1806
+ return { done: false, value: immutableRow(item.value) };
1807
+ } catch (error) {
1808
+ closed = true;
1809
+ await cursor.close().catch(() => {});
1810
+ throw wrapProviderError(error, "cursor next", state.table);
1811
+ }
1812
+ },
1813
+ async return() {
1814
+ closed = true;
1815
+ if (typeof iterator.return === "function") {
1816
+ await iterator.return();
1817
+ }
1818
+ await cursor.close();
1819
+ return { done: true };
1820
+ },
1821
+ };
1822
+ },
1823
+ };
1824
+ return Object.freeze(cursor);
1825
+ }
1826
+
1827
+ async function transaction(db, callback) {
1828
+ if (typeof callback !== "function") {
1829
+ throw new TypeError("Sloppy ORM transaction callback must be a function.");
1830
+ }
1831
+ if (typeof db?.transaction !== "function") {
1832
+ throw ormError("SLOPPY_ORM_TRANSACTION_UNAVAILABLE", "Sloppy ORM transaction requires a database provider with transaction(callback).");
1833
+ }
1834
+ return db.transaction((tx) => callback(tx));
1835
+ }
1836
+
1837
+ async function query(db, raw, mapper = undefined, options = {}) {
1838
+ if (raw === null || typeof raw !== "object" || raw[ORM_RAW] !== true) {
1839
+ throw new TypeError("Sloppy ORM query() expects a raw SQL fragment created by orm.sql.");
1840
+ }
1841
+ const dialect = dialectFor(db, options);
1842
+ if (raw.provider !== "any" && raw.provider !== dialect.provider) {
1843
+ throw ormError("SLOPPY_ORM_PROVIDER_SQL_MISMATCH", `Sloppy ORM raw SQL for '${raw.provider}' cannot run on '${dialect.provider}'.`);
1844
+ }
1845
+ const rows = await withProviderErrors("raw query", undefined, () =>
1846
+ callProvider(db, "query", raw.query, options));
1847
+ if (mapper === undefined) {
1848
+ return immutableRows(rows);
1849
+ }
1850
+ if (typeof mapper !== "function") {
1851
+ throw new TypeError("Sloppy ORM query() mapper must be a function.");
1852
+ }
1853
+ return immutableRows(rows.map((rowValue) => mapper(rowValue)));
1854
+ }
1855
+
1856
+ function cursor(db, raw, options = {}) {
1857
+ if (raw === null || typeof raw !== "object" || raw[ORM_RAW] !== true) {
1858
+ throw new TypeError("Sloppy ORM cursor() expects a raw SQL fragment created by orm.sql.");
1859
+ }
1860
+ const dialect = dialectFor(db, options);
1861
+ if (raw.provider !== "any" && raw.provider !== dialect.provider) {
1862
+ throw ormError("SLOPPY_ORM_PROVIDER_SQL_MISMATCH", `Sloppy ORM raw SQL for '${raw.provider}' cannot run on '${dialect.provider}'.`);
1863
+ }
1864
+ return withProviderErrors("raw cursor", undefined, () =>
1865
+ callProvider(db, "queryCursor", raw.query, options));
1866
+ }
1867
+
1868
+ function ndjson(cursorValue, mapper = undefined) {
1869
+ if (cursorValue === null || typeof cursorValue !== "object" || typeof cursorValue[Symbol.asyncIterator] !== "function") {
1870
+ throw new TypeError("Sloppy ORM ndjson() expects an ORM cursor or async iterable cursor.");
1871
+ }
1872
+ if (mapper !== undefined && typeof mapper !== "function") {
1873
+ throw new TypeError("Sloppy ORM ndjson() mapper must be a function.");
1874
+ }
1875
+ const stream = {
1876
+ contentType: "application/x-ndjson; charset=utf-8",
1877
+ selected: cursorValue.selected,
1878
+ columns: cursorValue.columns,
1879
+ columnNames: cursorValue.columnNames,
1880
+ async *[Symbol.asyncIterator]() {
1881
+ try {
1882
+ for await (const row of cursorValue) {
1883
+ yield `${JSON.stringify(mapper === undefined ? row : mapper(row))}\n`;
1884
+ }
1885
+ } finally {
1886
+ if (typeof cursorValue.close === "function" && cursorValue.closed !== true) {
1887
+ await cursorValue.close();
1888
+ }
1889
+ }
1890
+ },
1891
+ };
1892
+ return Object.freeze(stream);
1893
+ }
1894
+
1895
+ function columnSqlType(meta, dialect) {
1896
+ if (dialect.provider === "sqlserver" && meta.type === "text" && (meta.unique || meta.index)) {
1897
+ return "nvarchar(450)";
1898
+ }
1899
+ return dialect.types[meta.type];
1900
+ }
1901
+
1902
+ function columnDefinitionSql(meta, dialect, options = {}) {
1903
+ const pieces = [dialect.quote(meta.name), columnSqlType(meta, dialect)];
1904
+ if (meta.primaryKey) {
1905
+ pieces.push("primary key");
1906
+ }
1907
+ if (meta.notNull && !meta.primaryKey) {
1908
+ pieces.push("not null");
1909
+ }
1910
+ if (meta.unique && !meta.primaryKey) {
1911
+ pieces.push("unique");
1912
+ }
1913
+ if (meta.defaultNow) {
1914
+ pieces.push(`default ${dialect.defaultNow}`);
1915
+ } else if (meta.default !== undefined) {
1916
+ pieces.push("default " + literalSql(meta.default));
1917
+ }
1918
+ if (meta.reference !== null && options.inlineReferences !== false) {
1919
+ pieces.push(`references ${dialect.quote(meta.reference.table)} (${dialect.quote(meta.reference.column)})`);
1920
+ }
1921
+ return pieces.join(" ");
1922
+ }
1923
+
1924
+ function createIndexSql(tableObject, meta, dialect) {
1925
+ return `create index ${dialect.quote(`ix_${tableName(tableObject)}_${meta.name}`)} on ${dialect.quote(tableName(tableObject))} (${dialect.quote(meta.name)});`;
1926
+ }
1927
+
1928
+ function createTableSql(tableObject, provider = "sqlite", options = {}) {
1929
+ assertTable(tableObject);
1930
+ const dialect = DIALECTS[provider];
1931
+ if (dialect === undefined) {
1932
+ throw ormError("SLOPPY_ORM_UNSUPPORTED_PROVIDER", `Sloppy ORM provider '${provider}' is not supported.`);
1933
+ }
1934
+ const name = tableName(tableObject);
1935
+ const lines = [];
1936
+ for (const meta of Object.values(tableObject.metadata.columns)) {
1937
+ lines.push(` ${columnDefinitionSql(meta, dialect, options)}`);
1938
+ }
1939
+ const statements = [`create table ${dialect.quote(name)} (\n${lines.join(",\n")}\n);`];
1940
+ for (const meta of Object.values(tableObject.metadata.columns)) {
1941
+ if (meta.index && !meta.primaryKey && !meta.unique) {
1942
+ statements.push(createIndexSql(tableObject, meta, dialect));
1943
+ }
1944
+ }
1945
+ return statements.join("\n");
1946
+ }
1947
+
1948
+ function tableDependsOn(tableObject, targetName) {
1949
+ return tableObject.metadata.foreignKeys.some((foreignKey) => foreignKey.foreignTable === targetName && tableName(tableObject) !== targetName);
1950
+ }
1951
+
1952
+ function orderMigrationTables(tables) {
1953
+ const remaining = [...tables];
1954
+ const ordered = [];
1955
+ while (remaining.length !== 0) {
1956
+ let moved = false;
1957
+ for (let index = 0; index < remaining.length; index += 1) {
1958
+ const candidate = remaining[index];
1959
+ const blocked = remaining.some((other) => other !== candidate && tableDependsOn(candidate, tableName(other)));
1960
+ if (!blocked) {
1961
+ ordered.push(candidate);
1962
+ remaining.splice(index, 1);
1963
+ moved = true;
1964
+ break;
1965
+ }
1966
+ }
1967
+ if (!moved) {
1968
+ ordered.push(...remaining.splice(0, remaining.length));
1969
+ }
1970
+ }
1971
+ return ordered;
1972
+ }
1973
+
1974
+ function constraintName(tableObject, meta) {
1975
+ return `fk_${tableName(tableObject)}_${meta.name}_${meta.reference.table}_${meta.reference.column}`;
1976
+ }
1977
+
1978
+ function deferredForeignKeySql(tableObject, provider) {
1979
+ const dialect = DIALECTS[provider];
1980
+ const statements = [];
1981
+ for (const meta of Object.values(tableObject.metadata.columns)) {
1982
+ if (meta.reference === null) {
1983
+ continue;
1984
+ }
1985
+ statements.push(`alter table ${dialect.quote(tableName(tableObject))} add constraint ${dialect.quote(constraintName(tableObject, meta))} foreign key (${dialect.quote(meta.name)}) references ${dialect.quote(meta.reference.table)} (${dialect.quote(meta.reference.column)});`);
1986
+ }
1987
+ return statements;
1988
+ }
1989
+
1990
+ function literalSql(value) {
1991
+ if (value === null) {
1992
+ return "null";
1993
+ }
1994
+ if (typeof value === "number") {
1995
+ return String(value);
1996
+ }
1997
+ if (typeof value === "boolean") {
1998
+ return value ? "1" : "0";
1999
+ }
2000
+ return `'${String(value).replaceAll("'", "''")}'`;
2001
+ }
2002
+
2003
+ function migrationScript(tables, options = {}) {
2004
+ const provider = options.provider ?? "sqlite";
2005
+ const tableList = orderMigrationTables(Array.isArray(tables) ? tables : [tables]);
2006
+ const inlineReferences = provider === "sqlite";
2007
+ const statements = tableList.map((tableEntry) => createTableSql(tableEntry, provider, { inlineReferences }));
2008
+ if (!inlineReferences) {
2009
+ for (const tableEntry of tableList) {
2010
+ statements.push(...deferredForeignKeySql(tableEntry, provider));
2011
+ }
2012
+ }
2013
+ return `${statements.join("\n\n")}\n`;
2014
+ }
2015
+
2016
+ function stableStringify(value) {
2017
+ if (value === null || typeof value !== "object") {
2018
+ return JSON.stringify(value);
2019
+ }
2020
+ if (Array.isArray(value)) {
2021
+ return `[${value.map((item) => stableStringify(item)).join(",")}]`;
2022
+ }
2023
+ return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
2024
+ }
2025
+
2026
+ function migrationHash(value) {
2027
+ const text = typeof value === "string" ? value : stableStringify(value);
2028
+ let hash = 2166136261;
2029
+ for (let index = 0; index < text.length; index += 1) {
2030
+ hash ^= text.charCodeAt(index);
2031
+ hash = Math.imul(hash, 16777619) >>> 0;
2032
+ }
2033
+ return hash.toString(16).padStart(8, "0");
2034
+ }
2035
+
2036
+ function migrationSnapshot(tables) {
2037
+ const tableList = (Array.isArray(tables) ? tables : [tables]).map((tableEntry) => {
2038
+ assertTable(tableEntry);
2039
+ return tableEntry;
2040
+ });
2041
+ const snapshotTables = tableList
2042
+ .map((tableObject) => {
2043
+ const columns = Object.values(tableObject.metadata.columns).map((meta) => Object.freeze({
2044
+ name: meta.name,
2045
+ type: meta.type,
2046
+ nullable: meta.nullable,
2047
+ notNull: meta.notNull,
2048
+ primaryKey: meta.primaryKey,
2049
+ unique: meta.unique,
2050
+ index: meta.index,
2051
+ generated: meta.generated,
2052
+ default: meta.default,
2053
+ defaultNow: meta.defaultNow,
2054
+ private: meta.private,
2055
+ softDelete: meta.softDelete,
2056
+ concurrencyToken: meta.concurrencyToken,
2057
+ enumValues: meta.enumValues,
2058
+ reference: meta.reference,
2059
+ })).sort((left, right) => left.name.localeCompare(right.name));
2060
+ return Object.freeze({
2061
+ name: tableName(tableObject),
2062
+ columns: Object.freeze(columns),
2063
+ primaryKey: Object.freeze([...tableObject.metadata.primaryKey]),
2064
+ unique: Object.freeze([...tableObject.metadata.unique].sort()),
2065
+ indexes: Object.freeze([...tableObject.metadata.indexes].sort()),
2066
+ foreignKeys: Object.freeze([...tableObject.metadata.foreignKeys].sort((left, right) => left.column.localeCompare(right.column))),
2067
+ privateColumns: Object.freeze([...tableObject.metadata.privateColumns].sort()),
2068
+ softDeleteColumn: tableObject.metadata.softDeleteColumn,
2069
+ concurrencyTokenColumn: tableObject.metadata.concurrencyTokenColumn,
2070
+ });
2071
+ })
2072
+ .sort((left, right) => left.name.localeCompare(right.name));
2073
+ const payload = Object.freeze({
2074
+ format: "sloppy.orm.snapshot.v1",
2075
+ tables: Object.freeze(snapshotTables),
2076
+ });
2077
+ return freezeDeep({ ...payload, checksum: migrationHash(payload) });
2078
+ }
2079
+
2080
+ function snapshotTableMap(snapshot, subject) {
2081
+ if (!isPlainObject(snapshot) || snapshot.format !== "sloppy.orm.snapshot.v1" || !Array.isArray(snapshot.tables)) {
2082
+ throw ormError("SLOPPY_ORM_INVALID_MIGRATION_SNAPSHOT", `Sloppy ORM ${subject} snapshot is invalid.`);
2083
+ }
2084
+ return new Map(snapshot.tables.map((entry) => [entry.name, entry]));
2085
+ }
2086
+
2087
+ function snapshotColumnMap(snapshotTable) {
2088
+ return new Map(snapshotTable.columns.map((entry) => [entry.name, entry]));
2089
+ }
2090
+
2091
+ function migrationDiff(previousSnapshot, nextTables, options = {}) {
2092
+ const provider = options.provider ?? "sqlite";
2093
+ const dialect = DIALECTS[provider];
2094
+ if (dialect === undefined) {
2095
+ throw ormError("SLOPPY_ORM_UNSUPPORTED_PROVIDER", `Sloppy ORM provider '${provider}' is not supported.`);
2096
+ }
2097
+ const nextSnapshot = migrationSnapshot(nextTables);
2098
+ const previousTables = snapshotTableMap(previousSnapshot, "previous");
2099
+ const nextTablesByName = snapshotTableMap(nextSnapshot, "next");
2100
+ const statements = [];
2101
+ const destructiveChanges = [];
2102
+ const nextTableObjects = new Map((Array.isArray(nextTables) ? nextTables : [nextTables]).map((tableEntry) => [tableName(tableEntry), tableEntry]));
2103
+
2104
+ for (const [name] of previousTables) {
2105
+ if (!nextTablesByName.has(name)) {
2106
+ destructiveChanges.push(`drop table ${name}`);
2107
+ }
2108
+ }
2109
+ for (const [name, nextTable] of nextTablesByName) {
2110
+ const previousTable = previousTables.get(name);
2111
+ const nextTableObject = nextTableObjects.get(name);
2112
+ if (previousTable === undefined) {
2113
+ statements.push(migrationScript(nextTableObject, { provider }).trimEnd());
2114
+ continue;
2115
+ }
2116
+ const previousColumns = snapshotColumnMap(previousTable);
2117
+ const nextColumns = snapshotColumnMap(nextTable);
2118
+ for (const [columnName, previousColumn] of previousColumns) {
2119
+ const nextColumn = nextColumns.get(columnName);
2120
+ if (nextColumn === undefined) {
2121
+ destructiveChanges.push(`drop column ${name}.${columnName}`);
2122
+ } else if (stableStringify(previousColumn) !== stableStringify(nextColumn)) {
2123
+ destructiveChanges.push(`alter column ${name}.${columnName}`);
2124
+ }
2125
+ }
2126
+ for (const [columnName, nextColumn] of nextColumns) {
2127
+ if (!previousColumns.has(columnName)) {
2128
+ statements.push(`alter table ${dialect.quote(name)} add ${columnDefinitionSql(nextColumn, dialect)};`);
2129
+ }
2130
+ }
2131
+ for (const indexName of nextTable.indexes) {
2132
+ if (!previousTable.indexes.includes(indexName)) {
2133
+ statements.push(createIndexSql(nextTableObject, nextTableObject.metadata.columns[indexName], dialect));
2134
+ }
2135
+ }
2136
+ }
2137
+ if (destructiveChanges.length !== 0 && options.allowDestructive !== true) {
2138
+ throw ormError("SLOPPY_ORM_DESTRUCTIVE_MIGRATION", "Sloppy ORM migration diff contains destructive changes. Pass allowDestructive: true to inspect them explicitly.", {
2139
+ changes: destructiveChanges,
2140
+ });
2141
+ }
2142
+ return freezeDeep({
2143
+ provider,
2144
+ fromChecksum: previousSnapshot.checksum,
2145
+ toChecksum: nextSnapshot.checksum,
2146
+ destructive: destructiveChanges.length !== 0,
2147
+ destructiveChanges,
2148
+ statements,
2149
+ sql: statements.length === 0 ? "" : `${statements.join("\n\n")}\n`,
2150
+ snapshot: nextSnapshot,
2151
+ });
2152
+ }
2153
+
2154
+ const migrations = Object.freeze({
2155
+ script: migrationScript,
2156
+ createTableSql,
2157
+ snapshot: migrationSnapshot,
2158
+ diff: migrationDiff,
2159
+ hash: migrationHash,
2160
+ apply: Migrations.apply,
2161
+ status: Migrations.status,
2162
+ });
2163
+
2164
+ const orm = Object.freeze({
2165
+ from: createQueryBuilder,
2166
+ transaction,
2167
+ query,
2168
+ cursor,
2169
+ sql: rawSql,
2170
+ and,
2171
+ or,
2172
+ not,
2173
+ op: operation,
2174
+ operation,
2175
+ migrations,
2176
+ stream: Object.freeze({ ndjson }),
2177
+ dialects: DIALECTS,
2178
+ });
2179
+
2180
+ export {
2181
+ SloppyOrmConcurrencyError,
2182
+ SloppyOrmError,
2183
+ column,
2184
+ orm,
2185
+ rawSql as sql,
2186
+ relation,
2187
+ table,
2188
+ };