@kosmojs/dev 0.0.11 → 0.0.21

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 (225) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +13 -45
  3. package/pkg/{src/base-plugin/ast.d.ts → ast.d.ts} +11 -16
  4. package/pkg/{src/base-plugin → base-plugin}/api-handler.d.ts +2 -2
  5. package/pkg/{src/base-plugin → base-plugin}/index.d.ts +1 -1
  6. package/pkg/{src/base-plugin → base-plugin}/worker.d.ts +2 -3
  7. package/pkg/base-plugin/worker.js +614 -196
  8. package/pkg/base-plugin/worker.js.map +4 -4
  9. package/pkg/{src/base-plugin/cache.d.ts → cache.d.ts} +3 -3
  10. package/pkg/defaults.d.ts +13 -0
  11. package/pkg/fs.d.ts +1 -0
  12. package/pkg/index.d.ts +9 -0
  13. package/pkg/index.js +991 -313
  14. package/pkg/index.js.map +4 -4
  15. package/pkg/paths.d.ts +13 -0
  16. package/pkg/render.d.ts +28 -0
  17. package/pkg/routes-factory/base.d.ts +47 -0
  18. package/pkg/routes-factory/index.d.ts +9 -0
  19. package/pkg/routes-factory/nesting.d.ts +3 -0
  20. package/pkg/{src/base-plugin/routes → routes-factory}/resolve.d.ts +3 -3
  21. package/pkg/{src/stub-generator → stub-generator}/index.d.ts +1 -1
  22. package/pkg/stub-generator/index.js +155 -24
  23. package/pkg/stub-generator/index.js.map +4 -4
  24. package/pkg/typebox.d.ts +2 -0
  25. package/pkg/types.d.ts +305 -0
  26. package/pkg/base-plugin/routes.js +0 -819
  27. package/pkg/base-plugin/routes.js.map +0 -7
  28. package/pkg/cli/cli.js +0 -583
  29. package/pkg/cli/cli.js.map +0 -7
  30. package/pkg/cli/index.js +0 -422
  31. package/pkg/cli/index.js.map +0 -7
  32. package/pkg/cli/templates/.env +0 -1
  33. package/pkg/cli/templates/@src/api/app.hbs +0 -32
  34. package/pkg/cli/templates/@src/api/router.hbs +0 -11
  35. package/pkg/cli/templates/@src/api/server.hbs +0 -4
  36. package/pkg/cli/templates/@src/api/use.hbs +0 -6
  37. package/pkg/cli/templates/@src/config/index.hbs +0 -2
  38. package/pkg/cli/templates/@src/index.html +0 -4
  39. package/pkg/cli/templates/@src/vite.config.hbs +0 -29
  40. package/pkg/cli/templates/core/api/app.ts +0 -6
  41. package/pkg/cli/templates/core/api/env.d.ts +0 -4
  42. package/pkg/cli/templates/core/api/errors.ts +0 -18
  43. package/pkg/cli/templates/core/api/router.ts +0 -6
  44. package/pkg/cli/templates/core/api/server.ts +0 -63
  45. package/pkg/cli/templates/core/api/use.ts +0 -25
  46. package/pkg/cli/templates/vite.base.hbs +0 -44
  47. package/pkg/src/base-plugin/routes/nesting.d.ts +0 -5
  48. package/pkg/src/base-plugin/routes.d.ts +0 -9
  49. package/pkg/src/cli/base.d.ts +0 -30
  50. package/pkg/src/cli/cli.d.ts +0 -2
  51. package/pkg/src/cli/factory.d.ts +0 -12
  52. package/pkg/src/cli/index.d.ts +0 -2
  53. package/pkg/src/cli/templates/core/api/app.d.ts +0 -3
  54. package/pkg/src/cli/templates/core/api/errors.d.ts +0 -1
  55. package/pkg/src/cli/templates/core/api/router.d.ts +0 -3
  56. package/pkg/src/cli/templates/core/api/server.d.ts +0 -3
  57. package/pkg/src/cli/templates/core/api/use.d.ts +0 -2
  58. package/pkg/src/index.d.ts +0 -6
  59. package/pkg/test/@fixtures/app/@src/api/articles/[...path]/index.d.ts +0 -0
  60. package/pkg/test/@fixtures/app/@src/api/books/[category]/[[author]]/index.d.ts +0 -0
  61. package/pkg/test/@fixtures/app/@src/api/books/[category]/index.d.ts +0 -0
  62. package/pkg/test/@fixtures/app/@src/api/books/index.d.ts +0 -0
  63. package/pkg/test/@fixtures/app/@src/api/files/[[folder]]/[[id]].json/index.d.ts +0 -0
  64. package/pkg/test/@fixtures/app/@src/api/files/[[folder]]/index.d.ts +0 -0
  65. package/pkg/test/@fixtures/app/@src/api/index/index.d.ts +0 -0
  66. package/pkg/test/@fixtures/app/@src/api/pages/[...path].html/index.d.ts +0 -0
  67. package/pkg/test/@fixtures/app/@src/api/users/[id].json/index.d.ts +0 -0
  68. package/pkg/test/@fixtures/app/@src/pages/about/careers/[jobId]/index.d.ts +0 -0
  69. package/pkg/test/@fixtures/app/@src/pages/about/careers/layout.d.ts +0 -0
  70. package/pkg/test/@fixtures/app/@src/pages/about/index.d.ts +0 -0
  71. package/pkg/test/@fixtures/app/@src/pages/about/layout.d.ts +0 -0
  72. package/pkg/test/@fixtures/app/@src/pages/about/team/index.d.ts +0 -0
  73. package/pkg/test/@fixtures/app/@src/pages/account/layout.d.ts +0 -0
  74. package/pkg/test/@fixtures/app/@src/pages/account/profile/index.d.ts +0 -0
  75. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/index.d.ts +0 -0
  76. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/resources/[[type]]/[...path]/index.d.ts +0 -0
  77. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/resources/[[type]]/index.d.ts +0 -0
  78. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/resources/[[type]]/layout.d.ts +0 -0
  79. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/resources/index.d.ts +0 -0
  80. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/resources/layout.d.ts +0 -0
  81. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/settings/general/index.d.ts +0 -0
  82. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/settings/index.d.ts +0 -0
  83. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/settings/layout.d.ts +0 -0
  84. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/settings/permissions/index.d.ts +0 -0
  85. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/users/[userId]/index.d.ts +0 -0
  86. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/users/index.d.ts +0 -0
  87. package/pkg/test/@fixtures/app/@src/pages/admin/[tenant]/users/layout.d.ts +0 -0
  88. package/pkg/test/@fixtures/app/@src/pages/admin/index.d.ts +0 -0
  89. package/pkg/test/@fixtures/app/@src/pages/admin/layout.d.ts +0 -0
  90. package/pkg/test/@fixtures/app/@src/pages/blog/[[category]]/[[tag]]/index.d.ts +0 -0
  91. package/pkg/test/@fixtures/app/@src/pages/blog/[[category]]/index.d.ts +0 -0
  92. package/pkg/test/@fixtures/app/@src/pages/blog/index.d.ts +0 -0
  93. package/pkg/test/@fixtures/app/@src/pages/blog/layout.d.ts +0 -0
  94. package/pkg/test/@fixtures/app/@src/pages/blog/post/[slug]/index.d.ts +0 -0
  95. package/pkg/test/@fixtures/app/@src/pages/blog/post/[slug]/layout.d.ts +0 -0
  96. package/pkg/test/@fixtures/app/@src/pages/contact/index.d.ts +0 -0
  97. package/pkg/test/@fixtures/app/@src/pages/contact/layout.d.ts +0 -0
  98. package/pkg/test/@fixtures/app/@src/pages/courses/[courseId]/layout.d.ts +0 -0
  99. package/pkg/test/@fixtures/app/@src/pages/courses/[courseId]/lessons/[[lessonId]]/assignments/[...assignmentPath]/index.d.ts +0 -0
  100. package/pkg/test/@fixtures/app/@src/pages/dashboard/[view]/index.d.ts +0 -0
  101. package/pkg/test/@fixtures/app/@src/pages/dashboard/analytics/index.d.ts +0 -0
  102. package/pkg/test/@fixtures/app/@src/pages/dashboard/index.d.ts +0 -0
  103. package/pkg/test/@fixtures/app/@src/pages/dashboard/layout.d.ts +0 -0
  104. package/pkg/test/@fixtures/app/@src/pages/dashboard/settings/billing/index.d.ts +0 -0
  105. package/pkg/test/@fixtures/app/@src/pages/dashboard/settings/index.d.ts +0 -0
  106. package/pkg/test/@fixtures/app/@src/pages/dashboard/settings/layout.d.ts +0 -0
  107. package/pkg/test/@fixtures/app/@src/pages/dashboard/settings/notifications/index.d.ts +0 -0
  108. package/pkg/test/@fixtures/app/@src/pages/dashboard/settings/profile/index.d.ts +0 -0
  109. package/pkg/test/@fixtures/app/@src/pages/dashboard/settings/security/index.d.ts +0 -0
  110. package/pkg/test/@fixtures/app/@src/pages/dashboard/settings/security/layout.d.ts +0 -0
  111. package/pkg/test/@fixtures/app/@src/pages/docs/[...path]/index.d.ts +0 -0
  112. package/pkg/test/@fixtures/app/@src/pages/docs/index.d.ts +0 -0
  113. package/pkg/test/@fixtures/app/@src/pages/docs/layout.d.ts +0 -0
  114. package/pkg/test/@fixtures/app/@src/pages/files/[...filePath]/index.d.ts +0 -0
  115. package/pkg/test/@fixtures/app/@src/pages/files/[...filePath]/layout.d.ts +0 -0
  116. package/pkg/test/@fixtures/app/@src/pages/legal/layout.d.ts +0 -0
  117. package/pkg/test/@fixtures/app/@src/pages/legal/privacy/index.d.ts +0 -0
  118. package/pkg/test/@fixtures/app/@src/pages/legal/terms/index.d.ts +0 -0
  119. package/pkg/test/@fixtures/app/@src/pages/news/[category]/articles/[...articlePath]/index.d.ts +0 -0
  120. package/pkg/test/@fixtures/app/@src/pages/news/[category]/layout.d.ts +0 -0
  121. package/pkg/test/@fixtures/app/@src/pages/portal/[clientId]/layout.d.ts +0 -0
  122. package/pkg/test/@fixtures/app/@src/pages/portal/[clientId]/reports/[reportType]/data/[dataView]/index.d.ts +0 -0
  123. package/pkg/test/@fixtures/app/@src/pages/portal/[clientId]/reports/[reportType]/data/[dataView]/layout.d.ts +0 -0
  124. package/pkg/test/@fixtures/app/@src/pages/portal/[clientId]/reports/[reportType]/layout.d.ts +0 -0
  125. package/pkg/test/@fixtures/app/@src/pages/portal/[clientId]/reports/layout.d.ts +0 -0
  126. package/pkg/test/@fixtures/app/@src/pages/portal/layout.d.ts +0 -0
  127. package/pkg/test/@fixtures/app/@src/pages/products/[id]/index.d.ts +0 -0
  128. package/pkg/test/@fixtures/app/@src/pages/products/index.d.ts +0 -0
  129. package/pkg/test/@fixtures/app/@src/pages/profile/[username]/layout.d.ts +0 -0
  130. package/pkg/test/@fixtures/app/@src/pages/profile/[username]/posts/[postId]/comments/[...thread]/index.d.ts +0 -0
  131. package/pkg/test/@fixtures/app/@src/pages/profile/[username]/posts/[postId]/layout.d.ts +0 -0
  132. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/files/[...path]/index.d.ts +0 -0
  133. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/files/index.d.ts +0 -0
  134. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/files/layout.d.ts +0 -0
  135. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/index.d.ts +0 -0
  136. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/layout.d.ts +0 -0
  137. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/tasks/[taskId]/comments/[commentId]/index.d.ts +0 -0
  138. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/tasks/[taskId]/comments/index.d.ts +0 -0
  139. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/tasks/[taskId]/comments/layout.d.ts +0 -0
  140. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/tasks/[taskId]/index.d.ts +0 -0
  141. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/tasks/[taskId]/layout.d.ts +0 -0
  142. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/tasks/index.d.ts +0 -0
  143. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/tasks/layout.d.ts +0 -0
  144. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/team/[userId]/index.d.ts +0 -0
  145. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/team/index.d.ts +0 -0
  146. package/pkg/test/@fixtures/app/@src/pages/projects/[projectId]/team/layout.d.ts +0 -0
  147. package/pkg/test/@fixtures/app/@src/pages/projects/index.d.ts +0 -0
  148. package/pkg/test/@fixtures/app/@src/pages/projects/layout.d.ts +0 -0
  149. package/pkg/test/@fixtures/app/@src/pages/properties/[[city]]/filters/[...filters]/index.d.ts +0 -0
  150. package/pkg/test/@fixtures/app/@src/pages/properties/filters/index.d.ts +0 -0
  151. package/pkg/test/@fixtures/app/@src/pages/properties/layout.d.ts +0 -0
  152. package/pkg/test/@fixtures/app/@src/pages/search/[[query]]/[[page]]/index.d.ts +0 -0
  153. package/pkg/test/@fixtures/app/@src/pages/search/[[query]]/layout.d.ts +0 -0
  154. package/pkg/test/@fixtures/app/@src/pages/search/index.d.ts +0 -0
  155. package/pkg/test/@fixtures/app/@src/pages/shop/[category]/[productId]/index.d.ts +0 -0
  156. package/pkg/test/@fixtures/app/@src/pages/shop/[category]/[productId]/layout.d.ts +0 -0
  157. package/pkg/test/@fixtures/app/@src/pages/shop/cart/index.d.ts +0 -0
  158. package/pkg/test/@fixtures/app/@src/pages/shop/checkout/confirm/index.d.ts +0 -0
  159. package/pkg/test/@fixtures/app/@src/pages/shop/checkout/layout.d.ts +0 -0
  160. package/pkg/test/@fixtures/app/@src/pages/shop/checkout/payment/index.d.ts +0 -0
  161. package/pkg/test/@fixtures/app/@src/pages/shop/checkout/shipping/index.d.ts +0 -0
  162. package/pkg/test/@fixtures/app/@src/pages/shop/checkout/shipping/layout.d.ts +0 -0
  163. package/pkg/test/@fixtures/app/@src/pages/shop/index.d.ts +0 -0
  164. package/pkg/test/@fixtures/app/@src/pages/shop/layout.d.ts +0 -0
  165. package/pkg/test/@fixtures/app/@src/pages/shop/orders/[orderId]/index.d.ts +0 -0
  166. package/pkg/test/@fixtures/app/@src/pages/shop/orders/index.d.ts +0 -0
  167. package/pkg/test/@fixtures/app/@src/pages/shop/orders/layout.d.ts +0 -0
  168. package/pkg/test/@fixtures/app/@src/pages/shop/product/[id]/index.d.ts +0 -0
  169. package/pkg/test/@fixtures/app/@src/pages/shop/product/[id]/layout.d.ts +0 -0
  170. package/pkg/test/@fixtures/app/@src/pages/shop/product/[id]/reviews/index.d.ts +0 -0
  171. package/pkg/test/@fixtures/app/@src/pages/shop/products/[[category]]/index.d.ts +0 -0
  172. package/pkg/test/@fixtures/app/@src/pages/shop/products/index.d.ts +0 -0
  173. package/pkg/test/@fixtures/app/@src/pages/shop/products/layout.d.ts +0 -0
  174. package/pkg/test/@fixtures/app/@src/pages/signup/index.d.ts +0 -0
  175. package/pkg/test/@fixtures/app/@src/pages/store/[category]/filters/[...filters]/index.d.ts +0 -0
  176. package/pkg/test/@fixtures/app/@src/pages/store/[category]/sort/[sortBy]/index.d.ts +0 -0
  177. package/pkg/test/@fixtures/app/@src/pages/store/[category]/sort/layout.d.ts +0 -0
  178. package/pkg/test/@fixtures/app/@src/pages/store/layout.d.ts +0 -0
  179. package/pkg/test/@fixtures/app/@src/pages/users/[username]/followers/index.d.ts +0 -0
  180. package/pkg/test/@fixtures/app/@src/pages/users/[username]/following/index.d.ts +0 -0
  181. package/pkg/test/@fixtures/app/@src/pages/users/[username]/index.d.ts +0 -0
  182. package/pkg/test/@fixtures/app/@src/pages/users/[username]/layout.d.ts +0 -0
  183. package/pkg/test/@fixtures/app/@src/pages/users/[username]/posts/[postId]/index.d.ts +0 -0
  184. package/pkg/test/@fixtures/app/@src/pages/users/[username]/posts/[postId]/layout.d.ts +0 -0
  185. package/pkg/test/@fixtures/app/@src/pages/users/[username]/posts/index.d.ts +0 -0
  186. package/pkg/test/@fixtures/app/@src/pages/users/[username]/posts/layout.d.ts +0 -0
  187. package/pkg/test/@fixtures/app/@src/pages/users/index.d.ts +0 -0
  188. package/pkg/test/@fixtures/app/@src/pages/users/layout.d.ts +0 -0
  189. package/pkg/test/@fixtures/app/@src/pages/workspace/[workspaceId]/analytics/[range]/index.d.ts +0 -0
  190. package/pkg/test/@fixtures/app/@src/pages/workspace/[workspaceId]/analytics/[range]/layout.d.ts +0 -0
  191. package/pkg/test/@fixtures/app/@src/pages/workspace/[workspaceId]/analytics/index.d.ts +0 -0
  192. package/pkg/test/@fixtures/app/@src/pages/workspace/[workspaceId]/analytics/layout.d.ts +0 -0
  193. package/pkg/test/@fixtures/app/@src/pages/workspace/[workspaceId]/team/[memberId]/permissions/[...permissionPath]/index.d.ts +0 -0
  194. package/pkg/test/@fixtures/app/@src/pages/workspace/[workspaceId]/team/layout.d.ts +0 -0
  195. package/pkg/test/@fixtures/app/lib/@src/{api}/articles/[...path]/index.ts/types.d.ts +0 -3
  196. package/pkg/test/@fixtures/app/lib/@src/{api}/articles/[...path]/types.d.ts +0 -3
  197. package/pkg/test/@fixtures/app/lib/@src/{api}/books/[category]/[[author]]/index.ts/types.d.ts +0 -4
  198. package/pkg/test/@fixtures/app/lib/@src/{api}/books/[category]/[[author]]/types.d.ts +0 -4
  199. package/pkg/test/@fixtures/app/lib/@src/{api}/books/[category]/index.ts/types.d.ts +0 -3
  200. package/pkg/test/@fixtures/app/lib/@src/{api}/books/[category]/types.d.ts +0 -3
  201. package/pkg/test/@fixtures/app/lib/@src/{api}/books/index.ts/types.d.ts +0 -1
  202. package/pkg/test/@fixtures/app/lib/@src/{api}/books/types.d.ts +0 -1
  203. package/pkg/test/@fixtures/app/lib/@src/{api}/files/[[folder]]/[[id]].json/index.ts/types.d.ts +0 -4
  204. package/pkg/test/@fixtures/app/lib/@src/{api}/files/[[folder]]/[[id]].json/types.d.ts +0 -4
  205. package/pkg/test/@fixtures/app/lib/@src/{api}/files/[[folder]]/index.ts/types.d.ts +0 -3
  206. package/pkg/test/@fixtures/app/lib/@src/{api}/files/[[folder]]/types.d.ts +0 -3
  207. package/pkg/test/@fixtures/app/lib/@src/{api}/index/index.ts/types.d.ts +0 -1
  208. package/pkg/test/@fixtures/app/lib/@src/{api}/index/types.d.ts +0 -1
  209. package/pkg/test/@fixtures/app/lib/@src/{api}/pages/[...path].html/index.ts/types.d.ts +0 -3
  210. package/pkg/test/@fixtures/app/lib/@src/{api}/pages/[...path].html/types.d.ts +0 -3
  211. package/pkg/test/@fixtures/app/lib/@src/{api}/users/[id].json/index.ts/types.d.ts +0 -3
  212. package/pkg/test/@fixtures/app/lib/@src/{api}/users/[id].json/types.d.ts +0 -3
  213. package/pkg/test/@fixtures/ast/extractTypeDeclarations/exports/with-referenced-files.d.ts +0 -1
  214. package/pkg/test/@fixtures/ast/extractTypeDeclarations/imports/with-referenced-files.d.ts +0 -1
  215. package/pkg/test/ast/extractParamsRefinements.test.d.ts +0 -1
  216. package/pkg/test/ast/extractRouteMethods.test.d.ts +0 -1
  217. package/pkg/test/ast/extractTypeDeclarations.test.d.ts +0 -1
  218. package/pkg/test/cli/create.test.d.ts +0 -1
  219. package/pkg/test/routes/base.d.ts +0 -4
  220. package/pkg/test/routes/nesting.test.d.ts +0 -1
  221. package/pkg/test/routes/resolver.test.d.ts +0 -1
  222. package/pkg/test/setup.d.ts +0 -1
  223. /package/pkg/{src/alias-plugin → alias-plugin}/index.d.ts +0 -0
  224. /package/pkg/{src/base-plugin → base-plugin}/spinner.d.ts +0 -0
  225. /package/pkg/{src/define-plugin → define-plugin}/index.d.ts +0 -0
package/pkg/index.js CHANGED
@@ -1,33 +1,135 @@
1
- // src/index.ts
2
- import { default as default2 } from "@kosmojs/api-generator";
3
- import { default as default3 } from "@kosmojs/fetch-generator";
4
-
5
1
  // src/alias-plugin/index.ts
6
2
  import { glob } from "tinyglobby";
3
+
4
+ // src/paths.ts
5
+ import { join } from "node:path";
6
+
7
+ // src/defaults.ts
8
+ var defaults = {
9
+ appPrefix: "~",
10
+ srcPrefix: "@",
11
+ libPrefix: "_",
12
+ coreDir: "core",
13
+ srcDir: "src",
14
+ libDir: "lib",
15
+ configDir: "config",
16
+ apiDir: "api",
17
+ pagesDir: "pages",
18
+ entryDir: "entry",
19
+ fetchDir: "fetch"
20
+ };
21
+
22
+ // src/paths.ts
23
+ var createTsconfigPaths = (prefix) => {
24
+ return {
25
+ [`${defaults.appPrefix}/*`]: [`${prefix}/*`],
26
+ [`${defaults.srcPrefix}/*`]: [`${prefix}/${defaults.srcDir}/*`],
27
+ [`${defaults.libPrefix}/*`]: [
28
+ `${prefix}/${defaults.libDir}/${defaults.srcDir}/*`
29
+ ]
30
+ };
31
+ };
32
+ var pathResolver = ({
33
+ appRoot,
34
+ sourceFolder
35
+ }) => {
36
+ const createPath = (...a) => {
37
+ return appRoot ? join(appRoot, ...a) : join(...a);
38
+ };
39
+ const createImport = {
40
+ coreApi(...a) {
41
+ return join(defaults.appPrefix, defaults.coreDir, defaults.apiDir, ...a);
42
+ },
43
+ src(...a) {
44
+ return join(defaults.srcPrefix, sourceFolder, ...a);
45
+ },
46
+ config(...a) {
47
+ return this.src(defaults.configDir, ...a);
48
+ },
49
+ api(...a) {
50
+ return this.src(defaults.apiDir, ...a);
51
+ },
52
+ pages(...a) {
53
+ return this.src(defaults.pagesDir, ...a);
54
+ },
55
+ lib(...a) {
56
+ return join(defaults.libPrefix, sourceFolder, ...a);
57
+ },
58
+ libApi(...a) {
59
+ return this.lib(defaults.apiDir, ...a);
60
+ },
61
+ libEntry(...a) {
62
+ return this.lib(defaults.entryDir, ...a);
63
+ }
64
+ };
65
+ return {
66
+ createPath: {
67
+ coreApi(...a) {
68
+ return createPath(defaults.coreDir, defaults.apiDir, ...a);
69
+ },
70
+ src(...a) {
71
+ return createPath(defaults.srcDir, sourceFolder, ...a);
72
+ },
73
+ api(...a) {
74
+ return this.src(defaults.apiDir, ...a);
75
+ },
76
+ pages(...a) {
77
+ return this.src(defaults.pagesDir, ...a);
78
+ },
79
+ config(...a) {
80
+ return this.src(defaults.configDir, ...a);
81
+ },
82
+ entry(...a) {
83
+ return this.src(defaults.entryDir, ...a);
84
+ },
85
+ lib(...a) {
86
+ return createPath(defaults.libDir, defaults.srcDir, sourceFolder, ...a);
87
+ },
88
+ libApi(...a) {
89
+ return this.lib(defaults.apiDir, ...a);
90
+ },
91
+ libEntry(...a) {
92
+ return this.lib(defaults.entryDir, ...a);
93
+ },
94
+ libPages(...a) {
95
+ return this.lib(defaults.pagesDir, ...a);
96
+ }
97
+ },
98
+ createImport,
99
+ createImportHelper: (key, ...a) => {
100
+ return createImport[key](...a.slice(0, -1));
101
+ }
102
+ };
103
+ };
104
+
105
+ // src/alias-plugin/index.ts
7
106
  var alias_plugin_default = (appRoot, opt) => {
8
107
  return {
9
108
  name: "@kosmojs:aliasPlugin",
10
109
  async config() {
11
- const compilerOptions = await import(`${appRoot}/tsconfig.json`, {
12
- with: { type: "json" }
13
- }).then((e) => e.default.compilerOptions);
110
+ const paths = await import(`${appRoot}/tsconfig.json`, { with: { type: "json" } }).then((e) => {
111
+ return {
112
+ ...e.default.compilerOptions?.paths,
113
+ ...createTsconfigPaths(".")
114
+ };
115
+ });
14
116
  const aliasmap = [];
15
- const pathEntries = Object.entries({ ...compilerOptions?.paths });
117
+ const pathEntries = Object.entries(paths);
16
118
  for (const [aliasPattern, pathPatterns] of pathEntries) {
17
119
  const alias = aliasPattern.replace("/*", "");
18
- const paths = pathPatterns.map((e) => e.replace("/*", "")).sort((a, b) => a.split(/\/+/).length - b.split(/\/+/).length);
19
- if (paths.length === 1) {
120
+ const paths2 = pathPatterns.map((e) => e.replace("/*", "")).sort((a, b) => a.split(/\/+/).length - b.split(/\/+/).length);
121
+ if (paths2.length === 1) {
20
122
  aliasmap.push({
21
123
  find: new RegExp(`^${alias}/`),
22
- replacement: `${appRoot}/${paths[0]}/`
124
+ replacement: `${appRoot}/${paths2[0]}/`
23
125
  });
24
- } else if (paths.length > 1) {
126
+ } else if (paths2.length > 1) {
25
127
  aliasmap.push({
26
128
  find: new RegExp(`^${alias}/`),
27
129
  replacement: "",
28
130
  async customResolver(_src) {
29
131
  const src = _src.replace(/(\$|\^|\+|\(|\)|\[|\])/g, "\\$1");
30
- const patterns = paths.flatMap((path) => [
132
+ const patterns = paths2.flatMap((path) => [
31
133
  // Case 1: Extension is explicitly provided
32
134
  // e.g. import styles from "@admin/{solid}/styles.module.css"
33
135
  `${path}/${src}*`,
@@ -67,111 +169,33 @@ var alias_plugin_default = (appRoot, opt) => {
67
169
  };
68
170
 
69
171
  // src/base-plugin/index.ts
70
- import { basename, join as join3, resolve as resolve5 } from "node:path";
71
- import { styleText as styleText2 } from "node:util";
172
+ import { writeFile as writeFile3 } from "node:fs/promises";
173
+ import { basename as basename2, join as join5, resolve as resolve5 } from "node:path";
174
+ import { styleText as styleText4 } from "node:util";
72
175
  import { Worker } from "node:worker_threads";
73
- import apiGenerator from "@kosmojs/api-generator";
74
176
  import stubGenerator from "@kosmojs/dev/stub-generator";
75
- import fetchGenerator from "@kosmojs/fetch-generator";
76
177
 
77
- // src/base-plugin/api-handler.ts
78
- import { join, resolve } from "node:path";
79
- import { styleText } from "node:util";
80
- import { context } from "esbuild";
81
- import { defaults } from "@kosmojs/devlib";
82
- var api_handler_default = async (options) => {
83
- const { appRoot, sourceFolder, baseurl, apiurl } = options;
84
- const apiDir = join(sourceFolder, defaults.apiDir);
85
- const outDir = join(options.outDir, defaults.apiDir);
86
- const esbuildOptions = await import(resolve(appRoot, "esbuild.json"), { with: { type: "json" } }).then((e) => e.default);
87
- let app;
88
- let devMiddlewareFactory;
89
- let teardownHandler;
90
- const watcher = async () => {
91
- const rebuildPlugin = {
92
- name: "rebuild",
93
- setup(build) {
94
- build.onEnd(async () => {
95
- if (app) {
96
- await teardownHandler?.(app);
97
- }
98
- try {
99
- const exports = await import(`${outDir}/app.js?${Date.now()}`);
100
- devMiddlewareFactory = exports.devMiddlewareFactory;
101
- teardownHandler = exports.teardownHandler;
102
- app = await exports.default();
103
- console.debug(`${styleText("green", "\u279C")} Api handler ready`);
104
- } catch (error) {
105
- console.error(`${styleText("red", "\u2717")} Api handler error`);
106
- console.error(error);
107
- }
108
- });
109
- }
110
- };
111
- const ctx = await context({
112
- ...esbuildOptions,
113
- logLevel: "error",
114
- bundle: true,
115
- entryPoints: [join(apiDir, "app.ts")],
116
- plugins: [rebuildPlugin],
117
- outdir: outDir
118
- });
119
- return {
120
- async start() {
121
- await ctx.watch({
122
- // waits this many milliseconds before rebuilding after a change is detected
123
- delay: options.watcher.delay
124
- });
125
- },
126
- async stop() {
127
- await ctx.dispose();
128
- }
129
- };
130
- };
131
- const devMiddleware = async (req, res, viteHandler) => {
132
- const next = () => {
133
- return viteHandler();
134
- };
135
- if (devMiddlewareFactory) {
136
- const handler = devMiddlewareFactory(app);
137
- await handler(req, res, next);
138
- } else {
139
- !req?.url || !new RegExp(`^${join(baseurl, apiurl)}($|/)`).test(req.url) ? next() : await app?.callback()(req, res);
140
- }
141
- };
142
- return {
143
- watcher,
144
- devMiddleware
145
- };
146
- };
147
-
148
- // src/base-plugin/routes.ts
149
- import {
150
- defaults as defaults3
151
- } from "@kosmojs/devlib";
152
-
153
- // src/base-plugin/routes/resolve.ts
154
- import { dirname as dirname2, join as join2, resolve as resolve4 } from "node:path";
155
- import crc3 from "crc/crc32";
178
+ // src/routes-factory/resolve.ts
179
+ import { dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
180
+ import { styleText as styleText2 } from "node:util";
181
+ import crc5 from "crc/crc32";
182
+ import mimeTypes from "mime-types";
156
183
  import picomatch from "picomatch";
157
184
  import { glob as glob2 } from "tinyglobby";
158
- import {
159
- defaults as defaults2,
160
- pathResolver as pathResolver2,
161
- pathTokensFactory,
162
- render,
163
- renderToFile
164
- } from "@kosmojs/devlib";
165
185
 
166
- // src/base-plugin/ast.ts
167
- import { resolve as resolve2 } from "node:path";
186
+ // src/ast.ts
187
+ import { resolve } from "node:path";
188
+ import { styleText } from "node:util";
168
189
  import crc from "crc/crc32";
169
190
  import { flattener } from "tfusion";
170
191
  import {
171
192
  Project,
172
193
  SyntaxKind
173
194
  } from "ts-morph";
174
- import { HTTPMethods } from "@kosmojs/api";
195
+ import {
196
+ HTTPMethods,
197
+ RequestValidationTargets
198
+ } from "@kosmojs/api";
175
199
  var createProject = (opts) => new Project(opts);
176
200
  var resolveRouteSignature = async (route, opts) => {
177
201
  const {
@@ -183,19 +207,12 @@ var resolveRouteSignature = async (route, opts) => {
183
207
  );
184
208
  const defaultExport = extractDefaultExport(sourceFile);
185
209
  const paramsRefinements = defaultExport ? extractParamsRefinements(defaultExport) : void 0;
186
- const methods = defaultExport ? extractRouteMethods(defaultExport, route) : [];
187
- const payloadTypes = methods.flatMap((e) => {
188
- return e.payloadType ? [e.payloadType] : [];
189
- });
190
- const responseTypes = methods.flatMap((e) => {
191
- return e.responseType ? [e.responseType] : [];
192
- });
210
+ const methods = defaultExport ? extractRouteMethods(route, defaultExport) : [];
193
211
  return {
194
212
  typeDeclarations,
195
213
  paramsRefinements,
196
214
  methods: methods.map((e) => e.method),
197
- payloadTypes,
198
- responseTypes,
215
+ validationDefinitions: methods.flatMap((e) => e.validationDefinitions),
199
216
  referencedFiles
200
217
  };
201
218
  };
@@ -210,22 +227,23 @@ var extractDefaultExport = (sourceFile) => {
210
227
  return defaultExport;
211
228
  };
212
229
  var extractParamsRefinements = (callExpression) => {
213
- const [firstGeneric] = extractGenerics(callExpression);
214
- if (!firstGeneric?.node.isKind(SyntaxKind.TupleType)) {
215
- return;
216
- }
217
- const tupleElements = firstGeneric.node.getElements();
218
- if (!tupleElements?.length) {
230
+ const [
231
+ _routeName,
232
+ // first generic - the route name
233
+ paramsGeneric
234
+ // second generic - params refinements
235
+ ] = extractGenerics(callExpression);
236
+ if (!paramsGeneric?.isKind(SyntaxKind.TupleType)) {
219
237
  return;
220
238
  }
221
- return tupleElements.map((node, index) => {
239
+ return paramsGeneric.getElements().map((node, index) => {
222
240
  return {
223
241
  index,
224
242
  text: node.getText()
225
243
  };
226
244
  });
227
245
  };
228
- var extractRouteMethods = (callExpression, route) => {
246
+ var extractRouteMethods = (route, callExpression) => {
229
247
  const funcDeclaration = callExpression.getFirstChildByKind(SyntaxKind.ArrowFunction) || callExpression.getFirstChildByKind(SyntaxKind.FunctionExpression);
230
248
  if (!funcDeclaration) {
231
249
  return [];
@@ -246,35 +264,173 @@ var extractRouteMethods = (callExpression, route) => {
246
264
  }
247
265
  }
248
266
  const methods = [];
249
- const skipValidationFilter = (e) => /@skip-validation/.test(e);
250
267
  for (const [callExpression2, method] of callExpressions) {
251
- const [payloadGeneric, responseGeneric] = extractGenerics(callExpression2);
252
- const payloadText = payloadGeneric?.node ? payloadGeneric.node.getChildren().length === 0 ? "{}" : payloadGeneric.node.getFullText() : void 0;
253
- const responseText = responseGeneric?.node.getText();
254
- const responseType = responseText ? {
255
- id: ["ResponseT", crc(route.importName + method)].join(""),
256
- method,
257
- skipValidation: responseGeneric?.comments ? responseGeneric.comments.some(skipValidationFilter) : false,
258
- text: ["never", "object"].includes(responseText) ? "{}" : responseText,
259
- resolvedType: void 0
260
- } : void 0;
261
- const payloadType = payloadText ? {
262
- id: ["PayloadT", crc(route.importName + method)].join(""),
263
- responseTypeId: responseType?.id,
264
- method,
265
- skipValidation: payloadGeneric?.comments ? payloadGeneric.comments.some(skipValidationFilter) : false,
266
- isOptional: payloadText ? payloadText === "{}" || route.optionalParams : true,
267
- text: payloadText,
268
- resolvedType: void 0
269
- } : void 0;
268
+ const [vDefs, vOpts] = extractGenerics(callExpression2);
270
269
  methods.push({
271
270
  method,
272
- payloadType,
273
- responseType
271
+ validationDefinitions: extractValidationDefinitions(
272
+ route,
273
+ method,
274
+ vDefs,
275
+ vOpts
276
+ )
274
277
  });
275
278
  }
276
279
  return methods;
277
280
  };
281
+ var parseRuntimeValidation = (typeNode) => {
282
+ if (typeNode.isKind(SyntaxKind.LiteralType)) {
283
+ const literal = typeNode.getFirstChild();
284
+ if (literal?.isKind(SyntaxKind.TrueKeyword)) {
285
+ return true;
286
+ } else if (literal?.isKind(SyntaxKind.FalseKeyword)) {
287
+ return false;
288
+ }
289
+ }
290
+ return void 0;
291
+ };
292
+ var extractResponseVariant = (typeNode) => {
293
+ if (!typeNode.isKind(SyntaxKind.TupleType)) {
294
+ return;
295
+ }
296
+ let status = 200;
297
+ let contentType;
298
+ let body;
299
+ const [statusNode, contentTypeNode, bodyNode] = typeNode.getElements();
300
+ if (statusNode?.isKind(SyntaxKind.LiteralType)) {
301
+ const literal = statusNode.getFirstChildByKind(SyntaxKind.NumericLiteral);
302
+ if (literal) {
303
+ status = Number(literal.getText());
304
+ }
305
+ }
306
+ if (contentTypeNode) {
307
+ contentType = extractStringLiteral(contentTypeNode);
308
+ }
309
+ if (bodyNode) {
310
+ body = bodyNode.getText();
311
+ if (["object"].includes(body)) {
312
+ body = "{}";
313
+ }
314
+ }
315
+ return { status, contentType, body };
316
+ };
317
+ var parseValidationOptions = (typeNode) => {
318
+ const opts = {};
319
+ if (!typeNode?.isKind(SyntaxKind.TypeLiteral)) {
320
+ return opts;
321
+ }
322
+ for (const prop of typeNode.getMembers()) {
323
+ if (!prop.isKind(SyntaxKind.PropertySignature)) {
324
+ continue;
325
+ }
326
+ const target = prop.getName();
327
+ const typeNode2 = prop.getTypeNodeOrThrow();
328
+ if (!typeNode2.isKind(SyntaxKind.TypeLiteral)) {
329
+ continue;
330
+ }
331
+ let contentType;
332
+ let runtimeValidation;
333
+ const customErrors = {};
334
+ for (const member of typeNode2.getMembers()) {
335
+ if (!member.isKind(SyntaxKind.PropertySignature)) {
336
+ continue;
337
+ }
338
+ const nameNode = member.getNameNode();
339
+ const valueNode = member.getTypeNodeOrThrow();
340
+ const name = nameNode.isKind(SyntaxKind.StringLiteral) ? nameNode.getLiteralText() : nameNode.getText();
341
+ if (name === "contentType") {
342
+ contentType = extractStringLiteral(valueNode);
343
+ } else if (name === "runtimeValidation") {
344
+ runtimeValidation = parseRuntimeValidation(valueNode);
345
+ } else if (name.startsWith("error")) {
346
+ const literal = extractStringLiteral(valueNode);
347
+ if (literal) {
348
+ customErrors[name] = literal;
349
+ }
350
+ }
351
+ }
352
+ opts[target] = {
353
+ contentType,
354
+ runtimeValidation,
355
+ customErrors
356
+ };
357
+ }
358
+ return opts;
359
+ };
360
+ var extractStringLiteral = (typeNode) => {
361
+ const literal = typeNode.isKind(SyntaxKind.LiteralType) ? typeNode.getFirstChildByKind(SyntaxKind.StringLiteral) : void 0;
362
+ return literal ? literal.getLiteralText() : void 0;
363
+ };
364
+ var extractValidationDefinitions = (route, method, defsNode, optsNode) => {
365
+ const definitions = [];
366
+ if (!defsNode?.isKind(SyntaxKind.TypeLiteral)) {
367
+ return definitions;
368
+ }
369
+ const optsMap = parseValidationOptions(optsNode);
370
+ const createId = (target, hash) => {
371
+ return [
372
+ target.replace(/^./, (c) => c.toUpperCase()),
373
+ "T",
374
+ method,
375
+ crc(route.id + hash)
376
+ ].join("");
377
+ };
378
+ for (const prop of defsNode.getMembers()) {
379
+ if (!prop.isKind(SyntaxKind.PropertySignature)) {
380
+ continue;
381
+ }
382
+ const target = prop.getName();
383
+ const typeNode = prop.getTypeNodeOrThrow();
384
+ if (target === "response") {
385
+ const variants = typeNode.isKind(SyntaxKind.UnionType) ? typeNode.getChildrenOfKind(SyntaxKind.TupleType) : [typeNode];
386
+ definitions.push({
387
+ ...optsMap[target],
388
+ method,
389
+ target,
390
+ variants: variants.flatMap((e, i) => {
391
+ const { status, contentType, body } = extractResponseVariant(e) || {};
392
+ if (!status) {
393
+ return [];
394
+ }
395
+ if (contentType && typeof contentType !== "string") {
396
+ console.warn(
397
+ styleText(
398
+ ["bold", "red"],
399
+ `\u2717 The second element of a response variant should specify the Response Content Type`
400
+ )
401
+ );
402
+ console.warn(
403
+ styleText(["blue"], ` Example: [200, "json", Schema]`)
404
+ );
405
+ console.warn(
406
+ ` Route: ${route.name}; Method: ${method}; Response Variant: #${i}`
407
+ );
408
+ console.warn();
409
+ }
410
+ return [
411
+ {
412
+ id: createId(target, JSON.stringify([status, contentType, body])),
413
+ status,
414
+ contentType,
415
+ body
416
+ }
417
+ ];
418
+ })
419
+ });
420
+ } else if (Object.keys(RequestValidationTargets).includes(target)) {
421
+ definitions.push({
422
+ ...optsMap[target],
423
+ method,
424
+ target,
425
+ schema: {
426
+ id: createId(target),
427
+ text: typeNode.getText()
428
+ }
429
+ });
430
+ }
431
+ }
432
+ return definitions;
433
+ };
278
434
  var extractTypeDeclarations = (sourceFile, opts) => {
279
435
  const declarations = [];
280
436
  const referencedFiles = opts?.withReferencedFiles ? [] : void 0;
@@ -379,16 +535,11 @@ var getReferencedFiles = (importIdentifier) => {
379
535
  });
380
536
  };
381
537
  var extractGenerics = (callExpression) => {
382
- return callExpression.getTypeArguments().map((node) => {
383
- return {
384
- node,
385
- comments: node.getLeadingCommentRanges().map((range) => range.getText().trim())
386
- };
387
- });
538
+ return callExpression.getTypeArguments();
388
539
  };
389
540
  var typeResolverFactory = ({ appRoot }) => {
390
541
  const project = createProject({
391
- tsConfigFilePath: resolve2(appRoot, "tsconfig.json"),
542
+ tsConfigFilePath: resolve(appRoot, "tsconfig.json"),
392
543
  skipAddingFilesFromTsConfig: true
393
544
  });
394
545
  const literalTypesResolver = (literalTypes, options) => {
@@ -418,12 +569,24 @@ var typeResolverFactory = ({ appRoot }) => {
418
569
  };
419
570
  };
420
571
 
421
- // src/base-plugin/cache.ts
572
+ // src/cache.ts
422
573
  import { mkdir, readFile, writeFile } from "node:fs/promises";
423
- import { dirname, resolve as resolve3 } from "node:path";
574
+ import { dirname, resolve as resolve2 } from "node:path";
424
575
  import crc2 from "crc/crc32";
425
576
  import self from "@kosmojs/dev/package.json" with { type: "json" };
426
- import { pathExists, pathResolver } from "@kosmojs/devlib";
577
+
578
+ // src/fs.ts
579
+ import { access, constants } from "node:fs/promises";
580
+ var pathExists = async (path) => {
581
+ try {
582
+ await access(path, constants.F_OK);
583
+ return true;
584
+ } catch {
585
+ return false;
586
+ }
587
+ };
588
+
589
+ // src/cache.ts
427
590
  var cacheFactory = (route, {
428
591
  appRoot,
429
592
  sourceFolder,
@@ -432,7 +595,7 @@ var cacheFactory = (route, {
432
595
  const cacheFile = pathResolver({
433
596
  appRoot,
434
597
  sourceFolder
435
- }).resolve("apiLibDir", dirname(route.file), "cache.json");
598
+ }).createPath.libApi(dirname(route.file), "cache.json");
436
599
  const getCache = async (opt) => {
437
600
  if (await pathExists(cacheFile)) {
438
601
  try {
@@ -477,7 +640,7 @@ var cacheFactory = (route, {
477
640
  return;
478
641
  }
479
642
  for (const [file, hash2] of Object.entries(cache.referencedFiles)) {
480
- if (!identicalHashSum(hash2, await generateFileHash(resolve3(appRoot, file)))) {
643
+ if (!identicalHashSum(hash2, await generateFileHash(resolve2(appRoot, file)))) {
481
644
  return;
482
645
  }
483
646
  }
@@ -507,13 +670,232 @@ var identicalHashSum = (a, b) => {
507
670
  return a === b;
508
671
  };
509
672
 
510
- // src/base-plugin/templates/resolved-types.hbs
673
+ // src/render.ts
674
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
675
+ import { dirname as dirname2, join as join2 } from "node:path";
676
+ import crc3 from "crc/crc32";
677
+ import handlebars from "handlebars";
678
+ var render = (template, context2, options) => {
679
+ const { noEscape = true, renderer = handlebars } = { ...options };
680
+ return renderer.compile(template, { noEscape })(context2);
681
+ };
682
+ var renderToFile = async (file, template, context2, options) => {
683
+ const content = render(template, context2, options);
684
+ if (await pathExists(file)) {
685
+ const { overwrite = true } = { ...options };
686
+ if (overwrite === false) {
687
+ return;
688
+ }
689
+ const fileContent = await readFile2(file, "utf8");
690
+ if (typeof overwrite === "function" && !overwrite(fileContent)) {
691
+ return;
692
+ }
693
+ if (crc3(content) === crc3(fileContent)) {
694
+ return;
695
+ }
696
+ }
697
+ await mkdir2(dirname2(file), { recursive: true });
698
+ await writeFile2(file, content, "utf8");
699
+ };
700
+ var renderFactory = (options) => {
701
+ const renderer = handlebars.create();
702
+ renderer.registerPartial({ ...options?.partials });
703
+ renderer.registerHelper({ ...options?.helpers });
704
+ return {
705
+ render(template, context2, selfOoptions) {
706
+ return render(template, context2, {
707
+ renderer,
708
+ ...options,
709
+ ...selfOoptions
710
+ });
711
+ },
712
+ async renderToFile(file, template, context2, selfOoptions) {
713
+ return renderToFile(
714
+ options?.outdir ? join2(options.outdir, file) : file,
715
+ template,
716
+ context2,
717
+ { renderer, ...options, ...selfOoptions }
718
+ );
719
+ }
720
+ };
721
+ };
722
+ var renderHelpers = {
723
+ createParamsLiteral: (params) => {
724
+ return params.schema.map((p) => {
725
+ return p.kind === "splat" ? `${p.const}?: Array<string | number>` : p.kind === "optional" ? `${p.const}?: string | number` : `${p.const}: string | number`;
726
+ }).join(", ");
727
+ }
728
+ };
729
+
730
+ // src/routes-factory/base.ts
731
+ import crc4 from "crc/crc32";
732
+ import { parse } from "path-to-regexp";
733
+ var pathTokensFactory = (path, {
734
+ transformStaticValue = normalizeStaticValue
735
+ } = {}) => {
736
+ const extractParts = (tokens2, createConst, insideGroup = false) => {
737
+ const parts = [];
738
+ for (const token of tokens2) {
739
+ switch (token.type) {
740
+ case "text":
741
+ if (token.value !== "/") {
742
+ parts.push({
743
+ type: "static",
744
+ value: transformStaticValue(token.value)
745
+ });
746
+ }
747
+ break;
748
+ case "param":
749
+ parts.push({
750
+ type: "param",
751
+ kind: insideGroup ? "optional" : "required",
752
+ name: token.name,
753
+ const: createConst(token.name)
754
+ });
755
+ break;
756
+ case "wildcard":
757
+ parts.push({
758
+ type: "param",
759
+ kind: "splat",
760
+ name: token.name,
761
+ const: createConst(token.name)
762
+ });
763
+ break;
764
+ case "group":
765
+ parts.push(...extractParts(token.tokens, createConst, true));
766
+ break;
767
+ }
768
+ }
769
+ return parts;
770
+ };
771
+ const patternTransforms = [
772
+ // Transform required params: [id] => :id
773
+ // Only pure \w param names,
774
+ // [some-id] used as is, not treated as param,
775
+ // use [some_id] instead.
776
+ (s) => s.replace(/\[(\w+)\]/g, ":$1"),
777
+ // Transform optional params: {id} => {:id}
778
+ // Only pure \w param names,
779
+ // anything else treated as a path-to-regexp pattern and used as is.
780
+ // {some-id} treated as an optional static segment.
781
+ // use {some_id} for simple param syntax
782
+ // or {:some-id} pattern where :some is the param name and -id is a static segment.
783
+ (s) => s.replace(/\{(\w+)\}/g, "{:$1}"),
784
+ // Transform splat params: {...param} => {*param}
785
+ (s) => s.replace(/\{\.\.\./g, "{*"),
786
+ // Insert leading slash inside optional/splat groups.
787
+ // {:name} => {/:name}
788
+ // {*name} => {/*name}
789
+ (s) => {
790
+ return s.startsWith("{") ? s.replace(/^\{/, "{/") : s;
791
+ }
792
+ ];
793
+ const detectBareParams = (s) => {
794
+ let depth = 0;
795
+ for (const [i, ch] of [...s].entries()) {
796
+ if (ch === "{") {
797
+ depth += 1;
798
+ } else if (ch === "}") {
799
+ depth -= 1;
800
+ } else if (ch === ":" && depth === 0) {
801
+ const match = s.slice(i + 1).match(/^\w+/);
802
+ return match?.[0] || ":";
803
+ }
804
+ }
805
+ return;
806
+ };
807
+ const tokens = path.replace(/^index\/?/, "").split("/").flatMap((orig) => {
808
+ if (!orig.length) {
809
+ return [];
810
+ }
811
+ const bareParam = detectBareParams(orig);
812
+ if (bareParam === ":") {
813
+ throw new Error(
814
+ `${path} contains colons outside braces, use : only within {}`
815
+ );
816
+ } else if (bareParam) {
817
+ throw new Error(
818
+ `${path} contains bare params, use [${bareParam}] instead of :${bareParam}`
819
+ );
820
+ }
821
+ const pattern = patternTransforms.reduce((src, fn) => fn(src), orig);
822
+ const { tokens: tokens2 } = parse(pattern);
823
+ const parts = extractParts(tokens2, (val) => {
824
+ return /\W/.test(val) || /^\d/.test(val) ? [val.replace(/^\d+|\W/g, "_"), crc4(orig)].join("_") : val;
825
+ });
826
+ const isStatic = parts.length === 1 ? parts[0].type === "static" : false;
827
+ const isParam = parts.length === 1 ? parts[0].type === "param" : false;
828
+ const kind = isStatic ? "static" : isParam ? "param" : "mixed";
829
+ return [
830
+ {
831
+ kind,
832
+ orig,
833
+ pattern,
834
+ parts
835
+ }
836
+ ];
837
+ });
838
+ return [
839
+ tokens,
840
+ tokens.map(({ pattern }, i) => {
841
+ const next = tokens[i + 1];
842
+ if (!next || next.pattern.includes("/")) {
843
+ return pattern;
844
+ }
845
+ const slashRequired = tokens.slice(i + 1).some((e) => {
846
+ return e.parts.some((e2) => {
847
+ return e2.type === "static" || e2.kind === "required";
848
+ });
849
+ });
850
+ return slashRequired ? `${pattern}/` : pattern;
851
+ }).join("")
852
+ ];
853
+ };
854
+ var normalizeStaticValue = (value) => {
855
+ return value.replace(/\+/g, "\\\\+");
856
+ };
857
+ var sortRoutes = (a, b) => {
858
+ const aSpecificity = routeSpecificity(a.pathTokens);
859
+ const bSpecificity = routeSpecificity(b.pathTokens);
860
+ if (aSpecificity !== bSpecificity) {
861
+ return bSpecificity - aSpecificity;
862
+ }
863
+ if (a.pathTokens.length !== b.pathTokens.length) {
864
+ return a.pathTokens.length - b.pathTokens.length;
865
+ }
866
+ return a.name.localeCompare(b.name);
867
+ };
868
+ var paramWeight = (part) => {
869
+ return {
870
+ required: 2,
871
+ optional: 1,
872
+ splat: 0
873
+ }[part.kind];
874
+ };
875
+ var mixedSegmentWeight = (parts) => {
876
+ const hasSplat = parts.some((p) => {
877
+ return p.type === "param" ? p.kind === "splat" : false;
878
+ });
879
+ return hasSplat ? 0.5 : 3;
880
+ };
881
+ var segmentWeight = (token) => {
882
+ return {
883
+ static: 4,
884
+ mixed: mixedSegmentWeight(token.parts),
885
+ param: paramWeight(token.parts[0])
886
+ }[token.kind];
887
+ };
888
+ var routeSpecificity = (pathTokens) => {
889
+ return pathTokens.reduce((sum, token) => sum + segmentWeight(token), 0);
890
+ };
891
+
892
+ // src/routes-factory/templates/resolved-types.hbs
511
893
  var resolved_types_default = "{{#each resolvedTypes}}\nexport type {{name}} = {{text}};\n{{/each}}\n";
512
894
 
513
- // src/base-plugin/templates/types.hbs
514
- var types_default = '{{#each typeDeclarations}}{{text}}\n{{/each}}\n\nexport type {{params.id}} = {\n {{#each paramsSchema}}\n "{{name}}"{{#unless isRequired}}?{{/unless}}:{{#if isRest}} Array<{{/if}}\n {{#if refinement}}{{refinement.text}}{{else}}string{{/if}}\n {{#if isRest}}>{{/if}}\n {{/each}}\n};\n\n{{#each payloadTypes}}\nexport type {{id}} = {{text}};\n{{/each}}\n\n{{#each responseTypes}}\nexport type {{id}} = {{text}};\n{{/each}}\n';
895
+ // src/routes-factory/templates/types.hbs
896
+ var types_default = '{{#each typeDeclarations}}{{text}}\n{{/each}}\n\nexport type {{params.id}} = {\n {{#each paramsSchema}}\n "{{name}}"{{#unless isRequired}}?{{/unless}}: {{#if refinement}}\n {{refinement.text}},\n {{else}}\n {{#if isSplat}}Array<string>{{else}}string{{/if}},\n {{/if}}\n {{/each}}\n};\n\n{{#each validationTypes}}export type {{id}} = {{text}};\n{{/each}}\n';
515
897
 
516
- // src/base-plugin/routes/resolve.ts
898
+ // src/routes-factory/resolve.ts
517
899
  var API_INDEX_BASENAME = "index";
518
900
  var API_INDEX_PATTERN = `${API_INDEX_BASENAME}.ts`;
519
901
  var API_USE_BASENAME = "use";
@@ -524,29 +906,30 @@ var PAGE_LAYOUT_BASENAME = "layout";
524
906
  var PAGE_LAYOUT_PATTERN = `${PAGE_LAYOUT_BASENAME}.{tsx,vue}`;
525
907
  var ROUTE_FILE_PATTERNS = [
526
908
  // match index files in api dir
527
- `${defaults2.apiDir}/**/${API_INDEX_PATTERN}`,
909
+ `${defaults.apiDir}/**/${API_INDEX_PATTERN}`,
528
910
  // match use files in api dir
529
- `${defaults2.apiDir}/**/${API_USE_PATTERN}`,
911
+ `${defaults.apiDir}/**/${API_USE_PATTERN}`,
530
912
  // match index files in pages dir
531
- `${defaults2.pagesDir}/**/${PAGE_INDEX_PATTERN}`,
913
+ `${defaults.pagesDir}/**/${PAGE_INDEX_PATTERN}`,
532
914
  // match layout files in pages dir
533
- `${defaults2.pagesDir}/**/${PAGE_LAYOUT_PATTERN}`
915
+ `${defaults.pagesDir}/**/${PAGE_LAYOUT_PATTERN}`
534
916
  ];
535
917
  var scanRoutes = async ({
536
918
  appRoot,
537
919
  sourceFolder
538
920
  }) => {
921
+ const { createPath } = pathResolver({ appRoot, sourceFolder });
539
922
  return glob2(ROUTE_FILE_PATTERNS, {
540
- cwd: resolve4(appRoot, sourceFolder),
923
+ cwd: createPath.src(),
541
924
  absolute: true,
542
925
  onlyFiles: true,
543
926
  followSymbolicLinks: false,
544
927
  ignore: [
545
928
  // ignore top-level matches, routes resides in folders, even index route
546
- `${defaults2.apiDir}/${API_INDEX_PATTERN}`,
547
- `${defaults2.apiDir}/${API_USE_PATTERN}`,
548
- `${defaults2.pagesDir}/${PAGE_INDEX_PATTERN}`,
549
- `${defaults2.pagesDir}/${PAGE_LAYOUT_PATTERN}`
929
+ `${defaults.apiDir}/${API_INDEX_PATTERN}`,
930
+ `${defaults.apiDir}/${API_USE_PATTERN}`,
931
+ `${defaults.pagesDir}/${PAGE_INDEX_PATTERN}`,
932
+ `${defaults.pagesDir}/${PAGE_LAYOUT_PATTERN}`
550
933
  ]
551
934
  });
552
935
  };
@@ -554,11 +937,11 @@ var isRouteFile = (file, {
554
937
  appRoot,
555
938
  sourceFolder
556
939
  }) => {
557
- const [_sourceFolder, folder, ...rest] = resolve4(appRoot, file).replace(`${appRoot}/`, "").split("/");
940
+ const [_sourceFolder, folder, ...rest] = resolve3(appRoot, file).replace(`${appRoot}/${defaults.srcDir}/`, "").split("/");
558
941
  if (!folder || _sourceFolder !== sourceFolder || rest.length < 2) {
559
- return;
942
+ return false;
560
943
  }
561
- return picomatch.isMatch(join2(folder, ...rest), ROUTE_FILE_PATTERNS) ? [folder, rest.join("/")] : void 0;
944
+ return picomatch.isMatch(join3(folder, ...rest), ROUTE_FILE_PATTERNS) ? [folder, rest.join("/")] : false;
562
945
  };
563
946
  var isApiRoute = (file) => {
564
947
  return picomatch.matchBase(file, `**/${API_INDEX_PATTERN}`);
@@ -572,29 +955,27 @@ var isPageRoute = (file) => {
572
955
  var isPageLayout = (file) => {
573
956
  return picomatch.matchBase(file, `**/${PAGE_LAYOUT_PATTERN}`);
574
957
  };
575
- var createRouteEntry = (_file, {
958
+ var createRouteEntry = (fileFullpath, {
576
959
  appRoot,
577
960
  sourceFolder
578
961
  }) => {
579
- const resolvedPaths = isRouteFile(_file, { appRoot, sourceFolder });
962
+ const resolvedPaths = isRouteFile(fileFullpath, { appRoot, sourceFolder });
580
963
  if (!resolvedPaths) {
581
964
  return;
582
965
  }
583
966
  const [folder, file] = resolvedPaths;
584
- const fileFullpath = join2(appRoot, sourceFolder, folder, file);
585
- const pathTokens = pathTokensFactory(dirname2(file));
586
- const name = pathTokens.map((e) => e.orig).join("/");
587
- const importFile = file;
588
- const importName = `${importFile.replace(/\W+/g, "_")}_${crc3(importFile)}`;
589
- return {
590
- name,
591
- folder,
592
- file,
593
- fileFullpath,
594
- pathTokens,
595
- importFile,
596
- importName
597
- };
967
+ const id = `${file.replace(/\W+/g, "_")}_${crc5(file)}`;
968
+ const name = dirname3(file);
969
+ try {
970
+ const [pathTokens, pathPattern] = pathTokensFactory(dirname3(file));
971
+ return { id, name, folder, file, fileFullpath, pathTokens, pathPattern };
972
+ } catch (error) {
973
+ console.error(
974
+ `\u2757${styleText2("red", "ERROR")}: Failed parsing path for "${styleText2("cyan", file)}"`
975
+ );
976
+ console.error(error);
977
+ return;
978
+ }
598
979
  };
599
980
  var pageLayoutResolverFactory = () => {
600
981
  return (entry) => {
@@ -610,27 +991,21 @@ var pageLayoutResolverFactory = () => {
610
991
  };
611
992
  var pageRouteResolverFactory = () => {
612
993
  return (entry) => {
613
- const {
614
- name,
615
- folder,
616
- file,
617
- fileFullpath,
618
- pathTokens,
619
- importFile,
620
- importName
621
- } = entry;
994
+ const { id, name, folder, file, fileFullpath, pathTokens, pathPattern } = entry;
622
995
  const handler = async () => {
623
996
  const entry2 = {
997
+ id,
624
998
  name,
625
999
  pathTokens,
1000
+ pathPattern,
626
1001
  params: {
627
- schema: pathTokens.flatMap((e) => e.param ? [e.param] : [])
1002
+ schema: pathTokens.flatMap((e) => {
1003
+ return e.parts.filter((p) => p.type === "param");
1004
+ })
628
1005
  },
629
1006
  folder,
630
1007
  file,
631
- fileFullpath,
632
- importFile,
633
- importName
1008
+ fileFullpath
634
1009
  };
635
1010
  return {
636
1011
  kind: "pageRoute",
@@ -657,38 +1032,35 @@ var apiRouteResolverFactory = (pluginOptions) => {
657
1032
  appRoot,
658
1033
  sourceFolder,
659
1034
  generators = [],
660
- formatters = [],
661
1035
  refineTypeName
662
1036
  } = pluginOptions;
663
- let resolveTypes = false;
664
- for (const { options } of generators) {
665
- if (options?.resolveTypes) {
666
- resolveTypes = true;
667
- }
668
- }
1037
+ const resolveTypes = generators.some((e) => e.options?.resolveTypes);
669
1038
  const {
670
1039
  //
671
1040
  literalTypesResolver,
672
1041
  getSourceFile,
673
1042
  refreshSourceFile
674
1043
  } = typeResolverFactory(pluginOptions);
675
- return (entry) => {
676
- const {
677
- name,
678
- file,
679
- folder,
680
- fileFullpath,
681
- pathTokens,
682
- importFile,
683
- importName
684
- } = entry;
1044
+ return ({
1045
+ id,
1046
+ name,
1047
+ file,
1048
+ folder,
1049
+ fileFullpath,
1050
+ pathTokens,
1051
+ pathPattern
1052
+ }) => {
685
1053
  const handler = async (updatedFile) => {
686
- const paramsSchema = pathTokens.flatMap((e) => {
687
- return e.param ? [e.param] : [];
688
- });
689
- const optionalParams = paramsSchema.length ? !paramsSchema.some((e) => e.isRequired) : true;
1054
+ const paramsSchema = pathTokens.flatMap(
1055
+ (e) => {
1056
+ return e.parts.flatMap((p) => {
1057
+ return p.type === "param" ? [p] : [];
1058
+ });
1059
+ }
1060
+ );
1061
+ const optionalParams = paramsSchema.length ? paramsSchema.filter((e) => e.kind === "required").length === 0 : true;
690
1062
  const { getCache, persistCache } = cacheFactory(
691
- { file, fileFullpath, importName },
1063
+ { id, file, fileFullpath },
692
1064
  {
693
1065
  appRoot,
694
1066
  sourceFolder,
@@ -704,19 +1076,23 @@ var apiRouteResolverFactory = (pluginOptions) => {
704
1076
  typeDeclarations,
705
1077
  paramsRefinements,
706
1078
  methods,
707
- payloadTypes,
708
- responseTypes,
1079
+ validationDefinitions: validationDefinitions2,
709
1080
  referencedFiles = []
710
1081
  } = await resolveRouteSignature(
711
- { importName, fileFullpath, optionalParams },
1082
+ { id, name, fileFullpath, optionalParams },
712
1083
  {
713
1084
  withReferencedFiles: true,
714
1085
  sourceFile: getSourceFile(fileFullpath),
715
1086
  relpathResolver(path) {
716
- return join2(sourceFolder, defaults2.apiDir, dirname2(file), path);
1087
+ return join3(sourceFolder, defaults.apiDir, dirname3(file), path);
717
1088
  }
718
1089
  }
719
1090
  );
1091
+ const validationTypes = validationDefinitions2.flatMap((def) => {
1092
+ return def.target === "response" ? def.variants.flatMap(({ id: id2, body }) => {
1093
+ return body ? [{ id: id2, text: body }] : [];
1094
+ }) : [def.schema];
1095
+ });
720
1096
  const numericParams = paramsRefinements ? paramsRefinements.flatMap(({ text, index }) => {
721
1097
  if (text === "number") {
722
1098
  const param = paramsSchema.at(index);
@@ -724,13 +1100,12 @@ var apiRouteResolverFactory = (pluginOptions) => {
724
1100
  }
725
1101
  return [];
726
1102
  }) : [];
727
- const typesFile = pathResolver2({ appRoot, sourceFolder }).resolve(
728
- "apiLibDir",
729
- dirname2(file),
730
- "types.ts"
731
- );
1103
+ const typesFile = pathResolver({
1104
+ appRoot,
1105
+ sourceFolder
1106
+ }).createPath.libApi(dirname3(file), "types.ts");
732
1107
  const params = {
733
- id: ["ParamsT", crc3(name)].join(""),
1108
+ id: ["ParamsT", crc5(name)].join(""),
734
1109
  schema: paramsSchema,
735
1110
  resolvedType: void 0
736
1111
  };
@@ -739,25 +1114,21 @@ var apiRouteResolverFactory = (pluginOptions) => {
739
1114
  paramsSchema: paramsSchema.map((param, index) => {
740
1115
  return {
741
1116
  ...param,
1117
+ isRequired: param.kind === "required",
1118
+ isSplat: param.kind === "splat",
742
1119
  refinement: paramsRefinements?.at(index)
743
1120
  };
744
1121
  }),
745
1122
  typeDeclarations,
746
- payloadTypes,
747
- responseTypes
1123
+ validationTypes
748
1124
  });
749
1125
  const resolvedTypes = resolveTypes ? literalTypesResolver(typesFileContent, {
750
- overrides: [...payloadTypes, ...responseTypes].reduce(
751
- (map, { id, skipValidation }) => {
752
- if (skipValidation) {
753
- map[id] = "never";
754
- }
755
- return map;
756
- },
757
- { [refineTypeName]: refineTypeName }
758
- ),
759
- withProperties: [params.id, ...payloadTypes.map((e) => e.id)],
760
- formatters
1126
+ stripComments: true,
1127
+ overrides: { [refineTypeName]: refineTypeName },
1128
+ withProperties: [
1129
+ params.id,
1130
+ ...validationTypes.flatMap(({ id: id2 }) => id2)
1131
+ ]
761
1132
  }) : void 0;
762
1133
  await renderToFile(
763
1134
  typesFile,
@@ -770,59 +1141,205 @@ var apiRouteResolverFactory = (pluginOptions) => {
770
1141
  methods,
771
1142
  typeDeclarations,
772
1143
  numericParams,
773
- // text was needed at writing types.ts file, dropping from cache
774
- payloadTypes: payloadTypes.map(({ text, ...rest }) => {
775
- return {
776
- ...rest,
777
- resolvedType: resolvedTypes?.find((e) => e.name === rest.id)
778
- };
779
- }),
780
- responseTypes: responseTypes.map(({ text, ...rest }) => {
1144
+ referencedFiles,
1145
+ validationDefinitions: validationDefinitions2.map((def) => {
781
1146
  return {
782
- ...rest,
783
- resolvedType: resolvedTypes?.find((e) => e.name === rest.id)
1147
+ ...def,
1148
+ ...def.target === "response" ? {
1149
+ variants: def.variants.map((variant) => {
1150
+ return {
1151
+ ...variant,
1152
+ resolvedType: resolvedTypes?.find(
1153
+ (e) => e.name === variant.id
1154
+ )
1155
+ };
1156
+ })
1157
+ } : {
1158
+ schema: {
1159
+ ...def.schema,
1160
+ resolvedType: resolvedTypes?.find(
1161
+ (e) => e.name === def.schema.id
1162
+ )
1163
+ }
1164
+ }
784
1165
  };
785
- }),
786
- referencedFiles
1166
+ })
787
1167
  });
788
1168
  }
789
- const entry2 = {
1169
+ const validationDefinitions = cache.validationDefinitions.flatMap(
1170
+ (def) => {
1171
+ let augmentedDef = def;
1172
+ if (def.target === "response") {
1173
+ augmentedDef = {
1174
+ ...def,
1175
+ variants: def.variants.flatMap((variant, i) => {
1176
+ if (typeof variant.contentType !== "string") {
1177
+ return [variant];
1178
+ }
1179
+ if (variant.contentType.includes("/")) {
1180
+ return [variant];
1181
+ }
1182
+ const contentType = mimeTypes.lookup(variant.contentType);
1183
+ if (contentType === false) {
1184
+ console.warn(
1185
+ styleText2(
1186
+ ["bold", "red"],
1187
+ "\u2717 Failed resolving Response Content Type"
1188
+ )
1189
+ );
1190
+ console.warn(
1191
+ ` Invalid value provided for mime-types lookup - ${variant.contentType}`
1192
+ );
1193
+ console.warn(
1194
+ styleText2(
1195
+ ["cyan"],
1196
+ ` Response variant #${i} excluded from route schemas`
1197
+ )
1198
+ );
1199
+ console.warn(` Route: ${name}; Method: ${def.method}`);
1200
+ console.warn();
1201
+ return [];
1202
+ }
1203
+ return [{ ...variant, contentType }];
1204
+ })
1205
+ };
1206
+ } else if (def.contentType && !def.contentType.includes("/")) {
1207
+ const contentType = mimeTypes.lookup(def.contentType);
1208
+ if (contentType === false) {
1209
+ console.warn(
1210
+ styleText2(
1211
+ ["bold", "red"],
1212
+ "\u2717 Failed resolving Response Content Type"
1213
+ )
1214
+ );
1215
+ console.warn(
1216
+ ` Invalid value provided for mime-types lookup - ${def.contentType}`
1217
+ );
1218
+ console.warn(` Route: ${name}; Method: ${def.method}`);
1219
+ console.warn();
1220
+ } else {
1221
+ augmentedDef = { ...def, contentType };
1222
+ }
1223
+ }
1224
+ return augmentedDef ? [augmentedDef] : [];
1225
+ }
1226
+ );
1227
+ const entry = {
1228
+ id,
790
1229
  name,
791
1230
  pathTokens,
1231
+ pathPattern,
792
1232
  params: cache.params,
793
1233
  numericParams: cache.numericParams,
794
1234
  optionalParams,
795
- importName,
796
- importFile,
797
1235
  folder,
798
1236
  file,
799
1237
  fileFullpath,
800
1238
  methods: cache.methods,
801
1239
  typeDeclarations: cache.typeDeclarations,
802
- payloadTypes: cache.payloadTypes,
803
- responseTypes: cache.responseTypes,
1240
+ validationDefinitions,
804
1241
  referencedFiles: Object.keys(cache.referencedFiles).map(
805
1242
  // expand referenced files path,
806
1243
  // they are stored as relative in cache
807
- (e) => resolve4(appRoot, e)
1244
+ (e) => resolve3(appRoot, e)
808
1245
  )
809
1246
  };
810
1247
  return {
811
1248
  kind: "apiRoute",
812
- entry: entry2
1249
+ entry
813
1250
  };
814
1251
  };
815
1252
  return { name, handler };
816
1253
  };
817
1254
  };
818
1255
 
819
- // src/base-plugin/routes/nesting.ts
820
- import {
821
- sortRoutes
822
- } from "@kosmojs/devlib";
1256
+ // src/routes-factory/nesting.ts
1257
+ import { basename } from "node:path";
1258
+ var nestedRoutesFactory = (routeEntries) => {
1259
+ const entryStack = structuredClone(routeEntries).sort(sortRoutes);
1260
+ const transformEntries = (entries, parent) => {
1261
+ return [...new Set(entries.map((e) => e.name))].flatMap((name) => {
1262
+ const nameEntries = entryStack.flatMap(({ fileFullpath, ...entry }) => {
1263
+ return entry.name === name ? [entry] : [];
1264
+ });
1265
+ const index = nameEntries.find(
1266
+ (e) => basename(e.file).startsWith(PAGE_INDEX_BASENAME)
1267
+ );
1268
+ const layout = nameEntries.find(
1269
+ (e) => basename(e.file).startsWith(PAGE_LAYOUT_BASENAME)
1270
+ );
1271
+ if (index || layout) {
1272
+ return [
1273
+ {
1274
+ index: index ? {
1275
+ ...index,
1276
+ pathTokens: index.pathTokens.slice(
1277
+ parent?.pathTokens.length || 0
1278
+ )
1279
+ } : void 0,
1280
+ layout: layout ? {
1281
+ ...layout,
1282
+ pathTokens: layout.pathTokens.slice(
1283
+ parent?.pathTokens.length || 0
1284
+ )
1285
+ } : void 0,
1286
+ parent: parent?.name,
1287
+ children: transformEntries(
1288
+ findDescendantEntries(index || layout),
1289
+ index || layout
1290
+ )
1291
+ }
1292
+ ];
1293
+ }
1294
+ return [];
1295
+ });
1296
+ };
1297
+ const findDescendantEntries = ({
1298
+ name,
1299
+ pathTokens
1300
+ }) => {
1301
+ const potentialChildren = entryStack.filter((entry) => {
1302
+ if (entry.pathTokens.length <= pathTokens.length) {
1303
+ return false;
1304
+ }
1305
+ if (!entry.name.startsWith(`${name}/`)) {
1306
+ return false;
1307
+ }
1308
+ return true;
1309
+ });
1310
+ return potentialChildren.filter((child) => {
1311
+ const hasIntermediateRoute = potentialChildren.some((intermediate) => {
1312
+ if (intermediate === child) {
1313
+ return false;
1314
+ }
1315
+ if (intermediate.pathTokens.length <= pathTokens.length) {
1316
+ return false;
1317
+ }
1318
+ if (intermediate.pathTokens.length >= child.pathTokens.length) {
1319
+ return false;
1320
+ }
1321
+ return child.name.startsWith(`${intermediate.name}/`);
1322
+ });
1323
+ return !hasIntermediateRoute;
1324
+ });
1325
+ };
1326
+ const rootEntries = entryStack.filter((entry) => {
1327
+ const hasParent = entryStack.some((potential) => {
1328
+ if (potential === entry) {
1329
+ return false;
1330
+ }
1331
+ if (potential.pathTokens.length >= entry.pathTokens.length) {
1332
+ return false;
1333
+ }
1334
+ return entry.name.startsWith(`${potential.name}/`);
1335
+ });
1336
+ return !hasParent;
1337
+ });
1338
+ return transformEntries(rootEntries);
1339
+ };
823
1340
 
824
- // src/base-plugin/routes.ts
825
- var routes_default = async (pluginOptions) => {
1341
+ // src/routes-factory/index.ts
1342
+ var routesFactory = async (pluginOptions) => {
826
1343
  const { appRoot, sourceFolder } = pluginOptions;
827
1344
  const apiRouteResolver = apiRouteResolverFactory(pluginOptions);
828
1345
  const apiUseResolver = apiUseResolverFactory(pluginOptions);
@@ -835,13 +1352,13 @@ var routes_default = async (pluginOptions) => {
835
1352
  return entry ? [entry] : [];
836
1353
  });
837
1354
  for (const entry of entries) {
838
- if (entry.folder === defaults3.apiDir) {
1355
+ if (entry.folder === defaults.apiDir) {
839
1356
  if (isApiRoute(entry.file)) {
840
1357
  resolvers.set(entry.fileFullpath, apiRouteResolver(entry));
841
1358
  } else if (isApiUse(entry.file)) {
842
1359
  resolvers.set(entry.fileFullpath, apiUseResolver(entry));
843
1360
  }
844
- } else if (entry.folder === defaults3.pagesDir) {
1361
+ } else if (entry.folder === defaults.pagesDir) {
845
1362
  if (isPageRoute(entry.file)) {
846
1363
  resolvers.set(entry.fileFullpath, pageRouteResolver(entry));
847
1364
  } else if (isPageLayout(entry.file)) {
@@ -858,10 +1375,85 @@ var routes_default = async (pluginOptions) => {
858
1375
  };
859
1376
  };
860
1377
 
1378
+ // src/base-plugin/api-handler.ts
1379
+ import { join as join4, resolve as resolve4 } from "node:path";
1380
+ import { styleText as styleText3 } from "node:util";
1381
+ import { context } from "esbuild";
1382
+ var api_handler_default = async (options) => {
1383
+ const { appRoot, sourceFolder, baseurl, apiurl } = options;
1384
+ const { createPath } = pathResolver({ appRoot, sourceFolder });
1385
+ const outDir = join4(options.outDir, defaults.apiDir);
1386
+ const esbuildOptions = await import(resolve4(appRoot, "esbuild.json"), { with: { type: "json" } }).then((e) => e.default);
1387
+ let devSetup;
1388
+ const watcher = async () => {
1389
+ const rebuildPlugin = {
1390
+ name: "rebuild",
1391
+ setup(build) {
1392
+ build.onEnd(async () => {
1393
+ if (devSetup) {
1394
+ await devSetup.teardownHandler?.();
1395
+ }
1396
+ try {
1397
+ await import(`${outDir}/dev.js?${Date.now()}`).then((e) => {
1398
+ devSetup = e.default;
1399
+ });
1400
+ console.debug(`${styleText3("green", "\u279C")} Api handler ready`);
1401
+ } catch (error) {
1402
+ console.error(`${styleText3("red", "\u2717")} Api handler error`);
1403
+ console.error(error);
1404
+ }
1405
+ });
1406
+ }
1407
+ };
1408
+ const ctx = await context({
1409
+ ...esbuildOptions,
1410
+ define: {
1411
+ ...esbuildOptions.define,
1412
+ PRODUCTION_BUILD: "false"
1413
+ },
1414
+ logLevel: "error",
1415
+ bundle: true,
1416
+ entryPoints: [createPath.api("dev.ts")],
1417
+ plugins: [rebuildPlugin],
1418
+ outdir: outDir
1419
+ });
1420
+ return {
1421
+ async start() {
1422
+ await ctx.watch({
1423
+ // waits this many milliseconds before rebuilding after a change is detected
1424
+ delay: options.watcher.delay
1425
+ });
1426
+ },
1427
+ async stop() {
1428
+ await ctx.dispose();
1429
+ }
1430
+ };
1431
+ };
1432
+ const devMiddleware = async (req, res, next) => {
1433
+ const {
1434
+ requestMatcher = () => {
1435
+ return new RegExp(`^${join4(baseurl, apiurl)}($|/)`).test(
1436
+ req.url
1437
+ );
1438
+ },
1439
+ requestHandler
1440
+ } = { ...devSetup };
1441
+ if (!requestMatcher(req)) {
1442
+ return next();
1443
+ }
1444
+ const handler = requestHandler?.();
1445
+ return handler ? handler(req, res) : next();
1446
+ };
1447
+ return {
1448
+ watcher,
1449
+ devMiddleware
1450
+ };
1451
+ };
1452
+
861
1453
  // src/base-plugin/spinner.ts
862
- import ora from "ora";
1454
+ import { Spinner } from "@topcli/spinner";
863
1455
  var spinnerFactory = (startText) => {
864
- const spinner = ora().start(startText);
1456
+ const spinner = new Spinner().start(startText);
865
1457
  let _text = startText;
866
1458
  return {
867
1459
  text(text) {
@@ -883,7 +1475,7 @@ var spinnerFactory = (startText) => {
883
1475
  if (text) {
884
1476
  this.text([_text, text].join("\n"));
885
1477
  }
886
- spinner.fail();
1478
+ spinner.failed();
887
1479
  }
888
1480
  };
889
1481
  };
@@ -894,19 +1486,13 @@ var base_plugin_default = (apiurl, pluginOptions) => {
894
1486
  const outDirSuffix = "client";
895
1487
  const store = {};
896
1488
  const createWorker = () => {
897
- const {
898
- generators = [],
899
- formatters = [],
900
- ...restOptions
901
- } = store.resolvedOptions;
1489
+ const { generators = [], ...restOptions } = store.resolvedOptions;
902
1490
  const generatorModules = generators.map(
903
1491
  (e) => [e.moduleImport, e.moduleConfig]
904
1492
  );
905
- const formatterModules = pluginOptions?.formatters ? pluginOptions.formatters.map((e) => [e.moduleImport, e.moduleConfig]) : [];
906
1493
  const workerData = {
907
1494
  ...restOptions,
908
- generatorModules,
909
- formatterModules
1495
+ generatorModules
910
1496
  };
911
1497
  return new Worker(resolve5(import.meta.dirname, "base-plugin/worker.js"), {
912
1498
  workerData,
@@ -945,10 +1531,10 @@ var base_plugin_default = (apiurl, pluginOptions) => {
945
1531
  const { error } = msg;
946
1532
  if (error.stack) {
947
1533
  const [message, ...stack] = error.stack.split("\n");
948
- console.error(styleText2("red", message));
1534
+ console.error(styleText4("red", message));
949
1535
  console.error(stack.join("\n"));
950
1536
  } else if (error?.message) {
951
- console.error(`${styleText2("red", error?.name)}: ${error.message}`);
1537
+ console.error(`${styleText4("red", error?.name)}: ${error.message}`);
952
1538
  } else {
953
1539
  console.error(error);
954
1540
  }
@@ -974,15 +1560,15 @@ var base_plugin_default = (apiurl, pluginOptions) => {
974
1560
  }
975
1561
  return {
976
1562
  build: {
977
- outDir: join3(config.build.outDir, outDirSuffix),
1563
+ outDir: join5(config.build.outDir, outDirSuffix),
978
1564
  manifest: true
979
1565
  }
980
1566
  };
981
1567
  },
982
1568
  async configResolved(_config) {
983
1569
  store.config = _config;
984
- const appRoot = resolve5(store.config.root, "..");
985
- const sourceFolder = basename(store.config.root);
1570
+ const appRoot = resolve5(store.config.root, "../..");
1571
+ const sourceFolder = basename2(store.config.root);
986
1572
  const outDir = resolve5(appRoot, resolve5(store.config.build.outDir, ".."));
987
1573
  const { stabilityThreshold = 1e3 } = typeof store.config.server.watch?.awaitWriteFinish === "object" ? store.config.server.watch.awaitWriteFinish : {};
988
1574
  const watcher = {
@@ -990,14 +1576,12 @@ var base_plugin_default = (apiurl, pluginOptions) => {
990
1576
  ...store.config.server.watch ? { options: store.config.server.watch } : {}
991
1577
  };
992
1578
  {
993
- const {
994
- generators = [],
995
- formatters = [],
996
- refineTypeName = "TRefine"
997
- } = { ...pluginOptions };
998
- const _apiGenerator = generators.find((e) => e.kind === "api");
999
- const _fetchGenerator = generators.find((e) => e.kind === "fetch");
1000
- const _ssrGenerator = generators.find((e) => e.kind === "ssr");
1579
+ const { generators = [], refineTypeName = "TRefine" } = {
1580
+ ...pluginOptions
1581
+ };
1582
+ const apiGenerator = generators.find((e) => e.slot === "api");
1583
+ const fetchGenerator = generators.find((e) => e.slot === "fetch");
1584
+ const ssrGenerator = generators.find((e) => e.slot === "ssr");
1001
1585
  store.resolvedOptions = {
1002
1586
  ...pluginOptions,
1003
1587
  command: store.config.command,
@@ -1006,17 +1590,16 @@ var base_plugin_default = (apiurl, pluginOptions) => {
1006
1590
  // 1. stub generator should run first
1007
1591
  stubGenerator(),
1008
1592
  // 2. then api generator
1009
- _apiGenerator || apiGenerator(),
1010
- // 3. then fetch generator
1011
- _fetchGenerator || fetchGenerator(),
1593
+ ...apiGenerator ? [apiGenerator] : [],
1594
+ // 3. then fetch generator, only if api generator also enabled
1595
+ ...fetchGenerator && apiGenerator ? [fetchGenerator] : [],
1012
1596
  // 4. user generators in the order they were added
1013
1597
  ...generators.filter((e) => {
1014
- return e.kind ? !["api", "fetch", "ssr"].includes(e.kind) : true;
1598
+ return e.slot ? !["api", "fetch", "ssr"].includes(e.slot) : true;
1015
1599
  }),
1016
1600
  // 5. ssr generator should run last
1017
- ..._ssrGenerator ? [_ssrGenerator] : []
1601
+ ...ssrGenerator ? [ssrGenerator] : []
1018
1602
  ],
1019
- formatters: formatters.map((e) => e.formatter),
1020
1603
  refineTypeName,
1021
1604
  baseurl: store.config.base,
1022
1605
  apiurl,
@@ -1025,8 +1608,54 @@ var base_plugin_default = (apiurl, pluginOptions) => {
1025
1608
  outDir
1026
1609
  };
1027
1610
  }
1611
+ const packageJsonFile = resolve5(appRoot, "package.json");
1612
+ const packageJson = await import(packageJsonFile, {
1613
+ with: { type: "json" }
1614
+ }).then((e) => e.default);
1615
+ const newDependencies = [];
1616
+ for (const generator of store.resolvedOptions.generators) {
1617
+ for (const key of ["dependencies", "devDependencies"]) {
1618
+ for (const [pkg, ver] of Object.entries(generator[key] || {})) {
1619
+ if (!packageJson[key]?.[pkg]) {
1620
+ newDependencies.push([key, pkg, ver]);
1621
+ }
1622
+ }
1623
+ }
1624
+ }
1625
+ if (newDependencies.length) {
1626
+ console.warn();
1627
+ console.warn(
1628
+ [
1629
+ "\u{1F4A1} ",
1630
+ styleText4(["bold", "italic", "red"], "New dependencies added: "),
1631
+ styleText4("dim", newDependencies.map(([, pkg]) => pkg).join(", "))
1632
+ ].join("")
1633
+ );
1634
+ console.warn(
1635
+ "\u{1F4E6}",
1636
+ [
1637
+ styleText4(
1638
+ ["bold", "blueBright"],
1639
+ store.config.command === "build" ? "Install them and run a new build: " : "Install them and restart dev server: "
1640
+ ),
1641
+ styleText4(
1642
+ "dim",
1643
+ ["npm", "pnpm", "yarn"].map((e) => `\`${e} install\``).join(" / ")
1644
+ )
1645
+ ].join("")
1646
+ );
1647
+ console.warn();
1648
+ for (const [key, pkg, ver] of newDependencies) {
1649
+ packageJson[key] = { ...packageJson[key], [pkg]: ver };
1650
+ }
1651
+ await writeFile3(
1652
+ packageJsonFile,
1653
+ JSON.stringify(packageJson, null, 2),
1654
+ "utf8"
1655
+ );
1656
+ }
1028
1657
  if (store.config.command === "build") {
1029
- const { resolvers } = await routes_default(store.resolvedOptions);
1658
+ const { resolvers } = await routesFactory(store.resolvedOptions);
1030
1659
  const resolvedEntries = [];
1031
1660
  {
1032
1661
  const spinner = spinnerFactory("Resolving Routes");
@@ -1042,8 +1671,8 @@ var base_plugin_default = (apiurl, pluginOptions) => {
1042
1671
  const spinner = spinnerFactory("Running Generators");
1043
1672
  for (const { name, factory } of store.resolvedOptions.generators) {
1044
1673
  spinner.append(name);
1045
- const { watchHandler } = await factory(store.resolvedOptions);
1046
- await watchHandler(resolvedEntries);
1674
+ const { build } = await factory(store.resolvedOptions);
1675
+ await build(resolvedEntries);
1047
1676
  }
1048
1677
  spinner.succeed();
1049
1678
  }
@@ -1053,6 +1682,10 @@ var base_plugin_default = (apiurl, pluginOptions) => {
1053
1682
  if (store.config.command !== "serve") {
1054
1683
  return;
1055
1684
  }
1685
+ if (!store.resolvedOptions.generators.find((e) => e.slot === "api")) {
1686
+ const stopWorker2 = workerHandler(() => stopWorker2());
1687
+ return;
1688
+ }
1056
1689
  const apiHandler = await api_handler_default(store.resolvedOptions);
1057
1690
  const apiWatcher = await apiHandler.watcher();
1058
1691
  const stopWorker = workerHandler(
@@ -1070,9 +1703,8 @@ var base_plugin_default = (apiurl, pluginOptions) => {
1070
1703
  };
1071
1704
 
1072
1705
  // src/define-plugin/index.ts
1073
- import { readFile as readFile2 } from "node:fs/promises";
1706
+ import { readFile as readFile3 } from "node:fs/promises";
1074
1707
  import { parse as dotenv } from "dotenv";
1075
- import { pathExists as pathExists2 } from "@kosmojs/devlib";
1076
1708
  var define_plugin_default = (entries) => {
1077
1709
  return {
1078
1710
  name: "@kosmojs:definePlugin",
@@ -1080,8 +1712,8 @@ var define_plugin_default = (entries) => {
1080
1712
  const define = {};
1081
1713
  for (const { keys, file, defineOn = "process.env", use } of entries) {
1082
1714
  define[defineOn] = {};
1083
- const fileExists = file ? await pathExists2(file) : false;
1084
- const env = fileExists ? dotenv(await readFile2(file, "utf8")) : process.env;
1715
+ const fileExists = file ? await pathExists(file) : false;
1716
+ const env = fileExists ? dotenv(await readFile3(file, "utf8")) : process.env;
1085
1717
  for (const [key, val] of Object.entries(env)) {
1086
1718
  if (keys.includes(key)) {
1087
1719
  define[`${defineOn}.${key}`] = JSON.stringify(val);
@@ -1093,11 +1725,57 @@ var define_plugin_default = (entries) => {
1093
1725
  }
1094
1726
  };
1095
1727
  };
1728
+
1729
+ // src/typebox.ts
1730
+ var typeboxLiteralText = (text, options) => {
1731
+ return [
1732
+ // Escape backticks for safe use in template literals
1733
+ [/(?<!\\)`/g, "\\`"],
1734
+ // Escape $ for safe use in template literals
1735
+ [/(?<!\\)\$\{/g, "\\${"],
1736
+ /**
1737
+ * TypeBox's built-in `Options` type is not configurable.
1738
+ * To allow a custom type name, exposing `refineTypeName` option,
1739
+ * defaulted to TRefine, then renaming it to `Options`.
1740
+ * */
1741
+ [new RegExp(`\\b${options.refineTypeName}\\s*<`, "g"), "Options<"]
1742
+ ].reduce((text2, [a, b]) => text2.replace(a, b), text);
1743
+ };
1096
1744
  export {
1745
+ API_INDEX_BASENAME,
1746
+ API_INDEX_PATTERN,
1747
+ API_USE_BASENAME,
1748
+ API_USE_PATTERN,
1749
+ PAGE_INDEX_BASENAME,
1750
+ PAGE_INDEX_PATTERN,
1751
+ PAGE_LAYOUT_BASENAME,
1752
+ PAGE_LAYOUT_PATTERN,
1097
1753
  alias_plugin_default as aliasPlugin,
1098
- default2 as apiGenerator,
1754
+ apiRouteResolverFactory,
1755
+ apiUseResolverFactory,
1756
+ createRouteEntry,
1757
+ createTsconfigPaths,
1099
1758
  base_plugin_default as default,
1759
+ defaults,
1100
1760
  define_plugin_default as definePlugin,
1101
- default3 as fetchGenerator
1761
+ isApiRoute,
1762
+ isApiUse,
1763
+ isPageLayout,
1764
+ isPageRoute,
1765
+ isRouteFile,
1766
+ nestedRoutesFactory,
1767
+ normalizeStaticValue,
1768
+ pageLayoutResolverFactory,
1769
+ pageRouteResolverFactory,
1770
+ pathResolver,
1771
+ pathTokensFactory,
1772
+ render,
1773
+ renderFactory,
1774
+ renderHelpers,
1775
+ renderToFile,
1776
+ routesFactory,
1777
+ scanRoutes,
1778
+ sortRoutes,
1779
+ typeboxLiteralText
1102
1780
  };
1103
1781
  //# sourceMappingURL=index.js.map