@c9up/ream 0.1.26 → 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 (179) hide show
  1. package/README.md +65 -0
  2. package/dist/Ignitor.d.ts +115 -7
  3. package/dist/Ignitor.d.ts.map +1 -1
  4. package/dist/Ignitor.js +233 -37
  5. package/dist/Ignitor.js.map +1 -1
  6. package/dist/console/BaseCommand.d.ts +217 -0
  7. package/dist/console/BaseCommand.d.ts.map +1 -0
  8. package/dist/console/BaseCommand.js +340 -0
  9. package/dist/console/BaseCommand.js.map +1 -0
  10. package/dist/console/Console.d.ts +86 -0
  11. package/dist/console/Console.d.ts.map +1 -0
  12. package/dist/console/Console.js +134 -0
  13. package/dist/console/Console.js.map +1 -0
  14. package/dist/console/ExceptionHandler.d.ts +28 -0
  15. package/dist/console/ExceptionHandler.d.ts.map +1 -0
  16. package/dist/console/ExceptionHandler.js +76 -0
  17. package/dist/console/ExceptionHandler.js.map +1 -0
  18. package/dist/console/HelpCommand.d.ts +16 -0
  19. package/dist/console/HelpCommand.d.ts.map +1 -0
  20. package/dist/console/HelpCommand.js +40 -0
  21. package/dist/console/HelpCommand.js.map +1 -0
  22. package/dist/console/IndexGenerator.d.ts +24 -0
  23. package/dist/console/IndexGenerator.d.ts.map +1 -0
  24. package/dist/console/IndexGenerator.js +65 -0
  25. package/dist/console/IndexGenerator.js.map +1 -0
  26. package/dist/console/Kernel.d.ts +298 -0
  27. package/dist/console/Kernel.d.ts.map +1 -0
  28. package/dist/console/Kernel.js +1120 -0
  29. package/dist/console/Kernel.js.map +1 -0
  30. package/dist/console/ListCommand.d.ts +18 -0
  31. package/dist/console/ListCommand.d.ts.map +1 -0
  32. package/dist/console/ListCommand.js +60 -0
  33. package/dist/console/ListCommand.js.map +1 -0
  34. package/dist/console/assertions.d.ts +20 -0
  35. package/dist/console/assertions.d.ts.map +1 -0
  36. package/dist/console/assertions.js +84 -0
  37. package/dist/console/assertions.js.map +1 -0
  38. package/dist/console/cliui.d.ts +248 -0
  39. package/dist/console/cliui.d.ts.map +1 -0
  40. package/dist/console/cliui.js +587 -0
  41. package/dist/console/cliui.js.map +1 -0
  42. package/dist/console/decorators.d.ts +90 -0
  43. package/dist/console/decorators.d.ts.map +1 -0
  44. package/dist/console/decorators.js +142 -0
  45. package/dist/console/decorators.js.map +1 -0
  46. package/dist/console/index.d.ts +32 -0
  47. package/dist/console/index.d.ts.map +1 -0
  48. package/dist/console/index.js +24 -0
  49. package/dist/console/index.js.map +1 -0
  50. package/dist/console/loaders.d.ts +52 -0
  51. package/dist/console/loaders.d.ts.map +1 -0
  52. package/dist/console/loaders.js +131 -0
  53. package/dist/console/loaders.js.map +1 -0
  54. package/dist/console/parser.d.ts +86 -0
  55. package/dist/console/parser.d.ts.map +1 -0
  56. package/dist/console/parser.js +440 -0
  57. package/dist/console/parser.js.map +1 -0
  58. package/dist/console/prompts.d.ts +124 -0
  59. package/dist/console/prompts.d.ts.map +1 -0
  60. package/dist/console/prompts.js +356 -0
  61. package/dist/console/prompts.js.map +1 -0
  62. package/dist/console/tracing.d.ts +17 -0
  63. package/dist/console/tracing.d.ts.map +1 -0
  64. package/dist/console/tracing.js +11 -0
  65. package/dist/console/tracing.js.map +1 -0
  66. package/dist/console/types.d.ts +255 -0
  67. package/dist/console/types.d.ts.map +1 -0
  68. package/dist/console/types.js +25 -0
  69. package/dist/console/types.js.map +1 -0
  70. package/dist/console/ui.d.ts +20 -0
  71. package/dist/console/ui.d.ts.map +1 -0
  72. package/dist/console/ui.js +31 -0
  73. package/dist/console/ui.js.map +1 -0
  74. package/dist/console/utils.d.ts +27 -0
  75. package/dist/console/utils.d.ts.map +1 -0
  76. package/dist/console/utils.js +56 -0
  77. package/dist/console/utils.js.map +1 -0
  78. package/dist/container/Container.d.ts.map +1 -1
  79. package/dist/container/Container.js +40 -12
  80. package/dist/container/Container.js.map +1 -1
  81. package/dist/container/types.d.ts +14 -2
  82. package/dist/container/types.d.ts.map +1 -1
  83. package/dist/decorators/Service.d.ts +7 -0
  84. package/dist/decorators/Service.d.ts.map +1 -1
  85. package/dist/decorators/Service.js +23 -3
  86. package/dist/decorators/Service.js.map +1 -1
  87. package/dist/errors/ReamError.d.ts +25 -0
  88. package/dist/errors/ReamError.d.ts.map +1 -1
  89. package/dist/errors/ReamError.js +5 -0
  90. package/dist/errors/ReamError.js.map +1 -1
  91. package/dist/errors/prettyPrintError.d.ts +9 -0
  92. package/dist/errors/prettyPrintError.d.ts.map +1 -0
  93. package/dist/errors/prettyPrintError.js +22 -0
  94. package/dist/errors/prettyPrintError.js.map +1 -0
  95. package/dist/index.d.ts +14 -4
  96. package/dist/index.d.ts.map +1 -1
  97. package/dist/index.js +16 -2
  98. package/dist/index.js.map +1 -1
  99. package/dist/middleware/Pipeline.d.ts +29 -8
  100. package/dist/middleware/Pipeline.d.ts.map +1 -1
  101. package/dist/middleware/Pipeline.js +39 -1
  102. package/dist/middleware/Pipeline.js.map +1 -1
  103. package/dist/rpc/RpcRouter.d.ts.map +1 -1
  104. package/dist/rpc/RpcRouter.js +13 -2
  105. package/dist/rpc/RpcRouter.js.map +1 -1
  106. package/dist/services/app.d.ts +1 -1
  107. package/dist/services/app.js +1 -1
  108. package/dist/services/console.d.ts +21 -0
  109. package/dist/services/console.d.ts.map +1 -0
  110. package/dist/services/console.js +35 -0
  111. package/dist/services/console.js.map +1 -0
  112. package/dist/testing/ApiResponse.d.ts +218 -0
  113. package/dist/testing/ApiResponse.d.ts.map +1 -0
  114. package/dist/testing/ApiResponse.js +576 -0
  115. package/dist/testing/ApiResponse.js.map +1 -0
  116. package/dist/testing/RequestBuilder.d.ts +132 -72
  117. package/dist/testing/RequestBuilder.d.ts.map +1 -1
  118. package/dist/testing/RequestBuilder.js +389 -222
  119. package/dist/testing/RequestBuilder.js.map +1 -1
  120. package/dist/testing/TestClient.d.ts +34 -39
  121. package/dist/testing/TestClient.d.ts.map +1 -1
  122. package/dist/testing/TestClient.js +48 -56
  123. package/dist/testing/TestClient.js.map +1 -1
  124. package/dist/utils/Macroable.js +3 -3
  125. package/dist/utils/Macroable.js.map +1 -1
  126. package/events.darwin-arm64.node +0 -0
  127. package/events.darwin-x64.node +0 -0
  128. package/events.linux-arm64-gnu.node +0 -0
  129. package/events.linux-x64-gnu.node +0 -0
  130. package/events.win32-x64-msvc.node +0 -0
  131. package/index.darwin-arm64.node +0 -0
  132. package/index.darwin-x64.node +0 -0
  133. package/index.linux-arm64-gnu.node +0 -0
  134. package/index.linux-x64-gnu.node +0 -0
  135. package/index.win32-x64-msvc.node +0 -0
  136. package/package.json +15 -2
  137. package/scheduler.darwin-arm64.node +0 -0
  138. package/scheduler.darwin-x64.node +0 -0
  139. package/scheduler.linux-arm64-gnu.node +0 -0
  140. package/scheduler.linux-x64-gnu.node +0 -0
  141. package/scheduler.win32-x64-msvc.node +0 -0
  142. package/src/Ignitor.ts +340 -42
  143. package/src/console/BaseCommand.ts +467 -0
  144. package/src/console/Console.ts +174 -0
  145. package/src/console/ExceptionHandler.ts +91 -0
  146. package/src/console/HelpCommand.ts +37 -0
  147. package/src/console/IndexGenerator.ts +79 -0
  148. package/src/console/Kernel.ts +1382 -0
  149. package/src/console/ListCommand.ts +55 -0
  150. package/src/console/assertions.ts +114 -0
  151. package/src/console/cliui.ts +756 -0
  152. package/src/console/decorators.ts +199 -0
  153. package/src/console/index.ts +67 -0
  154. package/src/console/loaders.ts +145 -0
  155. package/src/console/parser.ts +598 -0
  156. package/src/console/prompts.ts +548 -0
  157. package/src/console/tracing.ts +22 -0
  158. package/src/console/types.ts +280 -0
  159. package/src/console/ui.ts +37 -0
  160. package/src/console/utils.ts +69 -0
  161. package/src/container/Container.ts +55 -13
  162. package/src/container/types.ts +15 -2
  163. package/src/decorators/Service.ts +28 -3
  164. package/src/errors/ReamError.ts +31 -0
  165. package/src/errors/prettyPrintError.ts +20 -0
  166. package/src/index.ts +30 -4
  167. package/src/middleware/Pipeline.ts +76 -5
  168. package/src/rpc/RpcRouter.ts +16 -2
  169. package/src/services/app.ts +1 -1
  170. package/src/services/console.ts +42 -0
  171. package/src/testing/ApiResponse.ts +691 -0
  172. package/src/testing/RequestBuilder.ts +498 -275
  173. package/src/testing/TestClient.ts +63 -86
  174. package/src/utils/Macroable.ts +3 -3
  175. package/dist/console/CommandRunner.d.ts +0 -18
  176. package/dist/console/CommandRunner.d.ts.map +0 -1
  177. package/dist/console/CommandRunner.js +0 -65
  178. package/dist/console/CommandRunner.js.map +0 -1
  179. package/src/console/CommandRunner.ts +0 -82
@@ -23,40 +23,24 @@ 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
- }
34
-
35
- /**
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.
40
- */
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
- }
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'
53
32
 
54
33
  /**
55
34
  * Named-route manifest — `name → path pattern`, e.g. `{ 'users.show':
56
35
  * '/users/:id' }`. Feed `router.namedManifest()` here so `visit()` can resolve
57
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).
58
41
  */
59
- export type RouteManifest = Record<string, string>
42
+ export type RouteEntry = string | { path: string; method?: HttpMethod }
43
+ export type RouteManifest = Record<string, RouteEntry>
60
44
 
61
45
  export class TestClient {
62
46
  #port = 0
@@ -106,29 +90,47 @@ export class TestClient {
106
90
  return this
107
91
  }
108
92
 
109
- /** GET request. Returns a chainable, awaitable builder (await sends it). */
110
- get(path: string): TestRequestBuilder {
111
- 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)
112
101
  }
113
102
 
114
- /** POST request. */
115
- post(path: string): TestRequestBuilder {
116
- return this.request('POST', path)
103
+ /** POST request — rich, awaitable builder. */
104
+ post(path: string): RequestBuilder {
105
+ return this.fluent('POST', path)
117
106
  }
118
107
 
119
- /** PUT request. */
120
- put(path: string): TestRequestBuilder {
121
- return this.request('PUT', path)
108
+ /** PUT request — rich, awaitable builder. */
109
+ put(path: string): RequestBuilder {
110
+ return this.fluent('PUT', path)
122
111
  }
123
112
 
124
- /** PATCH request. */
125
- patch(path: string): TestRequestBuilder {
126
- return this.request('PATCH', path)
113
+ /** PATCH request — rich, awaitable builder. */
114
+ patch(path: string): RequestBuilder {
115
+ return this.fluent('PATCH', path)
127
116
  }
128
117
 
129
- /** DELETE request. Returns a chainable, awaitable builder (await sends it). */
130
- delete(path: string): TestRequestBuilder {
131
- return this.request('DELETE', path)
118
+ /** DELETE request — rich, awaitable builder. */
119
+ delete(path: string): RequestBuilder {
120
+ return this.fluent('DELETE', path)
121
+ }
122
+
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)
132
134
  }
133
135
 
134
136
  /**
@@ -145,6 +147,7 @@ export class TestClient {
145
147
  p,
146
148
  { ...this.#headers, ...init.headers },
147
149
  init.body.toString('utf8'),
150
+ init.timeoutMs,
148
151
  )
149
152
  return new RequestBuilder(sender, method, path, this.#auth)
150
153
  }
@@ -161,48 +164,20 @@ export class TestClient {
161
164
  'TestClient: visit() needs a named-route manifest. Pass `routes: router.namedManifest()` in the client options.',
162
165
  )
163
166
  }
164
- return this.fluent('GET', resolveNamedRoute(this.#routes, name, params))
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))
165
172
  }
166
173
 
167
- /** Build a request with the low-level fluent API. */
168
- request(method: string, path: string): TestRequestBuilder {
169
- const headers: Dict = { ...this.#headers }
170
- let bodyContent = ''
171
- const cookies: string[] = []
172
-
173
- const builder: TestRequestBuilder = {
174
- header(name: string, value: string) {
175
- headers[name.toLowerCase()] = value
176
- return builder
177
- },
178
- json(data: unknown) {
179
- bodyContent = JSON.stringify(data)
180
- headers['content-type'] = 'application/json'
181
- return builder
182
- },
183
- body(content: string, contentType?: string) {
184
- bodyContent = content
185
- if (contentType) headers['content-type'] = contentType
186
- return builder
187
- },
188
- cookie(name: string, value: string) {
189
- cookies.push(`${name}=${value}`)
190
- return builder
191
- },
192
- send: async () => {
193
- if (cookies.length > 0) {
194
- headers.cookie = cookies.join('; ')
195
- }
196
- return sendRequest(this.#port, method, path, headers, bodyContent)
197
- },
198
- // Thenable: `await client.get('/x')` (or any builder) sends the request,
199
- // matching AdonisJS/japa's api-client ergonomics. The `then` IS the public
200
- // contract here — awaiting the builder is the documented way to send.
201
- // biome-ignore lint/suspicious/noThenProperty: thenable request builder is the intended API (AdonisJS/japa parity) — `await client.get(path)` sends
202
- then: (onfulfilled, onrejected) => builder.send().then(onfulfilled, onrejected),
203
- }
204
-
205
- 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)
206
181
  }
207
182
  }
208
183
 
@@ -237,12 +212,12 @@ export function resolveNamedRoute(
237
212
  name: string,
238
213
  params?: Record<string, string>,
239
214
  ): string {
240
- const pattern = manifest[name]
241
- if (pattern === undefined) {
215
+ const entry = manifest[name]
216
+ if (entry === undefined) {
242
217
  const available = Object.keys(manifest).join(', ') || '(none)'
243
218
  throw new Error(`Route '${name}' not found. Available: ${available}`)
244
219
  }
245
- let url = pattern
220
+ let url = typeof entry === 'string' ? entry : entry.path
246
221
  if (params) {
247
222
  for (const [key, value] of Object.entries(params)) {
248
223
  const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
@@ -264,6 +239,7 @@ async function sendRequest(
264
239
  path: string,
265
240
  headers: Dict,
266
241
  body: string,
242
+ timeoutMs?: number,
267
243
  ): Promise<TestResponse> {
268
244
  const { connect } = await import('node:net')
269
245
 
@@ -309,8 +285,9 @@ async function sendRequest(
309
285
  // tokio runtimes) the cumulative latency exceeded 5s ~30% of runs
310
286
  // and surfaced as flaky `bob accepts` / `non-member 403` failures
311
287
  // in kitchen-sink's workspace.test.ts. 30s matches the helix
312
- // `--timeout=60000` per-test budget without masking real hangs.
313
- 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, () => {
314
291
  socket.destroy()
315
292
  reject(new Error('TestClient request timed out'))
316
293
  })
@@ -24,7 +24,7 @@ export type GetterFn = () => unknown
24
24
  export class Macroable {
25
25
  /** Attach a method to every instance of this class (AdonisJS `macro`). */
26
26
  static macro(name: string, fn: MacroFn): void {
27
- Object.defineProperty(this.prototype, name, {
27
+ Object.defineProperty(Macroable.prototype, name, {
28
28
  value: fn,
29
29
  writable: true,
30
30
  configurable: true,
@@ -37,11 +37,11 @@ export class Macroable {
37
37
  */
38
38
  static getter(name: string, fn: GetterFn, singleton = false): void {
39
39
  if (!singleton) {
40
- Object.defineProperty(this.prototype, name, { get: fn, configurable: true })
40
+ Object.defineProperty(Macroable.prototype, name, { get: fn, configurable: true })
41
41
  return
42
42
  }
43
43
  const cacheKey = Symbol(name)
44
- Object.defineProperty(this.prototype, name, {
44
+ Object.defineProperty(Macroable.prototype, name, {
45
45
  configurable: true,
46
46
  get(this: Record<symbol, unknown>) {
47
47
  if (!(cacheKey in this)) {
@@ -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,82 +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
-
8
- export interface Command {
9
- name: string
10
- description: string
11
- run(args: string[], flags: Record<string, string | boolean>): Promise<void>
12
- }
13
-
14
- export class CommandRunner {
15
- #commands: Map<string, Command> = new Map()
16
-
17
- register(command: Command): this {
18
- this.#commands.set(command.name, command)
19
- return this
20
- }
21
-
22
- async handle(argv: string[]): Promise<void> {
23
- const { commandName, args, flags } = parseArgv(argv)
24
-
25
- if (!commandName || commandName === 'help') {
26
- this.#printHelp()
27
- return
28
- }
29
-
30
- const command = this.#commands.get(commandName)
31
- if (!command) {
32
- console.error(`Unknown command: ${commandName}`)
33
- console.error(`Run without arguments to see available commands.`)
34
- process.exitCode = 1
35
- return
36
- }
37
-
38
- await command.run(args, flags)
39
- }
40
-
41
- #printHelp(): void {
42
- console.log('\nAvailable commands:\n')
43
- for (const [name, cmd] of this.#commands) {
44
- console.log(` ${name.padEnd(25)} ${cmd.description}`)
45
- }
46
- console.log('')
47
- }
48
-
49
- getCommands(): Map<string, Command> {
50
- return new Map(this.#commands)
51
- }
52
- }
53
-
54
- function parseArgv(argv: string[]): {
55
- commandName: string | undefined
56
- args: string[]
57
- flags: Record<string, string | boolean>
58
- } {
59
- const flags: Record<string, string | boolean> = {}
60
- const positional: string[] = []
61
-
62
- for (const arg of argv) {
63
- if (arg.startsWith('--')) {
64
- const eqIdx = arg.indexOf('=')
65
- if (eqIdx !== -1) {
66
- flags[arg.slice(2, eqIdx)] = arg.slice(eqIdx + 1)
67
- } else {
68
- flags[arg.slice(2)] = true
69
- }
70
- } else if (arg.startsWith('-') && arg.length === 2) {
71
- flags[arg.slice(1)] = true
72
- } else {
73
- positional.push(arg)
74
- }
75
- }
76
-
77
- return {
78
- commandName: positional[0],
79
- args: positional.slice(1),
80
- flags,
81
- }
82
- }