gemi 0.51.0 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/dist/app/index.js +1 -1
  2. package/dist/bin/gemi.js +30 -1
  3. package/dist/bin/gemi.js.map +4 -4
  4. package/dist/broadcasting/index.js +1 -1
  5. package/dist/bun/plugin.js +1 -1
  6. package/dist/bun/preload.js +1 -1
  7. package/dist/{chunk-5v36t9j3.js → chunk-092pj2j2.js} +3 -3
  8. package/dist/{chunk-5v36t9j3.js.map → chunk-092pj2j2.js.map} +1 -1
  9. package/dist/{chunk-zwpx6z5f.js → chunk-0h3jr9d9.js} +2 -2
  10. package/dist/{chunk-8zd6d5nd.js.map → chunk-0h3jr9d9.js.map} +1 -1
  11. package/dist/{chunk-98sj7tkf.js → chunk-0tbwn6er.js} +3 -3
  12. package/dist/{chunk-98sj7tkf.js.map → chunk-0tbwn6er.js.map} +1 -1
  13. package/dist/{chunk-hwwp6x2s.js → chunk-1ab1ptq5.js} +2 -2
  14. package/dist/{chunk-hwwp6x2s.js.map → chunk-1ab1ptq5.js.map} +1 -1
  15. package/dist/{chunk-eby61053.js → chunk-1ce7veh9.js} +3 -3
  16. package/dist/{chunk-eby61053.js.map → chunk-1ce7veh9.js.map} +1 -1
  17. package/dist/{chunk-dk01r6se.js → chunk-23g7qdpg.js} +2 -2
  18. package/dist/{chunk-dk01r6se.js.map → chunk-23g7qdpg.js.map} +1 -1
  19. package/dist/{chunk-fs091xz7.js → chunk-2gw6cyyj.js} +2 -2
  20. package/dist/{chunk-fs091xz7.js.map → chunk-2gw6cyyj.js.map} +1 -1
  21. package/dist/chunk-2qn472j4.js +5 -0
  22. package/dist/{chunk-8gb1w19q.js.map → chunk-2qn472j4.js.map} +1 -1
  23. package/dist/{chunk-pkzj7c5w.js → chunk-2sfz64xh.js} +2 -2
  24. package/dist/{chunk-pkzj7c5w.js.map → chunk-2sfz64xh.js.map} +1 -1
  25. package/dist/{chunk-q0xq0pt0.js → chunk-2yth3kk2.js} +4 -4
  26. package/dist/{chunk-q0xq0pt0.js.map → chunk-2yth3kk2.js.map} +1 -1
  27. package/dist/{chunk-gdegb5t0.js → chunk-38ma6gg3.js} +2 -2
  28. package/dist/{chunk-gdegb5t0.js.map → chunk-38ma6gg3.js.map} +1 -1
  29. package/dist/{chunk-8vnbvfmd.js → chunk-3y9nf09k.js} +2 -2
  30. package/dist/{chunk-8vnbvfmd.js.map → chunk-3y9nf09k.js.map} +1 -1
  31. package/dist/{chunk-8zd6d5nd.js → chunk-4e61qbp4.js} +2 -2
  32. package/dist/{chunk-zwpx6z5f.js.map → chunk-4e61qbp4.js.map} +1 -1
  33. package/dist/{chunk-nbx9mgmr.js → chunk-4t6a82fh.js} +2 -2
  34. package/dist/{chunk-nbx9mgmr.js.map → chunk-4t6a82fh.js.map} +1 -1
  35. package/dist/{chunk-mrf9tjev.js → chunk-53e48ce8.js} +2 -2
  36. package/dist/{chunk-mrf9tjev.js.map → chunk-53e48ce8.js.map} +1 -1
  37. package/dist/{chunk-j33n723q.js → chunk-5s45prj5.js} +3 -3
  38. package/dist/{chunk-j33n723q.js.map → chunk-5s45prj5.js.map} +1 -1
  39. package/dist/{chunk-16bnbtyn.js → chunk-68w225rv.js} +2 -2
  40. package/dist/{chunk-16bnbtyn.js.map → chunk-68w225rv.js.map} +1 -1
  41. package/dist/{chunk-rxeyhwqz.js → chunk-69w4egkg.js} +3 -3
  42. package/dist/{chunk-rxeyhwqz.js.map → chunk-69w4egkg.js.map} +1 -1
  43. package/dist/{chunk-8m3wq94b.js → chunk-6scjrbw7.js} +2 -2
  44. package/dist/{chunk-8m3wq94b.js.map → chunk-6scjrbw7.js.map} +1 -1
  45. package/dist/{chunk-asgfqcwd.js → chunk-88pq4gmf.js} +2 -2
  46. package/dist/{chunk-asgfqcwd.js.map → chunk-88pq4gmf.js.map} +1 -1
  47. package/dist/{chunk-p0z35s59.js → chunk-8p6rqegw.js} +1 -1
  48. package/dist/{chunk-5t2c9194.js → chunk-a0sy9f9x.js} +1 -1
  49. package/dist/{chunk-g4gjn481.js → chunk-cmq5t8p9.js} +3 -3
  50. package/dist/{chunk-g4gjn481.js.map → chunk-cmq5t8p9.js.map} +1 -1
  51. package/dist/{chunk-zznpmnp7.js → chunk-d1ahbza9.js} +3 -3
  52. package/dist/{chunk-zznpmnp7.js.map → chunk-d1ahbza9.js.map} +1 -1
  53. package/dist/{chunk-an6nq9yd.js → chunk-ebcgdv00.js} +3 -3
  54. package/dist/{chunk-an6nq9yd.js.map → chunk-ebcgdv00.js.map} +1 -1
  55. package/dist/{chunk-3z4nwzvp.js → chunk-et0j6jx8.js} +3 -3
  56. package/dist/{chunk-3z4nwzvp.js.map → chunk-et0j6jx8.js.map} +1 -1
  57. package/dist/chunk-f29vd5mc.js +5 -0
  58. package/dist/{chunk-45c8sqb7.js.map → chunk-f29vd5mc.js.map} +2 -2
  59. package/dist/{chunk-gvf1d636.js → chunk-faa987e4.js} +2 -2
  60. package/dist/{chunk-gvf1d636.js.map → chunk-faa987e4.js.map} +1 -1
  61. package/dist/{chunk-6wm9bsz9.js → chunk-h5g05qwb.js} +2 -2
  62. package/dist/{chunk-6wm9bsz9.js.map → chunk-h5g05qwb.js.map} +1 -1
  63. package/dist/{chunk-x80n922n.js → chunk-h7jv3ame.js} +2 -2
  64. package/dist/{chunk-x80n922n.js.map → chunk-h7jv3ame.js.map} +1 -1
  65. package/dist/chunk-ha7n1c94.js +7 -0
  66. package/dist/{chunk-bm6q0q1q.js.map → chunk-ha7n1c94.js.map} +3 -3
  67. package/dist/chunk-hbycemy5.js +6 -0
  68. package/dist/{chunk-ygpzyxr6.js.map → chunk-hbycemy5.js.map} +2 -2
  69. package/dist/{chunk-3fcgg506.js → chunk-j1knf8gv.js} +3 -3
  70. package/dist/{chunk-3fcgg506.js.map → chunk-j1knf8gv.js.map} +1 -1
  71. package/dist/chunk-jdfe4dmh.js +5 -0
  72. package/dist/chunk-jdfe4dmh.js.map +30 -0
  73. package/dist/{chunk-1sbm38g3.js → chunk-kaxvw75w.js} +3 -3
  74. package/dist/{chunk-1sbm38g3.js.map → chunk-kaxvw75w.js.map} +1 -1
  75. package/dist/{chunk-c4r1zh9q.js → chunk-km6x37ws.js} +4 -4
  76. package/dist/{chunk-c4r1zh9q.js.map → chunk-km6x37ws.js.map} +1 -1
  77. package/dist/{chunk-eyvtf769.js → chunk-kxzwem3g.js} +2 -2
  78. package/dist/{chunk-eyvtf769.js.map → chunk-kxzwem3g.js.map} +1 -1
  79. package/dist/{chunk-hnw1jy86.js → chunk-msz8rngs.js} +2 -2
  80. package/dist/{chunk-hnw1jy86.js.map → chunk-msz8rngs.js.map} +1 -1
  81. package/dist/chunk-mt2755rs.js +5 -0
  82. package/dist/{chunk-g0w87jtw.js.map → chunk-mt2755rs.js.map} +4 -3
  83. package/dist/chunk-p22rkf1g.js +8 -0
  84. package/dist/chunk-p22rkf1g.js.map +11 -0
  85. package/dist/{chunk-kv8p4h79.js → chunk-q09mmr05.js} +3 -3
  86. package/dist/{chunk-kv8p4h79.js.map → chunk-q09mmr05.js.map} +1 -1
  87. package/dist/chunk-qj4mmner.js +4 -0
  88. package/dist/{chunk-6x6gb7zg.js.map → chunk-qj4mmner.js.map} +3 -3
  89. package/dist/{chunk-d3tf618w.js → chunk-qx6sahe0.js} +2 -2
  90. package/dist/{chunk-d3tf618w.js.map → chunk-qx6sahe0.js.map} +1 -1
  91. package/dist/{chunk-w7fpm2pw.js → chunk-qyaqg07q.js} +2 -2
  92. package/dist/{chunk-w7fpm2pw.js.map → chunk-qyaqg07q.js.map} +1 -1
  93. package/dist/{chunk-a6w4tpwz.js → chunk-rq770c71.js} +2 -2
  94. package/dist/{chunk-a6w4tpwz.js.map → chunk-rq770c71.js.map} +1 -1
  95. package/dist/{chunk-zezdm4ep.js → chunk-t4n25km9.js} +3 -3
  96. package/dist/{chunk-zezdm4ep.js.map → chunk-t4n25km9.js.map} +1 -1
  97. package/dist/chunk-tmk4zfz9.js +33 -0
  98. package/dist/chunk-tmk4zfz9.js.map +18 -0
  99. package/dist/{chunk-k4mjcvsm.js → chunk-ttzkn1qd.js} +2 -2
  100. package/dist/{chunk-k4mjcvsm.js.map → chunk-ttzkn1qd.js.map} +1 -1
  101. package/dist/{chunk-94k8pjmf.js → chunk-tztqkat2.js} +3 -3
  102. package/dist/{chunk-94k8pjmf.js.map → chunk-tztqkat2.js.map} +1 -1
  103. package/dist/{chunk-ybqftmjx.js → chunk-vnjvd82s.js} +2 -2
  104. package/dist/{chunk-ybqftmjx.js.map → chunk-vnjvd82s.js.map} +1 -1
  105. package/dist/{chunk-0k5jhyry.js → chunk-xsmt86n3.js} +2 -2
  106. package/dist/{chunk-0k5jhyry.js.map → chunk-xsmt86n3.js.map} +1 -1
  107. package/dist/{chunk-ntr81h76.js → chunk-z4q1ty6w.js} +2 -2
  108. package/dist/{chunk-ntr81h76.js.map → chunk-z4q1ty6w.js.map} +1 -1
  109. package/dist/{chunk-53w1njqz.js → chunk-zhsaz9cr.js} +1 -1
  110. package/dist/config/index.js +1 -1
  111. package/dist/console/Command.d.ts +143 -0
  112. package/dist/console/Command.d.ts.map +1 -0
  113. package/dist/console/CommandRegistry.d.ts +46 -0
  114. package/dist/console/CommandRegistry.d.ts.map +1 -0
  115. package/dist/console/builder.d.ts +115 -0
  116. package/dist/console/builder.d.ts.map +1 -0
  117. package/dist/console/builder.test-d.d.ts +2 -0
  118. package/dist/console/builder.test-d.d.ts.map +1 -0
  119. package/dist/console/config.d.ts +40 -0
  120. package/dist/console/config.d.ts.map +1 -0
  121. package/dist/console/context.d.ts +90 -0
  122. package/dist/console/context.d.ts.map +1 -0
  123. package/dist/console/errors.d.ts +19 -0
  124. package/dist/console/errors.d.ts.map +1 -0
  125. package/dist/console/parse.d.ts +65 -0
  126. package/dist/console/parse.d.ts.map +1 -0
  127. package/dist/console/run.d.ts +2 -0
  128. package/dist/console/run.d.ts.map +1 -0
  129. package/dist/console/run.js +16 -0
  130. package/dist/console/run.js.map +14 -0
  131. package/dist/console/runner.d.ts +41 -0
  132. package/dist/console/runner.d.ts.map +1 -0
  133. package/dist/console/usage.d.ts +7 -0
  134. package/dist/console/usage.d.ts.map +1 -0
  135. package/dist/container/index.js +2 -2
  136. package/dist/container/index.js.map +1 -1
  137. package/dist/database/index.js +2 -2
  138. package/dist/database/index.js.map +1 -1
  139. package/dist/email/index.js +2 -2
  140. package/dist/email/index.js.map +2 -2
  141. package/dist/facades/index.js +2 -2
  142. package/dist/facades/index.js.map +1 -1
  143. package/dist/foundation/index.js +2 -2
  144. package/dist/foundation/index.js.map +1 -1
  145. package/dist/http/ViewRouter.d.ts.map +1 -1
  146. package/dist/http/index.js +2 -2
  147. package/dist/http/index.js.map +1 -1
  148. package/dist/i18n/index.js +2 -2
  149. package/dist/i18n/index.js.map +2 -2
  150. package/dist/kernel/Kernel.d.ts +29 -0
  151. package/dist/kernel/Kernel.d.ts.map +1 -1
  152. package/dist/kernel/index.js +3 -3
  153. package/dist/kernel/index.js.map +3 -3
  154. package/dist/orm/compile/nested-writes.d.ts +11 -3
  155. package/dist/orm/compile/nested-writes.d.ts.map +1 -1
  156. package/dist/orm/index.js +3 -3
  157. package/dist/orm/index.js.map +4 -4
  158. package/dist/server/index.js +2 -2
  159. package/dist/server/index.js.map +1 -1
  160. package/dist/services/cron/ScheduleServiceProvider.d.ts.map +1 -1
  161. package/dist/services/discovery.d.ts +15 -0
  162. package/dist/services/discovery.d.ts.map +1 -1
  163. package/dist/services/index.d.ts +8 -1
  164. package/dist/services/index.d.ts.map +1 -1
  165. package/dist/services/index.js +11 -11
  166. package/dist/services/index.js.map +2 -2
  167. package/dist/support/Service.d.ts +90 -0
  168. package/dist/support/Service.d.ts.map +1 -0
  169. package/dist/support/discover.d.ts +9 -1
  170. package/dist/support/discover.d.ts.map +1 -1
  171. package/dist/support/index.d.ts +1 -0
  172. package/dist/support/index.d.ts.map +1 -1
  173. package/dist/support/index.js +2 -2
  174. package/dist/support/index.js.map +4 -3
  175. package/package.json +2 -1
  176. package/dist/chunk-45c8sqb7.js +0 -5
  177. package/dist/chunk-6fym5bfg.js +0 -23
  178. package/dist/chunk-6fym5bfg.js.map +0 -36
  179. package/dist/chunk-6x6gb7zg.js +0 -4
  180. package/dist/chunk-8gb1w19q.js +0 -5
  181. package/dist/chunk-bm6q0q1q.js +0 -7
  182. package/dist/chunk-g0w87jtw.js +0 -5
  183. package/dist/chunk-j4rkk01q.js +0 -5
  184. package/dist/chunk-j4rkk01q.js.map +0 -10
  185. package/dist/chunk-ygpzyxr6.js +0 -6
  186. /package/dist/{chunk-p0z35s59.js.map → chunk-8p6rqegw.js.map} +0 -0
  187. /package/dist/{chunk-5t2c9194.js.map → chunk-a0sy9f9x.js.map} +0 -0
  188. /package/dist/{chunk-53w1njqz.js.map → chunk-zhsaz9cr.js.map} +0 -0
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import"../chunk-zwpx6z5f.js";function j(h){return h}export{j as defineConfig};
2
+ import"../chunk-0h3jr9d9.js";function j(h){return h}export{j as defineConfig};
3
3
 
4
4
  //# debugId=FB1D7FB1C6C96D0964756E2164756E21
@@ -0,0 +1,143 @@
1
+ /**
2
+ * A command an operator runs by hand: `gemi run backfill-avatars --dry-run`.
3
+ *
4
+ * This file is the *runtime* shape — the base class discovery walks the
5
+ * prototype chain for, and the statics the runner reads. It is not the
6
+ * authoring surface. Applications write commands with `defineCommand` in
7
+ * `./builder`, which produces one of these; see that file for why the authoring
8
+ * surface is a builder and not a class body.
9
+ *
10
+ * ### Why the schema is descriptors and not a signature string
11
+ *
12
+ * Laravel spells this `protected $signature = 'mail:send {user} {--queue}'`, and
13
+ * the string is the whole schema. It reads beautifully and TypeScript cannot see
14
+ * a word of it: a brace in the wrong place is a parse error the first time
15
+ * somebody runs the command, months after it was written, and the parser is a
16
+ * piece of framework surface with its own failure modes. The descriptors below
17
+ * say the same thing in a shape the compiler checks — and, through the builder,
18
+ * in a shape that types the handler's own body.
19
+ *
20
+ * ### Why the name is declared and never falls back to the class name
21
+ *
22
+ * `Job` keys off `static name`, which shadows `Function.name`. That is the right
23
+ * trade there: a job's name *is* its class name, and the shadow is what makes it
24
+ * a string literal that survives minification. Here it is the wrong one twice
25
+ * over. A command's name is a CLI token (`backfill-avatars`, `db:seed`) with no
26
+ * relationship to the class name, so an implicit fallback would only ever be
27
+ * wrong; and shadowing `Function.name` costs the one error anybody will actually
28
+ * hit — "two commands claim `db:seed`" — the ability to say *which two*.
29
+ *
30
+ * ### The minification hazard does not reach here
31
+ *
32
+ * `services/discovery.ts` warns about a job that never declared `static name`,
33
+ * because two halves have to agree on one string across a bundler: the
34
+ * dispatcher, minified into `dist/server/server.mjs`, and discovery, reading the
35
+ * source. A command has one half — a human types the name and it is matched
36
+ * against discovery-on-source. Nothing in the server bundle names a command, and
37
+ * `gemi build` keeps `app/commands` out of the bundle anyway. So there is no
38
+ * `warnIfNameWillNotSurviveTheBuild` equivalent below, and adding one by analogy
39
+ * would be noise.
40
+ *
41
+ * If a `Command.call("db:seed")` reachable from a controller is ever added, that
42
+ * changes — and `commandName` being a declared string literal already covers it,
43
+ * which is one more reason not to fall back to the class binding.
44
+ */
45
+ /** A positional argument. `variadic` collects the rest and must come last. */
46
+ export interface ArgSpec {
47
+ description?: string;
48
+ /**
49
+ * Missing it is a usage error rather than an `undefined` in the handler.
50
+ * Mutually exclusive with `default` — an argument that is supplied when absent
51
+ * cannot also be absent, and declaring both is refused rather than silently
52
+ * resolved in one direction.
53
+ */
54
+ required?: boolean;
55
+ /** Collects every remaining positional. Only legal on the last argument. */
56
+ variadic?: boolean;
57
+ /** Supplied when the argument is absent, which makes it always present. */
58
+ default?: string;
59
+ }
60
+ /**
61
+ * A flag. The `type` decides both how the value is parsed and what the handler
62
+ * sees, which is why it is required rather than defaulted to `string`: a
63
+ * `--dry-run` declared without a type would arrive as the string `""`, and every
64
+ * `if (options.dryRun)` over it would be wrong in the one direction nobody
65
+ * tests.
66
+ */
67
+ export type OptionSpec = {
68
+ type: "boolean";
69
+ description?: string;
70
+ /** Single character, without the dash. */
71
+ alias?: string;
72
+ default?: boolean;
73
+ } | {
74
+ type: "string";
75
+ description?: string;
76
+ alias?: string;
77
+ default?: string;
78
+ required?: boolean;
79
+ } | {
80
+ type: "number";
81
+ description?: string;
82
+ alias?: string;
83
+ default?: number;
84
+ required?: boolean;
85
+ };
86
+ /** An argument as stored on the class: the spec, plus the name it was given. */
87
+ export type CommandArgument = ArgSpec & {
88
+ name: string;
89
+ };
90
+ /** An option as stored on the class: the spec, plus the name it was given. */
91
+ export type CommandOption = OptionSpec & {
92
+ name: string;
93
+ };
94
+ /** What a handler may return. `void` and `0` mean the same thing. */
95
+ export type CommandResult = Promise<number | void> | number | void;
96
+ /**
97
+ * A command that failed on purpose.
98
+ *
99
+ * The distinction `bin/check-models.ts` draws between `CheckModelsError` and
100
+ * everything else, in the one place an application can reach it: this is a
101
+ * sentence written for the terminal and is printed without a stack, while
102
+ * anything else that escapes a handler is a bug and keeps its stack.
103
+ */
104
+ export declare class CommandFailed extends Error {
105
+ readonly code: number;
106
+ constructor(message: string, code?: number);
107
+ }
108
+ /**
109
+ * The base class. Discovery finds subclasses of this; the runner reads the
110
+ * statics and calls `run`.
111
+ *
112
+ * Subclassing it by hand works and is what `defineCommand` does under the hood,
113
+ * but it gives up the typing that is the whole point of the builder — `run`'s
114
+ * parameter cannot be inferred from statics declared on the subclass, because
115
+ * TypeScript does not contextually type an overriding method's parameters. Write
116
+ * commands with `defineCommand`.
117
+ */
118
+ export declare class Command {
119
+ /**
120
+ * The name typed on the command line. Mandatory: there is no sensible default
121
+ * and the class name is not one.
122
+ */
123
+ static commandName: string;
124
+ /** One line, printed by `gemi run` with no name and at the top of `--help`. */
125
+ static description: string;
126
+ /** Positional arguments, in order. A variadic one must be last. */
127
+ static args: CommandArgument[];
128
+ /** Flags. Declared in camelCase; `dryRun` is spelled `--dry-run` on the CLI. */
129
+ static options: CommandOption[];
130
+ /**
131
+ * The body.
132
+ *
133
+ * Runs inside `kernel.run()`, so `app(MailManager)` and a model read resolve
134
+ * exactly as they do in a request handler. Resolve services *here* rather than
135
+ * at module load: a command file is imported by discovery before the container
136
+ * has finished booting, and a service captured at the top of the file is
137
+ * captured from a container that is not ready.
138
+ */
139
+ run(_ctx: any): CommandResult;
140
+ }
141
+ /** The type of a class `defineCommand(...).handle(...)` produces. */
142
+ export type CommandClass = typeof Command;
143
+ //# sourceMappingURL=Command.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Command.d.ts","sourceRoot":"","sources":["../../console/Command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2CG;AAEH,8EAA8E;AAC9E,MAAM,WAAW,OAAO;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4EAA4E;IAC5E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAClB;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0CAA0C;IAC1C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB,CAAC;AAEN,gFAAgF;AAChF,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,8EAA8E;AAC9E,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE1D,qEAAqE;AACrE,MAAM,MAAM,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,MAAM,GAAG,IAAI,CAAC;AAEnE;;;;;;;GAOG;AACH,qBAAa,aAAc,SAAQ,KAAK;IAGpC,QAAQ,CAAC,IAAI,EAAE,MAAM;gBADrB,OAAO,EAAE,MAAM,EACN,IAAI,GAAE,MAAU;CAK5B;AAED;;;;;;;;;GASG;AACH,qBAAa,OAAO;IAClB;;;OAGG;IACH,MAAM,CAAC,WAAW,SAAW;IAE7B,+EAA+E;IAC/E,MAAM,CAAC,WAAW,SAAM;IAExB,mEAAmE;IACnE,MAAM,CAAC,IAAI,EAAE,eAAe,EAAE,CAAM;IAEpC,gFAAgF;IAChF,MAAM,CAAC,OAAO,EAAE,aAAa,EAAE,CAAM;IAErC;;;;;;;;OAQG;IACH,GAAG,CAAC,IAAI,EAAE,GAAG,GAAG,aAAa;CAC9B;AAED,qEAAqE;AACrE,MAAM,MAAM,YAAY,GAAG,OAAO,OAAO,CAAC"}
@@ -0,0 +1,46 @@
1
+ import type { CommandClass } from "./Command";
2
+ /**
3
+ * The commands an application has, and the rules about what a command may be.
4
+ *
5
+ * A plain class, not a container service, and that is a decision rather than an
6
+ * omission. `QueueManager` and `Scheduler` are bound and populated during boot
7
+ * because the server needs them: a dispatch or a tick can arrive at any moment
8
+ * after boot finishes. A command registry has exactly one consumer — `gemi run`,
9
+ * which builds one, uses it, and exits. Making it a boot step would import every
10
+ * file under `app/commands` into every `gemi dev`, every `gemi start` and every
11
+ * deployed server process, on every start, for code no request will ever reach.
12
+ * That is the cost `bin/check-models.ts` argues against at length, and there is
13
+ * no benefit here to weigh against it.
14
+ *
15
+ * What the class is for is the rules below, and the `commands` getter — the same
16
+ * seam `QueueManager.registeredJobs` and `Scheduler.jobs` exist to give a test:
17
+ * what was actually taken, rather than what a walk would find if asked again.
18
+ */
19
+ export declare class CommandRegistry {
20
+ private readonly byName;
21
+ /**
22
+ * @throws ConsoleError naming the offending class for a command that cannot
23
+ * be run.
24
+ *
25
+ * Every refusal here is loud and immediate. There is a human at a terminal,
26
+ * and every one of these is a mistake in the application's own source that
27
+ * they are better off seeing now than after the command they meant to run
28
+ * silently did not exist.
29
+ *
30
+ * `ConsoleError` rather than a plain `Error` so the runner prints the message
31
+ * alone. These are sentences with the fix named in them, and burying one under
32
+ * `at new CommandRegistry (…)` plus the runner's own frames points the reader
33
+ * at framework code when the mistake is in their `app/commands`.
34
+ */
35
+ constructor(commands: CommandClass[]);
36
+ /** What was actually taken. */
37
+ get commands(): CommandClass[];
38
+ get(name: string): CommandClass | undefined;
39
+ /**
40
+ * The nearest names to something that did not match, for the "did you mean"
41
+ * line. Prefix matches first, then substring, then names sharing a namespace —
42
+ * `db:sed` should find `db:seed`, and a typo in a namespace is the common case.
43
+ */
44
+ suggest(name: string): string[];
45
+ }
46
+ //# sourceMappingURL=CommandRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CommandRegistry.d.ts","sourceRoot":"","sources":["../../console/CommandRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9C;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAmC;IAE1D;;;;;;;;;;;;;OAaG;gBACS,QAAQ,EAAE,YAAY,EAAE;IAgCpC,+BAA+B;IAC/B,IAAI,QAAQ,IAAI,YAAY,EAAE,CAE7B;IAED,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAI3C;;;;OAIG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;CAuBhC"}
@@ -0,0 +1,115 @@
1
+ import { type ArgSpec, type CommandClass, type CommandResult, type OptionSpec } from "./Command";
2
+ import type { CommandContext } from "./context";
3
+ /**
4
+ * Declaring a command, in the one shape that can type its own body.
5
+ *
6
+ * ### Why a builder and not a class
7
+ *
8
+ * The point of declaring arguments and options at all is that the handler then
9
+ * knows what it has. A class body cannot deliver that, and the reason is a hard
10
+ * TypeScript rule rather than a matter of arrangement: **TypeScript does not
11
+ * contextually type a method's parameters from the base-class method it
12
+ * overrides.** So both class-shaped designs fail in the same place:
13
+ *
14
+ * ```ts
15
+ * class Backfill extends Command {
16
+ * static args = [{ name: "since", required: true }];
17
+ * async run(ctx) {} // implicit any
18
+ * }
19
+ *
20
+ * class Backfill extends Command.define({ since: { required: true } }) {
21
+ * async run(ctx) {} // still implicit any — the
22
+ * } // configured base's signature does
23
+ * // not flow into the override
24
+ * ```
25
+ *
26
+ * The author's only way out is to restate the type by hand, which is the
27
+ * ceremony the descriptors existed to remove — and which nothing enforces, so a
28
+ * wrong restatement compiles.
29
+ *
30
+ * A **function expression passed as an argument is contextually typed**. That is
31
+ * the entire mechanism here, and it is why this is a builder rather than sugar:
32
+ * `.handle(fn)` carries the accumulated argument and option types into `fn`'s
33
+ * parameter with nothing written by hand and nothing to keep in step.
34
+ *
35
+ * ### What it produces
36
+ *
37
+ * A `Command` subclass, so nothing downstream knows the difference: discovery
38
+ * finds it by prototype chain, and the registry, parser and `--help` read the
39
+ * same statics they would read off a hand-written class. The builder is an
40
+ * authoring surface over one runtime shape, not a second mechanism.
41
+ */
42
+ /**
43
+ * Marks a builder that was never finished. `discoverCommands` looks for this on
44
+ * the exports it is about to discard — see the note on `handle` below.
45
+ */
46
+ export declare const BUILDER_BRAND: unique symbol;
47
+ /** Flattens an accumulated intersection so editor hovers stay readable. */
48
+ type Simplify<T> = {
49
+ [K in keyof T]: T[K];
50
+ } & {};
51
+ /**
52
+ * What the handler sees for one argument.
53
+ *
54
+ * The order matters: `variadic` first because it wins over everything, then the
55
+ * two ways an argument is guaranteed present. Anything else can be absent and
56
+ * says so.
57
+ */
58
+ export type ArgValue<S> = S extends {
59
+ variadic: true;
60
+ } ? string[] : S extends {
61
+ required: true;
62
+ } ? string : S extends {
63
+ default: string;
64
+ } ? string : string | undefined;
65
+ /** What the handler sees for one option. */
66
+ export type OptionValue<S> = S extends {
67
+ type: "boolean";
68
+ } ? boolean : S extends {
69
+ type: "number";
70
+ } ? S extends {
71
+ default: number;
72
+ } ? number : S extends {
73
+ required: true;
74
+ } ? number : number | undefined : S extends {
75
+ default: string;
76
+ } ? string : S extends {
77
+ required: true;
78
+ } ? string : string | undefined;
79
+ export interface CommandBuilder<A extends Record<string, unknown>, O extends Record<string, unknown>> {
80
+ /** One line, shown in the listing and at the top of `--help`. */
81
+ describe(text: string): CommandBuilder<A, O>;
82
+ /**
83
+ * A positional argument. Declared in the order it is read from the command
84
+ * line; a `variadic` one must be last.
85
+ */
86
+ arg<N extends string, const S extends ArgSpec = {}>(name: N, spec?: S): CommandBuilder<A & {
87
+ [K in N]: ArgValue<S>;
88
+ }, O>;
89
+ /**
90
+ * A flag. Declared in camelCase and spelled in kebab-case on the command line,
91
+ * so `.option("dryRun", …)` is `--dry-run` for the operator and
92
+ * `options.dryRun` in the handler — no string key nobody can autocomplete, and
93
+ * no CLI flag with a capital letter in it.
94
+ */
95
+ option<N extends string, const S extends OptionSpec>(name: N, spec: S): CommandBuilder<A, O & {
96
+ [K in N]: OptionValue<S>;
97
+ }>;
98
+ /**
99
+ * The body, and the terminal of the chain.
100
+ *
101
+ * A builder that never reaches here is not a class, so discovery would skip it
102
+ * and the command would simply vanish from `gemi run` with nothing raised —
103
+ * the silence #322 and #323 exist to remove, reintroduced by a new authoring
104
+ * surface. `discoverCommands` therefore looks for `BUILDER_BRAND` among the
105
+ * exports it discards and refuses the file by name.
106
+ */
107
+ handle(fn: (ctx: CommandContext<Simplify<A>, Simplify<O>>) => CommandResult): CommandClass;
108
+ }
109
+ export declare function defineCommand(name: string): CommandBuilder<{}, {}>;
110
+ /** True for a builder that never called `.handle()`. */
111
+ export declare function isUnfinishedBuilder(value: unknown): value is {
112
+ [BUILDER_BRAND]: string;
113
+ };
114
+ export {};
115
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../console/builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,OAAO,EAEZ,KAAK,YAAY,EAEjB,KAAK,aAAa,EAClB,KAAK,UAAU,EAChB,MAAM,WAAW,CAAC;AACnB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH;;;GAGG;AACH,eAAO,MAAM,aAAa,EAAE,OAAO,MAA2C,CAAC;AAE/E,2EAA2E;AAC3E,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAE,GAAG,EAAE,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAClD,MAAM,EAAE,GACR,CAAC,SAAS;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAC1B,MAAM,GACN,CAAC,SAAS;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3B,MAAM,GACN,MAAM,GAAG,SAAS,CAAC;AAE3B,4CAA4C;AAC5C,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACtD,OAAO,GACP,CAAC,SAAS;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAC1B,CAAC,SAAS;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3B,MAAM,GACN,CAAC,SAAS;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAC1B,MAAM,GACN,MAAM,GAAG,SAAS,GACtB,CAAC,SAAS;IAAE,OAAO,EAAE,MAAM,CAAA;CAAE,GAC3B,MAAM,GACN,CAAC,SAAS;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAC1B,MAAM,GACN,MAAM,GAAG,SAAS,CAAC;AAE7B,MAAM,WAAW,cAAc,CAC7B,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACjC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAEjC,iEAAiE;IACjE,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAE7C;;;OAGG;IACH,GAAG,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,OAAO,GAAG,EAAE,EAChD,IAAI,EAAE,CAAC,EACP,IAAI,CAAC,EAAE,CAAC,GACP,cAAc,CAAC,CAAC,GAAG;SAAG,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;KAAE,EAAE,CAAC,CAAC,CAAC;IAEpD;;;;;OAKG;IACH,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,KAAK,CAAC,CAAC,SAAS,UAAU,EACjD,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,CAAC,GACN,cAAc,CAAC,CAAC,EAAE,CAAC,GAAG;SAAG,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC;KAAE,CAAC,CAAC;IAEvD;;;;;;;;OAQG;IACH,MAAM,CACJ,EAAE,EAAE,CAAC,GAAG,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,KAAK,aAAa,GACnE,YAAY,CAAC;CACjB;AA4BD,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CAAC,EAAE,EAAE,EAAE,CAAC,CAqIlE;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI;IAAE,CAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAMtC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=builder.test-d.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.test-d.d.ts","sourceRoot":"","sources":["../../console/builder.test-d.ts"],"names":[],"mappings":""}
@@ -0,0 +1,40 @@
1
+ import type { CommandClass } from "./Command";
2
+ export interface CommandConfig {
3
+ /**
4
+ * The `Command` classes this application can run, or nothing.
5
+ *
6
+ * ### The rule, exactly
7
+ *
8
+ * **Declared wins, and `[]` is declared.** A `commands` that is present is
9
+ * used verbatim and no directory is read — the escape hatch for an app whose
10
+ * commands live somewhere the walk cannot reach, and for a deploy that ships
11
+ * no source. An empty array means an app with no commands and says so; it does
12
+ * not mean "find some".
13
+ *
14
+ * **Absent or `undefined` discovers.** `undefined` counts as absent for the
15
+ * same reason `withDefaults` treats it that way everywhere else: a key spread
16
+ * in from an optional value is an omission, not an instruction.
17
+ *
18
+ * ### One way this differs from `queue` and `schedule`
19
+ *
20
+ * There, the cost of the list and the directory disagreeing is silence — a
21
+ * dispatch dropped with a line on stderr nobody reads, a report that stops
22
+ * arriving. Here it is a person typing `gemi run thing` and being told, in
23
+ * those words, that there is no command called `thing`. The rule is kept for
24
+ * consistency and for the source-less-deploy case, not because the stakes are
25
+ * the same.
26
+ */
27
+ commands?: CommandClass[];
28
+ /**
29
+ * Where to look when `commands` was not declared. Relative to the project
30
+ * root, or absolute.
31
+ *
32
+ * Every `.ts`/`.tsx` file underneath is imported when a command is run, so
33
+ * this wants to be a directory of command declarations rather than a directory
34
+ * that merely contains some.
35
+ */
36
+ commandsDir?: string;
37
+ }
38
+ export declare function defineCommandConfig(config: CommandConfig): CommandConfig;
39
+ export declare function commandConfigDefaults(): Required<CommandConfig>;
40
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../console/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAU9C,MAAM,WAAW,aAAa;IAC5B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;IAE1B;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAExE;AAED,wBAAgB,qBAAqB,IAAI,QAAQ,CAAC,aAAa,CAAC,CAK/D"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The single argument a command handler receives.
3
+ *
4
+ * ### Why the output helpers live here and not on `this`
5
+ *
6
+ * The authoring surface is a builder terminating in `.handle(fn)`, and `fn` is
7
+ * an arrow function with no useful `this`. That turns out to be the better
8
+ * arrangement rather than a cost to work around: the helpers are destructured on
9
+ * the first line of the handler, so a reader sees which streams a command writes
10
+ * to before reading any of it, and a test supplies its own writers by passing a
11
+ * context instead of subclassing.
12
+ *
13
+ * ### Why there are three of them
14
+ *
15
+ * The load-bearing thing a framework can own here is **stream discipline, not
16
+ * colour**. A diagnostic printed to stdout is what breaks
17
+ * `TOTAL=$(gemi run count-users)` and what makes a CI grep match a progress line
18
+ * instead of the answer, and that mistake is invisible until somebody pipes the
19
+ * command somewhere.
20
+ *
21
+ * So: `line` is the command's answer and goes to stdout, `error` is a diagnostic
22
+ * and goes to stderr, and `fail` ends the command. Deliberately absent are
23
+ * `info`/`success` (`line` with a colour, and colour is the application's
24
+ * business), `warn` (identical to `error`; two names for one stream is a coin
25
+ * flip at every call site), and tables or progress bars — a console formatting
26
+ * library is a project, not a feature.
27
+ */
28
+ export interface CommandContext<A = Record<string, unknown>, O = Record<string, unknown>> {
29
+ /** The positional arguments, by the names `.arg()` declared. */
30
+ args: A;
31
+ /** The flags, by the names `.option()` declared. */
32
+ options: O;
33
+ /**
34
+ * Everything after the command name, unparsed.
35
+ *
36
+ * The escape hatch, for a command that wants to forward its tail to something
37
+ * else. Present even when parsing succeeded, so a command never has to choose
38
+ * between the declared schema and the raw truth.
39
+ */
40
+ argv: readonly string[];
41
+ /** The command's answer, on stdout. */
42
+ line(message?: string): void;
43
+ /** A diagnostic, on stderr. Does not end the command. */
44
+ error(message: string): void;
45
+ /**
46
+ * Ends the command with a message and an exit code, and no stack.
47
+ *
48
+ * Throws rather than returning, so it type-checks as the last statement of any
49
+ * branch and a caller cannot forget to `return` after it.
50
+ */
51
+ fail(message: string, code?: number): never;
52
+ }
53
+ /** Where a context writes. Injectable so tests can capture without spawning. */
54
+ export interface CommandWriters {
55
+ stdout: (chunk: string) => void;
56
+ stderr: (chunk: string) => void;
57
+ }
58
+ /**
59
+ * Writers onto this process's streams, and the means to know they landed.
60
+ *
61
+ * ### Why `flush` exists
62
+ *
63
+ * `process.stdout.write` is asynchronous when stdout is a pipe, and `gemi run`
64
+ * ends in an explicit `process.exit` — which it must, or a command holding a
65
+ * database pool open would finish its work and then sit there. Those two facts
66
+ * together truncate output at the pipe buffer: measured on Bun 1.3.14, a
67
+ * 2,000,000-byte write followed immediately by `process.exit(0)` delivers
68
+ * exactly 65,536 bytes through a pipe, and exits 0 while doing it. So
69
+ * `gemi run export-users | gzip > out.gz` silently stores a fragment — the exact
70
+ * idiom the three-stream design above exists to protect.
71
+ *
72
+ * The fence is the *last* write's completion callback. Chunks are flushed in
73
+ * order, so awaiting the newest one awaits all of them. The obvious alternatives
74
+ * do not work here: a zero-length `write("", cb)` used as a fence delivered only
75
+ * 1,310,720 of those bytes, and `writableLength` reports 0 while data is still
76
+ * pending, so a drain loop exits immediately. Both were measured rather than
77
+ * assumed.
78
+ */
79
+ export interface ProcessWriters extends CommandWriters {
80
+ /** Resolves once everything written so far has reached the OS. */
81
+ flush(): Promise<void>;
82
+ }
83
+ export declare function processWriters(): ProcessWriters;
84
+ export declare function createContext<A, O>(params: {
85
+ args: A;
86
+ options: O;
87
+ argv: readonly string[];
88
+ writers?: CommandWriters;
89
+ }): CommandContext<A, O>;
90
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../console/context.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,WAAW,cAAc,CAC7B,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC3B,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAE3B,gEAAgE;IAChE,IAAI,EAAE,CAAC,CAAC;IAER,oDAAoD;IACpD,OAAO,EAAE,CAAC,CAAC;IAEX;;;;;;OAMG;IACH,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IAExB,uCAAuC;IACvC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B,yDAAyD;IACzD,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAE7B;;;;;OAKG;IACH,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC;CAC7C;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CACjC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,cAAe,SAAQ,cAAc;IACpD,kEAAkE;IAClE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,wBAAgB,cAAc,IAAI,cAAc,CAyB/C;AAED,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE;IAC1C,IAAI,EAAE,CAAC,CAAC;IACR,OAAO,EAAE,CAAC,CAAC;IACX,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B,GAAG,cAAc,CAAC,CAAC,EAAE,CAAC,CAAC,CAavB"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The failures `gemi run` prints as sentences rather than stack traces.
3
+ *
4
+ * Its own file rather than a declaration in `runner.ts`, because the classes
5
+ * that *raise* these are the ones the runner imports — `CommandRegistry` most of
6
+ * all — and a registry importing the runner to reach its error type is a cycle
7
+ * for no reason.
8
+ *
9
+ * The split this exists to serve is the one `bin/gemi.ts` already draws around
10
+ * `CheckModelsError`: a message written for the operator standing at the
11
+ * terminal, with the fix named in it, is printed alone; anything else is a bug
12
+ * in the framework and keeps its stack so it can be reported.
13
+ */
14
+ export declare class ConsoleError extends Error {
15
+ constructor(message: string, options?: {
16
+ cause?: unknown;
17
+ });
18
+ }
19
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../console/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D"}
@@ -0,0 +1,65 @@
1
+ import type { CommandArgument, CommandOption } from "./Command";
2
+ /**
3
+ * Turning what an operator typed into what a handler declared it wanted.
4
+ *
5
+ * A pure function over `(spec, argv)`, deliberately: this is where every
6
+ * off-by-one in argument handling lives, and a pure function is one a test can
7
+ * exercise a hundred ways without a project on disk, a spawned process or a
8
+ * booted container. `runner.ts` does the parts that need those.
9
+ *
10
+ * ### Why gemi parses this itself instead of handing it to commander
11
+ *
12
+ * commander is a dependency of this package and would resolve here, and an
13
+ * earlier draft used it. Two things argued it back out. The schema is already
14
+ * declared — named options with a `type`, not commander's `"-t, --tries <n>"`
15
+ * flag strings — so using commander means translating one declaration into
16
+ * another and then translating its output back, with the type information lost
17
+ * in the middle. And commander is currently a *CLI-side* concern: `bin/gemi.ts`
18
+ * uses it, and nothing that runs inside an application does. Keeping it that way
19
+ * means a command's parsing has no dependency at all, which matters more here
20
+ * than it looks — this code runs in the application's process, next to the
21
+ * application's own module graph.
22
+ *
23
+ * `usage.ts` renders `--help` from the same spec this reads, so the help and the
24
+ * parser cannot drift.
25
+ */
26
+ /** `dryRun` -> `dry-run`. What the operator types. */
27
+ export declare function kebab(name: string): string;
28
+ export interface CommandSpec {
29
+ commandName: string;
30
+ args: CommandArgument[];
31
+ options: CommandOption[];
32
+ }
33
+ export type ParseResult = {
34
+ /**
35
+ * The invocation asked for help rather than for work.
36
+ *
37
+ * Reported by the parse rather than by a separate scan, because whether a
38
+ * `--help` is a request for help or the *value* of the option before it is a
39
+ * question only something tracking option arity can answer. A flat token scan
40
+ * reads `--message --help` as a request for help, prints usage and exits 0 —
41
+ * so the message is never sent and a cron wrapper branching on the exit code
42
+ * records a success for work that did not happen.
43
+ *
44
+ * Present on both branches, and the runner checks it first: `--help` has to
45
+ * work on an invocation that would otherwise be a usage error, which is
46
+ * precisely when somebody reaches for it.
47
+ */
48
+ help: boolean;
49
+ } & ({
50
+ ok: true;
51
+ args: Record<string, string | string[] | undefined>;
52
+ options: Record<string, string | number | boolean | undefined>;
53
+ } | {
54
+ ok: false;
55
+ errors: string[];
56
+ });
57
+ /**
58
+ * Whether this invocation is asking for help rather than running.
59
+ *
60
+ * A thin read of the parse, so the two cannot disagree about what counts as a
61
+ * `--help`. See `ParseResult["help"]` for why that question needs a parse at all.
62
+ */
63
+ export declare function wantsHelp(spec: CommandSpec, argv: readonly string[]): boolean;
64
+ export declare function parseArgv(spec: CommandSpec, argv: readonly string[]): ParseResult;
65
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../console/parse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAEhE;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,sDAAsD;AACtD,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK1C;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,eAAe,EAAE,CAAC;IACxB,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAED,MAAM,MAAM,WAAW,GAAG;IACxB;;;;;;;;;;;;;OAaG;IACH,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,CACA;IACE,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACpD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,CAAC;CAChE,GACD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAClC,CAAC;AAkBF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAE7E;AAuBD,wBAAgB,SAAS,CACvB,IAAI,EAAE,WAAW,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,WAAW,CA2Rb"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=run.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../console/run.ts"],"names":[],"mappings":""}
@@ -0,0 +1,16 @@
1
+ // @bun
2
+ import{Ba as A,Ca as l}from"../chunk-p22rkf1g.js";import{nb as q,qb as m,yb as p}from"../chunk-tmk4zfz9.js";import{Cb as c}from"../chunk-ha7n1c94.js";import{Ce as u}from"../chunk-3y9nf09k.js";import"../chunk-mt2755rs.js";import"../chunk-qyaqg07q.js";import"../chunk-0h3jr9d9.js";function F(){let z=Promise.resolve(),M=Promise.resolve(),X=(Q,G)=>new Promise(($)=>{Q.write(G,()=>$())});return{stdout:(Q)=>{z=X(process.stdout,Q)},stderr:(Q)=>{M=X(process.stderr,Q)},flush:async()=>{await Promise.all([z,M])}}}function b(z){let M=z.writers??F();return{args:z.args,options:z.options,argv:z.argv,line:(X="")=>M.stdout(`${X}
3
+ `),error:(X)=>M.stderr(`${X}
4
+ `),fail:(X,Q=1)=>{throw new q(X,Q)}}}import w from"path";function V(z){return z.replace(/([a-z0-9])([A-Z])/g,"$1-$2").replace(/([A-Z])([A-Z][a-z])/g,"$1-$2").toLowerCase()}function t(z){let M=new Map,X=new Map;for(let Q of z){M.set(V(Q.name),Q),M.set(Q.name,Q);let G="alias"in Q?Q.alias:void 0;if(G)X.set(G,Q)}return{byName:M,byAlias:X}}function x(z){if(z==="--")return!0;if(!z.startsWith("-"))return!1;if(z==="-")return!1;if(/^-(\d|\.\d)/.test(z))return!1;return!0}function k(z,M){let{byName:X,byAlias:Q}=t(z.options),G=[],$=[],Z=new Map,S=!X.has("help"),C=!Q.has("h"),P=!1,D=!1,I=(J)=>G.push(`Unknown option ${J} for command "${z.commandName}".`),T=(J,Y)=>{if(Y==="--help"||Y==="-h")D=!0;G.push(Y===void 0?`Option ${J} needs a value.`:`Option ${J} needs a value, but the next token is ${JSON.stringify(Y)}, which is another option. If that really is the value, write ${J}=${Y}.`)},_=0;while(_<M.length){let J=M[_];if(J==="--"){$.push(...M.slice(_+1));break}if(J.startsWith("--")){let Y=J.slice(2),j=Y.indexOf("="),B=j===-1?Y:Y.slice(0,j),K=j===-1?void 0:Y.slice(j+1),H=X.get(B),E=!1;if(!H&&B.startsWith("no-")){let W=X.get(B.slice(3));if(W&&W.type==="boolean")H=W,E=!0}if(!H&&B==="help"&&S){P=!0,_+=1;continue}if(!H){I(`--${B}`),_+=1;continue}if(H.type==="boolean"){if(K!==void 0&&K!=="true"&&K!=="false")G.push(`Option --${V(H.name)} is a flag and takes no value, but got ${JSON.stringify(K)}.`);else{let W=K===void 0?!0:K==="true";Z.set(H.name,E?!W:W)}_+=1;continue}if(K!==void 0){Z.set(H.name,K),_+=1;continue}let U=M[_+1];if(U===void 0||x(U)){T(`--${V(H.name)}`,U),_+=1;continue}Z.set(H.name,U),_+=2;continue}if(J.startsWith("-")&&J.length>1){let Y=J.slice(1),j=!1;for(let B=0;B<Y.length;B+=1){let K=Q.get(Y[B]);if(!K){if(Y[B]==="h"&&C){P=!0;continue}I(`-${Y[B]}`);break}if(K.type==="boolean"){Z.set(K.name,!0);continue}let H=Y.slice(B+1),E=H.startsWith("=")?H.slice(1):H;if(E!==""){Z.set(K.name,E);break}let U=M[_+1];if(U===void 0||x(U)){T(`-${K.alias}`,U);break}Z.set(K.name,U),j=!0;break}_+=j?2:1;continue}$.push(J),_+=1}let L={},O=0;for(let J of z.args){if(J.variadic){let j=$.slice(O);if(O=$.length,L[J.name]=j,J.required&&j.length===0)G.push(`Missing required argument <${J.name}...>. Command "${z.commandName}" needs at least one.`);continue}let Y=$[O];if(O+=1,Y!==void 0){L[J.name]=Y;continue}if(J.default!==void 0){L[J.name]=J.default;continue}if(J.required)G.push(`Missing required argument <${J.name}>.`);L[J.name]=void 0}if(O<$.length){let J=$.slice(O);G.push(`Unexpected argument${J.length===1?"":"s"} ${J.map((Y)=>JSON.stringify(Y)).join(", ")}. Command "${z.commandName}" takes ${z.args.length} argument${z.args.length===1?"":"s"}.`)}let R={};for(let J of z.options){let Y=Z.get(J.name);if(J.type==="boolean"){R[J.name]=Y===void 0?J.default??!1:Boolean(Y);continue}if(Y===void 0){if(J.default!==void 0)R[J.name]=J.default;else if(J.required)G.push(`Missing required option --${V(J.name)}.`),R[J.name]=void 0;else R[J.name]=void 0;continue}let j=String(Y);if(J.type==="number"){let B=Number(j);if(j.trim()===""||!Number.isFinite(B))G.push(`Option --${V(J.name)} expects a number, but got ${JSON.stringify(j)}.`),R[J.name]=void 0;else R[J.name]=B;continue}R[J.name]=j}let N=P&&!D;if(G.length>0)return{ok:!1,help:N,errors:G};return{ok:!0,help:N,args:L,options:R}}function f(z,M=" "){let X=z.reduce((Q,[G])=>Math.max(Q,G.length),0);return z.map(([Q,G])=>G===""?`${M}${Q}`:`${M}${Q.padEnd(X)} ${G}`)}function g(z){let M=z.variadic?`${z.name}...`:z.name;return z.required?`<${M}>`:`[${M}]`}function s(z){let M=(G)=>z.options.some(G),X=M((G)=>G.name==="help"||V(G.name)==="help"),Q=M((G)=>("alias"in G)&&G.alias==="h");if(X&&Q)return[];if(X)return[["-h","Show this message"]];return[[`${Q?"":"-h, "}--help`,"Show this message"]]}function v(z){let M=[],X=z.args.map(g).join(" "),Q=["gemi run",z.commandName,z.options.length>0?"[options]":"",X].filter(Boolean).join(" ");if(M.push(`Usage: ${Q}`),z.description)M.push("",z.description);if(z.args.length>0)M.push("","Arguments:"),M.push(...f(z.args.map((G)=>{let $=[G.description??"",G.default!==void 0?`(default: ${JSON.stringify(G.default)})`:""].filter(Boolean).join(" ");return[g(G),$]})));return M.push("","Options:"),M.push(...f([...z.options.map((G)=>{let $="alias"in G&&G.alias?`-${G.alias}, `:"",Z=G.type==="boolean"?"":` <${G.type}>`,S=[G.description??"",G.default!==void 0?`(default: ${JSON.stringify(G.default)})`:"","required"in G&&G.required?"(required)":""].filter(Boolean).join(" ");return[`${$}--${V(G.name)}${Z}`,S]}),...s(z)])),M.join(`
5
+ `)}function y(z,M){let X=M.declared?`${z.length} command${z.length===1?"":"s"} declared in app/config/command.ts:`:`Commands in ${M.dir}:`;if(z.length===0)return M.declared?`app/config/command.ts declares no commands.
6
+
7
+ Remove the \`commands\` key from that slice to discover the classes under ${M.dir} instead \u2014 a declared list wins, and an empty one `+'means "none, and I mean it".':`No commands found in ${M.dir}.
8
+
9
+ A command is registered by existing: write a file there that exports a \`defineCommand(...).handle(...)\` chain and it will appear here.`;let Q=[...z].sort((G,$)=>G.commandName<$.commandName?-1:G.commandName>$.commandName?1:0);return[X,"",...f(Q.map((G)=>[G.commandName,G.description])),"","Run one with `gemi run <name>`, and see its arguments with `gemi run <name> --help`."].join(`
10
+ `)}async function d(z){let M=z.writers??F(),X=(Z)=>M.stdout(`${Z}
11
+ `),Q=(Z)=>M.stderr(`${Z}
12
+ `),G,$;try{G=await n(z.rootDir),G.boot();let Z=G.app.config.get("command",{}),S=u(m(),Z).commandsDir,C=w.isAbsolute(S)?S:w.join(z.rootDir,S),P=Z.commands!==void 0,D=P?Z.commands:await p(C),I=new l(D),T={declared:P,dir:S};if(z.name===void 0)return X(y(I.commands,T)),0;let _=I.get(z.name);if($=_,!_){let J=I.suggest(z.name);return Q(`No command named \`${z.name}\`.`+(J.length>0?` Did you mean ${J.map((Y)=>`\`${Y}\``).join(", or ")}?`:"")),Q(""),Q(y(I.commands,T)),1}let L={commandName:_.commandName,args:_.args,options:_.options},O=k(L,z.argv);if(O.help)return X(v(_)),0;if(O.ok===!1){for(let J of O.errors)Q(J);return Q(""),Q(v(_)),1}await G.waitForBoot();let R=b({args:O.args,options:O.options,argv:z.argv,writers:M}),N=await G.run(()=>Promise.resolve(new _().run(R)));return h(N,_,Q)}catch(Z){if(Z instanceof q)return Q(Z.message),h(Z.code,$,Q,"called fail() with");if(Z instanceof A||Z instanceof c)return Q(Z.message),1;return Q(String(Z?.stack??Z)),1}finally{G?.destroy()}}async function n(z){let M=w.resolve(z,"app/kernel/Kernel.ts"),X;try{X=await import(M)}catch(Q){throw new A(`Could not load ${M}:
13
+ ${Q.message}
14
+ \`gemi run\` boots your application the way the server does, so it needs the Kernel. Run it from the root of a gemi project.`,{cause:Q})}if(typeof X.default!=="function")throw new A(`${M} has no default export. gemi expects the Kernel subclass to be the default export of that file.`);return new X.default}function h(z,M,X,Q="returned"){if(z===void 0||z===null)return 0;if(typeof z==="number"&&Number.isInteger(z)&&z>=0&&z<=255)return z;let G=M?`Command "${M.commandName}"`:"The command";return X(`${G} ${Q} ${JSON.stringify(z)}, which is not an exit `+"code \u2014 an exit code is an integer from 0 to 255. Treating this as a "+"failure."),1}var[a,...o]=process.argv.slice(2),i=F();d({rootDir:process.cwd(),name:a,argv:o,writers:i}).then(async(z)=>{await i.flush(),process.exit(z)});
15
+
16
+ //# debugId=3A559D4973E0E24564756E2164756E21