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.
- package/dist/app/index.js +1 -1
- package/dist/bin/gemi.js +30 -1
- package/dist/bin/gemi.js.map +4 -4
- package/dist/broadcasting/index.js +1 -1
- package/dist/bun/plugin.js +1 -1
- package/dist/bun/preload.js +1 -1
- package/dist/{chunk-5v36t9j3.js → chunk-092pj2j2.js} +3 -3
- package/dist/{chunk-5v36t9j3.js.map → chunk-092pj2j2.js.map} +1 -1
- package/dist/{chunk-zwpx6z5f.js → chunk-0h3jr9d9.js} +2 -2
- package/dist/{chunk-8zd6d5nd.js.map → chunk-0h3jr9d9.js.map} +1 -1
- package/dist/{chunk-98sj7tkf.js → chunk-0tbwn6er.js} +3 -3
- package/dist/{chunk-98sj7tkf.js.map → chunk-0tbwn6er.js.map} +1 -1
- package/dist/{chunk-hwwp6x2s.js → chunk-1ab1ptq5.js} +2 -2
- package/dist/{chunk-hwwp6x2s.js.map → chunk-1ab1ptq5.js.map} +1 -1
- package/dist/{chunk-eby61053.js → chunk-1ce7veh9.js} +3 -3
- package/dist/{chunk-eby61053.js.map → chunk-1ce7veh9.js.map} +1 -1
- package/dist/{chunk-dk01r6se.js → chunk-23g7qdpg.js} +2 -2
- package/dist/{chunk-dk01r6se.js.map → chunk-23g7qdpg.js.map} +1 -1
- package/dist/{chunk-fs091xz7.js → chunk-2gw6cyyj.js} +2 -2
- package/dist/{chunk-fs091xz7.js.map → chunk-2gw6cyyj.js.map} +1 -1
- package/dist/chunk-2qn472j4.js +5 -0
- package/dist/{chunk-8gb1w19q.js.map → chunk-2qn472j4.js.map} +1 -1
- package/dist/{chunk-pkzj7c5w.js → chunk-2sfz64xh.js} +2 -2
- package/dist/{chunk-pkzj7c5w.js.map → chunk-2sfz64xh.js.map} +1 -1
- package/dist/{chunk-q0xq0pt0.js → chunk-2yth3kk2.js} +4 -4
- package/dist/{chunk-q0xq0pt0.js.map → chunk-2yth3kk2.js.map} +1 -1
- package/dist/{chunk-gdegb5t0.js → chunk-38ma6gg3.js} +2 -2
- package/dist/{chunk-gdegb5t0.js.map → chunk-38ma6gg3.js.map} +1 -1
- package/dist/{chunk-8vnbvfmd.js → chunk-3y9nf09k.js} +2 -2
- package/dist/{chunk-8vnbvfmd.js.map → chunk-3y9nf09k.js.map} +1 -1
- package/dist/{chunk-8zd6d5nd.js → chunk-4e61qbp4.js} +2 -2
- package/dist/{chunk-zwpx6z5f.js.map → chunk-4e61qbp4.js.map} +1 -1
- package/dist/{chunk-nbx9mgmr.js → chunk-4t6a82fh.js} +2 -2
- package/dist/{chunk-nbx9mgmr.js.map → chunk-4t6a82fh.js.map} +1 -1
- package/dist/{chunk-mrf9tjev.js → chunk-53e48ce8.js} +2 -2
- package/dist/{chunk-mrf9tjev.js.map → chunk-53e48ce8.js.map} +1 -1
- package/dist/{chunk-j33n723q.js → chunk-5s45prj5.js} +3 -3
- package/dist/{chunk-j33n723q.js.map → chunk-5s45prj5.js.map} +1 -1
- package/dist/{chunk-16bnbtyn.js → chunk-68w225rv.js} +2 -2
- package/dist/{chunk-16bnbtyn.js.map → chunk-68w225rv.js.map} +1 -1
- package/dist/{chunk-rxeyhwqz.js → chunk-69w4egkg.js} +3 -3
- package/dist/{chunk-rxeyhwqz.js.map → chunk-69w4egkg.js.map} +1 -1
- package/dist/{chunk-8m3wq94b.js → chunk-6scjrbw7.js} +2 -2
- package/dist/{chunk-8m3wq94b.js.map → chunk-6scjrbw7.js.map} +1 -1
- package/dist/{chunk-asgfqcwd.js → chunk-88pq4gmf.js} +2 -2
- package/dist/{chunk-asgfqcwd.js.map → chunk-88pq4gmf.js.map} +1 -1
- package/dist/{chunk-p0z35s59.js → chunk-8p6rqegw.js} +1 -1
- package/dist/{chunk-5t2c9194.js → chunk-a0sy9f9x.js} +1 -1
- package/dist/{chunk-g4gjn481.js → chunk-cmq5t8p9.js} +3 -3
- package/dist/{chunk-g4gjn481.js.map → chunk-cmq5t8p9.js.map} +1 -1
- package/dist/{chunk-zznpmnp7.js → chunk-d1ahbza9.js} +3 -3
- package/dist/{chunk-zznpmnp7.js.map → chunk-d1ahbza9.js.map} +1 -1
- package/dist/{chunk-an6nq9yd.js → chunk-ebcgdv00.js} +3 -3
- package/dist/{chunk-an6nq9yd.js.map → chunk-ebcgdv00.js.map} +1 -1
- package/dist/{chunk-3z4nwzvp.js → chunk-et0j6jx8.js} +3 -3
- package/dist/{chunk-3z4nwzvp.js.map → chunk-et0j6jx8.js.map} +1 -1
- package/dist/chunk-f29vd5mc.js +5 -0
- package/dist/{chunk-45c8sqb7.js.map → chunk-f29vd5mc.js.map} +2 -2
- package/dist/{chunk-gvf1d636.js → chunk-faa987e4.js} +2 -2
- package/dist/{chunk-gvf1d636.js.map → chunk-faa987e4.js.map} +1 -1
- package/dist/{chunk-6wm9bsz9.js → chunk-h5g05qwb.js} +2 -2
- package/dist/{chunk-6wm9bsz9.js.map → chunk-h5g05qwb.js.map} +1 -1
- package/dist/{chunk-x80n922n.js → chunk-h7jv3ame.js} +2 -2
- package/dist/{chunk-x80n922n.js.map → chunk-h7jv3ame.js.map} +1 -1
- package/dist/chunk-ha7n1c94.js +7 -0
- package/dist/{chunk-bm6q0q1q.js.map → chunk-ha7n1c94.js.map} +3 -3
- package/dist/chunk-hbycemy5.js +6 -0
- package/dist/{chunk-ygpzyxr6.js.map → chunk-hbycemy5.js.map} +2 -2
- package/dist/{chunk-3fcgg506.js → chunk-j1knf8gv.js} +3 -3
- package/dist/{chunk-3fcgg506.js.map → chunk-j1knf8gv.js.map} +1 -1
- package/dist/chunk-jdfe4dmh.js +5 -0
- package/dist/chunk-jdfe4dmh.js.map +30 -0
- package/dist/{chunk-1sbm38g3.js → chunk-kaxvw75w.js} +3 -3
- package/dist/{chunk-1sbm38g3.js.map → chunk-kaxvw75w.js.map} +1 -1
- package/dist/{chunk-c4r1zh9q.js → chunk-km6x37ws.js} +4 -4
- package/dist/{chunk-c4r1zh9q.js.map → chunk-km6x37ws.js.map} +1 -1
- package/dist/{chunk-eyvtf769.js → chunk-kxzwem3g.js} +2 -2
- package/dist/{chunk-eyvtf769.js.map → chunk-kxzwem3g.js.map} +1 -1
- package/dist/{chunk-hnw1jy86.js → chunk-msz8rngs.js} +2 -2
- package/dist/{chunk-hnw1jy86.js.map → chunk-msz8rngs.js.map} +1 -1
- package/dist/chunk-mt2755rs.js +5 -0
- package/dist/{chunk-g0w87jtw.js.map → chunk-mt2755rs.js.map} +4 -3
- package/dist/chunk-p22rkf1g.js +8 -0
- package/dist/chunk-p22rkf1g.js.map +11 -0
- package/dist/{chunk-kv8p4h79.js → chunk-q09mmr05.js} +3 -3
- package/dist/{chunk-kv8p4h79.js.map → chunk-q09mmr05.js.map} +1 -1
- package/dist/chunk-qj4mmner.js +4 -0
- package/dist/{chunk-6x6gb7zg.js.map → chunk-qj4mmner.js.map} +3 -3
- package/dist/{chunk-d3tf618w.js → chunk-qx6sahe0.js} +2 -2
- package/dist/{chunk-d3tf618w.js.map → chunk-qx6sahe0.js.map} +1 -1
- package/dist/{chunk-w7fpm2pw.js → chunk-qyaqg07q.js} +2 -2
- package/dist/{chunk-w7fpm2pw.js.map → chunk-qyaqg07q.js.map} +1 -1
- package/dist/{chunk-a6w4tpwz.js → chunk-rq770c71.js} +2 -2
- package/dist/{chunk-a6w4tpwz.js.map → chunk-rq770c71.js.map} +1 -1
- package/dist/{chunk-zezdm4ep.js → chunk-t4n25km9.js} +3 -3
- package/dist/{chunk-zezdm4ep.js.map → chunk-t4n25km9.js.map} +1 -1
- package/dist/chunk-tmk4zfz9.js +33 -0
- package/dist/chunk-tmk4zfz9.js.map +18 -0
- package/dist/{chunk-k4mjcvsm.js → chunk-ttzkn1qd.js} +2 -2
- package/dist/{chunk-k4mjcvsm.js.map → chunk-ttzkn1qd.js.map} +1 -1
- package/dist/{chunk-94k8pjmf.js → chunk-tztqkat2.js} +3 -3
- package/dist/{chunk-94k8pjmf.js.map → chunk-tztqkat2.js.map} +1 -1
- package/dist/{chunk-ybqftmjx.js → chunk-vnjvd82s.js} +2 -2
- package/dist/{chunk-ybqftmjx.js.map → chunk-vnjvd82s.js.map} +1 -1
- package/dist/{chunk-0k5jhyry.js → chunk-xsmt86n3.js} +2 -2
- package/dist/{chunk-0k5jhyry.js.map → chunk-xsmt86n3.js.map} +1 -1
- package/dist/{chunk-ntr81h76.js → chunk-z4q1ty6w.js} +2 -2
- package/dist/{chunk-ntr81h76.js.map → chunk-z4q1ty6w.js.map} +1 -1
- package/dist/{chunk-53w1njqz.js → chunk-zhsaz9cr.js} +1 -1
- package/dist/config/index.js +1 -1
- package/dist/console/Command.d.ts +143 -0
- package/dist/console/Command.d.ts.map +1 -0
- package/dist/console/CommandRegistry.d.ts +46 -0
- package/dist/console/CommandRegistry.d.ts.map +1 -0
- package/dist/console/builder.d.ts +115 -0
- package/dist/console/builder.d.ts.map +1 -0
- package/dist/console/builder.test-d.d.ts +2 -0
- package/dist/console/builder.test-d.d.ts.map +1 -0
- package/dist/console/config.d.ts +40 -0
- package/dist/console/config.d.ts.map +1 -0
- package/dist/console/context.d.ts +90 -0
- package/dist/console/context.d.ts.map +1 -0
- package/dist/console/errors.d.ts +19 -0
- package/dist/console/errors.d.ts.map +1 -0
- package/dist/console/parse.d.ts +65 -0
- package/dist/console/parse.d.ts.map +1 -0
- package/dist/console/run.d.ts +2 -0
- package/dist/console/run.d.ts.map +1 -0
- package/dist/console/run.js +16 -0
- package/dist/console/run.js.map +14 -0
- package/dist/console/runner.d.ts +41 -0
- package/dist/console/runner.d.ts.map +1 -0
- package/dist/console/usage.d.ts +7 -0
- package/dist/console/usage.d.ts.map +1 -0
- package/dist/container/index.js +2 -2
- package/dist/container/index.js.map +1 -1
- package/dist/database/index.js +2 -2
- package/dist/database/index.js.map +1 -1
- package/dist/email/index.js +2 -2
- package/dist/email/index.js.map +2 -2
- package/dist/facades/index.js +2 -2
- package/dist/facades/index.js.map +1 -1
- package/dist/foundation/index.js +2 -2
- package/dist/foundation/index.js.map +1 -1
- package/dist/http/ViewRouter.d.ts.map +1 -1
- package/dist/http/index.js +2 -2
- package/dist/http/index.js.map +1 -1
- package/dist/i18n/index.js +2 -2
- package/dist/i18n/index.js.map +2 -2
- package/dist/kernel/Kernel.d.ts +29 -0
- package/dist/kernel/Kernel.d.ts.map +1 -1
- package/dist/kernel/index.js +3 -3
- package/dist/kernel/index.js.map +3 -3
- package/dist/orm/compile/nested-writes.d.ts +11 -3
- package/dist/orm/compile/nested-writes.d.ts.map +1 -1
- package/dist/orm/index.js +3 -3
- package/dist/orm/index.js.map +4 -4
- package/dist/server/index.js +2 -2
- package/dist/server/index.js.map +1 -1
- package/dist/services/cron/ScheduleServiceProvider.d.ts.map +1 -1
- package/dist/services/discovery.d.ts +15 -0
- package/dist/services/discovery.d.ts.map +1 -1
- package/dist/services/index.d.ts +8 -1
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +11 -11
- package/dist/services/index.js.map +2 -2
- package/dist/support/Service.d.ts +90 -0
- package/dist/support/Service.d.ts.map +1 -0
- package/dist/support/discover.d.ts +9 -1
- package/dist/support/discover.d.ts.map +1 -1
- package/dist/support/index.d.ts +1 -0
- package/dist/support/index.d.ts.map +1 -1
- package/dist/support/index.js +2 -2
- package/dist/support/index.js.map +4 -3
- package/package.json +2 -1
- package/dist/chunk-45c8sqb7.js +0 -5
- package/dist/chunk-6fym5bfg.js +0 -23
- package/dist/chunk-6fym5bfg.js.map +0 -36
- package/dist/chunk-6x6gb7zg.js +0 -4
- package/dist/chunk-8gb1w19q.js +0 -5
- package/dist/chunk-bm6q0q1q.js +0 -7
- package/dist/chunk-g0w87jtw.js +0 -5
- package/dist/chunk-j4rkk01q.js +0 -5
- package/dist/chunk-j4rkk01q.js.map +0 -10
- package/dist/chunk-ygpzyxr6.js +0 -6
- /package/dist/{chunk-p0z35s59.js.map → chunk-8p6rqegw.js.map} +0 -0
- /package/dist/{chunk-5t2c9194.js.map → chunk-a0sy9f9x.js.map} +0 -0
- /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
|
|
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"}
|
package/dist/support/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/support/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{
|
|
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=
|
|
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": ";
|
|
8
|
-
"debugId": "
|
|
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
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gemi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Enes Tufekci <enes@gemijs.dev>",
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"./database": "./dist/database/index.js",
|
|
36
36
|
"./orm": "./dist/orm/index.js",
|
|
37
37
|
"./support": "./dist/support/index.js",
|
|
38
|
+
"./console/run": "./dist/console/run.js",
|
|
38
39
|
"./bun/preload": "./dist/bun/preload.js",
|
|
39
40
|
"./bun/plugin": "./dist/bun/plugin.js"
|
|
40
41
|
},
|
package/dist/chunk-45c8sqb7.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{c as D,d as v}from"./chunk-ygpzyxr6.js";import"./chunk-ppt296ye.js";import{wb as q}from"./chunk-bm6q0q1q.js";import{Tb as P,Wb as V}from"./chunk-c4r1zh9q.js";import"./chunk-3z4nwzvp.js";import"./chunk-eby61053.js";import"./chunk-6x6gb7zg.js";import"./chunk-98sj7tkf.js";import"./chunk-nbx9mgmr.js";import"./chunk-8vnbvfmd.js";import"./chunk-asgfqcwd.js";import"./chunk-g0w87jtw.js";import"./chunk-w7fpm2pw.js";import"./chunk-j4rkk01q.js";import"./chunk-zwpx6z5f.js";import{join as E}from"path";import{Duplex as o}from"stream";import{constants as T,createBrotliCompress as l,createGzip as n}from"zlib";var i=["br","gzip"],t=1024,a=5,s=19,r=6;function e($){if($===void 0)return 1;if(!/^\d+(\.\d{0,3})?$/.test($.trim()))return 1;let J=Number($.trim());return Number.isFinite(J)?Math.min(J,1):1}function JJ($){if($===null||$===void 0)return null;let J=new Map;for(let H of $.split(",")){let[Z,...X]=H.split(";"),j=Z.trim().toLowerCase();if(j==="")continue;let x=1;for(let K of X){let O=K.indexOf("=");if(O===-1)continue;if(K.slice(0,O).trim().toLowerCase()==="q"){x=e(K.slice(O+1));break}}J.set(j,x)}return J}function $J($,J=i){let H=JJ($);if(!H)return null;let Z=H.get("*"),X=null,j=0;for(let x of J){let K=H.get(x)??Z??0;if(K>j)X=x,j=K}return X}function HJ($,J){if(!$)return!1;return $.split(",").some((H)=>H.trim().toLowerCase().split("=")[0].trim()===J)}function UJ($,J){if($==="HEAD")return!1;if(!J.body)return!1;if(J.status<200||J.status===204||J.status===206||J.status===304)return!1;if(J.headers.has("Content-Range"))return!1;let H=J.headers.get("Content-Type");if(!H||!/^\s*text\/html\s*(;|$)/i.test(H))return!1;let Z=J.headers.get("Content-Encoding");if(Z&&Z.trim().toLowerCase()!=="identity")return!1;if(HJ(J.headers.get("Cache-Control"),"no-transform"))return!1;let X=J.headers.get("Content-Length");if(X!==null&&Number(X)<t)return!1;return!0}function ZJ($){let J=$==="br"?l({params:{[T.BROTLI_PARAM_QUALITY]:a,[T.BROTLI_PARAM_LGWIN]:s},flush:T.BROTLI_OPERATION_FLUSH}):n({level:r,flush:T.Z_SYNC_FLUSH});return o.toWeb(J)}function zJ($){let J=$.get("Vary");if(!J){$.set("Vary","Accept-Encoding");return}let H=J.split(",").map((Z)=>Z.trim().toLowerCase());if(H.includes("*")||H.includes("accept-encoding"))return;$.append("Vary","Accept-Encoding")}function F($,J){if(!UJ($.method,J))return J;let H=new Headers(J.headers);zJ(H);let Z=$J($.headers.get("Accept-Encoding"));if(!Z)return new Response(J.body,{status:J.status,statusText:J.statusText,headers:H});let X;try{X=ZJ(Z)}catch{return new Response(J.body,{status:J.status,statusText:J.statusText,headers:H})}return H.set("Content-Encoding",Z),H.delete("Content-Length"),new Response(J.body.pipeThrough(X),{status:J.status,statusText:J.statusText,headers:H})}function y($){let J=$.toString(),H=new Bun.CryptoHasher("sha1");return H.update(J),H.digest("hex")}if(!globalThis.URLPattern)globalThis.URLPattern=P;import{exists as GJ}from"fs/promises";var g="app/client.tsx";function w($){let J=$["app/client.tsx"]?.file;if(!J)return;return{module:`/${J}`,preload:S($,"app/client.tsx")}}function S($,J){let H=[],Z=new Set,X=(j)=>{if(Z.has(j))return;Z.add(j);let x=$[j];if(!x?.file)return;H.push(`/${x.file}`);for(let K of x.imports??[])X(K)};return X(J),H}var I=q(),XJ=E(I,"app"),L=E(I,"dist");async function VJ($,J){let H=await import(`${L}/client/.vite/manifest.json`),Z=await import(`${L}/server/.vite/manifest.json`);process.env.ROOT_DIR=I,process.env.APP_DIR=XJ,process.env.DIST_DIR=L;let X={},j={},x={},K={},O={},f=(U,G)=>`"${U}": () => import("${G}")`,k=[];for(let U of["404",...$.getFlatComponentTree.call($)]){let G=Z[`app/views/${U}.tsx`];if(!G?.file){console.log(`Server file not found for ${U}`),console.log(G);let W=Object.keys(Z),A=`app/views/${U}.tsx`;console.log(`${A} not found in server manifest`),console.log(W)}let Q=await import(`${process.env.DIST_DIR}/server/${G?.file}`);X[U]=Q.default,j[U]=Q.OpenGraph,x[U]=Q;let z=H[`app/views/${U}.tsx`];if(z?.css&&z?.css.length>0)K[U]=z?.css;if(z)k.push(f(U,`/${z?.file}`)),O[U]=S(H,`app/views/${U}.tsx`)}let u=`{${k.join(",")}}`,N=w(H);if(!N)console.error(`Client manifest has no "${g}" entry \u2014 dist/client is incomplete, so documents will render but never hydrate.`);let m=H["app/client.tsx"]?.css??[],h=(await Promise.all(m.map((U)=>Bun.file(`${L}/client/${U}`).text()))).join(`
|
|
3
|
-
`),c=new P({pathname:"/*.:filetype(png|txt|js|css|jpg|svg|jpeg|avif|webp|ico|ttf|map)"});async function p(U){let{pathname:G}=new URL(U.url),Q=G.startsWith("/.well-known"),z=c.test({pathname:G})||Q,W=G.startsWith("/api");if(z&&!W){let A=new URL(U.url),b=U.url.replace(A.origin,"").split("?")[0],B=`${L}/client${b.replace("/assets/assets","/assets")}`,C=await GJ(B);if(!C&&A.pathname.includes(".js"))return new Response(`if(caches){caches?.delete("${B}")}window.location.reload();export {}`,{headers:{"Content-Type":"application/javascript"}});if(!C){if(G===V||G.startsWith(`${V}/`))return new Response("Not found",{status:404});return await R(U,G)}try{let Y=Bun.file(B),_=y(Y.lastModified);return new Response(Y.stream(),{headers:{"Content-Type":Y.type,"Cache-Control":"public, max-age=31536000, must-revalidate","Content-Length":String(Y.size),ETag:_}})}catch(Y){return $.onException?.(Y),new Response("Not found",{status:404})}}return await R(U,G)}async function R(U,G){let Q=$.fetch.bind($);try{let z=await Q(U);if(z instanceof Response)return z;else{let W=[];return W.push({content:h}),await z({getStyles:async(b)=>{if(!b)return D([]);for(let B of b){let C=H[`app/views/${B}.tsx`];for(let Y of C?.css??[]){let _=Bun.file(`${process.env.DIST_DIR}/client/${Y}`);W.push({id:Y,content:await _.text()})}}return D(W)},clientEntry:N,modulePreloadManifest:O,loaders:u,viewImportMap:X,viewModules:x,ogMap:j,cssManifest:K})}}catch(z){if(console.error(z),G.startsWith("/api"))return new Response(JSON.stringify({error:z.message}),{status:500,headers:{"Content-Type":"application/json"}});return $.onException?.(z),new Response(z.stack,{status:500})}}let d=(process.env.GEMI_COMPRESSION??"auto").toLowerCase()!=="off",M=Bun.serve({maxRequestBodySize:10737418240,fetch:async(U,G)=>{if(!U.headers.get("x-forwarded-for")){let z=G.requestIP(U);if(z)U.headers.set("x-forwarded-for",z.address)}let Q=await J(U,p);return d?F(U,Q):Q},idleTimeout:Number(process.env.SERVER_IDLE_TIMEOUT??10),port:process.env.PORT||5173});return v({port:M.port,rootDir:I}),M}export{VJ as httpProd};
|
|
4
|
-
|
|
5
|
-
//# debugId=C441E552AF44C0A164756E2164756E21
|
package/dist/chunk-6fym5bfg.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
import{vb as N,wb as f}from"./chunk-bm6q0q1q.js";import{xb as M}from"./chunk-k4mjcvsm.js";import{Hb as T,Nb as I,Ob as Q,Pb as m,Vb as W,Xb as U,Yb as V,bc as g,zb as S}from"./chunk-c4r1zh9q.js";import{Ed as h,Jd as L,Nd as y}from"./chunk-6x6gb7zg.js";import{ue as G}from"./chunk-8vnbvfmd.js";import{ze as A}from"./chunk-g4gjn481.js";import{Be as c,De as k}from"./chunk-g0w87jtw.js";async function o(_){try{if(!await _.shouldRun.call(_))return}catch(x){console.error(`Error evaluating shouldRun for cron job ${_.name}:`,x);return}try{await _.onTick.call(_)}catch(x){console.error(`Error executing cron job ${_.name}:`,x)}try{await _.callback.call(_)}catch(x){console.error(`Error in cron job ${_.name}:`,x)}try{await _.onComplete.call(_)}catch(x){console.error(`Error completing cron job ${_.name}:`,x)}}class K{static token="scheduler";handles=[];resolved;constructor(_){this.resolved=_.jobs}get jobs(){return[...this.resolved]}useJobs(_){this.resolved=_}start(_=(x)=>x()){if(this.resolved.length===0)return;let x=globalThis.__gemiCronJobs??=new Map,C=new Map;for(let F of this.resolved){let P=new F;if(!P.name){console.error(`Cron job must have a name \u2014 ${F.name||"an anonymous class"} has none, so it is not scheduled. If this is a base class that other jobs extend, move it out of the discovered directory: \`abstract\` does not survive to runtime, so a class under app/cron is indistinguishable from a job.`);continue}if(!P.cron){console.error(`Cron job must have an expression. Job: ${P.name}`);continue}let H=C.get(P.name);if(H!==void 0){console.error(`Two cron jobs claim the name "${P.name}" \u2014 ${H} is scheduled and ${F.name||"an anonymous class"} is not. A name is the scheduler's key, so only one job can hold it. Rename one.`);continue}C.set(P.name,F.name||"an anonymous class"),x.get(P.name)?.stop();let X=Bun.cron(P.cron,()=>_(()=>o(P)));x.set(P.name,X),this.handles.push(X)}}stop(){for(let _ of this.handles)_.stop();this.handles=[]}}function L_(_){return _}function E(){return{jobs:[],jobsDir:"app/jobs",concurrency:1}}function t(){let _=process.env.APP_DIR,x=process.env.ROOT_DIR,C=Bun.env.NODE_ENV==="production"?`${x}/dist/server/bootstrap.mjs`:`${_}/bootstrap.ts`,F=new File([`
|
|
3
|
-
import { app } from "${C}"
|
|
4
|
-
self.onmessage = async (event) => {
|
|
5
|
-
const clone = app.clone()
|
|
6
|
-
let result = null;
|
|
7
|
-
let error = null;
|
|
8
|
-
try {
|
|
9
|
-
result = clone.dispatchJob(event.data.jobName, event.data.args)
|
|
10
|
-
} catch (err) {
|
|
11
|
-
error = err
|
|
12
|
-
}
|
|
13
|
-
clone.destroy()
|
|
14
|
-
if(error) {
|
|
15
|
-
self.postMessage({error});
|
|
16
|
-
} else {
|
|
17
|
-
self.postMessage({result});
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
`],"worker.ts"),P=URL.createObjectURL(F);return new Worker(P)}async function e(_,x){let C=t();return C.postMessage({jobName:_,args:x}),await new Promise((F,P)=>{C.onmessage=(H)=>{let X=H.data;if("error"in X)P(X.error);else F(X.result)}})}class B{static token="queue";queue=new Set;activeRunningJobsCount=0;isRunning=!1;jobs={};config;constructor(_={}){this.config=G(E(),_),this.useJobs(this.config.jobs)}useJobs(_){this.config.jobs=_,this.jobs={};for(let x of _){if(this.jobs[x.name]){console.error(`Two queued jobs are named "${x.name}" \u2014 the first is registered `+"and this one is not, so dispatching either would have run one of them. A class name is the queue's key, so only one job can hold it. Rename one.");continue}this.jobs[x.name]=x}}get registeredJobs(){return[...this.config.jobs]}dispatchJob(_,x){if(this.jobs[_])return new this.jobs[_]().run(JSON.parse(x))}async next(){if(!this.isRunning)this.isRunning=!0;if(this.activeRunningJobsCount>=this.config.concurrency)return await new Promise((x)=>setTimeout(x,1000)),this.next();let _=this.queue.values().next().value;if(_)if(this.queue.delete(_),this.jobs[_.class])this.run(_);else console.error(`Dropped a queued job: nothing is registered under the name "${_.class}". If the class exists, it was not `+"discovered \u2014 check that it is under the queue slice's jobsDir "+"(app/jobs by default), or list it in app/config/queue.ts.");if(this.queue.size===0){this.isRunning=!1;return}await this.next()}async run(_){let x=this.jobs[_.class],C=new x,F=JSON.parse(_.args);this.activeRunningJobsCount++;try{let P=await(C.worker?e(_.class,_.args):C.run(...F));C.onSuccess(P,...F)}catch(P){if(C.onFail(P,...F),_.retries>=C.maxAttempts-1)C.onDeadletter(P,...F);else this.push(x,_.args,_.retries+1)}this.activeRunningJobsCount--}push(_,x,C=0){if(this.queue.add({class:_.name,args:x,createdAt:Date.now(),retries:C}),!this.isRunning)this.next()}}class __ extends M{register(){this.app.singleton(L,()=>new L(this.app.config.get("auth",{})))}}class J{name;cron;shouldRun(){return!0}callback(){}onTick(){}onComplete(){}static exp(_){return _}}function S_(_){return _}function O(){return{jobs:[],jobsDir:"app/cron"}}class q{static name="unset";worker=!1;maxAttempts=3;run(..._){}onFail(_,...x){}onSuccess(_,...x){}onDeadletter(_,...x){}static dispatch(..._){if(this.name==="unset")throw Error("Cannot dispatch a job with no name");k(B).push(this,JSON.stringify(_))}}import{existsSync as p}from"fs";import{dirname as x_,isAbsolute as C_,join as F_}from"path";function j(_){return C_(_)?_:F_(f(),_)}function w(_,x,C){if(p(_)||p(x_(_)))return;console.warn(`No ${x} were discovered: neither ${_} nor the directory above it exists, so this process cannot see the application's source. If this is a deploy that ships only the build output, list the classes explicitly in app/config/${C}.ts so they travel with it.`)}async function u(_=E().jobsDir){let x=j(_);w(x,"queue jobs","queue");let C=await N(x,q);return P_(C),C}function P_(_){for(let x of _){if(Object.getOwnPropertyDescriptor(x,"name")?.writable)continue;console.warn(`Queued job ${x.name} does not declare \`static name\`, so it is registered under its class name. A production build minifies the class that dispatches it and renames it, while discovery reads this `+"file from source and does not \u2014 so the dispatch would be dropped in "+`production and nowhere else. Add: static name = "${x.name}";`)}}async function b(_=O().jobsDir){let x=j(_);return w(x,"cron jobs","schedule"),await N(x,J)}class M_ extends M{register(){this.app.singleton(K,()=>new K(G(O(),this.app.config.get("schedule",{}))))}async boot(){let _=this.app.make(K),x=this.app.config.get("schedule",{});if(x.jobs===void 0){let{jobsDir:F}=G(O(),x);_.useJobs(await b(F))}let C=this.app;_.start((F)=>c.run(C,F))}}class G_ extends M{register(){this.app.singleton(A,()=>new A(this.app.config.get("mail",{})))}}class H_ extends M{register(){this.app.singleton(T,()=>new T(this.app.config.get("filesystem",{})))}}class X_ extends M{register(){this.app.singleton(I,()=>new I(this.app.config.get("image",{})))}}class Y_ extends M{register(){this.app.instance(W,new W)}}function Hx(_){return _}function l(){return{maxFileSize:10485760,onLogFileClosed:()=>{},onLogCreated:()=>{}}}class Z_ extends M{register(){this.app.singleton(Q,()=>new Q(G(l(),this.app.config.get("log",{}))))}async boot(){await this.app.make(Q).boot()}}class $_ extends M{register(){this.app.singleton(m,()=>new m(G(h(),this.app.config.get("middleware",{}))))}}function Vx(_){return _}function d(){return{channels:{}}}class B_ extends M{register(){this.app.singleton(S,()=>new S(G(d(),this.app.config.get("broadcast",{}))))}}class E_ extends M{register(){this.app.singleton(B,()=>new B(this.app.config.get("queue",{})))}async boot(){let _=this.app.config.get("queue",{});if(_.jobs!==void 0)return;let{jobsDir:x}=G(E(),_);this.app.make(B).useJobs(await u(x))}}class R{}function r(_,x){return Math.floor(_/x)*x}function z_(_,x){if(x<=0)return 0;return Math.min(1,Math.max(0,(x-_)/x))}function D(_,x,C,F){return x*z_(C,F)+_}function K_(_){let{current:x,previous:C,elapsed:F,window:P,limit:H,cost:X}=_,Y=H-X;if(Y<0)return P;let Z=Math.max(0,P-F);if(C>0){let $=P*(1-(Y-x)/C);if($<=P)return Math.max(0,Math.ceil($-F))}if(x<=Y)return Z;if(x<=0)return Z;let z=P*(1-Y/x);return Z+Math.max(0,Math.ceil(z))}function s(_){let{allowed:x,current:C,previous:F,now:P,elapsed:H,window:X,limit:Y,cost:Z}=_;if(x){let $=D(C,F,H,X);return{allowed:!0,limit:Y,remaining:Math.max(0,Math.floor(Y-$)),resetAt:P-H+X,retryAfter:0}}let z=K_({current:C,previous:F,elapsed:H,window:X,limit:Y,cost:Z});return{allowed:!1,limit:Y,remaining:0,resetAt:P+z,retryAfter:z}}class v extends R{buckets=new Map;maxKeys;constructor(_={}){super();this.maxKeys=Math.max(1,_.maxKeys??1e5)}consume(_){let{key:x,limit:C,window:F}=_,P=_.cost??1,H=Date.now(),X=r(H,F),Y=H-X,Z=this.roll(x,F,X),$=D(Z.current,Z.previous,Y,F)+P<=C;if($)Z.current+=P;if(this.buckets.delete(x),this.buckets.set(x,Z),this.buckets.size>this.maxKeys)this.evict(H);return s({allowed:$,current:Z.current,previous:Z.previous,now:H,elapsed:Y,window:F,limit:C,cost:P})}clear(){this.buckets.clear()}get size(){return this.buckets.size}roll(_,x,C){let F=this.buckets.get(_);if(!F||F.window!==x)return{window:x,windowStart:C,current:0,previous:0};if(F.windowStart===C)return F;if(F.windowStart+x===C)return F.previous=F.current,F.current=0,F.windowStart=C,F;return F.previous=0,F.current=0,F.windowStart=C,F}evict(_){for(let[x,C]of this.buckets)if(_-C.windowStart>=C.window*2)this.buckets.delete(x);for(let x of this.buckets.keys()){if(this.buckets.size<=this.maxKeys)break;this.buckets.delete(x)}}}function jx(_){return _}function i(){return{driver:new v,limit:1000,window:60}}class O_ extends M{register(){this.app.singleton(y,()=>new y(G(i(),this.app.config.get("ratelimiter",{}))))}}function dx(_){return _}function n(){return{url:process.env.REDIS_URL,options:void 0}}class Q_ extends M{register(){this.app.singleton(g,()=>new g(G(n(),this.app.config.get("redis",{}))))}}function a(_,x){let C=_.get(x);if(!C)throw Error(`Missing "${x}" configuration. Route dispatching needs a root router \u2014 declare it in "app/config/route.ts".`);return C}class U_ extends M{register(){this.app.singleton(V,()=>new V(a(this.app.config,"route.api"))),this.app.singleton(U,()=>new U(a(this.app.config,"route.view")))}boot(){this.app.make(V),this.app.make(U)}}
|
|
21
|
-
export{K as Ka,L_ as La,E as Ma,B as Na,__ as Oa,J as Pa,S_ as Qa,O as Ra,q as Sa,u as Ta,b as Ua,M_ as Va,G_ as Wa,H_ as Xa,X_ as Ya,Y_ as Za,Hx as _a,l as $a,Z_ as ab,$_ as bb,Vx as cb,d as db,B_ as eb,E_ as fb,r as gb,s as hb,R as ib,v as jb,jx as kb,i as lb,O_ as mb,dx as nb,n as ob,Q_ as pb,U_ as qb};
|
|
22
|
-
|
|
23
|
-
//# debugId=BF9FE5D10AAECB4B64756E2164756E21
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../services/cron/Scheduler.ts", "../services/queue/config.ts", "../services/queue/QueueManager.ts", "../auth/AuthServiceProvider.ts", "../services/cron/CronJob.ts", "../services/cron/config.ts", "../services/queue/Job.ts", "../services/discovery.ts", "../services/cron/ScheduleServiceProvider.ts", "../services/email/MailServiceProvider.ts", "../services/file-storage/FilesystemServiceProvider.ts", "../services/image-optimization/ImageServiceProvider.ts", "../services/kernel-id/KernelIdServiceProvider.ts", "../services/logging/config.ts", "../services/logging/LogServiceProvider.ts", "../services/middleware/MiddlewareServiceProvider.ts", "../services/pubsub/config.ts", "../services/pubsub/BroadcastServiceProvider.ts", "../services/queue/QueueServiceProvider.ts", "../services/rate-limiter/drivers/RateLimiterDriver.ts", "../services/rate-limiter/slidingWindow.ts", "../services/rate-limiter/drivers/InMemoryRateLimiterDriver.ts", "../services/rate-limiter/config.ts", "../services/rate-limiter/RateLimiterServiceProvider.ts", "../services/redis/config.ts", "../services/redis/RedisServiceProvider.ts", "../services/router/RouteServiceProvider.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import type { CronJob } from \"./CronJob\";\nimport type { ScheduleConfig } from \"./config\";\n\n// The handle the in-process `Bun.cron(schedule, callback)` overload returns\n// (has `.stop()`). `ReturnType<typeof Bun.cron>` can't be used — it resolves to\n// the module-path overload's `Promise<void>` instead.\ntype BunCronHandle = Bun.CronJob;\n\n// Runs a tick inside whatever async context the framework needs, so a job body\n// can resolve services exactly like a request handler can.\nexport type ScheduleRunner = <T>(cb: () => T | Promise<T>) => Promise<T> | T;\n\ndeclare global {\n // Registered cron jobs keyed by name, kept on `globalThis` so it survives\n // `bun --hot` reloads. Re-registering a name stops the previous schedule\n // first, so a reload *updates* the job in place instead of stacking a\n // duplicate. (Bun also stops all in-process cron jobs before re-evaluating on\n // hot reload; this makes the intent explicit and covers any re-construction.)\n var __gemiCronJobs: Map<string, BunCronHandle> | undefined;\n}\n\n/**\n * One tick of one job: the gate, then the three hooks in order.\n *\n * Named and exported rather than left inline in `start()` because otherwise the\n * only way to reach it is to wait — the tightest schedule `Bun.cron` accepts is\n * once a minute, which is not a thing a test can honestly sit through. What a\n * caller gives up by calling this directly is Bun's clock; what it keeps is\n * everything the scheduler puts between the clock and the job, which is the\n * part that has behaviour.\n *\n * The three hooks each catch and log, so one throwing does not stop the next —\n * `onComplete` in particular runs whether `callback` returned or threw, closer\n * to a `finally` than to a success handler.\n */\nexport async function runTick(job: CronJob): Promise<void> {\n try {\n // Plain truthiness, not `!== false`. The sloppy gate an app writes is\n // `if (!isProduction) return false;`, which returns `undefined` on the path\n // that was meant to run — and between the two readings of that, the one\n // that skips a recurring tick is recoverable and the one that mails a\n // customer from a developer's laptop is not. The declared return type makes\n // that method a compile error anyway, so the type system catches it while\n // it is still being written and the runtime stays on the safe side.\n if (!(await job.shouldRun.call(job))) {\n return;\n }\n } catch (error) {\n // Fail closed, for the same reason. A gate that throws has told us nothing\n // about whether the job may run, and the framework already refuses to\n // schedule a job whose name or expression it cannot read rather than run\n // something half-declared. The throw is logged, so this is loud; the missed\n // tick comes back on the next one.\n console.error(\n `Error evaluating shouldRun for cron job ${job.name}:`,\n error,\n );\n return;\n }\n\n try {\n await job.onTick.call(job);\n } catch (error) {\n console.error(`Error executing cron job ${job.name}:`, error);\n }\n try {\n await job.callback.call(job);\n } catch (error) {\n console.error(`Error in cron job ${job.name}:`, error);\n }\n try {\n await job.onComplete.call(job);\n } catch (error) {\n console.error(`Error completing cron job ${job.name}:`, error);\n }\n}\n\nexport class Scheduler {\n static token = \"scheduler\";\n\n private handles: BunCronHandle[] = [];\n private resolved: Array<new () => CronJob>;\n\n constructor(config: Required<ScheduleConfig>) {\n this.resolved = config.jobs;\n }\n\n /**\n * The jobs this scheduler will run, or ran — declared in the config slice or\n * discovered under `app/cron`, whichever the slice asked for.\n *\n * #323 turned an application's schedule from an array it could import into a\n * directory it cannot, and an app that had a test walking that array —\n * asserting every report has an owner, that no two share an expression — was\n * about to lose it. This is where that test goes instead, and it is the\n * better question anyway: the config slice says what was asked for, this says\n * what the scheduler took.\n *\n * It lists what `start()` iterates, not what `Bun.cron` accepted. A job with\n * no name, no expression, or a name another job already claimed is logged and\n * skipped, and still appears here — a test walking this should see the\n * problem rather than have it tidied away.\n *\n * A copy, so that walk cannot edit the set the scheduler is running from.\n */\n get jobs(): ReadonlyArray<new () => CronJob> {\n return [...this.resolved];\n }\n\n /**\n * Replaces the job set, for the provider to hand over what it found under\n * `app/cron` before it calls `start()`.\n *\n * The scheduler is constructed in `register()`, where reading a directory and\n * importing what is in it is not allowed to happen — that phase is\n * synchronous and resolves nothing. So it is built from whatever the slice\n * declared and corrected in `boot()`, one line before it starts. Calling this\n * after `start()` changes what `jobs` reports and nothing else; the schedules\n * are already registered.\n */\n useJobs(jobs: Array<new () => CronJob>) {\n this.resolved = jobs;\n }\n\n /**\n * Schedules every configured job. Called from `ScheduleServiceProvider.boot()`\n * — never from the constructor — so a job body may resolve any service the\n * container holds by the time it first ticks. That ordering is what replaced\n * the old `kernel.waitForBoot()` handshake.\n */\n start(run: ScheduleRunner = (cb) => cb()) {\n if (this.resolved.length === 0) {\n return;\n }\n\n const registry = (globalThis.__gemiCronJobs ??= new Map());\n\n // Names claimed during *this* pass, which is a different question from what\n // `registry` holds. `registry` spans hot reloads and is supposed to already\n // contain the previous reload's handle under the same name; this starts\n // empty every time and is how a name claimed twice in one schedule is told\n // apart from the same job coming back after an edit.\n const claimed = new Map<string, string>();\n\n for (const Job of this.resolved) {\n const job = new Job();\n if (!job.name) {\n // Named by its class, because under discovery nobody wrote this job\n // down anywhere and `JSON.stringify(job)` on a nameless one prints\n // `{}`. The overwhelmingly common cause is the second sentence: a\n // shared base class sitting in the walked directory. `abstract` is\n // erased before this code runs, so there is nothing here that could\n // tell a base class from a job — only the message can.\n console.error(\n `Cron job must have a name — ${Job.name || \"an anonymous class\"} ` +\n `has none, so it is not scheduled. If this is a base class that ` +\n `other jobs extend, move it out of the discovered directory: ` +\n `\\`abstract\\` does not survive to runtime, so a class under ` +\n `app/cron is indistinguishable from a job.`,\n );\n continue;\n }\n if (!job.cron) {\n console.error(`Cron job must have an expression. Job: ${job.name}`);\n continue;\n }\n const taken = claimed.get(job.name);\n if (taken !== undefined) {\n // Refusing the second one, rather than letting it through to the\n // `registry.get(...).stop()` below — which is what used to happen, and\n // it stopped the *first* job's schedule while leaving both classes in\n // `jobs`. That is the failure #323 exists to remove wearing a disguise:\n // a report that silently stops arriving, and a test walking `jobs` that\n // sees two healthy entries and passes.\n console.error(\n `Two cron jobs claim the name \"${job.name}\" — ${taken} is ` +\n `scheduled and ${Job.name || \"an anonymous class\"} is not. A name ` +\n `is the scheduler's key, so only one job can hold it. Rename one.`,\n );\n continue;\n }\n claimed.set(job.name, Job.name || \"an anonymous class\");\n\n // Update in place: stop any schedule previously registered under this name\n // before re-registering, so a hot reload replaces rather than duplicates.\n registry.get(job.name)?.stop();\n\n // Inside `run`, gate included: a `shouldRun` that resolves a service to\n // decide is the ordinary case, and evaluating it out here would leave it\n // without the application context every other line of the tick has.\n const handle = Bun.cron(job.cron, () => run(() => runTick(job)));\n\n registry.set(job.name, handle);\n this.handles.push(handle);\n }\n }\n\n stop() {\n for (const handle of this.handles) {\n handle.stop();\n }\n this.handles = [];\n }\n}\n",
|
|
6
|
-
"import type { Job } from \"./Job\";\n\n// Config key: `queue`. Derived from `QueueServiceProvider`.\nexport interface QueueConfig {\n /**\n * The `Job` subclasses this application dispatches, or nothing.\n *\n * ### Why \"or nothing\" is the point\n *\n * A dispatch that names a job the `QueueManager` has never heard of does not\n * fail — `next()` looks the name up in this list, and what is not there is\n * dropped with a line on stderr and nothing else. `Job.dispatch` has already\n * returned by then, so nothing upstream can be told. The list is therefore a\n * second spelling of `app/jobs`, kept in step by hand, and the cost of the\n * two disagreeing is paid by whatever was supposed to happen after the\n * dispatch.\n *\n * Leaving this out spells it once: the jobs are the classes under `jobsDir`.\n *\n * ### The rule, exactly\n *\n * **Declared wins, and `[]` is declared.** A `jobs` that is present is used\n * verbatim and no directory is read — that is the escape hatch for an app\n * whose jobs live somewhere this cannot walk, for one that deliberately\n * registers a subset, and for a deploy that ships no source. An empty array\n * means an app with no jobs and says so; it does not mean \"find some\".\n *\n * **Absent or `undefined` discovers.** `undefined` counts as absent for the\n * same reason `withDefaults` treats it that way everywhere else: a key spread\n * in from an optional value is an omission, not an instruction.\n */\n jobs?: Array<new () => Job>;\n\n /**\n * Where to look when `jobs` was not declared. Relative to the project root,\n * or absolute.\n *\n * Every `.ts`/`.tsx` file underneath it is imported at boot and every exported\n * class extending `Job` is registered, so this wants to be a directory of job\n * declarations rather than a directory that merely contains some. Moving the\n * jobs is what this field is for; pointing it at `app/` is not.\n */\n jobsDir?: string;\n\n concurrency?: number;\n}\n\nexport function defineQueueConfig(config: QueueConfig): QueueConfig {\n return config;\n}\n\nexport function queueConfigDefaults(): Required<QueueConfig> {\n return {\n jobs: [],\n jobsDir: \"app/jobs\",\n concurrency: 1,\n };\n}\n",
|
|
7
|
-
"import { Job } from \"./Job\";\nimport { queueConfigDefaults, type QueueConfig } from \"./config\";\nimport { withDefaults } from \"../../support/withDefaults\";\n\nfunction createWorker() {\n const APP_DIR = process.env.APP_DIR;\n const ROOT_DIR = process.env.ROOT_DIR;\n\n const appPath =\n process.env.NODE_ENV === \"production\"\n ? `${ROOT_DIR}/dist/server/bootstrap.mjs`\n : `${APP_DIR}/bootstrap.ts`;\n\n const file = new File(\n [\n `\n import { app } from \"${appPath}\"\n self.onmessage = async (event) => {\n const clone = app.clone()\n let result = null;\n let error = null;\n try {\n result = clone.dispatchJob(event.data.jobName, event.data.args)\n } catch (err) {\n error = err\n }\n clone.destroy()\n if(error) {\n self.postMessage({error});\n } else {\n self.postMessage({result});\n }\n };\n `,\n ],\n \"worker.ts\",\n );\n const url = URL.createObjectURL(file);\n return new Worker(url);\n}\n\n// TODO: terminate worker after the job is done\nasync function runInWorker(jobName: string, args: string) {\n const worker = createWorker();\n worker.postMessage({ jobName, args });\n return await new Promise((resolve, reject) => {\n worker.onmessage = (e) => {\n const data = e.data;\n if (\"error\" in data) {\n reject(data.error);\n } else {\n resolve(data.result);\n }\n };\n });\n}\n\ntype JobDefinition = {\n class: string;\n args: string;\n createdAt: number;\n retries: number;\n};\n\nexport class QueueManager {\n static token = \"queue\";\n\n queue: Set<JobDefinition> = new Set();\n activeRunningJobsCount = 0;\n isRunning = false;\n jobs: Record<string, new () => Job> = {};\n\n readonly config: Required<QueueConfig>;\n\n constructor(config: QueueConfig = {}) {\n this.config = withDefaults(queueConfigDefaults(), config);\n this.useJobs(this.config.jobs);\n }\n\n /**\n * Replaces the registered set, for the provider to hand over what it found\n * under `app/jobs`.\n *\n * This exists because the two phases disagree about when the answer is\n * knowable. The manager is constructed in `register()`, which is synchronous\n * and must resolve nothing; reading a directory and importing what is in it is\n * neither. So the manager is built from whatever the config slice declared —\n * nothing, when the app left `jobs` out — and the discovered set arrives in\n * `boot()`.\n *\n * The consequence worth knowing: anything that constructs an application and\n * skips phase two sees an empty registry, and every dispatch against an empty\n * registry is dropped — loudly on stderr, but after `Job.dispatch` has already\n * returned, so no caller finds out. An app that lists its jobs explicitly is\n * unaffected, because that list is already in place by the end of `register()`.\n */\n useJobs(jobs: Array<new () => Job>) {\n this.config.jobs = jobs;\n\n // Built one at a time rather than by `Object.fromEntries`, which resolves a\n // repeated key by keeping the last and saying nothing.\n //\n // Two classes with one name is the worst failure in this subsystem, and it\n // is worse than the dropped dispatch the rest of this file is about: the\n // registry is keyed by class name, a dispatch carries a class name, so\n // `SendEmail.dispatch(...)` on the one under `app/jobs/auth` would run the\n // body of the one under `app/jobs/billing`. Nothing is dropped and nothing\n // errors — the wrong work happens and reports success.\n //\n // Discovery is what makes it ordinary. A hand-written list forces an import\n // alias in one visible file the moment two names clash; a directory walk\n // does not, and `auth/SendEmail.ts` beside `billing/SendEmail.ts` is a\n // perfectly natural thing to write. So the first claim wins and the second\n // is refused out loud — the same rule `Scheduler.start` applies to a cron\n // name, and the reverse of the silent last-wins this replaces.\n this.jobs = {};\n for (const job of jobs) {\n const taken = this.jobs[job.name];\n if (taken) {\n console.error(\n `Two queued jobs are named \"${job.name}\" — the first is registered ` +\n `and this one is not, so dispatching either would have run one of ` +\n `them. A class name is the queue's key, so only one job can hold ` +\n `it. Rename one.`,\n );\n continue;\n }\n this.jobs[job.name] = job;\n }\n }\n\n /**\n * What the manager was handed, discovered or declared.\n *\n * The registry is keyed by name, and a name is exactly what a dispatch\n * carries, so \"is this job registered?\" is a question with a silent wrong\n * answer — `next()` drops an unknown name long after the caller moved on.\n * This is where a test asks it out loud.\n *\n * It reports what came in, not what the registry accepted, so a name claimed\n * twice appears twice here — deliberately, the same way `Scheduler.jobs`\n * does. A test walking this should see the collision rather than have it\n * tidied away. A copy, so that walk cannot edit the registry underneath it.\n */\n get registeredJobs(): ReadonlyArray<new () => Job> {\n return [...this.config.jobs];\n }\n\n dispatchJob(jobName: string, args: string) {\n if (this.jobs[jobName]) {\n const job = new this.jobs[jobName]();\n return job.run(JSON.parse(args));\n }\n }\n\n async next() {\n if (!this.isRunning) {\n this.isRunning = true;\n }\n\n if (this.activeRunningJobsCount >= this.config.concurrency) {\n await new Promise((resolve) => setTimeout(resolve, 1000));\n return this.next();\n }\n\n const jobDefinition = this.queue.values().next().value as\n | JobDefinition\n | undefined;\n\n if (jobDefinition) {\n // Taken off the queue before anything decides what to do with it. The\n // delete used to live inside the `if` below, so a name the registry could\n // not resolve left the head in place, the `size === 0` check below was\n // never reached, and `next()` recursed on the same entry until the stack\n // gave out. An unregistered dispatch is meant to be a dropped job, not a\n // crash — and discovery makes an empty registry newly reachable (a deploy\n // that ships no source finds nothing to register), so the difference\n // stopped being theoretical.\n this.queue.delete(jobDefinition);\n\n if (this.jobs[jobDefinition.class]) {\n this.run(jobDefinition);\n } else {\n // The one place this is observable. A dispatch carries a name, the\n // registry is keyed by name, and a name nobody registered matches\n // nothing — which is exactly the silence #322 is about, except here it\n // has already happened and the work is gone. Saying so is all that is\n // left to do about it.\n console.error(\n `Dropped a queued job: nothing is registered under the name ` +\n `\"${jobDefinition.class}\". If the class exists, it was not ` +\n `discovered — check that it is under the queue slice's jobsDir ` +\n `(app/jobs by default), or list it in app/config/queue.ts.`,\n );\n }\n }\n\n if (this.queue.size === 0) {\n this.isRunning = false;\n return;\n }\n\n await this.next();\n }\n\n private async run(jobDefinition: JobDefinition) {\n const Job = this.jobs[jobDefinition.class];\n const jobInstance = new Job();\n const args: any[] = JSON.parse(jobDefinition.args);\n\n this.activeRunningJobsCount++;\n\n try {\n const result = await (jobInstance.worker\n ? runInWorker(jobDefinition.class, jobDefinition.args)\n : jobInstance.run(...args));\n\n jobInstance.onSuccess(result, ...args);\n } catch (err) {\n jobInstance.onFail(err, ...args);\n if (jobDefinition.retries >= jobInstance.maxAttempts - 1) {\n jobInstance.onDeadletter(err, ...args);\n } else {\n this.push(Job, jobDefinition.args, jobDefinition.retries + 1);\n }\n }\n\n this.activeRunningJobsCount--;\n }\n\n push(job: new () => Job, args: string, retries = 0) {\n this.queue.add({\n class: job.name,\n args,\n createdAt: Date.now(),\n retries,\n });\n if (!this.isRunning) {\n this.next();\n }\n }\n}\n",
|
|
8
|
-
"import { ServiceProvider } from \"../support/ServiceProvider\";\nimport type { AuthConfig } from \"./config\";\nimport { AuthManager } from \"./AuthManager\";\n\nexport class AuthServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n AuthManager,\n () => new AuthManager(this.app.config.get<AuthConfig>(\"auth\", {})),\n );\n }\n}\n",
|
|
9
|
-
"// A cron expression accepted by `Bun.cron`: a 5-field expression\n// (`minute hour day-of-month month day-of-week`, interpreted in UTC) or one of\n// the nicknames below. The `string & {}` member keeps arbitrary expressions\n// assignable while still surfacing the nicknames in editor autocomplete.\nexport type CronExpression =\n | \"@yearly\"\n | \"@annually\"\n | \"@monthly\"\n | \"@weekly\"\n | \"@daily\"\n | \"@midnight\"\n | \"@hourly\"\n | (string & {});\n\nexport class CronJob {\n name: string;\n cron: CronExpression;\n\n /**\n * Decides whether this tick happens at all. Evaluated once per tick, before\n * anything else; returning `false` skips `onTick`, `callback` and\n * `onComplete` alike.\n *\n * ### Why this is a member and not a line the job writes for itself\n *\n * A job that reports outward — mails a digest, opens a Sentry issue, pings a\n * channel — must not fire from a laptop, and the guard for that has to cover\n * the whole tick. There was nowhere to write it. gemi calls `onTick` and\n * `onComplete` *outside* `callback`, so a guard placed in a lifecycle hook\n * only stops the hook it lives in: an early `return` at the top of `callback`\n * leaves the job still announcing that it started and still announcing that\n * it finished, which for an outward-reporting job is most of the damage.\n *\n * The only construct that covered every hook was an abstract base class that\n * shadowed `callback` with the guard and renamed the real body to `run()` —\n * and that costs the app the framework's own vocabulary. A job then reads\n * `async run()` while every page of the documentation reads `async\n * callback()`, so the next person to follow the docs writes a `callback` the\n * base class never calls: no error, no output, a job that silently does\n * nothing. Forgetting *this* member fails the other way round — the job runs,\n * which is what it did before anyone asked for a gate.\n *\n * ### What it gates\n *\n * Work, not registration. A job whose gate returns `false` is still\n * scheduled, still holds its `Bun.cron` handle, and still appears in\n * `app(Scheduler).jobs` — \"is it scheduled\" and \"will it do anything\" are\n * different questions and the second one is answered per tick. That is also\n * why this is a method: a field would be read once, when the scheduler\n * constructs the job at boot, and would freeze whatever it saw then.\n *\n * A gate that throws is logged and treated as `false`. Cron work recurs, so\n * a skipped tick is recovered by the next one, while an email sent from the\n * wrong machine is not recovered at all.\n *\n * ### Composing with a base class\n *\n * Nothing here calls `super` for you. A subclass that overrides `shouldRun`\n * for its own reason silently replaces whatever its base was gating on, so\n * narrow rather than redefine:\n *\n * ```typescript\n * async shouldRun() {\n * return (await super.shouldRun()) && this.hasSubscribers();\n * }\n * ```\n *\n * A shared base should annotate its return type as written here rather than\n * let it be inferred: a base returning a plain `boolean` narrows the\n * signature its subclasses inherit, and the `await super` above then does not\n * typecheck against it.\n */\n shouldRun(): Promise<boolean> | boolean {\n return true;\n }\n\n callback(): Promise<void> | void {}\n onTick(): Promise<void> | void {}\n onComplete(): Promise<void> | void {}\n\n static exp(expression: CronExpression) {\n return expression;\n }\n}\n",
|
|
10
|
-
"import type { CronJob } from \"./CronJob\";\n\n// Config key: `schedule`. Consumed by `ScheduleServiceProvider`.\nexport interface ScheduleConfig {\n /**\n * The `CronJob` subclasses this application runs on a schedule, or nothing.\n *\n * ### Why \"or nothing\" is the point\n *\n * A cron job that is written and not listed here never fires, and unlike a\n * dropped dispatch there is no caller left waiting to notice: nothing is\n * downstream of a tick that did not happen. The report is simply not sent,\n * for as long as it takes somebody to ask why they stopped seeing it. That is\n * the whole failure — the file exists, the expression is right, and the class\n * was never in the module graph.\n *\n * Leaving this out spells the schedule once: the jobs are the classes under\n * `jobsDir`.\n *\n * ### The rule, exactly\n *\n * **Declared wins, and `[]` is declared.** A `jobs` that is present is used\n * verbatim and no directory is read — the escape hatch for an app whose jobs\n * live somewhere this cannot walk, for one that runs a deliberate subset, and\n * for a deploy that ships no source. An empty array means an app with nothing\n * scheduled and says so; it does not mean \"find some\".\n *\n * **Absent or `undefined` discovers.** `undefined` counts as absent for the\n * same reason `withDefaults` treats it that way everywhere else: a key spread\n * in from an optional value is an omission, not an instruction.\n *\n * Either way the set the scheduler ended up with is readable afterwards, as\n * `app(Scheduler).jobs` — an app that used to import this array to assert\n * something about its own schedule asks the scheduler instead.\n */\n jobs?: Array<new () => CronJob>;\n\n /**\n * Where to look when `jobs` was not declared. Relative to the project root,\n * or absolute.\n *\n * Every `.ts`/`.tsx` file underneath it is imported at boot and every exported\n * class extending `CronJob` is scheduled, so this wants to be a directory of\n * cron declarations rather than a directory that merely contains some. Moving\n * the jobs is what this field is for; pointing it at `app/` is not.\n */\n jobsDir?: string;\n}\n\nexport function defineScheduleConfig(config: ScheduleConfig): ScheduleConfig {\n return config;\n}\n\nexport function scheduleConfigDefaults(): Required<ScheduleConfig> {\n return {\n jobs: [],\n jobsDir: \"app/cron\",\n };\n}\n",
|
|
11
|
-
"import { app } from \"../../foundation/app\";\nimport { QueueManager } from \"./QueueManager\";\n\nexport class Job {\n static name = \"unset\";\n worker = false;\n maxAttempts = 3;\n\n run(..._args: any[]): Promise<any> | any {}\n\n onFail(_error: Error, ..._args: any[]): void {}\n onSuccess(_result: any, ..._args: any[]): void {}\n onDeadletter(_error: Error, ..._args: any[]): void {}\n\n static dispatch<T extends Job>(\n this: new () => T,\n ...args: Parameters<T[\"run\"]>\n ): void {\n if (this.name === \"unset\") {\n throw new Error(\"Cannot dispatch a job with no name\");\n }\n\n app(QueueManager).push(this, JSON.stringify(args));\n }\n}\n",
|
|
12
|
-
"import { existsSync } from \"node:fs\";\nimport { dirname, isAbsolute, join } from \"node:path\";\n\nimport { discoverClasses, projectRoot } from \"../support/discover\";\nimport { CronJob } from \"./cron/CronJob\";\nimport { scheduleConfigDefaults } from \"./cron/config\";\nimport { Job } from \"./queue/Job\";\nimport { queueConfigDefaults } from \"./queue/config\";\n\n/**\n * Reading an application's jobs off the filesystem, for the two subsystems that\n * were asking every app to write its job list twice.\n *\n * Both functions here are the same call to `discoverClasses` with a different\n * base class, which is deliberate: one walk, one set of skip rules, one answer\n * to \"what does this app have\". They are exported from `gemi/services` rather\n * than kept private to the providers because the list stopped being something\n * an app could import — a test that used to walk the exported `jobs` array\n * needs somewhere to ask the same question, and so does a script.\n *\n * What the providers add on top of these is the decision of *whether* to ask,\n * which is the config's to make and not this file's.\n */\n\n/**\n * A directory the app named, resolved the way the app meant it.\n *\n * Relative paths hang off the project root rather than `process.cwd()` at the\n * moment of the call, because a config slice saying `app/jobs` means the app's\n * `app/jobs` and not whatever directory somebody happened to launch a script\n * from. Absolute paths are taken as given, which is what makes these callable\n * from a test that has no project around it.\n */\nfunction resolveDir(dir: string): string {\n return isAbsolute(dir) ? dir : join(projectRoot(), dir);\n}\n\n/**\n * Says something when the directory is missing *and so is the tree it lives in*.\n *\n * A missing directory has two readings and they want opposite responses. An app\n * that has never written a cron job has no `app/cron`, and warning about that on\n * every boot for the rest of its life is noise — the answer \"you have no cron\n * jobs\" is correct and uninteresting. But a deploy that ships `dist/` and\n * `node_modules/` without the source has no `app/cron` *and no `app/`*, and\n * there discovery quietly returns nothing while the app very much does have\n * jobs: the exact silence #322 and #323 exist to remove, reintroduced one layer\n * down and in production only.\n *\n * The parent directory is what tells them apart. If neither the directory nor\n * the one above it is there, the tree this was pointed into is not there\n * either, and that is worth a sentence.\n */\nfunction warnIfSourceIsMissing(\n resolved: string,\n subject: string,\n slice: string,\n) {\n if (existsSync(resolved) || existsSync(dirname(resolved))) return;\n\n console.warn(\n `No ${subject} were discovered: neither ${resolved} nor the directory ` +\n `above it exists, so this process cannot see the application's source. ` +\n `If this is a deploy that ships only the build output, list the classes ` +\n `explicitly in app/config/${slice}.ts so they travel with it.`,\n );\n}\n\n/**\n * The `Job` subclasses under `dir`, defaulting to the config slice's `app/jobs`.\n *\n * This is what a `queue` config slice with no `jobs` key resolves to. Call it\n * directly to ask the question a test used to answer by importing the array:\n * every job this application would register, in the order it would register\n * them.\n *\n * Every file underneath is imported, so calling this runs the app's job modules\n * — the cost `discoverClasses` documents at length, paid once per call.\n */\nexport async function discoverJobs(\n dir: string = queueConfigDefaults().jobsDir,\n): Promise<Array<new () => Job>> {\n const resolved = resolveDir(dir);\n warnIfSourceIsMissing(resolved, \"queue jobs\", \"queue\");\n const jobs = await discoverClasses(resolved, Job);\n warnIfNameWillNotSurviveTheBuild(jobs);\n return jobs;\n}\n\n/**\n * Says something about a discovered job that never declared `static name`.\n *\n * This is the one hazard discovery introduces rather than removes, and it is\n * production-only. `gemi build` minifies the server entry, and app code\n * reachable from it — a controller, and every job class that controller\n * imports to dispatch — is bundled and minified with it. Minification renames\n * the class binding, and a class's implicit `.name` is that binding: a\n * `TestJob` in the bundle reports `\"D\"`. Discovery, meanwhile, imports\n * `app/jobs/TestJob.ts` from source at runtime, where it is still `\"TestJob\"`.\n * The queue is keyed by that name, so the two halves stop agreeing and the\n * dispatch is dropped — in production, and only there.\n *\n * A declared `static name = \"TestJob\"` is a string literal, which survives\n * minification intact, and the two halves agree again. `docs/jobs-and-queues.md`\n * has always called it required; before discovery an app could get away without\n * it, because the explicit `jobs` array was bundled too and both sides were\n * wrong in the same way.\n *\n * The check is exact rather than a guess: a `static name = \"...\"` class field\n * defines a *writable* own property, while the implicit class name is\n * non-writable. Cron is unaffected — `CronJob.name` is an instance field\n * holding a string literal, not the class binding.\n */\nfunction warnIfNameWillNotSurviveTheBuild(jobs: Array<new () => Job>) {\n for (const job of jobs) {\n if (Object.getOwnPropertyDescriptor(job, \"name\")?.writable) continue;\n\n console.warn(\n `Queued job ${job.name} does not declare \\`static name\\`, so it is ` +\n `registered under its class name. A production build minifies the ` +\n `class that dispatches it and renames it, while discovery reads this ` +\n `file from source and does not — so the dispatch would be dropped in ` +\n `production and nowhere else. Add: static name = \"${job.name}\";`,\n );\n }\n}\n\n/**\n * The `CronJob` subclasses under `dir`, defaulting to the config slice's\n * `app/cron`.\n *\n * This is what a `schedule` config slice with no `jobs` key resolves to. Call it\n * directly to ask the question a test used to answer by importing the array —\n * though after boot `app(Scheduler).jobs` is the better one, because it is what\n * the scheduler actually took rather than what this would find if asked again.\n *\n * Every file underneath is imported, so calling this runs the app's cron\n * modules — the cost `discoverClasses` documents at length, paid once per call.\n */\nexport async function discoverCronJobs(\n dir: string = scheduleConfigDefaults().jobsDir,\n): Promise<Array<new () => CronJob>> {\n const resolved = resolveDir(dir);\n warnIfSourceIsMissing(resolved, \"cron jobs\", \"schedule\");\n return await discoverClasses(resolved, CronJob);\n}\n",
|
|
13
|
-
"import { kernelContext } from \"../../kernel/context\";\nimport { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { discoverCronJobs } from \"../discovery\";\nimport { scheduleConfigDefaults, type ScheduleConfig } from \"./config\";\nimport { Scheduler } from \"./Scheduler\";\n\nexport class ScheduleServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n Scheduler,\n () =>\n new Scheduler(\n withDefaults(\n scheduleConfigDefaults(),\n this.app.config.get<ScheduleConfig>(\"schedule\", {}),\n ),\n ),\n );\n }\n\n /**\n * Resolves the schedule, then starts it.\n *\n * Cron jobs are scheduled in phase two, not in `register()`: a job body is\n * free to resolve any service, and by the time `boot()` runs every provider\n * has registered. Each tick re-enters the application context so the job sees\n * the same container a request handler would.\n *\n * ### Why the raw slice and not the scheduler's config\n *\n * The decision here is whether the app said anything, and by the time the\n * scheduler holds a config it can no longer tell: `withDefaults` treats an\n * absent key and an `undefined` one alike and substitutes the default `[]`,\n * which is the same value an app writes when it means \"nothing scheduled, and\n * I mean it\". Reading the slice before defaults are applied is the only place\n * that difference still exists.\n *\n * So: `jobs` present, including `jobs: []`, is used verbatim and no directory\n * is read. Absent or `undefined` — which includes an app with no `schedule`\n * slice at all — the classes under `jobsDir` are, which is the case #323 is\n * about: a cron job that is written and never listed fires never, and nothing\n * downstream is waiting to notice.\n */\n async boot() {\n const scheduler = this.app.make(Scheduler);\n const slice = this.app.config.get<ScheduleConfig>(\"schedule\", {});\n\n if (slice.jobs === undefined) {\n const { jobsDir } = withDefaults(scheduleConfigDefaults(), slice);\n scheduler.useJobs(await discoverCronJobs(jobsDir));\n }\n\n const app = this.app;\n scheduler.start((cb) => kernelContext.run(app, cb));\n }\n}\n",
|
|
14
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport type { MailConfig } from \"./config\";\nimport { MailManager } from \"./MailManager\";\n\nexport class MailServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n MailManager,\n () => new MailManager(this.app.config.get<MailConfig>(\"mail\", {})),\n );\n }\n}\n",
|
|
15
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport type { FilesystemConfig } from \"./config\";\nimport { FilesystemManager } from \"./FilesystemManager\";\n\nexport class FilesystemServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n FilesystemManager,\n () =>\n new FilesystemManager(\n this.app.config.get<FilesystemConfig>(\"filesystem\", {}),\n ),\n );\n }\n}\n",
|
|
16
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport type { ImageConfig } from \"./config\";\nimport { ImageManager } from \"./ImageManager\";\n\nexport class ImageServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n ImageManager,\n () => new ImageManager(this.app.config.get<ImageConfig>(\"image\", {})),\n );\n }\n}\n",
|
|
17
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { KernelId } from \"./KernelId\";\n\n// Config-less by nature: the id is generated per process, never declared.\nexport class KernelIdServiceProvider extends ServiceProvider {\n register() {\n this.app.instance(KernelId, new KernelId());\n }\n}\n",
|
|
18
|
-
"import type { LogEntry } from \"./types\";\n\n// Config key: `log`. Derived from `LoggingServiceProvider`.\nexport interface LogConfig {\n maxFileSize?: number;\n\n onLogFileClosed?: (file: File) => void | Promise<void>;\n onLogCreated?: (logEntry: LogEntry) => void | Promise<void>;\n}\n\nexport function defineLogConfig(config: LogConfig): LogConfig {\n return config;\n}\n\nexport function logConfigDefaults(): Required<LogConfig> {\n return {\n maxFileSize: 1024 * 1024 * 10, // 10MB\n onLogFileClosed: () => {},\n onLogCreated: () => {},\n };\n}\n",
|
|
19
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { LogManager } from \"./LogManager\";\nimport { logConfigDefaults, type LogConfig } from \"./config\";\n\nexport class LogServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n LogManager,\n () =>\n new LogManager(\n withDefaults(\n logConfigDefaults(),\n this.app.config.get<LogConfig>(\"log\", {}),\n ),\n ),\n );\n }\n\n /**\n * The log directory is created once at kernel boot rather than on the first\n * `Log.info()`, which is what the old eagerly-constructed container did.\n * Resolving here is deliberate: it is the one service whose readiness is a\n * boot-time side effect, and paying for it lazily mid-request would add file\n * IO to whichever handler happened to log first.\n */\n async boot() {\n await this.app.make(LogManager).boot();\n }\n}\n",
|
|
20
|
-
"import {\n middlewareConfigDefaults,\n type MiddlewareConfig,\n} from \"../../http/middleware-config\";\nimport { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { MiddlewareRegistry } from \"./MiddlewareRegistry\";\n\nexport class MiddlewareServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n MiddlewareRegistry,\n () =>\n new MiddlewareRegistry(\n withDefaults(\n middlewareConfigDefaults(),\n this.app.config.get<MiddlewareConfig>(\"middleware\", {}),\n ),\n ),\n );\n }\n}\n",
|
|
21
|
-
"import type { BroadcastingChannel } from \"../../broadcasting/BroadcastingChannel\";\n\n// Config key: `broadcast`. Derived from `BroadcastingServiceProvider`.\nexport interface BroadcastConfig {\n channels?: Record<string, new () => BroadcastingChannel>;\n}\n\nexport function defineBroadcastConfig(\n config: BroadcastConfig,\n): BroadcastConfig {\n return config;\n}\n\nexport function broadcastConfigDefaults(): Required<BroadcastConfig> {\n return {\n channels: {},\n };\n}\n",
|
|
22
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { BroadcastManager } from \"./BroadcastManager\";\nimport { broadcastConfigDefaults, type BroadcastConfig } from \"./config\";\n\nexport class BroadcastServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n BroadcastManager,\n () =>\n new BroadcastManager(\n withDefaults(\n broadcastConfigDefaults(),\n this.app.config.get<BroadcastConfig>(\"broadcast\", {}),\n ),\n ),\n );\n }\n}\n",
|
|
23
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { discoverJobs } from \"../discovery\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { queueConfigDefaults, type QueueConfig } from \"./config\";\nimport { QueueManager } from \"./QueueManager\";\n\nexport class QueueServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n QueueManager,\n () => new QueueManager(this.app.config.get<QueueConfig>(\"queue\", {})),\n );\n }\n\n /**\n * Fills in the job registry when the app did not declare one.\n *\n * ### Why the raw slice and not the manager's config\n *\n * The decision here is whether the app said anything, and by the time the\n * manager holds a config it can no longer tell: `withDefaults` treats an\n * absent key and an `undefined` one alike and substitutes the default `[]`,\n * which is the same value an app writes when it means \"no jobs, and I mean\n * it\". Reading the slice before defaults are applied is the only place the\n * difference still exists.\n *\n * So: `jobs` present, including `jobs: []`, is used verbatim and no directory\n * is read. Absent or `undefined`, the classes under `jobsDir` are.\n *\n * ### Why phase two\n *\n * Discovery imports every file it walks, which is asynchronous, and\n * `register()` is not. It also has to happen before the first dispatch rather\n * than before the first tick — there is no equivalent of the scheduler's\n * `start()` to hang it off, so the registry has to be complete by the end of\n * boot.\n */\n async boot() {\n const slice = this.app.config.get<QueueConfig>(\"queue\", {});\n if (slice.jobs !== undefined) return;\n\n const { jobsDir } = withDefaults(queueConfigDefaults(), slice);\n this.app.make(QueueManager).useJobs(await discoverJobs(jobsDir));\n }\n}\n",
|
|
24
|
-
"import type { ConsumeParams, RateLimitResult } from \"../types\";\n\nexport abstract class RateLimiterDriver {\n /**\n * Records one request against `params.key` and reports whether it fits.\n *\n * Implementations must decide *and* record atomically — two concurrent calls\n * for the same key may never both be told they were the last one that fit.\n *\n * May be sync or async: the in-memory driver answers immediately, a network\n * backed driver returns a promise. Callers always `await` the result.\n */\n abstract consume(\n params: ConsumeParams,\n ): Promise<RateLimitResult> | RateLimitResult;\n}\n",
|
|
25
|
-
"import type { RateLimitResult } from \"./types\";\n\n/**\n * The sliding-window-counter algorithm, shared by every driver.\n *\n * A fixed window is cheap but lets a client spend its whole budget at the end\n * of one window and again at the start of the next — twice the limit across a\n * window's worth of time. A true sliding log fixes that but has to keep a\n * timestamp per request.\n *\n * The counter variant keeps two integers per key (this window and the one\n * before it) and estimates usage by weighting the previous window by how much\n * of it still overlaps the trailing window:\n *\n * usage = previous * (window - elapsed) / window + current\n *\n * That is a ~0.003% error on real traffic and costs two integers per key, so\n * it is what the in-memory and Redis drivers both implement.\n */\n\n/** Epoch ms of the start of the window `now` falls into. */\nexport function windowStartFor(now: number, window: number) {\n return Math.floor(now / window) * window;\n}\n\n/** Weight of the previous window's count, from `1` down to `0`. */\nexport function previousWeight(elapsed: number, window: number) {\n if (window <= 0) return 0;\n return Math.min(1, Math.max(0, (window - elapsed) / window));\n}\n\n/** Estimated usage of the trailing `window` ending at `now`. */\nexport function estimateUsage(\n current: number,\n previous: number,\n elapsed: number,\n window: number,\n) {\n return previous * previousWeight(elapsed, window) + current;\n}\n\n/**\n * How long until a request costing `cost` would be admitted, in milliseconds.\n *\n * Solves the usage equation for `elapsed` instead of guessing: the caller gets\n * a `Retry-After` that lands the moment capacity actually frees up rather than\n * at the end of the window, which for a sliding window is usually far too late.\n */\nexport function timeUntilAllowed(params: {\n current: number;\n previous: number;\n elapsed: number;\n window: number;\n limit: number;\n cost: number;\n}) {\n const { current, previous, elapsed, window, limit, cost } = params;\n // Usage the bucket may sit at for this request to still fit.\n const budget = limit - cost;\n\n // The request can never fit, no matter how long the caller waits. Report a\n // full window rather than `Infinity` so a `Retry-After` header stays usable.\n if (budget < 0) return window;\n\n const remainingWindow = Math.max(0, window - elapsed);\n\n // Phase 1 — capacity frees up inside the current window as `previous` decays.\n if (previous > 0) {\n // previous * (window - e) / window + current = budget => solve for e\n const target = window * (1 - (budget - current) / previous);\n if (target <= window) {\n return Math.max(0, Math.ceil(target - elapsed));\n }\n }\n\n // Phase 2 — `current` alone is already over budget, so nothing frees up until\n // the window rolls and `current` becomes the decaying `previous`.\n if (current <= budget) return remainingWindow;\n if (current <= 0) return remainingWindow;\n\n const target = window * (1 - budget / current);\n return remainingWindow + Math.max(0, Math.ceil(target));\n}\n\n/**\n * Turns a driver's post-operation counters into the result the caller sees.\n * `current` and `previous` are the counts *after* an allowed request has been\n * recorded, so every driver reports the same numbers for the same state.\n */\nexport function buildResult(params: {\n allowed: boolean;\n current: number;\n previous: number;\n now: number;\n elapsed: number;\n window: number;\n limit: number;\n cost: number;\n}): RateLimitResult {\n const { allowed, current, previous, now, elapsed, window, limit, cost } =\n params;\n\n if (allowed) {\n const usage = estimateUsage(current, previous, elapsed, window);\n return {\n allowed: true,\n limit,\n remaining: Math.max(0, Math.floor(limit - usage)),\n resetAt: now - elapsed + window,\n retryAfter: 0,\n };\n }\n\n const retryAfter = timeUntilAllowed({\n current,\n previous,\n elapsed,\n window,\n limit,\n cost,\n });\n\n return {\n allowed: false,\n limit,\n remaining: 0,\n resetAt: now + retryAfter,\n retryAfter,\n };\n}\n",
|
|
26
|
-
"import { buildResult, estimateUsage, windowStartFor } from \"../slidingWindow\";\nimport type { ConsumeParams, RateLimitResult } from \"../types\";\nimport { RateLimiterDriver } from \"./RateLimiterDriver\";\n\ninterface Bucket {\n /** Window length this bucket was counted with, so config changes reset it. */\n window: number;\n windowStart: number;\n current: number;\n previous: number;\n}\n\nexport interface InMemoryRateLimiterOptions {\n /**\n * Hard cap on tracked keys. Reached only by traffic that never repeats a key\n * (spoofed `x-forwarded-for`, for instance); the map is swept of dead buckets\n * first and only then evicts live ones, oldest touched first.\n *\n * Defaults to 100k keys — a few MB, and far more than a single instance\n * legitimately sees inside one window.\n */\n maxKeys?: number;\n}\n\n/**\n * Per-process rate limiter. The default driver, and the right one for a single\n * instance or for development.\n *\n * State lives in this process only: with N instances behind a load balancer the\n * effective limit is N × the configured limit, and it resets on deploy. Point\n * the service provider at `RedisRateLimiter` once you run more than one.\n */\nexport class InMemoryRateLimiter extends RateLimiterDriver {\n private buckets = new Map<string, Bucket>();\n private maxKeys: number;\n\n constructor(options: InMemoryRateLimiterOptions = {}) {\n super();\n this.maxKeys = Math.max(1, options.maxKeys ?? 100_000);\n }\n\n consume(params: ConsumeParams): RateLimitResult {\n const { key, limit, window } = params;\n const cost = params.cost ?? 1;\n const now = Date.now();\n const windowStart = windowStartFor(now, window);\n const elapsed = now - windowStart;\n\n const bucket = this.roll(key, window, windowStart);\n const usage = estimateUsage(\n bucket.current,\n bucket.previous,\n elapsed,\n window,\n );\n const allowed = usage + cost <= limit;\n\n if (allowed) {\n bucket.current += cost;\n }\n\n // Re-inserting moves the key to the end of the iteration order, which is\n // what makes eviction least-recently-used rather than arbitrary.\n this.buckets.delete(key);\n this.buckets.set(key, bucket);\n if (this.buckets.size > this.maxKeys) {\n this.evict(now);\n }\n\n return buildResult({\n allowed,\n current: bucket.current,\n previous: bucket.previous,\n now,\n elapsed,\n window,\n limit,\n cost,\n });\n }\n\n /** Drops all counters. Mainly useful in tests. */\n clear() {\n this.buckets.clear();\n }\n\n /** Number of tracked keys. Exposed for tests and diagnostics. */\n get size() {\n return this.buckets.size;\n }\n\n /** Advances a bucket to `windowStart`, creating it when absent or stale. */\n private roll(key: string, window: number, windowStart: number): Bucket {\n const bucket = this.buckets.get(key);\n\n if (!bucket || bucket.window !== window) {\n return { window, windowStart, current: 0, previous: 0 };\n }\n\n if (bucket.windowStart === windowStart) {\n return bucket;\n }\n\n if (bucket.windowStart + window === windowStart) {\n bucket.previous = bucket.current;\n bucket.current = 0;\n bucket.windowStart = windowStart;\n return bucket;\n }\n\n // Idle for more than a full window: everything it held has decayed away.\n bucket.previous = 0;\n bucket.current = 0;\n bucket.windowStart = windowStart;\n return bucket;\n }\n\n private evict(now: number) {\n for (const [key, bucket] of this.buckets) {\n // Fully decayed — it would be reset on the next read anyway.\n if (now - bucket.windowStart >= bucket.window * 2) {\n this.buckets.delete(key);\n }\n }\n\n // Still over the cap, so the traffic is live rather than stale. Drop the\n // least recently touched keys; the worst case is that a client whose bucket\n // was dropped gets a fresh budget, which beats growing without bound.\n for (const key of this.buckets.keys()) {\n if (this.buckets.size <= this.maxKeys) break;\n this.buckets.delete(key);\n }\n }\n}\n",
|
|
27
|
-
"import { InMemoryRateLimiter } from \"./drivers/InMemoryRateLimiterDriver\";\nimport type { RateLimiterDriver } from \"./drivers/RateLimiterDriver\";\n\n// Config key: `ratelimiter`. Derived from `RateLimiterServiceProvider`.\nexport interface RateLimiterConfig {\n /**\n * Where counters live. In-process by default; swap in `RedisRateLimiter` once\n * the app runs on more than one instance.\n */\n driver?: RateLimiterDriver;\n\n /** Requests allowed per window when the `rate-limit` DSL omits a limit. */\n limit?: number;\n\n /** Window length in **seconds** when the `rate-limit` DSL omits one. */\n window?: number;\n}\n\nexport function defineRateLimiterConfig(\n config: RateLimiterConfig,\n): RateLimiterConfig {\n return config;\n}\n\nexport function rateLimiterConfigDefaults(): Required<RateLimiterConfig> {\n return {\n driver: new InMemoryRateLimiter(),\n limit: 1000,\n window: 60,\n };\n}\n",
|
|
28
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { RateLimiter } from \"./RateLimiter\";\nimport { rateLimiterConfigDefaults, type RateLimiterConfig } from \"./config\";\n\nexport class RateLimiterServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n RateLimiter,\n () =>\n new RateLimiter(\n withDefaults(\n rateLimiterConfigDefaults(),\n this.app.config.get<RateLimiterConfig>(\"ratelimiter\", {}),\n ),\n ),\n );\n }\n}\n",
|
|
29
|
-
"import type { RedisOptions } from \"bun\";\n\n// Config key: `redis`. Derived from `RedisServiceProvider`.\nexport interface RedisConfig {\n // Connection URL for Bun's Redis client. Defaults to the `REDIS_URL`\n // environment variable (Bun itself falls back to `redis://localhost:6379`\n // when unset).\n url?: string;\n\n // Optional Bun Redis client options (TLS, timeouts, auto-reconnect, ...).\n options?: RedisOptions;\n}\n\nexport function defineRedisConfig(config: RedisConfig): RedisConfig {\n return config;\n}\n\nexport function redisConfigDefaults(): RedisConfig {\n return {\n url: process.env.REDIS_URL,\n options: undefined,\n };\n}\n",
|
|
30
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { withDefaults } from \"../../support/withDefaults\";\nimport { redisConfigDefaults, type RedisConfig } from \"./config\";\nimport { RedisManager } from \"./RedisManager\";\n\nexport class RedisServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n RedisManager,\n () =>\n new RedisManager(\n withDefaults(\n redisConfigDefaults(),\n this.app.config.get<RedisConfig>(\"redis\", {}),\n ),\n ),\n );\n }\n}\n",
|
|
31
|
-
"import { ServiceProvider } from \"../../support/ServiceProvider\";\nimport { ApiRouteDispatcher } from \"./ApiRouteDispatcher\";\nimport { ViewRouteDispatcher } from \"./ViewRouteDispatcher\";\nimport type { ApiRouteConfig, ViewRouteConfig } from \"./config\";\nimport type { Repository } from \"../../support/Repository\";\n\nfunction readConfig<T>(config: Repository, key: string): T {\n const slice = config.get<T>(key);\n if (!slice) {\n throw new Error(\n `Missing \"${key}\" configuration. Route dispatching needs a root router — declare it in \"app/config/route.ts\".`,\n );\n }\n return slice;\n}\n\n/**\n * Owns both route dispatchers. Laravel ships a single RouteServiceProvider for\n * every route file it loads; the api/view split is a config slice, not a\n * separate provider.\n */\nexport class RouteServiceProvider extends ServiceProvider {\n register() {\n this.app.singleton(\n ApiRouteDispatcher,\n () =>\n new ApiRouteDispatcher(\n readConfig<ApiRouteConfig>(this.app.config, \"route.api\"),\n ),\n );\n\n this.app.singleton(\n ViewRouteDispatcher,\n () =>\n new ViewRouteDispatcher(\n readConfig<ViewRouteConfig>(this.app.config, \"route.view\"),\n ),\n );\n }\n\n /**\n * The two dispatchers are the one pair of services that must not be built\n * lazily: constructing them flattens the route tables and runs\n * `assertNoReservedRoutePaths`, so a missing `route` config or a route under\n * a reserved prefix has to fail the boot rather than the first request that\n * happens to hit it.\n */\n boot() {\n this.app.make(ApiRouteDispatcher);\n this.app.make(ViewRouteDispatcher);\n }\n}\n"
|
|
32
|
-
],
|
|
33
|
-
"mappings": "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",
|
|
34
|
-
"debugId": "BF9FE5D10AAECB4B64756E2164756E21",
|
|
35
|
-
"names": []
|
|
36
|
-
}
|