nextly 0.0.1 → 0.0.2-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 (268) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +122 -0
  3. package/dist/_dts-chunks/collections-handler.d-DjgO74Wt.d.ts +20540 -0
  4. package/dist/_dts-chunks/config.d-DNwsDnjs.d.ts +2589 -0
  5. package/dist/_dts-chunks/define-component.d-BUgTHmt3.d.ts +1149 -0
  6. package/dist/_dts-chunks/image-processor.d-OO1PmMrv.d.ts +335 -0
  7. package/dist/_dts-chunks/index.d-axCAzZ7m.d.ts +17842 -0
  8. package/dist/_dts-chunks/media.d-DjDOZo4B.d.ts +117 -0
  9. package/dist/_dts-chunks/on-error.d-CHIKWNxd.d.ts +38 -0
  10. package/dist/_dts-chunks/storage.d-BUhQ2we_.d.ts +404 -0
  11. package/dist/actions/index.d.ts +239 -0
  12. package/dist/actions/index.mjs +281 -0
  13. package/dist/api/auth-state.d.ts +5 -0
  14. package/dist/api/auth-state.mjs +131 -0
  15. package/dist/api/collections-schema-detail.d.ts +56 -0
  16. package/dist/api/collections-schema-detail.mjs +244 -0
  17. package/dist/api/collections-schema-export.d.ts +56 -0
  18. package/dist/api/collections-schema-export.mjs +129 -0
  19. package/dist/api/collections-schema.d.ts +59 -0
  20. package/dist/api/collections-schema.mjs +207 -0
  21. package/dist/api/components-detail.d.ts +50 -0
  22. package/dist/api/components-detail.mjs +132 -0
  23. package/dist/api/components.d.ts +69 -0
  24. package/dist/api/components.mjs +144 -0
  25. package/dist/api/email-providers-default.d.ts +40 -0
  26. package/dist/api/email-providers-default.mjs +75 -0
  27. package/dist/api/email-providers-detail.d.ts +81 -0
  28. package/dist/api/email-providers-detail.mjs +109 -0
  29. package/dist/api/email-providers-test.d.ts +43 -0
  30. package/dist/api/email-providers-test.mjs +114 -0
  31. package/dist/api/email-providers.d.ts +69 -0
  32. package/dist/api/email-providers.mjs +110 -0
  33. package/dist/api/email-send-template.d.ts +41 -0
  34. package/dist/api/email-send-template.mjs +58 -0
  35. package/dist/api/email-send.d.ts +42 -0
  36. package/dist/api/email-send.mjs +58 -0
  37. package/dist/api/email-templates-detail.d.ts +74 -0
  38. package/dist/api/email-templates-detail.mjs +112 -0
  39. package/dist/api/email-templates-layout.d.ts +55 -0
  40. package/dist/api/email-templates-layout.mjs +92 -0
  41. package/dist/api/email-templates-preview.d.ts +48 -0
  42. package/dist/api/email-templates-preview.mjs +93 -0
  43. package/dist/api/email-templates.d.ts +61 -0
  44. package/dist/api/email-templates.mjs +118 -0
  45. package/dist/api/health.d.ts +68 -0
  46. package/dist/api/health.mjs +67 -0
  47. package/dist/api/index.d.ts +54 -0
  48. package/dist/api/index.mjs +16 -0
  49. package/dist/api/media-bulk.d.ts +74 -0
  50. package/dist/api/media-bulk.mjs +196 -0
  51. package/dist/api/media-folders.d.ts +112 -0
  52. package/dist/api/media-folders.mjs +187 -0
  53. package/dist/api/media-handlers.d.ts +102 -0
  54. package/dist/api/media-handlers.mjs +437 -0
  55. package/dist/api/media.d.ts +117 -0
  56. package/dist/api/media.mjs +242 -0
  57. package/dist/api/singles-detail.d.ts +87 -0
  58. package/dist/api/singles-detail.mjs +170 -0
  59. package/dist/api/singles-schema-detail.d.ts +54 -0
  60. package/dist/api/singles-schema-detail.mjs +182 -0
  61. package/dist/api/singles.d.ts +34 -0
  62. package/dist/api/singles.mjs +94 -0
  63. package/dist/api/storage-upload-url.d.ts +48 -0
  64. package/dist/api/storage-upload-url.mjs +202 -0
  65. package/dist/api/uploads.d.ts +109 -0
  66. package/dist/api/uploads.mjs +359 -0
  67. package/dist/auth/index.d.ts +425 -0
  68. package/dist/auth/index.mjs +199 -0
  69. package/dist/boot-apply-PQSYLDIN.mjs +7 -0
  70. package/dist/chunk-2OALJTK6.mjs +489 -0
  71. package/dist/chunk-2Q2SX2CS.mjs +365 -0
  72. package/dist/chunk-2TFX4ND3.mjs +13 -0
  73. package/dist/chunk-2TWPDSYD.mjs +87 -0
  74. package/dist/chunk-2W3DVD7S.mjs +647 -0
  75. package/dist/chunk-2ZFKXPQM.mjs +88 -0
  76. package/dist/chunk-3FA7FKAV.mjs +832 -0
  77. package/dist/chunk-3NZ2KMBL.mjs +58 -0
  78. package/dist/chunk-4MJLT6PZ.mjs +0 -0
  79. package/dist/chunk-56WO4WX7.mjs +0 -0
  80. package/dist/chunk-5APFUGAD.mjs +89 -0
  81. package/dist/chunk-5HMZ644B.mjs +108 -0
  82. package/dist/chunk-67GXH6PR.mjs +32 -0
  83. package/dist/chunk-6JNEPWRW.mjs +14368 -0
  84. package/dist/chunk-6NFHQIJD.mjs +45 -0
  85. package/dist/chunk-7P6ASYW6.mjs +9 -0
  86. package/dist/chunk-A3WPLSDT.mjs +1364 -0
  87. package/dist/chunk-AGJ6F2T3.mjs +144 -0
  88. package/dist/chunk-AK6Z23OX.mjs +1464 -0
  89. package/dist/chunk-APKKRD2G.mjs +102 -0
  90. package/dist/chunk-B2GV2BWH.mjs +73 -0
  91. package/dist/chunk-D5HQBNUB.mjs +74 -0
  92. package/dist/chunk-DNNG377Z.mjs +204 -0
  93. package/dist/chunk-DP3G27G5.mjs +135 -0
  94. package/dist/chunk-DV6WVX2Q.mjs +0 -0
  95. package/dist/chunk-DXGGXIUZ.mjs +57 -0
  96. package/dist/chunk-EGXBZCGC.mjs +943 -0
  97. package/dist/chunk-ERCNLX3V.mjs +176 -0
  98. package/dist/chunk-FQULBZ53.mjs +850 -0
  99. package/dist/chunk-G2AA4QLC.mjs +262 -0
  100. package/dist/chunk-GDBJ5JCU.mjs +488 -0
  101. package/dist/chunk-GJNSJU4S.mjs +19 -0
  102. package/dist/chunk-GZ6DCQKC.mjs +69 -0
  103. package/dist/chunk-H26B4FYG.mjs +167 -0
  104. package/dist/chunk-I4JMR3UR.mjs +21 -0
  105. package/dist/chunk-INV7QKLG.mjs +508 -0
  106. package/dist/chunk-IUDOC7N7.mjs +46 -0
  107. package/dist/chunk-IZWPRDC3.mjs +206 -0
  108. package/dist/chunk-KIMNCZGV.mjs +15 -0
  109. package/dist/chunk-L6HW2DA7.mjs +15 -0
  110. package/dist/chunk-LAZXX4HR.mjs +100 -0
  111. package/dist/chunk-LDKCUMHK.mjs +95 -0
  112. package/dist/chunk-LRXMECUA.mjs +0 -0
  113. package/dist/chunk-M52VMPGA.mjs +119 -0
  114. package/dist/chunk-MGUWEEI6.mjs +160 -0
  115. package/dist/chunk-NRUWQ5Z7.mjs +419 -0
  116. package/dist/chunk-NSEFNNU4.mjs +25360 -0
  117. package/dist/chunk-NTHVDFGO.mjs +138 -0
  118. package/dist/chunk-O3QHXMOX.mjs +3166 -0
  119. package/dist/chunk-P7NH2OSC.mjs +2605 -0
  120. package/dist/chunk-PKMABBB5.mjs +184 -0
  121. package/dist/chunk-PWS6XGJK.mjs +76 -0
  122. package/dist/chunk-R6JJQHFC.mjs +20 -0
  123. package/dist/chunk-RJLLGGPG.mjs +0 -0
  124. package/dist/chunk-SBACDPNX.mjs +689 -0
  125. package/dist/chunk-TO5AFLVQ.mjs +124 -0
  126. package/dist/chunk-TS7GHTG2.mjs +5436 -0
  127. package/dist/chunk-UJ2IMJ4W.mjs +133 -0
  128. package/dist/chunk-UOP63Q54.mjs +102 -0
  129. package/dist/chunk-UUOFWCM6.mjs +78 -0
  130. package/dist/chunk-V4EQTOA4.mjs +893 -0
  131. package/dist/chunk-VJ66NCL4.mjs +193 -0
  132. package/dist/chunk-VQJQHVEV.mjs +29 -0
  133. package/dist/chunk-VTJADRO3.mjs +141 -0
  134. package/dist/chunk-VWF3JO32.mjs +0 -0
  135. package/dist/chunk-W4MGXIRR.mjs +27 -0
  136. package/dist/chunk-W5KKPZT5.mjs +1204 -0
  137. package/dist/chunk-WD34YQ6T.mjs +381 -0
  138. package/dist/chunk-WZBYMYVW.mjs +14 -0
  139. package/dist/chunk-X23WKS3Z.mjs +50 -0
  140. package/dist/chunk-X7TXCYYN.mjs +6496 -0
  141. package/dist/chunk-XGI4EMS3.mjs +140 -0
  142. package/dist/chunk-XZKLBMN6.mjs +1153 -0
  143. package/dist/chunk-YB7INWPY.mjs +0 -0
  144. package/dist/chunk-YV4Y7SDL.mjs +83 -0
  145. package/dist/chunk-YZNBLFIW.mjs +1688 -0
  146. package/dist/chunk-YZZCTONM.mjs +263 -0
  147. package/dist/chunk-ZE6A3FYH.mjs +289 -0
  148. package/dist/cli/nextly.mjs +68 -0
  149. package/dist/cli/utils/index.d.ts +449 -0
  150. package/dist/cli/utils/index.mjs +49 -0
  151. package/dist/component-schema-service-5577KVW6.mjs +11 -0
  152. package/dist/config-loader-23YEMC3Z.mjs +23 -0
  153. package/dist/config.d.ts +44 -0
  154. package/dist/config.mjs +109 -0
  155. package/dist/container-ORGFGYSZ.mjs +9 -0
  156. package/dist/database/index.d.ts +12 -0
  157. package/dist/database/index.mjs +40 -0
  158. package/dist/database/seeders/index.d.ts +93 -0
  159. package/dist/database/seeders/index.mjs +47 -0
  160. package/dist/db-sync-demote-LJGKLB3S.mjs +117 -0
  161. package/dist/db-sync-promote-B26VSYQF.mjs +113 -0
  162. package/dist/dev-reload-broadcaster-B73IQ53V.mjs +25 -0
  163. package/dist/dist-M2NOU37V.mjs +19 -0
  164. package/dist/drizzle-kit-lazy-D2M2PXR2.mjs +13 -0
  165. package/dist/dynamic-collection-schema-service-IEXTPIZ7.mjs +8 -0
  166. package/dist/errors/index.d.ts +159 -0
  167. package/dist/errors/index.mjs +10 -0
  168. package/dist/factory-IWMBKUJM.mjs +15 -0
  169. package/dist/first-run-QIVKWJIF.mjs +63 -0
  170. package/dist/fresh-push-NR67DC3R.mjs +8 -0
  171. package/dist/index.d.ts +4175 -0
  172. package/dist/index.mjs +1336 -0
  173. package/dist/local-plugin-PTET4NAT.mjs +7 -0
  174. package/dist/logger-NU46DXNY.mjs +15 -0
  175. package/dist/logger-YE4TC7ZN.mjs +9 -0
  176. package/dist/migration-journal-EP532Y4L.mjs +139 -0
  177. package/dist/migrations/mysql/0000_eager_sentry.sql +174 -0
  178. package/dist/migrations/mysql/0001_soft_giant_girl.sql +27 -0
  179. package/dist/migrations/mysql/0002_media_table.sql +24 -0
  180. package/dist/migrations/mysql/0003_dynamic_singles.sql +37 -0
  181. package/dist/migrations/mysql/0004_dynamic_components.sql +35 -0
  182. package/dist/migrations/mysql/0005_user_management_tables.sql +92 -0
  183. package/dist/migrations/mysql/0006_api_keys.sql +36 -0
  184. package/dist/migrations/mysql/0007_general_settings.sql +20 -0
  185. package/dist/migrations/mysql/0008_site_settings_logo_url.sql +9 -0
  186. package/dist/migrations/mysql/0009_activity_log.sql +30 -0
  187. package/dist/migrations/mysql/0010_site_settings_sidebar.sql +13 -0
  188. package/dist/migrations/mysql/0011_missing_tables_and_columns.sql +54 -0
  189. package/dist/migrations/mysql/0012_image_sizes_and_focal_point.sql +30 -0
  190. package/dist/migrations/mysql/0012_media_folders.sql +43 -0
  191. package/dist/migrations/mysql/0013_user_brute_force_protection.sql +31 -0
  192. package/dist/migrations/mysql/0014_email_template_attachments.sql +12 -0
  193. package/dist/migrations/mysql/0015_media_uploaded_by_nullable.sql +15 -0
  194. package/dist/migrations/mysql/20260429_000000_000_initial_journal.sql +22 -0
  195. package/dist/migrations/mysql/20260501_000000_journal_batch.sql +17 -0
  196. package/dist/migrations/mysql/20260501_000001_audit_log.sql +24 -0
  197. package/dist/migrations/mysql/20260504_000000_nextly_meta.sql +21 -0
  198. package/dist/migrations/mysql/meta/0000_snapshot.json +1005 -0
  199. package/dist/migrations/mysql/meta/0001_snapshot.json +1099 -0
  200. package/dist/migrations/mysql/meta/_journal.json +41 -0
  201. package/dist/migrations/postgresql/0000_misty_king_bedlam.sql +169 -0
  202. package/dist/migrations/postgresql/0001_perpetual_captain_marvel.sql +8 -0
  203. package/dist/migrations/postgresql/0002_sad_spectrum.sql +16 -0
  204. package/dist/migrations/postgresql/0003_hesitant_ultron.sql +17 -0
  205. package/dist/migrations/postgresql/0004_media_table.sql +24 -0
  206. package/dist/migrations/postgresql/0005_media_folders.sql +36 -0
  207. package/dist/migrations/postgresql/0006_dynamic_collections_update.sql +50 -0
  208. package/dist/migrations/postgresql/0007_dynamic_singles.sql +38 -0
  209. package/dist/migrations/postgresql/0008_dynamic_components.sql +37 -0
  210. package/dist/migrations/postgresql/0009_user_management_tables.sql +95 -0
  211. package/dist/migrations/postgresql/0010_api_keys.sql +34 -0
  212. package/dist/migrations/postgresql/0011_general_settings.sql +20 -0
  213. package/dist/migrations/postgresql/0012_site_settings_logo_url.sql +9 -0
  214. package/dist/migrations/postgresql/0013_activity_log.sql +29 -0
  215. package/dist/migrations/postgresql/0014_image_sizes_and_focal_point.sql +33 -0
  216. package/dist/migrations/postgresql/0014_site_settings_sidebar.sql +13 -0
  217. package/dist/migrations/postgresql/0015_user_brute_force_protection.sql +29 -0
  218. package/dist/migrations/postgresql/0016_email_template_attachments.sql +12 -0
  219. package/dist/migrations/postgresql/0017_media_uploaded_by_nullable.sql +15 -0
  220. package/dist/migrations/postgresql/20260429_000000_000_initial_journal.sql +24 -0
  221. package/dist/migrations/postgresql/20260501_000000_journal_batch.sql +17 -0
  222. package/dist/migrations/postgresql/20260501_000001_audit_log.sql +24 -0
  223. package/dist/migrations/postgresql/20260504_000000_nextly_meta.sql +22 -0
  224. package/dist/migrations/postgresql/meta/0000_snapshot.json +1286 -0
  225. package/dist/migrations/postgresql/meta/0001_snapshot.json +1407 -0
  226. package/dist/migrations/postgresql/meta/0002_snapshot.json +1552 -0
  227. package/dist/migrations/postgresql/meta/0003_snapshot.json +1695 -0
  228. package/dist/migrations/postgresql/meta/0010_snapshot.json +2345 -0
  229. package/dist/migrations/postgresql/meta/_journal.json +90 -0
  230. package/dist/migrations/sqlite/0000_api_keys.sql +34 -0
  231. package/dist/migrations/sqlite/0001_general_settings.sql +20 -0
  232. package/dist/migrations/sqlite/0002_site_settings_logo_url.sql +9 -0
  233. package/dist/migrations/sqlite/0003_activity_log.sql +29 -0
  234. package/dist/migrations/sqlite/0004_image_sizes_and_focal_point.sql +29 -0
  235. package/dist/migrations/sqlite/0004_site_settings_sidebar.sql +11 -0
  236. package/dist/migrations/sqlite/0005_user_brute_force_protection.sql +29 -0
  237. package/dist/migrations/sqlite/0006_email_template_attachments.sql +12 -0
  238. package/dist/migrations/sqlite/0007_media_uploaded_by_nullable.sql +111 -0
  239. package/dist/migrations/sqlite/20260429_000000_000_initial_journal.sql +24 -0
  240. package/dist/migrations/sqlite/20260501_000000_journal_batch.sql +19 -0
  241. package/dist/migrations/sqlite/20260501_000001_audit_log.sql +24 -0
  242. package/dist/migrations/sqlite/20260504_000000_nextly_meta.sql +21 -0
  243. package/dist/migrations/sqlite/20260505_000000_user_management_tables.sql +77 -0
  244. package/dist/next.d.ts +57 -0
  245. package/dist/next.mjs +55 -0
  246. package/dist/observability/index.d.ts +87 -0
  247. package/dist/observability/index.mjs +57 -0
  248. package/dist/permissions-3DZZQZMI.mjs +39 -0
  249. package/dist/pipeline-YOML7SWF.mjs +29 -0
  250. package/dist/preview-ZZTR3QGS.mjs +9 -0
  251. package/dist/program-PW6UB2ZC.mjs +5934 -0
  252. package/dist/reconcile-single-tables-7ENVXJGB.mjs +7 -0
  253. package/dist/register-SF6E6FVU.mjs +49 -0
  254. package/dist/reload-config-HWQ4G5MM.mjs +23 -0
  255. package/dist/resolve-single-table-name-JSOMUB3R.mjs +7 -0
  256. package/dist/routeHandler-UNMMJIBM.mjs +77 -0
  257. package/dist/runtime-schema-generator-NRA6A6Z6.mjs +8 -0
  258. package/dist/runtime.d.ts +120 -0
  259. package/dist/runtime.mjs +73 -0
  260. package/dist/schema-hash-FMMG6VPJ.mjs +13 -0
  261. package/dist/schema-registry-EQ36FZDP.mjs +7 -0
  262. package/dist/scripts/load-env.mjs +42 -0
  263. package/dist/storage/index.d.ts +566 -0
  264. package/dist/storage/index.mjs +45 -0
  265. package/dist/super-admin-G5ZK5F4T.mjs +39 -0
  266. package/dist/system-table-service-WGSRVEGT.mjs +17 -0
  267. package/dist/users-7KELGRYJ.mjs +38 -0
  268. package/package.json +308 -9
@@ -0,0 +1,2605 @@
1
+ import {
2
+ reloadNextlyConfig,
3
+ resolveCollectionTableName
4
+ } from "./chunk-2OALJTK6.mjs";
5
+ import {
6
+ runBootTimeApplyIfDev
7
+ } from "./chunk-R6JJQHFC.mjs";
8
+ import {
9
+ buildClaims,
10
+ signAccessToken
11
+ } from "./chunk-X23WKS3Z.mjs";
12
+ import {
13
+ getService,
14
+ isServicesRegistered,
15
+ registerServices,
16
+ shutdownServices
17
+ } from "./chunk-X7TXCYYN.mjs";
18
+ import {
19
+ AuthService,
20
+ PermissionService,
21
+ RolePermissionService,
22
+ RoleService,
23
+ UserAccountService,
24
+ transformRichTextFields
25
+ } from "./chunk-NSEFNNU4.mjs";
26
+ import {
27
+ env
28
+ } from "./chunk-UJ2IMJ4W.mjs";
29
+ import {
30
+ getImageProcessor
31
+ } from "./chunk-EGXBZCGC.mjs";
32
+ import {
33
+ container
34
+ } from "./chunk-D5HQBNUB.mjs";
35
+ import {
36
+ NextlyError
37
+ } from "./chunk-NRUWQ5Z7.mjs";
38
+
39
+ // src/direct-api/namespaces/auth.ts
40
+ async function login(ctx, args) {
41
+ const user = await ctx.authService.verifyCredentials(
42
+ args.email,
43
+ args.password
44
+ );
45
+ const secret = env.NEXTLY_SECRET;
46
+ if (!secret) {
47
+ throw new NextlyError({
48
+ code: "INTERNAL_ERROR",
49
+ publicMessage: "NEXTLY_SECRET is not configured. Set NEXTLY_SECRET in your environment variables.",
50
+ statusCode: 500
51
+ });
52
+ }
53
+ const maxAge = 30 * 24 * 60 * 60;
54
+ const exp = Math.floor(Date.now() / 1e3) + maxAge;
55
+ const claims = buildClaims({
56
+ userId: String(user.id),
57
+ email: user.email,
58
+ name: user.name || "",
59
+ image: user.image ?? null,
60
+ roleIds: []
61
+ });
62
+ const token = await signAccessToken(claims, secret, maxAge);
63
+ return {
64
+ user,
65
+ token,
66
+ exp
67
+ };
68
+ }
69
+ async function logout() {
70
+ return;
71
+ }
72
+ async function me(ctx, args) {
73
+ if (!args.user?.id) {
74
+ throw new NextlyError({
75
+ code: "INVALID_INPUT",
76
+ publicMessage: "user.id is required for me() - Direct API requires explicit user context",
77
+ statusCode: 400
78
+ });
79
+ }
80
+ try {
81
+ const user = await ctx.userAccountService.getCurrentUser(args.user.id);
82
+ return {
83
+ user
84
+ };
85
+ } catch (err) {
86
+ if (NextlyError.isNotFound(err)) {
87
+ throw NextlyError.notFound({ logContext: { userId: args.user.id } });
88
+ }
89
+ throw err;
90
+ }
91
+ }
92
+ async function updateMe(ctx, args) {
93
+ if (!args.user?.id) {
94
+ throw new NextlyError({
95
+ code: "INVALID_INPUT",
96
+ publicMessage: "user.id is required for updateMe() - Direct API requires explicit user context",
97
+ statusCode: 400
98
+ });
99
+ }
100
+ try {
101
+ const user = await ctx.userAccountService.updateCurrentUser(
102
+ args.user.id,
103
+ args.data
104
+ );
105
+ return {
106
+ user
107
+ };
108
+ } catch (err) {
109
+ if (NextlyError.isNotFound(err)) {
110
+ throw NextlyError.notFound({ logContext: { userId: args.user.id } });
111
+ }
112
+ throw err;
113
+ }
114
+ }
115
+ async function register(ctx, args) {
116
+ const { email, password, collection: _collection, ...rest } = args;
117
+ const user = await ctx.authService.registerUser({
118
+ email,
119
+ password,
120
+ name: rest.name
121
+ });
122
+ return {
123
+ user
124
+ };
125
+ }
126
+ async function changePassword(ctx, args) {
127
+ if (!args.user?.id) {
128
+ throw new NextlyError({
129
+ code: "INVALID_INPUT",
130
+ publicMessage: "user.id is required for changePassword() - Direct API requires explicit user context",
131
+ statusCode: 400
132
+ });
133
+ }
134
+ await ctx.authService.changePassword(
135
+ args.user.id,
136
+ args.currentPassword,
137
+ args.newPassword
138
+ );
139
+ return { success: true };
140
+ }
141
+ async function forgotPassword(ctx, args) {
142
+ const result = await ctx.authService.generatePasswordResetToken(args.email, {
143
+ disableEmail: args.disableEmail,
144
+ expiration: args.expiration,
145
+ redirectPath: args.redirectPath
146
+ });
147
+ return {
148
+ success: true,
149
+ token: result.token
150
+ };
151
+ }
152
+ async function resetPassword(ctx, args) {
153
+ const result = await ctx.authService.resetPasswordWithToken(
154
+ args.token,
155
+ args.password
156
+ );
157
+ return {
158
+ success: true,
159
+ email: result.email
160
+ };
161
+ }
162
+ async function verifyEmail(ctx, args) {
163
+ const result = await ctx.authService.verifyEmail(args.token);
164
+ return {
165
+ success: true,
166
+ email: result.email
167
+ };
168
+ }
169
+
170
+ // src/direct-api/namespaces/helpers.ts
171
+ function mergeConfig(defaultConfig, args) {
172
+ return {
173
+ ...defaultConfig,
174
+ ...args
175
+ };
176
+ }
177
+ function createRequestContext(args) {
178
+ if (!args.user) {
179
+ return { locale: args.locale };
180
+ }
181
+ return {
182
+ user: {
183
+ id: args.user.id,
184
+ email: "",
185
+ role: args.user.role ?? "user",
186
+ permissions: []
187
+ },
188
+ locale: args.locale
189
+ };
190
+ }
191
+ function createErrorFromResult(result) {
192
+ return new NextlyError({
193
+ code: statusCodeToErrorCode(result.statusCode),
194
+ publicMessage: result.message,
195
+ statusCode: result.statusCode,
196
+ logContext: result.data !== void 0 && result.data !== null ? { resultData: result.data } : void 0
197
+ });
198
+ }
199
+ function createErrorFromSingleResult(result) {
200
+ const message = result.message || result.errors?.map((e) => e.message).join(", ") || "Operation failed";
201
+ if (result.statusCode === 400 && result.errors && result.errors.length > 0) {
202
+ return NextlyError.validation({
203
+ errors: result.errors.map((e) => ({
204
+ path: e.field ?? "",
205
+ code: "VALIDATION_ERROR",
206
+ message: e.message
207
+ }))
208
+ });
209
+ }
210
+ return new NextlyError({
211
+ code: statusCodeToErrorCode(result.statusCode),
212
+ publicMessage: message,
213
+ statusCode: result.statusCode
214
+ });
215
+ }
216
+ function statusCodeToErrorCode(statusCode) {
217
+ switch (statusCode) {
218
+ case 400:
219
+ return "VALIDATION_ERROR";
220
+ case 401:
221
+ return "AUTH_REQUIRED";
222
+ case 403:
223
+ return "FORBIDDEN";
224
+ case 404:
225
+ return "NOT_FOUND";
226
+ case 409:
227
+ return "CONFLICT";
228
+ case 413:
229
+ return "PAYLOAD_TOO_LARGE";
230
+ case 415:
231
+ return "UNSUPPORTED_MEDIA_TYPE";
232
+ case 422:
233
+ return "INVALID_INPUT";
234
+ case 429:
235
+ return "RATE_LIMITED";
236
+ case 502:
237
+ return "EXTERNAL_SERVICE_ERROR";
238
+ case 503:
239
+ return "SERVICE_UNAVAILABLE";
240
+ default:
241
+ return "INTERNAL_ERROR";
242
+ }
243
+ }
244
+ function isNotFoundError(error) {
245
+ return NextlyError.isNotFound(error);
246
+ }
247
+ function looksLikeId(value) {
248
+ if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(
249
+ value
250
+ )) {
251
+ return true;
252
+ }
253
+ if (/^\d+$/.test(value)) {
254
+ return true;
255
+ }
256
+ if (/^c[a-z0-9]{24,}$/i.test(value)) {
257
+ return true;
258
+ }
259
+ return false;
260
+ }
261
+ function mapRole(role) {
262
+ return {
263
+ id: role.id,
264
+ name: role.name,
265
+ slug: role.slug,
266
+ description: role.description ?? null,
267
+ level: role.level,
268
+ isSystem: Boolean(role.isSystem)
269
+ };
270
+ }
271
+ function mapPermission(perm) {
272
+ return {
273
+ id: perm.id,
274
+ name: perm.name,
275
+ slug: perm.slug,
276
+ action: perm.action,
277
+ resource: perm.resource,
278
+ description: perm.description ?? null
279
+ };
280
+ }
281
+ function mapComponentRecord(record) {
282
+ return {
283
+ id: record.id,
284
+ slug: record.slug,
285
+ label: record.label,
286
+ tableName: record.tableName,
287
+ description: record.description ?? void 0,
288
+ fields: Array.isArray(record.fields) ? record.fields : [],
289
+ admin: record.admin,
290
+ source: record.source,
291
+ locked: record.locked,
292
+ configPath: record.configPath ?? void 0,
293
+ schemaHash: record.schemaHash,
294
+ schemaVersion: record.schemaVersion,
295
+ migrationStatus: record.migrationStatus,
296
+ lastMigrationId: record.lastMigrationId ?? void 0,
297
+ createdBy: record.createdBy ?? void 0,
298
+ createdAt: record.createdAt,
299
+ updatedAt: record.updatedAt
300
+ };
301
+ }
302
+ function toListResult(result, limit, page) {
303
+ const total = result.pagination.total;
304
+ const totalPages = Math.max(1, Math.ceil(total / limit));
305
+ return {
306
+ items: result.data,
307
+ meta: {
308
+ total,
309
+ page,
310
+ limit,
311
+ totalPages,
312
+ hasNext: page < totalPages,
313
+ hasPrev: page > 1
314
+ }
315
+ };
316
+ }
317
+ function sliceListResult(items, limit, page) {
318
+ const effectiveLimit = limit ?? items.length;
319
+ const effectivePage = page ?? 1;
320
+ const start = (effectivePage - 1) * effectiveLimit;
321
+ const paged = items.slice(start, start + effectiveLimit);
322
+ const total = items.length;
323
+ const totalPages = effectiveLimit > 0 ? Math.max(1, Math.ceil(total / effectiveLimit)) : 1;
324
+ return {
325
+ items: paged,
326
+ meta: {
327
+ total,
328
+ page: effectivePage,
329
+ limit: effectiveLimit,
330
+ totalPages,
331
+ hasNext: effectivePage < totalPages,
332
+ hasPrev: effectivePage > 1
333
+ }
334
+ };
335
+ }
336
+ function buildMutationMessage(collection, verb) {
337
+ const noun = collection.charAt(0).toUpperCase() + collection.slice(1);
338
+ return `${noun} ${verb}.`;
339
+ }
340
+
341
+ // src/direct-api/namespaces/collections.ts
342
+ async function find(ctx, args) {
343
+ const config = mergeConfig(ctx.defaultConfig, args);
344
+ const result = await ctx.collectionsHandler.listEntries({
345
+ collectionName: args.collection,
346
+ page: args.page,
347
+ limit: args.limit,
348
+ where: args.where,
349
+ depth: config.depth,
350
+ select: args.select,
351
+ sort: args.sort,
352
+ richTextFormat: config.richTextFormat,
353
+ overrideAccess: config.overrideAccess,
354
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
355
+ context: config.context
356
+ });
357
+ if (!result.success) {
358
+ throw createErrorFromResult(result);
359
+ }
360
+ const legacy = result.data;
361
+ const total = legacy.totalDocs ?? legacy.docs.length;
362
+ const limit = legacy.limit ?? args.limit ?? legacy.docs.length;
363
+ const page = legacy.page ?? args.page ?? 1;
364
+ const totalPages = legacy.totalPages ?? Math.max(1, Math.ceil(total / Math.max(limit, 1)));
365
+ return {
366
+ items: legacy.docs,
367
+ meta: {
368
+ total,
369
+ page,
370
+ limit,
371
+ totalPages,
372
+ hasNext: legacy.hasNextPage ?? page < totalPages,
373
+ hasPrev: legacy.hasPrevPage ?? page > 1
374
+ }
375
+ };
376
+ }
377
+ async function findByID(ctx, args) {
378
+ const config = mergeConfig(ctx.defaultConfig, args);
379
+ try {
380
+ const result = await ctx.collectionsHandler.getEntry({
381
+ collectionName: args.collection,
382
+ entryId: args.id,
383
+ depth: config.depth,
384
+ select: args.select,
385
+ richTextFormat: config.richTextFormat,
386
+ overrideAccess: config.overrideAccess,
387
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
388
+ context: config.context
389
+ });
390
+ if (!result.success) {
391
+ if (config.disableErrors) {
392
+ return null;
393
+ }
394
+ throw createErrorFromResult(result);
395
+ }
396
+ return result.data;
397
+ } catch (error) {
398
+ if (config.disableErrors && isNotFoundError(error)) {
399
+ return null;
400
+ }
401
+ throw error;
402
+ }
403
+ }
404
+ async function create(ctx, args) {
405
+ const config = mergeConfig(ctx.defaultConfig, args);
406
+ const result = await ctx.collectionsHandler.createEntry(
407
+ {
408
+ collectionName: args.collection,
409
+ overrideAccess: config.overrideAccess,
410
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
411
+ context: config.context
412
+ },
413
+ args.data
414
+ );
415
+ if (!result.success) {
416
+ throw createErrorFromResult(result);
417
+ }
418
+ return {
419
+ message: buildMutationMessage(args.collection, "created"),
420
+ item: result.data
421
+ };
422
+ }
423
+ async function update(ctx, args) {
424
+ const config = mergeConfig(ctx.defaultConfig, args);
425
+ if (!args.id && !args.where) {
426
+ throw new NextlyError({
427
+ code: "INVALID_INPUT",
428
+ publicMessage: "Either 'id' or 'where' clause is required for update",
429
+ statusCode: 400
430
+ });
431
+ }
432
+ if (args.id) {
433
+ const result = await ctx.collectionsHandler.updateEntry(
434
+ {
435
+ collectionName: args.collection,
436
+ entryId: args.id,
437
+ overrideAccess: config.overrideAccess,
438
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
439
+ context: config.context
440
+ },
441
+ args.data
442
+ );
443
+ if (!result.success) {
444
+ throw createErrorFromResult(result);
445
+ }
446
+ return {
447
+ message: buildMutationMessage(args.collection, "updated"),
448
+ item: result.data
449
+ };
450
+ }
451
+ if (args.where) {
452
+ const bulkResult = await ctx.collectionsHandler.bulkUpdateByQuery(
453
+ {
454
+ collectionName: args.collection,
455
+ where: args.where,
456
+ data: args.data,
457
+ overrideAccess: config.overrideAccess,
458
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
459
+ context: config.context
460
+ },
461
+ { limit: 1 }
462
+ );
463
+ if (bulkResult.successCount === 0) {
464
+ throw NextlyError.notFound({
465
+ logContext: {
466
+ collection: args.collection,
467
+ where: args.where,
468
+ reason: "where-clause-no-match"
469
+ }
470
+ });
471
+ }
472
+ const updated = await findByID(ctx, {
473
+ collection: args.collection,
474
+ id: bulkResult.successes[0].id
475
+ });
476
+ if (!updated) {
477
+ throw new NextlyError({
478
+ code: "INTERNAL_ERROR",
479
+ publicMessage: "Document was updated but could not be retrieved",
480
+ statusCode: 500
481
+ });
482
+ }
483
+ return {
484
+ message: buildMutationMessage(args.collection, "updated"),
485
+ item: updated
486
+ };
487
+ }
488
+ throw new NextlyError({
489
+ code: "INVALID_INPUT",
490
+ publicMessage: "Either 'id' or 'where' clause is required for update",
491
+ statusCode: 400
492
+ });
493
+ }
494
+ async function deleteEntry(ctx, args) {
495
+ const config = mergeConfig(ctx.defaultConfig, args);
496
+ if (!args.id && !args.where) {
497
+ throw new NextlyError({
498
+ code: "INVALID_INPUT",
499
+ publicMessage: "Either 'id' or 'where' clause is required for delete",
500
+ statusCode: 400
501
+ });
502
+ }
503
+ if (args.id) {
504
+ const result = await ctx.collectionsHandler.deleteEntry({
505
+ collectionName: args.collection,
506
+ entryId: args.id,
507
+ overrideAccess: config.overrideAccess,
508
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
509
+ context: config.context
510
+ });
511
+ if (!result.success) {
512
+ throw createErrorFromResult(result);
513
+ }
514
+ return {
515
+ message: buildMutationMessage(args.collection, "deleted"),
516
+ item: { id: args.id }
517
+ };
518
+ }
519
+ if (args.where) {
520
+ const bulkResult = await ctx.collectionsHandler.bulkDeleteByQuery(
521
+ {
522
+ collectionName: args.collection,
523
+ where: args.where,
524
+ overrideAccess: config.overrideAccess,
525
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
526
+ context: config.context
527
+ },
528
+ { limit: 1e3 }
529
+ );
530
+ return {
531
+ deleted: true,
532
+ ids: bulkResult.successes.map((s) => s.id)
533
+ };
534
+ }
535
+ throw new NextlyError({
536
+ code: "INVALID_INPUT",
537
+ publicMessage: "Either 'id' or 'where' clause is required for delete",
538
+ statusCode: 400
539
+ });
540
+ }
541
+ async function count(ctx, args) {
542
+ const config = mergeConfig(ctx.defaultConfig, args);
543
+ const result = await ctx.collectionsHandler.countEntries({
544
+ collectionName: args.collection,
545
+ where: args.where,
546
+ overrideAccess: config.overrideAccess,
547
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
548
+ context: config.context
549
+ });
550
+ if (!result.success) {
551
+ throw createErrorFromResult(result);
552
+ }
553
+ const legacy = result.data;
554
+ return { total: legacy.total ?? legacy.totalDocs ?? 0 };
555
+ }
556
+ async function bulkDelete(ctx, args) {
557
+ const config = mergeConfig(ctx.defaultConfig, args);
558
+ const bulkResult = await ctx.collectionsHandler.bulkDeleteEntries({
559
+ collectionName: args.collection,
560
+ ids: args.ids,
561
+ overrideAccess: config.overrideAccess,
562
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
563
+ context: config.context
564
+ });
565
+ return bulkResult;
566
+ }
567
+ async function duplicate(ctx, args) {
568
+ const config = mergeConfig(ctx.defaultConfig, args);
569
+ const result = await ctx.collectionsHandler.duplicateEntry({
570
+ collectionName: args.collection,
571
+ entryId: args.id,
572
+ overrides: args.overrides,
573
+ overrideAccess: config.overrideAccess,
574
+ user: config.user ? { id: config.user.id, role: config.user.role } : void 0,
575
+ context: config.context
576
+ });
577
+ if (!result.success) {
578
+ throw createErrorFromResult(result);
579
+ }
580
+ return {
581
+ message: buildMutationMessage(args.collection, "duplicated"),
582
+ item: result.data
583
+ };
584
+ }
585
+
586
+ // src/direct-api/namespaces/singles.ts
587
+ async function findSingle(ctx, args) {
588
+ const config = mergeConfig(ctx.defaultConfig, args);
589
+ const result = await ctx.singleEntryService.get(args.slug, {
590
+ depth: config.depth,
591
+ locale: config.locale,
592
+ user: config.user ? { id: config.user.id, email: config.user.role } : void 0,
593
+ overrideAccess: config.overrideAccess,
594
+ context: config.context
595
+ });
596
+ if (!result.success) {
597
+ throw createErrorFromSingleResult(result);
598
+ }
599
+ let data = result.data;
600
+ if (config.richTextFormat && config.richTextFormat !== "json" && result.data) {
601
+ const single = await ctx.singleRegistryService.getSingleBySlug(args.slug);
602
+ if (single?.fields && Array.isArray(single.fields)) {
603
+ data = transformRichTextFields(
604
+ result.data,
605
+ single.fields,
606
+ config.richTextFormat
607
+ );
608
+ }
609
+ }
610
+ return data;
611
+ }
612
+ async function updateSingle(ctx, args) {
613
+ const config = mergeConfig(ctx.defaultConfig, args);
614
+ const result = await ctx.singleEntryService.update(args.slug, args.data, {
615
+ locale: config.locale,
616
+ user: config.user ? { id: config.user.id, email: config.user.role } : void 0,
617
+ overrideAccess: config.overrideAccess,
618
+ context: config.context
619
+ });
620
+ if (!result.success) {
621
+ throw createErrorFromSingleResult(result);
622
+ }
623
+ return result.data;
624
+ }
625
+ async function findSingles(ctx, args = {}) {
626
+ const config = mergeConfig(ctx.defaultConfig, args);
627
+ const registryResult = await ctx.singleRegistryService.listSingles({
628
+ source: args.source,
629
+ migrationStatus: args.migrationStatus,
630
+ locked: args.locked,
631
+ search: args.search,
632
+ limit: args.limit,
633
+ offset: args.offset
634
+ });
635
+ const entries = await Promise.all(
636
+ registryResult.data.map(async (record) => {
637
+ const result = await ctx.singleEntryService.get(record.slug, {
638
+ depth: config.depth,
639
+ locale: config.locale,
640
+ user: config.user ? { id: config.user.id, email: config.user.role } : void 0,
641
+ overrideAccess: config.overrideAccess,
642
+ context: config.context
643
+ });
644
+ if (!result.success) {
645
+ throw createErrorFromSingleResult(result);
646
+ }
647
+ return {
648
+ slug: record.slug,
649
+ label: record.label,
650
+ data: result.data
651
+ };
652
+ })
653
+ );
654
+ return {
655
+ docs: entries,
656
+ totalDocs: registryResult.total,
657
+ limit: args.limit ?? registryResult.data.length,
658
+ offset: args.offset ?? 0
659
+ };
660
+ }
661
+
662
+ // src/direct-api/namespaces/users.ts
663
+ function createUsersNamespace(ctx) {
664
+ return {
665
+ async find(args = {}) {
666
+ const limit = args.limit ?? 10;
667
+ const page = args.page ?? 1;
668
+ const result = await ctx.userService.listUsers(
669
+ {
670
+ pagination: { limit, page },
671
+ search: args.search,
672
+ emailVerified: args.emailVerified,
673
+ hasPassword: args.hasPassword,
674
+ sortBy: args.sortBy,
675
+ sortOrder: args.sortOrder
676
+ },
677
+ createRequestContext(args)
678
+ );
679
+ return toListResult(result, limit, page);
680
+ },
681
+ async findOne(args = {}) {
682
+ const result = await ctx.userService.listUsers(
683
+ {
684
+ pagination: { limit: 1, page: 1 },
685
+ search: args.search,
686
+ emailVerified: args.emailVerified,
687
+ hasPassword: args.hasPassword
688
+ },
689
+ createRequestContext(args)
690
+ );
691
+ return result.data[0] ?? null;
692
+ },
693
+ async findByID(args) {
694
+ const config = mergeConfig(ctx.defaultConfig, args);
695
+ try {
696
+ const user = await ctx.userService.findById(
697
+ args.id,
698
+ createRequestContext(args)
699
+ );
700
+ return user;
701
+ } catch (error) {
702
+ if (config.disableErrors && isNotFoundError(error)) {
703
+ return null;
704
+ }
705
+ throw error;
706
+ }
707
+ },
708
+ async create(args) {
709
+ const data = args.data ?? {};
710
+ const { name, image, roles, isActive, ...customFields } = data;
711
+ const user = await ctx.userService.create(
712
+ {
713
+ email: args.email,
714
+ name: name ?? "",
715
+ password: args.password,
716
+ image,
717
+ roles,
718
+ isActive,
719
+ ...customFields
720
+ },
721
+ createRequestContext(args)
722
+ );
723
+ return {
724
+ message: "User created.",
725
+ item: user
726
+ };
727
+ },
728
+ async update(args) {
729
+ if (!args.id) {
730
+ throw new NextlyError({
731
+ code: "INVALID_INPUT",
732
+ publicMessage: "'id' is required for users.update()",
733
+ statusCode: 400
734
+ });
735
+ }
736
+ const data = args.data ?? {};
737
+ const { email, name, image, emailVerified, isActive, ...customFields } = data;
738
+ const user = await ctx.userService.update(
739
+ args.id,
740
+ {
741
+ email,
742
+ name,
743
+ image,
744
+ emailVerified,
745
+ isActive,
746
+ ...customFields
747
+ },
748
+ createRequestContext(args)
749
+ );
750
+ return {
751
+ message: "User updated.",
752
+ item: user
753
+ };
754
+ },
755
+ async delete(args) {
756
+ if (!args.id) {
757
+ throw new NextlyError({
758
+ code: "INVALID_INPUT",
759
+ publicMessage: "'id' is required for users.delete()",
760
+ statusCode: 400
761
+ });
762
+ }
763
+ await ctx.userService.delete(args.id, createRequestContext(args));
764
+ return {
765
+ message: "User deleted.",
766
+ item: { id: args.id }
767
+ };
768
+ }
769
+ };
770
+ }
771
+
772
+ // src/direct-api/namespaces/media.ts
773
+ function createMediaNamespace(ctx) {
774
+ const folders = {
775
+ async list(args = {}) {
776
+ if (args.parent) {
777
+ return await ctx.mediaService.listSubfolders(
778
+ args.parent,
779
+ createRequestContext(args)
780
+ );
781
+ }
782
+ return await ctx.mediaService.listRootFolders(createRequestContext(args));
783
+ },
784
+ async create(args) {
785
+ if (!args.name) {
786
+ throw new NextlyError({
787
+ code: "INVALID_INPUT",
788
+ publicMessage: "'name' is required for media.folders.create()",
789
+ statusCode: 400
790
+ });
791
+ }
792
+ return await ctx.mediaService.createFolder(
793
+ {
794
+ name: args.name,
795
+ description: args.description,
796
+ color: args.color,
797
+ icon: args.icon,
798
+ parentId: args.parent ?? null
799
+ },
800
+ createRequestContext(args)
801
+ );
802
+ }
803
+ };
804
+ return {
805
+ async upload(args) {
806
+ return await ctx.mediaService.upload(
807
+ {
808
+ buffer: args.file.data,
809
+ filename: args.file.name,
810
+ mimeType: args.file.mimetype,
811
+ size: args.file.size,
812
+ altText: args.altText,
813
+ folderId: args.folder ?? null
814
+ },
815
+ createRequestContext(args)
816
+ );
817
+ },
818
+ async find(args = {}) {
819
+ const limit = args.limit ?? 24;
820
+ const page = args.page ?? 1;
821
+ const result = await ctx.mediaService.listMedia(
822
+ {
823
+ page,
824
+ limit,
825
+ search: args.search,
826
+ type: args.mimeType,
827
+ folderId: args.folder,
828
+ sortBy: args.sortBy,
829
+ sortOrder: args.sortOrder
830
+ },
831
+ createRequestContext(args)
832
+ );
833
+ return toListResult(result, limit, page);
834
+ },
835
+ async findByID(args) {
836
+ const config = mergeConfig(ctx.defaultConfig, args);
837
+ try {
838
+ return await ctx.mediaService.findById(
839
+ args.id,
840
+ createRequestContext(args)
841
+ );
842
+ } catch (error) {
843
+ if (config.disableErrors && isNotFoundError(error)) {
844
+ return null;
845
+ }
846
+ throw error;
847
+ }
848
+ },
849
+ async update(args) {
850
+ if (!args.id) {
851
+ throw new NextlyError({
852
+ code: "INVALID_INPUT",
853
+ publicMessage: "'id' is required for media.update()",
854
+ statusCode: 400
855
+ });
856
+ }
857
+ const item = await ctx.mediaService.update(
858
+ args.id,
859
+ {
860
+ filename: args.data.filename,
861
+ altText: args.data.altText,
862
+ caption: args.data.caption,
863
+ tags: args.data.tags,
864
+ folderId: args.data.folderId
865
+ },
866
+ createRequestContext(args)
867
+ );
868
+ return {
869
+ message: "Media updated.",
870
+ item
871
+ };
872
+ },
873
+ async delete(args) {
874
+ if (!args.id) {
875
+ throw new NextlyError({
876
+ code: "INVALID_INPUT",
877
+ publicMessage: "'id' is required for media.delete()",
878
+ statusCode: 400
879
+ });
880
+ }
881
+ await ctx.mediaService.delete(args.id, createRequestContext(args));
882
+ return {
883
+ message: "Media deleted.",
884
+ item: { id: args.id }
885
+ };
886
+ },
887
+ async bulkDelete(args) {
888
+ if (!args.ids || args.ids.length === 0) {
889
+ return {
890
+ successes: [],
891
+ failures: [],
892
+ total: 0,
893
+ successCount: 0,
894
+ failedCount: 0
895
+ };
896
+ }
897
+ return ctx.mediaService.bulkDelete(args.ids, createRequestContext(args));
898
+ },
899
+ folders
900
+ };
901
+ }
902
+
903
+ // src/direct-api/namespaces/forms.ts
904
+ function createFormsNamespace(ctx) {
905
+ const namespace = {
906
+ async find(args = {}) {
907
+ const limit = args.limit ?? 10;
908
+ const page = args.page ?? 1;
909
+ const where = {};
910
+ if (args.status) {
911
+ where.status = { equals: args.status };
912
+ }
913
+ const result = await ctx.collectionsHandler.listEntries({
914
+ collectionName: ctx.formsCollectionSlug,
915
+ page,
916
+ limit,
917
+ where: Object.keys(where).length > 0 ? where : void 0
918
+ });
919
+ if (!result.success) {
920
+ throw createErrorFromResult(result);
921
+ }
922
+ const legacy = result.data;
923
+ const total = legacy.totalDocs ?? legacy.docs.length;
924
+ const totalPages = legacy.totalPages ?? Math.max(1, Math.ceil(total / Math.max(limit, 1)));
925
+ return {
926
+ items: legacy.docs,
927
+ meta: {
928
+ total,
929
+ page,
930
+ limit,
931
+ totalPages,
932
+ hasNext: legacy.hasNextPage ?? page < totalPages,
933
+ hasPrev: legacy.hasPrevPage ?? page > 1
934
+ }
935
+ };
936
+ },
937
+ async findBySlug(args) {
938
+ const config = mergeConfig(ctx.defaultConfig, args);
939
+ if (!args.slug) {
940
+ throw new NextlyError({
941
+ code: "INVALID_INPUT",
942
+ publicMessage: "'slug' is required for forms.findBySlug()",
943
+ statusCode: 400
944
+ });
945
+ }
946
+ try {
947
+ const result = await ctx.collectionsHandler.listEntries({
948
+ collectionName: ctx.formsCollectionSlug,
949
+ where: { slug: { equals: args.slug } },
950
+ limit: 1
951
+ });
952
+ if (!result.success) {
953
+ if (config.disableErrors) {
954
+ return null;
955
+ }
956
+ throw createErrorFromResult(result);
957
+ }
958
+ const docs = result.data.docs;
959
+ if (!docs || docs.length === 0) {
960
+ if (config.disableErrors) {
961
+ return null;
962
+ }
963
+ throw NextlyError.notFound({
964
+ logContext: { slug: args.slug, entity: "form" }
965
+ });
966
+ }
967
+ return docs[0];
968
+ } catch (error) {
969
+ if (config.disableErrors && isNotFoundError(error)) {
970
+ return null;
971
+ }
972
+ throw error;
973
+ }
974
+ },
975
+ async submit(args) {
976
+ if (!args.form) {
977
+ throw new NextlyError({
978
+ code: "INVALID_INPUT",
979
+ publicMessage: "'form' (slug) is required for forms.submit()",
980
+ statusCode: 400
981
+ });
982
+ }
983
+ if (!args.data || typeof args.data !== "object") {
984
+ throw new NextlyError({
985
+ code: "INVALID_INPUT",
986
+ publicMessage: "'data' is required for forms.submit()",
987
+ statusCode: 400
988
+ });
989
+ }
990
+ const form = await namespace.findBySlug({
991
+ slug: args.form,
992
+ disableErrors: true
993
+ });
994
+ if (!form) {
995
+ return {
996
+ success: false,
997
+ error: "Form not found"
998
+ };
999
+ }
1000
+ if (form.status !== "published") {
1001
+ return {
1002
+ success: false,
1003
+ error: "This form is not currently accepting submissions"
1004
+ };
1005
+ }
1006
+ const submissionData = {
1007
+ form: form.id,
1008
+ data: args.data,
1009
+ status: "new",
1010
+ submittedAt: /* @__PURE__ */ new Date()
1011
+ };
1012
+ if (args.metadata?.ipAddress) {
1013
+ submissionData.ipAddress = args.metadata.ipAddress;
1014
+ }
1015
+ if (args.metadata?.userAgent) {
1016
+ submissionData.userAgent = args.metadata.userAgent;
1017
+ }
1018
+ const createResult = await ctx.collectionsHandler.createEntry(
1019
+ { collectionName: ctx.submissionsCollectionSlug },
1020
+ submissionData
1021
+ );
1022
+ if (!createResult.success) {
1023
+ return {
1024
+ success: false,
1025
+ error: createResult.message || "Failed to create submission"
1026
+ };
1027
+ }
1028
+ const settings = form.settings;
1029
+ const redirect = settings?.confirmationType === "redirect" ? settings.redirectUrl : void 0;
1030
+ return {
1031
+ success: true,
1032
+ submission: createResult.data,
1033
+ redirect
1034
+ };
1035
+ },
1036
+ async submissions(args) {
1037
+ if (!args.form) {
1038
+ throw new NextlyError({
1039
+ code: "INVALID_INPUT",
1040
+ publicMessage: "'form' (slug or ID) is required for forms.submissions()",
1041
+ statusCode: 400
1042
+ });
1043
+ }
1044
+ const limit = args.limit ?? 10;
1045
+ const page = args.page ?? 1;
1046
+ let formId = args.form;
1047
+ const isSlug = !looksLikeId(args.form);
1048
+ if (isSlug) {
1049
+ const form = await namespace.findBySlug({
1050
+ slug: args.form,
1051
+ disableErrors: true
1052
+ });
1053
+ if (!form) {
1054
+ throw NextlyError.notFound({
1055
+ logContext: { slug: args.form, entity: "form" }
1056
+ });
1057
+ }
1058
+ formId = form.id;
1059
+ }
1060
+ const result = await ctx.collectionsHandler.listEntries({
1061
+ collectionName: ctx.submissionsCollectionSlug,
1062
+ page,
1063
+ limit,
1064
+ where: { form: { equals: formId } }
1065
+ });
1066
+ if (!result.success) {
1067
+ throw createErrorFromResult(result);
1068
+ }
1069
+ const legacy = result.data;
1070
+ const total = legacy.totalDocs ?? legacy.docs.length;
1071
+ const totalPages = legacy.totalPages ?? Math.max(1, Math.ceil(total / Math.max(limit, 1)));
1072
+ return {
1073
+ items: legacy.docs,
1074
+ meta: {
1075
+ total,
1076
+ page,
1077
+ limit,
1078
+ totalPages,
1079
+ hasNext: legacy.hasNextPage ?? page < totalPages,
1080
+ hasPrev: legacy.hasPrevPage ?? page > 1
1081
+ }
1082
+ };
1083
+ }
1084
+ };
1085
+ return namespace;
1086
+ }
1087
+
1088
+ // src/direct-api/namespaces/components.ts
1089
+ function createComponentsNamespace(ctx) {
1090
+ return {
1091
+ async find(args = {}) {
1092
+ const result = await ctx.componentRegistryService.listComponents({
1093
+ source: args.source,
1094
+ migrationStatus: args.migrationStatus,
1095
+ locked: args.locked,
1096
+ search: args.search,
1097
+ limit: args.limit,
1098
+ offset: args.offset
1099
+ });
1100
+ const limit = args.limit ?? result.data.length;
1101
+ const offset = args.offset ?? 0;
1102
+ const total = result.total;
1103
+ const effectiveLimit = limit > 0 ? limit : Math.max(total, 1);
1104
+ const totalPages = Math.max(1, Math.ceil(total / effectiveLimit));
1105
+ const page = Math.floor(offset / effectiveLimit) + 1;
1106
+ return {
1107
+ items: result.data.map(mapComponentRecord),
1108
+ meta: {
1109
+ total,
1110
+ page,
1111
+ limit,
1112
+ totalPages,
1113
+ hasNext: page < totalPages,
1114
+ hasPrev: page > 1
1115
+ }
1116
+ };
1117
+ },
1118
+ async findBySlug(args) {
1119
+ const config = mergeConfig(ctx.defaultConfig, args);
1120
+ if (!args.slug) {
1121
+ throw new NextlyError({
1122
+ code: "INVALID_INPUT",
1123
+ publicMessage: "'slug' is required for components.findBySlug()",
1124
+ statusCode: 400
1125
+ });
1126
+ }
1127
+ try {
1128
+ const component = await ctx.componentRegistryService.getComponentBySlug(
1129
+ args.slug
1130
+ );
1131
+ if (!component) {
1132
+ if (config.disableErrors) {
1133
+ return null;
1134
+ }
1135
+ throw NextlyError.notFound({
1136
+ logContext: { slug: args.slug, entity: "component" }
1137
+ });
1138
+ }
1139
+ return mapComponentRecord(component);
1140
+ } catch (error) {
1141
+ if (error instanceof NextlyError) {
1142
+ throw error;
1143
+ }
1144
+ if (config.disableErrors && isNotFoundError(error)) {
1145
+ return null;
1146
+ }
1147
+ throw error;
1148
+ }
1149
+ },
1150
+ async create(args) {
1151
+ if (!args.slug) {
1152
+ throw new NextlyError({
1153
+ code: "INVALID_INPUT",
1154
+ publicMessage: "'slug' is required for components.create()",
1155
+ statusCode: 400
1156
+ });
1157
+ }
1158
+ if (!args.label) {
1159
+ throw new NextlyError({
1160
+ code: "INVALID_INPUT",
1161
+ publicMessage: "'label' is required for components.create()",
1162
+ statusCode: 400
1163
+ });
1164
+ }
1165
+ if (!args.fields || !Array.isArray(args.fields)) {
1166
+ throw new NextlyError({
1167
+ code: "INVALID_INPUT",
1168
+ publicMessage: "'fields' array is required for components.create()",
1169
+ statusCode: 400
1170
+ });
1171
+ }
1172
+ const { calculateSchemaHash } = await import("./schema-hash-FMMG6VPJ.mjs");
1173
+ const fieldsTyped = args.fields;
1174
+ const schemaHash = calculateSchemaHash(fieldsTyped);
1175
+ const component = await ctx.componentRegistryService.registerComponent({
1176
+ slug: args.slug,
1177
+ label: args.label,
1178
+ tableName: args.tableName ?? `comp_${args.slug}`,
1179
+ description: args.description,
1180
+ fields: fieldsTyped,
1181
+ admin: args.admin,
1182
+ source: "ui",
1183
+ locked: false,
1184
+ schemaHash,
1185
+ schemaVersion: 1,
1186
+ migrationStatus: "pending"
1187
+ });
1188
+ return {
1189
+ message: "Component created.",
1190
+ item: mapComponentRecord(component)
1191
+ };
1192
+ },
1193
+ async update(args) {
1194
+ if (!args.slug) {
1195
+ throw new NextlyError({
1196
+ code: "INVALID_INPUT",
1197
+ publicMessage: "'slug' is required for components.update()",
1198
+ statusCode: 400
1199
+ });
1200
+ }
1201
+ if (!args.data || typeof args.data !== "object") {
1202
+ throw new NextlyError({
1203
+ code: "INVALID_INPUT",
1204
+ publicMessage: "'data' object is required for components.update()",
1205
+ statusCode: 400
1206
+ });
1207
+ }
1208
+ const updateData = {};
1209
+ if (args.data.label !== void 0) {
1210
+ updateData.label = args.data.label;
1211
+ }
1212
+ if (args.data.description !== void 0) {
1213
+ updateData.description = args.data.description;
1214
+ }
1215
+ if (args.data.fields !== void 0) {
1216
+ const fieldsTyped = args.data.fields;
1217
+ updateData.fields = fieldsTyped;
1218
+ const { calculateSchemaHash } = await import("./schema-hash-FMMG6VPJ.mjs");
1219
+ updateData.schemaHash = calculateSchemaHash(fieldsTyped);
1220
+ }
1221
+ if (args.data.admin !== void 0) {
1222
+ updateData.admin = args.data.admin;
1223
+ }
1224
+ const component = await ctx.componentRegistryService.updateComponent(
1225
+ args.slug,
1226
+ updateData,
1227
+ { source: "ui" }
1228
+ );
1229
+ return {
1230
+ message: "Component updated.",
1231
+ item: mapComponentRecord(component)
1232
+ };
1233
+ },
1234
+ async delete(args) {
1235
+ if (!args.slug) {
1236
+ throw new NextlyError({
1237
+ code: "INVALID_INPUT",
1238
+ publicMessage: "'slug' is required for components.delete()",
1239
+ statusCode: 400
1240
+ });
1241
+ }
1242
+ await ctx.componentRegistryService.deleteComponent(args.slug);
1243
+ return {
1244
+ message: "Component deleted.",
1245
+ item: { slug: args.slug }
1246
+ };
1247
+ }
1248
+ };
1249
+ }
1250
+
1251
+ // src/direct-api/namespaces/email.ts
1252
+ function createEmailNamespace(ctx) {
1253
+ return {
1254
+ async send(args) {
1255
+ const to = Array.isArray(args.to) ? args.to[0] : args.to;
1256
+ const result = await ctx.emailSendService.send({
1257
+ to,
1258
+ subject: args.subject,
1259
+ html: args.html,
1260
+ plainText: args.text,
1261
+ providerId: args.providerId,
1262
+ attachments: args.attachments
1263
+ });
1264
+ return {
1265
+ success: result.success,
1266
+ messageId: result.messageId
1267
+ };
1268
+ },
1269
+ async sendWithTemplate(args) {
1270
+ const to = Array.isArray(args.to) ? args.to[0] : args.to;
1271
+ const sendOptions = {};
1272
+ if (args.providerId) sendOptions.providerId = args.providerId;
1273
+ if (args.attachments) sendOptions.attachments = args.attachments;
1274
+ const result = await ctx.emailSendService.sendWithTemplate(
1275
+ args.template,
1276
+ to,
1277
+ args.variables ?? {},
1278
+ Object.keys(sendOptions).length > 0 ? sendOptions : void 0
1279
+ );
1280
+ return {
1281
+ success: result.success,
1282
+ messageId: result.messageId
1283
+ };
1284
+ }
1285
+ };
1286
+ }
1287
+ function createEmailProvidersNamespace(ctx) {
1288
+ return {
1289
+ async find(args = {}) {
1290
+ const providers = await ctx.emailProviderService.listProviders();
1291
+ return sliceListResult(providers, args.limit, args.page);
1292
+ },
1293
+ async findByID(args) {
1294
+ const config = mergeConfig(ctx.defaultConfig, args);
1295
+ try {
1296
+ return await ctx.emailProviderService.getProvider(args.id);
1297
+ } catch (error) {
1298
+ if (config.disableErrors && isNotFoundError(error)) {
1299
+ return null;
1300
+ }
1301
+ throw error;
1302
+ }
1303
+ },
1304
+ async create(args) {
1305
+ const item = await ctx.emailProviderService.createProvider({
1306
+ name: args.data.name,
1307
+ type: args.data.type,
1308
+ fromEmail: args.data.fromEmail,
1309
+ fromName: args.data.fromName,
1310
+ configuration: args.data.configuration,
1311
+ isDefault: args.data.isDefault
1312
+ });
1313
+ return { message: "Email provider created.", item };
1314
+ },
1315
+ async update(args) {
1316
+ const item = await ctx.emailProviderService.updateProvider(
1317
+ args.id,
1318
+ args.data
1319
+ );
1320
+ return { message: "Email provider updated.", item };
1321
+ },
1322
+ async delete(args) {
1323
+ await ctx.emailProviderService.deleteProvider(args.id);
1324
+ return {
1325
+ message: "Email provider deleted.",
1326
+ item: { id: args.id }
1327
+ };
1328
+ },
1329
+ async setDefault(args) {
1330
+ return await ctx.emailProviderService.setDefault(args.id);
1331
+ },
1332
+ async test(args) {
1333
+ return await ctx.emailProviderService.testProvider(args.id, args.to);
1334
+ }
1335
+ };
1336
+ }
1337
+ function createEmailTemplatesNamespace(ctx) {
1338
+ return {
1339
+ async find(args = {}) {
1340
+ const templates = await ctx.emailTemplateService.listTemplates();
1341
+ return sliceListResult(templates, args.limit, args.page);
1342
+ },
1343
+ async findByID(args) {
1344
+ const config = mergeConfig(ctx.defaultConfig, args);
1345
+ try {
1346
+ return await ctx.emailTemplateService.getTemplate(args.id);
1347
+ } catch (error) {
1348
+ if (config.disableErrors && isNotFoundError(error)) {
1349
+ return null;
1350
+ }
1351
+ throw error;
1352
+ }
1353
+ },
1354
+ async findBySlug(args) {
1355
+ const config = mergeConfig(ctx.defaultConfig, args);
1356
+ try {
1357
+ const template = await ctx.emailTemplateService.getTemplateBySlug(
1358
+ args.slug
1359
+ );
1360
+ if (!template) {
1361
+ if (config.disableErrors) {
1362
+ return null;
1363
+ }
1364
+ throw NextlyError.notFound({
1365
+ logContext: { slug: args.slug, entity: "emailTemplate" }
1366
+ });
1367
+ }
1368
+ return template;
1369
+ } catch (error) {
1370
+ if (error instanceof NextlyError) {
1371
+ throw error;
1372
+ }
1373
+ if (config.disableErrors && isNotFoundError(error)) {
1374
+ return null;
1375
+ }
1376
+ throw error;
1377
+ }
1378
+ },
1379
+ async create(args) {
1380
+ const item = await ctx.emailTemplateService.createTemplate({
1381
+ name: args.data.name,
1382
+ slug: args.data.slug,
1383
+ subject: args.data.subject,
1384
+ htmlContent: args.data.htmlContent,
1385
+ plainTextContent: args.data.textContent,
1386
+ variables: args.data.variables,
1387
+ attachments: args.data.attachments
1388
+ });
1389
+ return { message: "Email template created.", item };
1390
+ },
1391
+ async update(args) {
1392
+ const { textContent, variables, attachments, ...rest } = args.data;
1393
+ const updateData = {
1394
+ ...rest
1395
+ };
1396
+ if (textContent !== void 0) {
1397
+ updateData.plainTextContent = textContent;
1398
+ }
1399
+ if (variables !== void 0) {
1400
+ updateData.variables = variables;
1401
+ }
1402
+ if (attachments !== void 0) {
1403
+ updateData.attachments = attachments;
1404
+ }
1405
+ const item = await ctx.emailTemplateService.updateTemplate(
1406
+ args.id,
1407
+ updateData
1408
+ );
1409
+ return { message: "Email template updated.", item };
1410
+ },
1411
+ async delete(args) {
1412
+ await ctx.emailTemplateService.deleteTemplate(args.id);
1413
+ return {
1414
+ message: "Email template deleted.",
1415
+ item: { id: args.id }
1416
+ };
1417
+ },
1418
+ async preview(args) {
1419
+ return await ctx.emailTemplateService.previewTemplate(
1420
+ args.id,
1421
+ args.data ?? {}
1422
+ );
1423
+ },
1424
+ async getLayout(_args = {}) {
1425
+ return await ctx.emailTemplateService.getLayout();
1426
+ },
1427
+ async updateLayout(args) {
1428
+ await ctx.emailTemplateService.updateLayout(args.data);
1429
+ }
1430
+ };
1431
+ }
1432
+ function createUserFieldsNamespace(ctx) {
1433
+ return {
1434
+ async find(args = {}) {
1435
+ const allFields = args.includeInactive ? await ctx.userFieldDefinitionService.listFields() : await ctx.userFieldDefinitionService.getMergedFields();
1436
+ return sliceListResult(allFields, args.limit, args.page);
1437
+ },
1438
+ async findByID(args) {
1439
+ const config = mergeConfig(ctx.defaultConfig, args);
1440
+ try {
1441
+ return await ctx.userFieldDefinitionService.getField(args.id);
1442
+ } catch (error) {
1443
+ if (config.disableErrors && isNotFoundError(error)) {
1444
+ return null;
1445
+ }
1446
+ throw error;
1447
+ }
1448
+ },
1449
+ async create(args) {
1450
+ const item = await ctx.userFieldDefinitionService.createField({
1451
+ name: args.data.name,
1452
+ label: args.data.label,
1453
+ type: args.data.type,
1454
+ required: args.data.required,
1455
+ defaultValue: args.data.defaultValue,
1456
+ options: args.data.options,
1457
+ placeholder: args.data.placeholder,
1458
+ description: args.data.description,
1459
+ sortOrder: args.data.sortOrder,
1460
+ source: "ui"
1461
+ });
1462
+ return { message: "User field created.", item };
1463
+ },
1464
+ async update(args) {
1465
+ const item = await ctx.userFieldDefinitionService.updateField(
1466
+ args.id,
1467
+ args.data
1468
+ );
1469
+ return { message: "User field updated.", item };
1470
+ },
1471
+ async delete(args) {
1472
+ await ctx.userFieldDefinitionService.deleteField(args.id);
1473
+ return {
1474
+ message: "User field deleted.",
1475
+ item: { id: args.id }
1476
+ };
1477
+ },
1478
+ async reorder(args) {
1479
+ return await ctx.userFieldDefinitionService.reorderFields(
1480
+ args.orderedIds
1481
+ );
1482
+ }
1483
+ };
1484
+ }
1485
+
1486
+ // src/direct-api/namespaces/rbac.ts
1487
+ import { eq } from "drizzle-orm";
1488
+ function createRolesNamespace(ctx) {
1489
+ return {
1490
+ async find(args = {}) {
1491
+ const result = await ctx.rbacRoleService.listRoles({
1492
+ search: args.search,
1493
+ page: args.page ?? 1,
1494
+ limit: args.limit ?? 10
1495
+ });
1496
+ const data = result.data;
1497
+ const items = data.map((r) => mapRole(r));
1498
+ const total = result.meta?.total ?? items.length;
1499
+ const limit = args.limit ?? 10;
1500
+ const page = args.page ?? 1;
1501
+ const totalPages = Math.max(1, Math.ceil(total / limit));
1502
+ return {
1503
+ items,
1504
+ meta: {
1505
+ total,
1506
+ page,
1507
+ limit,
1508
+ totalPages,
1509
+ hasNext: page < totalPages,
1510
+ hasPrev: page > 1
1511
+ }
1512
+ };
1513
+ },
1514
+ async findByID(args) {
1515
+ const role = await ctx.rbacRoleService.getRoleById(args.id);
1516
+ return mapRole(role);
1517
+ },
1518
+ async create(args) {
1519
+ const role = await ctx.rbacRoleService.createRole({
1520
+ name: args.data.name,
1521
+ slug: args.data.slug,
1522
+ description: args.data.description,
1523
+ level: args.data.level ?? 0,
1524
+ permissionIds: args.data.permissionIds ?? [],
1525
+ childRoleIds: args.data.childRoleIds ?? []
1526
+ });
1527
+ return {
1528
+ message: "Role created.",
1529
+ item: mapRole(role)
1530
+ };
1531
+ },
1532
+ async update(args) {
1533
+ await ctx.rbacRoleService.updateRole(args.id, {
1534
+ name: args.data.name,
1535
+ slug: args.data.slug,
1536
+ description: args.data.description ?? void 0,
1537
+ level: args.data.level
1538
+ });
1539
+ const updated = await ctx.rbacRoleService.getRoleById(args.id);
1540
+ return {
1541
+ message: "Role updated.",
1542
+ item: mapRole(updated)
1543
+ };
1544
+ },
1545
+ async delete(args) {
1546
+ await ctx.rbacRoleService.deleteRole(args.id);
1547
+ return {
1548
+ message: "Role deleted.",
1549
+ item: { id: args.id }
1550
+ };
1551
+ },
1552
+ async getPermissions(args) {
1553
+ const rolePerms = await ctx.rbacRolePermissionService.listRolePermissions(
1554
+ args.id
1555
+ );
1556
+ const fullPerms = await Promise.all(
1557
+ rolePerms.map(async (rp) => {
1558
+ try {
1559
+ const perm = await ctx.rbacPermissionService.getPermissionById(
1560
+ rp.id
1561
+ );
1562
+ return mapPermission(perm);
1563
+ } catch {
1564
+ return null;
1565
+ }
1566
+ })
1567
+ );
1568
+ return fullPerms.filter((p) => p !== null);
1569
+ },
1570
+ async setPermissions(args) {
1571
+ const updatedPerms = await ctx.rbacRolePermissionService.setRolePermissions(
1572
+ args.roleId,
1573
+ args.permissionIds
1574
+ );
1575
+ const fullPerms = await Promise.all(
1576
+ updatedPerms.map(async (rp) => {
1577
+ try {
1578
+ const perm = await ctx.rbacPermissionService.getPermissionById(
1579
+ rp.id
1580
+ );
1581
+ return mapPermission(perm);
1582
+ } catch {
1583
+ return null;
1584
+ }
1585
+ })
1586
+ );
1587
+ return fullPerms.filter((p) => p !== null);
1588
+ }
1589
+ };
1590
+ }
1591
+ function createPermissionsNamespace(ctx) {
1592
+ return {
1593
+ async find(args = {}) {
1594
+ const limit = args.limit ?? 10;
1595
+ const page = args.page ?? 1;
1596
+ const result = await ctx.rbacPermissionService.listPermissions({
1597
+ page,
1598
+ limit,
1599
+ search: args.search,
1600
+ resource: args.resource,
1601
+ action: args.action
1602
+ });
1603
+ const total = result.meta?.total ?? result.data.length;
1604
+ const totalPages = Math.max(1, Math.ceil(total / limit));
1605
+ const items = result.data.map((p) => mapPermission(p));
1606
+ return {
1607
+ items,
1608
+ meta: {
1609
+ total,
1610
+ page,
1611
+ limit,
1612
+ totalPages,
1613
+ hasNext: page < totalPages,
1614
+ hasPrev: page > 1
1615
+ }
1616
+ };
1617
+ },
1618
+ async findByID(args) {
1619
+ try {
1620
+ const perm = await ctx.rbacPermissionService.getPermissionById(args.id);
1621
+ return mapPermission(perm);
1622
+ } catch (error) {
1623
+ if (args.disableErrors && NextlyError.isNotFound(error)) {
1624
+ return null;
1625
+ }
1626
+ throw error;
1627
+ }
1628
+ },
1629
+ async create(args) {
1630
+ const { name, slug, action, resource, description } = args.data;
1631
+ const ensured = await ctx.rbacPermissionService.ensurePermission(
1632
+ action,
1633
+ resource,
1634
+ name,
1635
+ slug,
1636
+ description
1637
+ );
1638
+ const fetched = await ctx.rbacPermissionService.getPermissionById(
1639
+ ensured.id
1640
+ );
1641
+ return {
1642
+ message: "Permission created.",
1643
+ item: mapPermission(fetched)
1644
+ };
1645
+ },
1646
+ async delete(args) {
1647
+ await ctx.rbacPermissionService.deletePermissionById(args.id);
1648
+ return {
1649
+ message: "Permission deleted.",
1650
+ item: { id: args.id }
1651
+ };
1652
+ }
1653
+ };
1654
+ }
1655
+ function createAccessNamespace(ctx) {
1656
+ return {
1657
+ async check(args) {
1658
+ return await ctx.rbacAccessControlService.checkAccess({
1659
+ userId: args.userId,
1660
+ operation: args.operation,
1661
+ resource: args.resource
1662
+ });
1663
+ },
1664
+ async checkApiKey(args) {
1665
+ try {
1666
+ const auth = await ctx.apiKeyService.authenticateApiKey(args.rawKey);
1667
+ if (!auth) return { valid: false };
1668
+ const [permissions, roles, meta] = await Promise.all([
1669
+ ctx.apiKeyService.resolveApiKeyPermissions(
1670
+ auth.tokenType,
1671
+ auth.roleId,
1672
+ auth.userId,
1673
+ auth.id
1674
+ ),
1675
+ ctx.apiKeyService.resolveApiKeyRoles(
1676
+ auth.tokenType,
1677
+ auth.roleId,
1678
+ auth.userId
1679
+ ),
1680
+ ctx.apiKeyService.getApiKeyById(auth.id, auth.userId, {
1681
+ allUsers: true
1682
+ })
1683
+ ]);
1684
+ return {
1685
+ valid: true,
1686
+ userId: auth.userId,
1687
+ tokenType: auth.tokenType,
1688
+ permissions,
1689
+ roles,
1690
+ expiresAt: meta?.expiresAt ?? null
1691
+ };
1692
+ } catch {
1693
+ return { valid: false };
1694
+ }
1695
+ }
1696
+ };
1697
+ }
1698
+ async function resolveApiKeyOwner(apiKeyService, id) {
1699
+ const adapter = container.get("adapter");
1700
+ const privateTable = apiKeyService.apiKeysTable;
1701
+ const db = adapter.getDrizzle();
1702
+ const rows = await db.select({ userId: privateTable.userId }).from(privateTable).where(eq(privateTable.id, id)).limit(1);
1703
+ if (rows.length === 0) {
1704
+ throw NextlyError.notFound({ logContext: { entity: "apiKey", apiKeyId: id } });
1705
+ }
1706
+ return rows[0].userId;
1707
+ }
1708
+ function createApiKeysNamespace(ctx) {
1709
+ return {
1710
+ async list(args = {}) {
1711
+ const allUsers = !args.userId;
1712
+ return await ctx.apiKeyService.listApiKeys(args.userId ?? "", {
1713
+ allUsers
1714
+ });
1715
+ },
1716
+ async findByID(args) {
1717
+ return await ctx.apiKeyService.getApiKeyById(args.id, "", {
1718
+ allUsers: true
1719
+ });
1720
+ },
1721
+ async create(args) {
1722
+ const { meta, key } = await ctx.apiKeyService.createApiKey(args.userId, {
1723
+ name: args.name,
1724
+ description: args.description ?? void 0,
1725
+ tokenType: args.tokenType,
1726
+ roleId: args.roleId ?? void 0,
1727
+ expiresIn: args.expiresIn
1728
+ });
1729
+ return { doc: meta, key };
1730
+ },
1731
+ async update(args) {
1732
+ const userId = await resolveApiKeyOwner(ctx.apiKeyService, args.id);
1733
+ return await ctx.apiKeyService.updateApiKey(args.id, userId, {
1734
+ name: args.name,
1735
+ description: args.description ?? void 0
1736
+ });
1737
+ },
1738
+ async revoke(args) {
1739
+ const userId = await resolveApiKeyOwner(ctx.apiKeyService, args.id);
1740
+ await ctx.apiKeyService.revokeApiKey(args.id, userId);
1741
+ return { success: true };
1742
+ }
1743
+ };
1744
+ }
1745
+
1746
+ // src/direct-api/nextly.ts
1747
+ var Nextly = class {
1748
+ /**
1749
+ * Default configuration applied to all operations.
1750
+ *
1751
+ * @internal
1752
+ */
1753
+ defaultConfig;
1754
+ users;
1755
+ media;
1756
+ forms;
1757
+ components;
1758
+ email;
1759
+ emailProviders;
1760
+ emailTemplates;
1761
+ userFields;
1762
+ roles;
1763
+ permissions;
1764
+ access;
1765
+ apiKeys;
1766
+ /**
1767
+ * Create a new Nextly instance.
1768
+ *
1769
+ * @param config - Default configuration for all operations
1770
+ */
1771
+ constructor(config = {}) {
1772
+ this.defaultConfig = {
1773
+ overrideAccess: true,
1774
+ ...config
1775
+ };
1776
+ this.users = createUsersNamespace(this);
1777
+ this.media = createMediaNamespace(this);
1778
+ this.forms = createFormsNamespace(this);
1779
+ this.components = createComponentsNamespace(this);
1780
+ this.email = createEmailNamespace(this);
1781
+ this.emailProviders = createEmailProvidersNamespace(this);
1782
+ this.emailTemplates = createEmailTemplatesNamespace(this);
1783
+ this.userFields = createUserFieldsNamespace(this);
1784
+ this.roles = createRolesNamespace(this);
1785
+ this.permissions = createPermissionsNamespace(this);
1786
+ this.access = createAccessNamespace(this);
1787
+ this.apiKeys = createApiKeysNamespace(this);
1788
+ }
1789
+ /**
1790
+ * Get the forms collection slug.
1791
+ * Defaults to "forms" (matching the form builder plugin default).
1792
+ *
1793
+ * @internal
1794
+ */
1795
+ get formsCollectionSlug() {
1796
+ return this.defaultConfig.forms?.collectionSlug ?? "forms";
1797
+ }
1798
+ /**
1799
+ * Get the form submissions collection slug.
1800
+ * Defaults to "form-submissions" (matching the form builder plugin default).
1801
+ *
1802
+ * @internal
1803
+ */
1804
+ get submissionsCollectionSlug() {
1805
+ return this.defaultConfig.forms?.submissionCollectionSlug ?? "form-submissions";
1806
+ }
1807
+ /** @internal */
1808
+ get collectionsHandler() {
1809
+ return container.get("collectionsHandler");
1810
+ }
1811
+ /** @internal */
1812
+ get singleEntryService() {
1813
+ return container.get("singleEntryService");
1814
+ }
1815
+ /** @internal */
1816
+ get singleRegistryService() {
1817
+ return container.get("singleRegistryService");
1818
+ }
1819
+ /** Cached AuthService — not registered in the DI container. */
1820
+ _authService = null;
1821
+ /** @internal */
1822
+ get authService() {
1823
+ if (!this._authService) {
1824
+ const adapter = container.get("adapter");
1825
+ const logger = container.has("logger") ? container.get("logger") : console;
1826
+ const emailService = container.has("emailService") ? container.get("emailService") : void 0;
1827
+ this._authService = new AuthService(adapter, logger, emailService);
1828
+ }
1829
+ return this._authService;
1830
+ }
1831
+ /** Cached UserAccountService — not registered in the DI container. */
1832
+ _userAccountService = null;
1833
+ /** @internal */
1834
+ get userAccountService() {
1835
+ if (!this._userAccountService) {
1836
+ const adapter = container.get("adapter");
1837
+ const logger = container.has("logger") ? container.get("logger") : console;
1838
+ this._userAccountService = new UserAccountService(adapter, logger);
1839
+ }
1840
+ return this._userAccountService;
1841
+ }
1842
+ /** @internal */
1843
+ get userService() {
1844
+ return container.get("userService");
1845
+ }
1846
+ /** @internal */
1847
+ get mediaService() {
1848
+ return container.get("mediaService");
1849
+ }
1850
+ /** @internal */
1851
+ get componentRegistryService() {
1852
+ return container.get("componentRegistryService");
1853
+ }
1854
+ /** @internal */
1855
+ get emailProviderService() {
1856
+ return container.get("emailProviderService");
1857
+ }
1858
+ /** @internal */
1859
+ get emailTemplateService() {
1860
+ return container.get("emailTemplateService");
1861
+ }
1862
+ /** @internal */
1863
+ get userFieldDefinitionService() {
1864
+ return container.get(
1865
+ "userFieldDefinitionService"
1866
+ );
1867
+ }
1868
+ /** @internal */
1869
+ get emailSendService() {
1870
+ return container.get("emailService");
1871
+ }
1872
+ /** Cached RoleService — not registered in the DI container. */
1873
+ _rbacRoleService = null;
1874
+ /** @internal */
1875
+ get rbacRoleService() {
1876
+ if (!this._rbacRoleService) {
1877
+ const adapter = container.get("adapter");
1878
+ const logger = container.has("logger") ? container.get("logger") : console;
1879
+ this._rbacRoleService = new RoleService(adapter, logger);
1880
+ }
1881
+ return this._rbacRoleService;
1882
+ }
1883
+ /** Cached PermissionService — not registered in the DI container. */
1884
+ _rbacPermissionService = null;
1885
+ /** @internal */
1886
+ get rbacPermissionService() {
1887
+ if (!this._rbacPermissionService) {
1888
+ const adapter = container.get("adapter");
1889
+ const logger = container.has("logger") ? container.get("logger") : console;
1890
+ this._rbacPermissionService = new PermissionService(adapter, logger);
1891
+ }
1892
+ return this._rbacPermissionService;
1893
+ }
1894
+ /** Cached RolePermissionService — not registered in the DI container. */
1895
+ _rbacRolePermissionService = null;
1896
+ /** @internal */
1897
+ get rbacRolePermissionService() {
1898
+ if (!this._rbacRolePermissionService) {
1899
+ const adapter = container.get("adapter");
1900
+ const logger = container.has("logger") ? container.get("logger") : console;
1901
+ this._rbacRolePermissionService = new RolePermissionService(
1902
+ adapter,
1903
+ logger
1904
+ );
1905
+ }
1906
+ return this._rbacRolePermissionService;
1907
+ }
1908
+ /** @internal */
1909
+ get rbacAccessControlService() {
1910
+ return container.get("rbacAccessControlService");
1911
+ }
1912
+ /** @internal */
1913
+ get apiKeyService() {
1914
+ return container.get("apiKeyService");
1915
+ }
1916
+ /**
1917
+ * Find multiple documents in a collection.
1918
+ *
1919
+ * (`{ items, meta }`). Callers migrating from `{ docs, totalDocs, ... }`:
1920
+ * `result.docs` -> `result.items`, `result.totalDocs` -> `result.meta.total`.
1921
+ *
1922
+ * @throws {NextlyError} If the operation fails
1923
+ */
1924
+ find(args) {
1925
+ return find(this, args);
1926
+ }
1927
+ /**
1928
+ * Find a single document by ID. Returns `null` when not found and
1929
+ * `disableErrors` is `true`; otherwise throws.
1930
+ */
1931
+ findByID(args) {
1932
+ return findByID(this, args);
1933
+ }
1934
+ /**
1935
+ * Create a new document in a collection.
1936
+ *
1937
+ * created doc must read `result.item` (was a bare `T`).
1938
+ */
1939
+ create(args) {
1940
+ return create(this, args);
1941
+ }
1942
+ /**
1943
+ * Update a document by ID or by `where` clause (returns the first match).
1944
+ *
1945
+ * updated doc must read `result.item` (was a bare `T`).
1946
+ */
1947
+ update(args) {
1948
+ return update(this, args);
1949
+ }
1950
+ /**
1951
+ * Delete a document by ID or by `where` clause.
1952
+ *
1953
+ * where `item` carries the deleted `id`. The bulk-by-where path still
1954
+ * returns the legacy `DeleteResult` shape (`{ deleted, ids }`) because
1955
+ * a multi-row delete cannot collapse to a single mutation envelope.
1956
+ */
1957
+ delete(args) {
1958
+ return deleteEntry(this, args);
1959
+ }
1960
+ /**
1961
+ * Count documents matching a query.
1962
+ *
1963
+ */
1964
+ count(args) {
1965
+ return count(this, args);
1966
+ }
1967
+ /** Bulk-delete multiple documents by IDs (partial success pattern). */
1968
+ bulkDelete(args) {
1969
+ return bulkDelete(this, args);
1970
+ }
1971
+ /**
1972
+ * Duplicate a document (optionally applying field overrides).
1973
+ *
1974
+ * duplicated doc must read `result.item` (was a bare `T`).
1975
+ */
1976
+ duplicate(args) {
1977
+ return duplicate(this, args);
1978
+ }
1979
+ /** Get a Single (global) document by slug. */
1980
+ findSingle(args) {
1981
+ return findSingle(this, args);
1982
+ }
1983
+ /** Update a Single (global) document by slug. */
1984
+ updateSingle(args) {
1985
+ return updateSingle(this, args);
1986
+ }
1987
+ /** Fetch the content of every registered Single. */
1988
+ findSingles(args = {}) {
1989
+ return findSingles(this, args);
1990
+ }
1991
+ /** Verify credentials and return a signed session token. */
1992
+ login(args) {
1993
+ return login(this, args);
1994
+ }
1995
+ /** Logout — no-op for the Direct API (session lives in the app). */
1996
+ logout() {
1997
+ return logout();
1998
+ }
1999
+ /** Fetch the current user's profile (requires explicit `user.id`). */
2000
+ me(args) {
2001
+ return me(this, args);
2002
+ }
2003
+ /** Update the current user's profile (name/image only). */
2004
+ updateMe(args) {
2005
+ return updateMe(this, args);
2006
+ }
2007
+ /** Register a new user with email + password. */
2008
+ register(args) {
2009
+ return register(this, args);
2010
+ }
2011
+ /** Change the current user's password (requires the current password). */
2012
+ changePassword(args) {
2013
+ return changePassword(this, args);
2014
+ }
2015
+ /** Initiate password reset (always returns success to avoid leaking emails). */
2016
+ forgotPassword(args) {
2017
+ return forgotPassword(this, args);
2018
+ }
2019
+ /** Reset a user's password using a token from `forgotPassword`. */
2020
+ resetPassword(args) {
2021
+ return resetPassword(this, args);
2022
+ }
2023
+ /** Verify a user's email using a verification token. */
2024
+ verifyEmail(args) {
2025
+ return verifyEmail(this, args);
2026
+ }
2027
+ };
2028
+ var globalForDirectApi = globalThis;
2029
+ function getNextly(config) {
2030
+ if (!isServicesRegistered()) {
2031
+ throw new NextlyError({
2032
+ code: "INTERNAL_ERROR",
2033
+ publicMessage: "Nextly services not initialized. Call registerServices() before using the Direct API.",
2034
+ statusCode: 500
2035
+ });
2036
+ }
2037
+ if (!globalForDirectApi.__nextly_directApiInstance) {
2038
+ globalForDirectApi.__nextly_directApiInstance = new Nextly(config);
2039
+ if (!container.has("nextlyDirectAPI")) {
2040
+ container.register(
2041
+ "nextlyDirectAPI",
2042
+ () => globalForDirectApi.__nextly_directApiInstance
2043
+ );
2044
+ }
2045
+ }
2046
+ return globalForDirectApi.__nextly_directApiInstance;
2047
+ }
2048
+ var nextly = {
2049
+ find: (args) => getNextly().find(args),
2050
+ findByID: (args) => getNextly().findByID(args),
2051
+ create: (args) => getNextly().create(args),
2052
+ update: (args) => getNextly().update(args),
2053
+ delete: (args) => getNextly().delete(args),
2054
+ count: (args) => getNextly().count(args),
2055
+ bulkDelete: (args) => getNextly().bulkDelete(args),
2056
+ duplicate: (args) => getNextly().duplicate(args),
2057
+ findSingle: (args) => getNextly().findSingle(args),
2058
+ updateSingle: (args) => getNextly().updateSingle(args),
2059
+ findSingles: (args) => getNextly().findSingles(args ?? {}),
2060
+ login: (args) => getNextly().login(args),
2061
+ logout: () => getNextly().logout(),
2062
+ me: (args) => getNextly().me(args),
2063
+ updateMe: (args) => getNextly().updateMe(args),
2064
+ register: (args) => getNextly().register(args),
2065
+ changePassword: (args) => getNextly().changePassword(args),
2066
+ forgotPassword: (args) => getNextly().forgotPassword(args),
2067
+ resetPassword: (args) => getNextly().resetPassword(args),
2068
+ verifyEmail: (args) => getNextly().verifyEmail(args),
2069
+ users: {
2070
+ find: (args) => getNextly().users.find(args),
2071
+ findOne: (args) => getNextly().users.findOne(args),
2072
+ findByID: (args) => getNextly().users.findByID(args),
2073
+ create: (args) => getNextly().users.create(args),
2074
+ update: (args) => getNextly().users.update(args),
2075
+ delete: (args) => getNextly().users.delete(args)
2076
+ },
2077
+ media: {
2078
+ upload: (args) => getNextly().media.upload(args),
2079
+ find: (args) => getNextly().media.find(args),
2080
+ findByID: (args) => getNextly().media.findByID(args),
2081
+ update: (args) => getNextly().media.update(args),
2082
+ delete: (args) => getNextly().media.delete(args),
2083
+ bulkDelete: (args) => getNextly().media.bulkDelete(args),
2084
+ folders: {
2085
+ list: (args) => getNextly().media.folders.list(args),
2086
+ create: (args) => getNextly().media.folders.create(args)
2087
+ }
2088
+ },
2089
+ forms: {
2090
+ find: (args) => getNextly().forms.find(args),
2091
+ findBySlug: (args) => getNextly().forms.findBySlug(args),
2092
+ submit: (args) => getNextly().forms.submit(args),
2093
+ submissions: (args) => getNextly().forms.submissions(args)
2094
+ },
2095
+ components: {
2096
+ find: (args) => getNextly().components.find(args),
2097
+ findBySlug: (args) => getNextly().components.findBySlug(args),
2098
+ create: (args) => getNextly().components.create(args),
2099
+ update: (args) => getNextly().components.update(args),
2100
+ delete: (args) => getNextly().components.delete(args)
2101
+ },
2102
+ email: {
2103
+ send: (args) => getNextly().email.send(args),
2104
+ sendWithTemplate: (args) => getNextly().email.sendWithTemplate(args)
2105
+ },
2106
+ emailProviders: {
2107
+ find: (args) => getNextly().emailProviders.find(args),
2108
+ findByID: (args) => getNextly().emailProviders.findByID(args),
2109
+ create: (args) => getNextly().emailProviders.create(args),
2110
+ update: (args) => getNextly().emailProviders.update(args),
2111
+ delete: (args) => getNextly().emailProviders.delete(args),
2112
+ setDefault: (args) => getNextly().emailProviders.setDefault(args),
2113
+ test: (args) => getNextly().emailProviders.test(args)
2114
+ },
2115
+ emailTemplates: {
2116
+ find: (args) => getNextly().emailTemplates.find(args),
2117
+ findByID: (args) => getNextly().emailTemplates.findByID(args),
2118
+ findBySlug: (args) => getNextly().emailTemplates.findBySlug(args),
2119
+ create: (args) => getNextly().emailTemplates.create(args),
2120
+ update: (args) => getNextly().emailTemplates.update(args),
2121
+ delete: (args) => getNextly().emailTemplates.delete(args),
2122
+ preview: (args) => getNextly().emailTemplates.preview(args),
2123
+ getLayout: (args) => getNextly().emailTemplates.getLayout(args),
2124
+ updateLayout: (args) => getNextly().emailTemplates.updateLayout(args)
2125
+ },
2126
+ userFields: {
2127
+ find: (args) => getNextly().userFields.find(args),
2128
+ findByID: (args) => getNextly().userFields.findByID(args),
2129
+ create: (args) => getNextly().userFields.create(args),
2130
+ update: (args) => getNextly().userFields.update(args),
2131
+ delete: (args) => getNextly().userFields.delete(args),
2132
+ reorder: (args) => getNextly().userFields.reorder(args)
2133
+ },
2134
+ roles: {
2135
+ find: (args) => getNextly().roles.find(args),
2136
+ findByID: (args) => getNextly().roles.findByID(args),
2137
+ create: (args) => getNextly().roles.create(args),
2138
+ update: (args) => getNextly().roles.update(args),
2139
+ delete: (args) => getNextly().roles.delete(args),
2140
+ getPermissions: (args) => getNextly().roles.getPermissions(args),
2141
+ setPermissions: (args) => getNextly().roles.setPermissions(args)
2142
+ },
2143
+ permissions: {
2144
+ find: (args) => getNextly().permissions.find(args),
2145
+ findByID: (args) => getNextly().permissions.findByID(args),
2146
+ create: (args) => getNextly().permissions.create(args),
2147
+ delete: (args) => getNextly().permissions.delete(args)
2148
+ },
2149
+ apiKeys: {
2150
+ list: (args) => getNextly().apiKeys.list(args),
2151
+ findByID: (args) => getNextly().apiKeys.findByID(args),
2152
+ create: (args) => getNextly().apiKeys.create(args),
2153
+ update: (args) => getNextly().apiKeys.update(args),
2154
+ revoke: (args) => getNextly().apiKeys.revoke(args)
2155
+ },
2156
+ access: {
2157
+ check: (args) => getNextly().access.check(args),
2158
+ checkApiKey: (args) => getNextly().access.checkApiKey(args)
2159
+ }
2160
+ };
2161
+
2162
+ // src/init/build-service-config.ts
2163
+ function buildServiceConfig(providedConfig) {
2164
+ const serviceConfig = {};
2165
+ if (providedConfig) {
2166
+ const { config: nextlyConfig, ...rest } = providedConfig;
2167
+ Object.assign(serviceConfig, rest);
2168
+ if (!serviceConfig.storagePlugins && nextlyConfig?.storage) {
2169
+ serviceConfig.storagePlugins = nextlyConfig.storage;
2170
+ if (nextlyConfig.storage.length > 0) {
2171
+ console.log(
2172
+ `[Nextly] Using ${nextlyConfig.storage.length} storage plugin(s) from config`
2173
+ );
2174
+ }
2175
+ }
2176
+ if (!serviceConfig.collections && nextlyConfig?.collections) {
2177
+ serviceConfig.collections = nextlyConfig.collections;
2178
+ if (nextlyConfig.collections.length > 0) {
2179
+ console.log(
2180
+ `[Nextly] Using ${nextlyConfig.collections.length} collection(s) from config`
2181
+ );
2182
+ }
2183
+ }
2184
+ if (!serviceConfig.singles && nextlyConfig?.singles) {
2185
+ serviceConfig.singles = nextlyConfig.singles;
2186
+ if (nextlyConfig.singles.length > 0) {
2187
+ console.log(
2188
+ `[Nextly] Using ${nextlyConfig.singles.length} single(s) from config`
2189
+ );
2190
+ }
2191
+ }
2192
+ if (!serviceConfig.components && nextlyConfig?.components) {
2193
+ serviceConfig.components = nextlyConfig.components;
2194
+ if (nextlyConfig.components.length > 0) {
2195
+ console.log(
2196
+ `[Nextly] Using ${nextlyConfig.components.length} component(s) from config`
2197
+ );
2198
+ }
2199
+ }
2200
+ if (!serviceConfig.plugins && nextlyConfig?.plugins) {
2201
+ serviceConfig.plugins = nextlyConfig.plugins;
2202
+ if (nextlyConfig.plugins.length > 0) {
2203
+ console.log(
2204
+ `[Nextly] Using ${nextlyConfig.plugins.length} plugin(s) from config`
2205
+ );
2206
+ }
2207
+ }
2208
+ if (!serviceConfig.users && nextlyConfig?.users) {
2209
+ serviceConfig.users = nextlyConfig.users;
2210
+ }
2211
+ if (!serviceConfig.email && nextlyConfig?.email) {
2212
+ serviceConfig.email = nextlyConfig.email;
2213
+ }
2214
+ if (!serviceConfig.apiKeys && nextlyConfig?.apiKeys) {
2215
+ serviceConfig.apiKeys = nextlyConfig.apiKeys;
2216
+ }
2217
+ if (!serviceConfig.security && nextlyConfig?.security) {
2218
+ serviceConfig.security = nextlyConfig.security;
2219
+ }
2220
+ if (!serviceConfig.admin && nextlyConfig?.admin) {
2221
+ serviceConfig.admin = nextlyConfig.admin;
2222
+ }
2223
+ if (!serviceConfig.auth && nextlyConfig?.auth) {
2224
+ serviceConfig.auth = nextlyConfig.auth;
2225
+ }
2226
+ }
2227
+ if (!serviceConfig.imageProcessor) {
2228
+ serviceConfig.imageProcessor = getImageProcessor();
2229
+ }
2230
+ return serviceConfig;
2231
+ }
2232
+
2233
+ // src/init/drift-check.ts
2234
+ async function runDriftCheck(args) {
2235
+ const { adapter, collections, logger } = args;
2236
+ const deps = await resolveDeps(args.deps);
2237
+ if (collections.length === 0) return { kind: "clean" };
2238
+ const previewPromises = collections.map(async (collection) => {
2239
+ try {
2240
+ const preview = await deps.previewDesiredSchema({
2241
+ desired: {
2242
+ collections: {
2243
+ [collection.slug]: collection
2244
+ },
2245
+ singles: {},
2246
+ components: {}
2247
+ },
2248
+ db: adapter.getDrizzle(),
2249
+ dialect: adapter.dialect
2250
+ });
2251
+ return { ok: true, opCount: preview.operations.length };
2252
+ } catch (err) {
2253
+ const msg = err instanceof Error ? err.message : String(err);
2254
+ logger.debug?.(
2255
+ `[nextly] Drift preview failed for '${collection.slug}': ${msg} (continuing).`
2256
+ );
2257
+ return { ok: false, error: msg };
2258
+ }
2259
+ });
2260
+ const results = await Promise.all(previewPromises);
2261
+ let pendingOps = 0;
2262
+ let failureCount = 0;
2263
+ for (const r of results) {
2264
+ if (r.ok) pendingOps += r.opCount;
2265
+ else failureCount += 1;
2266
+ }
2267
+ if (failureCount > 0) {
2268
+ logger.warn(
2269
+ `[nextly] ${failureCount} of ${collections.length} drift previews failed. Run \`nextly db:sync\` to investigate.`
2270
+ );
2271
+ }
2272
+ if (pendingOps === 0) return { kind: "clean" };
2273
+ logger.warn(
2274
+ `[nextly] Detected schema drift: ${pendingOps} pending change(s). Save your nextly.config.ts to apply via HMR, or run \`nextly db:sync\`.`
2275
+ );
2276
+ return { kind: "drift", pending: pendingOps };
2277
+ }
2278
+ async function resolveDeps(injected) {
2279
+ if (injected?.previewDesiredSchema) {
2280
+ return injected;
2281
+ }
2282
+ const { previewDesiredSchema } = await import("./preview-ZZTR3QGS.mjs");
2283
+ return {
2284
+ previewDesiredSchema: injected?.previewDesiredSchema ?? previewDesiredSchema
2285
+ };
2286
+ }
2287
+
2288
+ // src/init/post-init-tasks.ts
2289
+ async function runPostInitTasks() {
2290
+ try {
2291
+ const emailTemplateService = getService("emailTemplateService");
2292
+ await emailTemplateService.ensureBuiltInTemplates();
2293
+ } catch {
2294
+ }
2295
+ try {
2296
+ const fieldDefService = getService("userFieldDefinitionService");
2297
+ const config = getService("config");
2298
+ const codeFields = config.users?.fields || [];
2299
+ await fieldDefService.syncCodeFields(
2300
+ codeFields
2301
+ );
2302
+ } catch {
2303
+ }
2304
+ try {
2305
+ const userExtSchemaService = getService("userExtSchemaService");
2306
+ await userExtSchemaService.loadMergedFields();
2307
+ if (userExtSchemaService.hasMergedFields()) {
2308
+ const adapter = getService("adapter");
2309
+ const drizzleDb = adapter.getDrizzle();
2310
+ await userExtSchemaService.ensureUserExtSchema(drizzleDb);
2311
+ }
2312
+ } catch {
2313
+ }
2314
+ try {
2315
+ const permissionSeedService = getService("permissionSeedService");
2316
+ const systemResult = await permissionSeedService.seedSystemPermissions();
2317
+ const collectionResult = await permissionSeedService.seedAllCollectionPermissions();
2318
+ const singleResult = await permissionSeedService.seedAllSinglePermissions();
2319
+ const allNewIds = [
2320
+ ...systemResult.newPermissionIds,
2321
+ ...collectionResult.newPermissionIds,
2322
+ ...singleResult.newPermissionIds
2323
+ ];
2324
+ if (allNewIds.length > 0) {
2325
+ await permissionSeedService.assignNewPermissionsToSuperAdmin(allNewIds);
2326
+ }
2327
+ } catch {
2328
+ }
2329
+ }
2330
+
2331
+ // src/runtime/hmr-listener.ts
2332
+ import { WebSocket } from "ws";
2333
+ var g = globalThis;
2334
+ function ensureHmrListener() {
2335
+ if (g.__nextly_hmrWs) return;
2336
+ if (process.env.NODE_ENV === "production") return;
2337
+ if (process.env.NODE_ENV === "test") return;
2338
+ if (process.env.NEXTLY_DISABLE_HMR === "1") return;
2339
+ try {
2340
+ const port = process.env.PORT ?? "3000";
2341
+ const hasHttps = process.env.USE_HTTPS === "true" || process.argv.includes("--experimental-https");
2342
+ const protocol = hasHttps ? "wss" : "ws";
2343
+ const prefix = process.env.__NEXT_ASSET_PREFIX ?? "";
2344
+ const url = process.env.NEXTLY_HMR_URL_OVERRIDE ?? `${protocol}://localhost:${port}${prefix}/_next/webpack-hmr`;
2345
+ g.__nextly_hmrWs = new WebSocket(url);
2346
+ g.__nextly_hmrWs.onmessage = (event) => {
2347
+ if (g.__nextly_hmrReload instanceof Promise) return;
2348
+ if (typeof event.data !== "string") return;
2349
+ let data;
2350
+ try {
2351
+ data = JSON.parse(event.data);
2352
+ } catch {
2353
+ return;
2354
+ }
2355
+ if (typeof data !== "object" || data === null) return;
2356
+ const record = data;
2357
+ const isServerChange = record.type === "serverComponentChanges" || record.action === "serverComponentChanges";
2358
+ if (isServerChange) {
2359
+ if (!(g.__nextly_hmrReload instanceof Promise)) {
2360
+ const reload = (async () => {
2361
+ try {
2362
+ const { reloadNextlyConfig: reloadNextlyConfig2 } = await import("./reload-config-HWQ4G5MM.mjs");
2363
+ await reloadNextlyConfig2();
2364
+ } catch {
2365
+ }
2366
+ })();
2367
+ markHmrReloadInFlight(reload);
2368
+ }
2369
+ }
2370
+ };
2371
+ g.__nextly_hmrWs.onerror = () => {
2372
+ };
2373
+ } catch {
2374
+ }
2375
+ }
2376
+ function consumeHmrReloadFlag() {
2377
+ if (g.__nextly_hmrReload === true) {
2378
+ g.__nextly_hmrReload = false;
2379
+ return true;
2380
+ }
2381
+ return false;
2382
+ }
2383
+ function markHmrReloadInFlight(promise) {
2384
+ g.__nextly_hmrReload = promise;
2385
+ void promise.finally(() => {
2386
+ if (g.__nextly_hmrReload === promise) {
2387
+ g.__nextly_hmrReload = false;
2388
+ }
2389
+ });
2390
+ }
2391
+
2392
+ // src/init.ts
2393
+ var globalForInit = globalThis;
2394
+ async function getNextly2(options) {
2395
+ if (!options?.config) {
2396
+ throw new NextlyError({
2397
+ code: "CONFIGURATION_ERROR",
2398
+ statusCode: 500,
2399
+ publicMessage: "Server configuration error.",
2400
+ logMessage: "getNextly() requires a `config` parameter. Import your config and pass it: `getNextly({ config })`. If you are inside a Nextly internal handler that just needs the cached singleton, use `getCachedNextly()` instead."
2401
+ });
2402
+ }
2403
+ if (globalForInit.__nextly_cachedInstance) {
2404
+ if (consumeHmrReloadFlag()) {
2405
+ const reloadPromise = reloadNextlyConfig();
2406
+ markHmrReloadInFlight(reloadPromise);
2407
+ await reloadPromise;
2408
+ }
2409
+ if (options?.config?.storage && options.config.storage.length > 0) {
2410
+ const mediaStorage = getService("mediaStorage");
2411
+ if (!mediaStorage.hasAdapter()) {
2412
+ for (const plugin of options.config.storage) {
2413
+ mediaStorage.registerPlugin(plugin);
2414
+ }
2415
+ console.log(
2416
+ `[Nextly] Late-registered ${options.config.storage.length} storage plugin(s)`
2417
+ );
2418
+ }
2419
+ }
2420
+ return globalForInit.__nextly_cachedInstance;
2421
+ }
2422
+ if (globalForInit.__nextly_initPromise) {
2423
+ return globalForInit.__nextly_initPromise;
2424
+ }
2425
+ const initPromise = (async () => {
2426
+ try {
2427
+ if (!isServicesRegistered()) {
2428
+ const finalConfig = buildServiceConfig(options);
2429
+ await registerServices(finalConfig);
2430
+ const adapter = getService("adapter");
2431
+ const capabilities = adapter.getCapabilities();
2432
+ console.log(`Nextly initialized with ${capabilities.dialect} database`);
2433
+ console.log(` - JSONB support: ${capabilities.supportsJsonb}`);
2434
+ console.log(` - RETURNING support: ${capabilities.supportsReturning}`);
2435
+ console.log(` - Full-text search: ${capabilities.supportsFts}`);
2436
+ const config = getService("config");
2437
+ const driftLogger = {
2438
+ debug: (msg) => console.debug(msg),
2439
+ info: (msg) => console.log(msg),
2440
+ warn: (msg) => console.warn(msg),
2441
+ error: (msg) => console.error(msg)
2442
+ };
2443
+ const collections = (config.collections ?? []).map((c) => ({
2444
+ slug: c.slug,
2445
+ tableName: resolveCollectionTableName(c.slug, c.dbName),
2446
+ fields: c.fields ?? [],
2447
+ // Why: forward the Draft/Published flag so the boot-time drift
2448
+ // check actually compares the desired status column against the
2449
+ // live DB. Without this, a code-first collection that opted into
2450
+ // `status: true` after its table was created would never report
2451
+ // drift, masking a missing system status column.
2452
+ status: c.status === true
2453
+ }));
2454
+ await runDriftCheck({
2455
+ adapter,
2456
+ collections,
2457
+ logger: driftLogger
2458
+ });
2459
+ await runBootTimeApplyIfDev({ caller: "init" });
2460
+ void runPostInitTasks();
2461
+ }
2462
+ const directAPI = getNextly();
2463
+ const instance = {
2464
+ // Direct API methods
2465
+ find: directAPI.find.bind(directAPI),
2466
+ findByID: directAPI.findByID.bind(directAPI),
2467
+ create: directAPI.create.bind(directAPI),
2468
+ update: directAPI.update.bind(directAPI),
2469
+ delete: directAPI.delete.bind(directAPI),
2470
+ count: directAPI.count.bind(directAPI),
2471
+ bulkDelete: directAPI.bulkDelete.bind(directAPI),
2472
+ duplicate: directAPI.duplicate.bind(directAPI),
2473
+ findSingle: directAPI.findSingle.bind(directAPI),
2474
+ updateSingle: directAPI.updateSingle.bind(directAPI),
2475
+ findSingles: directAPI.findSingles.bind(directAPI),
2476
+ // Authentication methods
2477
+ login: directAPI.login.bind(directAPI),
2478
+ logout: directAPI.logout.bind(directAPI),
2479
+ me: directAPI.me.bind(directAPI),
2480
+ updateMe: directAPI.updateMe.bind(directAPI),
2481
+ register: directAPI.register.bind(directAPI),
2482
+ changePassword: directAPI.changePassword.bind(directAPI),
2483
+ forgotPassword: directAPI.forgotPassword.bind(directAPI),
2484
+ resetPassword: directAPI.resetPassword.bind(directAPI),
2485
+ verifyEmail: directAPI.verifyEmail.bind(directAPI),
2486
+ // Users API namespace (Direct API style)
2487
+ users: directAPI.users,
2488
+ // Media API namespace (Direct API style)
2489
+ media: directAPI.media,
2490
+ // Forms API namespace (Direct API style)
2491
+ forms: directAPI.forms,
2492
+ // Email & User Field namespaces
2493
+ emailProviders: directAPI.emailProviders,
2494
+ emailTemplates: directAPI.emailTemplates,
2495
+ userFields: directAPI.userFields,
2496
+ email: directAPI.email,
2497
+ // RBAC namespaces
2498
+ roles: directAPI.roles,
2499
+ permissions: directAPI.permissions,
2500
+ access: directAPI.access,
2501
+ // Service accessors
2502
+ collections: getService("collectionService"),
2503
+ userService: getService("userService"),
2504
+ mediaService: getService("mediaService"),
2505
+ storage: getService("mediaStorage"),
2506
+ meta: getService("metaService"),
2507
+ // Direct adapter access
2508
+ adapter: getService("adapter"),
2509
+ // Shutdown method
2510
+ shutdown: async () => {
2511
+ await shutdownServices();
2512
+ globalForInit.__nextly_cachedInstance = null;
2513
+ console.log("Nextly shutdown complete");
2514
+ }
2515
+ };
2516
+ globalForInit.__nextly_cachedInstance = instance;
2517
+ ensureHmrListener();
2518
+ return instance;
2519
+ } finally {
2520
+ globalForInit.__nextly_initPromise = null;
2521
+ }
2522
+ })();
2523
+ globalForInit.__nextly_initPromise = initPromise;
2524
+ return initPromise;
2525
+ }
2526
+ async function getCachedNextly() {
2527
+ if (globalForInit.__nextly_cachedInstance) {
2528
+ return globalForInit.__nextly_cachedInstance;
2529
+ }
2530
+ if (globalForInit.__nextly_initPromise) {
2531
+ return globalForInit.__nextly_initPromise;
2532
+ }
2533
+ if (isServicesRegistered()) {
2534
+ const directAPI = getNextly();
2535
+ const instance = {
2536
+ find: directAPI.find.bind(directAPI),
2537
+ findByID: directAPI.findByID.bind(directAPI),
2538
+ create: directAPI.create.bind(directAPI),
2539
+ update: directAPI.update.bind(directAPI),
2540
+ delete: directAPI.delete.bind(directAPI),
2541
+ count: directAPI.count.bind(directAPI),
2542
+ bulkDelete: directAPI.bulkDelete.bind(directAPI),
2543
+ duplicate: directAPI.duplicate.bind(directAPI),
2544
+ findSingle: directAPI.findSingle.bind(directAPI),
2545
+ updateSingle: directAPI.updateSingle.bind(directAPI),
2546
+ findSingles: directAPI.findSingles.bind(directAPI),
2547
+ login: directAPI.login.bind(directAPI),
2548
+ logout: directAPI.logout.bind(directAPI),
2549
+ me: directAPI.me.bind(directAPI),
2550
+ updateMe: directAPI.updateMe.bind(directAPI),
2551
+ register: directAPI.register.bind(directAPI),
2552
+ changePassword: directAPI.changePassword.bind(directAPI),
2553
+ forgotPassword: directAPI.forgotPassword.bind(directAPI),
2554
+ resetPassword: directAPI.resetPassword.bind(directAPI),
2555
+ verifyEmail: directAPI.verifyEmail.bind(directAPI),
2556
+ users: directAPI.users,
2557
+ media: directAPI.media,
2558
+ forms: directAPI.forms,
2559
+ emailProviders: directAPI.emailProviders,
2560
+ emailTemplates: directAPI.emailTemplates,
2561
+ userFields: directAPI.userFields,
2562
+ email: directAPI.email,
2563
+ roles: directAPI.roles,
2564
+ permissions: directAPI.permissions,
2565
+ access: directAPI.access,
2566
+ collections: getService("collectionService"),
2567
+ userService: getService("userService"),
2568
+ mediaService: getService("mediaService"),
2569
+ storage: getService("mediaStorage"),
2570
+ meta: getService("metaService"),
2571
+ adapter: getService("adapter"),
2572
+ shutdown: async () => {
2573
+ await shutdownServices();
2574
+ globalForInit.__nextly_cachedInstance = null;
2575
+ }
2576
+ };
2577
+ globalForInit.__nextly_cachedInstance = instance;
2578
+ return instance;
2579
+ }
2580
+ throw new NextlyError({
2581
+ code: "CONFIGURATION_ERROR",
2582
+ statusCode: 500,
2583
+ publicMessage: "Server configuration error.",
2584
+ logMessage: "getCachedNextly() called before initialization. Ensure `getNextly({ config })` (or `createRegister(config)` from instrumentation.ts) has run at least once before any internal handler executes. See https://nextlyhq.com/docs/getting-started."
2585
+ });
2586
+ }
2587
+ function createRegister(config) {
2588
+ return async function register2() {
2589
+ await getNextly2({ config });
2590
+ };
2591
+ }
2592
+ async function shutdownNextly() {
2593
+ if (globalForInit.__nextly_cachedInstance) {
2594
+ await globalForInit.__nextly_cachedInstance.shutdown();
2595
+ }
2596
+ }
2597
+
2598
+ export {
2599
+ nextly,
2600
+ ensureHmrListener,
2601
+ getNextly2 as getNextly,
2602
+ getCachedNextly,
2603
+ createRegister,
2604
+ shutdownNextly
2605
+ };