@baasix/baasix 0.1.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.
- package/LICENSE.MD +85 -0
- package/README.md +526 -0
- package/assets/banner.jpg +0 -0
- package/assets/banner_small.jpg +0 -0
- package/assets/logo_icon.svg +20 -0
- package/assets/logo_icon_rounded.svg +20 -0
- package/dist/LICENSE.MD +85 -0
- package/dist/README.md +526 -0
- package/dist/app/404/index.html +1 -0
- package/dist/app/404.html +1 -0
- package/dist/app/_next/static/chunks/041e1f03-56ae8a902a7f2fe6.js +24 -0
- package/dist/app/_next/static/chunks/1117-05479929a8da73e3.js +1 -0
- package/dist/app/_next/static/chunks/1299.77cc7b7b76b75cba.js +1 -0
- package/dist/app/_next/static/chunks/1303-35a96e9c9cdeab9d.js +1 -0
- package/dist/app/_next/static/chunks/1509-56ac00cdaaecdf53.js +1 -0
- package/dist/app/_next/static/chunks/1668-e3eabd0f6753c780.js +1 -0
- package/dist/app/_next/static/chunks/1783-d9fb550fd324300c.js +1 -0
- package/dist/app/_next/static/chunks/2117-29b5fa47421595ad.js +2 -0
- package/dist/app/_next/static/chunks/2344.35b46d2179a765b5.js +1 -0
- package/dist/app/_next/static/chunks/257.990da16794a31292.js +1 -0
- package/dist/app/_next/static/chunks/2676-73b0ee7c80073a84.js +1 -0
- package/dist/app/_next/static/chunks/3563-b8842744384391fe.js +1 -0
- package/dist/app/_next/static/chunks/363642f4-933b579ed3c85f60.js +1 -0
- package/dist/app/_next/static/chunks/3817-e20c8f0a0810fc95.js +1 -0
- package/dist/app/_next/static/chunks/3834.84944e390d902509.js +2 -0
- package/dist/app/_next/static/chunks/4043-3a30c8a75896f241.js +1 -0
- package/dist/app/_next/static/chunks/4225-14090c7c0cd9dec6.js +1 -0
- package/dist/app/_next/static/chunks/4438-c9a12ca15b6e9160.js +1 -0
- package/dist/app/_next/static/chunks/4458-679fd0c6884f456a.js +1 -0
- package/dist/app/_next/static/chunks/4475-8bdfbd536fba8c48.js +1 -0
- package/dist/app/_next/static/chunks/4883-8a924721bb21b3b0.js +1 -0
- package/dist/app/_next/static/chunks/489-683ab07188f9df2b.js +1 -0
- package/dist/app/_next/static/chunks/4952-1b97320cf61f3f21.js +1 -0
- package/dist/app/_next/static/chunks/5094-8d53e403235d4ca6.js +1 -0
- package/dist/app/_next/static/chunks/5101-3a146e0625747ad1.js +1 -0
- package/dist/app/_next/static/chunks/54a60aa6-d9747982e0a81f58.js +79 -0
- package/dist/app/_next/static/chunks/5650-f096291df402bfc2.js +1 -0
- package/dist/app/_next/static/chunks/600-539045311240f579.js +1 -0
- package/dist/app/_next/static/chunks/6170-803b82e19d3ade6d.js +89 -0
- package/dist/app/_next/static/chunks/6241-30d7169d1010e5a4.js +1 -0
- package/dist/app/_next/static/chunks/6530-a91e10cffa4200c4.js +1 -0
- package/dist/app/_next/static/chunks/6547-4bbbdb5c399aef1e.js +1 -0
- package/dist/app/_next/static/chunks/6712-781937c53a2c49da.js +1 -0
- package/dist/app/_next/static/chunks/6fcbdc68-90be1a5480b8d353.js +1 -0
- package/dist/app/_next/static/chunks/70e0d97a-aeaf0cdc26ba1a58.js +1 -0
- package/dist/app/_next/static/chunks/7214-5154a89d08d24dde.js +1 -0
- package/dist/app/_next/static/chunks/7324-b53229c59a640880.js +10 -0
- package/dist/app/_next/static/chunks/7636-66424f0b51d350e9.js +1 -0
- package/dist/app/_next/static/chunks/7874-39a3f2541165a675.js +1 -0
- package/dist/app/_next/static/chunks/7982-9da12b83f11e3f5f.js +1 -0
- package/dist/app/_next/static/chunks/8213a2eb-da25a3b3c5521b2b.js +1 -0
- package/dist/app/_next/static/chunks/8473-6598318371eca31b.js +1 -0
- package/dist/app/_next/static/chunks/8640fa6b-72e43370f68e5587.js +1 -0
- package/dist/app/_next/static/chunks/9090-3ef676f29c95f1c7.js +1 -0
- package/dist/app/_next/static/chunks/9124-a02f9e209e6e3cce.js +1 -0
- package/dist/app/_next/static/chunks/926-156f32067d111d6b.js +1 -0
- package/dist/app/_next/static/chunks/9487-b17481605e513b83.js +1 -0
- package/dist/app/_next/static/chunks/9599-a7e572bb88c3392b.js +1 -0
- package/dist/app/_next/static/chunks/9881-419697138376e755.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/all-activity/page-8917930b4d663405.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/email-log/page-b27a6ee32782d7df.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/notifications/page-b7eda523ede2702c.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/page-1cfa62d1caedaed0.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/sessions/page-3e21e20db90aeff7.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/workflow-executions/page-27bcc26b747fb29b.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/activity-log/workflow-logs/page-9f9e9e952aef436e.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/change-password/page-8d61aa499eabb127.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/dashboard/page-1ceeac9e72997a8a.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/data-browser/page-8cda2b57759dd670.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/file-manager/page-8c6f1b1da66ad7e4.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/layout-f70d225b2759c998.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/migrations/page-aacec8f7cfb40ab2.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/permissions/page-828110cfcde429c6.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/project/page-420e794bb76bd204.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/roles/page-9001d02b28f70708.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/schema/page-899574f35091dd58.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/tasks/page-ad7ab3e27c83f44f.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/templates/edit/page-bd83414cb8c4cb04.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/templates/page-3181447f8772b1d3.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/settings/tenants/page-ef9bfbacef5a1d73.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/users/invites/page-480306b7b2bbac7e.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/users/list/page-74da51254c2606b3.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/users/page-e99c6f0b915001b2.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/users/preferences/page-1a935630ce8f2b12.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/users/user-roles/page-901dfb8ea1f39ca8.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/workflows/detail/page-9a6b839aea688ca4.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/workflows/edit/page-11774efbc2fecae2.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/workflows/execution/page-8ec1aea90412c03d.js +1 -0
- package/dist/app/_next/static/chunks/app/(authenticated)/workflows/page-88bc5b36ccb0a1f7.js +1 -0
- package/dist/app/_next/static/chunks/app/(public)/forgot-password/page-ed263fd46ef81c20.js +1 -0
- package/dist/app/_next/static/chunks/app/(public)/layout-f538977545844af8.js +1 -0
- package/dist/app/_next/static/chunks/app/(public)/login/page-c0a10b137f346096.js +1 -0
- package/dist/app/_next/static/chunks/app/(public)/register/page-4cb7644893efd9b3.js +1 -0
- package/dist/app/_next/static/chunks/app/_not-found/page-653f8815b78256cc.js +1 -0
- package/dist/app/_next/static/chunks/app/layout-591ca7a3e16528a1.js +1 -0
- package/dist/app/_next/static/chunks/app/page-dd19d124b5fa2577.js +1 -0
- package/dist/app/_next/static/chunks/c37d3baf.c2ff165f5b02c692.js +1 -0
- package/dist/app/_next/static/chunks/d0deef33.0379166a4ec23470.js +1 -0
- package/dist/app/_next/static/chunks/fd9d1056-54169f07cd680d6c.js +1 -0
- package/dist/app/_next/static/chunks/framework-8e0e0f4a6b83a956.js +1 -0
- package/dist/app/_next/static/chunks/main-324e91f5a430cddf.js +1 -0
- package/dist/app/_next/static/chunks/main-app-55bcae20c77aaf0e.js +1 -0
- package/dist/app/_next/static/chunks/pages/_app-3c9ca398d360b709.js +1 -0
- package/dist/app/_next/static/chunks/pages/_error-cf5ca766ac8f493f.js +1 -0
- package/dist/app/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/app/_next/static/chunks/webpack-2c306566f7ee1b63.js +1 -0
- package/dist/app/_next/static/css/6c4002bae4e236b2.css +3 -0
- package/dist/app/_next/static/css/a275cc2b185e04f8.css +1 -0
- package/dist/app/_next/static/eCWhKA8XHqmB1zgFcEtN2/_buildManifest.js +1 -0
- package/dist/app/_next/static/eCWhKA8XHqmB1zgFcEtN2/_ssgManifest.js +1 -0
- package/dist/app/activity-log/all-activity/index.html +1 -0
- package/dist/app/activity-log/all-activity/index.txt +14 -0
- package/dist/app/activity-log/email-log/index.html +1 -0
- package/dist/app/activity-log/email-log/index.txt +14 -0
- package/dist/app/activity-log/index.html +1 -0
- package/dist/app/activity-log/index.txt +14 -0
- package/dist/app/activity-log/notifications/index.html +1 -0
- package/dist/app/activity-log/notifications/index.txt +14 -0
- package/dist/app/activity-log/sessions/index.html +1 -0
- package/dist/app/activity-log/sessions/index.txt +14 -0
- package/dist/app/activity-log/workflow-executions/index.html +1 -0
- package/dist/app/activity-log/workflow-executions/index.txt +14 -0
- package/dist/app/activity-log/workflow-logs/index.html +1 -0
- package/dist/app/activity-log/workflow-logs/index.txt +14 -0
- package/dist/app/change-password/index.html +1 -0
- package/dist/app/change-password/index.txt +14 -0
- package/dist/app/dashboard/index.html +1 -0
- package/dist/app/dashboard/index.txt +14 -0
- package/dist/app/data-browser/index.html +1 -0
- package/dist/app/data-browser/index.txt +14 -0
- package/dist/app/file-manager/index.html +1 -0
- package/dist/app/file-manager/index.txt +14 -0
- package/dist/app/forgot-password/index.html +1 -0
- package/dist/app/forgot-password/index.txt +13 -0
- package/dist/app/index.html +1 -0
- package/dist/app/index.txt +9 -0
- package/dist/app/login/index.html +1 -0
- package/dist/app/login/index.txt +13 -0
- package/dist/app/logo-dark.png +0 -0
- package/dist/app/logo-icon.svg +81 -0
- package/dist/app/logo-light.png +0 -0
- package/dist/app/register/index.html +1 -0
- package/dist/app/register/index.txt +13 -0
- package/dist/app/settings/migrations/index.html +1 -0
- package/dist/app/settings/migrations/index.txt +14 -0
- package/dist/app/settings/permissions/index.html +1 -0
- package/dist/app/settings/permissions/index.txt +14 -0
- package/dist/app/settings/project/index.html +1 -0
- package/dist/app/settings/project/index.txt +14 -0
- package/dist/app/settings/roles/index.html +1 -0
- package/dist/app/settings/roles/index.txt +14 -0
- package/dist/app/settings/schema/index.html +1 -0
- package/dist/app/settings/schema/index.txt +14 -0
- package/dist/app/settings/tasks/index.html +1 -0
- package/dist/app/settings/tasks/index.txt +14 -0
- package/dist/app/settings/templates/edit/index.html +1 -0
- package/dist/app/settings/templates/edit/index.txt +14 -0
- package/dist/app/settings/templates/index.html +1 -0
- package/dist/app/settings/templates/index.txt +14 -0
- package/dist/app/settings/tenants/index.html +1 -0
- package/dist/app/settings/tenants/index.txt +14 -0
- package/dist/app/users/index.html +1 -0
- package/dist/app/users/index.txt +14 -0
- package/dist/app/users/invites/index.html +1 -0
- package/dist/app/users/invites/index.txt +14 -0
- package/dist/app/users/list/index.html +1 -0
- package/dist/app/users/list/index.txt +14 -0
- package/dist/app/users/preferences/index.html +1 -0
- package/dist/app/users/preferences/index.txt +14 -0
- package/dist/app/users/user-roles/index.html +1 -0
- package/dist/app/users/user-roles/index.txt +14 -0
- package/dist/app/workflows/detail/index.html +1 -0
- package/dist/app/workflows/detail/index.txt +14 -0
- package/dist/app/workflows/edit/index.html +1 -0
- package/dist/app/workflows/edit/index.txt +14 -0
- package/dist/app/workflows/execution/index.html +1 -0
- package/dist/app/workflows/execution/index.txt +14 -0
- package/dist/app/workflows/index.html +1 -0
- package/dist/app/workflows/index.txt +14 -0
- package/dist/app.d.ts +36 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +546 -0
- package/dist/app.js.map +1 -0
- package/dist/auth/adapters/baasix-adapter.d.ts +12 -0
- package/dist/auth/adapters/baasix-adapter.d.ts.map +1 -0
- package/dist/auth/adapters/baasix-adapter.js +318 -0
- package/dist/auth/adapters/baasix-adapter.js.map +1 -0
- package/dist/auth/adapters/index.d.ts +6 -0
- package/dist/auth/adapters/index.d.ts.map +1 -0
- package/dist/auth/adapters/index.js +5 -0
- package/dist/auth/adapters/index.js.map +1 -0
- package/dist/auth/core.d.ts +73 -0
- package/dist/auth/core.d.ts.map +1 -0
- package/dist/auth/core.js +528 -0
- package/dist/auth/core.js.map +1 -0
- package/dist/auth/index.d.ts +56 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +58 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauth2/index.d.ts +5 -0
- package/dist/auth/oauth2/index.d.ts.map +1 -0
- package/dist/auth/oauth2/index.js +5 -0
- package/dist/auth/oauth2/index.js.map +1 -0
- package/dist/auth/oauth2/utils.d.ts +90 -0
- package/dist/auth/oauth2/utils.d.ts.map +1 -0
- package/dist/auth/oauth2/utils.js +167 -0
- package/dist/auth/oauth2/utils.js.map +1 -0
- package/dist/auth/providers/apple.d.ts +28 -0
- package/dist/auth/providers/apple.d.ts.map +1 -0
- package/dist/auth/providers/apple.js +192 -0
- package/dist/auth/providers/apple.js.map +1 -0
- package/dist/auth/providers/credential.d.ts +87 -0
- package/dist/auth/providers/credential.d.ts.map +1 -0
- package/dist/auth/providers/credential.js +162 -0
- package/dist/auth/providers/credential.js.map +1 -0
- package/dist/auth/providers/facebook.d.ts +26 -0
- package/dist/auth/providers/facebook.d.ts.map +1 -0
- package/dist/auth/providers/facebook.js +112 -0
- package/dist/auth/providers/facebook.js.map +1 -0
- package/dist/auth/providers/github.d.ts +29 -0
- package/dist/auth/providers/github.d.ts.map +1 -0
- package/dist/auth/providers/github.js +144 -0
- package/dist/auth/providers/github.js.map +1 -0
- package/dist/auth/providers/google.d.ts +32 -0
- package/dist/auth/providers/google.d.ts.map +1 -0
- package/dist/auth/providers/google.js +145 -0
- package/dist/auth/providers/google.js.map +1 -0
- package/dist/auth/providers/index.d.ts +22 -0
- package/dist/auth/providers/index.d.ts.map +1 -0
- package/dist/auth/providers/index.js +17 -0
- package/dist/auth/providers/index.js.map +1 -0
- package/dist/auth/routes.d.ts +63 -0
- package/dist/auth/routes.d.ts.map +1 -0
- package/dist/auth/routes.js +827 -0
- package/dist/auth/routes.js.map +1 -0
- package/dist/auth/services/index.d.ts +10 -0
- package/dist/auth/services/index.d.ts.map +1 -0
- package/dist/auth/services/index.js +7 -0
- package/dist/auth/services/index.js.map +1 -0
- package/dist/auth/services/session.d.ts +81 -0
- package/dist/auth/services/session.d.ts.map +1 -0
- package/dist/auth/services/session.js +186 -0
- package/dist/auth/services/session.js.map +1 -0
- package/dist/auth/services/token.d.ts +41 -0
- package/dist/auth/services/token.d.ts.map +1 -0
- package/dist/auth/services/token.js +44 -0
- package/dist/auth/services/token.js.map +1 -0
- package/dist/auth/services/verification.d.ts +77 -0
- package/dist/auth/services/verification.d.ts.map +1 -0
- package/dist/auth/services/verification.js +143 -0
- package/dist/auth/services/verification.js.map +1 -0
- package/dist/auth/types.d.ts +318 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +6 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/customTypes/arrays.d.ts +200 -0
- package/dist/customTypes/arrays.d.ts.map +1 -0
- package/dist/customTypes/arrays.js +309 -0
- package/dist/customTypes/arrays.js.map +1 -0
- package/dist/customTypes/index.d.ts +8 -0
- package/dist/customTypes/index.d.ts.map +1 -0
- package/dist/customTypes/index.js +11 -0
- package/dist/customTypes/index.js.map +1 -0
- package/dist/customTypes/postgis.d.ts +146 -0
- package/dist/customTypes/postgis.d.ts.map +1 -0
- package/dist/customTypes/postgis.js +315 -0
- package/dist/customTypes/postgis.js.map +1 -0
- package/dist/customTypes/ranges.d.ts +128 -0
- package/dist/customTypes/ranges.d.ts.map +1 -0
- package/dist/customTypes/ranges.js +257 -0
- package/dist/customTypes/ranges.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +42 -0
- package/dist/index.js.map +1 -0
- package/dist/migrations/0.1.0-alpha.0_initial_setup.d.ts +29 -0
- package/dist/migrations/0.1.0-alpha.0_initial_setup.d.ts.map +1 -0
- package/dist/migrations/0.1.0-alpha.0_initial_setup.js +72 -0
- package/dist/migrations/0.1.0-alpha.0_initial_setup.js.map +1 -0
- package/dist/migrations/_example_migration.d.ts +31 -0
- package/dist/migrations/_example_migration.d.ts.map +1 -0
- package/dist/migrations/_example_migration.js +75 -0
- package/dist/migrations/_example_migration.js.map +1 -0
- package/dist/plugins/definePlugin.d.ts +49 -0
- package/dist/plugins/definePlugin.d.ts.map +1 -0
- package/dist/plugins/definePlugin.js +131 -0
- package/dist/plugins/definePlugin.js.map +1 -0
- package/dist/plugins/softDelete.d.ts +179 -0
- package/dist/plugins/softDelete.d.ts.map +1 -0
- package/dist/plugins/softDelete.js +235 -0
- package/dist/plugins/softDelete.js.map +1 -0
- package/dist/routes/auth.route.d.ts +14 -0
- package/dist/routes/auth.route.d.ts.map +1 -0
- package/dist/routes/auth.route.js +421 -0
- package/dist/routes/auth.route.js.map +1 -0
- package/dist/routes/file.route.d.ts +7 -0
- package/dist/routes/file.route.d.ts.map +1 -0
- package/dist/routes/file.route.js +274 -0
- package/dist/routes/file.route.js.map +1 -0
- package/dist/routes/items.route.d.ts +7 -0
- package/dist/routes/items.route.d.ts.map +1 -0
- package/dist/routes/items.route.js +369 -0
- package/dist/routes/items.route.js.map +1 -0
- package/dist/routes/migration.route.d.ts +7 -0
- package/dist/routes/migration.route.d.ts.map +1 -0
- package/dist/routes/migration.route.js +225 -0
- package/dist/routes/migration.route.js.map +1 -0
- package/dist/routes/notification.route.d.ts +7 -0
- package/dist/routes/notification.route.d.ts.map +1 -0
- package/dist/routes/notification.route.js +124 -0
- package/dist/routes/notification.route.js.map +1 -0
- package/dist/routes/openapi.route.d.ts +7 -0
- package/dist/routes/openapi.route.d.ts.map +1 -0
- package/dist/routes/openapi.route.js +2169 -0
- package/dist/routes/openapi.route.js.map +1 -0
- package/dist/routes/permission.route.d.ts +7 -0
- package/dist/routes/permission.route.d.ts.map +1 -0
- package/dist/routes/permission.route.js +158 -0
- package/dist/routes/permission.route.js.map +1 -0
- package/dist/routes/realtime.route.d.ts +21 -0
- package/dist/routes/realtime.route.d.ts.map +1 -0
- package/dist/routes/realtime.route.js +243 -0
- package/dist/routes/realtime.route.js.map +1 -0
- package/dist/routes/reports.route.d.ts +7 -0
- package/dist/routes/reports.route.d.ts.map +1 -0
- package/dist/routes/reports.route.js +95 -0
- package/dist/routes/reports.route.js.map +1 -0
- package/dist/routes/schema.route.d.ts +7 -0
- package/dist/routes/schema.route.d.ts.map +1 -0
- package/dist/routes/schema.route.js +1780 -0
- package/dist/routes/schema.route.js.map +1 -0
- package/dist/routes/settings.route.d.ts +7 -0
- package/dist/routes/settings.route.d.ts.map +1 -0
- package/dist/routes/settings.route.js +154 -0
- package/dist/routes/settings.route.js.map +1 -0
- package/dist/routes/templates.route.d.ts +7 -0
- package/dist/routes/templates.route.d.ts.map +1 -0
- package/dist/routes/templates.route.js +91 -0
- package/dist/routes/templates.route.js.map +1 -0
- package/dist/routes/utils.route.d.ts +7 -0
- package/dist/routes/utils.route.d.ts.map +1 -0
- package/dist/routes/utils.route.js +33 -0
- package/dist/routes/utils.route.js.map +1 -0
- package/dist/routes/workflow.route.d.ts +7 -0
- package/dist/routes/workflow.route.d.ts.map +1 -0
- package/dist/routes/workflow.route.js +787 -0
- package/dist/routes/workflow.route.js.map +1 -0
- package/dist/services/AssetsService.d.ts +39 -0
- package/dist/services/AssetsService.d.ts.map +1 -0
- package/dist/services/AssetsService.js +255 -0
- package/dist/services/AssetsService.js.map +1 -0
- package/dist/services/CacheService.d.ts +169 -0
- package/dist/services/CacheService.d.ts.map +1 -0
- package/dist/services/CacheService.js +722 -0
- package/dist/services/CacheService.js.map +1 -0
- package/dist/services/FilesService.d.ts +30 -0
- package/dist/services/FilesService.d.ts.map +1 -0
- package/dist/services/FilesService.js +268 -0
- package/dist/services/FilesService.js.map +1 -0
- package/dist/services/HooksManager.d.ts +38 -0
- package/dist/services/HooksManager.d.ts.map +1 -0
- package/dist/services/HooksManager.js +165 -0
- package/dist/services/HooksManager.js.map +1 -0
- package/dist/services/ItemsService.d.ts +273 -0
- package/dist/services/ItemsService.d.ts.map +1 -0
- package/dist/services/ItemsService.js +2458 -0
- package/dist/services/ItemsService.js.map +1 -0
- package/dist/services/MailService.d.ts +76 -0
- package/dist/services/MailService.d.ts.map +1 -0
- package/dist/services/MailService.js +585 -0
- package/dist/services/MailService.js.map +1 -0
- package/dist/services/MigrationService.d.ts +243 -0
- package/dist/services/MigrationService.d.ts.map +1 -0
- package/dist/services/MigrationService.js +914 -0
- package/dist/services/MigrationService.js.map +1 -0
- package/dist/services/NotificationService.d.ts +35 -0
- package/dist/services/NotificationService.d.ts.map +1 -0
- package/dist/services/NotificationService.js +159 -0
- package/dist/services/NotificationService.js.map +1 -0
- package/dist/services/PermissionService.d.ts +128 -0
- package/dist/services/PermissionService.d.ts.map +1 -0
- package/dist/services/PermissionService.js +373 -0
- package/dist/services/PermissionService.js.map +1 -0
- package/dist/services/PluginManager.d.ts +138 -0
- package/dist/services/PluginManager.d.ts.map +1 -0
- package/dist/services/PluginManager.js +463 -0
- package/dist/services/PluginManager.js.map +1 -0
- package/dist/services/RealtimeService.d.ts +209 -0
- package/dist/services/RealtimeService.d.ts.map +1 -0
- package/dist/services/RealtimeService.js +978 -0
- package/dist/services/RealtimeService.js.map +1 -0
- package/dist/services/ReportService.d.ts +13 -0
- package/dist/services/ReportService.d.ts.map +1 -0
- package/dist/services/ReportService.js +91 -0
- package/dist/services/ReportService.js.map +1 -0
- package/dist/services/SettingsService.d.ts +60 -0
- package/dist/services/SettingsService.d.ts.map +1 -0
- package/dist/services/SettingsService.js +474 -0
- package/dist/services/SettingsService.js.map +1 -0
- package/dist/services/SocketService.d.ts +129 -0
- package/dist/services/SocketService.d.ts.map +1 -0
- package/dist/services/SocketService.js +600 -0
- package/dist/services/SocketService.js.map +1 -0
- package/dist/services/StatsService.d.ts +10 -0
- package/dist/services/StatsService.d.ts.map +1 -0
- package/dist/services/StatsService.js +40 -0
- package/dist/services/StatsService.js.map +1 -0
- package/dist/services/StorageService.d.ts +20 -0
- package/dist/services/StorageService.d.ts.map +1 -0
- package/dist/services/StorageService.js +164 -0
- package/dist/services/StorageService.js.map +1 -0
- package/dist/services/TasksService.d.ts +74 -0
- package/dist/services/TasksService.d.ts.map +1 -0
- package/dist/services/TasksService.js +404 -0
- package/dist/services/TasksService.js.map +1 -0
- package/dist/services/WorkflowService.d.ts +305 -0
- package/dist/services/WorkflowService.d.ts.map +1 -0
- package/dist/services/WorkflowService.js +1811 -0
- package/dist/services/WorkflowService.js.map +1 -0
- package/dist/templates/logo/logo.png +0 -0
- package/dist/templates/mails/default.liquid +23 -0
- package/dist/types/aggregation.d.ts +40 -0
- package/dist/types/aggregation.d.ts.map +1 -0
- package/dist/types/aggregation.js +6 -0
- package/dist/types/aggregation.js.map +1 -0
- package/dist/types/assets.d.ts +32 -0
- package/dist/types/assets.d.ts.map +1 -0
- package/dist/types/assets.js +6 -0
- package/dist/types/assets.js.map +1 -0
- package/dist/types/auth.d.ts +50 -0
- package/dist/types/auth.d.ts.map +1 -0
- package/dist/types/auth.js +6 -0
- package/dist/types/auth.js.map +1 -0
- package/dist/types/cache.d.ts +47 -0
- package/dist/types/cache.d.ts.map +1 -0
- package/dist/types/cache.js +6 -0
- package/dist/types/cache.js.map +1 -0
- package/dist/types/database.d.ts +16 -0
- package/dist/types/database.d.ts.map +1 -0
- package/dist/types/database.js +6 -0
- package/dist/types/database.js.map +1 -0
- package/dist/types/fields.d.ts +71 -0
- package/dist/types/fields.d.ts.map +1 -0
- package/dist/types/fields.js +6 -0
- package/dist/types/fields.js.map +1 -0
- package/dist/types/files.d.ts +33 -0
- package/dist/types/files.d.ts.map +1 -0
- package/dist/types/files.js +6 -0
- package/dist/types/files.js.map +1 -0
- package/dist/types/hooks.d.ts +29 -0
- package/dist/types/hooks.d.ts.map +1 -0
- package/dist/types/hooks.js +6 -0
- package/dist/types/hooks.js.map +1 -0
- package/dist/types/import-export.d.ts +62 -0
- package/dist/types/import-export.d.ts.map +1 -0
- package/dist/types/import-export.js +6 -0
- package/dist/types/import-export.js.map +1 -0
- package/dist/types/index.d.ts +31 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +58 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/mail.d.ts +34 -0
- package/dist/types/mail.d.ts.map +1 -0
- package/dist/types/mail.js +6 -0
- package/dist/types/mail.js.map +1 -0
- package/dist/types/notifications.d.ts +16 -0
- package/dist/types/notifications.d.ts.map +1 -0
- package/dist/types/notifications.js +6 -0
- package/dist/types/notifications.js.map +1 -0
- package/dist/types/plugin.d.ts +351 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/plugin.js +8 -0
- package/dist/types/plugin.js.map +1 -0
- package/dist/types/query.d.ts +71 -0
- package/dist/types/query.d.ts.map +1 -0
- package/dist/types/query.js +6 -0
- package/dist/types/query.js.map +1 -0
- package/dist/types/relations.d.ts +111 -0
- package/dist/types/relations.d.ts.map +1 -0
- package/dist/types/relations.js +6 -0
- package/dist/types/relations.js.map +1 -0
- package/dist/types/reports.d.ts +17 -0
- package/dist/types/reports.d.ts.map +1 -0
- package/dist/types/reports.js +6 -0
- package/dist/types/reports.js.map +1 -0
- package/dist/types/schema.d.ts +26 -0
- package/dist/types/schema.d.ts.map +1 -0
- package/dist/types/schema.js +6 -0
- package/dist/types/schema.js.map +1 -0
- package/dist/types/seed.d.ts +27 -0
- package/dist/types/seed.d.ts.map +1 -0
- package/dist/types/seed.js +6 -0
- package/dist/types/seed.js.map +1 -0
- package/dist/types/services.d.ts +68 -0
- package/dist/types/services.d.ts.map +1 -0
- package/dist/types/services.js +6 -0
- package/dist/types/services.js.map +1 -0
- package/dist/types/settings.d.ts +36 -0
- package/dist/types/settings.d.ts.map +1 -0
- package/dist/types/settings.js +6 -0
- package/dist/types/settings.js.map +1 -0
- package/dist/types/sockets.d.ts +26 -0
- package/dist/types/sockets.d.ts.map +1 -0
- package/dist/types/sockets.js +6 -0
- package/dist/types/sockets.js.map +1 -0
- package/dist/types/sort.d.ts +25 -0
- package/dist/types/sort.d.ts.map +1 -0
- package/dist/types/sort.js +6 -0
- package/dist/types/sort.js.map +1 -0
- package/dist/types/spatial.d.ts +19 -0
- package/dist/types/spatial.d.ts.map +1 -0
- package/dist/types/spatial.js +6 -0
- package/dist/types/spatial.js.map +1 -0
- package/dist/types/stats.d.ts +21 -0
- package/dist/types/stats.d.ts.map +1 -0
- package/dist/types/stats.js +6 -0
- package/dist/types/stats.js.map +1 -0
- package/dist/types/storage.d.ts +19 -0
- package/dist/types/storage.d.ts.map +1 -0
- package/dist/types/storage.js +6 -0
- package/dist/types/storage.js.map +1 -0
- package/dist/types/tasks.d.ts +14 -0
- package/dist/types/tasks.d.ts.map +1 -0
- package/dist/types/tasks.js +6 -0
- package/dist/types/tasks.js.map +1 -0
- package/dist/types/utils.d.ts +54 -0
- package/dist/types/utils.d.ts.map +1 -0
- package/dist/types/utils.js +6 -0
- package/dist/types/utils.js.map +1 -0
- package/dist/types/workflow.d.ts +17 -0
- package/dist/types/workflow.d.ts.map +1 -0
- package/dist/types/workflow.js +6 -0
- package/dist/types/workflow.js.map +1 -0
- package/dist/utils/aggregationUtils.d.ts +192 -0
- package/dist/utils/aggregationUtils.d.ts.map +1 -0
- package/dist/utils/aggregationUtils.js +450 -0
- package/dist/utils/aggregationUtils.js.map +1 -0
- package/dist/utils/auth.d.ts +93 -0
- package/dist/utils/auth.d.ts.map +1 -0
- package/dist/utils/auth.js +557 -0
- package/dist/utils/auth.js.map +1 -0
- package/dist/utils/cache.d.ts +64 -0
- package/dist/utils/cache.d.ts.map +1 -0
- package/dist/utils/cache.js +464 -0
- package/dist/utils/cache.js.map +1 -0
- package/dist/utils/common.d.ts +53 -0
- package/dist/utils/common.d.ts.map +1 -0
- package/dist/utils/common.js +162 -0
- package/dist/utils/common.js.map +1 -0
- package/dist/utils/db.d.ts +101 -0
- package/dist/utils/db.d.ts.map +1 -0
- package/dist/utils/db.js +413 -0
- package/dist/utils/db.js.map +1 -0
- package/dist/utils/dirname.d.ts +30 -0
- package/dist/utils/dirname.d.ts.map +1 -0
- package/dist/utils/dirname.js +95 -0
- package/dist/utils/dirname.js.map +1 -0
- package/dist/utils/dynamicVariableResolver.d.ts +17 -0
- package/dist/utils/dynamicVariableResolver.d.ts.map +1 -0
- package/dist/utils/dynamicVariableResolver.js +262 -0
- package/dist/utils/dynamicVariableResolver.js.map +1 -0
- package/dist/utils/env.d.ts +38 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/env.js +80 -0
- package/dist/utils/env.js.map +1 -0
- package/dist/utils/errorHandler.d.ts +14 -0
- package/dist/utils/errorHandler.d.ts.map +1 -0
- package/dist/utils/errorHandler.js +79 -0
- package/dist/utils/errorHandler.js.map +1 -0
- package/dist/utils/fieldExpansion.d.ts +30 -0
- package/dist/utils/fieldExpansion.d.ts.map +1 -0
- package/dist/utils/fieldExpansion.js +145 -0
- package/dist/utils/fieldExpansion.js.map +1 -0
- package/dist/utils/fieldUtils.d.ts +179 -0
- package/dist/utils/fieldUtils.d.ts.map +1 -0
- package/dist/utils/fieldUtils.js +424 -0
- package/dist/utils/fieldUtils.js.map +1 -0
- package/dist/utils/filterOperators.d.ts +472 -0
- package/dist/utils/filterOperators.d.ts.map +1 -0
- package/dist/utils/filterOperators.js +1229 -0
- package/dist/utils/filterOperators.js.map +1 -0
- package/dist/utils/importUtils.d.ts +127 -0
- package/dist/utils/importUtils.d.ts.map +1 -0
- package/dist/utils/importUtils.js +437 -0
- package/dist/utils/importUtils.js.map +1 -0
- package/dist/utils/index.d.ts +75 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +101 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +41 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +217 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/orderUtils.d.ts +117 -0
- package/dist/utils/orderUtils.d.ts.map +1 -0
- package/dist/utils/orderUtils.js +249 -0
- package/dist/utils/orderUtils.js.map +1 -0
- package/dist/utils/queryBuilder.d.ts +118 -0
- package/dist/utils/queryBuilder.d.ts.map +1 -0
- package/dist/utils/queryBuilder.js +489 -0
- package/dist/utils/queryBuilder.js.map +1 -0
- package/dist/utils/relationLoader.d.ts +65 -0
- package/dist/utils/relationLoader.d.ts.map +1 -0
- package/dist/utils/relationLoader.js +1081 -0
- package/dist/utils/relationLoader.js.map +1 -0
- package/dist/utils/relationPathResolver.d.ts +30 -0
- package/dist/utils/relationPathResolver.d.ts.map +1 -0
- package/dist/utils/relationPathResolver.js +173 -0
- package/dist/utils/relationPathResolver.js.map +1 -0
- package/dist/utils/relationUtils.d.ts +139 -0
- package/dist/utils/relationUtils.d.ts.map +1 -0
- package/dist/utils/relationUtils.js +711 -0
- package/dist/utils/relationUtils.js.map +1 -0
- package/dist/utils/router.d.ts +6 -0
- package/dist/utils/router.d.ts.map +1 -0
- package/dist/utils/router.js +95 -0
- package/dist/utils/router.js.map +1 -0
- package/dist/utils/schema.d.ts +88 -0
- package/dist/utils/schema.d.ts.map +1 -0
- package/dist/utils/schema.js +24 -0
- package/dist/utils/schema.js.map +1 -0
- package/dist/utils/schemaManager.d.ts +238 -0
- package/dist/utils/schemaManager.d.ts.map +1 -0
- package/dist/utils/schemaManager.js +1992 -0
- package/dist/utils/schemaManager.js.map +1 -0
- package/dist/utils/schemaValidator.d.ts +83 -0
- package/dist/utils/schemaValidator.d.ts.map +1 -0
- package/dist/utils/schemaValidator.js +491 -0
- package/dist/utils/schemaValidator.js.map +1 -0
- package/dist/utils/seed.d.ts +45 -0
- package/dist/utils/seed.d.ts.map +1 -0
- package/dist/utils/seed.js +248 -0
- package/dist/utils/seed.js.map +1 -0
- package/dist/utils/sessionCleanup.d.ts +10 -0
- package/dist/utils/sessionCleanup.d.ts.map +1 -0
- package/dist/utils/sessionCleanup.js +49 -0
- package/dist/utils/sessionCleanup.js.map +1 -0
- package/dist/utils/sortUtils.d.ts +117 -0
- package/dist/utils/sortUtils.d.ts.map +1 -0
- package/dist/utils/sortUtils.js +232 -0
- package/dist/utils/sortUtils.js.map +1 -0
- package/dist/utils/spatialUtils.d.ts +244 -0
- package/dist/utils/spatialUtils.d.ts.map +1 -0
- package/dist/utils/spatialUtils.js +359 -0
- package/dist/utils/spatialUtils.js.map +1 -0
- package/dist/utils/systemschema.d.ts +11040 -0
- package/dist/utils/systemschema.d.ts.map +1 -0
- package/dist/utils/systemschema.js +1777 -0
- package/dist/utils/systemschema.js.map +1 -0
- package/dist/utils/tenantUtils.d.ts +34 -0
- package/dist/utils/tenantUtils.d.ts.map +1 -0
- package/dist/utils/tenantUtils.js +124 -0
- package/dist/utils/tenantUtils.js.map +1 -0
- package/dist/utils/typeMapper.d.ts +25 -0
- package/dist/utils/typeMapper.d.ts.map +1 -0
- package/dist/utils/typeMapper.js +282 -0
- package/dist/utils/typeMapper.js.map +1 -0
- package/dist/utils/valueValidator.d.ts +60 -0
- package/dist/utils/valueValidator.d.ts.map +1 -0
- package/dist/utils/valueValidator.js +303 -0
- package/dist/utils/valueValidator.js.map +1 -0
- package/dist/utils/workflow.d.ts +87 -0
- package/dist/utils/workflow.d.ts.map +1 -0
- package/dist/utils/workflow.js +205 -0
- package/dist/utils/workflow.js.map +1 -0
- package/package.json +115 -0
|
@@ -0,0 +1,1081 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relation Loader Module for Drizzle ORM
|
|
3
|
+
*
|
|
4
|
+
* This module provides utilities for building joins and loading related data
|
|
5
|
+
* in Drizzle ORM, matching Sequelize's include/association behavior.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - BelongsTo join building
|
|
9
|
+
* - HasOne join building
|
|
10
|
+
* - HasMany separate query loading
|
|
11
|
+
* - BelongsToMany (M2M) through junction tables
|
|
12
|
+
* - M2A polymorphic relations
|
|
13
|
+
* - Nested relation loading (include.include)
|
|
14
|
+
* - Relation filtering and ordering
|
|
15
|
+
*/
|
|
16
|
+
import { sql, and, isNull } from 'drizzle-orm';
|
|
17
|
+
import { alias } from 'drizzle-orm/pg-core';
|
|
18
|
+
import { schemaManager } from './schemaManager.js';
|
|
19
|
+
import { relationBuilder } from './relationUtils.js';
|
|
20
|
+
import { drizzleWhere } from './queryBuilder.js';
|
|
21
|
+
/**
|
|
22
|
+
* Extract field paths and build include tree
|
|
23
|
+
*
|
|
24
|
+
* @param fields - Array of field paths (e.g., ['id', 'name', 'author.name', 'author.posts.title'])
|
|
25
|
+
* @param tableName - Main table name
|
|
26
|
+
* @returns Expanded fields result with includes
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const fields = ['id', 'title', 'author.name', 'author.posts.title'];
|
|
31
|
+
* const result = expandFieldsWithIncludes(fields, 'posts');
|
|
32
|
+
*
|
|
33
|
+
* // result.directFields: ['id', 'title']
|
|
34
|
+
* // result.includes: [
|
|
35
|
+
* // {
|
|
36
|
+
* // relation: 'author',
|
|
37
|
+
* // nested: [{ relation: 'posts', ... }]
|
|
38
|
+
* // }
|
|
39
|
+
* // ]
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
/**
|
|
43
|
+
* Expand nested wildcards recursively up to maxDepth levels
|
|
44
|
+
* Example: "Review.*.*" expands to include all Review fields and all nested relations
|
|
45
|
+
*
|
|
46
|
+
* @param relationName - The relation to expand (e.g., "Review")
|
|
47
|
+
* @param tableName - The source table name
|
|
48
|
+
* @param wildcardCount - Number of wildcards after relation (e.g., 2 for "Review.*.*")
|
|
49
|
+
* @param maxDepth - Maximum depth to expand (default 7)
|
|
50
|
+
* @param currentDepth - Current recursion depth (internal use)
|
|
51
|
+
* @returns IncludeConfig with expanded relations
|
|
52
|
+
*/
|
|
53
|
+
function expandNestedWildcards(relationName, tableName, wildcardCount, maxDepth = 7, currentDepth = 0) {
|
|
54
|
+
// Stop if we've reached max depth
|
|
55
|
+
if (currentDepth >= maxDepth || wildcardCount === 0) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
// Get associations for the source table
|
|
59
|
+
const associations = relationBuilder.getAssociations(tableName);
|
|
60
|
+
if (!associations) {
|
|
61
|
+
console.warn(`No associations found for table '${tableName}'`);
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
const association = associations[relationName];
|
|
65
|
+
if (!association) {
|
|
66
|
+
console.warn(`Association '${relationName}' not found on ${tableName}`);
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
// Start building the include config
|
|
70
|
+
const config = {
|
|
71
|
+
relation: relationName,
|
|
72
|
+
attributes: ['*'], // First wildcard means all fields
|
|
73
|
+
include: []
|
|
74
|
+
};
|
|
75
|
+
// If we have more wildcards, expand nested relations
|
|
76
|
+
if (wildcardCount > 1) {
|
|
77
|
+
const targetTable = association.model;
|
|
78
|
+
const nestedAssociations = relationBuilder.getAssociations(targetTable);
|
|
79
|
+
if (nestedAssociations) {
|
|
80
|
+
// For each nested relation, recursively expand
|
|
81
|
+
for (const [nestedRelationName, nestedAssoc] of Object.entries(nestedAssociations)) {
|
|
82
|
+
const nestedConfig = expandNestedWildcards(nestedRelationName, targetTable, wildcardCount - 1, maxDepth, currentDepth + 1);
|
|
83
|
+
if (nestedConfig) {
|
|
84
|
+
config.include.push(nestedConfig);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return config;
|
|
90
|
+
}
|
|
91
|
+
export function expandFieldsWithIncludes(fields, tableName) {
|
|
92
|
+
const directFields = [];
|
|
93
|
+
const relationMap = new Map();
|
|
94
|
+
for (const field of fields) {
|
|
95
|
+
if (field === '*') {
|
|
96
|
+
// Wildcard - get all direct fields
|
|
97
|
+
const schema = schemaManager.getSchema(tableName);
|
|
98
|
+
if (schema) {
|
|
99
|
+
const allFields = Object.keys(schema);
|
|
100
|
+
directFields.push(...allFields);
|
|
101
|
+
}
|
|
102
|
+
continue;
|
|
103
|
+
}
|
|
104
|
+
if (!field.includes('.')) {
|
|
105
|
+
// Direct field
|
|
106
|
+
if (!directFields.includes(field)) {
|
|
107
|
+
directFields.push(field);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Relational field (e.g., 'author.name' or 'author.posts.title')
|
|
112
|
+
const parts = field.split('.');
|
|
113
|
+
const relationName = parts[0];
|
|
114
|
+
const remainingPath = parts.slice(1).join('.');
|
|
115
|
+
// Check if this is a nested wildcard pattern (e.g., "Review.*.*")
|
|
116
|
+
const remainingParts = parts.slice(1);
|
|
117
|
+
const wildcardCount = remainingParts.filter(p => p === '*').length;
|
|
118
|
+
const isNestedWildcard = wildcardCount > 0 && remainingParts.every(p => p === '*');
|
|
119
|
+
if (isNestedWildcard && wildcardCount >= 2) {
|
|
120
|
+
// Handle nested wildcard expansion
|
|
121
|
+
const expandedConfig = expandNestedWildcards(relationName, tableName, wildcardCount);
|
|
122
|
+
if (expandedConfig) {
|
|
123
|
+
if (relationMap.has(relationName)) {
|
|
124
|
+
// Merge with existing config
|
|
125
|
+
const existing = relationMap.get(relationName);
|
|
126
|
+
if (!existing.attributes.includes('*')) {
|
|
127
|
+
existing.attributes.push('*');
|
|
128
|
+
}
|
|
129
|
+
// Merge includes
|
|
130
|
+
for (const nestedInc of expandedConfig.include) {
|
|
131
|
+
const existingNested = existing.include.find(inc => inc.relation === nestedInc.relation);
|
|
132
|
+
if (!existingNested) {
|
|
133
|
+
existing.include.push(nestedInc);
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
// Merge nested includes recursively
|
|
137
|
+
mergeIncludeConfigs(existingNested, nestedInc);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
relationMap.set(relationName, expandedConfig);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
if (!relationMap.has(relationName)) {
|
|
148
|
+
relationMap.set(relationName, {
|
|
149
|
+
relation: relationName,
|
|
150
|
+
attributes: [],
|
|
151
|
+
include: []
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
const includeConfig = relationMap.get(relationName);
|
|
155
|
+
if (parts.length === 2) {
|
|
156
|
+
// Direct field on related table (e.g., 'author.name' or 'author.*')
|
|
157
|
+
if (remainingPath === '*') {
|
|
158
|
+
// Wildcard - mark for expansion later
|
|
159
|
+
if (!includeConfig.attributes.includes('*')) {
|
|
160
|
+
includeConfig.attributes.push('*');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
else if (!includeConfig.attributes.includes(remainingPath)) {
|
|
164
|
+
includeConfig.attributes.push(remainingPath);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
// Nested relation (e.g., 'author.posts.title' or 'employee.department.company.*')
|
|
169
|
+
// Recursively build nested include structure
|
|
170
|
+
buildNestedInclude(includeConfig, parts.slice(1));
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// Process includes
|
|
175
|
+
const includes = [];
|
|
176
|
+
const relationPaths = new Map();
|
|
177
|
+
for (const [relationName, config] of relationMap) {
|
|
178
|
+
const processed = processIncludeConfig(config, tableName);
|
|
179
|
+
if (processed) {
|
|
180
|
+
includes.push(processed);
|
|
181
|
+
relationPaths.set(relationName, processed);
|
|
182
|
+
// Add nested relation paths
|
|
183
|
+
for (const nested of processed.nested) {
|
|
184
|
+
relationPaths.set(`${relationName}.${nested.relation}`, nested);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return {
|
|
189
|
+
directFields,
|
|
190
|
+
includes,
|
|
191
|
+
relationPaths
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Recursively build nested include structure for deep relation paths
|
|
196
|
+
* E.g., for parts = ['department', 'company', '*'], creates:
|
|
197
|
+
* { relation: 'department', attributes: [], include: [
|
|
198
|
+
* { relation: 'company', attributes: ['*'], include: [] }
|
|
199
|
+
* ]}
|
|
200
|
+
*/
|
|
201
|
+
function buildNestedInclude(config, parts) {
|
|
202
|
+
if (parts.length === 0)
|
|
203
|
+
return;
|
|
204
|
+
if (parts.length === 1) {
|
|
205
|
+
// This is a field/attribute, add it to the current config
|
|
206
|
+
if (!config.attributes.includes(parts[0])) {
|
|
207
|
+
config.attributes.push(parts[0]);
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// This is a nested relation
|
|
212
|
+
const nestedRelation = parts[0];
|
|
213
|
+
let nestedInclude = config.include.find(inc => inc.relation === nestedRelation);
|
|
214
|
+
if (!nestedInclude) {
|
|
215
|
+
nestedInclude = {
|
|
216
|
+
relation: nestedRelation,
|
|
217
|
+
attributes: [],
|
|
218
|
+
include: []
|
|
219
|
+
};
|
|
220
|
+
config.include.push(nestedInclude);
|
|
221
|
+
}
|
|
222
|
+
// Recursively build the rest of the path
|
|
223
|
+
buildNestedInclude(nestedInclude, parts.slice(1));
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Merge two IncludeConfig objects recursively
|
|
227
|
+
*/
|
|
228
|
+
function mergeIncludeConfigs(target, source) {
|
|
229
|
+
// Merge attributes
|
|
230
|
+
for (const attr of source.attributes || []) {
|
|
231
|
+
if (!target.attributes.includes(attr)) {
|
|
232
|
+
target.attributes.push(attr);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Merge includes
|
|
236
|
+
for (const sourceNested of source.include || []) {
|
|
237
|
+
const targetNested = target.include.find(inc => inc.relation === sourceNested.relation);
|
|
238
|
+
if (!targetNested) {
|
|
239
|
+
target.include.push(sourceNested);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
mergeIncludeConfigs(targetNested, sourceNested);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Process include configuration into ProcessedInclude
|
|
248
|
+
*/
|
|
249
|
+
function processIncludeConfig(config, sourceTableName, parentPath = '', sourceAlias // Alias of the source table (for nested relations)
|
|
250
|
+
) {
|
|
251
|
+
// Get association info
|
|
252
|
+
const associations = relationBuilder.getAssociations(sourceTableName);
|
|
253
|
+
if (!associations) {
|
|
254
|
+
console.warn(`No associations found for table '${sourceTableName}'`);
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const association = associations[config.relation];
|
|
258
|
+
if (!association) {
|
|
259
|
+
console.warn(`Association '${config.relation}' not found on ${sourceTableName}`);
|
|
260
|
+
return null;
|
|
261
|
+
}
|
|
262
|
+
// Get target table
|
|
263
|
+
const targetTable = schemaManager.getSchema(association.model);
|
|
264
|
+
if (!targetTable) {
|
|
265
|
+
console.warn(`Target table '${association.model}' not found`);
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
// Expand wildcard in attributes
|
|
269
|
+
let expandedAttributes = config.attributes || [];
|
|
270
|
+
if (expandedAttributes.includes('*')) {
|
|
271
|
+
// Remove the wildcard and add all actual fields
|
|
272
|
+
expandedAttributes = expandedAttributes.filter(attr => attr !== '*');
|
|
273
|
+
const allFields = Object.keys(targetTable);
|
|
274
|
+
expandedAttributes.push(...allFields);
|
|
275
|
+
}
|
|
276
|
+
else if (expandedAttributes.length === 0 && config.include && config.include.length > 0) {
|
|
277
|
+
// If no attributes specified but has nested includes, automatically include all fields
|
|
278
|
+
// This ensures junction tables in M2M relations can load nested data properly
|
|
279
|
+
// e.g., "tags.tags.name" should load junction table fields even without "tags.*"
|
|
280
|
+
const allFields = Object.keys(targetTable);
|
|
281
|
+
expandedAttributes.push(...allFields);
|
|
282
|
+
}
|
|
283
|
+
// Always ensure 'id' is included in attributes for relational queries
|
|
284
|
+
// This prevents SQL errors when joining/grouping by related records
|
|
285
|
+
if (expandedAttributes.length > 0 && !expandedAttributes.includes('id') && targetTable['id']) {
|
|
286
|
+
expandedAttributes.unshift('id');
|
|
287
|
+
}
|
|
288
|
+
// Create a unique alias for the target table
|
|
289
|
+
// For nested relations with same name (e.g., M2M junction.target where both named "chapters"),
|
|
290
|
+
// we need to include the parent path to avoid alias conflicts
|
|
291
|
+
// E.g., "chapters" vs "chapters__chapters" for nested same-name relations
|
|
292
|
+
const uniqueAlias = parentPath ? `${parentPath.replace(/\./g, '__')}__${config.relation}` : config.relation;
|
|
293
|
+
// Create an alias for the target table using the unique alias
|
|
294
|
+
// This allows joining the same table multiple times with different aliases
|
|
295
|
+
const aliasedTable = alias(targetTable, uniqueAlias);
|
|
296
|
+
// Build join condition based on relation type
|
|
297
|
+
// Use sourceAlias (if provided) for JOIN SQL, otherwise use sourceTableName
|
|
298
|
+
// This is critical for nested relations where the parent is aliased
|
|
299
|
+
const joinCondition = buildJoinCondition(association, sourceAlias || sourceTableName, uniqueAlias);
|
|
300
|
+
// Process where clause
|
|
301
|
+
// Use unique alias as tableName for proper alias resolution
|
|
302
|
+
let whereClause;
|
|
303
|
+
if (config.where) {
|
|
304
|
+
whereClause = drizzleWhere(config.where, {
|
|
305
|
+
tableName: uniqueAlias,
|
|
306
|
+
schema: aliasedTable
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
// Process nested includes
|
|
310
|
+
const nested = [];
|
|
311
|
+
if (config.include && config.include.length > 0) {
|
|
312
|
+
for (const nestedConfig of config.include) {
|
|
313
|
+
const processedNested = processIncludeConfig(nestedConfig, association.model, parentPath ? `${parentPath}.${config.relation}` : config.relation, uniqueAlias // Pass unique alias as the source alias for nested includes
|
|
314
|
+
);
|
|
315
|
+
if (processedNested) {
|
|
316
|
+
nested.push(processedNested);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
// Determine if separate query needed (for HasMany and BelongsToMany)
|
|
321
|
+
// Both relation types require separate queries since they can't be efficiently joined
|
|
322
|
+
const separate = config.separate !== undefined
|
|
323
|
+
? config.separate
|
|
324
|
+
: association.type === 'HasMany' || association.type === 'BelongsToMany';
|
|
325
|
+
return {
|
|
326
|
+
relation: config.relation,
|
|
327
|
+
relationType: association.type,
|
|
328
|
+
table: aliasedTable,
|
|
329
|
+
joinCondition,
|
|
330
|
+
where: whereClause,
|
|
331
|
+
attributes: expandedAttributes,
|
|
332
|
+
nested,
|
|
333
|
+
required: config.required || false,
|
|
334
|
+
separate,
|
|
335
|
+
alias: uniqueAlias // Store the unique alias for use in queries
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Build join condition for an association
|
|
340
|
+
*/
|
|
341
|
+
function buildJoinCondition(association, sourceTableName, relationName = '') {
|
|
342
|
+
const { type, foreignKey, model } = association;
|
|
343
|
+
const targetKey = association.targetKey || 'id';
|
|
344
|
+
// Use relationName as the table alias in JOIN conditions
|
|
345
|
+
// This allows joining the same table multiple times with different aliases
|
|
346
|
+
const targetTableName = relationName || model;
|
|
347
|
+
switch (type) {
|
|
348
|
+
case 'BelongsTo':
|
|
349
|
+
// Source.foreignKey = Target.targetKey (usually id)
|
|
350
|
+
return sql `${sql.raw(`"${sourceTableName}"."${foreignKey}"`)} = ${sql.raw(`"${targetTableName}"."${targetKey}"`)}`;
|
|
351
|
+
case 'HasOne':
|
|
352
|
+
case 'HasMany':
|
|
353
|
+
// Target.foreignKey = Source.id
|
|
354
|
+
return sql `${sql.raw(`"${targetTableName}"."${foreignKey}"`)} = ${sql.raw(`"${sourceTableName}"."id"`)}`;
|
|
355
|
+
case 'BelongsToMany':
|
|
356
|
+
// Requires junction table - handle separately
|
|
357
|
+
const { through, otherKey } = association;
|
|
358
|
+
if (through) {
|
|
359
|
+
// Source.id = Junction.foreignKey
|
|
360
|
+
// Junction.otherKey = Target.id
|
|
361
|
+
return sql `${sql.raw(`"${sourceTableName}"."id"`)} = ${sql.raw(`"${through}"."${foreignKey}"`)} AND ${sql.raw(`"${through}"."${otherKey}"`)} = ${sql.raw(`"${targetTableName}"."id"`)}`;
|
|
362
|
+
}
|
|
363
|
+
throw new Error(`BelongsToMany association '${relationName}' missing through table`);
|
|
364
|
+
case 'M2A':
|
|
365
|
+
// Polymorphic - Target.id = Source.foreignKey AND Target.type = Source.typeKey
|
|
366
|
+
const typeKey = association.typeKey || `${relationName}Type`;
|
|
367
|
+
const typeValue = association.typeValue || model;
|
|
368
|
+
return and(sql `${sql.raw(`"${targetTableName}"."id"`)} = ${sql.raw(`"${sourceTableName}"."${foreignKey}"`)}`, sql `${sql.raw(`"${sourceTableName}"."${typeKey}"`)} = ${typeValue}`) || sql `1=1`;
|
|
369
|
+
default:
|
|
370
|
+
throw new Error(`Unknown relation type: ${type}`);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Build LEFT JOIN clause for includes
|
|
375
|
+
* Only for BelongsTo and HasOne relations
|
|
376
|
+
* HasMany will use separate queries
|
|
377
|
+
*/
|
|
378
|
+
export function buildJoinsForIncludes(includes) {
|
|
379
|
+
const joins = [];
|
|
380
|
+
for (const include of includes) {
|
|
381
|
+
if (include.separate) {
|
|
382
|
+
// Skip - will be loaded separately
|
|
383
|
+
continue;
|
|
384
|
+
}
|
|
385
|
+
if (include.relationType === 'BelongsTo' || include.relationType === 'HasOne') {
|
|
386
|
+
// Build LEFT JOIN or INNER JOIN
|
|
387
|
+
const joinType = include.required ? 'INNER JOIN' : 'LEFT JOIN';
|
|
388
|
+
const tableName = include.table;
|
|
389
|
+
let joinClause = sql `${sql.raw(joinType)} ${sql.raw(String(tableName))} ON ${include.joinCondition}`;
|
|
390
|
+
// Add WHERE conditions to join
|
|
391
|
+
if (include.where) {
|
|
392
|
+
joinClause = sql `${joinClause} AND ${include.where}`;
|
|
393
|
+
}
|
|
394
|
+
joins.push(joinClause);
|
|
395
|
+
// Process nested joins
|
|
396
|
+
if (include.nested.length > 0) {
|
|
397
|
+
const nestedJoins = buildJoinsForIncludes(include.nested);
|
|
398
|
+
joins.push(...nestedJoins);
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return joins;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Load HasMany relations with separate queries
|
|
406
|
+
* This is more efficient than joining for one-to-many relations
|
|
407
|
+
*/
|
|
408
|
+
export async function loadHasManyRelations(db, mainRecords, includes, sourceTableName) {
|
|
409
|
+
if (!mainRecords || mainRecords.length === 0) {
|
|
410
|
+
return mainRecords;
|
|
411
|
+
}
|
|
412
|
+
for (const include of includes) {
|
|
413
|
+
// If this include has nested HasMany relations, we need to load them even if this include itself is not separate
|
|
414
|
+
// Example: customer_products → product (BelongsTo) → product_contracts (HasMany)
|
|
415
|
+
if ((!include.separate || include.relationType !== 'HasMany') && include.nested.length > 0) {
|
|
416
|
+
// Check if there are nested HasMany relations
|
|
417
|
+
const hasNestedSeparate = include.nested.some(n => n.separate && n.relationType === 'HasMany');
|
|
418
|
+
if (hasNestedSeparate) {
|
|
419
|
+
// For BelongsTo/HasOne relations, the nested records are already loaded and attached
|
|
420
|
+
// We need to collect those nested records and call loadNestedRelationsForHasMany
|
|
421
|
+
const nestedRecords = [];
|
|
422
|
+
for (const mainRecord of mainRecords) {
|
|
423
|
+
const nestedRecord = mainRecord[include.relation];
|
|
424
|
+
if (nestedRecord) {
|
|
425
|
+
// For BelongsTo/HasOne, it's a single record
|
|
426
|
+
nestedRecords.push(nestedRecord);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (nestedRecords.length > 0) {
|
|
430
|
+
// Get the target table name for this include
|
|
431
|
+
const associationsMap = relationBuilder.getAssociations(sourceTableName);
|
|
432
|
+
const association = associationsMap?.[include.relation];
|
|
433
|
+
if (association) {
|
|
434
|
+
// Recursively load HasMany relations for these nested records
|
|
435
|
+
await loadNestedRelationsForHasMany(db, nestedRecords, include.nested, association.model);
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (!include.separate || include.relationType !== 'HasMany') {
|
|
442
|
+
continue;
|
|
443
|
+
}
|
|
444
|
+
// Get association info
|
|
445
|
+
const associationsMap = relationBuilder.getAssociations(sourceTableName);
|
|
446
|
+
if (!associationsMap)
|
|
447
|
+
continue;
|
|
448
|
+
const association = associationsMap[include.relation];
|
|
449
|
+
if (!association)
|
|
450
|
+
continue;
|
|
451
|
+
// Skip polymorphic (M2A) relations - they're handled by loadM2ARelations
|
|
452
|
+
if (association.polymorphic) {
|
|
453
|
+
console.log(`[loadHasManyRelations] Skipping polymorphic relation: ${include.relation}`);
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
// Get primary key for source table (usually 'id' but could be different)
|
|
457
|
+
const sourceTable = schemaManager.getSchema(sourceTableName);
|
|
458
|
+
const sourcePrimaryKey = schemaManager.getPrimaryKey(sourceTableName) || 'id';
|
|
459
|
+
// Get IDs from main records using the correct primary key
|
|
460
|
+
const mainIds = mainRecords.map(r => r[sourcePrimaryKey]).filter(Boolean);
|
|
461
|
+
if (mainIds.length === 0)
|
|
462
|
+
continue;
|
|
463
|
+
// Build query for related records
|
|
464
|
+
const targetTable = include.table;
|
|
465
|
+
const foreignKey = association.foreignKey || `${sourceTableName}Id`;
|
|
466
|
+
// Build where clause: foreignKey IN (mainIds) AND optional filters
|
|
467
|
+
let whereConditions = [
|
|
468
|
+
sql `${sql.raw(`"${foreignKey}"`)} IN (${sql.join(mainIds.map(id => sql `${id}`), sql `, `)})`
|
|
469
|
+
];
|
|
470
|
+
// Handle where filter
|
|
471
|
+
// If it's a FilterObject, rebuild it using the actual target table
|
|
472
|
+
// If it's SQL, it was built with an aliased table, so we can't use it directly - skip it
|
|
473
|
+
if (include.where) {
|
|
474
|
+
// Check if it's a FilterObject (has properties like AND, OR, field names)
|
|
475
|
+
// vs SQL object (has queryChunks property)
|
|
476
|
+
const isFilterObject = !('queryChunks' in include.where);
|
|
477
|
+
if (isFilterObject) {
|
|
478
|
+
// It's a FilterObject from relConditions - build SQL for target table
|
|
479
|
+
// Use include.alias if available for proper table alias reference in SQL
|
|
480
|
+
const filterJoins = [];
|
|
481
|
+
const rebuiltWhere = drizzleWhere(include.where, {
|
|
482
|
+
table: targetTable,
|
|
483
|
+
tableName: include.alias || association.model,
|
|
484
|
+
schema: targetTable,
|
|
485
|
+
joins: filterJoins
|
|
486
|
+
});
|
|
487
|
+
if (rebuiltWhere) {
|
|
488
|
+
whereConditions.push(rebuiltWhere);
|
|
489
|
+
}
|
|
490
|
+
// If the filter created joins, we need to apply them to the query
|
|
491
|
+
// For now, log a warning as this is an edge case
|
|
492
|
+
if (filterJoins.length > 0) {
|
|
493
|
+
console.warn('[loadHasManyRelations] Relation filters with nested paths not fully supported yet');
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
// It's a SQL object built with an aliased table - we can't use it in a separate query
|
|
498
|
+
// Log a warning and skip it
|
|
499
|
+
console.warn('[loadHasManyRelations] SQL where filters built with aliases are not supported in separate queries');
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
// Check if target table has soft deletes (paranoid mode)
|
|
503
|
+
const isParanoid = schemaManager.isParanoid(association.model);
|
|
504
|
+
if (isParanoid) {
|
|
505
|
+
const deletedAtColumn = targetTable['deletedAt'];
|
|
506
|
+
if (deletedAtColumn) {
|
|
507
|
+
whereConditions.push(isNull(deletedAtColumn));
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const whereClause = whereConditions.length > 1
|
|
511
|
+
? and(...whereConditions)
|
|
512
|
+
: whereConditions[0];
|
|
513
|
+
// Build select columns - respect include.attributes
|
|
514
|
+
let selectColumns = {};
|
|
515
|
+
if (include.attributes && include.attributes.length > 0) {
|
|
516
|
+
// Select only specified attributes
|
|
517
|
+
for (const attr of include.attributes) {
|
|
518
|
+
const column = targetTable[attr];
|
|
519
|
+
if (column) {
|
|
520
|
+
selectColumns[attr] = column;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
// Always include the foreign key so we can group by it
|
|
525
|
+
if (!selectColumns[foreignKey] && targetTable[foreignKey]) {
|
|
526
|
+
selectColumns[foreignKey] = targetTable[foreignKey];
|
|
527
|
+
}
|
|
528
|
+
// Always include 'id' for nested relations and proper record matching
|
|
529
|
+
if (!selectColumns['id'] && targetTable['id']) {
|
|
530
|
+
selectColumns['id'] = targetTable['id'];
|
|
531
|
+
}
|
|
532
|
+
// Execute query with specific columns or all columns
|
|
533
|
+
const relatedRecords = await db
|
|
534
|
+
.select(Object.keys(selectColumns).length > 0 ? selectColumns : undefined)
|
|
535
|
+
.from(targetTable)
|
|
536
|
+
.where(whereClause);
|
|
537
|
+
// Group by foreign key
|
|
538
|
+
const groupedRecords = new Map();
|
|
539
|
+
for (const record of relatedRecords) {
|
|
540
|
+
const fkValue = record[foreignKey];
|
|
541
|
+
if (!groupedRecords.has(fkValue)) {
|
|
542
|
+
groupedRecords.set(fkValue, []);
|
|
543
|
+
}
|
|
544
|
+
groupedRecords.get(fkValue).push(record);
|
|
545
|
+
}
|
|
546
|
+
// Attach to main records using correct primary key
|
|
547
|
+
for (const mainRecord of mainRecords) {
|
|
548
|
+
mainRecord[include.relation] = groupedRecords.get(mainRecord[sourcePrimaryKey]) || [];
|
|
549
|
+
}
|
|
550
|
+
// Load nested relations for the HasMany records
|
|
551
|
+
if (include.nested && include.nested.length > 0) {
|
|
552
|
+
await loadNestedRelationsForHasMany(db, relatedRecords, include.nested, association.model);
|
|
553
|
+
// Update grouped records with nested data using correct primary key
|
|
554
|
+
for (const mainRecord of mainRecords) {
|
|
555
|
+
const recordsForThis = groupedRecords.get(mainRecord[sourcePrimaryKey]) || [];
|
|
556
|
+
mainRecord[include.relation] = recordsForThis;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return mainRecords;
|
|
561
|
+
}
|
|
562
|
+
/**
|
|
563
|
+
* Load nested relations for HasMany records
|
|
564
|
+
* This handles nested includes within HasMany relations (e.g., userRoles.role)
|
|
565
|
+
*/
|
|
566
|
+
async function loadNestedRelationsForHasMany(db, records, includes, sourceTableName) {
|
|
567
|
+
if (!records || records.length === 0) {
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
for (const include of includes) {
|
|
571
|
+
// For BelongsTo and HasOne, we can load them directly
|
|
572
|
+
if (include.relationType === 'BelongsTo' || include.relationType === 'HasOne') {
|
|
573
|
+
// Get association info
|
|
574
|
+
const associationsMap = relationBuilder.getAssociations(sourceTableName);
|
|
575
|
+
if (!associationsMap)
|
|
576
|
+
continue;
|
|
577
|
+
const association = associationsMap[include.relation];
|
|
578
|
+
if (!association)
|
|
579
|
+
continue;
|
|
580
|
+
// Get foreign key values from records
|
|
581
|
+
const foreignKey = association.foreignKey;
|
|
582
|
+
const targetKey = association.targetKey || 'id';
|
|
583
|
+
const foreignKeyValues = records
|
|
584
|
+
.map(r => r[foreignKey])
|
|
585
|
+
.filter(v => v != null);
|
|
586
|
+
if (foreignKeyValues.length === 0)
|
|
587
|
+
continue;
|
|
588
|
+
// Load related records
|
|
589
|
+
const targetTable = include.table;
|
|
590
|
+
let whereConditions = [
|
|
591
|
+
sql `${sql.raw(`"${targetKey}"`)} IN (${sql.join([...new Set(foreignKeyValues)].map(id => sql `${id}`), sql `, `)})`
|
|
592
|
+
];
|
|
593
|
+
// Handle where filter - check if it's a FilterObject or SQL
|
|
594
|
+
if (include.where) {
|
|
595
|
+
const isFilterObject = !('queryChunks' in include.where);
|
|
596
|
+
if (isFilterObject) {
|
|
597
|
+
// It's a FilterObject from relConditions - build SQL for target table
|
|
598
|
+
// Use include.alias if available for proper table alias reference in SQL
|
|
599
|
+
const filterJoins = [];
|
|
600
|
+
const rebuiltWhere = drizzleWhere(include.where, {
|
|
601
|
+
table: targetTable,
|
|
602
|
+
tableName: include.alias || association.model,
|
|
603
|
+
schema: targetTable,
|
|
604
|
+
joins: filterJoins
|
|
605
|
+
});
|
|
606
|
+
if (rebuiltWhere) {
|
|
607
|
+
whereConditions.push(rebuiltWhere);
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
else {
|
|
611
|
+
// It's already SQL
|
|
612
|
+
whereConditions.push(include.where);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
const whereClause = whereConditions.length > 1
|
|
616
|
+
? and(...whereConditions)
|
|
617
|
+
: whereConditions[0];
|
|
618
|
+
// Build select columns - respect include.attributes
|
|
619
|
+
let selectColumns = {};
|
|
620
|
+
if (include.attributes && include.attributes.length > 0) {
|
|
621
|
+
// Select only specified attributes
|
|
622
|
+
for (const attr of include.attributes) {
|
|
623
|
+
const column = targetTable[attr];
|
|
624
|
+
if (column) {
|
|
625
|
+
selectColumns[attr] = column;
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
// Always include the targetKey so we can match records
|
|
630
|
+
if (!selectColumns[targetKey] && targetTable[targetKey]) {
|
|
631
|
+
selectColumns[targetKey] = targetTable[targetKey];
|
|
632
|
+
}
|
|
633
|
+
// Always include 'id' for nested relations and proper record matching
|
|
634
|
+
if (!selectColumns['id'] && targetTable['id']) {
|
|
635
|
+
selectColumns['id'] = targetTable['id'];
|
|
636
|
+
}
|
|
637
|
+
// Execute query with specific columns or all columns
|
|
638
|
+
const relatedRecords = await db
|
|
639
|
+
.select(Object.keys(selectColumns).length > 0 ? selectColumns : undefined)
|
|
640
|
+
.from(targetTable)
|
|
641
|
+
.where(whereClause);
|
|
642
|
+
// Create a map of related records by target key
|
|
643
|
+
const relatedMap = new Map(relatedRecords.map((r) => [r[targetKey], r]));
|
|
644
|
+
// Attach to records
|
|
645
|
+
for (const record of records) {
|
|
646
|
+
const fkValue = record[foreignKey];
|
|
647
|
+
if (fkValue != null) {
|
|
648
|
+
if (include.relationType === 'BelongsTo') {
|
|
649
|
+
record[include.relation] = relatedMap.get(fkValue) || null;
|
|
650
|
+
}
|
|
651
|
+
else {
|
|
652
|
+
// HasOne
|
|
653
|
+
record[include.relation] = relatedMap.get(fkValue) || null;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
else {
|
|
657
|
+
record[include.relation] = null;
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
// Recursively load nested includes
|
|
661
|
+
if (include.nested && include.nested.length > 0 && relatedRecords.length > 0) {
|
|
662
|
+
await loadNestedRelationsForHasMany(db, relatedRecords, include.nested, association.model);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
else if (include.relationType === 'HasMany') {
|
|
666
|
+
// Check if this is a polymorphic (M2A) relation
|
|
667
|
+
const associationsMap = relationBuilder.getAssociations(sourceTableName);
|
|
668
|
+
const association = associationsMap?.[include.relation];
|
|
669
|
+
if (association?.polymorphic) {
|
|
670
|
+
// Handle M2A relations
|
|
671
|
+
await loadM2ARelations(db, records, [include], sourceTableName);
|
|
672
|
+
}
|
|
673
|
+
else {
|
|
674
|
+
// Recursively handle nested HasMany (non-polymorphic)
|
|
675
|
+
await loadHasManyRelations(db, records, [include], sourceTableName);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Load M2A (polymorphic) relations with junction table
|
|
682
|
+
*/
|
|
683
|
+
export async function loadM2ARelations(db, mainRecords, includes, sourceTableName) {
|
|
684
|
+
if (!mainRecords || mainRecords.length === 0) {
|
|
685
|
+
return mainRecords;
|
|
686
|
+
}
|
|
687
|
+
for (const include of includes) {
|
|
688
|
+
// Only process polymorphic HasMany relations (M2A)
|
|
689
|
+
if (!include.separate || include.relationType !== 'HasMany') {
|
|
690
|
+
continue;
|
|
691
|
+
}
|
|
692
|
+
// Get association info
|
|
693
|
+
const associationsMap = relationBuilder.getAssociations(sourceTableName);
|
|
694
|
+
if (!associationsMap)
|
|
695
|
+
continue;
|
|
696
|
+
const association = associationsMap[include.relation];
|
|
697
|
+
if (!association || !association.polymorphic)
|
|
698
|
+
continue; // Only handle polymorphic
|
|
699
|
+
console.log(`[loadM2ARelations] Loading M2A relation: ${include.relation}`);
|
|
700
|
+
// Get junction table (target is the junction table for M2A)
|
|
701
|
+
const junctionTable = schemaManager.getSchema(association.model);
|
|
702
|
+
if (!junctionTable) {
|
|
703
|
+
console.warn(`Junction table '${association.model}' not found for M2A relation`);
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
// Get primary key for source table
|
|
707
|
+
const sourcePrimaryKey = schemaManager.getPrimaryKey(sourceTableName) || 'id';
|
|
708
|
+
const mainIds = mainRecords.map(r => r[sourcePrimaryKey]).filter(Boolean);
|
|
709
|
+
if (mainIds.length === 0)
|
|
710
|
+
continue;
|
|
711
|
+
const foreignKey = association.foreignKey || `${sourceTableName}_id`;
|
|
712
|
+
// Load junction records
|
|
713
|
+
const junctionRecords = await db
|
|
714
|
+
.select()
|
|
715
|
+
.from(junctionTable)
|
|
716
|
+
.where(sql `${sql.raw(`"${foreignKey}"`)} IN (${sql.join(mainIds.map(id => sql `${id}`), sql `, `)})`);
|
|
717
|
+
console.log(`[loadM2ARelations] Loaded ${junctionRecords.length} junction records`);
|
|
718
|
+
if (junctionRecords.length === 0) {
|
|
719
|
+
// No related records - set empty arrays
|
|
720
|
+
for (const mainRecord of mainRecords) {
|
|
721
|
+
mainRecord[include.relation] = [];
|
|
722
|
+
}
|
|
723
|
+
continue;
|
|
724
|
+
}
|
|
725
|
+
// Group junction records by collection type
|
|
726
|
+
const recordsByCollection = new Map();
|
|
727
|
+
for (const junctionRecord of junctionRecords) {
|
|
728
|
+
const collectionName = junctionRecord.collection;
|
|
729
|
+
if (!recordsByCollection.has(collectionName)) {
|
|
730
|
+
recordsByCollection.set(collectionName, []);
|
|
731
|
+
}
|
|
732
|
+
recordsByCollection.get(collectionName).push(junctionRecord);
|
|
733
|
+
}
|
|
734
|
+
console.log(`[loadM2ARelations] Collections found:`, Array.from(recordsByCollection.keys()));
|
|
735
|
+
// Determine which collections to load based on nested includes
|
|
736
|
+
// If nested includes are specified, only load those collections
|
|
737
|
+
// If no nested includes, load all collections found in junction records
|
|
738
|
+
let collectionsToLoad;
|
|
739
|
+
console.log(`[loadM2ARelations] include.nested:`, include.nested?.map(n => ({ relation: n.relation, attributes: n.attributes })));
|
|
740
|
+
if (include.nested && include.nested.length > 0) {
|
|
741
|
+
// Extract collection names from nested includes
|
|
742
|
+
collectionsToLoad = include.nested.map(n => n.relation);
|
|
743
|
+
console.log(`[loadM2ARelations] Loading only requested collections:`, collectionsToLoad);
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
// Load all collections found
|
|
747
|
+
collectionsToLoad = Array.from(recordsByCollection.keys());
|
|
748
|
+
console.log(`[loadM2ARelations] Loading all collections found in junction records`);
|
|
749
|
+
}
|
|
750
|
+
// For each collection, load the target records
|
|
751
|
+
for (const collectionName of collectionsToLoad) {
|
|
752
|
+
const junctionRecs = recordsByCollection.get(collectionName);
|
|
753
|
+
if (!junctionRecs || junctionRecs.length === 0) {
|
|
754
|
+
console.log(`[loadM2ARelations] No junction records found for collection ${collectionName}`);
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
const targetTable = schemaManager.getSchema(collectionName);
|
|
758
|
+
if (!targetTable) {
|
|
759
|
+
console.warn(`Target table '${collectionName}' not found for M2A relation`);
|
|
760
|
+
continue;
|
|
761
|
+
}
|
|
762
|
+
const targetIds = junctionRecs.map(r => r.item_id).filter(Boolean);
|
|
763
|
+
if (targetIds.length === 0)
|
|
764
|
+
continue;
|
|
765
|
+
// Query target table
|
|
766
|
+
const targetRecords = await db
|
|
767
|
+
.select()
|
|
768
|
+
.from(targetTable)
|
|
769
|
+
.where(sql `"id" IN (${sql.join(targetIds.map(id => sql `${id}`), sql `, `)})`);
|
|
770
|
+
console.log(`[loadM2ARelations] Loaded ${targetRecords.length} records from ${collectionName}`);
|
|
771
|
+
// Create map of target records by ID
|
|
772
|
+
const targetMap = new Map(targetRecords.map((r) => [r.id, r]));
|
|
773
|
+
// Attach target records to junction records
|
|
774
|
+
for (const junctionRec of junctionRecs) {
|
|
775
|
+
const targetRecord = targetMap.get(junctionRec.item_id);
|
|
776
|
+
if (targetRecord) {
|
|
777
|
+
// Attach as nested object with collection name as key
|
|
778
|
+
junctionRec[collectionName] = targetRecord;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
}
|
|
782
|
+
// Filter junction records to only include those with loaded collections
|
|
783
|
+
// This prevents returning junction records for collections we didn't load
|
|
784
|
+
// IMPORTANT: Do this BEFORE processing nested includes
|
|
785
|
+
const filteredJunctionRecords = junctionRecords.filter(jr => {
|
|
786
|
+
const hasLoadedCollection = collectionsToLoad.includes(jr.collection);
|
|
787
|
+
if (!hasLoadedCollection) {
|
|
788
|
+
console.log(`[loadM2ARelations] Filtering out junction record for unloaded collection: ${jr.collection}`);
|
|
789
|
+
}
|
|
790
|
+
return hasLoadedCollection;
|
|
791
|
+
});
|
|
792
|
+
console.log(`[loadM2ARelations] Filtered junction records count: ${filteredJunctionRecords.length} (from ${junctionRecords.length})`);
|
|
793
|
+
// Process nested includes on filtered junction records if any
|
|
794
|
+
if (include.nested && include.nested.length > 0) {
|
|
795
|
+
await loadNestedRelationsForHasMany(db, filteredJunctionRecords, // Use filtered records
|
|
796
|
+
include.nested, association.model);
|
|
797
|
+
}
|
|
798
|
+
// Group junction records by source ID
|
|
799
|
+
const groupedRecords = new Map();
|
|
800
|
+
for (const junctionRecord of filteredJunctionRecords) {
|
|
801
|
+
const sourceId = junctionRecord[foreignKey];
|
|
802
|
+
if (!groupedRecords.has(sourceId)) {
|
|
803
|
+
groupedRecords.set(sourceId, []);
|
|
804
|
+
}
|
|
805
|
+
groupedRecords.get(sourceId).push(junctionRecord);
|
|
806
|
+
}
|
|
807
|
+
// Attach to main records
|
|
808
|
+
for (const mainRecord of mainRecords) {
|
|
809
|
+
mainRecord[include.relation] = groupedRecords.get(mainRecord[sourcePrimaryKey]) || [];
|
|
810
|
+
}
|
|
811
|
+
console.log(`[loadM2ARelations] Completed M2A relation: ${include.relation}`);
|
|
812
|
+
}
|
|
813
|
+
return mainRecords;
|
|
814
|
+
}
|
|
815
|
+
/**
|
|
816
|
+
* Load BelongsToMany (M2M) relations with junction table
|
|
817
|
+
*/
|
|
818
|
+
export async function loadBelongsToManyRelations(db, mainRecords, includes, sourceTableName) {
|
|
819
|
+
if (!mainRecords || mainRecords.length === 0) {
|
|
820
|
+
return mainRecords;
|
|
821
|
+
}
|
|
822
|
+
for (const include of includes) {
|
|
823
|
+
if (include.relationType !== 'BelongsToMany') {
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
// Get association info
|
|
827
|
+
const associationsMap = relationBuilder.getAssociations(sourceTableName);
|
|
828
|
+
if (!associationsMap)
|
|
829
|
+
continue;
|
|
830
|
+
const association = associationsMap[include.relation];
|
|
831
|
+
if (!association || !association.through)
|
|
832
|
+
continue;
|
|
833
|
+
// Get primary key for source table
|
|
834
|
+
const sourcePrimaryKey = schemaManager.getPrimaryKey(sourceTableName) || 'id';
|
|
835
|
+
// Get IDs from main records using correct primary key
|
|
836
|
+
const mainIds = mainRecords.map(r => r[sourcePrimaryKey]).filter(Boolean);
|
|
837
|
+
if (mainIds.length === 0)
|
|
838
|
+
continue;
|
|
839
|
+
// Get junction and target tables
|
|
840
|
+
const junctionTable = schemaManager.getSchema(association.through);
|
|
841
|
+
const targetTable = include.table;
|
|
842
|
+
if (!junctionTable) {
|
|
843
|
+
console.warn(`Junction table '${association.through}' not found`);
|
|
844
|
+
continue;
|
|
845
|
+
}
|
|
846
|
+
const foreignKey = association.foreignKey || `${sourceTableName}Id`; // Key in junction pointing to source
|
|
847
|
+
const otherKey = association.otherKey || `${association.model}Id`; // Key in junction pointing to target
|
|
848
|
+
// Query junction table
|
|
849
|
+
const junctionRecords = await db
|
|
850
|
+
.select()
|
|
851
|
+
.from(junctionTable)
|
|
852
|
+
.where(sql `${sql.raw(`"${foreignKey}"`)} IN (${sql.join(mainIds.map(id => sql `${id}`), sql `, `)})`);
|
|
853
|
+
// Get target IDs from junction
|
|
854
|
+
const targetIds = [...new Set(junctionRecords.map((r) => r[otherKey]))];
|
|
855
|
+
if (targetIds.length === 0) {
|
|
856
|
+
// No related records - set empty arrays
|
|
857
|
+
for (const mainRecord of mainRecords) {
|
|
858
|
+
mainRecord[include.relation] = [];
|
|
859
|
+
}
|
|
860
|
+
continue;
|
|
861
|
+
}
|
|
862
|
+
// Query target table
|
|
863
|
+
let whereConditions = [
|
|
864
|
+
sql `"id" IN (${sql.join(targetIds.map(id => sql `${id}`), sql `, `)})`
|
|
865
|
+
];
|
|
866
|
+
if (include.where) {
|
|
867
|
+
whereConditions.push(include.where);
|
|
868
|
+
}
|
|
869
|
+
const whereClause = whereConditions.length > 1
|
|
870
|
+
? and(...whereConditions)
|
|
871
|
+
: whereConditions[0];
|
|
872
|
+
// Build select columns - respect include.attributes
|
|
873
|
+
let selectColumns = {};
|
|
874
|
+
if (include.attributes && include.attributes.length > 0) {
|
|
875
|
+
// Select only specified attributes
|
|
876
|
+
for (const attr of include.attributes) {
|
|
877
|
+
const column = targetTable[attr];
|
|
878
|
+
if (column) {
|
|
879
|
+
selectColumns[attr] = column;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
// Always include 'id' so we can map records
|
|
884
|
+
if (!selectColumns.id && targetTable.id) {
|
|
885
|
+
selectColumns.id = targetTable.id;
|
|
886
|
+
}
|
|
887
|
+
const targetRecords = await db
|
|
888
|
+
.select(Object.keys(selectColumns).length > 0 ? selectColumns : undefined)
|
|
889
|
+
.from(targetTable)
|
|
890
|
+
.where(whereClause);
|
|
891
|
+
// Create target records map
|
|
892
|
+
const targetMap = new Map(targetRecords.map((r) => [r.id, r]));
|
|
893
|
+
// Group by source ID
|
|
894
|
+
const groupedRecords = new Map();
|
|
895
|
+
for (const junctionRecord of junctionRecords) {
|
|
896
|
+
const sourceId = junctionRecord[foreignKey];
|
|
897
|
+
const targetId = junctionRecord[otherKey];
|
|
898
|
+
const targetRecord = targetMap.get(targetId);
|
|
899
|
+
if (targetRecord) {
|
|
900
|
+
if (!groupedRecords.has(sourceId)) {
|
|
901
|
+
groupedRecords.set(sourceId, []);
|
|
902
|
+
}
|
|
903
|
+
groupedRecords.get(sourceId).push(targetRecord);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
// Attach to main records using correct primary key
|
|
907
|
+
for (const mainRecord of mainRecords) {
|
|
908
|
+
mainRecord[include.relation] = groupedRecords.get(mainRecord[sourcePrimaryKey]) || [];
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
return mainRecords;
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Load all separate-query relations (HasMany, BelongsToMany)
|
|
915
|
+
*/
|
|
916
|
+
/**
|
|
917
|
+
* Nest flattened BelongsTo/HasOne relation data from JOINs
|
|
918
|
+
* Converts { "category.name": "Electronics", "category.id": "123" }
|
|
919
|
+
* to { category: { name: "Electronics", id: "123" } }
|
|
920
|
+
*/
|
|
921
|
+
export function nestJoinedRelations(records, includes, parentPath = '') {
|
|
922
|
+
if (!records || records.length === 0)
|
|
923
|
+
return records;
|
|
924
|
+
return records.map(record => {
|
|
925
|
+
const nested = { ...record };
|
|
926
|
+
// Process each include
|
|
927
|
+
for (const include of includes) {
|
|
928
|
+
// Skip separate queries (HasMany, BelongsToMany) - they're handled elsewhere
|
|
929
|
+
if (include.separate)
|
|
930
|
+
continue;
|
|
931
|
+
// Only nest BelongsTo and HasOne relations
|
|
932
|
+
if (include.relationType === 'BelongsTo' || include.relationType === 'HasOne') {
|
|
933
|
+
const relationName = include.relation;
|
|
934
|
+
const fullPath = parentPath ? `${parentPath}.${relationName}` : relationName;
|
|
935
|
+
const relationData = {};
|
|
936
|
+
let hasData = false;
|
|
937
|
+
let hasNonNullValue = false;
|
|
938
|
+
// Extract all fields for this relation from the full path
|
|
939
|
+
for (const key in record) {
|
|
940
|
+
if (key.startsWith(`${fullPath}.`)) {
|
|
941
|
+
const fieldName = key.substring(fullPath.length + 1);
|
|
942
|
+
relationData[fieldName] = record[key];
|
|
943
|
+
hasData = true;
|
|
944
|
+
// Check if at least one value is non-null
|
|
945
|
+
if (record[key] !== null && record[key] !== undefined) {
|
|
946
|
+
hasNonNullValue = true;
|
|
947
|
+
}
|
|
948
|
+
// Remove the flattened field
|
|
949
|
+
delete nested[key];
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
// Only add the nested object if it has data with at least one non-null value
|
|
953
|
+
// If all values are null, the foreign key was null, so set the relation to null
|
|
954
|
+
if (hasData) {
|
|
955
|
+
nested[relationName] = hasNonNullValue ? relationData : null;
|
|
956
|
+
}
|
|
957
|
+
// Recursively nest nested includes
|
|
958
|
+
// Note: We pass empty parentPath because the nested object's keys don't include the parent prefix
|
|
959
|
+
if (include.nested && include.nested.length > 0 && nested[relationName]) {
|
|
960
|
+
const nestedArray = [nested[relationName]];
|
|
961
|
+
const result = nestJoinedRelations(nestedArray, include.nested, '');
|
|
962
|
+
nested[relationName] = result[0];
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
return nested;
|
|
967
|
+
});
|
|
968
|
+
}
|
|
969
|
+
export async function loadSeparateRelations(db, mainRecords, includes, sourceTableName) {
|
|
970
|
+
// First, nest the joined BelongsTo/HasOne relations
|
|
971
|
+
const nestedRecords = nestJoinedRelations(mainRecords, includes);
|
|
972
|
+
// Load M2A (polymorphic) relations
|
|
973
|
+
await loadM2ARelations(db, nestedRecords, includes, sourceTableName);
|
|
974
|
+
// Then load HasMany relations
|
|
975
|
+
await loadHasManyRelations(db, nestedRecords, includes, sourceTableName);
|
|
976
|
+
// Load BelongsToMany relations
|
|
977
|
+
await loadBelongsToManyRelations(db, nestedRecords, includes, sourceTableName);
|
|
978
|
+
return nestedRecords;
|
|
979
|
+
}
|
|
980
|
+
/**
|
|
981
|
+
* Build complete select with joins
|
|
982
|
+
* Returns SELECT columns and JOIN clauses
|
|
983
|
+
*/
|
|
984
|
+
export function buildSelectWithJoins(mainTable, directFields, includes) {
|
|
985
|
+
const selectColumns = {};
|
|
986
|
+
// Extract table name from PgTable object
|
|
987
|
+
// Drizzle stores table name in [Table.Symbol.Name] property
|
|
988
|
+
const mainTableName = mainTable[Symbol.for('drizzle:Name')] || mainTable._.name || 'unknown';
|
|
989
|
+
// Add direct fields from main table using actual column references
|
|
990
|
+
// This is required by Drizzle's select() method
|
|
991
|
+
for (const field of directFields) {
|
|
992
|
+
// Use the actual column from the table schema
|
|
993
|
+
const column = mainTable[field];
|
|
994
|
+
if (column) {
|
|
995
|
+
selectColumns[field] = column;
|
|
996
|
+
}
|
|
997
|
+
else {
|
|
998
|
+
console.warn(`[buildSelectWithJoins] Column '${field}' not found in table '${mainTableName}'`);
|
|
999
|
+
console.warn(`[buildSelectWithJoins] Available columns:`, Object.keys(mainTable).filter(k => !k.startsWith('_') && k !== Symbol.for('drizzle:Name')));
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
// Add fields from joined relations
|
|
1003
|
+
for (const include of includes) {
|
|
1004
|
+
if (include.separate)
|
|
1005
|
+
continue; // Skip separate query relations
|
|
1006
|
+
const relationAlias = include.relation;
|
|
1007
|
+
const relationTable = include.table;
|
|
1008
|
+
for (const attr of include.attributes) {
|
|
1009
|
+
const columnKey = `${relationAlias}.${attr}`;
|
|
1010
|
+
// Use actual column from the relation table
|
|
1011
|
+
const column = relationTable[attr];
|
|
1012
|
+
if (column) {
|
|
1013
|
+
selectColumns[columnKey] = column;
|
|
1014
|
+
}
|
|
1015
|
+
else {
|
|
1016
|
+
console.warn(`Column '${attr}' not found in relation table '${relationAlias}'`);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
// Process nested includes
|
|
1020
|
+
if (include.nested.length > 0) {
|
|
1021
|
+
addNestedSelectColumns(selectColumns, include.nested, relationAlias);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
// Build join clauses
|
|
1025
|
+
const joins = buildJoinsForIncludes(includes);
|
|
1026
|
+
return { selectColumns, joins };
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* Add nested relation columns to select
|
|
1030
|
+
*/
|
|
1031
|
+
function addNestedSelectColumns(selectColumns, includes, parentPath = '') {
|
|
1032
|
+
for (const include of includes) {
|
|
1033
|
+
if (include.separate)
|
|
1034
|
+
continue;
|
|
1035
|
+
const relationAlias = include.relation;
|
|
1036
|
+
const relationTable = include.table;
|
|
1037
|
+
const fullPath = parentPath ? `${parentPath}.${relationAlias}` : relationAlias;
|
|
1038
|
+
for (const attr of include.attributes) {
|
|
1039
|
+
const columnKey = `${fullPath}.${attr}`;
|
|
1040
|
+
// Use actual column from the relation table
|
|
1041
|
+
const column = relationTable[attr];
|
|
1042
|
+
if (column) {
|
|
1043
|
+
selectColumns[columnKey] = column;
|
|
1044
|
+
}
|
|
1045
|
+
else {
|
|
1046
|
+
console.warn(`Column '${attr}' not found in nested relation table '${relationAlias}'`);
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (include.nested.length > 0) {
|
|
1050
|
+
addNestedSelectColumns(selectColumns, include.nested, fullPath);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
/**
|
|
1055
|
+
* Utility: Check if any include requires separate query
|
|
1056
|
+
*/
|
|
1057
|
+
export function hasSeparateQueries(includes) {
|
|
1058
|
+
for (const include of includes) {
|
|
1059
|
+
if (include.separate)
|
|
1060
|
+
return true;
|
|
1061
|
+
if (include.nested.length > 0 && hasSeparateQueries(include.nested)) {
|
|
1062
|
+
return true;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
return false;
|
|
1066
|
+
}
|
|
1067
|
+
/**
|
|
1068
|
+
* Utility: Get all relation names from includes (flat list)
|
|
1069
|
+
*/
|
|
1070
|
+
export function getRelationNames(includes) {
|
|
1071
|
+
const names = [];
|
|
1072
|
+
for (const include of includes) {
|
|
1073
|
+
names.push(include.relation);
|
|
1074
|
+
if (include.nested.length > 0) {
|
|
1075
|
+
const nestedNames = getRelationNames(include.nested);
|
|
1076
|
+
names.push(...nestedNames.map(n => `${include.relation}.${n}`));
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
return names;
|
|
1080
|
+
}
|
|
1081
|
+
//# sourceMappingURL=relationLoader.js.map
|