@c9up/ream 0.1.25 → 0.1.27

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 (302) hide show
  1. package/README.md +65 -0
  2. package/dist/ConfigLoader.d.ts +46 -5
  3. package/dist/ConfigLoader.d.ts.map +1 -1
  4. package/dist/ConfigLoader.js +60 -9
  5. package/dist/ConfigLoader.js.map +1 -1
  6. package/dist/HttpKernel.d.ts.map +1 -1
  7. package/dist/HttpKernel.js +46 -6
  8. package/dist/HttpKernel.js.map +1 -1
  9. package/dist/Ignitor.d.ts +115 -7
  10. package/dist/Ignitor.d.ts.map +1 -1
  11. package/dist/Ignitor.js +254 -40
  12. package/dist/Ignitor.js.map +1 -1
  13. package/dist/console/BaseCommand.d.ts +217 -0
  14. package/dist/console/BaseCommand.d.ts.map +1 -0
  15. package/dist/console/BaseCommand.js +340 -0
  16. package/dist/console/BaseCommand.js.map +1 -0
  17. package/dist/console/Console.d.ts +86 -0
  18. package/dist/console/Console.d.ts.map +1 -0
  19. package/dist/console/Console.js +134 -0
  20. package/dist/console/Console.js.map +1 -0
  21. package/dist/console/ExceptionHandler.d.ts +28 -0
  22. package/dist/console/ExceptionHandler.d.ts.map +1 -0
  23. package/dist/console/ExceptionHandler.js +76 -0
  24. package/dist/console/ExceptionHandler.js.map +1 -0
  25. package/dist/console/HelpCommand.d.ts +16 -0
  26. package/dist/console/HelpCommand.d.ts.map +1 -0
  27. package/dist/console/HelpCommand.js +40 -0
  28. package/dist/console/HelpCommand.js.map +1 -0
  29. package/dist/console/IndexGenerator.d.ts +24 -0
  30. package/dist/console/IndexGenerator.d.ts.map +1 -0
  31. package/dist/console/IndexGenerator.js +65 -0
  32. package/dist/console/IndexGenerator.js.map +1 -0
  33. package/dist/console/Kernel.d.ts +298 -0
  34. package/dist/console/Kernel.d.ts.map +1 -0
  35. package/dist/console/Kernel.js +1120 -0
  36. package/dist/console/Kernel.js.map +1 -0
  37. package/dist/console/ListCommand.d.ts +18 -0
  38. package/dist/console/ListCommand.d.ts.map +1 -0
  39. package/dist/console/ListCommand.js +60 -0
  40. package/dist/console/ListCommand.js.map +1 -0
  41. package/dist/console/assertions.d.ts +20 -0
  42. package/dist/console/assertions.d.ts.map +1 -0
  43. package/dist/console/assertions.js +84 -0
  44. package/dist/console/assertions.js.map +1 -0
  45. package/dist/console/cliui.d.ts +248 -0
  46. package/dist/console/cliui.d.ts.map +1 -0
  47. package/dist/console/cliui.js +587 -0
  48. package/dist/console/cliui.js.map +1 -0
  49. package/dist/console/decorators.d.ts +90 -0
  50. package/dist/console/decorators.d.ts.map +1 -0
  51. package/dist/console/decorators.js +142 -0
  52. package/dist/console/decorators.js.map +1 -0
  53. package/dist/console/index.d.ts +32 -0
  54. package/dist/console/index.d.ts.map +1 -0
  55. package/dist/console/index.js +24 -0
  56. package/dist/console/index.js.map +1 -0
  57. package/dist/console/loaders.d.ts +52 -0
  58. package/dist/console/loaders.d.ts.map +1 -0
  59. package/dist/console/loaders.js +131 -0
  60. package/dist/console/loaders.js.map +1 -0
  61. package/dist/console/parser.d.ts +86 -0
  62. package/dist/console/parser.d.ts.map +1 -0
  63. package/dist/console/parser.js +440 -0
  64. package/dist/console/parser.js.map +1 -0
  65. package/dist/console/prompts.d.ts +124 -0
  66. package/dist/console/prompts.d.ts.map +1 -0
  67. package/dist/console/prompts.js +356 -0
  68. package/dist/console/prompts.js.map +1 -0
  69. package/dist/console/tracing.d.ts +17 -0
  70. package/dist/console/tracing.d.ts.map +1 -0
  71. package/dist/console/tracing.js +11 -0
  72. package/dist/console/tracing.js.map +1 -0
  73. package/dist/console/types.d.ts +255 -0
  74. package/dist/console/types.d.ts.map +1 -0
  75. package/dist/console/types.js +25 -0
  76. package/dist/console/types.js.map +1 -0
  77. package/dist/console/ui.d.ts +20 -0
  78. package/dist/console/ui.d.ts.map +1 -0
  79. package/dist/console/ui.js +31 -0
  80. package/dist/console/ui.js.map +1 -0
  81. package/dist/console/utils.d.ts +27 -0
  82. package/dist/console/utils.d.ts.map +1 -0
  83. package/dist/console/utils.js +56 -0
  84. package/dist/console/utils.js.map +1 -0
  85. package/dist/container/Container.d.ts +23 -3
  86. package/dist/container/Container.d.ts.map +1 -1
  87. package/dist/container/Container.js +177 -70
  88. package/dist/container/Container.js.map +1 -1
  89. package/dist/container/types.d.ts +14 -2
  90. package/dist/container/types.d.ts.map +1 -1
  91. package/dist/decorators/Service.d.ts +7 -0
  92. package/dist/decorators/Service.d.ts.map +1 -1
  93. package/dist/decorators/Service.js +23 -3
  94. package/dist/decorators/Service.js.map +1 -1
  95. package/dist/env/Env.d.ts +29 -4
  96. package/dist/env/Env.d.ts.map +1 -1
  97. package/dist/env/Env.js +27 -4
  98. package/dist/env/Env.js.map +1 -1
  99. package/dist/env/Secret.d.ts +11 -0
  100. package/dist/env/Secret.d.ts.map +1 -0
  101. package/dist/env/Secret.js +36 -0
  102. package/dist/env/Secret.js.map +1 -0
  103. package/dist/env/interpolate.d.ts +26 -0
  104. package/dist/env/interpolate.d.ts.map +1 -0
  105. package/dist/env/interpolate.js +48 -0
  106. package/dist/env/interpolate.js.map +1 -0
  107. package/dist/env/loadEnvFiles.d.ts.map +1 -1
  108. package/dist/env/loadEnvFiles.js +14 -4
  109. package/dist/env/loadEnvFiles.js.map +1 -1
  110. package/dist/env/schema.d.ts +23 -6
  111. package/dist/env/schema.d.ts.map +1 -1
  112. package/dist/env/schema.js +67 -33
  113. package/dist/env/schema.js.map +1 -1
  114. package/dist/errors/ReamError.d.ts +25 -0
  115. package/dist/errors/ReamError.d.ts.map +1 -1
  116. package/dist/errors/ReamError.js +5 -0
  117. package/dist/errors/ReamError.js.map +1 -1
  118. package/dist/errors/prettyPrintError.d.ts +9 -0
  119. package/dist/errors/prettyPrintError.d.ts.map +1 -0
  120. package/dist/errors/prettyPrintError.js +22 -0
  121. package/dist/errors/prettyPrintError.js.map +1 -0
  122. package/dist/events/Emitter.d.ts +1 -1
  123. package/dist/events/Emitter.d.ts.map +1 -1
  124. package/dist/events/Emitter.js +1 -1
  125. package/dist/events/Emitter.js.map +1 -1
  126. package/dist/events/EventsProvider.d.ts +2 -2
  127. package/dist/events/EventsProvider.d.ts.map +1 -1
  128. package/dist/events/EventsProvider.js +3 -3
  129. package/dist/events/EventsProvider.js.map +1 -1
  130. package/dist/graphql/GraphQLEngine.d.ts +1 -1
  131. package/dist/graphql/GraphQLEngine.d.ts.map +1 -1
  132. package/dist/graphql/GraphQLEngine.js +2 -2
  133. package/dist/graphql/GraphQLEngine.js.map +1 -1
  134. package/dist/graphql/GraphQLProvider.d.ts.map +1 -1
  135. package/dist/graphql/GraphQLProvider.js +7 -3
  136. package/dist/graphql/GraphQLProvider.js.map +1 -1
  137. package/dist/http/Exception.d.ts +63 -1
  138. package/dist/http/Exception.d.ts.map +1 -1
  139. package/dist/http/Exception.js +185 -48
  140. package/dist/http/Exception.js.map +1 -1
  141. package/dist/http/HttpContext.d.ts +53 -7
  142. package/dist/http/HttpContext.d.ts.map +1 -1
  143. package/dist/http/HttpContext.js +56 -15
  144. package/dist/http/HttpContext.js.map +1 -1
  145. package/dist/http/RedirectBuilder.d.ts.map +1 -1
  146. package/dist/http/RedirectBuilder.js +4 -1
  147. package/dist/http/RedirectBuilder.js.map +1 -1
  148. package/dist/http/Request.d.ts +141 -10
  149. package/dist/http/Request.d.ts.map +1 -1
  150. package/dist/http/Request.js +244 -34
  151. package/dist/http/Request.js.map +1 -1
  152. package/dist/http/Response.d.ts +176 -9
  153. package/dist/http/Response.d.ts.map +1 -1
  154. package/dist/http/Response.js +404 -16
  155. package/dist/http/Response.js.map +1 -1
  156. package/dist/index.d.ts +19 -8
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +20 -5
  159. package/dist/index.js.map +1 -1
  160. package/dist/middleware/Pipeline.d.ts +29 -8
  161. package/dist/middleware/Pipeline.d.ts.map +1 -1
  162. package/dist/middleware/Pipeline.js +40 -2
  163. package/dist/middleware/Pipeline.js.map +1 -1
  164. package/dist/openapi/OpenApiProvider.js +4 -4
  165. package/dist/openapi/OpenApiProvider.js.map +1 -1
  166. package/dist/router/Router.d.ts +223 -31
  167. package/dist/router/Router.d.ts.map +1 -1
  168. package/dist/router/Router.js +520 -102
  169. package/dist/router/Router.js.map +1 -1
  170. package/dist/rpc/RpcProvider.d.ts +1 -0
  171. package/dist/rpc/RpcProvider.d.ts.map +1 -1
  172. package/dist/rpc/RpcProvider.js +7 -3
  173. package/dist/rpc/RpcProvider.js.map +1 -1
  174. package/dist/rpc/RpcRouter.d.ts +2 -2
  175. package/dist/rpc/RpcRouter.d.ts.map +1 -1
  176. package/dist/rpc/RpcRouter.js +17 -5
  177. package/dist/rpc/RpcRouter.js.map +1 -1
  178. package/dist/scheduler/ScheduleProvider.d.ts.map +1 -1
  179. package/dist/scheduler/ScheduleProvider.js +7 -15
  180. package/dist/scheduler/ScheduleProvider.js.map +1 -1
  181. package/dist/server/Server.d.ts +17 -4
  182. package/dist/server/Server.d.ts.map +1 -1
  183. package/dist/server/Server.js +41 -7
  184. package/dist/server/Server.js.map +1 -1
  185. package/dist/services/app.d.ts +1 -1
  186. package/dist/services/app.js +1 -1
  187. package/dist/services/console.d.ts +21 -0
  188. package/dist/services/console.d.ts.map +1 -0
  189. package/dist/services/console.js +35 -0
  190. package/dist/services/console.js.map +1 -0
  191. package/dist/session/SessionMiddleware.js +3 -3
  192. package/dist/session/SessionMiddleware.js.map +1 -1
  193. package/dist/storage/StaticProvider.js +1 -1
  194. package/dist/storage/StaticProvider.js.map +1 -1
  195. package/dist/testing/ApiResponse.d.ts +218 -0
  196. package/dist/testing/ApiResponse.d.ts.map +1 -0
  197. package/dist/testing/ApiResponse.js +576 -0
  198. package/dist/testing/ApiResponse.js.map +1 -0
  199. package/dist/testing/RequestBuilder.d.ts +155 -35
  200. package/dist/testing/RequestBuilder.d.ts.map +1 -1
  201. package/dist/testing/RequestBuilder.js +455 -109
  202. package/dist/testing/RequestBuilder.js.map +1 -1
  203. package/dist/testing/TestClient.d.ts +53 -37
  204. package/dist/testing/TestClient.d.ts.map +1 -1
  205. package/dist/testing/TestClient.js +84 -52
  206. package/dist/testing/TestClient.js.map +1 -1
  207. package/dist/utils/Macroable.d.ts +30 -0
  208. package/dist/utils/Macroable.d.ts.map +1 -0
  209. package/dist/utils/Macroable.js +43 -0
  210. package/dist/utils/Macroable.js.map +1 -0
  211. package/dist/utils/inflect.d.ts +17 -0
  212. package/dist/utils/inflect.d.ts.map +1 -0
  213. package/dist/utils/inflect.js +32 -0
  214. package/dist/utils/inflect.js.map +1 -0
  215. package/dist/utils/objectPath.d.ts +57 -0
  216. package/dist/utils/objectPath.d.ts.map +1 -0
  217. package/dist/utils/objectPath.js +141 -0
  218. package/dist/utils/objectPath.js.map +1 -0
  219. package/events.darwin-arm64.node +0 -0
  220. package/events.darwin-x64.node +0 -0
  221. package/events.linux-arm64-gnu.node +0 -0
  222. package/events.linux-x64-gnu.node +0 -0
  223. package/events.win32-x64-msvc.node +0 -0
  224. package/index.darwin-arm64.node +0 -0
  225. package/index.darwin-x64.node +0 -0
  226. package/index.linux-arm64-gnu.node +0 -0
  227. package/index.linux-x64-gnu.node +0 -0
  228. package/index.win32-x64-msvc.node +0 -0
  229. package/package.json +20 -2
  230. package/scheduler.darwin-arm64.node +0 -0
  231. package/scheduler.darwin-x64.node +0 -0
  232. package/scheduler.linux-arm64-gnu.node +0 -0
  233. package/scheduler.linux-x64-gnu.node +0 -0
  234. package/scheduler.win32-x64-msvc.node +0 -0
  235. package/src/ConfigLoader.ts +72 -9
  236. package/src/HttpKernel.ts +53 -15
  237. package/src/Ignitor.ts +364 -45
  238. package/src/console/BaseCommand.ts +467 -0
  239. package/src/console/Console.ts +174 -0
  240. package/src/console/ExceptionHandler.ts +91 -0
  241. package/src/console/HelpCommand.ts +37 -0
  242. package/src/console/IndexGenerator.ts +79 -0
  243. package/src/console/Kernel.ts +1382 -0
  244. package/src/console/ListCommand.ts +55 -0
  245. package/src/console/assertions.ts +114 -0
  246. package/src/console/cliui.ts +756 -0
  247. package/src/console/decorators.ts +199 -0
  248. package/src/console/index.ts +67 -0
  249. package/src/console/loaders.ts +145 -0
  250. package/src/console/parser.ts +598 -0
  251. package/src/console/prompts.ts +548 -0
  252. package/src/console/tracing.ts +22 -0
  253. package/src/console/types.ts +280 -0
  254. package/src/console/ui.ts +37 -0
  255. package/src/console/utils.ts +69 -0
  256. package/src/container/Container.ts +198 -72
  257. package/src/container/types.ts +15 -2
  258. package/src/decorators/Service.ts +28 -3
  259. package/src/env/Env.ts +49 -5
  260. package/src/env/Secret.ts +41 -0
  261. package/src/env/interpolate.ts +54 -0
  262. package/src/env/loadEnvFiles.ts +12 -4
  263. package/src/env/schema.ts +109 -35
  264. package/src/errors/ReamError.ts +31 -0
  265. package/src/errors/prettyPrintError.ts +20 -0
  266. package/src/events/Emitter.ts +2 -2
  267. package/src/events/EventsProvider.ts +5 -5
  268. package/src/graphql/GraphQLEngine.ts +4 -4
  269. package/src/graphql/GraphQLProvider.ts +7 -3
  270. package/src/http/Exception.ts +214 -37
  271. package/src/http/HttpContext.ts +87 -18
  272. package/src/http/RedirectBuilder.ts +4 -1
  273. package/src/http/Request.ts +264 -34
  274. package/src/http/Response.ts +478 -27
  275. package/src/index.ts +41 -6
  276. package/src/middleware/Pipeline.ts +77 -6
  277. package/src/openapi/OpenApiProvider.ts +4 -4
  278. package/src/router/Router.ts +631 -113
  279. package/src/rpc/RpcProvider.ts +6 -2
  280. package/src/rpc/RpcRouter.ts +22 -7
  281. package/src/scheduler/ScheduleProvider.ts +7 -15
  282. package/src/server/Server.ts +49 -10
  283. package/src/services/app.ts +1 -1
  284. package/src/services/console.ts +42 -0
  285. package/src/session/SessionMiddleware.ts +4 -4
  286. package/src/storage/StaticProvider.ts +1 -1
  287. package/src/testing/ApiResponse.ts +691 -0
  288. package/src/testing/RequestBuilder.ts +577 -137
  289. package/src/testing/TestClient.ts +110 -77
  290. package/src/utils/Macroable.ts +54 -0
  291. package/src/utils/inflect.ts +29 -0
  292. package/src/utils/objectPath.ts +142 -0
  293. package/dist/console/CommandRunner.d.ts +0 -18
  294. package/dist/console/CommandRunner.d.ts.map +0 -1
  295. package/dist/console/CommandRunner.js +0 -65
  296. package/dist/console/CommandRunner.js.map +0 -1
  297. package/dist/decorators/Lazy.d.ts +0 -19
  298. package/dist/decorators/Lazy.d.ts.map +0 -1
  299. package/dist/decorators/Lazy.js +0 -53
  300. package/dist/decorators/Lazy.js.map +0 -1
  301. package/src/console/CommandRunner.ts +0 -82
  302. package/src/decorators/Lazy.ts +0 -57
@@ -23,46 +23,39 @@ import {
23
23
  type HttpMethod,
24
24
  type HttpSender,
25
25
  RequestBuilder,
26
+ type TestResponse,
26
27
  } from './RequestBuilder.js'
27
28
 
28
- export interface TestResponse {
29
- status: number
30
- headers: Dict
31
- body: string
32
- json<T = unknown>(): T
33
- }
29
+ // The raw response shape + the rich response live in ApiResponse.ts and are
30
+ // re-exported here so `@c9up/ream/testing` consumers keep their import path.
31
+ export { ApiResponse, type TestResponse } from './RequestBuilder.js'
34
32
 
35
33
  /**
36
- * Fluent request builder. Thenable like AdonisJS/japa's api-client: `await
37
- * client.get('/x')` sends the request, and chaining works on every verb —
38
- * `await client.get('/x').header('authorization', token)`. The explicit
39
- * `.send()` is kept for callers that prefer it.
34
+ * Named-route manifest — `name → path pattern`, e.g. `{ 'users.show':
35
+ * '/users/:id' }`. Feed `router.namedManifest()` here so `visit()` can resolve
36
+ * named routes without the full Router.
37
+ *
38
+ * A value may be a bare path string OR `{ path, method }` — when the method is
39
+ * present, `visit(name)` uses it (Japa route-registry parity, e.g.
40
+ * `visit('users.store').json(...).assertCreated()` issues a POST).
40
41
  */
41
- export interface TestRequestBuilder extends PromiseLike<TestResponse> {
42
- /** Set a request header. */
43
- header(name: string, value: string): TestRequestBuilder
44
- /** Set the request body as JSON. */
45
- json(data: unknown): TestRequestBuilder
46
- /** Set a raw string body with optional content type. */
47
- body(content: string, contentType?: string): TestRequestBuilder
48
- /** Set a cookie. */
49
- cookie(name: string, value: string): TestRequestBuilder
50
- /** Send the request and return the response. */
51
- send(): Promise<TestResponse>
52
- }
42
+ export type RouteEntry = string | { path: string; method?: HttpMethod }
43
+ export type RouteManifest = Record<string, RouteEntry>
53
44
 
54
45
  export class TestClient {
55
46
  #port = 0
56
47
  #headers: Dict = {}
57
48
  #bootFn: (port: number) => Promise<{ port: number; close: () => Promise<void> | void }>
58
49
  #auth: AuthStrategy | null
50
+ #routes: RouteManifest | null
59
51
 
60
52
  constructor(
61
53
  bootFn: (port: number) => Promise<{ port: number; close: () => Promise<void> | void }>,
62
- options: { auth?: AuthStrategy } = {},
54
+ options: { auth?: AuthStrategy; routes?: RouteManifest } = {},
63
55
  ) {
64
56
  this.#bootFn = bootFn
65
57
  this.#auth = options.auth ?? null
58
+ this.#routes = options.routes ?? null
66
59
  }
67
60
 
68
61
  #server: { port: number; close: () => Promise<void> | void } | null = null
@@ -97,29 +90,47 @@ export class TestClient {
97
90
  return this
98
91
  }
99
92
 
100
- /** GET request. Returns a chainable, awaitable builder (await sends it). */
101
- get(path: string): TestRequestBuilder {
102
- return this.request('GET', path)
93
+ // Verb shortcuts return the RICH builder — the full japa/api-client surface
94
+ // (assertOk/assertStatus/assertBody/… + auth/csrf) AND awaitable (`await
95
+ // client.get('/x')` sends and resolves to the response). One unified builder,
96
+ // no split between `get()` and `fluent()`.
97
+
98
+ /** GET request — rich, awaitable builder. */
99
+ get(path: string): RequestBuilder {
100
+ return this.fluent('GET', path)
101
+ }
102
+
103
+ /** POST request — rich, awaitable builder. */
104
+ post(path: string): RequestBuilder {
105
+ return this.fluent('POST', path)
103
106
  }
104
107
 
105
- /** POST request. */
106
- post(path: string): TestRequestBuilder {
107
- return this.request('POST', path)
108
+ /** PUT request — rich, awaitable builder. */
109
+ put(path: string): RequestBuilder {
110
+ return this.fluent('PUT', path)
108
111
  }
109
112
 
110
- /** PUT request. */
111
- put(path: string): TestRequestBuilder {
112
- return this.request('PUT', path)
113
+ /** PATCH request — rich, awaitable builder. */
114
+ patch(path: string): RequestBuilder {
115
+ return this.fluent('PATCH', path)
113
116
  }
114
117
 
115
- /** PATCH request. */
116
- patch(path: string): TestRequestBuilder {
117
- return this.request('PATCH', path)
118
+ /** DELETE request — rich, awaitable builder. */
119
+ delete(path: string): RequestBuilder {
120
+ return this.fluent('DELETE', path)
118
121
  }
119
122
 
120
- /** DELETE request. Returns a chainable, awaitable builder (await sends it). */
121
- delete(path: string): TestRequestBuilder {
122
- return this.request('DELETE', path)
123
+ /**
124
+ * HEAD request — japa/api-client's `.head()`. Returns the rich builder so you
125
+ * can assert status/headers (a HEAD response carries headers, no body).
126
+ */
127
+ head(path: string): RequestBuilder {
128
+ return this.fluent('HEAD', path)
129
+ }
130
+
131
+ /** OPTIONS request — japa/api-client's `.options()`. */
132
+ options(path: string): RequestBuilder {
133
+ return this.fluent('OPTIONS', path)
123
134
  }
124
135
 
125
136
  /**
@@ -136,49 +147,37 @@ export class TestClient {
136
147
  p,
137
148
  { ...this.#headers, ...init.headers },
138
149
  init.body.toString('utf8'),
150
+ init.timeoutMs,
139
151
  )
140
152
  return new RequestBuilder(sender, method, path, this.#auth)
141
153
  }
142
154
 
143
- /** Build a request with the low-level fluent API. */
144
- request(method: string, path: string): TestRequestBuilder {
145
- const headers: Dict = { ...this.#headers }
146
- let bodyContent = ''
147
- const cookies: string[] = []
148
-
149
- const builder: TestRequestBuilder = {
150
- header(name: string, value: string) {
151
- headers[name.toLowerCase()] = value
152
- return builder
153
- },
154
- json(data: unknown) {
155
- bodyContent = JSON.stringify(data)
156
- headers['content-type'] = 'application/json'
157
- return builder
158
- },
159
- body(content: string, contentType?: string) {
160
- bodyContent = content
161
- if (contentType) headers['content-type'] = contentType
162
- return builder
163
- },
164
- cookie(name: string, value: string) {
165
- cookies.push(`${name}=${value}`)
166
- return builder
167
- },
168
- send: async () => {
169
- if (cookies.length > 0) {
170
- headers.cookie = cookies.join('; ')
171
- }
172
- return sendRequest(this.#port, method, path, headers, bodyContent)
173
- },
174
- // Thenable: `await client.get('/x')` (or any builder) sends the request,
175
- // matching AdonisJS/japa's api-client ergonomics. The `then` IS the public
176
- // contract here — awaiting the builder is the documented way to send.
177
- // biome-ignore lint/suspicious/noThenProperty: thenable request builder is the intended API (AdonisJS/japa parity) — `await client.get(path)` sends
178
- then: (onfulfilled, onrejected) => builder.send().then(onfulfilled, onrejected),
155
+ /**
156
+ * GET a named route — japa/api-client's `.visit()`. Resolves `name` against
157
+ * the `routes` manifest (`router.namedManifest()`), fills `:param`
158
+ * placeholders, and returns the rich chainable builder. Throws a clear error
159
+ * when no manifest was configured or the name is unknown.
160
+ */
161
+ visit(name: string, params?: Record<string, string>): RequestBuilder {
162
+ if (!this.#routes) {
163
+ throw new Error(
164
+ 'TestClient: visit() needs a named-route manifest. Pass `routes: router.namedManifest()` in the client options.',
165
+ )
179
166
  }
167
+ const entry = this.#routes[name]
168
+ // Method comes from the registry when the entry carries one (Japa parity),
169
+ // otherwise GET.
170
+ const method: HttpMethod = typeof entry === 'object' && entry.method ? entry.method : 'GET'
171
+ return this.fluent(method, resolveNamedRoute(this.#routes, name, params))
172
+ }
180
173
 
181
- return builder
174
+ /**
175
+ * Generic request — japa/api-client's `client.request(url, method)` (note the
176
+ * arg order: URL first, method second, defaulting to GET). Returns the same
177
+ * rich, awaitable builder as the verb shortcuts.
178
+ */
179
+ request(path: string, method: HttpMethod = 'GET'): RequestBuilder {
180
+ return this.fluent(method, path)
182
181
  }
183
182
  }
184
183
 
@@ -198,9 +197,41 @@ export {
198
197
  type HttpMethod,
199
198
  type HttpSender,
200
199
  partialMatch,
200
+ type QueryParams,
201
201
  RequestBuilder,
202
202
  } from './RequestBuilder.js'
203
203
 
204
+ /**
205
+ * Resolve a named route against a manifest — the client-side twin of
206
+ * `Router.urlFor`. Fills `:param` placeholders (word-boundary safe), strips
207
+ * unprovided `:optional?` segments, and throws on an unknown name or a missing
208
+ * required param.
209
+ */
210
+ export function resolveNamedRoute(
211
+ manifest: RouteManifest,
212
+ name: string,
213
+ params?: Record<string, string>,
214
+ ): string {
215
+ const entry = manifest[name]
216
+ if (entry === undefined) {
217
+ const available = Object.keys(manifest).join(', ') || '(none)'
218
+ throw new Error(`Route '${name}' not found. Available: ${available}`)
219
+ }
220
+ let url = typeof entry === 'string' ? entry : entry.path
221
+ if (params) {
222
+ for (const [key, value] of Object.entries(params)) {
223
+ const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
224
+ url = url.replace(new RegExp(`:${escaped}\\??(?![\\w])`, 'g'), encodeURIComponent(value))
225
+ }
226
+ }
227
+ url = url.replace(/\/:[A-Za-z_][\w]*\?/g, '')
228
+ const missing = url.match(/:[A-Za-z_][\w]*/g)
229
+ if (missing && missing.length > 0) {
230
+ throw new Error(`Cannot generate URL for route '${name}': missing params ${missing.join(', ')}`)
231
+ }
232
+ return url
233
+ }
234
+
204
235
  /** Send an HTTP request using raw TCP (no external dependencies). */
205
236
  async function sendRequest(
206
237
  port: number,
@@ -208,6 +239,7 @@ async function sendRequest(
208
239
  path: string,
209
240
  headers: Dict,
210
241
  body: string,
242
+ timeoutMs?: number,
211
243
  ): Promise<TestResponse> {
212
244
  const { connect } = await import('node:net')
213
245
 
@@ -253,8 +285,9 @@ async function sendRequest(
253
285
  // tokio runtimes) the cumulative latency exceeded 5s ~30% of runs
254
286
  // and surfaced as flaky `bob accepts` / `non-member 403` failures
255
287
  // in kitchen-sink's workspace.test.ts. 30s matches the helix
256
- // `--timeout=60000` per-test budget without masking real hangs.
257
- socket.setTimeout(30_000, () => {
288
+ // `--timeout=60000` per-test budget without masking real hangs. A
289
+ // per-request `.timeout(ms)` (Japa) overrides the default.
290
+ socket.setTimeout(timeoutMs && timeoutMs > 0 ? timeoutMs : 30_000, () => {
258
291
  socket.destroy()
259
292
  reject(new Error('TestClient request timed out'))
260
293
  })
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Macroable — lets a package extend a class at runtime with extra methods
3
+ * (`macro`) and computed properties (`getter`), AdonisJS-style. A framework
4
+ * class (Request, Response, HttpContext, Router, …) extends this so an add-on
5
+ * can attach behaviour without a subclass:
6
+ *
7
+ * Request.macro('wantsJson', function () { return this.accepts(['json']) === 'json' })
8
+ * HttpContext.getter('tenant', function () { return resolveTenant(this) })
9
+ *
10
+ * TypeScript sees only the base class; consumers type their macros with
11
+ * declaration merging (`declare module`) exactly as in AdonisJS.
12
+ */
13
+
14
+ /**
15
+ * A macro method. When invoked, `this` is the instance it's attached to —
16
+ * declare it explicitly in your callback (`function (this: Request) { … }`) to
17
+ * type the receiver; the alias itself leaves `this` open so any class fits.
18
+ */
19
+ export type MacroFn = (...args: unknown[]) => unknown
20
+
21
+ /** A getter implementation — declare `this` in your callback to type the receiver. */
22
+ export type GetterFn = () => unknown
23
+
24
+ export class Macroable {
25
+ /** Attach a method to every instance of this class (AdonisJS `macro`). */
26
+ static macro(name: string, fn: MacroFn): void {
27
+ Object.defineProperty(Macroable.prototype, name, {
28
+ value: fn,
29
+ writable: true,
30
+ configurable: true,
31
+ })
32
+ }
33
+
34
+ /**
35
+ * Attach a computed property to every instance (AdonisJS `getter`). With
36
+ * `singleton`, the value is computed once per instance and cached.
37
+ */
38
+ static getter(name: string, fn: GetterFn, singleton = false): void {
39
+ if (!singleton) {
40
+ Object.defineProperty(Macroable.prototype, name, { get: fn, configurable: true })
41
+ return
42
+ }
43
+ const cacheKey = Symbol(name)
44
+ Object.defineProperty(Macroable.prototype, name, {
45
+ configurable: true,
46
+ get(this: Record<symbol, unknown>) {
47
+ if (!(cacheKey in this)) {
48
+ Object.defineProperty(this, cacheKey, { value: fn.call(this), configurable: true })
49
+ }
50
+ return this[cacheKey]
51
+ },
52
+ })
53
+ }
54
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Minimal zero-dependency string inflection — the two operations resourceful
3
+ * routing needs from AdonisJS's `@poppinss/utils/string`: `snakeCase` (route
4
+ * name normalisation) and `singular` (nested-resource param naming, `photos`
5
+ * → `photo_id`).
6
+ *
7
+ * Deviation from AdonisJS (named): `singular` covers regular English plurals
8
+ * only (`-ies→-y`, `-(s|x|z|ch|sh)es→drop es`, trailing `-s→drop`). Irregular
9
+ * plurals (`people→person`, `geese→goose`) are NOT handled — resource names in
10
+ * REST APIs are overwhelmingly regular, and a full inflector (irregular tables,
11
+ * uncountables) would be a dependency-sized rabbit hole for a niche case.
12
+ */
13
+
14
+ /** Convert `camelCase`/`PascalCase`/`kebab-case`/spaced text to `snake_case`. */
15
+ export function snakeCase(value: string): string {
16
+ return value
17
+ .replace(/([a-z0-9])([A-Z])/g, '$1_$2')
18
+ .replace(/[-\s]+/g, '_')
19
+ .toLowerCase()
20
+ }
21
+
22
+ /** Singularize a regular English plural (see module deviation note). */
23
+ export function singular(word: string): string {
24
+ if (word.length > 3 && /ies$/i.test(word)) return word.replace(/ies$/i, 'y')
25
+ if (/(?:s|x|z|ch|sh)es$/i.test(word)) return word.replace(/es$/i, '')
26
+ if (/ss$/i.test(word)) return word
27
+ if (/s$/i.test(word)) return word.replace(/s$/i, '')
28
+ return word
29
+ }
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Zero-dependency dot-path object access — the small subset of `lodash`
3
+ * (`get`/`set`/`has`/`pick`/`omit`/`merge`) that Config and Request need for
4
+ * AdonisJS-parity nested access (`config.get('database.mysql.host')`,
5
+ * `request.input('user.address.city')`).
6
+ *
7
+ * AdonisJS uses `@poppinss/utils/lodash` for this. Ream stays lean and
8
+ * dependency-free (same stance as aurora's hand-written `cn`/`clsx`), so we
9
+ * reimplement exactly the four semantics we rely on — no more.
10
+ *
11
+ * Path syntax: `a.b.c` and `a[0].c` / `a.0.c` (bracket + dotted indices) are
12
+ * both accepted, matching lodash's tokenizer for the shapes config/request
13
+ * data actually take (plain JSON objects and arrays).
14
+ */
15
+
16
+ /** A JSON-ish record we can index by string key. */
17
+ type UnknownRecord = Record<string, unknown>
18
+
19
+ /** True for plain objects and arrays (anything indexable by a path segment). */
20
+ function isIndexable(value: unknown): value is UnknownRecord {
21
+ return typeof value === 'object' && value !== null
22
+ }
23
+
24
+ /**
25
+ * Split a dot/bracket path into segments: `a.b[0].c` → `['a','b','0','c']`.
26
+ * A single already-simple key (no `.`/`[`) is returned as-is, so keys that
27
+ * happen to contain neither delimiter cost nothing.
28
+ */
29
+ function toSegments(path: string): string[] {
30
+ if (path.indexOf('.') === -1 && path.indexOf('[') === -1) return [path]
31
+ const segments: string[] = []
32
+ const matcher = /[^.[\]]+/g
33
+ let match: RegExpExecArray | null = matcher.exec(path)
34
+ while (match !== null) {
35
+ segments.push(match[0])
36
+ match = matcher.exec(path)
37
+ }
38
+ return segments
39
+ }
40
+
41
+ /**
42
+ * Read a nested value by dot-path. Returns `defaultValue` when any segment is
43
+ * missing or the resolved value is `undefined` (lodash.get semantics).
44
+ */
45
+ export function getPath(source: unknown, path: string, defaultValue?: unknown): unknown {
46
+ const segments = toSegments(path)
47
+ let current: unknown = source
48
+ for (const segment of segments) {
49
+ if (!isIndexable(current)) return defaultValue
50
+ current = current[segment]
51
+ }
52
+ return current === undefined ? defaultValue : current
53
+ }
54
+
55
+ /**
56
+ * True when the full dot-path exists on `source` (each segment is an own key),
57
+ * even if the leaf value is `undefined` (lodash.has semantics).
58
+ */
59
+ export function hasPath(source: unknown, path: string): boolean {
60
+ const segments = toSegments(path)
61
+ let current: unknown = source
62
+ for (const segment of segments) {
63
+ if (!isIndexable(current) || !Object.hasOwn(current, segment)) return false
64
+ current = current[segment]
65
+ }
66
+ return true
67
+ }
68
+
69
+ /**
70
+ * Set a nested value by dot-path, creating intermediate plain objects as
71
+ * needed, and return the (mutated) root.
72
+ *
73
+ * Deviation from lodash.set (named): integer segments create plain objects,
74
+ * NOT arrays. Config trees and request-data picks are keyed objects; ream
75
+ * never sets array indices through a path, so array auto-vivification would be
76
+ * dead behaviour.
77
+ */
78
+ export function setPath(target: UnknownRecord, path: string, value: unknown): UnknownRecord {
79
+ const segments = toSegments(path)
80
+ let current: UnknownRecord = target
81
+ for (let i = 0; i < segments.length - 1; i += 1) {
82
+ const key = segments[i]
83
+ const next = current[key]
84
+ if (!isIndexable(next)) {
85
+ const created: UnknownRecord = {}
86
+ current[key] = created
87
+ current = created
88
+ } else {
89
+ current = next
90
+ }
91
+ }
92
+ current[segments[segments.length - 1]] = value
93
+ return target
94
+ }
95
+
96
+ /**
97
+ * Return a new object containing only the given dot-paths (lodash.pick).
98
+ * Missing paths are skipped; nested paths reconstruct their branch, so
99
+ * `pickPaths({a:{b:1,c:2}}, ['a.b'])` yields `{a:{b:1}}`.
100
+ */
101
+ export function pickPaths(source: unknown, paths: string[]): UnknownRecord {
102
+ const result: UnknownRecord = {}
103
+ for (const path of paths) {
104
+ if (hasPath(source, path)) setPath(result, path, getPath(source, path))
105
+ }
106
+ return result
107
+ }
108
+
109
+ /**
110
+ * Return a deep clone of `source` with the given dot-paths removed
111
+ * (lodash.omit). Operates on JSON-ish data (body + query), so `structuredClone`
112
+ * is a safe deep copy that never mutates the caller's input.
113
+ */
114
+ export function omitPaths(source: UnknownRecord, paths: string[]): UnknownRecord {
115
+ const result = structuredClone(source)
116
+ for (const path of paths) {
117
+ const segments = toSegments(path)
118
+ let current: unknown = result
119
+ for (let i = 0; i < segments.length - 1; i += 1) {
120
+ if (!isIndexable(current)) break
121
+ current = current[segments[i]]
122
+ }
123
+ if (isIndexable(current)) delete current[segments[segments.length - 1]]
124
+ }
125
+ return result
126
+ }
127
+
128
+ /**
129
+ * Deep-merge `source` over `target` (recursively for nested plain objects;
130
+ * scalars and arrays from `source` replace `target`). Returns a new object;
131
+ * neither input is mutated. Backs `Config.defaults` — the AdonisJS semantics
132
+ * where existing config wins over provided defaults.
133
+ */
134
+ export function mergeDeep(target: unknown, source: unknown): unknown {
135
+ if (!isIndexable(target) || Array.isArray(target)) return source === undefined ? target : source
136
+ if (!isIndexable(source) || Array.isArray(source)) return source === undefined ? target : source
137
+ const out: UnknownRecord = { ...target }
138
+ for (const key of Object.keys(source)) {
139
+ out[key] = mergeDeep(out[key], source[key])
140
+ }
141
+ return out
142
+ }
@@ -1,18 +0,0 @@
1
- /**
2
- * CommandRunner — minimal CLI command framework.
3
- *
4
- * Registers commands and dispatches based on argv.
5
- * Like AdonisJS Ace but lightweight.
6
- */
7
- export interface Command {
8
- name: string;
9
- description: string;
10
- run(args: string[], flags: Record<string, string | boolean>): Promise<void>;
11
- }
12
- export declare class CommandRunner {
13
- #private;
14
- register(command: Command): this;
15
- handle(argv: string[]): Promise<void>;
16
- getCommands(): Map<string, Command>;
17
- }
18
- //# sourceMappingURL=CommandRunner.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CommandRunner.d.ts","sourceRoot":"","sources":["../../src/console/CommandRunner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5E;AAED,qBAAa,aAAa;;IAGxB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAK1B,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA2B3C,WAAW,IAAI,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;CAGpC"}
@@ -1,65 +0,0 @@
1
- /**
2
- * CommandRunner — minimal CLI command framework.
3
- *
4
- * Registers commands and dispatches based on argv.
5
- * Like AdonisJS Ace but lightweight.
6
- */
7
- export class CommandRunner {
8
- #commands = new Map();
9
- register(command) {
10
- this.#commands.set(command.name, command);
11
- return this;
12
- }
13
- async handle(argv) {
14
- const { commandName, args, flags } = parseArgv(argv);
15
- if (!commandName || commandName === 'help') {
16
- this.#printHelp();
17
- return;
18
- }
19
- const command = this.#commands.get(commandName);
20
- if (!command) {
21
- console.error(`Unknown command: ${commandName}`);
22
- console.error(`Run without arguments to see available commands.`);
23
- process.exitCode = 1;
24
- return;
25
- }
26
- await command.run(args, flags);
27
- }
28
- #printHelp() {
29
- console.log('\nAvailable commands:\n');
30
- for (const [name, cmd] of this.#commands) {
31
- console.log(` ${name.padEnd(25)} ${cmd.description}`);
32
- }
33
- console.log('');
34
- }
35
- getCommands() {
36
- return new Map(this.#commands);
37
- }
38
- }
39
- function parseArgv(argv) {
40
- const flags = {};
41
- const positional = [];
42
- for (const arg of argv) {
43
- if (arg.startsWith('--')) {
44
- const eqIdx = arg.indexOf('=');
45
- if (eqIdx !== -1) {
46
- flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1);
47
- }
48
- else {
49
- flags[arg.slice(2)] = true;
50
- }
51
- }
52
- else if (arg.startsWith('-') && arg.length === 2) {
53
- flags[arg.slice(1)] = true;
54
- }
55
- else {
56
- positional.push(arg);
57
- }
58
- }
59
- return {
60
- commandName: positional[0],
61
- args: positional.slice(1),
62
- flags,
63
- };
64
- }
65
- //# sourceMappingURL=CommandRunner.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"CommandRunner.js","sourceRoot":"","sources":["../../src/console/CommandRunner.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAQH,MAAM,OAAO,aAAa;IACxB,SAAS,GAAyB,IAAI,GAAG,EAAE,CAAA;IAE3C,QAAQ,CAAC,OAAgB;QACvB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QACzC,OAAO,IAAI,CAAA;IACb,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,IAAc;QACzB,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,CAAA;QAEpD,IAAI,CAAC,WAAW,IAAI,WAAW,KAAK,MAAM,EAAE,CAAC;YAC3C,IAAI,CAAC,UAAU,EAAE,CAAA;YACjB,OAAM;QACR,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;QAC/C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,oBAAoB,WAAW,EAAE,CAAC,CAAA;YAChD,OAAO,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAA;YACjE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAA;YACpB,OAAM;QACR,CAAC;QAED,MAAM,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAChC,CAAC;IAED,UAAU;QACR,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAA;QACtC,KAAK,MAAM,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,CAAC,CAAA;QACxD,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;IACjB,CAAC;IAED,WAAW;QACT,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAChC,CAAC;CACF;AAED,SAAS,SAAS,CAAC,IAAc;IAK/B,MAAM,KAAK,GAAqC,EAAE,CAAA;IAClD,MAAM,UAAU,GAAa,EAAE,CAAA;IAE/B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC9B,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;gBACjB,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAA;YACnD,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;YAC5B,CAAC;QACH,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,GAAG,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAA;QAC5B,CAAC;aAAM,CAAC;YACN,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;IAED,OAAO;QACL,WAAW,EAAE,UAAU,CAAC,CAAC,CAAC;QAC1B,IAAI,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC;QACzB,KAAK;KACN,CAAA;AACH,CAAC"}
@@ -1,19 +0,0 @@
1
- /**
2
- * @Lazy() decorator — deferred injection to break circular dependencies.
3
- *
4
- * @implements FR15
5
- */
6
- import 'reflect-metadata';
7
- /**
8
- * @Lazy() — marks a constructor parameter for deferred resolution.
9
- * The dependency is resolved on first property access, not at construction.
10
- */
11
- export declare function Lazy(): ParameterDecorator;
12
- /** Get lazy parameter indices for a constructor. */
13
- export declare function getLazyParams(target: object): number[];
14
- /**
15
- * Create a lazy proxy that defers resolution until first access.
16
- * Traps get, set, has, ownKeys, getOwnPropertyDescriptor for full compatibility.
17
- */
18
- export declare function createLazyProxy<T extends object>(resolver: () => T): T;
19
- //# sourceMappingURL=Lazy.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Lazy.d.ts","sourceRoot":"","sources":["../../src/decorators/Lazy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,kBAAkB,CAAA;AAIzB;;;GAGG;AACH,wBAAgB,IAAI,IAAI,kBAAkB,CAMzC;AAED,oDAAoD;AACpD,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAEtD;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,CAyBtE"}
@@ -1,53 +0,0 @@
1
- /**
2
- * @Lazy() decorator — deferred injection to break circular dependencies.
3
- *
4
- * @implements FR15
5
- */
6
- import 'reflect-metadata';
7
- const LAZY_KEY = Symbol.for('ream:lazy');
8
- /**
9
- * @Lazy() — marks a constructor parameter for deferred resolution.
10
- * The dependency is resolved on first property access, not at construction.
11
- */
12
- export function Lazy() {
13
- return (target, propertyKey, parameterIndex) => {
14
- const existing = Reflect.getOwnMetadata(LAZY_KEY, target, propertyKey ?? '') ?? [];
15
- existing.push(parameterIndex);
16
- Reflect.defineMetadata(LAZY_KEY, existing, target, propertyKey ?? '');
17
- };
18
- }
19
- /** Get lazy parameter indices for a constructor. */
20
- export function getLazyParams(target) {
21
- return Reflect.getOwnMetadata(LAZY_KEY, target, '') ?? [];
22
- }
23
- /**
24
- * Create a lazy proxy that defers resolution until first access.
25
- * Traps get, set, has, ownKeys, getOwnPropertyDescriptor for full compatibility.
26
- */
27
- export function createLazyProxy(resolver) {
28
- let instance;
29
- const ensure = () => {
30
- if (!instance)
31
- instance = resolver();
32
- return instance;
33
- };
34
- // biome-ignore lint/suspicious/noExplicitAny: Proxy target must be an object literal; T is branded at call site
35
- return new Proxy({}, {
36
- get(_target, prop, receiver) {
37
- return Reflect.get(ensure(), prop, receiver);
38
- },
39
- set(_target, prop, value, receiver) {
40
- return Reflect.set(ensure(), prop, value, receiver);
41
- },
42
- has(_target, prop) {
43
- return Reflect.has(ensure(), prop);
44
- },
45
- ownKeys() {
46
- return Reflect.ownKeys(ensure());
47
- },
48
- getOwnPropertyDescriptor(_target, prop) {
49
- return Object.getOwnPropertyDescriptor(ensure(), prop);
50
- },
51
- });
52
- }
53
- //# sourceMappingURL=Lazy.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Lazy.js","sourceRoot":"","sources":["../../src/decorators/Lazy.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,kBAAkB,CAAA;AAEzB,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;AAExC;;;GAGG;AACH,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,MAAM,EAAE,WAAW,EAAE,cAAc,EAAE,EAAE;QAC7C,MAAM,QAAQ,GAAa,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,IAAI,EAAE,CAAC,IAAI,EAAE,CAAA;QAC5F,QAAQ,CAAC,IAAI,CAAC,cAAc,CAAC,CAAA;QAC7B,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,IAAI,EAAE,CAAC,CAAA;IACvE,CAAC,CAAA;AACH,CAAC;AAED,oDAAoD;AACpD,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,OAAO,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,EAAE,CAAA;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAmB,QAAiB;IACjE,IAAI,QAAuB,CAAA;IAC3B,MAAM,MAAM,GAAG,GAAM,EAAE;QACrB,IAAI,CAAC,QAAQ;YAAE,QAAQ,GAAG,QAAQ,EAAE,CAAA;QACpC,OAAO,QAAQ,CAAA;IACjB,CAAC,CAAA;IAED,gHAAgH;IAChH,OAAO,IAAI,KAAK,CAAC,EAAc,EAAE;QAC/B,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,QAAQ;YACzB,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;QAC9C,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ;YAChC,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAA;QACrD,CAAC;QACD,GAAG,CAAC,OAAO,EAAE,IAAI;YACf,OAAO,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAA;QACpC,CAAC;QACD,OAAO;YACL,OAAO,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,CAAA;QAClC,CAAC;QACD,wBAAwB,CAAC,OAAO,EAAE,IAAI;YACpC,OAAO,MAAM,CAAC,wBAAwB,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAA;QACxD,CAAC;KACF,CAAC,CAAA;AACJ,CAAC"}