gemi 0.51.1 → 0.53.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 (211) 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-npeq63a2.js → chunk-07jtzbf0.js} +1 -1
  8. package/dist/{chunk-5v36t9j3.js → chunk-092pj2j2.js} +3 -3
  9. package/dist/{chunk-5v36t9j3.js.map → chunk-092pj2j2.js.map} +1 -1
  10. package/dist/{chunk-zwpx6z5f.js → chunk-0h3jr9d9.js} +2 -2
  11. package/dist/{chunk-8zd6d5nd.js.map → chunk-0h3jr9d9.js.map} +1 -1
  12. package/dist/{chunk-hwwp6x2s.js → chunk-1ab1ptq5.js} +2 -2
  13. package/dist/{chunk-hwwp6x2s.js.map → chunk-1ab1ptq5.js.map} +1 -1
  14. package/dist/chunk-1pqfmw5h.js +5 -0
  15. package/dist/chunk-1pqfmw5h.js.map +30 -0
  16. package/dist/{chunk-dk01r6se.js → chunk-23g7qdpg.js} +2 -2
  17. package/dist/{chunk-dk01r6se.js.map → chunk-23g7qdpg.js.map} +1 -1
  18. package/dist/{chunk-fs091xz7.js → chunk-2gw6cyyj.js} +2 -2
  19. package/dist/{chunk-fs091xz7.js.map → chunk-2gw6cyyj.js.map} +1 -1
  20. package/dist/chunk-2qn472j4.js +5 -0
  21. package/dist/{chunk-8gb1w19q.js.map → chunk-2qn472j4.js.map} +1 -1
  22. package/dist/{chunk-pkzj7c5w.js → chunk-2sfz64xh.js} +2 -2
  23. package/dist/{chunk-pkzj7c5w.js.map → chunk-2sfz64xh.js.map} +1 -1
  24. package/dist/{chunk-q0xq0pt0.js → chunk-2yth3kk2.js} +4 -4
  25. package/dist/{chunk-q0xq0pt0.js.map → chunk-2yth3kk2.js.map} +1 -1
  26. package/dist/chunk-3d3nmr5g.js +4 -0
  27. package/dist/{chunk-6x6gb7zg.js.map → chunk-3d3nmr5g.js.map} +3 -3
  28. package/dist/{chunk-8vnbvfmd.js → chunk-3y9nf09k.js} +2 -2
  29. package/dist/{chunk-8vnbvfmd.js.map → chunk-3y9nf09k.js.map} +1 -1
  30. package/dist/{chunk-8zd6d5nd.js → chunk-4e61qbp4.js} +2 -2
  31. package/dist/{chunk-zwpx6z5f.js.map → chunk-4e61qbp4.js.map} +1 -1
  32. package/dist/{chunk-j33n723q.js → chunk-5s45prj5.js} +3 -3
  33. package/dist/{chunk-j33n723q.js.map → chunk-5s45prj5.js.map} +1 -1
  34. package/dist/{chunk-16bnbtyn.js → chunk-68w225rv.js} +2 -2
  35. package/dist/{chunk-16bnbtyn.js.map → chunk-68w225rv.js.map} +1 -1
  36. package/dist/{chunk-rxeyhwqz.js → chunk-69w4egkg.js} +3 -3
  37. package/dist/{chunk-rxeyhwqz.js.map → chunk-69w4egkg.js.map} +1 -1
  38. package/dist/{chunk-8m3wq94b.js → chunk-6scjrbw7.js} +2 -2
  39. package/dist/{chunk-8m3wq94b.js.map → chunk-6scjrbw7.js.map} +1 -1
  40. package/dist/{chunk-3z4nwzvp.js → chunk-7s1g3v1y.js} +3 -3
  41. package/dist/{chunk-3z4nwzvp.js.map → chunk-7s1g3v1y.js.map} +1 -1
  42. package/dist/{chunk-asgfqcwd.js → chunk-88pq4gmf.js} +2 -2
  43. package/dist/{chunk-asgfqcwd.js.map → chunk-88pq4gmf.js.map} +1 -1
  44. package/dist/{chunk-p0z35s59.js → chunk-8p6rqegw.js} +1 -1
  45. package/dist/chunk-92793zh4.js +5 -0
  46. package/dist/{chunk-98sj7tkf.js.map → chunk-92793zh4.js.map} +3 -4
  47. package/dist/chunk-9279y4af.js +5 -0
  48. package/dist/{chunk-tqbp8550.js.map → chunk-9279y4af.js.map} +2 -2
  49. package/dist/chunk-bq4w14a6.js +5 -0
  50. package/dist/chunk-bq4w14a6.js.map +10 -0
  51. package/dist/{chunk-nbx9mgmr.js → chunk-cg53h5ht.js} +2 -2
  52. package/dist/{chunk-nbx9mgmr.js.map → chunk-cg53h5ht.js.map} +1 -1
  53. package/dist/{chunk-g4gjn481.js → chunk-cmq5t8p9.js} +3 -3
  54. package/dist/{chunk-g4gjn481.js.map → chunk-cmq5t8p9.js.map} +1 -1
  55. package/dist/{chunk-zznpmnp7.js → chunk-d1ahbza9.js} +3 -3
  56. package/dist/{chunk-zznpmnp7.js.map → chunk-d1ahbza9.js.map} +1 -1
  57. package/dist/{chunk-c4r1zh9q.js → chunk-ear03axe.js} +24 -24
  58. package/dist/{chunk-c4r1zh9q.js.map → chunk-ear03axe.js.map} +3 -5
  59. package/dist/{chunk-an6nq9yd.js → chunk-ebcgdv00.js} +3 -3
  60. package/dist/{chunk-an6nq9yd.js.map → chunk-ebcgdv00.js.map} +1 -1
  61. package/dist/{chunk-gvf1d636.js → chunk-faa987e4.js} +2 -2
  62. package/dist/{chunk-gvf1d636.js.map → chunk-faa987e4.js.map} +1 -1
  63. package/dist/{chunk-gdegb5t0.js → chunk-fbp5vapa.js} +2 -2
  64. package/dist/{chunk-gdegb5t0.js.map → chunk-fbp5vapa.js.map} +2 -2
  65. package/dist/chunk-ffre1z8s.js +6 -0
  66. package/dist/{chunk-y0wg8aq9.js.map → chunk-ffre1z8s.js.map} +2 -2
  67. package/dist/{chunk-6wm9bsz9.js → chunk-h5g05qwb.js} +2 -2
  68. package/dist/{chunk-6wm9bsz9.js.map → chunk-h5g05qwb.js.map} +1 -1
  69. package/dist/{chunk-x80n922n.js → chunk-h7jv3ame.js} +2 -2
  70. package/dist/{chunk-x80n922n.js.map → chunk-h7jv3ame.js.map} +1 -1
  71. package/dist/chunk-ha7n1c94.js +7 -0
  72. package/dist/{chunk-bm6q0q1q.js.map → chunk-ha7n1c94.js.map} +3 -3
  73. package/dist/{chunk-3fcgg506.js → chunk-j1knf8gv.js} +3 -3
  74. package/dist/{chunk-3fcgg506.js.map → chunk-j1knf8gv.js.map} +1 -1
  75. package/dist/{chunk-1sbm38g3.js → chunk-kaxvw75w.js} +3 -3
  76. package/dist/{chunk-1sbm38g3.js.map → chunk-kaxvw75w.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-mrf9tjev.js → chunk-qjnv2rkh.js} +2 -2
  88. package/dist/{chunk-mrf9tjev.js.map → chunk-qjnv2rkh.js.map} +1 -1
  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-eby61053.js → chunk-tkv6cw94.js} +3 -3
  98. package/dist/{chunk-eby61053.js.map → chunk-tkv6cw94.js.map} +1 -1
  99. package/dist/chunk-tmk4zfz9.js +33 -0
  100. package/dist/chunk-tmk4zfz9.js.map +18 -0
  101. package/dist/{chunk-k4mjcvsm.js → chunk-ttzkn1qd.js} +2 -2
  102. package/dist/{chunk-k4mjcvsm.js.map → chunk-ttzkn1qd.js.map} +1 -1
  103. package/dist/{chunk-94k8pjmf.js → chunk-tztqkat2.js} +3 -3
  104. package/dist/{chunk-94k8pjmf.js.map → chunk-tztqkat2.js.map} +1 -1
  105. package/dist/{chunk-ybqftmjx.js → chunk-vnjvd82s.js} +2 -2
  106. package/dist/{chunk-ybqftmjx.js.map → chunk-vnjvd82s.js.map} +1 -1
  107. package/dist/{chunk-0k5jhyry.js → chunk-xsmt86n3.js} +2 -2
  108. package/dist/{chunk-0k5jhyry.js.map → chunk-xsmt86n3.js.map} +1 -1
  109. package/dist/chunk-y4djde42.js +5 -0
  110. package/dist/chunk-y4djde42.js.map +11 -0
  111. package/dist/{chunk-ntr81h76.js → chunk-z4q1ty6w.js} +2 -2
  112. package/dist/{chunk-ntr81h76.js.map → chunk-z4q1ty6w.js.map} +1 -1
  113. package/dist/{chunk-53w1njqz.js → chunk-zhsaz9cr.js} +1 -1
  114. package/dist/chunks/ThemeProvider-CTX9LOFC.js +2718 -0
  115. package/dist/chunks/ThemeProvider-CTX9LOFC.js.map +1 -0
  116. package/dist/client/Mutation.d.ts +1 -1
  117. package/dist/client/Mutation.d.ts.map +1 -1
  118. package/dist/client/ThemeProvider.d.ts +7 -0
  119. package/dist/client/ThemeProvider.d.ts.map +1 -1
  120. package/dist/client/index.js +107 -2673
  121. package/dist/client/index.js.map +1 -1
  122. package/dist/client/useIsNavigationPending.d.ts.map +1 -1
  123. package/dist/client/useMutate.d.ts.map +1 -1
  124. package/dist/config/index.js +1 -1
  125. package/dist/console/Command.d.ts +143 -0
  126. package/dist/console/Command.d.ts.map +1 -0
  127. package/dist/console/CommandRegistry.d.ts +46 -0
  128. package/dist/console/CommandRegistry.d.ts.map +1 -0
  129. package/dist/console/builder.d.ts +115 -0
  130. package/dist/console/builder.d.ts.map +1 -0
  131. package/dist/console/builder.test-d.d.ts +2 -0
  132. package/dist/console/builder.test-d.d.ts.map +1 -0
  133. package/dist/console/config.d.ts +40 -0
  134. package/dist/console/config.d.ts.map +1 -0
  135. package/dist/console/context.d.ts +90 -0
  136. package/dist/console/context.d.ts.map +1 -0
  137. package/dist/console/errors.d.ts +19 -0
  138. package/dist/console/errors.d.ts.map +1 -0
  139. package/dist/console/parse.d.ts +65 -0
  140. package/dist/console/parse.d.ts.map +1 -0
  141. package/dist/console/run.d.ts +2 -0
  142. package/dist/console/run.d.ts.map +1 -0
  143. package/dist/console/run.js +16 -0
  144. package/dist/console/run.js.map +14 -0
  145. package/dist/console/runner.d.ts +41 -0
  146. package/dist/console/runner.d.ts.map +1 -0
  147. package/dist/console/usage.d.ts +7 -0
  148. package/dist/console/usage.d.ts.map +1 -0
  149. package/dist/container/index.js +2 -2
  150. package/dist/container/index.js.map +1 -1
  151. package/dist/database/index.js +2 -2
  152. package/dist/database/index.js.map +1 -1
  153. package/dist/email/index.js +2 -2
  154. package/dist/email/index.js.map +2 -2
  155. package/dist/facades/index.js +2 -2
  156. package/dist/facades/index.js.map +1 -1
  157. package/dist/foundation/index.js +2 -2
  158. package/dist/foundation/index.js.map +1 -1
  159. package/dist/http/ViewRouter.d.ts.map +1 -1
  160. package/dist/http/index.js +2 -2
  161. package/dist/http/index.js.map +1 -1
  162. package/dist/i18n/Dictionary.d.ts.map +1 -1
  163. package/dist/i18n/index.js +2 -2
  164. package/dist/i18n/index.js.map +3 -3
  165. package/dist/kernel/Kernel.d.ts +29 -0
  166. package/dist/kernel/Kernel.d.ts.map +1 -1
  167. package/dist/kernel/index.js +3 -3
  168. package/dist/kernel/index.js.map +3 -3
  169. package/dist/orm/index.js +2 -2
  170. package/dist/orm/index.js.map +1 -1
  171. package/dist/server/index.js +2 -2
  172. package/dist/server/index.js.map +1 -1
  173. package/dist/services/cron/ScheduleServiceProvider.d.ts.map +1 -1
  174. package/dist/services/discovery.d.ts +15 -0
  175. package/dist/services/discovery.d.ts.map +1 -1
  176. package/dist/services/index.d.ts +8 -1
  177. package/dist/services/index.d.ts.map +1 -1
  178. package/dist/services/index.js +11 -11
  179. package/dist/services/index.js.map +2 -2
  180. package/dist/support/Service.d.ts +90 -0
  181. package/dist/support/Service.d.ts.map +1 -0
  182. package/dist/support/discover.d.ts +9 -1
  183. package/dist/support/discover.d.ts.map +1 -1
  184. package/dist/support/index.d.ts +1 -0
  185. package/dist/support/index.d.ts.map +1 -1
  186. package/dist/support/index.js +2 -2
  187. package/dist/support/index.js.map +4 -3
  188. package/dist/testing/Page.d.ts +166 -0
  189. package/dist/testing/Page.d.ts.map +1 -0
  190. package/dist/testing/index.d.ts +3 -0
  191. package/dist/testing/index.d.ts.map +1 -0
  192. package/dist/testing/index.js +352 -0
  193. package/dist/testing/index.js.map +1 -0
  194. package/dist/utils/Subject.d.ts.map +1 -1
  195. package/dist/utils/variantKey.d.ts +19 -0
  196. package/dist/utils/variantKey.d.ts.map +1 -0
  197. package/package.json +3 -1
  198. package/dist/chunk-6fym5bfg.js +0 -23
  199. package/dist/chunk-6fym5bfg.js.map +0 -36
  200. package/dist/chunk-6x6gb7zg.js +0 -4
  201. package/dist/chunk-8gb1w19q.js +0 -5
  202. package/dist/chunk-98sj7tkf.js +0 -5
  203. package/dist/chunk-bm6q0q1q.js +0 -7
  204. package/dist/chunk-g0w87jtw.js +0 -5
  205. package/dist/chunk-j4rkk01q.js +0 -5
  206. package/dist/chunk-j4rkk01q.js.map +0 -10
  207. package/dist/chunk-tqbp8550.js +0 -5
  208. package/dist/chunk-y0wg8aq9.js +0 -6
  209. /package/dist/{chunk-npeq63a2.js.map → chunk-07jtzbf0.js.map} +0 -0
  210. /package/dist/{chunk-p0z35s59.js.map → chunk-8p6rqegw.js.map} +0 -0
  211. /package/dist/{chunk-53w1njqz.js.map → chunk-zhsaz9cr.js.map} +0 -0
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Base for the application's own long-lived singletons — an API client, a
3
+ * billing gateway, a search index. A subclass declares a `static token`, holds
4
+ * its settings as ordinary fields, and is listed in `services` on the Kernel;
5
+ * the kernel constructs it once and awaits its `boot()` before the first
6
+ * request is served.
7
+ *
8
+ * ### Why the settings are fields and not a config slice
9
+ *
10
+ * `app/config/*.ts` exists because you do not own the class. `MailManager` and
11
+ * `AuthManager` ship with the framework, so a config slice is the only place
12
+ * you can reach in and change them. You *do* own a `Service` subclass, so there
13
+ * is nothing to reach in from the outside for — a field is a real declaration
14
+ * the compiler checks, where a config slice is a string key and a cast over
15
+ * `Repository.get`, which returns `any`. Use `.with()` to override at the
16
+ * wiring site or in a test.
17
+ *
18
+ * ### Why `boot()` is async and the constructor is not
19
+ *
20
+ * `Container.make` is synchronous, so a lazily-constructed service could never
21
+ * await anything. Construction therefore does no I/O — it runs during the
22
+ * kernel's synchronous phase, which is what makes an instance available to a
23
+ * provider's `boot()` — and everything that needs to await happens in `boot()`,
24
+ * during the asynchronous phase. The cost is that every listed service is
25
+ * constructed and booted on every application boot, including in a worker
26
+ * thread and in every test that boots a kernel. Keep `boot()` cheap: validate
27
+ * settings there, and open connections lazily inside the methods that need
28
+ * them.
29
+ */
30
+ export declare abstract class Service {
31
+ /**
32
+ * The container key. Required, and a string literal rather than the class's
33
+ * implicit `.name`, for the same reason every other service in the framework
34
+ * carries one — see the note on `ServiceToken`. Minification renames the
35
+ * class; it cannot rename a string.
36
+ */
37
+ static token: string;
38
+ /**
39
+ * Awaited once during kernel boot, in `services` order. A service whose
40
+ * `boot()` needs another service must be listed after it.
41
+ */
42
+ boot(): Promise<void>;
43
+ /**
44
+ * The booted singleton for the current application.
45
+ *
46
+ * Written as a constructor default so a controller reads as an ordinary
47
+ * class and stays constructible by hand:
48
+ *
49
+ * class FooController extends Controller {
50
+ * constructor(private billing = Billing.inject()) {
51
+ * super();
52
+ * }
53
+ * }
54
+ *
55
+ * The default is evaluated when the controller is constructed — per request,
56
+ * inside the kernel's async context — so this resolves against the
57
+ * Application handling that request, and a test that passes its own instance
58
+ * never evaluates it at all.
59
+ */
60
+ static inject<T>(this: (abstract new (...args: any[]) => T) & {
61
+ token: string;
62
+ name: string;
63
+ }): T;
64
+ /**
65
+ * A configured instance: constructs the service and assigns over its fields.
66
+ * Typed against the subclass, which a constructor parameter on this base
67
+ * class could not be — there is no `Partial<this>` in a constructor
68
+ * signature.
69
+ *
70
+ * services = [Billing.with({ apiKey: process.env.STAGING_KEY })];
71
+ * const billing = Billing.with({ apiKey: "test" }); // in a test
72
+ *
73
+ * A key whose value is `undefined` is skipped rather than assigned. The first
74
+ * line above is the reason: `process.env.STAGING_KEY` is `undefined` in every
75
+ * environment that does not set it, and `Object.assign` would copy that over
76
+ * the field's declared default — so the service would boot holding
77
+ * `undefined` where it had a perfectly good fallback. An absent value is not
78
+ * an override; write `null` if you mean to clear a field.
79
+ */
80
+ static with<T extends Service>(this: new () => T, overrides: Partial<T>): T;
81
+ }
82
+ /**
83
+ * A `Service` subclass, as the Kernel's `services` array accepts it. The array
84
+ * also takes instances, which is what `.with()` returns.
85
+ */
86
+ export type ServiceConstructor = (new () => Service) & {
87
+ token: string;
88
+ name: string;
89
+ };
90
+ //# sourceMappingURL=Service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Service.d.ts","sourceRoot":"","sources":["../../support/Service.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,8BAAsB,OAAO;IAC3B;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC;IAErB;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAE3B;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,MAAM,CAAC,CAAC,EACb,IAAI,EAAE,CAAC,QAAQ,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,GAAG;QAC3C,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,GACA,CAAC;IAoBJ;;;;;;;;;;;;;;;OAeG;IACH,MAAM,CAAC,IAAI,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,UAAU,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;CAO5E;AAED;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,CAAC,UAAU,OAAO,CAAC,GAAG;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,CAAC"}
@@ -130,11 +130,19 @@ export declare class DiscoveryError extends Error {
130
130
  * where the caller is a scheduler and the alternative is a boot order that
131
131
  * depends on the filesystem.
132
132
  *
133
+ * `onSkipped` is handed every export that did *not* match, with the file it came
134
+ * from. Discovery's characteristic failure is a thing the author wrote and this
135
+ * walk cannot see, and by the time an export has been discarded here the
136
+ * information about why is gone — the caller is left holding "four classes"
137
+ * where the directory holds five. A caller that can recognise its own
138
+ * near-misses, as `discoverCommands` recognises a builder chain that was never
139
+ * finished, uses this to say so by name instead.
140
+ *
133
141
  * @throws DiscoveryError if a walked file cannot be imported. Not a skip: a file
134
142
  * that will not load is a class the caller was going to be told about and now is
135
143
  * not, which is the failure discovery exists to remove.
136
144
  */
137
- export declare function discoverClasses<T extends ClassLike>(dir: string, base: T, ignore?: string[]): Promise<T[]>;
145
+ export declare function discoverClasses<T extends ClassLike>(dir: string, base: T, ignore?: string[], onSkipped?: (value: unknown, file: string) => void): Promise<T[]>;
138
146
  /**
139
147
  * The directory an app's `app/` sits in, as the running process can know it.
140
148
  *
@@ -1 +1 @@
1
- {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../support/discover.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAqCxE;AAED;;;GAGG;AACH,KAAK,SAAS,GAAG,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;AAE5D;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,SAAS,EACvD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,EACP,MAAM,GAAE,MAAM,EAAO,GACpB,OAAO,CAAC,CAAC,EAAE,CAAC,CA0Bd;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../../support/discover.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AAEH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,EAAO,GAAG,MAAM,EAAE,CAqCxE;AAED;;;GAGG;AACH,KAAK,SAAS,GAAG,QAAQ,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK,OAAO,CAAC;AAE5D;;;;;;;;;GASG;AACH,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAI3D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,SAAS,EACvD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,EACP,MAAM,GAAE,MAAM,EAAO,EACrB,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,GACjD,OAAO,CAAC,CAAC,EAAE,CAAC,CAiCd;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,IAAI,MAAM,CAEpC"}
@@ -1,4 +1,5 @@
1
1
  export { ServiceProvider } from "./ServiceProvider";
2
+ export { Service, type ServiceConstructor } from "./Service";
2
3
  export { Repository, type ConfigItems } from "./Repository";
3
4
  export { createConfigRepository, loadConfigFrom } from "./loadConfig";
4
5
  export { withDefaults } from "./withDefaults";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../support/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../support/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAC5D,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,cAAc,EACd,eAAe,EACf,WAAW,EACX,WAAW,GACZ,MAAM,YAAY,CAAC"}
@@ -1,4 +1,4 @@
1
1
  // @bun
2
- import{tb as K,ub as L,vb as M,wb as Q}from"../chunk-bm6q0q1q.js";import{xb as R}from"../chunk-k4mjcvsm.js";import{ue as J}from"../chunk-8vnbvfmd.js";import{Ge as g}from"../chunk-j4rkk01q.js";import"../chunk-zwpx6z5f.js";import{readdir as q}from"fs/promises";import{join as z,parse as c}from"path";var A=[".ts",".tsx",".js",".mjs",".jsx"];function B(f={}){let v={};for(let[C,D]of Object.entries(f))v[C]=j(D);return new g(v)}async function H(f,v=new g){let C=[];try{C=await q(f)}catch(x){return v}let D=C.filter((x)=>{let{ext:F,name:P}=c(x);return A.includes(F)&&!P.startsWith(".")}).sort();for(let x of D){let{name:F}=c(x),P=await import(z(f,x));v.set(F,j(P))}return v}function j(f){if(f&&typeof f==="object"&&"default"in f)return f.default;return f}export{J as withDefaults,K as sourceFiles,Q as projectRoot,H as loadConfigFrom,M as discoverClasses,B as createConfigRepository,R as ServiceProvider,g as Repository,L as DiscoveryError};
2
+ import{Bb as S,Cb as U,Db as V,Eb as W}from"../chunk-ha7n1c94.js";import{Fb as Y}from"../chunk-ttzkn1qd.js";import{Ce as R}from"../chunk-3y9nf09k.js";import{Ke as F,Me as q}from"../chunk-mt2755rs.js";import"../chunk-qyaqg07q.js";import"../chunk-0h3jr9d9.js";class v{static token;async boot(){}static inject(){if(!this.token)throw Error(`Service [${this.name}] does not declare a \`static token\`, so the container has no key to resolve it by.`);try{return q(this)}catch(x){throw Error(`Could not inject [${this.name}]. Services are constructed when the kernel boots, so `+"inject() only works from code that runs after that \u2014 a constructor default or a method "+`body, never module top level. Check that [${this.name}] is listed in \`services\` on your Kernel.`,{cause:x})}}static with(x){let f=new this;for(let[C,g]of Object.entries(x))if(g!==void 0)f[C]=g;return f}}import{readdir as H}from"fs/promises";import{join as J,parse as z}from"path";var L=[".ts",".tsx",".js",".mjs",".jsx"];function M(x={}){let f={};for(let[C,g]of Object.entries(x))f[C]=B(g);return new F(f)}async function Q(x,f=new F){let C=[];try{C=await H(x)}catch(D){return f}let g=C.filter((D)=>{let{ext:P,name:j}=z(D);return L.includes(P)&&!j.startsWith(".")}).sort();for(let D of g){let{name:P}=z(D),j=await import(J(x,D));f.set(P,B(j))}return f}function B(x){if(x&&typeof x==="object"&&"default"in x)return x.default;return x}export{R as withDefaults,S as sourceFiles,W as projectRoot,Q as loadConfigFrom,V as discoverClasses,M as createConfigRepository,Y as ServiceProvider,v as Service,F as Repository,U as DiscoveryError};
3
3
 
4
- //# debugId=57A0058B0002363264756E2164756E21
4
+ //# debugId=94EA4BF19A2C421664756E2164756E21
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../support/loadConfig.ts"],
3
+ "sources": ["../support/Service.ts", "../support/loadConfig.ts"],
4
4
  "sourcesContent": [
5
+ "import type { ServiceToken } from \"../container/Container\";\nimport { app } from \"../foundation/app\";\n\n/**\n * Base for the application's own long-lived singletons — an API client, a\n * billing gateway, a search index. A subclass declares a `static token`, holds\n * its settings as ordinary fields, and is listed in `services` on the Kernel;\n * the kernel constructs it once and awaits its `boot()` before the first\n * request is served.\n *\n * ### Why the settings are fields and not a config slice\n *\n * `app/config/*.ts` exists because you do not own the class. `MailManager` and\n * `AuthManager` ship with the framework, so a config slice is the only place\n * you can reach in and change them. You *do* own a `Service` subclass, so there\n * is nothing to reach in from the outside for — a field is a real declaration\n * the compiler checks, where a config slice is a string key and a cast over\n * `Repository.get`, which returns `any`. Use `.with()` to override at the\n * wiring site or in a test.\n *\n * ### Why `boot()` is async and the constructor is not\n *\n * `Container.make` is synchronous, so a lazily-constructed service could never\n * await anything. Construction therefore does no I/O — it runs during the\n * kernel's synchronous phase, which is what makes an instance available to a\n * provider's `boot()` — and everything that needs to await happens in `boot()`,\n * during the asynchronous phase. The cost is that every listed service is\n * constructed and booted on every application boot, including in a worker\n * thread and in every test that boots a kernel. Keep `boot()` cheap: validate\n * settings there, and open connections lazily inside the methods that need\n * them.\n */\nexport abstract class Service {\n /**\n * The container key. Required, and a string literal rather than the class's\n * implicit `.name`, for the same reason every other service in the framework\n * carries one — see the note on `ServiceToken`. Minification renames the\n * class; it cannot rename a string.\n */\n static token: string;\n\n /**\n * Awaited once during kernel boot, in `services` order. A service whose\n * `boot()` needs another service must be listed after it.\n */\n async boot(): Promise<void> {}\n\n /**\n * The booted singleton for the current application.\n *\n * Written as a constructor default so a controller reads as an ordinary\n * class and stays constructible by hand:\n *\n * class FooController extends Controller {\n * constructor(private billing = Billing.inject()) {\n * super();\n * }\n * }\n *\n * The default is evaluated when the controller is constructed — per request,\n * inside the kernel's async context — so this resolves against the\n * Application handling that request, and a test that passes its own instance\n * never evaluates it at all.\n */\n static inject<T>(\n this: (abstract new (...args: any[]) => T) & {\n token: string;\n name: string;\n },\n ): T {\n if (!this.token) {\n throw new Error(\n `Service [${this.name}] does not declare a \\`static token\\`, so the container has no key to resolve it by.`,\n );\n }\n\n try {\n return app(this as ServiceToken<T>);\n } catch (cause) {\n throw new Error(\n `Could not inject [${this.name}]. Services are constructed when the kernel boots, so ` +\n `inject() only works from code that runs after that — a constructor default or a method ` +\n `body, never module top level. Check that [${this.name}] is listed in \\`services\\` on ` +\n `your Kernel.`,\n { cause },\n );\n }\n }\n\n /**\n * A configured instance: constructs the service and assigns over its fields.\n * Typed against the subclass, which a constructor parameter on this base\n * class could not be — there is no `Partial<this>` in a constructor\n * signature.\n *\n * services = [Billing.with({ apiKey: process.env.STAGING_KEY })];\n * const billing = Billing.with({ apiKey: \"test\" }); // in a test\n *\n * A key whose value is `undefined` is skipped rather than assigned. The first\n * line above is the reason: `process.env.STAGING_KEY` is `undefined` in every\n * environment that does not set it, and `Object.assign` would copy that over\n * the field's declared default — so the service would boot holding\n * `undefined` where it had a perfectly good fallback. An absent value is not\n * an override; write `null` if you mean to clear a field.\n */\n static with<T extends Service>(this: new () => T, overrides: Partial<T>): T {\n const instance = new this();\n for (const [key, value] of Object.entries(overrides)) {\n if (value !== undefined) instance[key] = value;\n }\n return instance;\n }\n}\n\n/**\n * A `Service` subclass, as the Kernel's `services` array accepts it. The array\n * also takes instances, which is what `.with()` returns.\n */\nexport type ServiceConstructor = (new () => Service) & {\n token: string;\n name: string;\n};\n",
5
6
  "import { readdir } from \"node:fs/promises\";\nimport { join, parse } from \"node:path\";\n\nimport { Repository } from \"./Repository\";\n\nconst CONFIG_EXTENSIONS = [\".ts\", \".tsx\", \".js\", \".mjs\", \".jsx\"];\n\n// Builds a Repository from an already-imported map of config modules. The map\n// key becomes the top-level config key, so `{ mail: mailModule }` is readable\n// as `config.get(\"mail.driver\")`. Each entry may be either the module namespace\n// (its `default` export is unwrapped) or the config object itself.\nexport function createConfigRepository(\n modules: Record<string, any> = {},\n): Repository {\n const items: Record<string, any> = {};\n\n for (const [key, mod] of Object.entries(modules)) {\n items[key] = unwrap(mod);\n }\n\n return new Repository(items);\n}\n\n// Reads every config module in an app's `app/config` directory into a\n// Repository. The file's basename is the top-level key: `app/config/mail.ts`\n// populates `mail.*`. A missing directory yields an empty Repository so apps\n// that declare no config still boot.\nexport async function loadConfigFrom(\n directory: string,\n repository = new Repository(),\n): Promise<Repository> {\n let entries: string[] = [];\n\n try {\n entries = await readdir(directory);\n } catch (err) {\n return repository;\n }\n\n const files = entries\n .filter((entry) => {\n const { ext, name } = parse(entry);\n return CONFIG_EXTENSIONS.includes(ext) && !name.startsWith(\".\");\n })\n .sort();\n\n for (const file of files) {\n const { name } = parse(file);\n const mod = await import(join(directory, file));\n repository.set(name, unwrap(mod));\n }\n\n return repository;\n}\n\nfunction unwrap(mod: any) {\n if (mod && typeof mod === \"object\" && \"default\" in mod) {\n return mod.default;\n }\n return mod;\n}\n"
6
7
  ],
7
- "mappings": ";iPAAA,aAAS,eACT,UAAS,MAAM,aAIf,IAAM,EAAoB,CAAC,MAAO,OAAQ,MAAO,OAAQ,MAAM,EAMxD,SAAS,CAAsB,CACpC,EAA+B,CAAC,EACpB,CACZ,IAAM,EAA6B,CAAC,EAEpC,QAAY,EAAK,KAAQ,OAAO,QAAQ,CAAO,EAC7C,EAAM,GAAO,EAAO,CAAG,EAGzB,OAAO,IAAI,EAAW,CAAK,EAO7B,eAAsB,CAAc,CAClC,EACA,EAAa,IAAI,EACI,CACrB,IAAI,EAAoB,CAAC,EAEzB,GAAI,CACF,EAAU,MAAM,EAAQ,CAAS,EACjC,MAAO,EAAK,CACZ,OAAO,EAGT,IAAM,EAAQ,EACX,OAAO,CAAC,IAAU,CACjB,IAAQ,MAAK,QAAS,EAAM,CAAK,EACjC,OAAO,EAAkB,SAAS,CAAG,GAAK,CAAC,EAAK,WAAW,GAAG,EAC/D,EACA,KAAK,EAER,QAAW,KAAQ,EAAO,CACxB,IAAQ,QAAS,EAAM,CAAI,EACrB,EAAM,MAAa,SAAK,EAAW,CAAI,GAC7C,EAAW,IAAI,EAAM,EAAO,CAAG,CAAC,EAGlC,OAAO,EAGT,SAAS,CAAM,CAAC,EAAU,CACxB,GAAI,GAAO,OAAO,IAAQ,UAAY,YAAa,EACjD,OAAO,EAAI,QAEb,OAAO",
8
- "debugId": "57A0058B0002363264756E2164756E21",
8
+ "mappings": ";2RAgCO,CAAe,JAAQ,EAOrB,OAMD,AAAI,EAAkB,QAmBrB,OAAS,EAKX,CACH,GAAI,CAAC,KAAK,MACR,MAAU,MACR,YAAY,KAAK,0FACnB,EAGF,GAAI,CACF,OAAO,EAAI,IAAuB,EAClC,MAAO,EAAO,CACd,MAAU,MACR,qBAAqB,KAAK,6DACxB,+FACA,6CAA6C,KAAK,kDAEpD,CAAE,OAAM,CACV,SAoBG,KAAuB,CAAoB,EAA0B,CAC1E,IAAM,EAAW,IAAI,KACrB,QAAY,EAAK,KAAU,OAAO,QAAQ,CAAS,EACjD,GAAI,IAAU,OAAW,EAAS,GAAO,EAE3C,OAAO,EAEX,CChHA,kBAAS,oBACT,eAAS,WAAM,aAIf,IAAM,EAAoB,CAAC,MAAO,OAAQ,MAAO,OAAQ,MAAM,EAMxD,SAAS,CAAsB,CACpC,EAA+B,CAAC,EACpB,CACZ,IAAM,EAA6B,CAAC,EAEpC,QAAY,EAAK,KAAQ,OAAO,QAAQ,CAAO,EAC7C,EAAM,GAAO,EAAO,CAAG,EAGzB,OAAO,IAAI,EAAW,CAAK,EAO7B,eAAsB,CAAc,CAClC,EACA,EAAa,IAAI,EACI,CACrB,IAAI,EAAoB,CAAC,EAEzB,GAAI,CACF,EAAU,MAAM,EAAQ,CAAS,EACjC,MAAO,EAAK,CACZ,OAAO,EAGT,IAAM,EAAQ,EACX,OAAO,CAAC,IAAU,CACjB,IAAQ,MAAK,QAAS,EAAM,CAAK,EACjC,OAAO,EAAkB,SAAS,CAAG,GAAK,CAAC,EAAK,WAAW,GAAG,EAC/D,EACA,KAAK,EAER,QAAW,KAAQ,EAAO,CACxB,IAAQ,QAAS,EAAM,CAAI,EACrB,EAAM,MAAa,SAAK,EAAW,CAAI,GAC7C,EAAW,IAAI,EAAM,EAAO,CAAG,CAAC,EAGlC,OAAO,EAGT,SAAS,CAAM,CAAC,EAAU,CACxB,GAAI,GAAO,OAAO,IAAQ,UAAY,YAAa,EACjD,OAAO,EAAI,QAEb,OAAO",
9
+ "debugId": "94EA4BF19A2C421664756E2164756E21",
9
10
  "names": []
10
11
  }
@@ -0,0 +1,166 @@
1
+ import { type ComponentType, type PropsWithChildren, type ReactNode } from "react";
2
+ import { type FallbackProps } from "react-error-boundary";
3
+ import type { User } from "../auth/types";
4
+ import { type QueryConfig } from "../client/QueryManagerContext";
5
+ import type { Breadcrumb } from "../client/useBreadcrumbs";
6
+ /**
7
+ * A dictionary as `Dictionary.create` produces it: a name and a
8
+ * `{ key: { locale: string } }` map.
9
+ *
10
+ * Typed structurally rather than as `Dictionary<T>` for two reasons. It keeps
11
+ * `gemi/testing` from importing `gemi/i18n` — a server entry, which reaches the
12
+ * `Lang` facade and through it the container. And it means an object literal of
13
+ * the same shape is accepted, so a test never *has* to import the app's
14
+ * dictionaries at all.
15
+ *
16
+ * Only `name` and `dictionary` are read — both plain data. `Dictionary`'s
17
+ * server-only methods (`render`, `reference`, which throw once `window` is
18
+ * defined) are never called from here.
19
+ */
20
+ export interface PageDictionary {
21
+ name: string;
22
+ dictionary: Record<string, Record<string, string>>;
23
+ }
24
+ export interface PageProps {
25
+ /**
26
+ * The URL the view is mounted at. A template (`/app/:orgId/chat`) is resolved
27
+ * against `params`, so the same string can be pasted from the router.
28
+ */
29
+ pathname?: string;
30
+ /** Route params, as the router would have parsed them out of `pathname`. */
31
+ params?: Record<string, string>;
32
+ /** `"?tab=recent"`, `"tab=recent"`, or `{ tab: "recent" }`. */
33
+ searchParams?: string | Record<string, string | number | boolean>;
34
+ hash?: string;
35
+ /** The locale `useTranslator` and `useLocale` report. */
36
+ locale?: string;
37
+ /**
38
+ * The app's default locale. Links and `useNavigate` omit the locale segment
39
+ * for it, so leaving this equal to `locale` (the default) keeps hrefs
40
+ * unprefixed — set it to seed a page being viewed in a non-default locale.
41
+ */
42
+ defaultLocale?: string;
43
+ supportedLocales?: string[];
44
+ /**
45
+ * Dictionaries the components under test translate against — the app's own,
46
+ * or literals of the same shape.
47
+ *
48
+ * Note what importing the app's costs: `app/i18n/index.ts` imports
49
+ * `gemi/i18n`, a server entry, so the test file pulls the container and
50
+ * `node:async_hooks` into its module graph. Nothing there runs (and nothing
51
+ * here calls it), so it is fine under any runner that has Node builtins —
52
+ * vitest, `bun test` — and not under one that renders in a real browser. Use
53
+ * `translations` there, which is the shape the client actually receives.
54
+ */
55
+ dictionaries?: PageDictionary[];
56
+ /**
57
+ * Translations for the current locale, already resolved: dictionary name →
58
+ * key → string. This is the shape the server serializes onto the page, so it
59
+ * is what the client reads at runtime — and it imports nothing.
60
+ *
61
+ * ```tsx
62
+ * <Page translations={{ Chat: { greeting: "Hello {{name}}" } }}>
63
+ * ```
64
+ *
65
+ * Merged over `dictionaries`, so the two compose: seed from the app's real
66
+ * dictionary and override the one key a test is about.
67
+ */
68
+ translations?: Record<string, Record<string, string>>;
69
+ /**
70
+ * Data `useQuery` finds already cached, keyed by API path — the path passed
71
+ * to `useQuery`, without the `/api` prefix the client adds when it fetches.
72
+ * A key may carry a query string (`"/lists?page=2"`) to seed one search
73
+ * variant.
74
+ *
75
+ * The cache is keyed by the *resolved* path, so a key's `:params` are
76
+ * resolved against the page's `params` — and a key naming one the page does
77
+ * not carry is an error, naming the key. That case is common enough to be
78
+ * worth the loud failure: a query often takes its params from the call site
79
+ * rather than the route (`useQuery("/orgs/:orgId/lists", { params: { orgId } })`
80
+ * with `orgId` in component state), and the right seed for it is the resolved
81
+ * path, `"/orgs/abc/lists"`.
82
+ *
83
+ * Seeded at mount only: a component that mutates or refetches its data owns
84
+ * the cache from then on.
85
+ */
86
+ queryData?: Record<string, unknown>;
87
+ /** App-wide `useQuery` defaults, as `createRoot` threads them. */
88
+ queryConfig?: QueryConfig;
89
+ /**
90
+ * What `useUser()` reports. Defaults to `null` — an anonymous visitor — and
91
+ * either way the hook resolves from here without issuing a request.
92
+ */
93
+ user?: Partial<User> | null;
94
+ /** What `useBreadcrumbs()` returns, in order. */
95
+ breadcrumbs?: Breadcrumb[];
96
+ /**
97
+ * The theme `useTheme()` starts on. The app reads a visitor's stored choice
98
+ * out of `localStorage`; a test has no session to have made one in, so this
99
+ * seeds it without writing to storage. `setTheme` works from either.
100
+ */
101
+ theme?: "light" | "dark" | "system";
102
+ /**
103
+ * Fallback for the `Suspense` boundary wrapped around the children — the
104
+ * stand-in for the view's own `Loading` export, which the real router
105
+ * supplies. Defaults to `null`.
106
+ */
107
+ fallback?: ReactNode;
108
+ /**
109
+ * Renders in place of the children when one throws, mirroring a view's
110
+ * `Error` export. Without it a thrown render (an errored suspense query,
111
+ * say) propagates out of `render()` and fails the test, which is usually
112
+ * what you want.
113
+ */
114
+ errorFallback?: ComponentType<FallbackProps>;
115
+ /**
116
+ * Called when something navigates — a `Link` click, `useNavigate().push`.
117
+ * The navigation is reported, not performed: `<Page>` renders one route and
118
+ * does not re-resolve the tree, so `useLocation()` keeps reporting the
119
+ * pathname it was given.
120
+ */
121
+ onNavigate?: (href: string, action: "push" | "replace") => void;
122
+ }
123
+ /**
124
+ * Mounts a component with the inputs a view normally arrives with — route
125
+ * params, the current locale and its dictionaries, prefetched query data, the
126
+ * signed-in user — so a unit test can assert what it renders rather than only
127
+ * its empty state.
128
+ *
129
+ * ```tsx
130
+ * import { render, screen } from "@testing-library/react";
131
+ * import { Page } from "gemi/testing";
132
+ *
133
+ * render(
134
+ * <Page
135
+ * pathname="/app/:orgId/chat"
136
+ * params={{ orgId: "abc" }}
137
+ * queryData={{ "/organizations/:orgId/messages": [{ id: 1, body: "hi" }] }}
138
+ * dictionaries={[ChatDictionary]}
139
+ * >
140
+ * <OrgChat />
141
+ * </Page>,
142
+ * );
143
+ *
144
+ * expect(screen.getByText("hi")).toBeDefined();
145
+ * ```
146
+ *
147
+ * It is a plain component, so it composes with any renderer — Testing
148
+ * Library's `render`, `react-test-renderer`, or `renderToString`.
149
+ *
150
+ * Two things it does not do.
151
+ *
152
+ * It does not route: there is no view tree and no route manifest behind it, so
153
+ * a navigation is reported through `onNavigate` rather than resolved. To
154
+ * assert the page *after* one, render a second `<Page>`.
155
+ *
156
+ * And under `renderToString` it renders the server's own no-data branch for an
157
+ * *unseeded* query rather than suspending — deliberately, because suspending
158
+ * there is the streaming server's job: `createRoot` threads the request's
159
+ * `ServerQueryStore` through `ServerQueryContext`, and that store is what runs
160
+ * the route handler. A harness has no request and no handler to run, so there
161
+ * is nothing to suspend on. Seeded queries render their data on the server
162
+ * exactly as they do in the browser; unseeded ones behave as a route with no
163
+ * `Query.prefetch` does, framework warning included.
164
+ */
165
+ export declare const Page: (props: PropsWithChildren<PageProps>) => import("react/jsx-runtime").JSX.Element;
166
+ //# sourceMappingURL=Page.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Page.d.ts","sourceRoot":"","sources":["../../testing/Page.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAGzE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAI1C,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAYvC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAM3D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,SAAS;IACxB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,kEAAkE;IAClE,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B;;;OAGG;IACH,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IAC5B,iDAAiD;IACjD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;IACpC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,CAAC;IAC7C;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,KAAK,IAAI,CAAC;CACjE;AA6ID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,IAAI,GAAI,OAAO,iBAAiB,CAAC,SAAS,CAAC,4CA4PvD,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { Page } from "./Page";
2
+ export type { PageProps, PageDictionary } from "./Page";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9B,YAAY,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC"}