@c9up/ream 0.2.4 → 0.2.6
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/README.md +3 -9
- package/dist/Application.d.ts +85 -5
- package/dist/Application.d.ts.map +1 -1
- package/dist/Application.js +171 -21
- package/dist/Application.js.map +1 -1
- package/dist/ErrorBoundary.d.ts +1 -7
- package/dist/ErrorBoundary.d.ts.map +1 -1
- package/dist/ErrorBoundary.js +31 -31
- package/dist/ErrorBoundary.js.map +1 -1
- package/dist/HttpKernel.js +2 -2
- package/dist/HttpKernel.js.map +1 -1
- package/dist/Ignitor.d.ts +18 -49
- package/dist/Ignitor.d.ts.map +1 -1
- package/dist/Ignitor.js +211 -173
- package/dist/Ignitor.js.map +1 -1
- package/dist/env/Env.d.ts +6 -0
- package/dist/env/Env.d.ts.map +1 -1
- package/dist/env/Env.js +6 -0
- package/dist/env/Env.js.map +1 -1
- package/dist/events/Emitter.d.ts +0 -6
- package/dist/events/Emitter.d.ts.map +1 -1
- package/dist/events/Emitter.js +42 -42
- package/dist/events/Emitter.js.map +1 -1
- package/dist/events/native.d.ts +13 -11
- package/dist/events/native.d.ts.map +1 -1
- package/dist/events/native.js.map +1 -1
- package/dist/helpers/compose.d.ts +30 -0
- package/dist/helpers/compose.d.ts.map +1 -0
- package/dist/helpers/compose.js +27 -0
- package/dist/helpers/compose.js.map +1 -0
- package/dist/helpers/index.d.ts +1 -0
- package/dist/helpers/index.d.ts.map +1 -1
- package/dist/helpers/index.js +1 -0
- package/dist/helpers/index.js.map +1 -1
- package/dist/helpers/is.d.ts +69 -16
- package/dist/helpers/is.d.ts.map +1 -1
- package/dist/helpers/is.js +79 -16
- package/dist/helpers/is.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/middleware/Pipeline.d.ts +9 -3
- package/dist/middleware/Pipeline.d.ts.map +1 -1
- package/dist/middleware/Pipeline.js +10 -4
- package/dist/middleware/Pipeline.js.map +1 -1
- package/dist/native/generated.d.ts +243 -0
- package/dist/native/generated.d.ts.map +1 -0
- package/dist/native/generated.js +7 -0
- package/dist/native/generated.js.map +1 -0
- package/dist/rpc/RpcRouter.js +2 -2
- package/dist/rpc/RpcRouter.js.map +1 -1
- package/dist/services/app.d.ts +6 -0
- package/dist/services/app.d.ts.map +1 -1
- package/dist/services/app.js +8 -0
- package/dist/services/app.js.map +1 -1
- package/dist/services/config.d.ts +15 -0
- package/dist/services/config.d.ts.map +1 -0
- package/dist/services/config.js +18 -0
- package/dist/services/config.js.map +1 -0
- package/dist/services/encryption.d.ts +23 -0
- package/dist/services/encryption.d.ts.map +1 -0
- package/dist/services/encryption.js +33 -0
- package/dist/services/encryption.js.map +1 -0
- package/dist/services/urlBuilder.d.ts +21 -0
- package/dist/services/urlBuilder.d.ts.map +1 -0
- package/dist/services/urlBuilder.js +34 -0
- package/dist/services/urlBuilder.js.map +1 -0
- package/dist/session/drivers/CookieDriver.d.ts +1 -1
- package/dist/session/drivers/CookieDriver.d.ts.map +1 -1
- package/dist/session/drivers/CookieDriver.js +4 -4
- package/dist/session/drivers/CookieDriver.js.map +1 -1
- package/dist/testing/ApiResponse.d.ts +44 -44
- package/dist/testing/ApiResponse.d.ts.map +1 -1
- package/dist/testing/ApiResponse.js +48 -48
- package/dist/testing/ApiResponse.js.map +1 -1
- package/dist/testing/RequestBuilder.d.ts +39 -39
- package/dist/testing/RequestBuilder.d.ts.map +1 -1
- package/dist/testing/RequestBuilder.js +49 -49
- package/dist/testing/RequestBuilder.js.map +1 -1
- package/dist/testing/TestClient.d.ts +5 -5
- package/dist/testing/TestClient.d.ts.map +1 -1
- package/dist/testing/TestClient.js +7 -7
- package/dist/testing/TestClient.js.map +1 -1
- package/events.win32-x64-msvc.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +15 -2
- package/scheduler.win32-x64-msvc.node +0 -0
- package/scripts/build-napi-types.mjs +68 -0
- package/scripts/generate-napi-types.mjs +171 -0
- package/src/Application.ts +221 -21
- package/src/ErrorBoundary.ts +31 -31
- package/src/HttpKernel.ts +2 -2
- package/src/Ignitor.ts +228 -176
- package/src/env/Env.ts +6 -0
- package/src/events/Emitter.ts +42 -42
- package/src/events/native.ts +21 -14
- package/src/helpers/compose.ts +84 -0
- package/src/helpers/index.ts +1 -0
- package/src/helpers/is.ts +87 -16
- package/src/index.ts +3 -3
- package/src/middleware/Pipeline.ts +10 -4
- package/src/native/generated.ts +291 -0
- package/src/rpc/RpcRouter.ts +2 -2
- package/src/services/app.ts +9 -0
- package/src/services/config.ts +24 -0
- package/src/services/encryption.ts +42 -0
- package/src/services/urlBuilder.ts +41 -0
- package/src/session/drivers/CookieDriver.ts +4 -4
- package/src/testing/ApiResponse.ts +52 -52
- package/src/testing/RequestBuilder.ts +55 -55
- package/src/testing/TestClient.ts +8 -8
package/src/env/Env.ts
CHANGED
|
@@ -68,6 +68,12 @@ export class Env<Values extends Record<string, unknown>> {
|
|
|
68
68
|
|
|
69
69
|
readonly #values: Values
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Private on purpose: an `Env` is only ever built by {@link create}, which is
|
|
73
|
+
* what validates. `private` and not `#` because a private CONSTRUCTOR has no
|
|
74
|
+
* native form — this is the one place the keyword carries a meaning `#`
|
|
75
|
+
* cannot express.
|
|
76
|
+
*/
|
|
71
77
|
private constructor(values: Values) {
|
|
72
78
|
this.#values = values
|
|
73
79
|
}
|
package/src/events/Emitter.ts
CHANGED
|
@@ -66,10 +66,10 @@ export type UnsubscribeFunction = () => void
|
|
|
66
66
|
export type AsyncUnsubscribeFunction = () => Promise<void>
|
|
67
67
|
|
|
68
68
|
export class Emitter {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
#bus: EventBus
|
|
70
|
+
#resolver?: EmitterResolver
|
|
71
|
+
#classListeners: Map<EventConstructor, Listener[]> = new Map()
|
|
72
|
+
#stringListeners: Map<string, ListenerFn[]> = new Map()
|
|
73
73
|
/** Set while faking; every emission lands here instead of running. */
|
|
74
74
|
#buffer?: EventsBuffer
|
|
75
75
|
/** Which events are faked. `undefined` means all of them. */
|
|
@@ -78,8 +78,8 @@ export class Emitter {
|
|
|
78
78
|
readonly #anySubscriptions = new Map<(eventName: string, data: unknown) => void, Set<number>>()
|
|
79
79
|
|
|
80
80
|
constructor(bus: EventBus, resolver?: EmitterResolver) {
|
|
81
|
-
this
|
|
82
|
-
this
|
|
81
|
+
this.#bus = bus
|
|
82
|
+
this.#resolver = resolver
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// ─── Class-based events ───────────────────────────────────
|
|
@@ -102,18 +102,18 @@ export class Emitter {
|
|
|
102
102
|
on<T>(event: string, listener: ListenerFn<T>): UnsubscribeFunction
|
|
103
103
|
on(event: EventConstructor | string, listener: Listener): UnsubscribeFunction {
|
|
104
104
|
if (typeof event === 'string') {
|
|
105
|
-
const list = this
|
|
105
|
+
const list = this.#stringListeners.get(event) ?? []
|
|
106
106
|
list.push(listener as ListenerFn)
|
|
107
|
-
this
|
|
107
|
+
this.#stringListeners.set(event, list)
|
|
108
108
|
return () => {
|
|
109
109
|
this.off(event, listener as ListenerFn)
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
|
-
const list = this
|
|
112
|
+
const list = this.#classListeners.get(event) ?? []
|
|
113
113
|
list.push(listener)
|
|
114
|
-
this
|
|
114
|
+
this.#classListeners.set(event, list)
|
|
115
115
|
return () => {
|
|
116
|
-
const current = this
|
|
116
|
+
const current = this.#classListeners.get(event)
|
|
117
117
|
if (!current) return
|
|
118
118
|
const index = current.indexOf(listener)
|
|
119
119
|
if (index !== -1) current.splice(index, 1)
|
|
@@ -159,11 +159,11 @@ export class Emitter {
|
|
|
159
159
|
|
|
160
160
|
/** Remove one listener from an event (AdonisJS `off`). */
|
|
161
161
|
off(event: string, listener: ListenerFn): void {
|
|
162
|
-
const list = this
|
|
162
|
+
const list = this.#stringListeners.get(event)
|
|
163
163
|
if (!list) return
|
|
164
164
|
const index = list.indexOf(listener)
|
|
165
165
|
if (index !== -1) list.splice(index, 1)
|
|
166
|
-
if (list.length === 0) this
|
|
166
|
+
if (list.length === 0) this.#stringListeners.delete(event)
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
/** Alias of {@link off} (AdonisJS `clearListener`). */
|
|
@@ -173,13 +173,13 @@ export class Emitter {
|
|
|
173
173
|
|
|
174
174
|
/** Drop every listener of one event (AdonisJS `clearListeners`). */
|
|
175
175
|
clearListeners(event: string): void {
|
|
176
|
-
this
|
|
176
|
+
this.#stringListeners.delete(event)
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
/** Drop every listener of every event (AdonisJS `clearAllListeners`). */
|
|
180
180
|
clearAllListeners(): void {
|
|
181
|
-
this
|
|
182
|
-
this
|
|
181
|
+
this.#stringListeners.clear()
|
|
182
|
+
this.#classListeners.clear()
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
/**
|
|
@@ -191,17 +191,17 @@ export class Emitter {
|
|
|
191
191
|
*/
|
|
192
192
|
get eventsListeners(): Map<EventConstructor | string, Listener[]> {
|
|
193
193
|
const all = new Map<EventConstructor | string, Listener[]>()
|
|
194
|
-
for (const [event, listeners] of this
|
|
195
|
-
for (const [event, listeners] of this
|
|
194
|
+
for (const [event, listeners] of this.#classListeners) all.set(event, [...listeners])
|
|
195
|
+
for (const [event, listeners] of this.#stringListeners) all.set(event, [...listeners])
|
|
196
196
|
return all
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
/** How many listeners an event has, or all of them (AdonisJS `listenerCount`). */
|
|
200
200
|
listenerCount(event?: string): number {
|
|
201
|
-
if (event !== undefined) return this
|
|
201
|
+
if (event !== undefined) return this.#stringListeners.get(event)?.length ?? 0
|
|
202
202
|
let total = 0
|
|
203
|
-
for (const list of this
|
|
204
|
-
for (const list of this
|
|
203
|
+
for (const list of this.#stringListeners.values()) total += list.length
|
|
204
|
+
for (const list of this.#classListeners.values()) total += list.length
|
|
205
205
|
return total
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -216,7 +216,7 @@ export class Emitter {
|
|
|
216
216
|
*/
|
|
217
217
|
async #dispatchListeners(event: string, data: unknown, serial: boolean): Promise<void> {
|
|
218
218
|
const listeners = [
|
|
219
|
-
...(this
|
|
219
|
+
...(this.#stringListeners.get(event) ?? []),
|
|
220
220
|
...this.#wildcardListenersFor(event),
|
|
221
221
|
]
|
|
222
222
|
if (listeners.length === 0) return
|
|
@@ -224,7 +224,7 @@ export class Emitter {
|
|
|
224
224
|
try {
|
|
225
225
|
await fn(data)
|
|
226
226
|
} catch (err) {
|
|
227
|
-
this
|
|
227
|
+
this.#emitError(event, err)
|
|
228
228
|
}
|
|
229
229
|
}
|
|
230
230
|
if (serial) {
|
|
@@ -281,17 +281,17 @@ export class Emitter {
|
|
|
281
281
|
try {
|
|
282
282
|
payload = JSON.stringify(this.#wrapForBus(data))
|
|
283
283
|
} catch (err) {
|
|
284
|
-
this
|
|
284
|
+
this.#emitError(event, err)
|
|
285
285
|
return
|
|
286
286
|
}
|
|
287
|
-
void this
|
|
288
|
-
this
|
|
287
|
+
void this.#bus.emit(event, payload).catch((err: unknown) => {
|
|
288
|
+
this.#emitError(event, err)
|
|
289
289
|
})
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
/** Emit an error event for listener failures. */
|
|
293
|
-
|
|
294
|
-
const errorListeners = this
|
|
293
|
+
#emitError(event: string, error: unknown): void {
|
|
294
|
+
const errorListeners = this.#stringListeners.get('emitter:error')
|
|
295
295
|
if (errorListeners && errorListeners.length > 0) {
|
|
296
296
|
for (const fn of errorListeners) {
|
|
297
297
|
try {
|
|
@@ -314,7 +314,7 @@ export class Emitter {
|
|
|
314
314
|
// Faked by the class, as AdonisJS does — `fake([TaskDeclared])` catches
|
|
315
315
|
// `new TaskDeclared(...).emit()`.
|
|
316
316
|
if (this.#capture(EventClass, event)) return
|
|
317
|
-
const listeners = this
|
|
317
|
+
const listeners = this.#classListeners.get(EventClass) ?? []
|
|
318
318
|
const name = (EventClass as { eventName?: string }).eventName ?? classToEventName(EventClass)
|
|
319
319
|
|
|
320
320
|
// Per-listener error isolation — mirrors the string `emit()` contract.
|
|
@@ -326,22 +326,22 @@ export class Emitter {
|
|
|
326
326
|
try {
|
|
327
327
|
if (isListenerClass(listener)) {
|
|
328
328
|
// Listener class — resolve via container for @inject() support
|
|
329
|
-
const instance = this
|
|
330
|
-
? await this
|
|
329
|
+
const instance = this.#resolver
|
|
330
|
+
? await this.#resolver.make(listener as ListenerConstructor<T>)
|
|
331
331
|
: new (listener as ListenerConstructor<T>)()
|
|
332
332
|
await instance.handle(event)
|
|
333
333
|
} else {
|
|
334
334
|
await (listener as ListenerFn<T>)(event)
|
|
335
335
|
}
|
|
336
336
|
} catch (err) {
|
|
337
|
-
this
|
|
337
|
+
this.#emitError(name, err)
|
|
338
338
|
}
|
|
339
339
|
}
|
|
340
340
|
|
|
341
341
|
// Also push through EventBus — same correlation-envelope wrapping as
|
|
342
342
|
// the string-event path so distributed tracing covers class events too.
|
|
343
343
|
// Reached unconditionally: a listener failure above no longer skips it.
|
|
344
|
-
await this
|
|
344
|
+
await this.#bus.emit(name, JSON.stringify(this.#wrapForBus(event)))
|
|
345
345
|
}
|
|
346
346
|
|
|
347
347
|
// ─── Faking (AdonisJS `fake` / `restore`) ─────────────────
|
|
@@ -436,7 +436,7 @@ export class Emitter {
|
|
|
436
436
|
pattern: string,
|
|
437
437
|
listener: (eventName: string, data: unknown) => void,
|
|
438
438
|
): Promise<number> {
|
|
439
|
-
return this
|
|
439
|
+
return this.#bus.subscribe(pattern, (eventJson: string) => {
|
|
440
440
|
try {
|
|
441
441
|
const event = JSON.parse(eventJson)
|
|
442
442
|
// The bus delivers a Rust `Event` envelope whose `correlationId`
|
|
@@ -471,7 +471,7 @@ export class Emitter {
|
|
|
471
471
|
*/
|
|
472
472
|
async offAny(listenerOrId: number | ((eventName: string, data: unknown) => void)): Promise<this> {
|
|
473
473
|
if (typeof listenerOrId === 'number') {
|
|
474
|
-
await this
|
|
474
|
+
await this.#bus.unsubscribe(listenerOrId)
|
|
475
475
|
for (const ids of this.#anySubscriptions.values()) ids.delete(listenerOrId)
|
|
476
476
|
return this
|
|
477
477
|
}
|
|
@@ -479,13 +479,13 @@ export class Emitter {
|
|
|
479
479
|
const ids = this.#anySubscriptions.get(listenerOrId)
|
|
480
480
|
if (!ids) return this
|
|
481
481
|
this.#anySubscriptions.delete(listenerOrId)
|
|
482
|
-
await Promise.all([...ids].map((id) => this
|
|
482
|
+
await Promise.all([...ids].map((id) => this.#bus.unsubscribe(id)))
|
|
483
483
|
return this
|
|
484
484
|
}
|
|
485
485
|
|
|
486
486
|
/** Check if a pattern matches an event name (wildcard matching via Rust). */
|
|
487
487
|
matchesPattern(pattern: string, eventName: string): boolean {
|
|
488
|
-
return this
|
|
488
|
+
return this.#bus.matchesWildcard(pattern, eventName)
|
|
489
489
|
}
|
|
490
490
|
|
|
491
491
|
// ─── Request / Reply ──────────────────────────────────────
|
|
@@ -499,7 +499,7 @@ export class Emitter {
|
|
|
499
499
|
// Same correlation injection as `emit()` so a `setCorrelationId`
|
|
500
500
|
// preceding a request actually reaches the responder via the
|
|
501
501
|
// top-level `correlationId` field on the parsed payload.
|
|
502
|
-
const result = await this
|
|
502
|
+
const result = await this.#bus.request(name, JSON.stringify(this.#wrapForBus(data)), timeoutMs)
|
|
503
503
|
try {
|
|
504
504
|
return JSON.parse(result) as T
|
|
505
505
|
} catch {
|
|
@@ -519,7 +519,7 @@ export class Emitter {
|
|
|
519
519
|
name: string,
|
|
520
520
|
handler: (eventJson: string, reply: (response: string) => void) => void,
|
|
521
521
|
): void {
|
|
522
|
-
this
|
|
522
|
+
this.#bus.onRequest(name, handler)
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
// ─── Correlation context ──────────────────────────────────
|
|
@@ -565,13 +565,13 @@ export class Emitter {
|
|
|
565
565
|
// AdonisJS makes the argument optional: no argument asks whether ANY event
|
|
566
566
|
// has a listener at all.
|
|
567
567
|
if (event === undefined) return this.listenerCount() > 0
|
|
568
|
-
if (typeof event === 'string') return (this
|
|
569
|
-
return (this
|
|
568
|
+
if (typeof event === 'string') return (this.#stringListeners.get(event)?.length ?? 0) > 0
|
|
569
|
+
return (this.#classListeners.get(event)?.length ?? 0) > 0
|
|
570
570
|
}
|
|
571
571
|
|
|
572
572
|
/** Get the number of Rust-side subscriptions. */
|
|
573
573
|
async subscriptionCount(): Promise<number> {
|
|
574
|
-
return this
|
|
574
|
+
return this.#bus.subscriptionCount()
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
|
package/src/events/native.ts
CHANGED
|
@@ -10,22 +10,28 @@
|
|
|
10
10
|
|
|
11
11
|
import { loadNapi } from '../helpers/napi-loader.js'
|
|
12
12
|
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
subscriptionCount(): Promise<number>
|
|
25
|
-
}
|
|
13
|
+
/**
|
|
14
|
+
* Rust-backed bus surface.
|
|
15
|
+
*
|
|
16
|
+
* Re-exported from the generated declarations rather than restated here: the
|
|
17
|
+
* shape is derived from the `#[napi]` items themselves, so it cannot drift
|
|
18
|
+
* from the Rust. It did before — making `Bus::on_request` synchronous changed
|
|
19
|
+
* the Rust with nothing on this side to notice.
|
|
20
|
+
*
|
|
21
|
+
* Run `pnpm build:napi-types` after touching a `#[napi]` signature.
|
|
22
|
+
*/
|
|
23
|
+
import type { EventBus as NativeEventBus } from '../native/generated.js'
|
|
26
24
|
|
|
25
|
+
/**
|
|
26
|
+
* What the `events` binary exports.
|
|
27
|
+
*
|
|
28
|
+
* The instance shape comes from the generated declarations — derived from the
|
|
29
|
+
* `#[napi]` items, so it cannot drift from the Rust without the generated file
|
|
30
|
+
* changing. Only the constructor is named here, because the binary is a module
|
|
31
|
+
* object rather than a class the loader can see.
|
|
32
|
+
*/
|
|
27
33
|
interface EventsNative {
|
|
28
|
-
EventBus: new (requestHandlerTimeoutMs?: number) =>
|
|
34
|
+
EventBus: new (requestHandlerTimeoutMs?: number) => NativeEventBus
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
const native = loadNapi<EventsNative>({
|
|
@@ -35,3 +41,4 @@ const native = loadNapi<EventsNative>({
|
|
|
35
41
|
})
|
|
36
42
|
|
|
37
43
|
export const EventBus = native.EventBus
|
|
44
|
+
export type EventBus = NativeEventBus
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compose a class by applying mixins to it.
|
|
3
|
+
*
|
|
4
|
+
* class User extends compose(BaseModel, SoftDeletes, Sluggable) {}
|
|
5
|
+
*
|
|
6
|
+
* Each mixin is a function taking the class built so far and returning a
|
|
7
|
+
* subclass of it, so the chain is a plain left fold. Written out by hand that
|
|
8
|
+
* is `Sluggable(SoftDeletes(BaseModel))`, which reads inside-out and gets
|
|
9
|
+
* worse with every mixin — the whole point of the helper.
|
|
10
|
+
*
|
|
11
|
+
* The overloads exist so the RETURN type is the last mixin's, not the base
|
|
12
|
+
* class's: without them a composed class would lose every member the mixins
|
|
13
|
+
* added, statics included.
|
|
14
|
+
*
|
|
15
|
+
* Note there is no `Constructor` bound on `T`. The mixin's own parameter type
|
|
16
|
+
* already constrains what can be passed, and a bound would have to be written
|
|
17
|
+
* with `any` to admit a class with typed constructor arguments.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/** A mixin: takes the class built so far, returns a subclass of it. */
|
|
21
|
+
type Mixin<T, R> = (superclass: T) => R
|
|
22
|
+
|
|
23
|
+
export function compose<T, A>(superclass: T, a: Mixin<T, A>): A
|
|
24
|
+
export function compose<T, A, B>(superclass: T, a: Mixin<T, A>, b: Mixin<A, B>): B
|
|
25
|
+
export function compose<T, A, B, C>(
|
|
26
|
+
superclass: T,
|
|
27
|
+
a: Mixin<T, A>,
|
|
28
|
+
b: Mixin<A, B>,
|
|
29
|
+
c: Mixin<B, C>,
|
|
30
|
+
): C
|
|
31
|
+
export function compose<T, A, B, C, D>(
|
|
32
|
+
superclass: T,
|
|
33
|
+
a: Mixin<T, A>,
|
|
34
|
+
b: Mixin<A, B>,
|
|
35
|
+
c: Mixin<B, C>,
|
|
36
|
+
d: Mixin<C, D>,
|
|
37
|
+
): D
|
|
38
|
+
export function compose<T, A, B, C, D, E>(
|
|
39
|
+
superclass: T,
|
|
40
|
+
a: Mixin<T, A>,
|
|
41
|
+
b: Mixin<A, B>,
|
|
42
|
+
c: Mixin<B, C>,
|
|
43
|
+
d: Mixin<C, D>,
|
|
44
|
+
e: Mixin<D, E>,
|
|
45
|
+
): E
|
|
46
|
+
export function compose<T, A, B, C, D, E, F>(
|
|
47
|
+
superclass: T,
|
|
48
|
+
a: Mixin<T, A>,
|
|
49
|
+
b: Mixin<A, B>,
|
|
50
|
+
c: Mixin<B, C>,
|
|
51
|
+
d: Mixin<C, D>,
|
|
52
|
+
e: Mixin<D, E>,
|
|
53
|
+
f: Mixin<E, F>,
|
|
54
|
+
): F
|
|
55
|
+
export function compose<T, A, B, C, D, E, F, G>(
|
|
56
|
+
superclass: T,
|
|
57
|
+
a: Mixin<T, A>,
|
|
58
|
+
b: Mixin<A, B>,
|
|
59
|
+
c: Mixin<B, C>,
|
|
60
|
+
d: Mixin<C, D>,
|
|
61
|
+
e: Mixin<D, E>,
|
|
62
|
+
f: Mixin<E, F>,
|
|
63
|
+
g: Mixin<F, G>,
|
|
64
|
+
): G
|
|
65
|
+
export function compose<T, A, B, C, D, E, F, G, H>(
|
|
66
|
+
superclass: T,
|
|
67
|
+
a: Mixin<T, A>,
|
|
68
|
+
b: Mixin<A, B>,
|
|
69
|
+
c: Mixin<B, C>,
|
|
70
|
+
d: Mixin<C, D>,
|
|
71
|
+
e: Mixin<D, E>,
|
|
72
|
+
f: Mixin<E, F>,
|
|
73
|
+
g: Mixin<F, G>,
|
|
74
|
+
h: Mixin<G, H>,
|
|
75
|
+
): H
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The single implementation. Its parameters are deliberately looser than the
|
|
79
|
+
* overloads above — a variadic fold cannot express "each mixin takes what the
|
|
80
|
+
* previous one returned" — and the overloads are what callers see.
|
|
81
|
+
*/
|
|
82
|
+
export function compose(superclass: unknown, ...mixins: Mixin<unknown, unknown>[]): unknown {
|
|
83
|
+
return mixins.reduce((applied, mixin) => mixin(applied), superclass)
|
|
84
|
+
}
|
package/src/helpers/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Secret } from '../env/Secret.js'
|
|
2
2
|
export { base64 } from './base64.js'
|
|
3
|
+
export { compose } from './compose.js'
|
|
3
4
|
export { defineStaticProperty } from './defineStaticProperty.js'
|
|
4
5
|
export { milliseconds, seconds } from './duration.js'
|
|
5
6
|
export type { ImportAllFilesOptions, ReadAllFilesOptions } from './fs.js'
|
package/src/helpers/is.ts
CHANGED
|
@@ -140,23 +140,94 @@ export function isEmpty(v: unknown): boolean {
|
|
|
140
140
|
return false
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Returns true if value is a string with at least one character.
|
|
145
|
+
* @example isNonEmptyString('hi') // true; isNonEmptyString('') // false
|
|
146
|
+
*/
|
|
147
|
+
export function isNonEmptyString(v: unknown): v is string {
|
|
148
|
+
return typeof v === 'string' && v.length > 0
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Returns true if value is an array with at least one element.
|
|
153
|
+
* @example isNonEmptyArray([1]) // true; isNonEmptyArray([]) // false
|
|
154
|
+
*/
|
|
155
|
+
export function isNonEmptyArray<T = unknown>(v: unknown): v is [T, ...T[]] {
|
|
156
|
+
return Array.isArray(v) && v.length > 0
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Returns true if value is an integer. `Number.isInteger` already excludes NaN
|
|
161
|
+
* and the infinities.
|
|
162
|
+
* @example isInteger(42) // true; isInteger(4.2) // false
|
|
163
|
+
*/
|
|
164
|
+
export function isInteger(v: unknown): v is number {
|
|
165
|
+
return Number.isInteger(v)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Returns true if value is a number greater than zero. Zero is NOT positive
|
|
170
|
+
* here, matching `@sindresorhus/is`.
|
|
171
|
+
* @example isPositive(1) // true; isPositive(0) // false
|
|
172
|
+
*/
|
|
173
|
+
export function isPositive(v: unknown): v is number {
|
|
174
|
+
return typeof v === 'number' && !Number.isNaN(v) && v > 0
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Returns true if `key` is an OWN property of `value`.
|
|
179
|
+
*
|
|
180
|
+
* Own, not inherited: an inherited `toString` is not something the caller put
|
|
181
|
+
* there, and treating it as present is how a prototype member gets mistaken
|
|
182
|
+
* for data.
|
|
183
|
+
* @example hasProperty({ a: 1 }, 'a') // true; hasProperty({}, 'toString') // false
|
|
184
|
+
*/
|
|
185
|
+
export function hasProperty<K extends PropertyKey>(
|
|
186
|
+
value: unknown,
|
|
187
|
+
key: K,
|
|
188
|
+
): value is Record<K, unknown> {
|
|
189
|
+
return value !== null && typeof value === 'object' && Object.hasOwn(value, key)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The `is` namespace, keyed the way AdonisJS keys it.
|
|
194
|
+
*
|
|
195
|
+
* Adonis re-exports `@sindresorhus/is`, whose members read `is.string(v)`,
|
|
196
|
+
* `is.plainObject(v)`, `is.nonEmptyArray(v)`. This object used to mirror the
|
|
197
|
+
* function names instead — `is.isString(v)` — which stutters, and which turns
|
|
198
|
+
* `is.string(value)` ported from an Adonis app into "is.string is not a
|
|
199
|
+
* function" at runtime rather than a compile error.
|
|
200
|
+
*
|
|
201
|
+
* The standalone `isX` functions above keep their names: they read correctly
|
|
202
|
+
* on their own, and this object is the namespace form.
|
|
203
|
+
*/
|
|
143
204
|
const is = {
|
|
144
|
-
isString,
|
|
145
|
-
isNumber,
|
|
146
|
-
isBoolean,
|
|
147
|
-
isFunction,
|
|
148
|
-
isObject,
|
|
149
|
-
isPlainObject,
|
|
150
|
-
isArray,
|
|
151
|
-
isPromise,
|
|
152
|
-
isNull,
|
|
153
|
-
isUndefined,
|
|
154
|
-
isNullOrUndefined,
|
|
155
|
-
isBuffer,
|
|
156
|
-
isDate,
|
|
157
|
-
isRegExp,
|
|
158
|
-
isError,
|
|
159
|
-
|
|
205
|
+
string: isString,
|
|
206
|
+
number: isNumber,
|
|
207
|
+
boolean: isBoolean,
|
|
208
|
+
function: isFunction,
|
|
209
|
+
object: isObject,
|
|
210
|
+
plainObject: isPlainObject,
|
|
211
|
+
array: isArray,
|
|
212
|
+
promise: isPromise,
|
|
213
|
+
null: isNull,
|
|
214
|
+
undefined: isUndefined,
|
|
215
|
+
nullOrUndefined: isNullOrUndefined,
|
|
216
|
+
buffer: isBuffer,
|
|
217
|
+
date: isDate,
|
|
218
|
+
regExp: isRegExp,
|
|
219
|
+
error: isError,
|
|
220
|
+
nonEmptyString: isNonEmptyString,
|
|
221
|
+
nonEmptyArray: isNonEmptyArray,
|
|
222
|
+
integer: isInteger,
|
|
223
|
+
positive: isPositive,
|
|
224
|
+
hasProperty: hasProperty,
|
|
225
|
+
/**
|
|
226
|
+
* Named deviation: `@sindresorhus/is` has `emptyString` / `emptyArray` /
|
|
227
|
+
* `emptyObject` / `emptyMap` / `emptySet` but no umbrella. This is the
|
|
228
|
+
* umbrella, and the one member here that is not a type guard.
|
|
229
|
+
*/
|
|
230
|
+
empty: isEmpty,
|
|
160
231
|
}
|
|
161
232
|
|
|
162
233
|
export default is
|
package/src/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ import 'reflect-metadata'
|
|
|
8
8
|
|
|
9
9
|
// ─── Core ───────────────────────────────────────────────────
|
|
10
10
|
|
|
11
|
+
export type { AppEnvironment, ApplicationMode, ApplicationState } from './Application.js'
|
|
11
12
|
export { Application } from './Application.js'
|
|
12
13
|
export { ConfigStore, configProvider, defineModuleConfig, env } from './ConfigLoader.js'
|
|
13
14
|
export { Container } from './container/Container.js'
|
|
@@ -201,7 +202,7 @@ export {
|
|
|
201
202
|
// ─── Middleware ──────────────────────────────────────────────
|
|
202
203
|
|
|
203
204
|
export type { MiddlewareFunction } from './middleware/Pipeline.js'
|
|
204
|
-
export {
|
|
205
|
+
export { composeMiddleware, MiddlewareRegistry } from './middleware/Pipeline.js'
|
|
205
206
|
|
|
206
207
|
// ─── HttpKernel ─────────────────────────────────────────────
|
|
207
208
|
|
|
@@ -242,7 +243,6 @@ export type {
|
|
|
242
243
|
FlagMetaData,
|
|
243
244
|
} from './console/types.js'
|
|
244
245
|
export type {
|
|
245
|
-
AppEnvironment,
|
|
246
246
|
HyperServerLike,
|
|
247
247
|
IgnitorConfig,
|
|
248
248
|
ReamrcConfig,
|
|
@@ -371,4 +371,4 @@ export {
|
|
|
371
371
|
StatsTracker,
|
|
372
372
|
} from './scheduler/index.js'
|
|
373
373
|
|
|
374
|
-
export const VERSION = '0.2.
|
|
374
|
+
export const VERSION = '0.2.6'
|
|
@@ -170,12 +170,18 @@ export class MiddlewareRegistry {
|
|
|
170
170
|
handler,
|
|
171
171
|
]
|
|
172
172
|
|
|
173
|
-
return
|
|
173
|
+
return composeMiddleware(stack)
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
/**
|
|
178
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Compose middleware into a single handler (onion pattern).
|
|
179
|
+
*
|
|
180
|
+
* NOT `compose` — that name means mixin composition in `helpers`, the way it
|
|
181
|
+
* does in AdonisJS, and the two were one import away from each other on the
|
|
182
|
+
* barrel.
|
|
183
|
+
*/
|
|
184
|
+
function composeMiddleware(middleware: MiddlewareFunction[]): MiddlewareFunction {
|
|
179
185
|
return async (ctx: HttpContext, finalNext: () => Promise<void>) => {
|
|
180
186
|
let index = -1
|
|
181
187
|
|
|
@@ -257,4 +263,4 @@ function createValidationMiddleware(validators: RuntimeValidator[]): MiddlewareF
|
|
|
257
263
|
}
|
|
258
264
|
}
|
|
259
265
|
|
|
260
|
-
export {
|
|
266
|
+
export { composeMiddleware }
|