gemi 0.44.1 → 0.46.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 (162) hide show
  1. package/dist/app/index.js +1 -1
  2. package/dist/bin/gemi.js +185 -49
  3. package/dist/bin/gemi.js.map +10 -7
  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-5kx4hw7c.js → chunk-0r110r6m.js} +3 -3
  8. package/dist/{chunk-5kx4hw7c.js.map → chunk-0r110r6m.js.map} +1 -1
  9. package/dist/{chunk-ytr22fm4.js → chunk-1nkxmecw.js} +3 -3
  10. package/dist/{chunk-ytr22fm4.js.map → chunk-1nkxmecw.js.map} +1 -1
  11. package/dist/{chunk-20376aj6.js → chunk-27tkdqp6.js} +4 -4
  12. package/dist/{chunk-20376aj6.js.map → chunk-27tkdqp6.js.map} +1 -1
  13. package/dist/{chunk-3xqyzegw.js → chunk-2a4ze3n3.js} +2 -2
  14. package/dist/{chunk-3xqyzegw.js.map → chunk-2a4ze3n3.js.map} +1 -1
  15. package/dist/{chunk-sdj112nj.js → chunk-2cew23ww.js} +3 -3
  16. package/dist/{chunk-sdj112nj.js.map → chunk-2cew23ww.js.map} +1 -1
  17. package/dist/{chunk-nhyc69tx.js → chunk-2pazt3hc.js} +2 -2
  18. package/dist/{chunk-nhyc69tx.js.map → chunk-2pazt3hc.js.map} +1 -1
  19. package/dist/{chunk-fksvw2gz.js → chunk-3ervqphj.js} +2 -2
  20. package/dist/{chunk-fksvw2gz.js.map → chunk-3ervqphj.js.map} +1 -1
  21. package/dist/{chunk-8b461343.js → chunk-4k7p344e.js} +2 -2
  22. package/dist/{chunk-8b461343.js.map → chunk-4k7p344e.js.map} +1 -1
  23. package/dist/{chunk-whj1a3vr.js → chunk-4z53shav.js} +2 -2
  24. package/dist/{chunk-whj1a3vr.js.map → chunk-4z53shav.js.map} +1 -1
  25. package/dist/{chunk-y133781b.js → chunk-5hggqap1.js} +51 -24
  26. package/dist/{chunk-y133781b.js.map → chunk-5hggqap1.js.map} +9 -7
  27. package/dist/{chunk-jvcbdxtq.js → chunk-6av6dnxx.js} +2 -2
  28. package/dist/{chunk-jvcbdxtq.js.map → chunk-6av6dnxx.js.map} +1 -1
  29. package/dist/{chunk-ngnrytfh.js → chunk-7bggj8g7.js} +3 -3
  30. package/dist/{chunk-ngnrytfh.js.map → chunk-7bggj8g7.js.map} +1 -1
  31. package/dist/{chunk-x2cq1qp8.js → chunk-8jns0zmx.js} +2 -2
  32. package/dist/{chunk-x2cq1qp8.js.map → chunk-8jns0zmx.js.map} +1 -1
  33. package/dist/{chunk-knrx23te.js → chunk-a3bmbfn3.js} +2 -2
  34. package/dist/{chunk-knrx23te.js.map → chunk-a3bmbfn3.js.map} +1 -1
  35. package/dist/{chunk-fvtfwssb.js → chunk-b0ca3dzf.js} +2 -2
  36. package/dist/{chunk-fvtfwssb.js.map → chunk-b0ca3dzf.js.map} +1 -1
  37. package/dist/{chunk-kkdtznm5.js → chunk-cmnhs4mh.js} +3 -3
  38. package/dist/{chunk-kkdtznm5.js.map → chunk-cmnhs4mh.js.map} +1 -1
  39. package/dist/chunk-d9k245jt.js +79 -0
  40. package/dist/{chunk-q8haepmm.js.map → chunk-d9k245jt.js.map} +10 -6
  41. package/dist/chunk-evkv0xa7.js +4 -0
  42. package/dist/{chunk-fk9xtngd.js.map → chunk-evkv0xa7.js.map} +6 -6
  43. package/dist/{chunk-6gtmkbh0.js → chunk-f18weffa.js} +2 -2
  44. package/dist/{chunk-6gtmkbh0.js.map → chunk-f18weffa.js.map} +1 -1
  45. package/dist/{chunk-9s3te5pa.js → chunk-jrnbyhgq.js} +4 -4
  46. package/dist/{chunk-9s3te5pa.js.map → chunk-jrnbyhgq.js.map} +1 -1
  47. package/dist/{chunk-sdx8r2qb.js → chunk-m0earq80.js} +2 -2
  48. package/dist/{chunk-sdx8r2qb.js.map → chunk-m0earq80.js.map} +1 -1
  49. package/dist/{chunk-errq965d.js → chunk-mped4eh6.js} +3 -3
  50. package/dist/{chunk-errq965d.js.map → chunk-mped4eh6.js.map} +1 -1
  51. package/dist/{chunk-mfwbdztp.js → chunk-n4wrg0sn.js} +2 -2
  52. package/dist/{chunk-drbqadx7.js.map → chunk-n4wrg0sn.js.map} +1 -1
  53. package/dist/{chunk-ty3q916e.js → chunk-p5r0t9jh.js} +3 -3
  54. package/dist/{chunk-ty3q916e.js.map → chunk-p5r0t9jh.js.map} +1 -1
  55. package/dist/chunk-pf4r36w2.js +5 -0
  56. package/dist/{chunk-tatd75jn.js.map → chunk-pf4r36w2.js.map} +1 -1
  57. package/dist/{chunk-revm9qww.js → chunk-qmsngk2y.js} +1 -1
  58. package/dist/{chunk-m359awjw.js → chunk-r64ba1pf.js} +2 -2
  59. package/dist/{chunk-m359awjw.js.map → chunk-r64ba1pf.js.map} +1 -1
  60. package/dist/{chunk-s49529x2.js → chunk-snp18f2m.js} +3 -3
  61. package/dist/{chunk-s49529x2.js.map → chunk-snp18f2m.js.map} +1 -1
  62. package/dist/{chunk-nx8t65g3.js → chunk-sv4v623e.js} +3 -3
  63. package/dist/{chunk-nx8t65g3.js.map → chunk-sv4v623e.js.map} +1 -1
  64. package/dist/{chunk-cbvq4keh.js → chunk-tjzbw6eg.js} +2 -2
  65. package/dist/{chunk-cbvq4keh.js.map → chunk-tjzbw6eg.js.map} +1 -1
  66. package/dist/{chunk-9cw39se2.js → chunk-tyjhatbq.js} +2 -2
  67. package/dist/{chunk-9cw39se2.js.map → chunk-tyjhatbq.js.map} +1 -1
  68. package/dist/{chunk-wrp2v923.js → chunk-vh6dnbty.js} +2 -2
  69. package/dist/{chunk-wrp2v923.js.map → chunk-vh6dnbty.js.map} +1 -1
  70. package/dist/{chunk-w7czqgx2.js → chunk-vpqq7v9e.js} +2 -2
  71. package/dist/{chunk-w7czqgx2.js.map → chunk-vpqq7v9e.js.map} +1 -1
  72. package/dist/{chunk-32ct42e2.js → chunk-w54fj10r.js} +1 -1
  73. package/dist/{chunk-ywhazhqt.js → chunk-wed042aw.js} +2 -2
  74. package/dist/{chunk-ywhazhqt.js.map → chunk-wed042aw.js.map} +1 -1
  75. package/dist/{chunk-gv4v6qsg.js → chunk-wjhh4pf0.js} +3 -3
  76. package/dist/{chunk-gv4v6qsg.js.map → chunk-wjhh4pf0.js.map} +1 -1
  77. package/dist/{chunk-qy3aczt9.js → chunk-wtydwxgg.js} +3 -3
  78. package/dist/{chunk-qy3aczt9.js.map → chunk-wtydwxgg.js.map} +1 -1
  79. package/dist/{chunk-bsfbba90.js → chunk-ww5vyb4s.js} +2 -2
  80. package/dist/{chunk-bsfbba90.js.map → chunk-ww5vyb4s.js.map} +1 -1
  81. package/dist/{chunk-h509r9s3.js → chunk-xdk6y558.js} +2 -2
  82. package/dist/{chunk-h509r9s3.js.map → chunk-xdk6y558.js.map} +1 -1
  83. package/dist/{chunk-9r2k5ykk.js → chunk-xkrthw08.js} +3 -3
  84. package/dist/{chunk-9r2k5ykk.js.map → chunk-xkrthw08.js.map} +1 -1
  85. package/dist/{chunk-87keq057.js → chunk-y4q11zr2.js} +2 -2
  86. package/dist/{chunk-87keq057.js.map → chunk-y4q11zr2.js.map} +1 -1
  87. package/dist/chunk-yyn0qk1w.js +4 -0
  88. package/dist/{chunk-mfwbdztp.js.map → chunk-yyn0qk1w.js.map} +1 -1
  89. package/dist/{chunk-ks0xswsd.js → chunk-zp9ybbp6.js} +2 -2
  90. package/dist/{chunk-ks0xswsd.js.map → chunk-zp9ybbp6.js.map} +2 -2
  91. package/dist/client/ClientRouter.d.ts.map +1 -1
  92. package/dist/client/Head.d.ts.map +1 -1
  93. package/dist/client/QueryManagerContext.d.ts +5 -2
  94. package/dist/client/QueryManagerContext.d.ts.map +1 -1
  95. package/dist/client/QueryResource.d.ts +23 -1
  96. package/dist/client/QueryResource.d.ts.map +1 -1
  97. package/dist/client/helpers/mergeCarriedSegments.d.ts +23 -0
  98. package/dist/client/helpers/mergeCarriedSegments.d.ts.map +1 -0
  99. package/dist/client/index.js +173 -27
  100. package/dist/client/index.js.map +1 -1
  101. package/dist/client/useQuery.d.ts +2 -0
  102. package/dist/client/useQuery.d.ts.map +1 -1
  103. package/dist/config/index.js +1 -1
  104. package/dist/email/index.js +2 -2
  105. package/dist/email/index.js.map +1 -1
  106. package/dist/facades/RateLimiter.d.ts +23 -0
  107. package/dist/facades/RateLimiter.d.ts.map +1 -0
  108. package/dist/facades/index.d.ts +1 -0
  109. package/dist/facades/index.d.ts.map +1 -1
  110. package/dist/facades/index.js +2 -2
  111. package/dist/facades/index.js.map +1 -1
  112. package/dist/http/Metadata.d.ts +6 -0
  113. package/dist/http/Metadata.d.ts.map +1 -1
  114. package/dist/http/RateLimitMiddleware.d.ts +58 -2
  115. package/dist/http/RateLimitMiddleware.d.ts.map +1 -1
  116. package/dist/http/ViewRouter.d.ts +12 -0
  117. package/dist/http/ViewRouter.d.ts.map +1 -1
  118. package/dist/http/index.d.ts +1 -1
  119. package/dist/http/index.d.ts.map +1 -1
  120. package/dist/http/index.js +2 -2
  121. package/dist/http/index.js.map +1 -1
  122. package/dist/i18n/index.js +1 -1
  123. package/dist/kernel/index.js +2 -2
  124. package/dist/kernel/index.js.map +2 -2
  125. package/dist/server/index.js +2 -2
  126. package/dist/server/index.js.map +1 -1
  127. package/dist/services/index.d.ts +6 -3
  128. package/dist/services/index.d.ts.map +1 -1
  129. package/dist/services/index.js +2 -2
  130. package/dist/services/index.js.map +1 -1
  131. package/dist/services/rate-limiter/RateLimiterServiceContainer.d.ts +17 -1
  132. package/dist/services/rate-limiter/RateLimiterServiceContainer.d.ts.map +1 -1
  133. package/dist/services/rate-limiter/RateLimiterServiceProvider.d.ts +8 -0
  134. package/dist/services/rate-limiter/RateLimiterServiceProvider.d.ts.map +1 -1
  135. package/dist/services/rate-limiter/drivers/InMemoryRateLimiterDriver.d.ts +31 -5
  136. package/dist/services/rate-limiter/drivers/InMemoryRateLimiterDriver.d.ts.map +1 -1
  137. package/dist/services/rate-limiter/drivers/RateLimiterDriver.d.ts +11 -1
  138. package/dist/services/rate-limiter/drivers/RateLimiterDriver.d.ts.map +1 -1
  139. package/dist/services/rate-limiter/drivers/RedisRateLimiterDriver.d.ts +59 -0
  140. package/dist/services/rate-limiter/drivers/RedisRateLimiterDriver.d.ts.map +1 -0
  141. package/dist/services/rate-limiter/slidingWindow.d.ts +55 -0
  142. package/dist/services/rate-limiter/slidingWindow.d.ts.map +1 -0
  143. package/dist/services/rate-limiter/types.d.ts +35 -1
  144. package/dist/services/rate-limiter/types.d.ts.map +1 -1
  145. package/dist/services/router/ViewRouterServiceContainer.d.ts.map +1 -1
  146. package/dist/services/router/ViewRouterServiceProvider.d.ts +10 -0
  147. package/dist/services/router/ViewRouterServiceProvider.d.ts.map +1 -1
  148. package/dist/services/router/createFlatViewRoutes.d.ts +16 -0
  149. package/dist/services/router/createFlatViewRoutes.d.ts.map +1 -1
  150. package/dist/services/router/matchViewRoute.d.ts +12 -0
  151. package/dist/services/router/matchViewRoute.d.ts.map +1 -0
  152. package/dist/services/router/planPartialRender.d.ts +43 -0
  153. package/dist/services/router/planPartialRender.d.ts.map +1 -0
  154. package/dist/utils/partialRender.d.ts +28 -0
  155. package/dist/utils/partialRender.d.ts.map +1 -0
  156. package/package.json +1 -1
  157. package/dist/chunk-drbqadx7.js +0 -4
  158. package/dist/chunk-fk9xtngd.js +0 -4
  159. package/dist/chunk-q8haepmm.js +0 -79
  160. package/dist/chunk-tatd75jn.js +0 -5
  161. /package/dist/{chunk-revm9qww.js.map → chunk-qmsngk2y.js.map} +0 -0
  162. /package/dist/{chunk-32ct42e2.js.map → chunk-w54fj10r.js.map} +0 -0
@@ -1,8 +1,24 @@
1
1
  import { ServiceContainer } from "../ServiceContainer";
2
- import { RateLimiterServiceProvider } from "./RateLimiterServiceProvider";
2
+ import type { RateLimiterServiceProvider } from "./RateLimiterServiceProvider";
3
+ import type { RateLimitResult } from "./types";
4
+ export interface ConsumeOptions {
5
+ /** Requests allowed per window. Defaults to the provider's `limit`. */
6
+ limit?: number;
7
+ /** Window length in **seconds**. Defaults to the provider's `window`. */
8
+ window?: number;
9
+ /** How much this call costs. Defaults to `1`. */
10
+ cost?: number;
11
+ }
3
12
  export declare class RateLimiterServiceContainer extends ServiceContainer {
4
13
  service: RateLimiterServiceProvider;
5
14
  static _name: string;
6
15
  constructor(service: RateLimiterServiceProvider);
16
+ /**
17
+ * Records one hit against `key` and reports whether it fits the budget.
18
+ *
19
+ * Windows are given in seconds here — the unit routes, middleware and config
20
+ * are written in — and converted to the milliseconds drivers work in.
21
+ */
22
+ consume(key: string, options?: ConsumeOptions): Promise<RateLimitResult>;
7
23
  }
8
24
  //# sourceMappingURL=RateLimiterServiceContainer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RateLimiterServiceContainer.d.ts","sourceRoot":"","sources":["../../../services/rate-limiter/RateLimiterServiceContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,qBAAa,2BAA4B,SAAQ,gBAAgB;IAG5C,OAAO,EAAE,0BAA0B;IAFtD,MAAM,CAAC,KAAK,SAAiC;gBAE1B,OAAO,EAAE,0BAA0B;CAGvD"}
1
+ {"version":3,"file":"RateLimiterServiceContainer.d.ts","sourceRoot":"","sources":["../../../services/rate-limiter/RateLimiterServiceContainer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAC/E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,MAAM,WAAW,cAAc;IAC7B,uEAAuE;IACvE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,qBAAa,2BAA4B,SAAQ,gBAAgB;IAG5C,OAAO,EAAE,0BAA0B;IAFtD,MAAM,CAAC,KAAK,SAAiC;gBAE1B,OAAO,EAAE,0BAA0B;IAItD;;;;;OAKG;IACG,OAAO,CACX,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,cAAmB,GAC3B,OAAO,CAAC,eAAe,CAAC;CAsB5B"}
@@ -1,7 +1,15 @@
1
1
  import { ServiceProvider } from "../ServiceProvider";
2
2
  import { RateLimiterDriver } from "./drivers/RateLimiterDriver";
3
3
  export declare class RateLimiterServiceProvider extends ServiceProvider {
4
+ /**
5
+ * Where counters live. In-process by default; swap in `RedisRateLimiter` once
6
+ * the app runs on more than one instance.
7
+ */
4
8
  driver: RateLimiterDriver;
9
+ /** Requests allowed per window when the `rate-limit` DSL omits a limit. */
10
+ limit: number;
11
+ /** Window length in **seconds** when the `rate-limit` DSL omits one. */
12
+ window: number;
5
13
  boot(): void;
6
14
  }
7
15
  //# sourceMappingURL=RateLimiterServiceProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RateLimiterServiceProvider.d.ts","sourceRoot":"","sources":["../../../services/rate-limiter/RateLimiterServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,qBAAa,0BAA2B,SAAQ,eAAe;IAC7D,MAAM,EAAE,iBAAiB,CAA6B;IAEtD,IAAI;CACL"}
1
+ {"version":3,"file":"RateLimiterServiceProvider.d.ts","sourceRoot":"","sources":["../../../services/rate-limiter/RateLimiterServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AAEhE,qBAAa,0BAA2B,SAAQ,eAAe;IAC7D;;;OAGG;IACH,MAAM,EAAE,iBAAiB,CAA6B;IAEtD,2EAA2E;IAC3E,KAAK,SAAQ;IAEb,wEAAwE;IACxE,MAAM,SAAM;IAEZ,IAAI;CACL"}
@@ -1,9 +1,35 @@
1
+ import type { ConsumeParams, RateLimitResult } from "../types";
1
2
  import { RateLimiterDriver } from "./RateLimiterDriver";
3
+ export interface InMemoryRateLimiterOptions {
4
+ /**
5
+ * Hard cap on tracked keys. Reached only by traffic that never repeats a key
6
+ * (spoofed `x-forwarded-for`, for instance); the map is swept of dead buckets
7
+ * first and only then evicts live ones, oldest touched first.
8
+ *
9
+ * Defaults to 100k keys — a few MB, and far more than a single instance
10
+ * legitimately sees inside one window.
11
+ */
12
+ maxKeys?: number;
13
+ }
14
+ /**
15
+ * Per-process rate limiter. The default driver, and the right one for a single
16
+ * instance or for development.
17
+ *
18
+ * State lives in this process only: with N instances behind a load balancer the
19
+ * effective limit is N × the configured limit, and it resets on deploy. Point
20
+ * the service provider at `RedisRateLimiter` once you run more than one.
21
+ */
2
22
  export declare class InMemoryRateLimiter extends RateLimiterDriver {
3
- requests: Map<string, {
4
- date: number;
5
- count: number;
6
- }>;
7
- consume(id: string, path: string): number;
23
+ private buckets;
24
+ private maxKeys;
25
+ constructor(options?: InMemoryRateLimiterOptions);
26
+ consume(params: ConsumeParams): RateLimitResult;
27
+ /** Drops all counters. Mainly useful in tests. */
28
+ clear(): void;
29
+ /** Number of tracked keys. Exposed for tests and diagnostics. */
30
+ get size(): number;
31
+ /** Advances a bucket to `windowStart`, creating it when absent or stale. */
32
+ private roll;
33
+ private evict;
8
34
  }
9
35
  //# sourceMappingURL=InMemoryRateLimiterDriver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InMemoryRateLimiterDriver.d.ts","sourceRoot":"","sources":["../../../../services/rate-limiter/drivers/InMemoryRateLimiterDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,qBAAa,mBAAoB,SAAQ,iBAAiB;IACxD,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAa;IAEnE,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;CAejC"}
1
+ {"version":3,"file":"InMemoryRateLimiterDriver.d.ts","sourceRoot":"","sources":["../../../../services/rate-limiter/drivers/InMemoryRateLimiterDriver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAUxD,MAAM,WAAW,0BAA0B;IACzC;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,qBAAa,mBAAoB,SAAQ,iBAAiB;IACxD,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,GAAE,0BAA+B;IAKpD,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,eAAe;IAwC/C,kDAAkD;IAClD,KAAK;IAIL,iEAAiE;IACjE,IAAI,IAAI,WAEP;IAED,4EAA4E;IAC5E,OAAO,CAAC,IAAI;IAyBZ,OAAO,CAAC,KAAK;CAgBd"}
@@ -1,4 +1,14 @@
1
+ import type { ConsumeParams, RateLimitResult } from "../types";
1
2
  export declare abstract class RateLimiterDriver {
2
- abstract consume(userId: string, requestPath: string): number;
3
+ /**
4
+ * Records one request against `params.key` and reports whether it fits.
5
+ *
6
+ * Implementations must decide *and* record atomically — two concurrent calls
7
+ * for the same key may never both be told they were the last one that fit.
8
+ *
9
+ * May be sync or async: the in-memory driver answers immediately, a network
10
+ * backed driver returns a promise. Callers always `await` the result.
11
+ */
12
+ abstract consume(params: ConsumeParams): Promise<RateLimitResult> | RateLimitResult;
3
13
  }
4
14
  //# sourceMappingURL=RateLimiterDriver.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RateLimiterDriver.d.ts","sourceRoot":"","sources":["../../../../services/rate-limiter/drivers/RateLimiterDriver.ts"],"names":[],"mappings":"AAAA,8BAAsB,iBAAiB;IACrC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM;CAC9D"}
1
+ {"version":3,"file":"RateLimiterDriver.d.ts","sourceRoot":"","sources":["../../../../services/rate-limiter/drivers/RateLimiterDriver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAE/D,8BAAsB,iBAAiB;IACrC;;;;;;;;OAQG;IACH,QAAQ,CAAC,OAAO,CACd,MAAM,EAAE,aAAa,GACpB,OAAO,CAAC,eAAe,CAAC,GAAG,eAAe;CAC9C"}
@@ -0,0 +1,59 @@
1
+ import type { ConsumeParams, RateLimitResult } from "../types";
2
+ import { RateLimiterDriver } from "./RateLimiterDriver";
3
+ /**
4
+ * The slice of Bun's `RedisClient` this driver needs. Narrow on purpose: any
5
+ * client exposing `send(command, args)` works, which keeps the driver testable
6
+ * without a live server.
7
+ */
8
+ export interface RateLimiterRedisClient {
9
+ send(command: string, args: string[]): Promise<any>;
10
+ }
11
+ export interface RedisRateLimiterOptions {
12
+ /**
13
+ * Client to run against. Defaults to the app's shared client from
14
+ * `RedisServiceProvider`, resolved lazily so constructing the driver never
15
+ * requires a kernel or a connection.
16
+ */
17
+ client?: RateLimiterRedisClient;
18
+ /** Key namespace. Defaults to `gemi:rl`. */
19
+ prefix?: string;
20
+ /**
21
+ * What to do when Redis is unreachable. `true` (the default) admits the
22
+ * request — a limiter outage should not take the site down with it. Set
23
+ * `false` for endpoints where an unmetered request is worse than a rejected
24
+ * one (payments, sign-up, anything that costs money per call).
25
+ */
26
+ failOpen?: boolean;
27
+ /** Called with every Redis failure. Defaults to `console.error`. */
28
+ onError?: (error: unknown) => void;
29
+ }
30
+ /**
31
+ * Rate limiter backed by Redis, so every instance of the app counts against the
32
+ * same budget. Use it as soon as you run more than one process.
33
+ *
34
+ * ```ts
35
+ * // app/kernel/providers/RateLimiterServiceProvider.ts
36
+ * export default class extends RateLimiterServiceProvider {
37
+ * driver = new RedisRateLimiter();
38
+ * }
39
+ * ```
40
+ *
41
+ * Window boundaries come from the app server's clock, not Redis's, so keep
42
+ * instances on NTP. A few seconds of skew only shifts where a window starts; it
43
+ * cannot double a budget the way an unsynchronised fixed window would.
44
+ */
45
+ export declare class RedisRateLimiter extends RateLimiterDriver {
46
+ private injectedClient?;
47
+ private prefix;
48
+ private failOpen;
49
+ private onError;
50
+ /** Cached `SCRIPT LOAD` result, so the script body is sent at most once. */
51
+ private scriptSha;
52
+ constructor(options?: RedisRateLimiterOptions);
53
+ consume(params: ConsumeParams): Promise<RateLimitResult>;
54
+ private get client();
55
+ /** Runs the script by digest, loading it on first use and after a flush. */
56
+ private run;
57
+ private load;
58
+ }
59
+ //# sourceMappingURL=RedisRateLimiterDriver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisRateLimiterDriver.d.ts","sourceRoot":"","sources":["../../../../services/rate-limiter/drivers/RedisRateLimiterDriver.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACrC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,uBAAuB;IACtC;;;;OAIG;IACH,MAAM,CAAC,EAAE,sBAAsB,CAAC;IAChC,4CAA4C;IAC5C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oEAAoE;IACpE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CACpC;AAyCD;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAiB,SAAQ,iBAAiB;IACrD,OAAO,CAAC,cAAc,CAAC,CAAyB;IAChD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,OAAO,CAA2B;IAE1C,4EAA4E;IAC5E,OAAO,CAAC,SAAS,CAAgC;gBAErC,OAAO,GAAE,uBAA4B;IAQ3C,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC;IAwD9D,OAAO,KAAK,MAAM,GAkBjB;IAED,4EAA4E;YAC9D,GAAG;IAejB,OAAO,CAAC,IAAI;CAcb"}
@@ -0,0 +1,55 @@
1
+ import type { RateLimitResult } from "./types";
2
+ /**
3
+ * The sliding-window-counter algorithm, shared by every driver.
4
+ *
5
+ * A fixed window is cheap but lets a client spend its whole budget at the end
6
+ * of one window and again at the start of the next — twice the limit across a
7
+ * window's worth of time. A true sliding log fixes that but has to keep a
8
+ * timestamp per request.
9
+ *
10
+ * The counter variant keeps two integers per key (this window and the one
11
+ * before it) and estimates usage by weighting the previous window by how much
12
+ * of it still overlaps the trailing window:
13
+ *
14
+ * usage = previous * (window - elapsed) / window + current
15
+ *
16
+ * That is a ~0.003% error on real traffic and costs two integers per key, so
17
+ * it is what the in-memory and Redis drivers both implement.
18
+ */
19
+ /** Epoch ms of the start of the window `now` falls into. */
20
+ export declare function windowStartFor(now: number, window: number): number;
21
+ /** Weight of the previous window's count, from `1` down to `0`. */
22
+ export declare function previousWeight(elapsed: number, window: number): number;
23
+ /** Estimated usage of the trailing `window` ending at `now`. */
24
+ export declare function estimateUsage(current: number, previous: number, elapsed: number, window: number): number;
25
+ /**
26
+ * How long until a request costing `cost` would be admitted, in milliseconds.
27
+ *
28
+ * Solves the usage equation for `elapsed` instead of guessing: the caller gets
29
+ * a `Retry-After` that lands the moment capacity actually frees up rather than
30
+ * at the end of the window, which for a sliding window is usually far too late.
31
+ */
32
+ export declare function timeUntilAllowed(params: {
33
+ current: number;
34
+ previous: number;
35
+ elapsed: number;
36
+ window: number;
37
+ limit: number;
38
+ cost: number;
39
+ }): number;
40
+ /**
41
+ * Turns a driver's post-operation counters into the result the caller sees.
42
+ * `current` and `previous` are the counts *after* an allowed request has been
43
+ * recorded, so every driver reports the same numbers for the same state.
44
+ */
45
+ export declare function buildResult(params: {
46
+ allowed: boolean;
47
+ current: number;
48
+ previous: number;
49
+ now: number;
50
+ elapsed: number;
51
+ window: number;
52
+ limit: number;
53
+ cost: number;
54
+ }): RateLimitResult;
55
+ //# sourceMappingURL=slidingWindow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slidingWindow.d.ts","sourceRoot":"","sources":["../../../services/rate-limiter/slidingWindow.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C;;;;;;;;;;;;;;;;GAgBG;AAEH,4DAA4D;AAC5D,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAEzD;AAED,mEAAmE;AACnE,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAG7D;AAED,gEAAgE;AAChE,wBAAgB,aAAa,CAC3B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,UAGf;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,UA2BA;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CA+BlB"}
@@ -1,2 +1,36 @@
1
- export {};
1
+ /**
2
+ * A single request for capacity from the rate limiter.
3
+ *
4
+ * `key` identifies the bucket — everything the caller wants counted together.
5
+ * The limiter never invents part of the key on its own, so callers stay in
6
+ * control of what is limited (an IP, an IP plus a route, a user id, ...).
7
+ */
8
+ export interface ConsumeParams {
9
+ key: string;
10
+ /** Maximum cost allowed inside one window. */
11
+ limit: number;
12
+ /** Window length in **milliseconds**. */
13
+ window: number;
14
+ /** How much this request costs. Defaults to `1`. */
15
+ cost?: number;
16
+ }
17
+ export interface RateLimitResult {
18
+ /** `false` means the caller is over budget and should be rejected. */
19
+ allowed: boolean;
20
+ /** The limit this decision was made against. */
21
+ limit: number;
22
+ /**
23
+ * Capacity left in the window, floored to a whole request. `0` when the
24
+ * request was denied.
25
+ */
26
+ remaining: number;
27
+ /**
28
+ * Epoch milliseconds. When allowed, the end of the current window — the
29
+ * point where the oldest counted requests have fully decayed away. When
30
+ * denied, the earliest moment a retry of the same cost can succeed.
31
+ */
32
+ resetAt: number;
33
+ /** Milliseconds to wait before retrying. `0` when the request was allowed. */
34
+ retryAfter: number;
35
+ }
2
36
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../services/rate-limiter/types.ts"],"names":[],"mappings":""}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../services/rate-limiter/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,8CAA8C;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAC;CACpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ViewRouterServiceContainer.d.ts","sourceRoot":"","sources":["../../../services/router/ViewRouterServiceContainer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIzC,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAO7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAKvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA4CxD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,QAc9D;AAED,qBAAa,0BAA2B,SAAQ,gBAAgB;IAW3C,OAAO,EAAE,yBAAyB;IAVrD,MAAM,CAAC,KAAK,SAAgC;IAE5C,cAAc,EAAE,cAAc,CAAM;IACpC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAM;IAC7C,0FAA0F;IAC1F,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAM;IACnD,aAAa,EAAE,aAAa,CAAM;IAClC,iBAAiB,EAAE,MAAM,EAAE,CAAM;IACjC,IAAI,EAAE,GAAG,CAAQ;gBAEE,OAAO,EAAE,yBAAyB;IAiBrD,IAAI;IAEE,YAAY,CAAC,GAAG,EAAE,WAAW;YAarB,MAAM;IAuKd,iBAAiB,CAAC,GAAG,EAAE,OAAO,gCArGZ;QACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3C,aAAa,EAAE,GAAG,CAAC;QACnB,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC5B;CA+VJ"}
1
+ {"version":3,"file":"ViewRouterServiceContainer.d.ts","sourceRoot":"","sources":["../../../services/router/ViewRouterServiceContainer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAIzC,OAAO,EAEL,KAAK,cAAc,EAEpB,MAAM,wBAAwB,CAAC;AAIhC,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,6BAA6B,CAAC;AAK7E,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAKvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AA4CxD;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,YAAY,CAAC;AAE/C,wBAAgB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,QAc9D;AAED,qBAAa,0BAA2B,SAAQ,gBAAgB;IAW3C,OAAO,EAAE,yBAAyB;IAVrD,MAAM,CAAC,KAAK,SAAgC;IAE5C,cAAc,EAAE,cAAc,CAAM;IACpC,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAM;IAC7C,0FAA0F;IAC1F,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAM;IACnD,aAAa,EAAE,aAAa,CAAM;IAClC,iBAAiB,EAAE,MAAM,EAAE,CAAM;IACjC,IAAI,EAAE,GAAG,CAAQ;gBAEE,OAAO,EAAE,yBAAyB;IAiBrD,IAAI;IAEE,YAAY,CAAC,GAAG,EAAE,WAAW;YAarB,MAAM;IAuKd,iBAAiB,CAAC,GAAG,EAAE,OAAO,gCArGZ;QACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QAC3C,aAAa,EAAE,GAAG,CAAC;QACnB,gBAAgB,EAAE,MAAM,EAAE,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;QACtC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC5B;CAyXJ"}
@@ -5,6 +5,16 @@ import { ServiceProvider } from "../ServiceProvider";
5
5
  export declare class ViewRouterServiceProvider extends ServiceProvider {
6
6
  root: (props: any) => JSX.Element;
7
7
  rootRouter: new () => ViewRouter;
8
+ /**
9
+ * Whether a client-side navigation may skip the handlers of the layout
10
+ * segments the client already has mounted, rather than re-running the whole
11
+ * chain for every route below a layout.
12
+ *
13
+ * Set to `false` to run every handler on every navigation. Prefer
14
+ * `alwaysRun()` on the one layout that needs it — this switch is for when an
15
+ * app cannot audit them all at once.
16
+ */
17
+ partialRendering: boolean;
8
18
  boot(): void;
9
19
  onRequestStart(_req: HttpRequest): void | Promise<void>;
10
20
  onRequestEnd(_req: HttpRequest): void | Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"ViewRouterServiceProvider.d.ts","sourceRoot":"","sources":["../../../services/router/ViewRouterServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,qBAAa,yBAA0B,SAAQ,eAAe;IAC5D,IAAI,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC,OAAO,CAAC;IAClC,UAAU,EAAE,UAAU,UAAU,CAAC;IAEjC,IAAI;IAEJ,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACvD,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACrD,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CACpE"}
1
+ {"version":3,"file":"ViewRouterServiceProvider.d.ts","sourceRoot":"","sources":["../../../services/router/ViewRouterServiceProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAErD,qBAAa,yBAA0B,SAAQ,eAAe;IAC5D,IAAI,EAAE,CAAC,KAAK,EAAE,GAAG,KAAK,GAAG,CAAC,OAAO,CAAC;IAClC,UAAU,EAAE,UAAU,UAAU,CAAC;IAEjC;;;;;;;;OAQG;IACH,gBAAgB,UAAQ;IAExB,IAAI;IAEJ,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACvD,YAAY,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IACrD,aAAa,CAAC,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;CACpE"}
@@ -1,16 +1,32 @@
1
1
  import type { HttpRequest } from "../../http";
2
2
  import type { ViewRoutes } from "../../http/ViewRouter";
3
3
  export type ViewRouteExec = (req: HttpRequest<any, any>) => any;
4
+ /**
5
+ * One entry per segment of a route, parallel to `exec` — `segments[i]` is what
6
+ * `exec[i]` renders. `path` is the segment's own route pattern, prefixed by
7
+ * every ancestor, so the last segment's `path` is always the flat route key.
8
+ *
9
+ * Partial rendering compares these pairwise between the route the client is on
10
+ * and the one it is navigating to, to find the segments it can skip.
11
+ */
12
+ export type ViewRouteSegment = {
13
+ path: string;
14
+ viewPath: string;
15
+ /** Layout opted out of being skipped, via `alwaysRun()`. */
16
+ alwaysRun?: boolean;
17
+ };
4
18
  export type FlatViewRoutes = Record<string, {
5
19
  exec: ViewRouteExec[];
6
20
  middleware: (string | any)[];
7
21
  viewPath: string;
22
+ segments: ViewRouteSegment[];
8
23
  }>;
9
24
  export declare function createFlatViewRoutes(routes: ViewRoutes): {
10
25
  [k: string]: {
11
26
  exec: ViewRouteExec[];
12
27
  middleware: (string | any)[];
13
28
  viewPath: string;
29
+ segments: ViewRouteSegment[];
14
30
  };
15
31
  };
16
32
  //# sourceMappingURL=createFlatViewRoutes.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"createFlatViewRoutes.d.ts","sourceRoot":"","sources":["../../../services/router/createFlatViewRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC;AAEhE,MAAM,MAAM,cAAc,GAAG,MAAM,CACjC,MAAM,EACN;IAAE,IAAI,EAAE,aAAa,EAAE,CAAC;IAAC,UAAU,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAC1E,CAAC;AAWF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU;;cAZ7C,aAAa,EAAE;oBAAc,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE;kBAAY,MAAM;;EAoExE"}
1
+ {"version":3,"file":"createFlatViewRoutes.d.ts","sourceRoot":"","sources":["../../../services/router/createFlatViewRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,GAAG,CAAC;AAEhE;;;;;;;GAOG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,CACjC,MAAM,EACN;IACE,IAAI,EAAE,aAAa,EAAE,CAAC;IACtB,UAAU,EAAE,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,gBAAgB,EAAE,CAAC;CAC9B,CACF,CAAC;AAmCF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,UAAU;;cAxC7C,aAAa,EAAE;oBACT,CAAC,MAAM,GAAG,GAAG,CAAC,EAAE;kBAClB,MAAM;kBACN,gBAAgB,EAAE;;EAwG/B"}
@@ -0,0 +1,12 @@
1
+ import type { FlatViewRoutes } from "./createFlatViewRoutes";
2
+ export interface ViewRouteMatch {
3
+ routePath: string;
4
+ params: Record<string, string>;
5
+ route: FlatViewRoutes[string];
6
+ }
7
+ /**
8
+ * First route whose pattern matches `pathname`. `flatViewRoutes` is ordered
9
+ * least-specific-first, so this is insertion order, not a best match.
10
+ */
11
+ export declare function matchViewRoute(flatViewRoutes: FlatViewRoutes, pathname: string): ViewRouteMatch | null;
12
+ //# sourceMappingURL=matchViewRoute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matchViewRoute.d.ts","sourceRoot":"","sources":["../../../services/router/matchViewRoute.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,KAAK,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,cAAc,EAAE,cAAc,EAC9B,QAAQ,EAAE,MAAM,GACf,cAAc,GAAG,IAAI,CAavB"}
@@ -0,0 +1,43 @@
1
+ import type { FlatViewRoutes, ViewRouteSegment } from "./createFlatViewRoutes";
2
+ /** A route, as matched for one request, in the terms partial rendering needs. */
3
+ export interface PartialRenderRoute {
4
+ segments: ViewRouteSegment[];
5
+ params: Record<string, string>;
6
+ search: string;
7
+ }
8
+ export interface PartialRenderPlan {
9
+ /** Index of the first segment whose handler has to run. */
10
+ startIndex: number;
11
+ /** View paths of the segments that were skipped, in order. */
12
+ carriedViews: string[];
13
+ }
14
+ /**
15
+ * Works out which of the target route's segments the client already has
16
+ * rendered, and so which of its handlers this request can skip.
17
+ *
18
+ * A segment is carried forward only when it is *the same segment on the same
19
+ * resolved path*: `/app/A/chat` → `/app/A/lists` reuses the layout, but
20
+ * `/app/A/chat` → `/app/B/chat` re-runs it even though the view is the same.
21
+ *
22
+ * The target's own leaf always runs — navigating to the URL you are already on
23
+ * is a reload, not a no-op.
24
+ */
25
+ export declare function planPartialRender(from: PartialRenderRoute | null, to: PartialRenderRoute): PartialRenderPlan;
26
+ /**
27
+ * Turns the `x-gemi-from` header into a plan for the request being served.
28
+ *
29
+ * Every way of not knowing what the client has — no header, an unparseable
30
+ * one, a path this server does not route — ends in the same place: render
31
+ * everything. The client is telling the server what to skip, so the failure
32
+ * mode has to be doing the work, never trusting a value that did not check out.
33
+ */
34
+ export declare function resolvePartialRender(options: {
35
+ flatViewRoutes: FlatViewRoutes;
36
+ supportedLocales: string[];
37
+ /** Raw `x-gemi-from` value. */
38
+ from: string | null;
39
+ /** Origin to resolve the header value against. */
40
+ origin: string;
41
+ to: PartialRenderRoute;
42
+ }): PartialRenderPlan;
43
+ //# sourceMappingURL=planPartialRender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planPartialRender.d.ts","sourceRoot":"","sources":["../../../services/router/planPartialRender.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAG/E,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,gBAAgB,EAAE,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAWD;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,kBAAkB,GAAG,IAAI,EAC/B,EAAE,EAAE,kBAAkB,GACrB,iBAAiB,CAsCnB;AAWD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE;IAC5C,cAAc,EAAE,cAAc,CAAC;IAC/B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,+BAA+B;IAC/B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAC;IACf,EAAE,EAAE,kBAAkB,CAAC;CACxB,GAAG,iBAAiB,CA6BpB"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Route the client is currently rendering, sent on `.json` navigations so the
3
+ * server can skip the handlers of the segments that route already has mounted.
4
+ * Value is a locale-less pathname plus search, e.g. `/app/A/chat?tab=2`.
5
+ */
6
+ export declare const PARTIAL_RENDER_HEADER = "x-gemi-from";
7
+ /** What the server reports back about the skip it performed, if any. */
8
+ export interface PartialRenderInfo {
9
+ /** The `x-gemi-from` value the plan was computed against. */
10
+ from: string;
11
+ /** View paths of the skipped segments, in order. Their data is carried forward. */
12
+ carriedViews: string[];
13
+ }
14
+ /**
15
+ * The `x-gemi-from` value for the route the client starts on.
16
+ *
17
+ * On the first render the router's `pathname` is still the route *pattern* the
18
+ * server matched — it only becomes a resolved path once the history listener
19
+ * has run — so this has to apply the params. Sending `/app/:orgId/chat` names
20
+ * no route the server can resolve, and the first navigation after every full
21
+ * page load would skip nothing.
22
+ */
23
+ export declare function initialRenderedRoute(route: {
24
+ pathname?: string;
25
+ params?: Record<string, string>;
26
+ search?: string;
27
+ }): string;
28
+ //# sourceMappingURL=partialRender.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partialRender.d.ts","sourceRoot":"","sources":["../../utils/partialRender.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,gBAAgB,CAAC;AAEnD,wEAAwE;AACxE,MAAM,WAAW,iBAAiB;IAChC,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,mFAAmF;IACnF,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,UAKA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gemi",
3
- "version": "0.44.1",
3
+ "version": "0.46.0",
4
4
  "private": false,
5
5
  "license": "MIT",
6
6
  "author": "Enes Tufekci <enes@gemijs.dev>",
@@ -1,4 +0,0 @@
1
- // @bun
2
- import{Na as a,Oa as b,Pa as c,Qa as d,Ra as e,Sa as f,Ta as g}from"./chunk-knrx23te.js";import"./chunk-nx8t65g3.js";import"./chunk-sdj112nj.js";import"./chunk-6gtmkbh0.js";export{g as fromEnv,c as ENV_SESSION,b as ENV_SECRET,a as ENV_KEY,d as ENV_EXPIRATION,e as ENV_CREDENTIAL_SCOPE,f as ENV_ACCOUNT_ID};
3
-
4
- //# debugId=1329E74B99E0D77464756E2164756E21