@kyro-cms/core 0.1.6 → 0.1.8

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 (185) hide show
  1. package/README.md +25 -2
  2. package/dist/WebhookService-CXJ5oz6L.d.ts +112 -0
  3. package/dist/WebhookService-Dqx9Is7m.d.cts +112 -0
  4. package/dist/{base-CQkFzqQl.d.ts → base-CciYzoDF.d.ts} +1 -1
  5. package/dist/{base-DlhVlwnN.d.cts → base-Cfek4fp3.d.cts} +1 -1
  6. package/dist/bootstrap-BMWVB2T6.cjs +31 -0
  7. package/dist/{bootstrap-WMWQ4DBX.cjs.map → bootstrap-BMWVB2T6.cjs.map} +1 -1
  8. package/dist/bootstrap-LL6O7PWO.js +6 -0
  9. package/dist/{bootstrap-WOVGAKZP.js.map → bootstrap-LL6O7PWO.js.map} +1 -1
  10. package/dist/{chunk-3VZCX4DF.cjs → chunk-42JPONZU.cjs} +77 -14
  11. package/dist/chunk-42JPONZU.cjs.map +1 -0
  12. package/dist/{chunk-3EVLFWH2.cjs → chunk-4M5PHMUE.cjs} +60 -346
  13. package/dist/chunk-4M5PHMUE.cjs.map +1 -0
  14. package/dist/chunk-4PWRCMTQ.cjs +15 -0
  15. package/dist/chunk-4PWRCMTQ.cjs.map +1 -0
  16. package/dist/chunk-6COM32WF.js +47 -0
  17. package/dist/chunk-6COM32WF.js.map +1 -0
  18. package/dist/chunk-6MSSF46R.js +941 -0
  19. package/dist/chunk-6MSSF46R.js.map +1 -0
  20. package/dist/{chunk-TZFJMPCH.cjs → chunk-7YITG2US.cjs} +9 -18
  21. package/dist/chunk-7YITG2US.cjs.map +1 -0
  22. package/dist/{chunk-A3RQWHKD.cjs → chunk-BLMFBDBG.cjs} +56 -6
  23. package/dist/chunk-BLMFBDBG.cjs.map +1 -0
  24. package/dist/{chunk-EINVJPFM.js → chunk-BTOE3VUK.js} +65 -3
  25. package/dist/chunk-BTOE3VUK.js.map +1 -0
  26. package/dist/chunk-E5X75WNB.js +497 -0
  27. package/dist/chunk-E5X75WNB.js.map +1 -0
  28. package/dist/chunk-E63IF3MD.cjs +951 -0
  29. package/dist/chunk-E63IF3MD.cjs.map +1 -0
  30. package/dist/chunk-EWP5AT6A.cjs +268 -0
  31. package/dist/chunk-EWP5AT6A.cjs.map +1 -0
  32. package/dist/{chunk-K7QF2QCM.cjs → chunk-FTSSDDZQ.cjs} +7 -3
  33. package/dist/chunk-FTSSDDZQ.cjs.map +1 -0
  34. package/dist/chunk-G7VZBCD6.cjs +35 -0
  35. package/dist/{chunk-5BLDMQED.cjs.map → chunk-G7VZBCD6.cjs.map} +1 -1
  36. package/dist/{chunk-VMSRTAH7.js → chunk-GLCPGZPM.js} +56 -6
  37. package/dist/chunk-GLCPGZPM.js.map +1 -0
  38. package/dist/{chunk-V3B25QOK.cjs → chunk-GVFB5C6O.cjs} +74 -2
  39. package/dist/chunk-GVFB5C6O.cjs.map +1 -0
  40. package/dist/chunk-HVSQDZZJ.cjs +765 -0
  41. package/dist/chunk-HVSQDZZJ.cjs.map +1 -0
  42. package/dist/chunk-HYC4GNHX.js +758 -0
  43. package/dist/chunk-HYC4GNHX.js.map +1 -0
  44. package/dist/chunk-KDVDIZ4Y.cjs +3479 -0
  45. package/dist/chunk-KDVDIZ4Y.cjs.map +1 -0
  46. package/dist/{chunk-OG3KX56O.js → chunk-KWGNR4HM.js} +7 -3
  47. package/dist/chunk-KWGNR4HM.js.map +1 -0
  48. package/dist/chunk-LIJVWQKU.cjs +256 -0
  49. package/dist/chunk-LIJVWQKU.cjs.map +1 -0
  50. package/dist/{chunk-XTZSUDSI.js → chunk-LTRCYJAG.js} +3 -18
  51. package/dist/chunk-LTRCYJAG.js.map +1 -0
  52. package/dist/{chunk-UEYC46RL.js → chunk-OUGKLCYF.js} +71 -8
  53. package/dist/chunk-OUGKLCYF.js.map +1 -0
  54. package/dist/chunk-QKOFKITP.js +258 -0
  55. package/dist/chunk-QKOFKITP.js.map +1 -0
  56. package/dist/chunk-RONAX6UU.js +3456 -0
  57. package/dist/chunk-RONAX6UU.js.map +1 -0
  58. package/dist/{chunk-5Y7QGIHD.js → chunk-RRYXQMZG.js} +60 -344
  59. package/dist/chunk-RRYXQMZG.js.map +1 -0
  60. package/dist/{chunk-QUJ4OLSC.js → chunk-U74F3YZU.js} +87 -7
  61. package/dist/chunk-U74F3YZU.js.map +1 -0
  62. package/dist/chunk-VIONYQ2K.cjs +517 -0
  63. package/dist/chunk-VIONYQ2K.cjs.map +1 -0
  64. package/dist/chunk-VSTRLXMQ.cjs +50 -0
  65. package/dist/chunk-VSTRLXMQ.cjs.map +1 -0
  66. package/dist/chunk-YT7HXXVN.js +13 -0
  67. package/dist/chunk-YT7HXXVN.js.map +1 -0
  68. package/dist/chunk-Z6ZWNWWR.js +30 -0
  69. package/dist/{chunk-NSBPE2FW.js.map → chunk-Z6ZWNWWR.js.map} +1 -1
  70. package/dist/cli/index.cjs +11 -7
  71. package/dist/cli/index.cjs.map +1 -1
  72. package/dist/cli/index.js +11 -7
  73. package/dist/cli/index.js.map +1 -1
  74. package/dist/client.cjs +45 -0
  75. package/dist/client.cjs.map +1 -0
  76. package/dist/client.d.cts +11 -0
  77. package/dist/client.d.ts +11 -0
  78. package/dist/client.js +4 -0
  79. package/dist/client.js.map +1 -0
  80. package/dist/drizzle/index.cjs +20 -17
  81. package/dist/drizzle/index.d.cts +115 -5
  82. package/dist/drizzle/index.d.ts +115 -5
  83. package/dist/drizzle/index.js +4 -5
  84. package/dist/graphql/index.cjs +4 -4
  85. package/dist/graphql/index.d.cts +3 -2
  86. package/dist/graphql/index.d.ts +3 -2
  87. package/dist/graphql/index.js +2 -2
  88. package/dist/{index-4fJKLFK2.d.ts → index-BvZ1iWm2.d.ts} +1 -1
  89. package/dist/{index-DI0DRPNv.d.cts → index-CTLPjpMH.d.cts} +1 -1
  90. package/dist/index.cjs +3391 -649
  91. package/dist/index.cjs.map +1 -1
  92. package/dist/index.d.cts +368 -309
  93. package/dist/index.d.ts +368 -309
  94. package/dist/index.js +3253 -599
  95. package/dist/index.js.map +1 -1
  96. package/dist/media-HOT3O7RW.js +4 -0
  97. package/dist/media-HOT3O7RW.js.map +1 -0
  98. package/dist/media-WKP5AOX2.cjs +17 -0
  99. package/dist/media-WKP5AOX2.cjs.map +1 -0
  100. package/dist/mongodb/index.cjs +1 -1
  101. package/dist/mongodb/index.d.cts +2 -2
  102. package/dist/mongodb/index.d.ts +2 -2
  103. package/dist/mongodb/index.js +1 -1
  104. package/dist/mysql-media-AI6YK767.cjs +48 -0
  105. package/dist/mysql-media-AI6YK767.cjs.map +1 -0
  106. package/dist/mysql-media-CDZUS7YX.js +45 -0
  107. package/dist/mysql-media-CDZUS7YX.js.map +1 -0
  108. package/dist/postgres-auth-adapter-EVRPO7BQ.cjs +14 -0
  109. package/dist/{postgres-auth-adapter-VK6GY7LX.cjs.map → postgres-auth-adapter-EVRPO7BQ.cjs.map} +1 -1
  110. package/dist/postgres-auth-adapter-OTRWSTT5.js +5 -0
  111. package/dist/{postgres-auth-adapter-REJFUMP7.js.map → postgres-auth-adapter-OTRWSTT5.js.map} +1 -1
  112. package/dist/rate-limit-BvUAVCzw.d.cts +223 -0
  113. package/dist/rate-limit-CJnqG1mG.d.ts +223 -0
  114. package/dist/redis-adapter-2N6VA7BI.cjs +13 -0
  115. package/dist/{redis-adapter-LBLNKGNS.cjs.map → redis-adapter-2N6VA7BI.cjs.map} +1 -1
  116. package/dist/redis-adapter-RA24FNCX.js +4 -0
  117. package/dist/{redis-adapter-4YDY4LWE.js.map → redis-adapter-RA24FNCX.js.map} +1 -1
  118. package/dist/rest/index.cjs +7 -5
  119. package/dist/rest/index.d.cts +29 -3
  120. package/dist/rest/index.d.ts +29 -3
  121. package/dist/rest/index.js +5 -3
  122. package/dist/schema-CNB2DDTX.js +6 -0
  123. package/dist/schema-CNB2DDTX.js.map +1 -0
  124. package/dist/schema-Y777CQQS.cjs +67 -0
  125. package/dist/schema-Y777CQQS.cjs.map +1 -0
  126. package/dist/templates/index.cjs +24 -28
  127. package/dist/templates/index.d.cts +2 -4
  128. package/dist/templates/index.d.ts +2 -4
  129. package/dist/templates/index.js +2 -2
  130. package/dist/trpc/index.cjs +12 -12
  131. package/dist/trpc/index.d.cts +19 -14
  132. package/dist/trpc/index.d.ts +19 -14
  133. package/dist/trpc/index.js +3 -3
  134. package/dist/types-BSR91JFN.d.cts +159 -0
  135. package/dist/types-BSR91JFN.d.ts +159 -0
  136. package/dist/{types-BGM5MV_K.d.ts → types-Bt1OEk0S.d.cts} +78 -38
  137. package/dist/{types-BGM5MV_K.d.cts → types-Bt1OEk0S.d.ts} +78 -38
  138. package/dist/ws/index.cjs +1 -1
  139. package/dist/ws/index.js +1 -1
  140. package/package.json +19 -2
  141. package/dist/bootstrap-WMWQ4DBX.cjs +0 -29
  142. package/dist/bootstrap-WOVGAKZP.js +0 -4
  143. package/dist/chunk-3EVLFWH2.cjs.map +0 -1
  144. package/dist/chunk-3QX6KG2S.js +0 -2125
  145. package/dist/chunk-3QX6KG2S.js.map +0 -1
  146. package/dist/chunk-3VZCX4DF.cjs.map +0 -1
  147. package/dist/chunk-5BLDMQED.cjs +0 -18
  148. package/dist/chunk-5Y7QGIHD.js.map +0 -1
  149. package/dist/chunk-7G6EVYCU.cjs +0 -94
  150. package/dist/chunk-7G6EVYCU.cjs.map +0 -1
  151. package/dist/chunk-A3RQWHKD.cjs.map +0 -1
  152. package/dist/chunk-EINVJPFM.js.map +0 -1
  153. package/dist/chunk-F5B64H5S.cjs +0 -2149
  154. package/dist/chunk-F5B64H5S.cjs.map +0 -1
  155. package/dist/chunk-K7QF2QCM.cjs.map +0 -1
  156. package/dist/chunk-LRTZJJPD.js +0 -86
  157. package/dist/chunk-LRTZJJPD.js.map +0 -1
  158. package/dist/chunk-NSBPE2FW.js +0 -15
  159. package/dist/chunk-OG3KX56O.js.map +0 -1
  160. package/dist/chunk-QUJ4OLSC.js.map +0 -1
  161. package/dist/chunk-R3XIBBAW.cjs +0 -34
  162. package/dist/chunk-R3XIBBAW.cjs.map +0 -1
  163. package/dist/chunk-SDMNUYVU.js +0 -30
  164. package/dist/chunk-SDMNUYVU.js.map +0 -1
  165. package/dist/chunk-TZFJMPCH.cjs.map +0 -1
  166. package/dist/chunk-UEG7KMKC.cjs +0 -228
  167. package/dist/chunk-UEG7KMKC.cjs.map +0 -1
  168. package/dist/chunk-UEYC46RL.js.map +0 -1
  169. package/dist/chunk-V3B25QOK.cjs.map +0 -1
  170. package/dist/chunk-VMSRTAH7.js.map +0 -1
  171. package/dist/chunk-XTZSUDSI.js.map +0 -1
  172. package/dist/chunk-YD7Y25W7.cjs +0 -176
  173. package/dist/chunk-YD7Y25W7.cjs.map +0 -1
  174. package/dist/chunk-YPAFJ7EV.js +0 -225
  175. package/dist/chunk-YPAFJ7EV.js.map +0 -1
  176. package/dist/database-7CJOXEZR.js +0 -5
  177. package/dist/database-7CJOXEZR.js.map +0 -1
  178. package/dist/database-QOIV44GT.cjs +0 -22
  179. package/dist/database-QOIV44GT.cjs.map +0 -1
  180. package/dist/index-BMySjW6o.d.cts +0 -198
  181. package/dist/index-CMUNCIWQ.d.ts +0 -198
  182. package/dist/postgres-auth-adapter-REJFUMP7.js +0 -5
  183. package/dist/postgres-auth-adapter-VK6GY7LX.cjs +0 -14
  184. package/dist/redis-adapter-4YDY4LWE.js +0 -4
  185. package/dist/redis-adapter-LBLNKGNS.cjs +0 -13
package/dist/index.js CHANGED
@@ -1,29 +1,1025 @@
1
- export { allSettingsGlobals, blogCollections, blogGlobals, coreSettingsGlobals, createTemplateConfig, ecommerceCollections, ecommerceGlobals, ecommerceSettingsGlobals, kitchenSinkCollections, mediaCollections, minimalCollections } from './chunk-3QX6KG2S.js';
2
- export { RedisAuthAdapter } from './chunk-VMSRTAH7.js';
3
- import { EmailTransport, PasswordPolicy, SQLiteAuthAdapter } from './chunk-5Y7QGIHD.js';
4
- export { EmailTransport, PasswordPolicy, SQLiteAuthAdapter, autoBootstrap, bootstrapAdmin, getBootstrapFromEnv } from './chunk-5Y7QGIHD.js';
5
- import { createKyroServer } from './chunk-UEYC46RL.js';
6
- export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure } from './chunk-UEYC46RL.js';
7
- import { buildGraphQLSchema } from './chunk-OG3KX56O.js';
8
- export { buildGraphQLSchema, createGraphQLSchema } from './chunk-OG3KX56O.js';
9
- import { createHonoApp } from './chunk-YPAFJ7EV.js';
10
- export { createHonoApp, createRESTAPI } from './chunk-YPAFJ7EV.js';
11
- export { evaluateAccess, getWhereClause, mergeWhereClauses } from './chunk-SDMNUYVU.js';
1
+ export { allSettingsGlobals, blogCollections, blogGlobals, coreSettingsGlobals, createTemplateConfig, ecommerceCollections, ecommerceGlobals, ecommerceSettingsGlobals, kitchenSinkCollections, mediaCollections, minimalCollections } from './chunk-RONAX6UU.js';
2
+ export { RedisAuthAdapter } from './chunk-GLCPGZPM.js';
3
+ import { PasswordPolicy, SQLiteAuthAdapter } from './chunk-RRYXQMZG.js';
4
+ export { PasswordPolicy, SQLiteAuthAdapter, autoBootstrap, bootstrapAdmin, getBootstrapFromEnv } from './chunk-RRYXQMZG.js';
5
+ import { createAuditContext } from './chunk-QKOFKITP.js';
6
+ export { CSSGenerator, createAdminStyling, defaultDarkTheme, defaultFieldStyling, defaultLightTheme, ecommerce2026Theme, generateCSSVariables, generateTailwindConfig } from './chunk-QKOFKITP.js';
7
+ import { createKyroServer } from './chunk-OUGKLCYF.js';
8
+ export { createContext, createCountProcedure, createCreateProcedure, createDeleteProcedure, createDynamicRouter, createFindByIDProcedure, createFindProcedure, createKyroServer, createUpdateProcedure } from './chunk-OUGKLCYF.js';
9
+ import { buildGraphQLSchema } from './chunk-KWGNR4HM.js';
10
+ export { buildGraphQLSchema, createGraphQLSchema } from './chunk-KWGNR4HM.js';
11
+ import { InMemoryRateLimiter, generateToken, defaultExtractToken, createHonoApp } from './chunk-6MSSF46R.js';
12
+ export { InMemoryRateLimiter, createHonoApp, createRESTAPI } from './chunk-6MSSF46R.js';
13
+ import { EmailTransport, ConfigService } from './chunk-HYC4GNHX.js';
14
+ export { ConfigService, EmailTransport } from './chunk-HYC4GNHX.js';
15
+ import './chunk-YT7HXXVN.js';
16
+ export { ALL_WEBHOOK_EVENTS, WEBHOOK_COLLECTION, WEBHOOK_DELIVERY_COLLECTION, WEBHOOK_EVENTS, WebhookService, buildDeliveryRecord, createTestPayload, createWebhookService, deliverWebhook, deliverWithRetry, evaluateAccess, generateWebhookSecret, getWhereClause, mergeWhereClauses, signPayload } from './chunk-E5X75WNB.js';
12
17
  import { KyroPubSub, createWSServer } from './chunk-3TPQ2BU6.js';
13
18
  export { KyroPubSub, KyroWSServer, PubSub, createWSServer } from './chunk-3TPQ2BU6.js';
14
- export { DrizzleAdapter, collectionToDrizzleSchema, createDrizzleAdapter, fieldToDrizzleType } from './chunk-EINVJPFM.js';
15
- export { PostgresAuthAdapter } from './chunk-QUJ4OLSC.js';
16
- export { createDatabase, runMigrations, seedDefaultRoles } from './chunk-LRTZJJPD.js';
17
- import './chunk-XTZSUDSI.js';
19
+ import { genId } from './chunk-BTOE3VUK.js';
20
+ export { DrizzleAdapter, collectionToDrizzleSchema, createDatabase, createDrizzleAdapter, fieldToDrizzleType, runMigrations, seedDefaultRoles } from './chunk-BTOE3VUK.js';
21
+ export { PostgresAuthAdapter } from './chunk-U74F3YZU.js';
22
+ import './chunk-LTRCYJAG.js';
18
23
  export { MongoDBAdapter, createMongoDBAdapter } from './chunk-DIC236EW.js';
19
24
  import { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
20
25
  export { AbstractBaseAdapter } from './chunk-BXMWDUED.js';
21
- import { __require } from './chunk-NSBPE2FW.js';
26
+ import { __esm, __export, __require, __toCommonJS } from './chunk-Z6ZWNWWR.js';
27
+ import { Readable, Writable } from 'stream';
28
+ import { Agent, request } from 'https';
29
+ import http2, { constants } from 'http2';
22
30
  import { z } from 'zod';
23
31
  export { z } from 'zod';
24
32
  import bcrypt from 'bcrypt';
25
- import jwt2 from 'jsonwebtoken';
26
- import { randomBytes } from 'crypto';
33
+ import jwt from 'jsonwebtoken';
34
+ import { randomBytes, createHash } from 'crypto';
35
+ import path, { join, basename, extname } from 'path';
36
+ import { readdir, stat, rename, unlink, writeFile, mkdir } from 'fs/promises';
37
+ import { existsSync } from 'fs';
38
+ import { S3Client, HeadObjectCommand, ListObjectsV2Command, CopyObjectCommand, DeleteObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3';
39
+ import { Client } from 'basic-ftp';
40
+ import sharp from 'sharp';
41
+
42
+ // node_modules/@smithy/protocol-http/dist-es/extensions/httpExtensionConfiguration.js
43
+ var init_httpExtensionConfiguration = __esm({
44
+ "node_modules/@smithy/protocol-http/dist-es/extensions/httpExtensionConfiguration.js"() {
45
+ }
46
+ });
47
+
48
+ // node_modules/@smithy/protocol-http/dist-es/extensions/index.js
49
+ var init_extensions = __esm({
50
+ "node_modules/@smithy/protocol-http/dist-es/extensions/index.js"() {
51
+ init_httpExtensionConfiguration();
52
+ }
53
+ });
54
+
55
+ // node_modules/@smithy/protocol-http/dist-es/Field.js
56
+ var init_Field = __esm({
57
+ "node_modules/@smithy/protocol-http/dist-es/Field.js"() {
58
+ }
59
+ });
60
+
61
+ // node_modules/@smithy/protocol-http/dist-es/Fields.js
62
+ var init_Fields = __esm({
63
+ "node_modules/@smithy/protocol-http/dist-es/Fields.js"() {
64
+ }
65
+ });
66
+
67
+ // node_modules/@smithy/protocol-http/dist-es/httpHandler.js
68
+ var init_httpHandler = __esm({
69
+ "node_modules/@smithy/protocol-http/dist-es/httpHandler.js"() {
70
+ }
71
+ });
72
+
73
+ // node_modules/@smithy/protocol-http/dist-es/httpRequest.js
74
+ var init_httpRequest = __esm({
75
+ "node_modules/@smithy/protocol-http/dist-es/httpRequest.js"() {
76
+ }
77
+ });
78
+
79
+ // node_modules/@smithy/protocol-http/dist-es/httpResponse.js
80
+ var HttpResponse;
81
+ var init_httpResponse = __esm({
82
+ "node_modules/@smithy/protocol-http/dist-es/httpResponse.js"() {
83
+ HttpResponse = class {
84
+ statusCode;
85
+ reason;
86
+ headers;
87
+ body;
88
+ constructor(options) {
89
+ this.statusCode = options.statusCode;
90
+ this.reason = options.reason;
91
+ this.headers = options.headers || {};
92
+ this.body = options.body;
93
+ }
94
+ static isInstance(response) {
95
+ if (!response)
96
+ return false;
97
+ const resp = response;
98
+ return typeof resp.statusCode === "number" && typeof resp.headers === "object";
99
+ }
100
+ };
101
+ }
102
+ });
103
+
104
+ // node_modules/@smithy/protocol-http/dist-es/isValidHostname.js
105
+ var init_isValidHostname = __esm({
106
+ "node_modules/@smithy/protocol-http/dist-es/isValidHostname.js"() {
107
+ }
108
+ });
109
+
110
+ // node_modules/@smithy/protocol-http/dist-es/types.js
111
+ var init_types = __esm({
112
+ "node_modules/@smithy/protocol-http/dist-es/types.js"() {
113
+ }
114
+ });
115
+
116
+ // node_modules/@smithy/protocol-http/dist-es/index.js
117
+ var init_dist_es = __esm({
118
+ "node_modules/@smithy/protocol-http/dist-es/index.js"() {
119
+ init_extensions();
120
+ init_Field();
121
+ init_Fields();
122
+ init_httpHandler();
123
+ init_httpRequest();
124
+ init_httpResponse();
125
+ init_isValidHostname();
126
+ init_types();
127
+ }
128
+ });
129
+
130
+ // node_modules/@smithy/util-uri-escape/dist-es/escape-uri.js
131
+ var escapeUri, hexEncode;
132
+ var init_escape_uri = __esm({
133
+ "node_modules/@smithy/util-uri-escape/dist-es/escape-uri.js"() {
134
+ escapeUri = (uri) => encodeURIComponent(uri).replace(/[!'()*]/g, hexEncode);
135
+ hexEncode = (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`;
136
+ }
137
+ });
138
+
139
+ // node_modules/@smithy/util-uri-escape/dist-es/escape-uri-path.js
140
+ var init_escape_uri_path = __esm({
141
+ "node_modules/@smithy/util-uri-escape/dist-es/escape-uri-path.js"() {
142
+ }
143
+ });
144
+
145
+ // node_modules/@smithy/util-uri-escape/dist-es/index.js
146
+ var init_dist_es2 = __esm({
147
+ "node_modules/@smithy/util-uri-escape/dist-es/index.js"() {
148
+ init_escape_uri();
149
+ init_escape_uri_path();
150
+ }
151
+ });
152
+
153
+ // node_modules/@smithy/querystring-builder/dist-es/index.js
154
+ function buildQueryString(query) {
155
+ const parts = [];
156
+ for (let key of Object.keys(query).sort()) {
157
+ const value = query[key];
158
+ key = escapeUri(key);
159
+ if (Array.isArray(value)) {
160
+ for (let i = 0, iLen = value.length; i < iLen; i++) {
161
+ parts.push(`${key}=${escapeUri(value[i])}`);
162
+ }
163
+ } else {
164
+ let qsEntry = key;
165
+ if (value || typeof value === "string") {
166
+ qsEntry += `=${escapeUri(value)}`;
167
+ }
168
+ parts.push(qsEntry);
169
+ }
170
+ }
171
+ return parts.join("&");
172
+ }
173
+ var init_dist_es3 = __esm({
174
+ "node_modules/@smithy/querystring-builder/dist-es/index.js"() {
175
+ init_dist_es2();
176
+ }
177
+ });
178
+
179
+ // node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js
180
+ function buildAbortError(abortSignal) {
181
+ const reason = abortSignal && typeof abortSignal === "object" && "reason" in abortSignal ? abortSignal.reason : void 0;
182
+ if (reason) {
183
+ if (reason instanceof Error) {
184
+ const abortError3 = new Error("Request aborted");
185
+ abortError3.name = "AbortError";
186
+ abortError3.cause = reason;
187
+ return abortError3;
188
+ }
189
+ const abortError2 = new Error(String(reason));
190
+ abortError2.name = "AbortError";
191
+ return abortError2;
192
+ }
193
+ const abortError = new Error("Request aborted");
194
+ abortError.name = "AbortError";
195
+ return abortError;
196
+ }
197
+ var init_build_abort_error = __esm({
198
+ "node_modules/@smithy/node-http-handler/dist-es/build-abort-error.js"() {
199
+ }
200
+ });
201
+
202
+ // node_modules/@smithy/node-http-handler/dist-es/constants.js
203
+ var NODEJS_TIMEOUT_ERROR_CODES;
204
+ var init_constants = __esm({
205
+ "node_modules/@smithy/node-http-handler/dist-es/constants.js"() {
206
+ NODEJS_TIMEOUT_ERROR_CODES = ["ECONNRESET", "EPIPE", "ETIMEDOUT"];
207
+ }
208
+ });
209
+
210
+ // node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js
211
+ var getTransformedHeaders;
212
+ var init_get_transformed_headers = __esm({
213
+ "node_modules/@smithy/node-http-handler/dist-es/get-transformed-headers.js"() {
214
+ getTransformedHeaders = (headers) => {
215
+ const transformedHeaders = {};
216
+ for (const name of Object.keys(headers)) {
217
+ const headerValues = headers[name];
218
+ transformedHeaders[name] = Array.isArray(headerValues) ? headerValues.join(",") : headerValues;
219
+ }
220
+ return transformedHeaders;
221
+ };
222
+ }
223
+ });
224
+
225
+ // node_modules/@smithy/node-http-handler/dist-es/timing.js
226
+ var timing;
227
+ var init_timing = __esm({
228
+ "node_modules/@smithy/node-http-handler/dist-es/timing.js"() {
229
+ timing = {
230
+ setTimeout: (cb, ms) => setTimeout(cb, ms),
231
+ clearTimeout: (timeoutId) => clearTimeout(timeoutId)
232
+ };
233
+ }
234
+ });
235
+
236
+ // node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js
237
+ var DEFER_EVENT_LISTENER_TIME, setConnectionTimeout;
238
+ var init_set_connection_timeout = __esm({
239
+ "node_modules/@smithy/node-http-handler/dist-es/set-connection-timeout.js"() {
240
+ init_timing();
241
+ DEFER_EVENT_LISTENER_TIME = 1e3;
242
+ setConnectionTimeout = (request, reject, timeoutInMs = 0) => {
243
+ if (!timeoutInMs) {
244
+ return -1;
245
+ }
246
+ const registerTimeout = (offset) => {
247
+ const timeoutId = timing.setTimeout(() => {
248
+ request.destroy();
249
+ reject(Object.assign(new Error(`@smithy/node-http-handler - the request socket did not establish a connection with the server within the configured timeout of ${timeoutInMs} ms.`), {
250
+ name: "TimeoutError"
251
+ }));
252
+ }, timeoutInMs - offset);
253
+ const doWithSocket = (socket) => {
254
+ if (socket?.connecting) {
255
+ socket.on("connect", () => {
256
+ timing.clearTimeout(timeoutId);
257
+ });
258
+ } else {
259
+ timing.clearTimeout(timeoutId);
260
+ }
261
+ };
262
+ if (request.socket) {
263
+ doWithSocket(request.socket);
264
+ } else {
265
+ request.on("socket", doWithSocket);
266
+ }
267
+ };
268
+ if (timeoutInMs < 2e3) {
269
+ registerTimeout(0);
270
+ return 0;
271
+ }
272
+ return timing.setTimeout(registerTimeout.bind(null, DEFER_EVENT_LISTENER_TIME), DEFER_EVENT_LISTENER_TIME);
273
+ };
274
+ }
275
+ });
276
+
277
+ // node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js
278
+ var setRequestTimeout;
279
+ var init_set_request_timeout = __esm({
280
+ "node_modules/@smithy/node-http-handler/dist-es/set-request-timeout.js"() {
281
+ init_timing();
282
+ setRequestTimeout = (req, reject, timeoutInMs = 0, throwOnRequestTimeout, logger) => {
283
+ if (timeoutInMs) {
284
+ return timing.setTimeout(() => {
285
+ let msg = `@smithy/node-http-handler - [${throwOnRequestTimeout ? "ERROR" : "WARN"}] a request has exceeded the configured ${timeoutInMs} ms requestTimeout.`;
286
+ if (throwOnRequestTimeout) {
287
+ const error = Object.assign(new Error(msg), {
288
+ name: "TimeoutError",
289
+ code: "ETIMEDOUT"
290
+ });
291
+ req.destroy(error);
292
+ reject(error);
293
+ } else {
294
+ msg += ` Init client requestHandler with throwOnRequestTimeout=true to turn this into an error.`;
295
+ logger?.warn?.(msg);
296
+ }
297
+ }, timeoutInMs);
298
+ }
299
+ return -1;
300
+ };
301
+ }
302
+ });
303
+
304
+ // node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js
305
+ var DEFER_EVENT_LISTENER_TIME2, setSocketKeepAlive;
306
+ var init_set_socket_keep_alive = __esm({
307
+ "node_modules/@smithy/node-http-handler/dist-es/set-socket-keep-alive.js"() {
308
+ init_timing();
309
+ DEFER_EVENT_LISTENER_TIME2 = 3e3;
310
+ setSocketKeepAlive = (request, { keepAlive, keepAliveMsecs }, deferTimeMs = DEFER_EVENT_LISTENER_TIME2) => {
311
+ if (keepAlive !== true) {
312
+ return -1;
313
+ }
314
+ const registerListener = () => {
315
+ if (request.socket) {
316
+ request.socket.setKeepAlive(keepAlive, keepAliveMsecs || 0);
317
+ } else {
318
+ request.on("socket", (socket) => {
319
+ socket.setKeepAlive(keepAlive, keepAliveMsecs || 0);
320
+ });
321
+ }
322
+ };
323
+ if (deferTimeMs === 0) {
324
+ registerListener();
325
+ return 0;
326
+ }
327
+ return timing.setTimeout(registerListener, deferTimeMs);
328
+ };
329
+ }
330
+ });
331
+
332
+ // node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js
333
+ var DEFER_EVENT_LISTENER_TIME3, setSocketTimeout;
334
+ var init_set_socket_timeout = __esm({
335
+ "node_modules/@smithy/node-http-handler/dist-es/set-socket-timeout.js"() {
336
+ init_timing();
337
+ DEFER_EVENT_LISTENER_TIME3 = 3e3;
338
+ setSocketTimeout = (request, reject, timeoutInMs = 0) => {
339
+ const registerTimeout = (offset) => {
340
+ const timeout = timeoutInMs - offset;
341
+ const onTimeout = () => {
342
+ request.destroy();
343
+ reject(Object.assign(new Error(`@smithy/node-http-handler - the request socket timed out after ${timeoutInMs} ms of inactivity (configured by client requestHandler).`), { name: "TimeoutError" }));
344
+ };
345
+ if (request.socket) {
346
+ request.socket.setTimeout(timeout, onTimeout);
347
+ request.on("close", () => request.socket?.removeListener("timeout", onTimeout));
348
+ } else {
349
+ request.setTimeout(timeout, onTimeout);
350
+ }
351
+ };
352
+ if (0 < timeoutInMs && timeoutInMs < 6e3) {
353
+ registerTimeout(0);
354
+ return 0;
355
+ }
356
+ return timing.setTimeout(registerTimeout.bind(null, timeoutInMs === 0 ? 0 : DEFER_EVENT_LISTENER_TIME3), DEFER_EVENT_LISTENER_TIME3);
357
+ };
358
+ }
359
+ });
360
+ async function writeRequestBody(httpRequest, request, maxContinueTimeoutMs = MIN_WAIT_TIME, externalAgent = false) {
361
+ const headers = request.headers ?? {};
362
+ const expect = headers.Expect || headers.expect;
363
+ let timeoutId = -1;
364
+ let sendBody = true;
365
+ if (!externalAgent && expect === "100-continue") {
366
+ sendBody = await Promise.race([
367
+ new Promise((resolve) => {
368
+ timeoutId = Number(timing.setTimeout(() => resolve(true), Math.max(MIN_WAIT_TIME, maxContinueTimeoutMs)));
369
+ }),
370
+ new Promise((resolve) => {
371
+ httpRequest.on("continue", () => {
372
+ timing.clearTimeout(timeoutId);
373
+ resolve(true);
374
+ });
375
+ httpRequest.on("response", () => {
376
+ timing.clearTimeout(timeoutId);
377
+ resolve(false);
378
+ });
379
+ httpRequest.on("error", () => {
380
+ timing.clearTimeout(timeoutId);
381
+ resolve(false);
382
+ });
383
+ })
384
+ ]);
385
+ }
386
+ if (sendBody) {
387
+ writeBody(httpRequest, request.body);
388
+ }
389
+ }
390
+ function writeBody(httpRequest, body) {
391
+ if (body instanceof Readable) {
392
+ body.pipe(httpRequest);
393
+ return;
394
+ }
395
+ if (body) {
396
+ const isBuffer = Buffer.isBuffer(body);
397
+ const isString = typeof body === "string";
398
+ if (isBuffer || isString) {
399
+ if (isBuffer && body.byteLength === 0) {
400
+ httpRequest.end();
401
+ } else {
402
+ httpRequest.end(body);
403
+ }
404
+ return;
405
+ }
406
+ const uint8 = body;
407
+ if (typeof uint8 === "object" && uint8.buffer && typeof uint8.byteOffset === "number" && typeof uint8.byteLength === "number") {
408
+ httpRequest.end(Buffer.from(uint8.buffer, uint8.byteOffset, uint8.byteLength));
409
+ return;
410
+ }
411
+ httpRequest.end(Buffer.from(body));
412
+ return;
413
+ }
414
+ httpRequest.end();
415
+ }
416
+ var MIN_WAIT_TIME;
417
+ var init_write_request_body = __esm({
418
+ "node_modules/@smithy/node-http-handler/dist-es/write-request-body.js"() {
419
+ init_timing();
420
+ MIN_WAIT_TIME = 6e3;
421
+ }
422
+ });
423
+ var DEFAULT_REQUEST_TIMEOUT, hAgent, hRequest, NodeHttpHandler;
424
+ var init_node_http_handler = __esm({
425
+ "node_modules/@smithy/node-http-handler/dist-es/node-http-handler.js"() {
426
+ init_dist_es();
427
+ init_dist_es3();
428
+ init_build_abort_error();
429
+ init_constants();
430
+ init_get_transformed_headers();
431
+ init_set_connection_timeout();
432
+ init_set_request_timeout();
433
+ init_set_socket_keep_alive();
434
+ init_set_socket_timeout();
435
+ init_timing();
436
+ init_write_request_body();
437
+ DEFAULT_REQUEST_TIMEOUT = 0;
438
+ hAgent = void 0;
439
+ hRequest = void 0;
440
+ NodeHttpHandler = class _NodeHttpHandler {
441
+ config;
442
+ configProvider;
443
+ socketWarningTimestamp = 0;
444
+ externalAgent = false;
445
+ metadata = { handlerProtocol: "http/1.1" };
446
+ static create(instanceOrOptions) {
447
+ if (typeof instanceOrOptions?.handle === "function") {
448
+ return instanceOrOptions;
449
+ }
450
+ return new _NodeHttpHandler(instanceOrOptions);
451
+ }
452
+ static checkSocketUsage(agent, socketWarningTimestamp, logger = console) {
453
+ const { sockets, requests, maxSockets } = agent;
454
+ if (typeof maxSockets !== "number" || maxSockets === Infinity) {
455
+ return socketWarningTimestamp;
456
+ }
457
+ const interval = 15e3;
458
+ if (Date.now() - interval < socketWarningTimestamp) {
459
+ return socketWarningTimestamp;
460
+ }
461
+ if (sockets && requests) {
462
+ for (const origin in sockets) {
463
+ const socketsInUse = sockets[origin]?.length ?? 0;
464
+ const requestsEnqueued = requests[origin]?.length ?? 0;
465
+ if (socketsInUse >= maxSockets && requestsEnqueued >= 2 * maxSockets) {
466
+ logger?.warn?.(`@smithy/node-http-handler:WARN - socket usage at capacity=${socketsInUse} and ${requestsEnqueued} additional requests are enqueued.
467
+ See https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/node-configuring-maxsockets.html
468
+ or increase socketAcquisitionWarningTimeout=(millis) in the NodeHttpHandler config.`);
469
+ return Date.now();
470
+ }
471
+ }
472
+ }
473
+ return socketWarningTimestamp;
474
+ }
475
+ constructor(options) {
476
+ this.configProvider = new Promise((resolve, reject) => {
477
+ if (typeof options === "function") {
478
+ options().then((_options) => {
479
+ resolve(this.resolveDefaultConfig(_options));
480
+ }).catch(reject);
481
+ } else {
482
+ resolve(this.resolveDefaultConfig(options));
483
+ }
484
+ });
485
+ }
486
+ destroy() {
487
+ this.config?.httpAgent?.destroy();
488
+ this.config?.httpsAgent?.destroy();
489
+ }
490
+ async handle(request$1, { abortSignal, requestTimeout } = {}) {
491
+ if (!this.config) {
492
+ this.config = await this.configProvider;
493
+ }
494
+ const config = this.config;
495
+ const isSSL = request$1.protocol === "https:";
496
+ if (!isSSL && !this.config.httpAgent) {
497
+ this.config.httpAgent = await this.config.httpAgentProvider();
498
+ }
499
+ return new Promise((_resolve, _reject) => {
500
+ let writeRequestBodyPromise = void 0;
501
+ const timeouts = [];
502
+ const resolve = async (arg) => {
503
+ await writeRequestBodyPromise;
504
+ timeouts.forEach(timing.clearTimeout);
505
+ _resolve(arg);
506
+ };
507
+ const reject = async (arg) => {
508
+ await writeRequestBodyPromise;
509
+ timeouts.forEach(timing.clearTimeout);
510
+ _reject(arg);
511
+ };
512
+ if (abortSignal?.aborted) {
513
+ const abortError = buildAbortError(abortSignal);
514
+ reject(abortError);
515
+ return;
516
+ }
517
+ const headers = request$1.headers ?? {};
518
+ const expectContinue = (headers.Expect ?? headers.expect) === "100-continue";
519
+ let agent = isSSL ? config.httpsAgent : config.httpAgent;
520
+ if (expectContinue && !this.externalAgent) {
521
+ agent = new (isSSL ? Agent : hAgent)({
522
+ keepAlive: false,
523
+ maxSockets: Infinity
524
+ });
525
+ }
526
+ timeouts.push(timing.setTimeout(() => {
527
+ this.socketWarningTimestamp = _NodeHttpHandler.checkSocketUsage(agent, this.socketWarningTimestamp, config.logger);
528
+ }, config.socketAcquisitionWarningTimeout ?? (config.requestTimeout ?? 2e3) + (config.connectionTimeout ?? 1e3)));
529
+ const queryString = buildQueryString(request$1.query || {});
530
+ let auth = void 0;
531
+ if (request$1.username != null || request$1.password != null) {
532
+ const username = request$1.username ?? "";
533
+ const password = request$1.password ?? "";
534
+ auth = `${username}:${password}`;
535
+ }
536
+ let path2 = request$1.path;
537
+ if (queryString) {
538
+ path2 += `?${queryString}`;
539
+ }
540
+ if (request$1.fragment) {
541
+ path2 += `#${request$1.fragment}`;
542
+ }
543
+ let hostname = request$1.hostname ?? "";
544
+ if (hostname[0] === "[" && hostname.endsWith("]")) {
545
+ hostname = request$1.hostname.slice(1, -1);
546
+ } else {
547
+ hostname = request$1.hostname;
548
+ }
549
+ const nodeHttpsOptions = {
550
+ headers: request$1.headers,
551
+ host: hostname,
552
+ method: request$1.method,
553
+ path: path2,
554
+ port: request$1.port,
555
+ agent,
556
+ auth
557
+ };
558
+ const requestFunc = isSSL ? request : hRequest;
559
+ const req = requestFunc(nodeHttpsOptions, (res) => {
560
+ const httpResponse = new HttpResponse({
561
+ statusCode: res.statusCode || -1,
562
+ reason: res.statusMessage,
563
+ headers: getTransformedHeaders(res.headers),
564
+ body: res
565
+ });
566
+ resolve({ response: httpResponse });
567
+ });
568
+ req.on("error", (err) => {
569
+ if (NODEJS_TIMEOUT_ERROR_CODES.includes(err.code)) {
570
+ reject(Object.assign(err, { name: "TimeoutError" }));
571
+ } else {
572
+ reject(err);
573
+ }
574
+ });
575
+ if (abortSignal) {
576
+ const onAbort = () => {
577
+ req.destroy();
578
+ const abortError = buildAbortError(abortSignal);
579
+ reject(abortError);
580
+ };
581
+ if (typeof abortSignal.addEventListener === "function") {
582
+ const signal = abortSignal;
583
+ signal.addEventListener("abort", onAbort, { once: true });
584
+ req.once("close", () => signal.removeEventListener("abort", onAbort));
585
+ } else {
586
+ abortSignal.onabort = onAbort;
587
+ }
588
+ }
589
+ const effectiveRequestTimeout = requestTimeout ?? config.requestTimeout;
590
+ timeouts.push(setConnectionTimeout(req, reject, config.connectionTimeout));
591
+ timeouts.push(setRequestTimeout(req, reject, effectiveRequestTimeout, config.throwOnRequestTimeout, config.logger ?? console));
592
+ timeouts.push(setSocketTimeout(req, reject, config.socketTimeout));
593
+ const httpAgent = nodeHttpsOptions.agent;
594
+ if (typeof httpAgent === "object" && "keepAlive" in httpAgent) {
595
+ timeouts.push(setSocketKeepAlive(req, {
596
+ keepAlive: httpAgent.keepAlive,
597
+ keepAliveMsecs: httpAgent.keepAliveMsecs
598
+ }));
599
+ }
600
+ writeRequestBodyPromise = writeRequestBody(req, request$1, effectiveRequestTimeout, this.externalAgent).catch((e) => {
601
+ timeouts.forEach(timing.clearTimeout);
602
+ return _reject(e);
603
+ });
604
+ });
605
+ }
606
+ updateHttpClientConfig(key, value) {
607
+ this.config = void 0;
608
+ this.configProvider = this.configProvider.then((config) => {
609
+ return {
610
+ ...config,
611
+ [key]: value
612
+ };
613
+ });
614
+ }
615
+ httpHandlerConfigs() {
616
+ return this.config ?? {};
617
+ }
618
+ resolveDefaultConfig(options) {
619
+ const { requestTimeout, connectionTimeout, socketTimeout, socketAcquisitionWarningTimeout, httpAgent, httpsAgent, throwOnRequestTimeout, logger } = options || {};
620
+ const keepAlive = true;
621
+ const maxSockets = 50;
622
+ return {
623
+ connectionTimeout,
624
+ requestTimeout,
625
+ socketTimeout,
626
+ socketAcquisitionWarningTimeout,
627
+ throwOnRequestTimeout,
628
+ httpAgentProvider: async () => {
629
+ const { Agent, request } = await import('http');
630
+ hRequest = request;
631
+ hAgent = Agent;
632
+ if (httpAgent instanceof hAgent || typeof httpAgent?.destroy === "function") {
633
+ this.externalAgent = true;
634
+ return httpAgent;
635
+ }
636
+ return new hAgent({ keepAlive, maxSockets, ...httpAgent });
637
+ },
638
+ httpsAgent: (() => {
639
+ if (httpsAgent instanceof Agent || typeof httpsAgent?.destroy === "function") {
640
+ this.externalAgent = true;
641
+ return httpsAgent;
642
+ }
643
+ return new Agent({ keepAlive, maxSockets, ...httpsAgent });
644
+ })(),
645
+ logger
646
+ };
647
+ }
648
+ };
649
+ }
650
+ });
651
+
652
+ // node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js
653
+ var NodeHttp2ConnectionPool;
654
+ var init_node_http2_connection_pool = __esm({
655
+ "node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-pool.js"() {
656
+ NodeHttp2ConnectionPool = class {
657
+ sessions = [];
658
+ constructor(sessions) {
659
+ this.sessions = sessions ?? [];
660
+ }
661
+ poll() {
662
+ if (this.sessions.length > 0) {
663
+ return this.sessions.shift();
664
+ }
665
+ }
666
+ offerLast(session) {
667
+ this.sessions.push(session);
668
+ }
669
+ contains(session) {
670
+ return this.sessions.includes(session);
671
+ }
672
+ remove(session) {
673
+ this.sessions = this.sessions.filter((s) => s !== session);
674
+ }
675
+ [Symbol.iterator]() {
676
+ return this.sessions[Symbol.iterator]();
677
+ }
678
+ destroy(connection) {
679
+ for (const session of this.sessions) {
680
+ if (session === connection) {
681
+ if (!session.destroyed) {
682
+ session.destroy();
683
+ }
684
+ }
685
+ }
686
+ }
687
+ };
688
+ }
689
+ });
690
+ var NodeHttp2ConnectionManager;
691
+ var init_node_http2_connection_manager = __esm({
692
+ "node_modules/@smithy/node-http-handler/dist-es/node-http2-connection-manager.js"() {
693
+ init_node_http2_connection_pool();
694
+ NodeHttp2ConnectionManager = class {
695
+ constructor(config) {
696
+ this.config = config;
697
+ if (this.config.maxConcurrency && this.config.maxConcurrency <= 0) {
698
+ throw new RangeError("maxConcurrency must be greater than zero.");
699
+ }
700
+ }
701
+ config;
702
+ sessionCache = /* @__PURE__ */ new Map();
703
+ lease(requestContext, connectionConfiguration) {
704
+ const url = this.getUrlString(requestContext);
705
+ const existingPool = this.sessionCache.get(url);
706
+ if (existingPool) {
707
+ const existingSession = existingPool.poll();
708
+ if (existingSession && !this.config.disableConcurrency) {
709
+ return existingSession;
710
+ }
711
+ }
712
+ const session = http2.connect(url);
713
+ if (this.config.maxConcurrency) {
714
+ session.settings({ maxConcurrentStreams: this.config.maxConcurrency }, (err) => {
715
+ if (err) {
716
+ throw new Error("Fail to set maxConcurrentStreams to " + this.config.maxConcurrency + "when creating new session for " + requestContext.destination.toString());
717
+ }
718
+ });
719
+ }
720
+ session.unref();
721
+ const destroySessionCb = () => {
722
+ session.destroy();
723
+ this.deleteSession(url, session);
724
+ };
725
+ session.on("goaway", destroySessionCb);
726
+ session.on("error", destroySessionCb);
727
+ session.on("frameError", destroySessionCb);
728
+ session.on("close", () => this.deleteSession(url, session));
729
+ if (connectionConfiguration.requestTimeout) {
730
+ session.setTimeout(connectionConfiguration.requestTimeout, destroySessionCb);
731
+ }
732
+ const connectionPool = this.sessionCache.get(url) || new NodeHttp2ConnectionPool();
733
+ connectionPool.offerLast(session);
734
+ this.sessionCache.set(url, connectionPool);
735
+ return session;
736
+ }
737
+ deleteSession(authority, session) {
738
+ const existingConnectionPool = this.sessionCache.get(authority);
739
+ if (!existingConnectionPool) {
740
+ return;
741
+ }
742
+ if (!existingConnectionPool.contains(session)) {
743
+ return;
744
+ }
745
+ existingConnectionPool.remove(session);
746
+ this.sessionCache.set(authority, existingConnectionPool);
747
+ }
748
+ release(requestContext, session) {
749
+ const cacheKey = this.getUrlString(requestContext);
750
+ this.sessionCache.get(cacheKey)?.offerLast(session);
751
+ }
752
+ destroy() {
753
+ for (const [key, connectionPool] of this.sessionCache) {
754
+ for (const session of connectionPool) {
755
+ if (!session.destroyed) {
756
+ session.destroy();
757
+ }
758
+ connectionPool.remove(session);
759
+ }
760
+ this.sessionCache.delete(key);
761
+ }
762
+ }
763
+ setMaxConcurrentStreams(maxConcurrentStreams) {
764
+ if (maxConcurrentStreams && maxConcurrentStreams <= 0) {
765
+ throw new RangeError("maxConcurrentStreams must be greater than zero.");
766
+ }
767
+ this.config.maxConcurrency = maxConcurrentStreams;
768
+ }
769
+ setDisableConcurrentStreams(disableConcurrentStreams) {
770
+ this.config.disableConcurrency = disableConcurrentStreams;
771
+ }
772
+ getUrlString(request) {
773
+ return request.destination.toString();
774
+ }
775
+ };
776
+ }
777
+ });
778
+ var NodeHttp2Handler;
779
+ var init_node_http2_handler = __esm({
780
+ "node_modules/@smithy/node-http-handler/dist-es/node-http2-handler.js"() {
781
+ init_dist_es();
782
+ init_dist_es3();
783
+ init_build_abort_error();
784
+ init_get_transformed_headers();
785
+ init_node_http2_connection_manager();
786
+ init_write_request_body();
787
+ NodeHttp2Handler = class _NodeHttp2Handler {
788
+ config;
789
+ configProvider;
790
+ metadata = { handlerProtocol: "h2" };
791
+ connectionManager = new NodeHttp2ConnectionManager({});
792
+ static create(instanceOrOptions) {
793
+ if (typeof instanceOrOptions?.handle === "function") {
794
+ return instanceOrOptions;
795
+ }
796
+ return new _NodeHttp2Handler(instanceOrOptions);
797
+ }
798
+ constructor(options) {
799
+ this.configProvider = new Promise((resolve, reject) => {
800
+ if (typeof options === "function") {
801
+ options().then((opts) => {
802
+ resolve(opts || {});
803
+ }).catch(reject);
804
+ } else {
805
+ resolve(options || {});
806
+ }
807
+ });
808
+ }
809
+ destroy() {
810
+ this.connectionManager.destroy();
811
+ }
812
+ async handle(request, { abortSignal, requestTimeout } = {}) {
813
+ if (!this.config) {
814
+ this.config = await this.configProvider;
815
+ this.connectionManager.setDisableConcurrentStreams(this.config.disableConcurrentStreams || false);
816
+ if (this.config.maxConcurrentStreams) {
817
+ this.connectionManager.setMaxConcurrentStreams(this.config.maxConcurrentStreams);
818
+ }
819
+ }
820
+ const { requestTimeout: configRequestTimeout, disableConcurrentStreams } = this.config;
821
+ const effectiveRequestTimeout = requestTimeout ?? configRequestTimeout;
822
+ return new Promise((_resolve, _reject) => {
823
+ let fulfilled = false;
824
+ let writeRequestBodyPromise = void 0;
825
+ const resolve = async (arg) => {
826
+ await writeRequestBodyPromise;
827
+ _resolve(arg);
828
+ };
829
+ const reject = async (arg) => {
830
+ await writeRequestBodyPromise;
831
+ _reject(arg);
832
+ };
833
+ if (abortSignal?.aborted) {
834
+ fulfilled = true;
835
+ const abortError = buildAbortError(abortSignal);
836
+ reject(abortError);
837
+ return;
838
+ }
839
+ const { hostname, method, port, protocol, query } = request;
840
+ let auth = "";
841
+ if (request.username != null || request.password != null) {
842
+ const username = request.username ?? "";
843
+ const password = request.password ?? "";
844
+ auth = `${username}:${password}@`;
845
+ }
846
+ const authority = `${protocol}//${auth}${hostname}${port ? `:${port}` : ""}`;
847
+ const requestContext = { destination: new URL(authority) };
848
+ const session = this.connectionManager.lease(requestContext, {
849
+ requestTimeout: this.config?.sessionTimeout,
850
+ disableConcurrentStreams: disableConcurrentStreams || false
851
+ });
852
+ const rejectWithDestroy = (err) => {
853
+ if (disableConcurrentStreams) {
854
+ this.destroySession(session);
855
+ }
856
+ fulfilled = true;
857
+ reject(err);
858
+ };
859
+ const queryString = buildQueryString(query || {});
860
+ let path2 = request.path;
861
+ if (queryString) {
862
+ path2 += `?${queryString}`;
863
+ }
864
+ if (request.fragment) {
865
+ path2 += `#${request.fragment}`;
866
+ }
867
+ const req = session.request({
868
+ ...request.headers,
869
+ [constants.HTTP2_HEADER_PATH]: path2,
870
+ [constants.HTTP2_HEADER_METHOD]: method
871
+ });
872
+ session.ref();
873
+ req.on("response", (headers) => {
874
+ const httpResponse = new HttpResponse({
875
+ statusCode: headers[":status"] || -1,
876
+ headers: getTransformedHeaders(headers),
877
+ body: req
878
+ });
879
+ fulfilled = true;
880
+ resolve({ response: httpResponse });
881
+ if (disableConcurrentStreams) {
882
+ session.close();
883
+ this.connectionManager.deleteSession(authority, session);
884
+ }
885
+ });
886
+ if (effectiveRequestTimeout) {
887
+ req.setTimeout(effectiveRequestTimeout, () => {
888
+ req.close();
889
+ const timeoutError = new Error(`Stream timed out because of no activity for ${effectiveRequestTimeout} ms`);
890
+ timeoutError.name = "TimeoutError";
891
+ rejectWithDestroy(timeoutError);
892
+ });
893
+ }
894
+ if (abortSignal) {
895
+ const onAbort = () => {
896
+ req.close();
897
+ const abortError = buildAbortError(abortSignal);
898
+ rejectWithDestroy(abortError);
899
+ };
900
+ if (typeof abortSignal.addEventListener === "function") {
901
+ const signal = abortSignal;
902
+ signal.addEventListener("abort", onAbort, { once: true });
903
+ req.once("close", () => signal.removeEventListener("abort", onAbort));
904
+ } else {
905
+ abortSignal.onabort = onAbort;
906
+ }
907
+ }
908
+ req.on("frameError", (type, code, id) => {
909
+ rejectWithDestroy(new Error(`Frame type id ${type} in stream id ${id} has failed with code ${code}.`));
910
+ });
911
+ req.on("error", rejectWithDestroy);
912
+ req.on("aborted", () => {
913
+ rejectWithDestroy(new Error(`HTTP/2 stream is abnormally aborted in mid-communication with result code ${req.rstCode}.`));
914
+ });
915
+ req.on("close", () => {
916
+ session.unref();
917
+ if (disableConcurrentStreams) {
918
+ session.destroy();
919
+ }
920
+ if (!fulfilled) {
921
+ rejectWithDestroy(new Error("Unexpected error: http2 request did not get a response"));
922
+ }
923
+ });
924
+ writeRequestBodyPromise = writeRequestBody(req, request, effectiveRequestTimeout);
925
+ });
926
+ }
927
+ updateHttpClientConfig(key, value) {
928
+ this.config = void 0;
929
+ this.configProvider = this.configProvider.then((config) => {
930
+ return {
931
+ ...config,
932
+ [key]: value
933
+ };
934
+ });
935
+ }
936
+ httpHandlerConfigs() {
937
+ return this.config ?? {};
938
+ }
939
+ destroySession(session) {
940
+ if (!session.destroyed) {
941
+ session.destroy();
942
+ }
943
+ }
944
+ };
945
+ }
946
+ });
947
+ var Collector;
948
+ var init_collector = __esm({
949
+ "node_modules/@smithy/node-http-handler/dist-es/stream-collector/collector.js"() {
950
+ Collector = class extends Writable {
951
+ bufferedBytes = [];
952
+ _write(chunk, encoding, callback) {
953
+ this.bufferedBytes.push(chunk);
954
+ callback();
955
+ }
956
+ };
957
+ }
958
+ });
959
+
960
+ // node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js
961
+ async function collectReadableStream(stream) {
962
+ const chunks = [];
963
+ const reader = stream.getReader();
964
+ let isDone = false;
965
+ let length = 0;
966
+ while (!isDone) {
967
+ const { done, value } = await reader.read();
968
+ if (value) {
969
+ chunks.push(value);
970
+ length += value.length;
971
+ }
972
+ isDone = done;
973
+ }
974
+ const collected = new Uint8Array(length);
975
+ let offset = 0;
976
+ for (const chunk of chunks) {
977
+ collected.set(chunk, offset);
978
+ offset += chunk.length;
979
+ }
980
+ return collected;
981
+ }
982
+ var streamCollector, isReadableStreamInstance;
983
+ var init_stream_collector = __esm({
984
+ "node_modules/@smithy/node-http-handler/dist-es/stream-collector/index.js"() {
985
+ init_collector();
986
+ streamCollector = (stream) => {
987
+ if (isReadableStreamInstance(stream)) {
988
+ return collectReadableStream(stream);
989
+ }
990
+ return new Promise((resolve, reject) => {
991
+ const collector = new Collector();
992
+ stream.pipe(collector);
993
+ stream.on("error", (err) => {
994
+ collector.end();
995
+ reject(err);
996
+ });
997
+ collector.on("error", reject);
998
+ collector.on("finish", function() {
999
+ const bytes = new Uint8Array(Buffer.concat(this.bufferedBytes));
1000
+ resolve(bytes);
1001
+ });
1002
+ });
1003
+ };
1004
+ isReadableStreamInstance = (stream) => typeof ReadableStream === "function" && stream instanceof ReadableStream;
1005
+ }
1006
+ });
1007
+
1008
+ // node_modules/@smithy/node-http-handler/dist-es/index.js
1009
+ var dist_es_exports = {};
1010
+ __export(dist_es_exports, {
1011
+ DEFAULT_REQUEST_TIMEOUT: () => DEFAULT_REQUEST_TIMEOUT,
1012
+ NodeHttp2Handler: () => NodeHttp2Handler,
1013
+ NodeHttpHandler: () => NodeHttpHandler,
1014
+ streamCollector: () => streamCollector
1015
+ });
1016
+ var init_dist_es4 = __esm({
1017
+ "node_modules/@smithy/node-http-handler/dist-es/index.js"() {
1018
+ init_node_http_handler();
1019
+ init_node_http2_handler();
1020
+ init_stream_collector();
1021
+ }
1022
+ });
27
1023
 
28
1024
  // src/registry/validator.ts
29
1025
  var ConfigValidationError = class extends Error {
@@ -377,10 +1373,7 @@ function checkboxToZod(field) {
377
1373
  return schema;
378
1374
  }
379
1375
  function dateToZod(field) {
380
- let schema = z.string().refine(
381
- (val) => !isNaN(Date.parse(val)),
382
- "Invalid date format"
383
- );
1376
+ let schema = z.string().refine((val) => !isNaN(Date.parse(val)), "Invalid date format");
384
1377
  if (field.minDate) {
385
1378
  schema = schema.refine(
386
1379
  (val) => new Date(val) >= new Date(field.minDate),
@@ -438,19 +1431,28 @@ function radioToZod(field) {
438
1431
  }
439
1432
  function colorToZod(field) {
440
1433
  let schema = z.string();
441
- if (field.format === "hex") schema = schema.regex(/^#[0-9A-Fa-f]{6}$/);
442
- if (field.format === "rgb") schema = schema.regex(/^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/);
443
- if (field.format === "hsl") schema = schema.regex(/^hsl\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*\)$/);
1434
+ if (field.format === "hex")
1435
+ schema = schema.regex(/^#[0-9A-Fa-f]{6}$/);
1436
+ if (field.format === "rgb")
1437
+ schema = schema.regex(
1438
+ /^rgb\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}\s*\)$/
1439
+ );
1440
+ if (field.format === "hsl")
1441
+ schema = schema.regex(
1442
+ /^hsl\(\s*\d{1,3}\s*,\s*\d{1,3}%\s*,\s*\d{1,3}%\s*\)$/
1443
+ );
444
1444
  if (!field.required) schema = schema.optional();
445
1445
  if (field.validate) schema = addCustomValidation(schema, field.validate);
446
1446
  return schema;
447
1447
  }
448
1448
  function richTextToZod(field) {
449
- let schema = z.array(z.object({
450
- type: z.string(),
451
- data: z.record(z.any()),
452
- children: z.array(z.any()).optional()
453
- }));
1449
+ let schema = z.array(
1450
+ z.object({
1451
+ type: z.string(),
1452
+ data: z.record(z.any()),
1453
+ children: z.array(z.any()).optional()
1454
+ })
1455
+ );
454
1456
  if (!field.required) schema = schema.optional();
455
1457
  if (field.validate) schema = addCustomValidation(schema, field.validate);
456
1458
  return schema;
@@ -516,7 +1518,8 @@ function groupToZod(field) {
516
1518
  return schema;
517
1519
  }
518
1520
  function blocksToZod(field) {
519
- const blockSchemas = field.blocks.map((block) => {
1521
+ const blocks = field.blocks || [];
1522
+ const blockSchemas = blocks.map((block) => {
520
1523
  return z.object({
521
1524
  blockType: z.literal(block.slug),
522
1525
  ...Object.fromEntries(
@@ -524,7 +1527,9 @@ function blocksToZod(field) {
524
1527
  )
525
1528
  });
526
1529
  });
527
- let schema = z.array(z.discriminatedUnion("blockType", blockSchemas));
1530
+ let schema = z.array(
1531
+ z.discriminatedUnion("blockType", blockSchemas)
1532
+ );
528
1533
  if (field.minRows) schema = schema.min(field.minRows);
529
1534
  if (field.maxRows) schema = schema.max(field.maxRows);
530
1535
  if (!field.required) schema = schema.optional();
@@ -984,6 +1989,7 @@ var Kyro = class {
984
1989
  registry;
985
1990
  db;
986
1991
  pubsub;
1992
+ settings;
987
1993
  wsServer;
988
1994
  config;
989
1995
  constructor(config) {
@@ -1005,7 +2011,10 @@ var Kyro = class {
1005
2011
  }
1006
2012
  async init() {
1007
2013
  await this.registry.init();
1008
- await this.db.init(this.registry.getCollections(), this.registry.getGlobals());
2014
+ await this.db.init(
2015
+ this.registry.getCollections(),
2016
+ this.registry.getGlobals()
2017
+ );
1009
2018
  this.pubsub.autoRegisterHooks();
1010
2019
  console.log("\u2705 Kyro CMS initialized");
1011
2020
  console.log(` Collections: ${this.registry.getCollections().length}`);
@@ -1014,19 +2023,37 @@ var Kyro = class {
1014
2023
  // ============================================================================
1015
2024
  // API Methods
1016
2025
  // ============================================================================
2026
+ // Load settings from globals if not already loaded
2027
+ async loadSettings() {
2028
+ if (this.settings) return this.settings;
2029
+ try {
2030
+ const accessSettings = await this.db.findOne({
2031
+ collection: "_globals",
2032
+ where: { slug: "access-settings" }
2033
+ });
2034
+ if (accessSettings) {
2035
+ this.settings = accessSettings.data;
2036
+ }
2037
+ } catch (e) {
2038
+ console.log("\u26A0\uFE0F No access-settings found, using defaults");
2039
+ }
2040
+ return this.settings || {};
2041
+ }
1017
2042
  getREST(options) {
1018
2043
  return createHonoApp({
1019
2044
  registry: this.registry,
1020
2045
  db: this.db,
1021
2046
  ...options,
1022
- cors: this.config.cors
2047
+ cors: this.config.cors,
2048
+ settings: this.settings
1023
2049
  });
1024
2050
  }
1025
2051
  getGraphQL(options) {
1026
2052
  return buildGraphQLSchema({
1027
2053
  registry: this.registry,
1028
2054
  db: this.db,
1029
- ...options
2055
+ ...options,
2056
+ settings: this.settings
1030
2057
  });
1031
2058
  }
1032
2059
  getTRPC(options) {
@@ -1034,14 +2061,20 @@ var Kyro = class {
1034
2061
  registry: this.registry,
1035
2062
  db: this.db,
1036
2063
  req: options?.req || { headers: {} },
1037
- ...options
2064
+ ...options,
2065
+ settings: this.settings
1038
2066
  });
1039
2067
  }
1040
2068
  async startWebSocket(options) {
2069
+ const apiAccess = this.settings?.access?.apiAccess;
2070
+ if (apiAccess?.websocketEnabled === false) {
2071
+ console.log("\u26A0\uFE0F WebSocket is disabled in settings");
2072
+ return null;
2073
+ }
1041
2074
  this.wsServer = createWSServer({
1042
2075
  pubsub: this.pubsub,
1043
2076
  port: options?.port || 8080,
1044
- requireAuth: options?.requireAuth,
2077
+ requireAuth: options?.requireAuth ?? apiAccess?.requireAuth,
1045
2078
  verifyToken: options?.verifyToken
1046
2079
  });
1047
2080
  console.log(`\u{1F50C} WebSocket server started on port ${options?.port || 8080}`);
@@ -1084,6 +2117,9 @@ function isBlocksField(field) {
1084
2117
  function isUploadField(field) {
1085
2118
  return field.type === "upload";
1086
2119
  }
2120
+ function isImageField(field) {
2121
+ return field.type === "image";
2122
+ }
1087
2123
  function isRichTextField(field) {
1088
2124
  return field.type === "richtext";
1089
2125
  }
@@ -1110,6 +2146,7 @@ var COMPLEX_FIELD_TYPES = [
1110
2146
  "json",
1111
2147
  "code",
1112
2148
  "upload",
2149
+ "image",
1113
2150
  "markdown"
1114
2151
  ];
1115
2152
  var RELATIONAL_FIELD_TYPES = [
@@ -1118,11 +2155,7 @@ var RELATIONAL_FIELD_TYPES = [
1118
2155
  "group",
1119
2156
  "blocks"
1120
2157
  ];
1121
- var LAYOUT_FIELD_TYPES = [
1122
- "row",
1123
- "collapsible",
1124
- "tabs"
1125
- ];
2158
+ var LAYOUT_FIELD_TYPES = ["row", "collapsible", "tabs"];
1126
2159
  var ALL_FIELD_TYPES = [
1127
2160
  ...PRIMITIVE_FIELD_TYPES,
1128
2161
  ...COMPLEX_FIELD_TYPES,
@@ -1130,41 +2163,449 @@ var ALL_FIELD_TYPES = [
1130
2163
  ...LAYOUT_FIELD_TYPES
1131
2164
  ];
1132
2165
 
1133
- // src/hooks/types.ts
1134
- async function runHooks(hooks, args) {
1135
- let result = args.data;
1136
- for (const hook of hooks) {
1137
- const hookResult = await hook({
1138
- ...args,
1139
- data: result
1140
- });
1141
- if (hookResult !== void 0) {
1142
- result = hookResult;
1143
- }
1144
- }
1145
- return result;
2166
+ // src/fields/richtext.ts
2167
+ var MIN_COLUMNS = 1;
2168
+ var MAX_COLUMNS = 6;
2169
+ var richTextStyles = `
2170
+ .kyro-richtext {
2171
+ color: inherit;
2172
+ line-height: 1.7;
1146
2173
  }
1147
- async function runFieldHooks(hooks, args) {
1148
- return runHooks(hooks, args);
2174
+
2175
+ .kyro-richtext > *:first-child {
2176
+ margin-top: 0;
1149
2177
  }
1150
2178
 
1151
- // src/database/local/adapter.ts
1152
- var LocalAdapter = class extends AbstractBaseAdapter {
1153
- db;
1154
- path;
1155
- migrations = /* @__PURE__ */ new Map();
1156
- constructor(options) {
1157
- super();
1158
- this.path = options.path;
1159
- if (options.db) {
1160
- this.db = options.db;
1161
- } else {
1162
- this.db = null;
1163
- }
1164
- }
1165
- async connect() {
1166
- if (!this.db) {
1167
- const Database = (await import('better-sqlite3')).default;
2179
+ .kyro-richtext > *:last-child {
2180
+ margin-bottom: 0;
2181
+ }
2182
+
2183
+ .kyro-richtext p,
2184
+ .kyro-richtext ul,
2185
+ .kyro-richtext ol,
2186
+ .kyro-richtext blockquote,
2187
+ .kyro-richtext pre,
2188
+ .kyro-richtext kyro-callout,
2189
+ .kyro-richtext kyro-hero,
2190
+ .kyro-richtext kyro-heading,
2191
+ .kyro-richtext kyro-stack,
2192
+ .kyro-richtext kyro-columns {
2193
+ margin: 0 0 1rem;
2194
+ }
2195
+
2196
+ .kyro-richtext h1,
2197
+ .kyro-richtext h2,
2198
+ .kyro-richtext h3,
2199
+ .kyro-richtext h4,
2200
+ .kyro-richtext h5,
2201
+ .kyro-richtext h6 {
2202
+ margin: 0 0 0.75rem;
2203
+ line-height: 1.2;
2204
+ }
2205
+
2206
+ .kyro-richtext ul,
2207
+ .kyro-richtext ol {
2208
+ padding-left: 1.5rem;
2209
+ }
2210
+
2211
+ .kyro-richtext blockquote {
2212
+ border-left: 4px solid rgba(148, 163, 184, 0.5);
2213
+ margin-left: 0;
2214
+ padding-left: 1rem;
2215
+ font-style: italic;
2216
+ }
2217
+
2218
+ .kyro-richtext pre {
2219
+ overflow-x: auto;
2220
+ border-radius: 0.75rem;
2221
+ background: rgba(15, 23, 42, 0.92);
2222
+ color: #f8fafc;
2223
+ padding: 1rem;
2224
+ }
2225
+
2226
+ .kyro-richtext code {
2227
+ font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
2228
+ }
2229
+
2230
+ .kyro-richtext img {
2231
+ display: block;
2232
+ max-width: 100%;
2233
+ height: auto;
2234
+ border-radius: 0.75rem;
2235
+ }
2236
+
2237
+ .kyro-richtext kyro-columns {
2238
+ display: grid;
2239
+ grid-template-columns: repeat(var(--kyro-columns-count, 1), minmax(0, 1fr));
2240
+ gap: 1rem;
2241
+ }
2242
+
2243
+ .kyro-richtext kyro-column {
2244
+ display: block;
2245
+ min-width: 0;
2246
+ }
2247
+
2248
+ .kyro-richtext kyro-column > *:last-child {
2249
+ margin-bottom: 0;
2250
+ }
2251
+
2252
+ .kyro-richtext kyro-hero,
2253
+ .kyro-richtext kyro-callout,
2254
+ .kyro-richtext kyro-stack,
2255
+ .kyro-richtext kyro-heading {
2256
+ display: block;
2257
+ }
2258
+
2259
+ .kyro-richtext kyro-hero {
2260
+ border: 1px solid rgba(148, 163, 184, 0.25);
2261
+ border-radius: 1rem;
2262
+ padding: 1.5rem;
2263
+ text-align: center;
2264
+ background: linear-gradient(to bottom, rgba(148, 163, 184, 0.12), transparent);
2265
+ }
2266
+
2267
+ .kyro-richtext kyro-heading {
2268
+ font-size: 1.125rem;
2269
+ font-weight: 700;
2270
+ }
2271
+
2272
+ .kyro-richtext kyro-callout {
2273
+ border: 1px solid rgba(59, 130, 246, 0.25);
2274
+ border-radius: 0.875rem;
2275
+ padding: 0.875rem 1rem;
2276
+ background: rgba(59, 130, 246, 0.06);
2277
+ }
2278
+
2279
+ @media (max-width: 720px) {
2280
+ .kyro-richtext kyro-columns {
2281
+ grid-template-columns: 1fr;
2282
+ }
2283
+ }
2284
+ `.trim();
2285
+ function createEmptyParagraphNode() {
2286
+ return {
2287
+ type: "paragraph",
2288
+ content: []
2289
+ };
2290
+ }
2291
+ function createColumnNode(content = [createEmptyParagraphNode()]) {
2292
+ return {
2293
+ type: "kyroColumn",
2294
+ content: content.length > 0 ? content : [createEmptyParagraphNode()]
2295
+ };
2296
+ }
2297
+ function createColumnsNode(columns = 2) {
2298
+ const count = clampColumns(columns);
2299
+ return {
2300
+ type: "kyroColumns",
2301
+ attrs: { columns: count },
2302
+ content: Array.from({ length: count }, () => createColumnNode())
2303
+ };
2304
+ }
2305
+ function normalizeRichTextDocument(value) {
2306
+ const documentNode = toRichTextDocument(value);
2307
+ return {
2308
+ type: "doc",
2309
+ content: normalizeNodes(documentNode.content)
2310
+ };
2311
+ }
2312
+ function normalizeRichTextValue(value) {
2313
+ if (Array.isArray(value)) {
2314
+ return normalizeRichTextDocument(value).content;
2315
+ }
2316
+ if (isObject(value) && value.type === "doc") {
2317
+ return normalizeRichTextDocument(value);
2318
+ }
2319
+ return value;
2320
+ }
2321
+ function renderRichText(value) {
2322
+ if (typeof value === "string") {
2323
+ return `<div class="kyro-richtext">${value}</div>`;
2324
+ }
2325
+ const documentNode = normalizeRichTextDocument(value);
2326
+ return `<div class="kyro-richtext">${renderNodes(documentNode.content)}</div>`;
2327
+ }
2328
+ function toRichTextDocument(value) {
2329
+ if (Array.isArray(value)) {
2330
+ return {
2331
+ type: "doc",
2332
+ content: normalizeNodes(value)
2333
+ };
2334
+ }
2335
+ if (isObject(value) && value.type === "doc") {
2336
+ const doc = value;
2337
+ return {
2338
+ type: "doc",
2339
+ content: normalizeNodes(doc.content ?? [])
2340
+ };
2341
+ }
2342
+ return {
2343
+ type: "doc",
2344
+ content: []
2345
+ };
2346
+ }
2347
+ function normalizeNodes(nodes) {
2348
+ return nodes.filter((node) => isObject(node) && typeof node.type === "string").map((node) => normalizeNode(node));
2349
+ }
2350
+ function normalizeNode(node) {
2351
+ if (node.type === "kyroColumns") {
2352
+ return normalizeColumnsNode(node);
2353
+ }
2354
+ if (node.type === "kyroColumn") {
2355
+ const normalizedContent = normalizeNodes(
2356
+ Array.isArray(node.content) ? node.content : []
2357
+ ).filter((child) => child.type !== "kyroColumns");
2358
+ return {
2359
+ ...node,
2360
+ content: normalizedContent.length > 0 ? normalizedContent : [createEmptyParagraphNode()]
2361
+ };
2362
+ }
2363
+ if (Array.isArray(node.content)) {
2364
+ return {
2365
+ ...node,
2366
+ content: normalizeNodes(node.content)
2367
+ };
2368
+ }
2369
+ return {
2370
+ ...node
2371
+ };
2372
+ }
2373
+ function normalizeColumnsNode(node) {
2374
+ const rawChildren = Array.isArray(node.content) ? node.content : [];
2375
+ const hasStructuredColumns = rawChildren.every((child) => child?.type === "kyroColumn");
2376
+ const targetColumns = clampColumns(
2377
+ typeof node.attrs?.columns === "number" ? node.attrs.columns : hasStructuredColumns && rawChildren.length > 0 ? rawChildren.length : 2
2378
+ );
2379
+ const content = hasStructuredColumns ? normalizeStructuredColumns(rawChildren, targetColumns) : normalizeLegacyColumns(rawChildren, targetColumns);
2380
+ return {
2381
+ ...node,
2382
+ attrs: {
2383
+ ...node.attrs ?? {},
2384
+ columns: content.length
2385
+ },
2386
+ content
2387
+ };
2388
+ }
2389
+ function normalizeStructuredColumns(columns, targetColumns) {
2390
+ const normalized = columns.slice(0, MAX_COLUMNS).map((column) => normalizeNode(column)).filter((column) => column.type === "kyroColumn");
2391
+ if (normalized.length === 0) {
2392
+ return Array.from({ length: targetColumns }, () => createColumnNode());
2393
+ }
2394
+ if (normalized.length > targetColumns) {
2395
+ const kept = normalized.slice(0, targetColumns);
2396
+ const extras = normalized.slice(targetColumns);
2397
+ const mergedTail = extras.flatMap(
2398
+ (column) => normalizeColumnChildren(column.content)
2399
+ );
2400
+ const lastIndex = kept.length - 1;
2401
+ kept[lastIndex] = createColumnNode([
2402
+ ...normalizeColumnChildren(kept[lastIndex].content),
2403
+ ...mergedTail
2404
+ ]);
2405
+ return kept;
2406
+ }
2407
+ if (normalized.length < targetColumns) {
2408
+ return [
2409
+ ...normalized,
2410
+ ...Array.from(
2411
+ { length: targetColumns - normalized.length },
2412
+ () => createColumnNode()
2413
+ )
2414
+ ];
2415
+ }
2416
+ return normalized;
2417
+ }
2418
+ function normalizeLegacyColumns(blocks, targetColumns) {
2419
+ const normalizedBlocks = normalizeNodes(blocks);
2420
+ return Array.from({ length: targetColumns }, (_, index) => {
2421
+ if (index < targetColumns - 1) {
2422
+ const block = normalizedBlocks[index];
2423
+ return block ? createColumnNode([block]) : createColumnNode();
2424
+ }
2425
+ const tail = normalizedBlocks.slice(index);
2426
+ return createColumnNode(tail.length > 0 ? tail : [createEmptyParagraphNode()]);
2427
+ });
2428
+ }
2429
+ function normalizeColumnChildren(content) {
2430
+ const children = Array.isArray(content) ? normalizeNodes(content) : [];
2431
+ const filtered = children.filter((child) => child.type !== "kyroColumns");
2432
+ return filtered.length > 0 ? filtered : [createEmptyParagraphNode()];
2433
+ }
2434
+ function renderNodes(nodes) {
2435
+ return nodes.map((node) => renderNode(node)).join("");
2436
+ }
2437
+ function renderNode(node) {
2438
+ switch (node.type) {
2439
+ case "paragraph":
2440
+ return `<p>${renderInlineContent(node.content)}</p>`;
2441
+ case "text":
2442
+ return renderTextNode(node);
2443
+ case "heading": {
2444
+ const level = clampHeadingLevel(node.attrs?.level);
2445
+ return `<h${level}>${renderInlineContent(node.content)}</h${level}>`;
2446
+ }
2447
+ case "bulletList":
2448
+ return `<ul>${renderNodes(node.content ?? [])}</ul>`;
2449
+ case "orderedList":
2450
+ return `<ol>${renderNodes(node.content ?? [])}</ol>`;
2451
+ case "listItem":
2452
+ return `<li>${renderNodes(node.content ?? [])}</li>`;
2453
+ case "blockquote":
2454
+ return `<blockquote>${renderNodes(node.content ?? [])}</blockquote>`;
2455
+ case "codeBlock":
2456
+ return `<pre><code>${escapeHtml(extractText(node.content ?? []))}</code></pre>`;
2457
+ case "horizontalRule":
2458
+ return "<hr />";
2459
+ case "hardBreak":
2460
+ return "<br />";
2461
+ case "image":
2462
+ return renderImageNode(node);
2463
+ case "kyroColumns": {
2464
+ const columns = clampColumns(
2465
+ Array.isArray(node.content) ? node.content.length : 1
2466
+ );
2467
+ return `<kyro-columns data-columns="${columns}" style="--kyro-columns-count:${columns}">${renderNodes(node.content ?? [])}</kyro-columns>`;
2468
+ }
2469
+ case "kyroColumn":
2470
+ return `<kyro-column>${renderNodes(node.content ?? [])}</kyro-column>`;
2471
+ case "kyroHero":
2472
+ return `<kyro-hero>${renderInlineContent(node.content)}</kyro-hero>`;
2473
+ case "kyroHeading":
2474
+ return `<kyro-heading>${renderInlineContent(node.content)}</kyro-heading>`;
2475
+ case "kyroDivider":
2476
+ return '<hr class="kyro-divider" />';
2477
+ case "kyroCallout":
2478
+ return `<kyro-callout>${renderInlineContent(node.content)}</kyro-callout>`;
2479
+ case "kyroStack":
2480
+ return `<kyro-stack>${renderNodes(node.content ?? [])}</kyro-stack>`;
2481
+ case "kyroImage":
2482
+ return renderCustomMediaNode("kyro-image", node);
2483
+ case "kyroVideo":
2484
+ return renderCustomMediaNode("kyro-video", node);
2485
+ default:
2486
+ return renderNodes(node.content ?? []);
2487
+ }
2488
+ }
2489
+ function renderInlineContent(content) {
2490
+ const nodes = content ?? [];
2491
+ if (nodes.length === 0) {
2492
+ return "<br />";
2493
+ }
2494
+ return nodes.map((node) => renderNode(node)).join("");
2495
+ }
2496
+ function renderTextNode(node) {
2497
+ const text = typeof node.text === "string" ? escapeHtml(node.text) : "";
2498
+ return (node.marks ?? []).reduce((acc, mark) => applyMark(acc, mark), text);
2499
+ }
2500
+ function renderImageNode(node) {
2501
+ const src = typeof node.attrs?.src === "string" ? node.attrs.src : "";
2502
+ if (!src) {
2503
+ return "";
2504
+ }
2505
+ const alt = typeof node.attrs?.alt === "string" ? node.attrs.alt : "";
2506
+ return `<img src="${escapeAttribute(src)}" alt="${escapeAttribute(alt)}" />`;
2507
+ }
2508
+ function renderCustomMediaNode(tagName, node) {
2509
+ const attrs = renderAttributes(node.attrs);
2510
+ return `<${tagName}${attrs}></${tagName}>`;
2511
+ }
2512
+ function renderAttributes(attrs, keysToSkip = []) {
2513
+ if (!attrs) {
2514
+ return "";
2515
+ }
2516
+ const entries = Object.entries(attrs).filter(([key, value]) => !keysToSkip.includes(key) && value !== void 0 && value !== null).map(([key, value]) => `${key}="${escapeAttribute(String(value))}"`);
2517
+ return entries.length > 0 ? ` ${entries.join(" ")}` : "";
2518
+ }
2519
+ function applyMark(content, mark) {
2520
+ switch (mark.type) {
2521
+ case "bold":
2522
+ return `<strong>${content}</strong>`;
2523
+ case "italic":
2524
+ return `<em>${content}</em>`;
2525
+ case "underline":
2526
+ return `<u>${content}</u>`;
2527
+ case "strike":
2528
+ return `<s>${content}</s>`;
2529
+ case "code":
2530
+ return `<code>${content}</code>`;
2531
+ case "link": {
2532
+ const href = typeof mark.attrs?.href === "string" ? escapeAttribute(mark.attrs.href) : "#";
2533
+ return `<a href="${href}" target="_blank" rel="noopener noreferrer">${content}</a>`;
2534
+ }
2535
+ default:
2536
+ return content;
2537
+ }
2538
+ }
2539
+ function extractText(nodes) {
2540
+ return nodes.map((node) => {
2541
+ if (node.type === "text") {
2542
+ return typeof node.text === "string" ? node.text : "";
2543
+ }
2544
+ if (node.type === "hardBreak") {
2545
+ return "\n";
2546
+ }
2547
+ return extractText(node.content ?? []);
2548
+ }).join("");
2549
+ }
2550
+ function clampColumns(value) {
2551
+ const numericValue = typeof value === "number" ? value : Number(value);
2552
+ if (!Number.isFinite(numericValue)) {
2553
+ return 2;
2554
+ }
2555
+ return Math.max(MIN_COLUMNS, Math.min(MAX_COLUMNS, Math.round(numericValue)));
2556
+ }
2557
+ function clampHeadingLevel(level) {
2558
+ const numericLevel = typeof level === "number" ? level : Number(level);
2559
+ if (!Number.isFinite(numericLevel)) {
2560
+ return 2;
2561
+ }
2562
+ return Math.max(1, Math.min(6, Math.round(numericLevel)));
2563
+ }
2564
+ function escapeHtml(value) {
2565
+ return value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
2566
+ }
2567
+ function escapeAttribute(value) {
2568
+ return escapeHtml(value).replaceAll('"', "&quot;");
2569
+ }
2570
+ function isObject(value) {
2571
+ return typeof value === "object" && value !== null;
2572
+ }
2573
+
2574
+ // src/hooks/types.ts
2575
+ async function runHooks(hooks, args) {
2576
+ let result = args.data;
2577
+ for (const hook of hooks) {
2578
+ const hookResult = await hook({
2579
+ ...args,
2580
+ data: result
2581
+ });
2582
+ if (hookResult !== void 0) {
2583
+ result = hookResult;
2584
+ }
2585
+ }
2586
+ return result;
2587
+ }
2588
+ async function runFieldHooks(hooks, args) {
2589
+ return runHooks(hooks, args);
2590
+ }
2591
+
2592
+ // src/database/local/adapter.ts
2593
+ var LocalAdapter = class extends AbstractBaseAdapter {
2594
+ db;
2595
+ path;
2596
+ migrations = /* @__PURE__ */ new Map();
2597
+ constructor(options) {
2598
+ super();
2599
+ this.path = options.path;
2600
+ if (options.db) {
2601
+ this.db = options.db;
2602
+ } else {
2603
+ this.db = null;
2604
+ }
2605
+ }
2606
+ async connect() {
2607
+ if (!this.db) {
2608
+ const Database = (await import('better-sqlite3')).default;
1168
2609
  this.db = new Database(this.path || ":memory:");
1169
2610
  }
1170
2611
  this.db.pragma("journal_mode = WAL");
@@ -1649,9 +3090,19 @@ var SEOPLugin = class extends KyroPlugin {
1649
3090
  slug: "seo-settings",
1650
3091
  label: "SEO Settings",
1651
3092
  fields: [
1652
- { name: "sitemap", type: "checkbox", label: "Enable Sitemap", defaultValue: true },
1653
- { name: "robotsTxt", type: "textarea", label: "robots.txt Content" },
1654
- { name: "canonicalUrl", type: "text", variant: "url", label: "Canonical URL" },
3093
+ {
3094
+ name: "sitemap",
3095
+ type: "checkbox",
3096
+ label: "Enable Sitemap",
3097
+ defaultValue: true
3098
+ },
3099
+ { name: "robotsTxt", type: "richtext", label: "robots.txt Content" },
3100
+ {
3101
+ name: "canonicalUrl",
3102
+ type: "text",
3103
+ variant: "url",
3104
+ label: "Canonical URL"
3105
+ },
1655
3106
  { name: "ogImage", type: "text", label: "Default OG Image URL" }
1656
3107
  ]
1657
3108
  });
@@ -1683,7 +3134,7 @@ var CommentsPlugin = class extends KyroPlugin {
1683
3134
  slug: "comments",
1684
3135
  label: "Comments",
1685
3136
  fields: [
1686
- { name: "content", type: "textarea", required: true },
3137
+ { name: "content", type: "richtext", required: true },
1687
3138
  { name: "author", type: "text", required: true },
1688
3139
  { name: "email", type: "email" },
1689
3140
  { name: "approved", type: "checkbox", defaultValue: false },
@@ -1705,12 +3156,22 @@ var ReviewsPlugin = class extends KyroPlugin {
1705
3156
  fields: [
1706
3157
  { name: "rating", type: "number", required: true, min: 1, max: 5 },
1707
3158
  { name: "title", type: "text" },
1708
- { name: "content", type: "textarea", required: true },
3159
+ { name: "content", type: "richtext", required: true },
1709
3160
  { name: "author", type: "relationship", relationTo: "customers" },
1710
- { name: "product", type: "relationship", relationTo: "products", required: true },
3161
+ {
3162
+ name: "product",
3163
+ type: "relationship",
3164
+ relationTo: "products",
3165
+ required: true
3166
+ },
1711
3167
  { name: "approved", type: "checkbox", defaultValue: false },
1712
3168
  { name: "verified", type: "checkbox", label: "Verified Purchase" },
1713
- { name: "helpful", type: "number", label: "Helpful Count", defaultValue: 0 }
3169
+ {
3170
+ name: "helpful",
3171
+ type: "number",
3172
+ label: "Helpful Count",
3173
+ defaultValue: 0
3174
+ }
1714
3175
  ]
1715
3176
  });
1716
3177
  this.adminComponents["ReviewModeration"] = {};
@@ -1724,24 +3185,47 @@ var WishlistPlugin = class extends KyroPlugin {
1724
3185
  slug: "wishlists",
1725
3186
  label: "Wishlists",
1726
3187
  fields: [
1727
- { name: "customer", type: "relationship", relationTo: "customers", required: true },
1728
- { name: "name", type: "text", label: "Wishlist Name", defaultValue: "My Wishlist" },
1729
- { name: "items", type: "blocks", label: "Items", blocks: [
1730
- {
1731
- slug: "wishlist-item",
1732
- label: "Item",
1733
- fields: [
1734
- { name: "product", type: "relationship", relationTo: "products" },
1735
- { name: "quantity", type: "number", defaultValue: 1 },
1736
- { name: "addedAt", type: "date" },
1737
- { name: "priority", type: "select", options: [
1738
- { label: "Low", value: "low" },
1739
- { label: "Medium", value: "medium" },
1740
- { label: "High", value: "high" }
1741
- ] }
1742
- ]
1743
- }
1744
- ] }
3188
+ {
3189
+ name: "customer",
3190
+ type: "relationship",
3191
+ relationTo: "customers",
3192
+ required: true
3193
+ },
3194
+ {
3195
+ name: "name",
3196
+ type: "text",
3197
+ label: "Wishlist Name",
3198
+ defaultValue: "My Wishlist"
3199
+ },
3200
+ {
3201
+ name: "items",
3202
+ type: "blocks",
3203
+ label: "Items",
3204
+ blocks: [
3205
+ {
3206
+ slug: "wishlist-item",
3207
+ label: "Item",
3208
+ fields: [
3209
+ {
3210
+ name: "product",
3211
+ type: "relationship",
3212
+ relationTo: "products"
3213
+ },
3214
+ { name: "quantity", type: "number", defaultValue: 1 },
3215
+ { name: "addedAt", type: "date" },
3216
+ {
3217
+ name: "priority",
3218
+ type: "select",
3219
+ options: [
3220
+ { label: "Low", value: "low" },
3221
+ { label: "Medium", value: "medium" },
3222
+ { label: "High", value: "high" }
3223
+ ]
3224
+ }
3225
+ ]
3226
+ }
3227
+ ]
3228
+ }
1745
3229
  ]
1746
3230
  });
1747
3231
  }
@@ -1754,253 +3238,6 @@ var presetPlugins = {
1754
3238
  Wishlist: WishlistPlugin
1755
3239
  };
1756
3240
 
1757
- // src/styling/index.ts
1758
- var CSSGenerator = class {
1759
- constructor(config) {
1760
- this.config = config;
1761
- }
1762
- config;
1763
- css = [];
1764
- addRule(selector, properties) {
1765
- const props = Object.entries(properties).map(([k, v]) => ` ${k}: ${v};`).join("\n");
1766
- this.css.push(`${selector} {
1767
- ${props}
1768
- }`);
1769
- return this;
1770
- }
1771
- addMediaQuery(breakpoint, rules) {
1772
- this.css.push(`@media (min-width: ${breakpoint}) {
1773
- ${rules.join("\n ")}
1774
- }`);
1775
- return this;
1776
- }
1777
- generate() {
1778
- return this.css.join("\n\n");
1779
- }
1780
- };
1781
- function generateTailwindConfig(theme) {
1782
- return {
1783
- theme: {
1784
- extend: {
1785
- colors: theme.colors || {},
1786
- fontFamily: theme.fonts || {},
1787
- spacing: theme.spacing || {},
1788
- borderRadius: theme.borderRadius || {},
1789
- boxShadow: theme.shadows || {},
1790
- screens: theme.breakpoints || {}
1791
- }
1792
- }
1793
- };
1794
- }
1795
- var defaultLightTheme = {
1796
- colors: {
1797
- primary: "#3b82f6",
1798
- secondary: "#6366f1",
1799
- accent: "#ec4899",
1800
- background: "#ffffff",
1801
- surface: "#f9fafb",
1802
- text: "#111827",
1803
- textMuted: "#6b7280",
1804
- border: "#e5e7eb",
1805
- error: "#ef4444",
1806
- warning: "#f59e0b",
1807
- success: "#10b981",
1808
- info: "#3b82f6"
1809
- },
1810
- fonts: {
1811
- sans: "system-ui, -apple-system, sans-serif",
1812
- serif: "Georgia, serif",
1813
- mono: "Menlo, monospace"
1814
- },
1815
- spacing: {
1816
- xs: "0.25rem",
1817
- sm: "0.5rem",
1818
- md: "1rem",
1819
- lg: "1.5rem",
1820
- xl: "2rem",
1821
- "2xl": "3rem",
1822
- "3xl": "4rem"
1823
- },
1824
- borderRadius: {
1825
- sm: "0.125rem",
1826
- md: "0.375rem",
1827
- lg: "0.5rem",
1828
- xl: "0.75rem",
1829
- full: "9999px"
1830
- },
1831
- shadows: {
1832
- sm: "0 1px 2px 0 rgb(0 0 0 / 0.05)",
1833
- md: "0 4px 6px -1px rgb(0 0 0 / 0.1)",
1834
- lg: "0 10px 15px -3px rgb(0 0 0 / 0.1)",
1835
- xl: "0 20px 25px -5px rgb(0 0 0 / 0.1)"
1836
- }
1837
- };
1838
- var defaultDarkTheme = {
1839
- colors: {
1840
- primary: "#60a5fa",
1841
- secondary: "#818cf8",
1842
- accent: "#f472b6",
1843
- background: "#111827",
1844
- surface: "#1f2937",
1845
- text: "#f9fafb",
1846
- textMuted: "#9ca3af",
1847
- border: "#374151",
1848
- error: "#f87171",
1849
- warning: "#fbbf24",
1850
- success: "#34d399",
1851
- info: "#60a5fa"
1852
- },
1853
- fonts: defaultLightTheme.fonts,
1854
- spacing: defaultLightTheme.spacing,
1855
- borderRadius: defaultLightTheme.borderRadius,
1856
- shadows: {
1857
- sm: "0 1px 2px 0 rgb(0 0 0 / 0.3)",
1858
- md: "0 4px 6px -1px rgb(0 0 0 / 0.4)",
1859
- lg: "0 10px 15px -3px rgb(0 0 0 / 0.5)",
1860
- xl: "0 20px 25px -5px rgb(0 0 0 / 0.6)"
1861
- }
1862
- };
1863
- var ecommerce2026Theme = {
1864
- colors: {
1865
- primary: "#FF6B35",
1866
- secondary: "#1A1A2E",
1867
- accent: "#16C79A",
1868
- background: "#FFFFFF",
1869
- surface: "#F8F9FA",
1870
- text: "#1A1A2E",
1871
- textMuted: "#6B7280",
1872
- border: "#E5E7EB",
1873
- error: "#EF4444",
1874
- warning: "#F59E0B",
1875
- success: "#16C79A",
1876
- info: "#3B82F6"
1877
- },
1878
- fonts: {
1879
- sans: '"Inter", "Satoshi", system-ui, sans-serif',
1880
- serif: '"Playfair Display", Georgia, serif',
1881
- mono: '"JetBrains Mono", monospace'
1882
- },
1883
- spacing: {
1884
- xs: "0.125rem",
1885
- sm: "0.25rem",
1886
- md: "0.5rem",
1887
- lg: "1rem",
1888
- xl: "1.5rem",
1889
- "2xl": "2rem",
1890
- "3xl": "3rem",
1891
- "4xl": "4rem"
1892
- },
1893
- borderRadius: {
1894
- sm: "0",
1895
- md: "0",
1896
- lg: "0",
1897
- xl: "0",
1898
- full: "9999px"
1899
- },
1900
- shadows: {
1901
- sm: "0 1px 2px rgba(0,0,0,0.05)",
1902
- md: "0 4px 6px rgba(0,0,0,0.07)",
1903
- lg: "0 10px 15px rgba(0,0,0,0.1)",
1904
- xl: "0 20px 25px rgba(0,0,0,0.15)"
1905
- }
1906
- };
1907
- function generateCSSVariables(theme) {
1908
- const variables = [];
1909
- if (theme.colors) {
1910
- for (const [key, value] of Object.entries(theme.colors)) {
1911
- variables.push(` --color-${key}: ${value};`);
1912
- }
1913
- }
1914
- if (theme.fonts) {
1915
- for (const [key, value] of Object.entries(theme.fonts)) {
1916
- variables.push(` --font-${key}: ${value};`);
1917
- }
1918
- }
1919
- if (theme.spacing) {
1920
- for (const [key, value] of Object.entries(theme.spacing)) {
1921
- variables.push(` --spacing-${key}: ${value};`);
1922
- }
1923
- }
1924
- if (theme.borderRadius) {
1925
- for (const [key, value] of Object.entries(theme.borderRadius)) {
1926
- variables.push(` --radius-${key}: ${value};`);
1927
- }
1928
- }
1929
- if (theme.shadows) {
1930
- for (const [key, value] of Object.entries(theme.shadows)) {
1931
- variables.push(` --shadow-${key}: ${value};`);
1932
- }
1933
- }
1934
- return `:root {
1935
- ${variables.join("\n")}
1936
- }`;
1937
- }
1938
- function createAdminStyling(config) {
1939
- const cssVars = generateCSSVariables(config.theme || defaultLightTheme);
1940
- const componentStyles = [];
1941
- if (config.componentOverrides) {
1942
- for (const [selector, styles] of Object.entries(config.componentOverrides)) {
1943
- const props = Object.entries(styles).map(([k, v]) => ` ${k}: ${v};`).join("\n");
1944
- componentStyles.push(`${selector} {
1945
- ${props}
1946
- }`);
1947
- }
1948
- }
1949
- return `
1950
- ${cssVars}
1951
- ${config.customStyles || ""}
1952
- ${componentStyles.join("\n")}
1953
- `;
1954
- }
1955
- var defaultFieldStyling = {
1956
- text: {
1957
- wrapper: { marginBottom: "var(--spacing-md)" },
1958
- label: {
1959
- display: "block",
1960
- marginBottom: "var(--spacing-xs)",
1961
- fontWeight: "500",
1962
- color: "var(--color-text)"
1963
- },
1964
- input: {
1965
- width: "100%",
1966
- padding: "var(--spacing-sm) var(--spacing-md)",
1967
- border: "1px solid var(--color-border)",
1968
- borderRadius: "var(--radius-md)",
1969
- fontSize: "0.875rem"
1970
- },
1971
- error: {
1972
- color: "var(--color-error)",
1973
- fontSize: "0.75rem",
1974
- marginTop: "var(--spacing-xs)"
1975
- }
1976
- },
1977
- number: {
1978
- wrapper: { marginBottom: "var(--spacing-md)" },
1979
- label: { display: "block", marginBottom: "var(--spacing-xs)", fontWeight: "500" },
1980
- input: {
1981
- width: "100%",
1982
- padding: "var(--spacing-sm) var(--spacing-md)",
1983
- border: "1px solid var(--color-border)",
1984
- borderRadius: "var(--radius-md)"
1985
- }
1986
- },
1987
- checkbox: {
1988
- wrapper: { display: "flex", alignItems: "center", gap: "var(--spacing-sm)" },
1989
- input: { width: "1rem", height: "1rem" },
1990
- label: { cursor: "pointer" }
1991
- },
1992
- select: {
1993
- wrapper: { marginBottom: "var(--spacing-md)" },
1994
- input: {
1995
- width: "100%",
1996
- padding: "var(--spacing-sm) var(--spacing-md)",
1997
- border: "1px solid var(--color-border)",
1998
- borderRadius: "var(--radius-md)",
1999
- backgroundColor: "white"
2000
- }
2001
- }
2002
- };
2003
-
2004
3241
  // src/auth/security/lockout.ts
2005
3242
  var DEFAULT_LOCKOUT_CONFIG = {
2006
3243
  maxAttempts: 5,
@@ -2492,7 +3729,7 @@ var AuditLogger = class {
2492
3729
  };
2493
3730
  }
2494
3731
  };
2495
- function createAuditContext(req) {
3732
+ function createAuditContext2(req) {
2496
3733
  return {
2497
3734
  ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || req.headers.get("x-real-ip") || "unknown",
2498
3735
  userAgent: req.headers.get("user-agent") || "unknown"
@@ -2502,10 +3739,9 @@ var InMemoryAuthAdapter = class {
2502
3739
  users = /* @__PURE__ */ new Map();
2503
3740
  sessions = /* @__PURE__ */ new Map();
2504
3741
  refreshTokens = /* @__PURE__ */ new Map();
2505
- // refreshToken -> sessionId
2506
3742
  emailToUserId = /* @__PURE__ */ new Map();
2507
3743
  passwordHistory = /* @__PURE__ */ new Map();
2508
- // userId -> passwordHash[]
3744
+ auditLogs = [];
2509
3745
  externalDb = false;
2510
3746
  constructor() {
2511
3747
  }
@@ -2521,10 +3757,11 @@ var InMemoryAuthAdapter = class {
2521
3757
  async createUser(data) {
2522
3758
  const userId = randomBytes(16).toString("hex");
2523
3759
  const now = (/* @__PURE__ */ new Date()).toISOString();
3760
+ const passwordHash = await this.hashPassword(data.password);
2524
3761
  const user = {
2525
3762
  id: userId,
2526
3763
  email: data.email.toLowerCase(),
2527
- passwordHash: data.passwordHash,
3764
+ passwordHash,
2528
3765
  role: data.role || "customer",
2529
3766
  tenantId: data.tenantId,
2530
3767
  createdAt: now,
@@ -2582,9 +3819,12 @@ var InMemoryAuthAdapter = class {
2582
3819
  const bcrypt2 = (await import('bcryptjs')).default;
2583
3820
  return bcrypt2.hash(password, 12);
2584
3821
  }
2585
- async verifyPassword(password, hash) {
3822
+ async verifyPassword(email, password) {
3823
+ const user = await this.findUserByEmail(email);
3824
+ if (!user || !user.passwordHash) return null;
2586
3825
  const bcrypt2 = (await import('bcryptjs')).default;
2587
- return bcrypt2.compare(password, hash);
3826
+ const valid = await bcrypt2.compare(password, user.passwordHash);
3827
+ return valid ? user : null;
2588
3828
  }
2589
3829
  async createSession(userId, data = {}) {
2590
3830
  const sessionId = randomBytes(32).toString("hex");
@@ -2655,179 +3895,65 @@ var InMemoryAuthAdapter = class {
2655
3895
  async hasAnyUsers() {
2656
3896
  return this.users.size > 0;
2657
3897
  }
3898
+ async findAuditLogs(filter) {
3899
+ const { limit = 50, offset = 0 } = filter;
3900
+ let logs = this.auditLogs.slice().reverse();
3901
+ if (filter.userId) logs = logs.filter((l) => l.userId === filter.userId);
3902
+ if (filter.action) {
3903
+ if (Array.isArray(filter.action)) {
3904
+ logs = logs.filter((l) => filter.action.includes(String(l.action)));
3905
+ } else {
3906
+ logs = logs.filter((l) => l.action === filter.action);
3907
+ }
3908
+ }
3909
+ if (filter.resource)
3910
+ logs = logs.filter((l) => l.resource === filter.resource);
3911
+ if (filter.success !== void 0)
3912
+ logs = logs.filter((l) => l.success === filter.success);
3913
+ return { logs: logs.slice(offset, offset + limit), total: logs.length };
3914
+ }
3915
+ async createAuditLog(data) {
3916
+ const id = randomBytes(16).toString("hex");
3917
+ const timestamp = /* @__PURE__ */ new Date();
3918
+ const log = { ...data, id, timestamp };
3919
+ this.auditLogs.push(log);
3920
+ return log;
3921
+ }
2658
3922
  };
2659
3923
 
2660
- // src/auth/security/in-memory-rate-limit.ts
2661
- var InMemoryRateLimiter = class {
3924
+ // src/auth/security/in-memory-lockout.ts
3925
+ var InMemoryAccountLockout = class {
2662
3926
  storage = /* @__PURE__ */ new Map();
2663
- userStorage = /* @__PURE__ */ new Map();
2664
- limits;
2665
- userLimits;
2666
- constructor(limits, userLimits) {
2667
- this.limits = { ...DEFAULT_RATE_LIMITS2, ...limits };
2668
- this.userLimits = userLimits || {
2669
- "user:api": { window: 6e4, max: 500 },
2670
- "user:write": { window: 36e5, max: 100 }
3927
+ history = /* @__PURE__ */ new Map();
3928
+ // userId -> attempt timestamps
3929
+ config;
3930
+ constructor(config = {}) {
3931
+ this.config = {
3932
+ maxAttempts: 5,
3933
+ lockDuration: 9e5,
3934
+ // 15 minutes
3935
+ notifyUser: true,
3936
+ notifyAdmin: true,
3937
+ adminNotifyAfter: 3,
3938
+ ...config
2671
3939
  };
2672
3940
  }
2673
- getKey(type, identifier) {
2674
- return `${type}:${identifier}`;
2675
- }
2676
- getUserKey(type, userId, identifier) {
2677
- return `user:${type}:${userId}:${identifier}`;
2678
- }
2679
- cleanupOldEntries(entries, window) {
3941
+ async checkLockout(userId) {
2680
3942
  const now = Date.now();
2681
- const windowStart = now - window;
2682
- while (entries.length > 0 && entries[0].timestamp < windowStart) {
2683
- entries.shift();
2684
- }
2685
- }
2686
- async check(type, identifier) {
2687
- const config = this.limits[type] || this.limits["api:general"];
2688
- const key = this.getKey(type, identifier);
2689
- let entries = this.storage.get(key);
2690
- if (!entries) {
2691
- entries = [];
2692
- this.storage.set(key, entries);
3943
+ const record = this.storage.get(userId);
3944
+ if (record && record.lockedUntil !== null && record.lockedUntil <= now) {
3945
+ await this.resetAttempts(userId);
3946
+ return {
3947
+ locked: false,
3948
+ attemptsRemaining: this.config.maxAttempts,
3949
+ totalAttempts: 0
3950
+ };
2693
3951
  }
2694
- this.cleanupOldEntries(entries, config.window);
2695
- const now = Date.now();
2696
- const count = entries.reduce((sum, entry) => sum + entry.count, 0);
2697
- entries.push({ timestamp: now, count: 1 });
2698
- if (count >= config.max) {
2699
- const oldestEntry = entries.reduce(
2700
- (oldest, current) => oldest.timestamp < current.timestamp ? oldest : current,
2701
- entries[0]
2702
- );
2703
- const resetAt = oldestEntry.timestamp + config.window;
3952
+ if (!record) {
2704
3953
  return {
2705
- allowed: false,
2706
- remaining: 0,
2707
- resetAt,
2708
- retryAfter: Math.ceil((resetAt - now) / 1e3)
2709
- };
2710
- }
2711
- return {
2712
- allowed: true,
2713
- remaining: config.max - count - 1,
2714
- resetAt: now + config.window
2715
- };
2716
- }
2717
- async checkUser(type, userId, identifier) {
2718
- const config = this.userLimits[type] || this.userLimits["user:api"];
2719
- const userMap = this.userStorage.get(userId);
2720
- let entries = [];
2721
- if (userMap) {
2722
- entries = userMap.get(this.getKey(type, identifier)) || [];
2723
- } else {
2724
- if (!this.userStorage.has(userId)) {
2725
- this.userStorage.set(userId, /* @__PURE__ */ new Map());
2726
- }
2727
- this.userStorage.get(userId).set(this.getKey(type, identifier), entries);
2728
- }
2729
- this.cleanupOldEntries(entries, config.window);
2730
- const now = Date.now();
2731
- const count = entries.reduce((sum, entry) => sum + entry.count, 0);
2732
- entries.push({ timestamp: now, count: 1 });
2733
- if (count >= config.max) {
2734
- const oldestEntry = entries.reduce(
2735
- (oldest, current) => oldest.timestamp < current.timestamp ? oldest : current,
2736
- entries[0]
2737
- );
2738
- const resetAt = oldestEntry.timestamp + config.window;
2739
- return {
2740
- allowed: false,
2741
- remaining: 0,
2742
- resetAt,
2743
- retryAfter: Math.ceil((resetAt - now) / 1e3)
2744
- };
2745
- }
2746
- return {
2747
- allowed: true,
2748
- remaining: config.max - count - 1,
2749
- resetAt: now + config.window
2750
- };
2751
- }
2752
- async reset(type, identifier) {
2753
- const key = this.getKey(type, identifier);
2754
- this.storage.delete(key);
2755
- }
2756
- async resetUser(type, userId, identifier) {
2757
- const userMap = this.userStorage.get(userId);
2758
- if (userMap) {
2759
- const key = this.getKey(type, identifier);
2760
- userMap.delete(key);
2761
- }
2762
- }
2763
- async getStatus(type, identifier) {
2764
- const config = this.limits[type] || this.limits["api:general"];
2765
- const key = this.getKey(type, identifier);
2766
- let entries = this.storage.get(key);
2767
- if (!entries) {
2768
- entries = [];
2769
- this.storage.set(key, entries);
2770
- }
2771
- this.cleanupOldEntries(entries, config.window);
2772
- const now = Date.now();
2773
- const count = entries.reduce((sum, entry) => sum + entry.count, 0);
2774
- return {
2775
- count,
2776
- limit: config.max,
2777
- remaining: Math.max(0, config.max - count),
2778
- resetAt: now + config.window
2779
- };
2780
- }
2781
- setLimit(type, config) {
2782
- this.limits[type] = config;
2783
- }
2784
- setUserLimit(type, config) {
2785
- this.userLimits[type] = config;
2786
- }
2787
- };
2788
- var DEFAULT_RATE_LIMITS2 = {
2789
- "auth:login": { window: 9e5, max: 5 },
2790
- "auth:register": { window: 36e5, max: 3 },
2791
- "auth:forgot": { window: 36e5, max: 3 },
2792
- "auth:reset": { window: 36e5, max: 5 },
2793
- "auth:verify": { window: 36e5, max: 5 },
2794
- "api:general": { window: 6e4, max: 100 },
2795
- "api:authenticated": { window: 6e4, max: 200 }
2796
- };
2797
-
2798
- // src/auth/security/in-memory-lockout.ts
2799
- var InMemoryAccountLockout = class {
2800
- storage = /* @__PURE__ */ new Map();
2801
- history = /* @__PURE__ */ new Map();
2802
- // userId -> attempt timestamps
2803
- config;
2804
- constructor(config = {}) {
2805
- this.config = {
2806
- maxAttempts: 5,
2807
- lockDuration: 9e5,
2808
- // 15 minutes
2809
- notifyUser: true,
2810
- notifyAdmin: true,
2811
- adminNotifyAfter: 3,
2812
- ...config
2813
- };
2814
- }
2815
- async checkLockout(userId) {
2816
- const now = Date.now();
2817
- const record = this.storage.get(userId);
2818
- if (record && record.lockedUntil !== null && record.lockedUntil <= now) {
2819
- await this.resetAttempts(userId);
2820
- return {
2821
- locked: false,
2822
- attemptsRemaining: this.config.maxAttempts,
2823
- totalAttempts: 0
2824
- };
2825
- }
2826
- if (!record) {
2827
- return {
2828
- locked: false,
2829
- attemptsRemaining: this.config.maxAttempts,
2830
- totalAttempts: 0
3954
+ locked: false,
3955
+ attemptsRemaining: this.config.maxAttempts,
3956
+ totalAttempts: 0
2831
3957
  };
2832
3958
  }
2833
3959
  const { attempts, lockedUntil } = record;
@@ -3058,38 +4184,6 @@ var InMemoryAuditLogger = class {
3058
4184
  this.logs = this.logs.filter((log) => log.timestamp >= cutoffDate);
3059
4185
  }
3060
4186
  };
3061
- function createAuditContext2(req) {
3062
- return {
3063
- ipAddress: req.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || req.headers.get("x-real-ip") || "unknown",
3064
- userAgent: req.headers.get("user-agent") || "unknown"
3065
- };
3066
- }
3067
- function defaultExtractToken(req) {
3068
- const authHeader = req.headers.get("Authorization");
3069
- if (authHeader?.startsWith("Bearer ")) {
3070
- return authHeader.slice(7);
3071
- }
3072
- const cookieHeader = req.headers.get("Cookie");
3073
- if (cookieHeader) {
3074
- const cookies = Object.fromEntries(
3075
- cookieHeader.split("; ").map((c) => {
3076
- const [key, ...val] = c.split("=");
3077
- return [key.trim(), val.join("=")];
3078
- })
3079
- );
3080
- return cookies["auth_token"] || null;
3081
- }
3082
- return null;
3083
- }
3084
- function generateToken(payload, secret, options = {}) {
3085
- return jwt2.sign(payload, secret, {
3086
- expiresIn: options.expiresIn || "24h",
3087
- issuer: options.issuer,
3088
- audience: options.audience
3089
- });
3090
- }
3091
-
3092
- // src/api/rest/auth-routes.ts
3093
4187
  var AuthRoutes = class {
3094
4188
  authAdapter;
3095
4189
  email;
@@ -3118,7 +4212,7 @@ var AuthRoutes = class {
3118
4212
  this.emailVerificationRequired = config.emailVerificationRequired ?? true;
3119
4213
  }
3120
4214
  async register(req) {
3121
- const { ipAddress, userAgent } = createAuditContext2(req);
4215
+ const { ipAddress, userAgent } = createAuditContext(req);
3122
4216
  if (this.rateLimiter) {
3123
4217
  const limit = await this.rateLimiter.check("auth:register", ipAddress);
3124
4218
  if (!limit.allowed) {
@@ -3141,10 +4235,9 @@ var AuthRoutes = class {
3141
4235
  if (existingUser) {
3142
4236
  return this.errorResponse("Email already registered", 400);
3143
4237
  }
3144
- const passwordHash = await this.authAdapter.hashPassword(body.password);
3145
4238
  const user = await this.authAdapter.createUser({
3146
4239
  email: body.email,
3147
- passwordHash,
4240
+ password: body.password,
3148
4241
  role: body.role || "customer",
3149
4242
  tenantId: body.tenantId
3150
4243
  });
@@ -3180,7 +4273,7 @@ var AuthRoutes = class {
3180
4273
  }
3181
4274
  }
3182
4275
  async login(req) {
3183
- const { ipAddress, userAgent } = createAuditContext2(req);
4276
+ const { ipAddress, userAgent } = createAuditContext(req);
3184
4277
  if (this.rateLimiter) {
3185
4278
  const limit = await this.rateLimiter.check("auth:login", ipAddress);
3186
4279
  if (!limit.allowed) {
@@ -3277,9 +4370,9 @@ var AuthRoutes = class {
3277
4370
  if (!token) {
3278
4371
  return this.errorResponse("No session to logout", 401);
3279
4372
  }
3280
- const { ipAddress, userAgent } = createAuditContext2(req);
4373
+ const { ipAddress, userAgent } = createAuditContext(req);
3281
4374
  try {
3282
- const payload = jwt2.decode(token);
4375
+ const payload = jwt.decode(token);
3283
4376
  if (payload && payload.sub) {
3284
4377
  await this.authAdapter.deleteUserSessions(payload.sub);
3285
4378
  if (this.auditLogger) {
@@ -3320,7 +4413,7 @@ var AuthRoutes = class {
3320
4413
  return this.errorResponse("Not authenticated", 401);
3321
4414
  }
3322
4415
  try {
3323
- const payload = jwt2.verify(token, this.jwtSecret, {
4416
+ const payload = jwt.verify(token, this.jwtSecret, {
3324
4417
  issuer: this.jwtIssuer,
3325
4418
  audience: this.jwtAudience
3326
4419
  });
@@ -3341,9 +4434,9 @@ var AuthRoutes = class {
3341
4434
  if (!token) {
3342
4435
  return this.errorResponse("Not authenticated", 401);
3343
4436
  }
3344
- const { ipAddress, userAgent } = createAuditContext2(req);
4437
+ const { ipAddress, userAgent } = createAuditContext(req);
3345
4438
  try {
3346
- const payload = jwt2.verify(token, this.jwtSecret);
4439
+ const payload = jwt.verify(token, this.jwtSecret);
3347
4440
  const body = await req.json();
3348
4441
  const { currentPassword, newPassword, confirmPassword } = body;
3349
4442
  if (!currentPassword || !newPassword) {
@@ -3417,7 +4510,7 @@ var AuthRoutes = class {
3417
4510
  }
3418
4511
  }
3419
4512
  async forgotPassword(req) {
3420
- const { ipAddress, userAgent } = createAuditContext2(req);
4513
+ const { ipAddress, userAgent } = createAuditContext(req);
3421
4514
  if (this.rateLimiter) {
3422
4515
  const limit = await this.rateLimiter.check("auth:forgot", ipAddress);
3423
4516
  if (!limit.allowed) {
@@ -3550,8 +4643,8 @@ function detectDatabaseType() {
3550
4643
  }
3551
4644
  try {
3552
4645
  const { readFileSync } = __require("fs");
3553
- const { join } = __require("path");
3554
- const configPath = join(process.cwd(), "kyro.config.ts");
4646
+ const { join: join2 } = __require("path");
4647
+ const configPath = join2(process.cwd(), "kyro.config.ts");
3555
4648
  const configContent = readFileSync(configPath, "utf8");
3556
4649
  if (configContent.includes("createLocalAdapter")) {
3557
4650
  return "sqlite";
@@ -3593,7 +4686,7 @@ async function createAuthConfig(databaseType) {
3593
4686
  const distributed = getEnvBool("KYRO_DISTRIBUTED", false);
3594
4687
  let authAdapter;
3595
4688
  if (distributed) {
3596
- const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-4YDY4LWE.js');
4689
+ const { RedisAuthAdapter: RedisAuthAdapter2 } = await import('./redis-adapter-RA24FNCX.js');
3597
4690
  const redisUrl = getEnv("REDIS_URL", "redis://localhost:6379");
3598
4691
  const redisTls = getEnvBool("REDIS_TLS", false);
3599
4692
  const redisAdapter = new RedisAuthAdapter2({ url: redisUrl, tls: redisTls });
@@ -3606,8 +4699,7 @@ async function createAuthConfig(databaseType) {
3606
4699
  await authAdapter.connect();
3607
4700
  }
3608
4701
  }
3609
- const emailConfig = getEmailConfig();
3610
- const email = emailConfig ? new EmailTransport(emailConfig) : void 0;
4702
+ const email = EmailTransport.fromEnv() || void 0;
3611
4703
  const passwordPolicy = new PasswordPolicy({
3612
4704
  minLength: getEnvNum("PASSWORD_MIN_LENGTH", 12),
3613
4705
  requireUppercase: getEnvBool("PASSWORD_REQUIRE_UPPERCASE", true),
@@ -3684,21 +4776,6 @@ async function createAuthConfig(databaseType) {
3684
4776
  routes
3685
4777
  };
3686
4778
  }
3687
- function getEmailConfig() {
3688
- const host = getEnv("SMTP_HOST");
3689
- if (!host) return void 0;
3690
- return {
3691
- host,
3692
- port: getEnvNum("SMTP_PORT", 587),
3693
- secure: getEnvBool("SMTP_SECURE", false),
3694
- auth: {
3695
- user: getEnv("SMTP_USER"),
3696
- pass: getEnv("SMTP_PASS")
3697
- },
3698
- from: getEnv("SMTP_FROM", "noreply@example.com"),
3699
- fromName: getEnv("SMTP_FROM_NAME", "Kyro CMS")
3700
- };
3701
- }
3702
4779
  var authConfig = createAuthConfig();
3703
4780
 
3704
4781
  // src/auth/index.ts
@@ -3725,10 +4802,9 @@ var Auth = class {
3725
4802
  if (existing) {
3726
4803
  return { success: false, error: "Email already registered" };
3727
4804
  }
3728
- const passwordHash = await this.hashPassword(data.password);
3729
4805
  const user = await this.adapter.createUser({
3730
4806
  email: data.email,
3731
- passwordHash,
4807
+ password: data.password,
3732
4808
  role: data.role ?? "customer",
3733
4809
  tenantId: data.tenantId
3734
4810
  });
@@ -3739,15 +4815,11 @@ var Auth = class {
3739
4815
  }
3740
4816
  async login(credentials) {
3741
4817
  try {
3742
- const user = await this.adapter.findUserByEmail(credentials.email);
3743
- if (!user || !user.passwordHash) {
3744
- return { success: false, error: "Invalid credentials" };
3745
- }
3746
- const valid = await this.adapter.verifyPassword(
3747
- credentials.password,
3748
- user.passwordHash
4818
+ const user = await this.adapter.verifyPassword(
4819
+ credentials.email,
4820
+ credentials.password
3749
4821
  );
3750
- if (!valid) {
4822
+ if (!user) {
3751
4823
  return { success: false, error: "Invalid credentials" };
3752
4824
  }
3753
4825
  return this.createSessionForUser(user);
@@ -3776,7 +4848,7 @@ var Auth = class {
3776
4848
  }
3777
4849
  async verifyToken(token) {
3778
4850
  try {
3779
- const decoded = jwt2.verify(token, this.config.secret, {
4851
+ const decoded = jwt.verify(token, this.config.secret, {
3780
4852
  issuer: this.config.issuer,
3781
4853
  audience: this.config.audience.length > 0 ? this.config.audience[0] : void 0
3782
4854
  });
@@ -3793,18 +4865,17 @@ var Auth = class {
3793
4865
  async changePassword(userId, currentPassword, newPassword) {
3794
4866
  try {
3795
4867
  const user = await this.adapter.findUserById(userId);
3796
- if (!user || !user.passwordHash) {
4868
+ if (!user) {
3797
4869
  return { success: false, error: "User not found" };
3798
4870
  }
3799
4871
  const valid = await this.adapter.verifyPassword(
3800
- currentPassword,
3801
- user.passwordHash
4872
+ user.email,
4873
+ currentPassword
3802
4874
  );
3803
4875
  if (!valid) {
3804
4876
  return { success: false, error: "Current password is incorrect" };
3805
4877
  }
3806
- const newHash = await this.hashPassword(newPassword);
3807
- await this.adapter.updateUser(userId, { passwordHash: newHash });
4878
+ await this.adapter.updateUser(userId, { password: newPassword });
3808
4879
  await this.adapter.deleteUserSessions(userId);
3809
4880
  return { success: true, user };
3810
4881
  } catch (error) {
@@ -3817,8 +4888,7 @@ var Auth = class {
3817
4888
  if (!user) {
3818
4889
  return { success: false, error: "User not found" };
3819
4890
  }
3820
- const passwordHash = await this.hashPassword(newPassword);
3821
- await this.adapter.updateUser(user.id, { passwordHash });
4891
+ await this.adapter.updateUser(user.id, { password: newPassword });
3822
4892
  await this.adapter.deleteUserSessions(user.id);
3823
4893
  return { success: true, user };
3824
4894
  } catch (error) {
@@ -3862,7 +4932,7 @@ var Auth = class {
3862
4932
  if (this.config.audience.length > 0) {
3863
4933
  signOptions.audience = this.config.audience[0];
3864
4934
  }
3865
- return jwt2.sign(payload, this.config.secret, signOptions);
4935
+ return jwt.sign(payload, this.config.secret, signOptions);
3866
4936
  }
3867
4937
  async hashPassword(password) {
3868
4938
  return bcrypt.hash(password, this.config.saltRounds);
@@ -4036,6 +5106,1590 @@ function isDraft(status) {
4036
5106
  function isArchived(status) {
4037
5107
  return status === "archived";
4038
5108
  }
5109
+ function createLocalStorage(config) {
5110
+ const { uploadDir, baseUrl = "/uploads" } = config;
5111
+ async function ensureDir(dir) {
5112
+ if (!existsSync(dir)) {
5113
+ await mkdir(dir, { recursive: true });
5114
+ }
5115
+ }
5116
+ function getMimeType(filename) {
5117
+ const ext = extname(filename).toLowerCase();
5118
+ const mimeTypes = {
5119
+ ".jpg": "image/jpeg",
5120
+ ".jpeg": "image/jpeg",
5121
+ ".png": "image/png",
5122
+ ".gif": "image/gif",
5123
+ ".webp": "image/webp",
5124
+ ".svg": "image/svg+xml",
5125
+ ".mp4": "video/mp4",
5126
+ ".webm": "video/webm",
5127
+ ".pdf": "application/pdf",
5128
+ ".doc": "application/msword",
5129
+ ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
5130
+ ".xls": "application/vnd.ms-excel",
5131
+ ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
5132
+ ".zip": "application/zip"
5133
+ };
5134
+ return mimeTypes[ext] || "application/octet-stream";
5135
+ }
5136
+ async function getImageDimensions(buffer) {
5137
+ try {
5138
+ const header = buffer.toString("hex", 0, 8);
5139
+ if (header.startsWith("89504e47")) {
5140
+ const width = buffer.readUInt32BE(16);
5141
+ const height = buffer.readUInt32BE(20);
5142
+ return { width, height };
5143
+ }
5144
+ if (header.startsWith("ffd8")) {
5145
+ let offset = 2;
5146
+ while (offset < buffer.length) {
5147
+ if (buffer[offset] !== 255) break;
5148
+ const marker = buffer[offset + 1];
5149
+ if (marker === 192 || marker === 194) {
5150
+ const height = buffer.readUInt16BE(offset + 5);
5151
+ const width = buffer.readUInt16BE(offset + 7);
5152
+ return { width, height };
5153
+ }
5154
+ offset += 2 + buffer.readUInt16BE(offset + 2);
5155
+ }
5156
+ }
5157
+ } catch {
5158
+ }
5159
+ return {};
5160
+ }
5161
+ return {
5162
+ name: "local",
5163
+ displayName: "Local Storage",
5164
+ supportsDynamicResize: true,
5165
+ async upload(file, options) {
5166
+ await ensureDir(uploadDir);
5167
+ const buffer = Buffer.from(await file.arrayBuffer());
5168
+ const hash = createHash("md5").update(buffer).digest("hex");
5169
+ const ext = extname(file.name);
5170
+ const filename = options?.filename || `${hash}${ext}`;
5171
+ const folder = options?.folder || "";
5172
+ const targetDir = folder ? join(uploadDir, folder) : uploadDir;
5173
+ await ensureDir(targetDir);
5174
+ const filepath = join(targetDir, filename);
5175
+ await writeFile(filepath, buffer);
5176
+ const dimensions = file.type.startsWith("image/") ? await getImageDimensions(buffer) : {};
5177
+ const normalizedBaseUrl = baseUrl || "/uploads";
5178
+ const urlPath = folder ? `/${folder}/${filename}` : `/${filename}`;
5179
+ const url = normalizedBaseUrl + urlPath;
5180
+ return {
5181
+ id: hash,
5182
+ filename,
5183
+ originalName: file.name,
5184
+ mimeType: file.type || getMimeType(file.name),
5185
+ size: buffer.length,
5186
+ url,
5187
+ thumbnailUrl: file.type.startsWith("image/") ? url : void 0,
5188
+ folder: folder || void 0,
5189
+ provider: "local",
5190
+ metadata: {
5191
+ ...dimensions,
5192
+ ...options?.metadata
5193
+ },
5194
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5195
+ };
5196
+ },
5197
+ async uploadFromUrl(url, options) {
5198
+ const response = await fetch(url);
5199
+ if (!response.ok) {
5200
+ throw new Error(`Failed to fetch file: ${response.statusText}`);
5201
+ }
5202
+ const blob = await response.blob();
5203
+ const contentDisposition = response.headers.get("content-disposition");
5204
+ let filename = options?.filename;
5205
+ if (!filename && contentDisposition) {
5206
+ const match = contentDisposition.match(
5207
+ /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
5208
+ );
5209
+ if (match) {
5210
+ filename = match[1].replace(/['"]/g, "");
5211
+ }
5212
+ }
5213
+ if (!filename) {
5214
+ filename = basename(new URL(url).pathname);
5215
+ }
5216
+ const file = new File([blob], filename, {
5217
+ type: blob.type || getMimeType(filename)
5218
+ });
5219
+ return this.upload(file, options);
5220
+ },
5221
+ async delete(url) {
5222
+ const filepath = join(uploadDir, url.replace(baseUrl + "/", ""));
5223
+ try {
5224
+ await unlink(filepath);
5225
+ } catch {
5226
+ }
5227
+ },
5228
+ async rename(oldUrl, newFilename) {
5229
+ const oldPath = join(uploadDir, oldUrl.replace(baseUrl + "/", ""));
5230
+ const newPath = join(uploadDir, newFilename);
5231
+ await rename(oldPath, newPath);
5232
+ return `${baseUrl}/${newFilename}`;
5233
+ },
5234
+ getImageUrl(url, transforms) {
5235
+ if (!transforms || Object.keys(transforms).length === 0) return url;
5236
+ const params = new URLSearchParams({ url });
5237
+ if (transforms.width) params.set("w", String(transforms.width));
5238
+ if (transforms.height) params.set("h", String(transforms.height));
5239
+ if (transforms.quality) params.set("q", String(transforms.quality));
5240
+ if (transforms.format) params.set("f", transforms.format);
5241
+ return `/api/media/resize?${params.toString()}`;
5242
+ },
5243
+ async generateThumbnail(file) {
5244
+ if (!file.mimeType.startsWith("image/")) return "";
5245
+ return this.getImageUrl(file.url, { width: 400, height: 400 });
5246
+ },
5247
+ async list(prefix) {
5248
+ const dir = prefix ? join(uploadDir, prefix) : uploadDir;
5249
+ if (!existsSync(dir)) return [];
5250
+ const files = [];
5251
+ const entries = await readdir(dir, { withFileTypes: true });
5252
+ for (const entry of entries) {
5253
+ if (entry.isFile()) {
5254
+ const filepath = join(dir, entry.name);
5255
+ const stats = await stat(filepath);
5256
+ const url = `${baseUrl}${prefix ? `/${prefix}` : ""}/${entry.name}`;
5257
+ files.push({
5258
+ id: createHash("md5").update(entry.name).digest("hex"),
5259
+ filename: entry.name,
5260
+ originalName: entry.name,
5261
+ mimeType: getMimeType(entry.name),
5262
+ size: stats.size,
5263
+ url,
5264
+ provider: "local",
5265
+ createdAt: stats.birthtime.toISOString()
5266
+ });
5267
+ }
5268
+ }
5269
+ return files;
5270
+ },
5271
+ async exists(url) {
5272
+ const filepath = join(uploadDir, url.replace(baseUrl + "/", ""));
5273
+ return existsSync(filepath);
5274
+ }
5275
+ };
5276
+ }
5277
+ function extractPublicDevUrlId(url) {
5278
+ if (!url) return "";
5279
+ if (url.startsWith("pub-")) return url;
5280
+ const match = url.match(/pub-[a-zA-Z0-9]+/i);
5281
+ return match ? match[0] : "";
5282
+ }
5283
+ function getPublicUrl(key, config) {
5284
+ const normalizedKey = key.startsWith("/") ? key.slice(1) : key;
5285
+ if (config.cdnUrl) {
5286
+ const cdn = config.cdnUrl.replace(/\/$/, "");
5287
+ return `${cdn}/${normalizedKey}`;
5288
+ }
5289
+ switch (config.provider) {
5290
+ case "r2": {
5291
+ const pubId = extractPublicDevUrlId(config.publicDevUrl);
5292
+ if (pubId) {
5293
+ return `https://${pubId}.r2.dev/${normalizedKey}`;
5294
+ }
5295
+ return `https://${config.bucket}.${config.accountId}.r2.cloudflarestorage.com/${normalizedKey}`;
5296
+ }
5297
+ case "gcs":
5298
+ return `https://storage.googleapis.com/${config.bucket}/${normalizedKey}`;
5299
+ case "digitalocean":
5300
+ return `https://${config.bucket}.${config.region}.cdn.digitaloceanspaces.com/${normalizedKey}`;
5301
+ case "backblaze":
5302
+ return `https://${config.bucket}.s3.backblazeb2.com/${normalizedKey}`;
5303
+ case "wasabi":
5304
+ return `https://${config.bucket}.s3.wasabisys.com/${normalizedKey}`;
5305
+ case "aws":
5306
+ default:
5307
+ return `https://${config.bucket}.s3.${config.region}.amazonaws.com/${normalizedKey}`;
5308
+ }
5309
+ }
5310
+ function getUrlPrefix(config) {
5311
+ if (config.cdnUrl) {
5312
+ return config.cdnUrl.replace(/\/$/, "") + "/";
5313
+ }
5314
+ switch (config.provider) {
5315
+ case "r2": {
5316
+ const pubId = extractPublicDevUrlId(config.publicDevUrl);
5317
+ if (pubId) {
5318
+ return `https://${pubId}.r2.dev/`;
5319
+ }
5320
+ return `https://${config.bucket}.${config.accountId}.r2.cloudflarestorage.com/`;
5321
+ }
5322
+ case "gcs":
5323
+ return `https://storage.googleapis.com/${config.bucket}/`;
5324
+ case "digitalocean":
5325
+ return `https://${config.bucket}.${config.region}.cdn.digitaloceanspaces.com/`;
5326
+ case "backblaze":
5327
+ return `https://${config.bucket}.s3.backblazeb2.com/`;
5328
+ case "wasabi":
5329
+ return `https://${config.bucket}.s3.wasabisys.com/`;
5330
+ case "aws":
5331
+ default:
5332
+ return `https://${config.bucket}.s3.${config.region}.amazonaws.com/`;
5333
+ }
5334
+ }
5335
+ function getDisplayName(provider) {
5336
+ switch (provider) {
5337
+ case "r2":
5338
+ return "Cloudflare R2";
5339
+ case "gcs":
5340
+ return "Google Cloud Storage";
5341
+ case "digitalocean":
5342
+ return "DigitalOcean Spaces";
5343
+ case "backblaze":
5344
+ return "Backblaze B2";
5345
+ case "wasabi":
5346
+ return "Wasabi";
5347
+ case "aws":
5348
+ default:
5349
+ return "AWS S3";
5350
+ }
5351
+ }
5352
+ function createS3Storage(config) {
5353
+ console.log("[createS3Storage] Creating provider:", config.provider);
5354
+ console.log("[createS3Storage] Credentials:", {
5355
+ accessKeyId: config.accessKeyId ? "SET" : "UNDEFINED",
5356
+ secretAccessKey: config.secretAccessKey ? "SET" : "UNDEFINED",
5357
+ bucket: config.bucket,
5358
+ accountId: config.accountId,
5359
+ endpoint: config.endpoint
5360
+ });
5361
+ const client = new S3Client({
5362
+ region: config.region || "auto",
5363
+ endpoint: config.endpoint,
5364
+ credentials: {
5365
+ accessKeyId: config.accessKeyId,
5366
+ secretAccessKey: config.secretAccessKey
5367
+ },
5368
+ forcePathStyle: true,
5369
+ tls: true,
5370
+ // R2 requires specific SSL configuration
5371
+ ...config.provider === "r2" && {
5372
+ requestHandler: new (init_dist_es4(), __toCommonJS(dist_es_exports)).NodeHttpHandler({
5373
+ connectionTimeout: 1e4,
5374
+ socketTimeout: 1e4
5375
+ })
5376
+ }
5377
+ });
5378
+ const getKey = (path2) => {
5379
+ const prefix = config.prefix ? `${config.prefix}/` : "";
5380
+ return `${prefix}${path2}`.replace(/\/+/g, "/");
5381
+ };
5382
+ const getUrl = (key) => getPublicUrl(key, config);
5383
+ return {
5384
+ name: config.provider,
5385
+ displayName: getDisplayName(config.provider),
5386
+ supportsDynamicResize: true,
5387
+ async upload(file, options) {
5388
+ const key = getKey(
5389
+ `${options?.folder ? `${options.folder}/` : ""}${options?.filename || file.name}`
5390
+ );
5391
+ const buffer = Buffer.from(await file.arrayBuffer());
5392
+ await client.send(
5393
+ new PutObjectCommand({
5394
+ Bucket: config.bucket,
5395
+ Key: key,
5396
+ Body: buffer,
5397
+ ContentType: file.type,
5398
+ Metadata: options?.metadata
5399
+ })
5400
+ );
5401
+ const head = await client.send(
5402
+ new HeadObjectCommand({
5403
+ Bucket: config.bucket,
5404
+ Key: key
5405
+ })
5406
+ );
5407
+ return {
5408
+ id: Buffer.from(key).toString("base64url"),
5409
+ filename: options?.filename || file.name,
5410
+ originalName: file.name,
5411
+ mimeType: file.type,
5412
+ size: buffer.length,
5413
+ url: getUrl(key),
5414
+ thumbnailUrl: file.type.startsWith("image/") ? getUrl(key) : void 0,
5415
+ folder: options?.folder,
5416
+ provider: config.provider,
5417
+ metadata: {
5418
+ ...options?.metadata,
5419
+ etag: head.ETag
5420
+ },
5421
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5422
+ };
5423
+ },
5424
+ async uploadFromUrl(url) {
5425
+ const response = await fetch(url);
5426
+ if (!response.ok) {
5427
+ throw new Error(`Failed to fetch: ${response.statusText}`);
5428
+ }
5429
+ const blob = await response.blob();
5430
+ const filename = url.split("/").pop() || "file";
5431
+ const file = new File([blob], filename, { type: blob.type });
5432
+ return this.upload(file);
5433
+ },
5434
+ async delete(url) {
5435
+ const key = url.replace(getUrlPrefix(config), "");
5436
+ await client.send(
5437
+ new DeleteObjectCommand({
5438
+ Bucket: config.bucket,
5439
+ Key: key
5440
+ })
5441
+ );
5442
+ },
5443
+ async rename(oldUrl, newKey) {
5444
+ const oldKey = oldUrl.replace(getUrlPrefix(config), "");
5445
+ const newKeyWithPrefix = config.prefix ? `${config.prefix}/${newKey}` : newKey;
5446
+ await client.send(
5447
+ new CopyObjectCommand({
5448
+ Bucket: config.bucket,
5449
+ CopySource: `${config.bucket}/${oldKey}`,
5450
+ Key: newKeyWithPrefix
5451
+ })
5452
+ );
5453
+ await client.send(
5454
+ new DeleteObjectCommand({
5455
+ Bucket: config.bucket,
5456
+ Key: oldKey
5457
+ })
5458
+ );
5459
+ return getUrl(newKeyWithPrefix);
5460
+ },
5461
+ getImageUrl(url, transforms) {
5462
+ if (!transforms || Object.keys(transforms).length === 0) return url;
5463
+ const params = new URLSearchParams({ url });
5464
+ if (transforms.width) params.set("w", String(transforms.width));
5465
+ if (transforms.height) params.set("h", String(transforms.height));
5466
+ if (transforms.quality) params.set("q", String(transforms.quality));
5467
+ if (transforms.format) params.set("f", transforms.format);
5468
+ return `/api/media/resize?${params.toString()}`;
5469
+ },
5470
+ async generateThumbnail(file) {
5471
+ return this.getImageUrl(file.url, { width: 400, height: 400 });
5472
+ },
5473
+ async list(prefix) {
5474
+ const key = getKey(prefix || "");
5475
+ const response = await client.send(
5476
+ new ListObjectsV2Command({
5477
+ Bucket: config.bucket,
5478
+ Prefix: key
5479
+ })
5480
+ );
5481
+ return (response.Contents || []).map((item) => ({
5482
+ id: Buffer.from(item.Key || "").toString("base64url"),
5483
+ filename: item.Key?.split("/").pop() || "",
5484
+ originalName: item.Key?.split("/").pop() || "",
5485
+ mimeType: "application/octet-stream",
5486
+ size: item.Size || 0,
5487
+ url: getUrl(item.Key || ""),
5488
+ provider: config.provider,
5489
+ createdAt: item.LastModified?.toISOString() || (/* @__PURE__ */ new Date()).toISOString()
5490
+ }));
5491
+ },
5492
+ async exists(url) {
5493
+ try {
5494
+ const key = url.replace(getUrlPrefix(config), "");
5495
+ await client.send(
5496
+ new HeadObjectCommand({
5497
+ Bucket: config.bucket,
5498
+ Key: key
5499
+ })
5500
+ );
5501
+ return true;
5502
+ } catch {
5503
+ return false;
5504
+ }
5505
+ }
5506
+ };
5507
+ }
5508
+
5509
+ // src/storage/cloudinary.ts
5510
+ function createCloudinaryStorage(config) {
5511
+ const getBaseUrl = () => `https://api.cloudinary.com/v1_1/${config.cloudName}/upload`;
5512
+ const generateSignature = async (params) => {
5513
+ const crypto = await import('crypto');
5514
+ const sortedParams = Object.keys(params).sort().map((key) => `${key}=${params[key]}`).join("&");
5515
+ return crypto.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
5516
+ };
5517
+ return {
5518
+ name: "cloudinary",
5519
+ displayName: "Cloudinary",
5520
+ supportsDynamicResize: true,
5521
+ async upload(file, options) {
5522
+ const formData = new FormData();
5523
+ formData.append("file", file);
5524
+ if (config.uploadPreset) {
5525
+ formData.append("upload_preset", config.uploadPreset);
5526
+ } else {
5527
+ const timestamp = Math.round(Date.now() / 1e3);
5528
+ const signatureParams = {
5529
+ timestamp: String(timestamp)
5530
+ };
5531
+ if (options?.folder || config.folder) {
5532
+ signatureParams.folder = options?.folder || config.folder || "";
5533
+ }
5534
+ const signature = await generateSignature(signatureParams);
5535
+ formData.append("timestamp", String(timestamp));
5536
+ formData.append("signature", signature);
5537
+ formData.append("api_key", config.apiKey);
5538
+ }
5539
+ if (options?.folder || config.folder) {
5540
+ formData.append("folder", options?.folder || config.folder || "");
5541
+ }
5542
+ const response = await fetch(getBaseUrl(), {
5543
+ method: "POST",
5544
+ body: formData
5545
+ });
5546
+ if (!response.ok) {
5547
+ const error = await response.json();
5548
+ throw new Error(
5549
+ `Cloudinary upload failed: ${error.error?.message || response.statusText}`
5550
+ );
5551
+ }
5552
+ const data = await response.json();
5553
+ return {
5554
+ id: data.public_id,
5555
+ filename: `${data.public_id}.${data.format}`,
5556
+ originalName: data.original_filename || file.name,
5557
+ mimeType: file.type || `image/${data.format}`,
5558
+ size: data.bytes,
5559
+ url: data.secure_url,
5560
+ thumbnailUrl: this.getImageUrl(data.secure_url, {
5561
+ width: 200,
5562
+ height: 200,
5563
+ fit: "crop"
5564
+ }),
5565
+ width: data.width,
5566
+ height: data.height,
5567
+ folder: options?.folder || config.folder,
5568
+ provider: "cloudinary",
5569
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5570
+ };
5571
+ },
5572
+ async uploadFromUrl(url, options) {
5573
+ const formData = new FormData();
5574
+ formData.append("file", url);
5575
+ formData.append("upload_preset", "ml_default");
5576
+ if (options?.folder || config.folder) {
5577
+ formData.append("folder", options?.folder || config.folder || "");
5578
+ }
5579
+ const response = await fetch(getBaseUrl(), {
5580
+ method: "POST",
5581
+ body: formData
5582
+ });
5583
+ if (!response.ok) throw new Error("Cloudinary upload failed");
5584
+ const data = await response.json();
5585
+ return {
5586
+ id: data.public_id,
5587
+ filename: `${data.public_id}.${data.format}`,
5588
+ originalName: data.original_filename || url.split("/").pop() || "file",
5589
+ mimeType: `image/${data.format}`,
5590
+ size: data.bytes,
5591
+ url: data.secure_url,
5592
+ thumbnailUrl: this.getImageUrl(data.secure_url, {
5593
+ width: 200,
5594
+ height: 200,
5595
+ fit: "crop"
5596
+ }),
5597
+ width: data.width,
5598
+ height: data.height,
5599
+ provider: "cloudinary",
5600
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5601
+ };
5602
+ },
5603
+ async delete(id) {
5604
+ try {
5605
+ const parts = id.split("/image/upload/");
5606
+ if (parts.length !== 2) {
5607
+ console.warn("[Cloudinary delete] Could not parse URL:", id);
5608
+ return;
5609
+ }
5610
+ const publicId = parts[1].replace(/^v\d+\//, "").replace(/\.[^.]+$/, "");
5611
+ const timestamp = Math.round(Date.now() / 1e3);
5612
+ const signatureParams = {
5613
+ timestamp: String(timestamp),
5614
+ public_id: publicId
5615
+ };
5616
+ const sortedParams = Object.keys(signatureParams).sort().map((key) => `${key}=${signatureParams[key]}`).join("&");
5617
+ const crypto = await import('crypto');
5618
+ const signature = crypto.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
5619
+ const deleteUrl = `https://api.cloudinary.com/v1_1/${config.cloudName}/image/destroy`;
5620
+ const formData = new FormData();
5621
+ formData.append("public_id", publicId);
5622
+ formData.append("timestamp", String(timestamp));
5623
+ formData.append("signature", signature);
5624
+ formData.append("api_key", config.apiKey);
5625
+ const response = await fetch(deleteUrl, {
5626
+ method: "POST",
5627
+ body: formData
5628
+ });
5629
+ if (!response.ok) {
5630
+ const error = await response.json();
5631
+ console.warn("[Cloudinary delete] Failed:", error);
5632
+ }
5633
+ } catch (e) {
5634
+ console.warn("[Cloudinary delete] Error:", e.message);
5635
+ }
5636
+ },
5637
+ async rename(oldUrl, newKey) {
5638
+ let version = "";
5639
+ let newPublicId = newKey.replace(/\.[^.]+$/, "");
5640
+ let folder = "";
5641
+ try {
5642
+ const parts = oldUrl.split("/image/upload/");
5643
+ if (parts.length !== 2) {
5644
+ console.warn("[Cloudinary rename] Could not parse old URL:", oldUrl);
5645
+ return oldUrl;
5646
+ }
5647
+ const urlPath = parts[1];
5648
+ const versionMatch = urlPath.match(/^(v\d+)\//);
5649
+ version = versionMatch ? versionMatch[1] : "";
5650
+ const publicIdWithoutVersion = urlPath.replace(/^v\d+\//, "").replace(/\.[^.]+$/, "");
5651
+ const folderParts = publicIdWithoutVersion.split("/");
5652
+ folder = folderParts.length > 1 ? folderParts.slice(0, -1).join("/") : "";
5653
+ const newFilename = newKey.replace(/\.[^.]+$/, "");
5654
+ newPublicId = folder ? `${folder}/${newFilename}` : newFilename;
5655
+ console.log("[Cloudinary rename]", {
5656
+ urlPath,
5657
+ version,
5658
+ publicIdWithoutVersion,
5659
+ folder,
5660
+ newPublicId
5661
+ });
5662
+ const timestamp = Math.round(Date.now() / 1e3);
5663
+ const signatureParams = {
5664
+ from_public_id: publicIdWithoutVersion,
5665
+ to_public_id: newPublicId,
5666
+ timestamp: String(timestamp)
5667
+ };
5668
+ const sortedParams = Object.keys(signatureParams).sort().map((key) => `${key}=${signatureParams[key]}`).join("&");
5669
+ const crypto = await import('crypto');
5670
+ const signature = crypto.createHash("sha256").update(sortedParams + config.apiSecret).digest("hex");
5671
+ const formData = new FormData();
5672
+ formData.append("from_public_id", publicIdWithoutVersion);
5673
+ formData.append("to_public_id", newPublicId);
5674
+ formData.append("timestamp", String(timestamp));
5675
+ formData.append("signature", signature);
5676
+ formData.append("api_key", config.apiKey);
5677
+ const response = await fetch(
5678
+ `https://api.cloudinary.com/v1_1/${config.cloudName}/image/rename`,
5679
+ {
5680
+ method: "POST",
5681
+ body: formData
5682
+ }
5683
+ );
5684
+ if (response.ok) {
5685
+ const data = await response.json();
5686
+ console.log("[Cloudinary rename] Success:", data.secure_url);
5687
+ return data.secure_url;
5688
+ } else {
5689
+ const error = await response.json();
5690
+ console.warn("[Cloudinary rename] Failed:", error);
5691
+ const versionStr = version ? `/${version}/` : "/";
5692
+ return `https://res.cloudinary.com/${config.cloudName}/image/upload${versionStr}${newPublicId}.${newKey.split(".").pop()}`;
5693
+ }
5694
+ } catch (e) {
5695
+ console.warn("[Cloudinary rename] Error:", e.message);
5696
+ const versionStr = version ? `/${version}/` : "/";
5697
+ return `https://res.cloudinary.com/${config.cloudName}/image/upload${versionStr}${newPublicId}.${newKey.split(".").pop()}`;
5698
+ }
5699
+ },
5700
+ getImageUrl(url, transforms) {
5701
+ if (!transforms) return url;
5702
+ const parts = url.split("/upload/");
5703
+ if (parts.length !== 2) return url;
5704
+ const transformationArr = [];
5705
+ if (transforms.width) transformationArr.push(`w_${transforms.width}`);
5706
+ if (transforms.height) transformationArr.push(`h_${transforms.height}`);
5707
+ if (transforms.fit) {
5708
+ const fitMap = {
5709
+ crop: "c_fill",
5710
+ clip: "c_fit",
5711
+ scale: "c_scale",
5712
+ fill: "c_fill"
5713
+ };
5714
+ transformationArr.push(fitMap[transforms.fit] || "c_limit");
5715
+ }
5716
+ if (transforms.quality) transformationArr.push(`q_${transforms.quality}`);
5717
+ if (transforms.format) transformationArr.push(`f_${transforms.format}`);
5718
+ const transformationStr = transformationArr.join(",");
5719
+ return `${parts[0]}/upload/${transformationStr}/${parts[1]}`;
5720
+ },
5721
+ async generateThumbnail(file) {
5722
+ return this.getImageUrl(file.url, {
5723
+ width: 200,
5724
+ height: 200,
5725
+ fit: "crop"
5726
+ });
5727
+ },
5728
+ async list() {
5729
+ return [];
5730
+ },
5731
+ async exists(url) {
5732
+ const response = await fetch(url, { method: "HEAD" });
5733
+ return response.ok;
5734
+ }
5735
+ };
5736
+ }
5737
+
5738
+ // src/storage/imgix.ts
5739
+ function createImgixStorage(config) {
5740
+ const signUrl = (path2, params) => {
5741
+ if (!config.signKey) return path2;
5742
+ const signer = new TextEncoder();
5743
+ const key = signer.encode(config.signKey);
5744
+ const data = signer.encode(path2 + params.toString());
5745
+ let hash = 0;
5746
+ const combined = new Uint8Array(key.length + data.length);
5747
+ combined.set(key);
5748
+ combined.set(data, key.length);
5749
+ for (let i = 0; i < combined.length; i++) {
5750
+ hash = (hash << 5) - hash + combined[i] | 0;
5751
+ }
5752
+ params.set("s", Math.abs(hash).toString(16));
5753
+ return path2;
5754
+ };
5755
+ return {
5756
+ name: "imgix",
5757
+ displayName: "Imgix",
5758
+ supportsDynamicResize: true,
5759
+ async upload(_file, _options) {
5760
+ throw new Error(
5761
+ "Imgix is a transformation service. Use another provider for uploads."
5762
+ );
5763
+ },
5764
+ async uploadFromUrl(url, options) {
5765
+ const filename = options?.filename || url.split("/").pop() || "file";
5766
+ const response = await fetch(url);
5767
+ if (!response.ok) {
5768
+ throw new Error(`Failed to fetch: ${response.statusText}`);
5769
+ }
5770
+ const blob = await response.blob();
5771
+ new File([blob], filename, { type: blob.type });
5772
+ return {
5773
+ id: Buffer.from(url).toString("base64").slice(0, 20),
5774
+ filename,
5775
+ originalName: filename,
5776
+ mimeType: blob.type,
5777
+ size: blob.size,
5778
+ url: this.getImageUrl(url),
5779
+ thumbnailUrl: this.getImageUrl(url, {
5780
+ width: 200,
5781
+ height: 200,
5782
+ fit: "crop"
5783
+ }),
5784
+ provider: "imgix",
5785
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5786
+ };
5787
+ },
5788
+ async delete(_url) {
5789
+ },
5790
+ async rename(_oldUrl, newKey) {
5791
+ return `https://${config.domain}/${newKey}`;
5792
+ },
5793
+ getImageUrl(url, transforms) {
5794
+ const parsed = new URL(url);
5795
+ const params = new URLSearchParams(parsed.search);
5796
+ if (config.defaultParameters) {
5797
+ Object.entries(config.defaultParameters).forEach(([key, value]) => {
5798
+ if (!params.has(key)) {
5799
+ params.set(key, value);
5800
+ }
5801
+ });
5802
+ }
5803
+ if (transforms) {
5804
+ if (transforms.width) params.set("w", String(transforms.width));
5805
+ if (transforms.height) params.set("h", String(transforms.height));
5806
+ if (transforms.quality) params.set("q", String(transforms.quality));
5807
+ if (transforms.format) params.set("fm", transforms.format);
5808
+ if (transforms.fit) params.set("fit", transforms.fit);
5809
+ if (transforms.blur) params.set("blur", String(transforms.blur));
5810
+ if (transforms.sharpen) params.set("sharp", String(transforms.sharpen));
5811
+ }
5812
+ params.set("auto", "compress,format");
5813
+ parsed.pathname + "?" + params.toString();
5814
+ if (config.signKey) {
5815
+ signUrl(parsed.pathname + params.toString(), params);
5816
+ }
5817
+ return `https://${config.domain}${parsed.pathname}${params.toString() ? "?" + params.toString() : ""}`;
5818
+ },
5819
+ async generateThumbnail(file) {
5820
+ return this.getImageUrl(file.url, {
5821
+ width: 200,
5822
+ height: 200,
5823
+ fit: "crop"
5824
+ });
5825
+ },
5826
+ async list() {
5827
+ return [];
5828
+ },
5829
+ async exists(url) {
5830
+ try {
5831
+ const response = await fetch(url, { method: "HEAD" });
5832
+ return response.ok;
5833
+ } catch {
5834
+ return false;
5835
+ }
5836
+ }
5837
+ };
5838
+ }
5839
+ function createFtpStorage(config) {
5840
+ let client = null;
5841
+ async function getClient() {
5842
+ if (!client) {
5843
+ client = new Client(6e4, { allowSeparateTransferHost: true });
5844
+ client.ftp.verbose = false;
5845
+ await client.access({
5846
+ host: config.host,
5847
+ port: config.port || 21,
5848
+ user: config.user,
5849
+ password: config.password,
5850
+ secure: config.secure,
5851
+ secureOptions: {}
5852
+ });
5853
+ }
5854
+ return client;
5855
+ }
5856
+ const getKey = (path2) => {
5857
+ const prefix = config.prefix ? `${config.prefix}/` : "";
5858
+ return `${prefix}${path2}`.replace(/\/+/g, "/");
5859
+ };
5860
+ const getUrl = (key) => {
5861
+ const base = config.baseUrl.replace(/\/$/, "");
5862
+ return `${base}/${key}`;
5863
+ };
5864
+ const getUrlPrefix2 = () => {
5865
+ const base = config.baseUrl.replace(/\/$/, "");
5866
+ return base + "/";
5867
+ };
5868
+ return {
5869
+ name: config.type,
5870
+ displayName: config.type === "sftp" ? "SFTP Storage" : "FTP Storage",
5871
+ supportsDynamicResize: false,
5872
+ async upload(file, options) {
5873
+ const ftp = await getClient();
5874
+ const key = getKey(
5875
+ `${options?.folder ? `${options.folder}/` : ""}${options?.filename || file.name}`
5876
+ );
5877
+ const buffer = Buffer.from(await file.arrayBuffer());
5878
+ const parts = key.split("/").slice(0, -1);
5879
+ let currentPath = "";
5880
+ for (const part of parts) {
5881
+ currentPath = currentPath ? `${currentPath}/${part}` : part;
5882
+ try {
5883
+ await ftp.ensureDir(currentPath);
5884
+ } catch {
5885
+ }
5886
+ }
5887
+ const readable = Readable.from(buffer);
5888
+ await ftp.uploadFrom(readable, key);
5889
+ return {
5890
+ id: Buffer.from(key).toString("base64url"),
5891
+ filename: options?.filename || file.name,
5892
+ originalName: file.name,
5893
+ mimeType: file.type,
5894
+ size: buffer.length,
5895
+ url: getUrl(key),
5896
+ thumbnailUrl: file.type.startsWith("image/") ? getUrl(key) : void 0,
5897
+ folder: options?.folder,
5898
+ provider: config.type,
5899
+ metadata: options?.metadata,
5900
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
5901
+ };
5902
+ },
5903
+ async uploadFromUrl(url) {
5904
+ const response = await fetch(url);
5905
+ if (!response.ok) {
5906
+ throw new Error(`Failed to fetch: ${response.statusText}`);
5907
+ }
5908
+ const blob = await response.blob();
5909
+ const filename = url.split("/").pop() || "file";
5910
+ const file = new File([blob], filename, { type: blob.type });
5911
+ return this.upload(file);
5912
+ },
5913
+ async delete(url) {
5914
+ const ftp = await getClient();
5915
+ const key = url.replace(getUrlPrefix2(), "");
5916
+ await ftp.remove(key);
5917
+ },
5918
+ async rename(oldUrl, newKey) {
5919
+ const ftp = await getClient();
5920
+ const oldKey = oldUrl.replace(getUrlPrefix2(), "");
5921
+ const fullPath = config.prefix ? `${config.prefix}/${newKey}` : newKey;
5922
+ await ftp.rename(oldKey, fullPath);
5923
+ return getUrl(fullPath);
5924
+ },
5925
+ getImageUrl(url, transforms) {
5926
+ if (!transforms || Object.keys(transforms).length === 0) return url;
5927
+ const params = new URLSearchParams({ url });
5928
+ if (transforms.width) params.set("w", String(transforms.width));
5929
+ if (transforms.height) params.set("h", String(transforms.height));
5930
+ if (transforms.quality) params.set("q", String(transforms.quality));
5931
+ if (transforms.format) params.set("f", transforms.format);
5932
+ return `/api/media/resize?${params.toString()}`;
5933
+ },
5934
+ async generateThumbnail(file) {
5935
+ return file.url;
5936
+ },
5937
+ async list(prefix) {
5938
+ const ftp = await getClient();
5939
+ const key = getKey(prefix || "");
5940
+ let items;
5941
+ try {
5942
+ items = await ftp.list(key);
5943
+ } catch {
5944
+ return [];
5945
+ }
5946
+ return items.filter((item) => item.type === 0).map((item) => ({
5947
+ id: Buffer.from(`${key}/${item.name}`).toString("base64url"),
5948
+ filename: item.name,
5949
+ originalName: item.name,
5950
+ mimeType: "application/octet-stream",
5951
+ size: Number(item.size) || 0,
5952
+ url: getUrl(`${key}/${item.name}`.replace(/\/+/g, "/")),
5953
+ provider: config.type,
5954
+ createdAt: item.modifiedAt ? item.modifiedAt.toISOString() : (/* @__PURE__ */ new Date()).toISOString()
5955
+ }));
5956
+ },
5957
+ async exists(url) {
5958
+ const ftp = await getClient();
5959
+ const key = url.replace(getUrlPrefix2(), "");
5960
+ try {
5961
+ await ftp.size(key);
5962
+ return true;
5963
+ } catch {
5964
+ return false;
5965
+ }
5966
+ }
5967
+ };
5968
+ }
5969
+
5970
+ // src/storage/index.ts
5971
+ async function resolveProvider(configService) {
5972
+ const config = configService.getStorageConfig();
5973
+ switch (config.type) {
5974
+ case "aws":
5975
+ return createS3Storage({
5976
+ provider: "aws",
5977
+ bucket: config.s3.bucket || "",
5978
+ region: config.s3.region || "us-east-1",
5979
+ accessKeyId: config.s3.accessKeyId || "",
5980
+ secretAccessKey: config.s3.secretAccessKey || "",
5981
+ endpoint: config.s3.endpoint,
5982
+ cdnUrl: config.s3.cdnUrl,
5983
+ prefix: config.s3.prefix
5984
+ });
5985
+ case "r2":
5986
+ return createS3Storage({
5987
+ provider: "r2",
5988
+ bucket: config.r2.bucket || "",
5989
+ region: "auto",
5990
+ accessKeyId: config.r2.accessKeyId || "",
5991
+ secretAccessKey: config.r2.secretAccessKey || "",
5992
+ accountId: config.r2.accountId || "",
5993
+ publicDevUrl: config.r2.publicDevUrl,
5994
+ endpoint: `https://${config.r2.accountId || ""}.r2.cloudflarestorage.com`,
5995
+ cdnUrl: config.r2.cdnUrl,
5996
+ prefix: config.r2.prefix
5997
+ });
5998
+ case "gcs":
5999
+ return createS3Storage({
6000
+ provider: "gcs",
6001
+ bucket: config.gcs.bucket || "",
6002
+ region: config.gcs.projectId || "auto",
6003
+ accessKeyId: config.gcs.clientEmail || "",
6004
+ secretAccessKey: config.gcs.privateKey || "",
6005
+ cdnUrl: config.gcs.cdnUrl,
6006
+ prefix: config.gcs.prefix
6007
+ });
6008
+ case "digitalocean":
6009
+ return createS3Storage({
6010
+ provider: "digitalocean",
6011
+ bucket: config.digitalocean.bucket || "",
6012
+ region: config.digitalocean.region || "nyc3",
6013
+ accessKeyId: config.digitalocean.accessKeyId || "",
6014
+ secretAccessKey: config.digitalocean.secretAccessKey || "",
6015
+ endpoint: `https://${config.digitalocean.region || "nyc3"}.digitaloceanspaces.com`,
6016
+ cdnUrl: config.digitalocean.cdnUrl,
6017
+ prefix: config.digitalocean.prefix
6018
+ });
6019
+ case "backblaze":
6020
+ return createS3Storage({
6021
+ provider: "backblaze",
6022
+ bucket: config.backblaze.bucket || "",
6023
+ region: "auto",
6024
+ accessKeyId: config.backblaze.applicationKeyId || "",
6025
+ secretAccessKey: config.backblaze.applicationKey || "",
6026
+ accountId: config.backblaze.accountId || "",
6027
+ endpoint: `https://s3.backblazeb2.com`,
6028
+ cdnUrl: config.backblaze.cdnUrl,
6029
+ prefix: config.backblaze.prefix
6030
+ });
6031
+ case "wasabi":
6032
+ return createS3Storage({
6033
+ provider: "wasabi",
6034
+ bucket: config.wasabi.bucket || "",
6035
+ region: config.wasabi.region || "us-east-1",
6036
+ accessKeyId: config.wasabi.accessKeyId || "",
6037
+ secretAccessKey: config.wasabi.secretAccessKey || "",
6038
+ endpoint: `https://s3.${config.wasabi.region || "us-east-1"}.wasabisys.com`,
6039
+ cdnUrl: config.wasabi.cdnUrl,
6040
+ prefix: config.wasabi.prefix
6041
+ });
6042
+ case "ftp":
6043
+ case "sftp":
6044
+ return createFtpStorage({
6045
+ host: config.ftp?.host || "",
6046
+ port: config.ftp?.port || 21,
6047
+ user: config.ftp?.user || "",
6048
+ password: config.ftp?.password || "",
6049
+ secure: config.ftp?.secure || false,
6050
+ baseUrl: config.ftp?.baseUrl || "",
6051
+ prefix: config.ftp?.prefix,
6052
+ type: "ftp"
6053
+ });
6054
+ case "cloudinary":
6055
+ return createCloudinaryStorage({
6056
+ cloudName: config.cloudinary.cloudName || "",
6057
+ apiKey: config.cloudinary.apiKey || "",
6058
+ apiSecret: config.cloudinary.apiSecret || "",
6059
+ folder: config.cloudinary.folder
6060
+ });
6061
+ case "imgix":
6062
+ return createImgixStorage({
6063
+ domain: config.imgix.domain || "",
6064
+ signKey: config.imgix.signKey
6065
+ });
6066
+ case "local":
6067
+ default:
6068
+ return createLocalStorage({
6069
+ uploadDir: config.local.uploadDir || path.join(process.cwd(), "public", "uploads"),
6070
+ baseUrl: config.local.baseUrl || "/uploads"
6071
+ });
6072
+ }
6073
+ }
6074
+ async function resolveProviderWithConfig(config) {
6075
+ if (!config) {
6076
+ console.warn("[resolveProviderWithConfig] No config, using local");
6077
+ return createLocalStorage({
6078
+ uploadDir: path.join(process.cwd(), "public", "uploads"),
6079
+ baseUrl: "/uploads"
6080
+ });
6081
+ }
6082
+ console.log("[resolveProviderWithConfig] Creating provider:", config.type);
6083
+ switch (config.type) {
6084
+ case "aws":
6085
+ return createS3Storage({
6086
+ provider: "aws",
6087
+ bucket: config.s3?.bucket || "",
6088
+ region: config.s3?.region || "us-east-1",
6089
+ accessKeyId: config.s3?.accessKeyId || "",
6090
+ secretAccessKey: config.s3?.secretAccessKey || "",
6091
+ endpoint: config.s3?.endpoint,
6092
+ cdnUrl: config.s3?.cdnUrl,
6093
+ prefix: config.s3?.prefix
6094
+ });
6095
+ case "r2":
6096
+ return createS3Storage({
6097
+ provider: "r2",
6098
+ bucket: config.r2?.bucket || "",
6099
+ region: "auto",
6100
+ accessKeyId: config.r2?.accessKeyId || "",
6101
+ secretAccessKey: config.r2?.secretAccessKey || "",
6102
+ accountId: config.r2?.accountId || "",
6103
+ publicDevUrl: config.r2?.publicDevUrl,
6104
+ endpoint: `https://${config.r2?.accountId || ""}.r2.cloudflarestorage.com`,
6105
+ cdnUrl: config.r2?.cdnUrl,
6106
+ prefix: config.r2?.prefix
6107
+ });
6108
+ case "gcs":
6109
+ return createS3Storage({
6110
+ provider: "gcs",
6111
+ bucket: config.gcs?.bucket || "",
6112
+ region: config.gcs?.projectId || "auto",
6113
+ accessKeyId: config.gcs?.clientEmail || "",
6114
+ secretAccessKey: config.gcs?.privateKey || "",
6115
+ cdnUrl: config.gcs?.cdnUrl,
6116
+ prefix: config.gcs?.prefix
6117
+ });
6118
+ case "digitalocean":
6119
+ return createS3Storage({
6120
+ provider: "digitalocean",
6121
+ bucket: config.digitalocean?.bucket || "",
6122
+ region: config.digitalocean?.region || "nyc3",
6123
+ accessKeyId: config.digitalocean?.accessKeyId || "",
6124
+ secretAccessKey: config.digitalocean?.secretAccessKey || "",
6125
+ cdnUrl: config.digitalocean?.cdnUrl,
6126
+ prefix: config.digitalocean?.prefix
6127
+ });
6128
+ case "backblaze":
6129
+ return createS3Storage({
6130
+ provider: "backblaze",
6131
+ bucket: config.backblaze?.bucket || "",
6132
+ region: "auto",
6133
+ accessKeyId: config.backblaze?.applicationKeyId || "",
6134
+ secretAccessKey: config.backblaze?.applicationKey || "",
6135
+ cdnUrl: config.backblaze?.cdnUrl,
6136
+ prefix: config.backblaze?.prefix
6137
+ });
6138
+ case "wasabi":
6139
+ return createS3Storage({
6140
+ provider: "wasabi",
6141
+ bucket: config.wasabi?.bucket || "",
6142
+ region: config.wasabi?.region || "us-east-1",
6143
+ accessKeyId: config.wasabi?.accessKeyId || "",
6144
+ secretAccessKey: config.wasabi?.secretAccessKey || "",
6145
+ cdnUrl: config.wasabi?.cdnUrl,
6146
+ prefix: config.wasabi?.prefix
6147
+ });
6148
+ case "cloudinary":
6149
+ return createCloudinaryStorage({
6150
+ cloudName: config.cloudinary?.cloudName || "",
6151
+ apiKey: config.cloudinary?.apiKey || "",
6152
+ apiSecret: config.cloudinary?.apiSecret || "",
6153
+ folder: config.cloudinary?.folder
6154
+ });
6155
+ case "ftp":
6156
+ case "sftp": {
6157
+ const ftpConf = config.ftp || config;
6158
+ return createFtpStorage({
6159
+ type: "ftp",
6160
+ host: ftpConf.host || "",
6161
+ port: ftpConf.port || 21,
6162
+ user: ftpConf.user || "",
6163
+ password: ftpConf.password || "",
6164
+ secure: ftpConf.secure || false,
6165
+ baseUrl: ftpConf.baseUrl || "",
6166
+ prefix: ftpConf.prefix
6167
+ });
6168
+ }
6169
+ case "local":
6170
+ default: {
6171
+ const localConfig = config.local || {
6172
+ uploadDir: config["local.uploadDir"],
6173
+ baseUrl: config["local.baseUrl"]
6174
+ };
6175
+ const savedUploadDir = (localConfig?.uploadDir || "").trim();
6176
+ let uploadDir;
6177
+ if (savedUploadDir) {
6178
+ if (path.isAbsolute(savedUploadDir)) {
6179
+ uploadDir = savedUploadDir;
6180
+ } else if (savedUploadDir.includes("/") || savedUploadDir.includes("\\")) {
6181
+ uploadDir = path.resolve(process.cwd(), savedUploadDir);
6182
+ } else {
6183
+ uploadDir = path.join(process.cwd(), "public", savedUploadDir);
6184
+ }
6185
+ } else {
6186
+ uploadDir = path.join(process.cwd(), "public", "uploads");
6187
+ }
6188
+ const savedBaseUrl = (localConfig?.baseUrl || "").trim();
6189
+ let baseUrl;
6190
+ if (savedBaseUrl) {
6191
+ baseUrl = savedBaseUrl.startsWith("/") ? savedBaseUrl : `/${savedBaseUrl}`;
6192
+ } else {
6193
+ baseUrl = "/uploads";
6194
+ }
6195
+ return createLocalStorage({ uploadDir, baseUrl });
6196
+ }
6197
+ }
6198
+ }
6199
+ async function processImage(buffer) {
6200
+ const metadata = await sharp(buffer).metadata();
6201
+ const mainImage = sharp(buffer).webp({ quality: 85 });
6202
+ const thumbnail = sharp(buffer).resize({ width: 500, withoutEnlargement: true }).webp({ quality: 80 });
6203
+ return {
6204
+ buffer: await mainImage.toBuffer(),
6205
+ thumbnailBuffer: await thumbnail.toBuffer(),
6206
+ width: metadata.width,
6207
+ height: metadata.height,
6208
+ format: "webp"
6209
+ };
6210
+ }
6211
+
6212
+ // src/storage/MediaService.ts
6213
+ var MediaService = class _MediaService {
6214
+ db;
6215
+ storage;
6216
+ dialect;
6217
+ genId;
6218
+ mediaTable = "media";
6219
+ foldersTable = "media_folders";
6220
+ constructor(db, storage, options) {
6221
+ this.db = db;
6222
+ this.storage = storage;
6223
+ this.dialect = options?.dialect || "sqlite";
6224
+ this.genId = options?.genId || genId;
6225
+ }
6226
+ static async init(db, options) {
6227
+ let storage;
6228
+ if (options?.storageConfig) {
6229
+ console.log(
6230
+ "[MediaService.init] Using provided storageConfig:",
6231
+ options.storageConfig.type
6232
+ );
6233
+ storage = await resolveProviderWithConfig(options.storageConfig);
6234
+ } else {
6235
+ const configService = new ConfigService(db);
6236
+ await configService.load();
6237
+ storage = await resolveProvider(configService);
6238
+ }
6239
+ return new _MediaService(db, storage, options);
6240
+ }
6241
+ now() {
6242
+ return (/* @__PURE__ */ new Date()).toISOString();
6243
+ }
6244
+ buildFindConditions(params) {
6245
+ const conditions = [];
6246
+ const p = [];
6247
+ const sortCol = params.sortBy === "name" ? "title" : params.sortBy || "created_at";
6248
+ const sortDir = params.sortDir === "asc" ? "ASC" : "DESC";
6249
+ if (params.search) {
6250
+ conditions.push(
6251
+ `(title LIKE ? OR filename LIKE ? OR original_name LIKE ? OR alt LIKE ?)`
6252
+ );
6253
+ const s = `%${params.search}%`;
6254
+ p.push(s, s, s, s);
6255
+ }
6256
+ if (params.type && params.type !== "all") {
6257
+ conditions.push(`mime_type LIKE ?`);
6258
+ p.push(`${params.type}/%`);
6259
+ }
6260
+ if (params.folder) {
6261
+ conditions.push(`(folder = ? OR folder LIKE ?)`);
6262
+ p.push(params.folder, `${params.folder}/%`);
6263
+ }
6264
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
6265
+ return { where, params: p, orderBy: sortDir, sortCol };
6266
+ }
6267
+ rowToMedia(row) {
6268
+ return {
6269
+ id: row.id,
6270
+ filename: row.filename,
6271
+ title: row.title ?? null,
6272
+ originalName: row.original_name ?? row.originalName,
6273
+ mimeType: row.mime_type ?? row.mimeType,
6274
+ fileSize: row.file_size ?? row.fileSize,
6275
+ width: row.width ?? null,
6276
+ height: row.height ?? null,
6277
+ url: row.url,
6278
+ thumbnailUrl: row.thumbnail_url ?? row.thumbnailUrl ?? null,
6279
+ folder: row.folder ?? null,
6280
+ provider: row.provider,
6281
+ alt: row.alt ?? null,
6282
+ caption: row.caption ?? null,
6283
+ metadata: row.metadata ?? null,
6284
+ createdAt: row.created_at ?? row.createdAt,
6285
+ updatedAt: row.updated_at ?? row.updatedAt
6286
+ };
6287
+ }
6288
+ async sqliteRun(sql, params = []) {
6289
+ const stmt = this.db.prepare(sql);
6290
+ const sqlLower = sql.trim().toLowerCase();
6291
+ if (sqlLower.startsWith("insert") || sqlLower.startsWith("update") || sqlLower.startsWith("delete")) {
6292
+ return stmt.run(...params);
6293
+ }
6294
+ return stmt.all(...params);
6295
+ }
6296
+ sqliteGet(sql, params = []) {
6297
+ const stmt = this.db.prepare(sql);
6298
+ return stmt.get(...params);
6299
+ }
6300
+ async upload(file, folder = "") {
6301
+ const isImage = file.type.startsWith("image/");
6302
+ let processed;
6303
+ let uploadFile = file;
6304
+ let filename = file.name;
6305
+ let width = null;
6306
+ let height = null;
6307
+ if (isImage && !file.type.includes("svg")) {
6308
+ const buffer = Buffer.from(await file.arrayBuffer());
6309
+ processed = await processImage(buffer);
6310
+ const originalName = file.name.replace(/\.[^/.]+$/, "");
6311
+ const safeName = originalName.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").substring(0, 50);
6312
+ filename = `${safeName}.webp`;
6313
+ width = processed.width ?? null;
6314
+ height = processed.height ?? null;
6315
+ uploadFile = new File([processed.buffer], filename, {
6316
+ type: "image/webp"
6317
+ });
6318
+ }
6319
+ const storageResult = await this.storage.upload(uploadFile, {
6320
+ folder,
6321
+ filename
6322
+ });
6323
+ const thumbnailUrl = await this.storage.generateThumbnail(
6324
+ {
6325
+ ...storageResult,
6326
+ id: "",
6327
+ provider: this.storage.name,
6328
+ createdAt: this.now()
6329
+ },
6330
+ { width: 400, height: 400 }
6331
+ );
6332
+ const id = this.genId();
6333
+ const now = this.now();
6334
+ if (this.dialect === "sqlite") {
6335
+ await this.sqliteRun(
6336
+ `INSERT INTO ${this.mediaTable}
6337
+ (id, filename, title, original_name, mime_type, file_size, width, height, url, thumbnail_url, folder, provider, alt, caption, metadata, created_at, updated_at)
6338
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
6339
+ [
6340
+ id,
6341
+ storageResult.filename,
6342
+ file.name.replace(/\.[^/.]+$/, ""),
6343
+ file.name,
6344
+ storageResult.mimeType,
6345
+ storageResult.size,
6346
+ width,
6347
+ height,
6348
+ storageResult.url,
6349
+ thumbnailUrl,
6350
+ folder || null,
6351
+ this.storage.name,
6352
+ null,
6353
+ null,
6354
+ null,
6355
+ now,
6356
+ now
6357
+ ]
6358
+ );
6359
+ } else {
6360
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6361
+ await this.db.insert(mediaSchema).values({
6362
+ id,
6363
+ filename: storageResult.filename,
6364
+ title: file.name.replace(/\.[^/.]+$/, ""),
6365
+ originalName: file.name,
6366
+ mimeType: storageResult.mimeType,
6367
+ fileSize: storageResult.size,
6368
+ width,
6369
+ height,
6370
+ url: storageResult.url,
6371
+ thumbnailUrl,
6372
+ folder: folder || "",
6373
+ provider: this.storage.name,
6374
+ createdAt: /* @__PURE__ */ new Date(),
6375
+ updatedAt: /* @__PURE__ */ new Date()
6376
+ }).returning();
6377
+ }
6378
+ return {
6379
+ id,
6380
+ filename: storageResult.filename,
6381
+ title: file.name.replace(/\.[^/.]+$/, ""),
6382
+ originalName: file.name,
6383
+ mimeType: storageResult.mimeType,
6384
+ fileSize: storageResult.size,
6385
+ width,
6386
+ height,
6387
+ url: storageResult.url,
6388
+ thumbnailUrl,
6389
+ folder: folder || null,
6390
+ provider: this.storage.name,
6391
+ alt: null,
6392
+ caption: null,
6393
+ metadata: null,
6394
+ createdAt: now,
6395
+ updatedAt: now
6396
+ };
6397
+ }
6398
+ async delete(id) {
6399
+ let item = null;
6400
+ if (this.dialect === "sqlite") {
6401
+ item = this.sqliteGet(`SELECT * FROM ${this.mediaTable} WHERE id = ?`, [
6402
+ id
6403
+ ]);
6404
+ } else {
6405
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6406
+ const [row] = await this.db.select().from(mediaSchema).where(mediaSchema.id.equals(id));
6407
+ if (row) item = this.rowToMedia(row);
6408
+ }
6409
+ if (!item) return;
6410
+ await this.deleteFile(item.url);
6411
+ if (item.thumbnailUrl && item.thumbnailUrl !== item.url && item.thumbnailUrl !== item.url + "?thumb") {
6412
+ try {
6413
+ await this.deleteFile(item.thumbnailUrl);
6414
+ } catch {
6415
+ }
6416
+ }
6417
+ if (this.dialect === "sqlite") {
6418
+ await this.sqliteRun(`DELETE FROM ${this.mediaTable} WHERE id = ?`, [id]);
6419
+ } else {
6420
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6421
+ await this.db.delete(mediaSchema).where(mediaSchema.id.equals(id));
6422
+ }
6423
+ }
6424
+ async deleteFile(url) {
6425
+ await this.storage.delete(url);
6426
+ }
6427
+ async rename(id, newKey) {
6428
+ let item = null;
6429
+ if (this.dialect === "sqlite") {
6430
+ item = this.sqliteGet(`SELECT * FROM ${this.mediaTable} WHERE id = ?`, [
6431
+ id
6432
+ ]);
6433
+ } else {
6434
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6435
+ const [row] = await this.db.select().from(mediaSchema).where(mediaSchema.id.equals(id));
6436
+ if (row) item = this.rowToMedia(row);
6437
+ }
6438
+ if (!item) return null;
6439
+ const newUrl = await this.storage.rename(item.url, newKey);
6440
+ let newThumbnailUrl;
6441
+ if (item.thumbnailUrl || this.storage.name === "cloudinary") {
6442
+ const versionMatch = newUrl.match(/\/upload\/(v\d+)\//);
6443
+ const version = versionMatch ? versionMatch[1] + "/" : "";
6444
+ const baseUrlMatch = newUrl.match(/(.+?)\/upload\//);
6445
+ const baseUrl = baseUrlMatch ? baseUrlMatch[1] : "https://res.cloudinary.com/" + this.storage.config?.cloudName || "unknown";
6446
+ newThumbnailUrl = `${baseUrl}/upload/w_200,h_200,c_fill/${version}${newKey}`;
6447
+ }
6448
+ const ext = item.filename.split(".").pop();
6449
+ const newFilename = newKey.includes(".") ? newKey : `${newKey}.${ext}`;
6450
+ const updateData = {
6451
+ url: newUrl,
6452
+ filename: newFilename,
6453
+ thumbnailUrl: newThumbnailUrl
6454
+ };
6455
+ if (this.dialect === "sqlite") {
6456
+ const sqliteUpdateData = { ...updateData };
6457
+ if ("thumbnailUrl" in sqliteUpdateData) {
6458
+ sqliteUpdateData.thumbnail_url = sqliteUpdateData.thumbnailUrl;
6459
+ delete sqliteUpdateData.thumbnailUrl;
6460
+ }
6461
+ const sets = Object.keys(sqliteUpdateData).map((k) => `${k} = ?`).join(", ");
6462
+ const vals = Object.values(sqliteUpdateData);
6463
+ await this.sqliteRun(
6464
+ `UPDATE ${this.mediaTable} SET ${sets}, updated_at = ? WHERE id = ?`,
6465
+ [...vals, this.now(), id]
6466
+ );
6467
+ } else {
6468
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6469
+ await this.db.update(mediaSchema).set({ ...updateData, updatedAt: this.now() }).where(mediaSchema.id.equals(id));
6470
+ }
6471
+ return {
6472
+ ...item,
6473
+ ...updateData,
6474
+ updatedAt: this.now(),
6475
+ thumbnailUrl: updateData.thumbnailUrl ?? null
6476
+ };
6477
+ }
6478
+ async find(params = {}) {
6479
+ const {
6480
+ page = 1,
6481
+ limit = 30,
6482
+ search = "",
6483
+ type = "",
6484
+ folder = "",
6485
+ sortBy = "createdAt",
6486
+ sortDir = "desc"
6487
+ } = params;
6488
+ const {
6489
+ where,
6490
+ params: p,
6491
+ orderBy,
6492
+ sortCol
6493
+ } = this.buildFindConditions({
6494
+ page,
6495
+ limit,
6496
+ search,
6497
+ type,
6498
+ folder,
6499
+ sortBy,
6500
+ sortDir
6501
+ });
6502
+ const offset = (page - 1) * limit;
6503
+ if (this.dialect === "sqlite") {
6504
+ const countRow = this.sqliteGet(
6505
+ `SELECT COUNT(*) as cnt FROM ${this.mediaTable} ${where}`,
6506
+ p
6507
+ );
6508
+ const totalDocs2 = countRow?.cnt ?? 0;
6509
+ const rows = await this.sqliteRun(
6510
+ `SELECT * FROM ${this.mediaTable} ${where} ORDER BY ${sortCol} ${orderBy} LIMIT ? OFFSET ?`,
6511
+ [...p, limit, offset]
6512
+ );
6513
+ return {
6514
+ docs: rows.map((r) => this.rowToMedia(r)),
6515
+ totalDocs: totalDocs2,
6516
+ page,
6517
+ limit,
6518
+ totalPages: Math.ceil(totalDocs2 / limit)
6519
+ };
6520
+ }
6521
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6522
+ const { like, or, and, asc, desc, eq, sql } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
6523
+ const conditions = [];
6524
+ if (search) {
6525
+ conditions.push(
6526
+ or(
6527
+ like(mediaSchema.title, `%${search}%`),
6528
+ like(mediaSchema.filename, `%${search}%`),
6529
+ like(mediaSchema.originalName, `%${search}%`),
6530
+ like(mediaSchema.alt, `%${search}%`)
6531
+ )
6532
+ );
6533
+ }
6534
+ if (type && type !== "all") {
6535
+ conditions.push(like(mediaSchema.mimeType, `${type}/%`));
6536
+ }
6537
+ if (folder) {
6538
+ conditions.push(
6539
+ or(
6540
+ eq(mediaSchema.folder, folder),
6541
+ like(mediaSchema.folder, `${folder}/%`)
6542
+ )
6543
+ );
6544
+ }
6545
+ const whereClause = conditions.length > 0 ? and(...conditions) : void 0;
6546
+ const order = sortDir === "asc" ? asc(mediaSchema[sortCol]) : desc(mediaSchema[sortCol]);
6547
+ const docs = await this.db.select().from(mediaSchema).where(whereClause).orderBy(order).limit(limit).offset(offset);
6548
+ const [{ count }] = await this.db.select({ count: sql`count(*)` }).from(mediaSchema).where(whereClause);
6549
+ const totalDocs = Number(count);
6550
+ return {
6551
+ docs: docs.map((r) => this.rowToMedia(r)),
6552
+ totalDocs,
6553
+ page,
6554
+ limit,
6555
+ totalPages: Math.ceil(totalDocs / limit)
6556
+ };
6557
+ }
6558
+ async update(id, data) {
6559
+ const now = this.now();
6560
+ if (this.dialect === "sqlite") {
6561
+ const sets = ["updated_at = ?"];
6562
+ const p = [now];
6563
+ if (data.title !== void 0) {
6564
+ sets.push("title = ?");
6565
+ p.push(data.title);
6566
+ }
6567
+ if (data.alt !== void 0) {
6568
+ sets.push("alt = ?");
6569
+ p.push(data.alt);
6570
+ }
6571
+ if (data.caption !== void 0) {
6572
+ sets.push("caption = ?");
6573
+ p.push(data.caption);
6574
+ }
6575
+ if (data.folder !== void 0) {
6576
+ sets.push("folder = ?");
6577
+ p.push(data.folder || null);
6578
+ }
6579
+ p.push(id);
6580
+ await this.sqliteRun(
6581
+ `UPDATE ${this.mediaTable} SET ${sets.join(", ")} WHERE id = ?`,
6582
+ p
6583
+ );
6584
+ const row = this.sqliteGet(
6585
+ `SELECT * FROM ${this.mediaTable} WHERE id = ?`,
6586
+ [id]
6587
+ );
6588
+ return row ? this.rowToMedia(row) : null;
6589
+ }
6590
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6591
+ const [updated] = await this.db.update(mediaSchema).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(mediaSchema.id.equals(id)).returning();
6592
+ return updated ? this.rowToMedia(updated) : null;
6593
+ }
6594
+ async updateMany(ids, data) {
6595
+ const now = this.now();
6596
+ if (this.dialect === "sqlite") {
6597
+ for (const id of ids) {
6598
+ const sets = ["updated_at = ?"];
6599
+ const p = [now];
6600
+ if (data.folder !== void 0) {
6601
+ sets.push("folder = ?");
6602
+ p.push(data.folder || null);
6603
+ }
6604
+ p.push(id);
6605
+ await this.sqliteRun(
6606
+ `UPDATE ${this.mediaTable} SET ${sets.join(", ")} WHERE id = ?`,
6607
+ p
6608
+ );
6609
+ }
6610
+ } else {
6611
+ const { media: mediaSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6612
+ for (const id of ids) {
6613
+ await this.db.update(mediaSchema).set({ ...data, updatedAt: /* @__PURE__ */ new Date() }).where(mediaSchema.id.equals(id));
6614
+ }
6615
+ }
6616
+ }
6617
+ async listFolders() {
6618
+ if (this.dialect === "sqlite") {
6619
+ const rows = await this.sqliteRun(
6620
+ `SELECT path FROM ${this.foldersTable} UNION
6621
+ SELECT folder as path FROM ${this.mediaTable} WHERE folder IS NOT NULL AND folder != ''`
6622
+ );
6623
+ return rows.map((r) => r.path).filter((f) => f && f !== "").sort();
6624
+ }
6625
+ const { media: mediaSchema, mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6626
+ const { eq, sql } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
6627
+ const fromMedia = await this.db.select({ folder: mediaSchema.folder }).from(mediaSchema).groupBy(mediaSchema.folder);
6628
+ const fromFolders = await this.db.select({ path: folderSchema.path }).from(folderSchema);
6629
+ const allPaths = /* @__PURE__ */ new Set([
6630
+ ...fromMedia.map((r) => r.folder),
6631
+ ...fromFolders.map((r) => r.path)
6632
+ ]);
6633
+ return Array.from(allPaths).filter((f) => f && f !== "").sort();
6634
+ }
6635
+ async createFolder(name, parentPath = "") {
6636
+ const fullPath = parentPath ? `${parentPath}/${name}` : name;
6637
+ if (this.storage.name === "local") {
6638
+ const { mkdir: mkdir2 } = await import('fs/promises');
6639
+ const { join: join2 } = await import('path');
6640
+ await mkdir2(join2(process.cwd(), "public", "uploads", fullPath), {
6641
+ recursive: true
6642
+ });
6643
+ }
6644
+ const now = this.now();
6645
+ if (this.dialect === "sqlite") {
6646
+ await this.sqliteRun(
6647
+ `INSERT OR IGNORE INTO ${this.foldersTable} (path, name, parent_path, created_at) VALUES (?, ?, ?, ?)`,
6648
+ [fullPath, name, parentPath || null, now]
6649
+ );
6650
+ } else {
6651
+ const { mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6652
+ await this.db.insert(folderSchema).values({
6653
+ path: fullPath,
6654
+ name,
6655
+ parentPath: parentPath || null,
6656
+ createdAt: /* @__PURE__ */ new Date()
6657
+ }).onConflictDoNothing();
6658
+ }
6659
+ }
6660
+ async deleteFolder(folder) {
6661
+ const result = await this.find({ folder, limit: 1e4 });
6662
+ for (const item of result.docs) {
6663
+ await this.delete(item.id);
6664
+ }
6665
+ if (this.dialect === "sqlite") {
6666
+ await this.sqliteRun(
6667
+ `DELETE FROM ${this.foldersTable} WHERE path = ? OR path LIKE ?`,
6668
+ [folder, `${folder}/%`]
6669
+ );
6670
+ } else {
6671
+ const { mediaFolders: folderSchema } = await (this.dialect === "mysql" ? import('./mysql-media-CDZUS7YX.js') : import('./media-HOT3O7RW.js'));
6672
+ const { like, or, eq } = await (this.dialect === "mysql" ? import('drizzle-orm/mysql-core') : import('drizzle-orm/pg-core'));
6673
+ await this.db.delete(folderSchema).where(
6674
+ or(
6675
+ eq(folderSchema.path, folder),
6676
+ like(folderSchema.path, `${folder}/%`)
6677
+ )
6678
+ );
6679
+ }
6680
+ if (this.storage.name === "local") {
6681
+ const { rm } = await import('fs/promises');
6682
+ const { join: join2 } = await import('path');
6683
+ try {
6684
+ await rm(join2(process.cwd(), "public", "uploads", folder), {
6685
+ recursive: true,
6686
+ force: true
6687
+ });
6688
+ } catch {
6689
+ }
6690
+ }
6691
+ }
6692
+ };
4039
6693
 
4040
6694
  // src/registry/config.ts
4041
6695
  function normalizeCollections(collections) {
@@ -4066,6 +6720,6 @@ function defineConfig(config) {
4066
6720
  };
4067
6721
  }
4068
6722
 
4069
- export { ALL_FIELD_TYPES, AccountLockout, AnalyticsPlugin, AuditLogger, Auth, COMPLEX_FIELD_TYPES, CSSGenerator, CommentsPlugin, ConfigValidationError, InMemoryAccountLockout, InMemoryAuditLogger, InMemoryAuthAdapter, InMemoryRateLimiter, Kyro, KyroPlugin, LAYOUT_FIELD_TYPES, LocalAdapter, PRIMITIVE_FIELD_TYPES, PluginManager, RELATIONAL_FIELD_TYPES, RateLimiter, Registry, ReviewsPlugin, SEOPLugin, VersionManager, WishlistPlugin, authConfig, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createAdminStyling, createAuditContext, createAuth, createAuthConfig, createKyro, createLocalAdapter, createRegistry, createVersionManager, defaultDarkTheme, defaultFieldStyling, defaultLightTheme, defineConfig, ecommerce2026Theme, fieldToZod, generateCSSVariables, generateTailwindConfig, getDefaultDraftPublishConfig, getRegistry, globalToZod, isArchived, isArrayField, isBlocksField, isDraft, isGroupField, isLayoutField, isNumberField, isPublished, isRelationshipField, isRichTextField, isSelectField, isTextField, isUploadField, presetPlugins, resetRegistry, runFieldHooks, runHooks, validateCollection, validateConfig, validateFields, validateGlobal };
6723
+ export { ALL_FIELD_TYPES, AccountLockout, AnalyticsPlugin, AuditLogger, Auth, COMPLEX_FIELD_TYPES, CommentsPlugin, ConfigValidationError, InMemoryAccountLockout, InMemoryAuditLogger, InMemoryAuthAdapter, Kyro, KyroPlugin, LAYOUT_FIELD_TYPES, LocalAdapter, MediaService, PRIMITIVE_FIELD_TYPES, PluginManager, RELATIONAL_FIELD_TYPES, RateLimiter, Registry, ReviewsPlugin, SEOPLugin, VersionManager, WishlistPlugin, authConfig, collectionToCreateZod, collectionToUpdateZod, collectionToWhereZod, collectionToZod, createAuditContext2 as createAuditContext, createAuth, createAuthConfig, createColumnsNode, createKyro, createLocalAdapter, createLocalStorage, createRegistry, createVersionManager, defineConfig, fieldToZod, getDefaultDraftPublishConfig, getRegistry, globalToZod, isArchived, isArrayField, isBlocksField, isDraft, isGroupField, isImageField, isLayoutField, isNumberField, isPublished, isRelationshipField, isRichTextField, isSelectField, isTextField, isUploadField, normalizeRichTextDocument, normalizeRichTextValue, presetPlugins, renderRichText, resetRegistry, resolveProvider, richTextStyles, runFieldHooks, runHooks, validateCollection, validateConfig, validateFields, validateGlobal };
4070
6724
  //# sourceMappingURL=index.js.map
4071
6725
  //# sourceMappingURL=index.js.map