@gramio/composer 0.1.1 → 0.3.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/README.md +271 -1
- package/dist/index.cjs +96 -7
- package/dist/index.d.cts +292 -37
- package/dist/index.d.ts +292 -37
- package/dist/index.js +94 -8
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -119,13 +119,21 @@ app.guard(
|
|
|
119
119
|
);
|
|
120
120
|
```
|
|
121
121
|
|
|
122
|
-
**Without handlers
|
|
122
|
+
**Without handlers (gate mode)** — if false, stop this composer's remaining middleware. When a type predicate is used, downstream context is narrowed:
|
|
123
123
|
|
|
124
124
|
```ts
|
|
125
125
|
// Only admin can reach subsequent middleware
|
|
126
126
|
app
|
|
127
127
|
.guard((ctx) => ctx.role === "admin")
|
|
128
128
|
.use(adminOnlyHandler); // skipped if not admin
|
|
129
|
+
|
|
130
|
+
// Type predicate narrows context for all downstream handlers
|
|
131
|
+
app
|
|
132
|
+
.guard((ctx): ctx is Ctx & { text: string } => "text" in ctx)
|
|
133
|
+
.on("message", (ctx, next) => {
|
|
134
|
+
ctx.text; // string (narrowed by guard)
|
|
135
|
+
return next();
|
|
136
|
+
});
|
|
129
137
|
```
|
|
130
138
|
|
|
131
139
|
When used inside an `extend()`-ed plugin, the guard stops the plugin's chain but the parent continues:
|
|
@@ -305,6 +313,186 @@ const app = new Composer()
|
|
|
305
313
|
});
|
|
306
314
|
```
|
|
307
315
|
|
|
316
|
+
#### `.on()` with filters
|
|
317
|
+
|
|
318
|
+
**Filter-only (no event name)** — the 2-arg `on(filter, handler)` applies the filter to **all** events without discriminating by event type:
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
// Type-narrowing filter — handler sees narrowed context across all compatible events
|
|
322
|
+
app.on(
|
|
323
|
+
(ctx): ctx is { text: string } => typeof (ctx as any).text === "string",
|
|
324
|
+
(ctx, next) => {
|
|
325
|
+
ctx.text; // string (narrowed)
|
|
326
|
+
return next();
|
|
327
|
+
},
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
// Boolean filter — no narrowing, handler gets base TOut
|
|
331
|
+
app.on(
|
|
332
|
+
(ctx) => ctx.updateType === "message",
|
|
333
|
+
(ctx, next) => {
|
|
334
|
+
// no type narrowing, full context
|
|
335
|
+
return next();
|
|
336
|
+
},
|
|
337
|
+
);
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
**Event + filter** — the 3-arg `on(event, filter, handler)` supports both type-narrowing predicates and boolean filters:
|
|
341
|
+
|
|
342
|
+
```ts
|
|
343
|
+
// Type-narrowing filter — handler sees narrowed context
|
|
344
|
+
app.on(
|
|
345
|
+
"message",
|
|
346
|
+
(ctx): ctx is MessageCtx & { text: string } => ctx.text !== undefined,
|
|
347
|
+
(ctx, next) => {
|
|
348
|
+
ctx.text; // string (narrowed, not string | undefined)
|
|
349
|
+
return next();
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
// Boolean filter — no narrowing, handler sees full context
|
|
354
|
+
app.on(
|
|
355
|
+
"message",
|
|
356
|
+
(ctx) => ctx.text !== undefined,
|
|
357
|
+
(ctx, next) => {
|
|
358
|
+
ctx.text; // string | undefined (not narrowed)
|
|
359
|
+
return next();
|
|
360
|
+
},
|
|
361
|
+
);
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
The 2-arg `on()` also accepts an optional `Patch` generic for context extensions (useful in custom methods):
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
app.on<"message", { args: string }>("message", (ctx, next) => {
|
|
368
|
+
ctx.args; // string — type-safe without casting
|
|
369
|
+
return next();
|
|
370
|
+
});
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
`.use()` supports the same `Patch` generic — handy when a custom method enriches context before delegating to a user-provided handler:
|
|
374
|
+
|
|
375
|
+
```ts
|
|
376
|
+
app.use<{ args: string }>((ctx, next) => {
|
|
377
|
+
ctx.args; // string — type-safe without casting
|
|
378
|
+
return next();
|
|
379
|
+
});
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
`Patch` does not change `TOut` — it is a local escape hatch for one handler, not a permanent context extension. Use `derive()` when you want the addition to propagate to all downstream middleware.
|
|
383
|
+
|
|
384
|
+
#### `types` + `eventTypes()` — phantom type inference
|
|
385
|
+
|
|
386
|
+
TypeScript cannot partially infer type arguments, so when you need both `TEventMap` and `TMethods` inferred together, use the `types` phantom field with the `eventTypes()` helper instead of explicit type parameters:
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
import { createComposer, eventTypes } from "@gramio/composer";
|
|
390
|
+
|
|
391
|
+
// eventTypes<T>() returns undefined at runtime — purely for inference
|
|
392
|
+
const { Composer } = createComposer({
|
|
393
|
+
discriminator: (ctx: BaseCtx) => ctx.updateType,
|
|
394
|
+
types: eventTypes<{ message: MessageCtx; callback_query: CallbackCtx }>(),
|
|
395
|
+
});
|
|
396
|
+
// TBase inferred from discriminator, TEventMap inferred from types
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
#### `methods` — custom prototype methods
|
|
400
|
+
|
|
401
|
+
Inject framework-specific DX sugar directly onto the Composer prototype. Custom methods are preserved through **all** method chains (`on`, `use`, `derive`, `extend`, etc.). A runtime conflict check throws if a method name collides with a built-in.
|
|
402
|
+
|
|
403
|
+
**Simple methods** (no access to accumulated derives) work directly in `methods`:
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
const { Composer } = createComposer({
|
|
407
|
+
discriminator: (ctx: BaseCtx) => ctx.updateType,
|
|
408
|
+
types: eventTypes<{ message: MessageCtx }>(),
|
|
409
|
+
methods: {
|
|
410
|
+
hears(trigger: RegExp | string, handler: (ctx: MessageCtx) => unknown) {
|
|
411
|
+
return this.on("message", (ctx, next) => {
|
|
412
|
+
const text = ctx.text;
|
|
413
|
+
if (
|
|
414
|
+
(typeof trigger === "string" && text === trigger) ||
|
|
415
|
+
(trigger instanceof RegExp && text && trigger.test(text))
|
|
416
|
+
) {
|
|
417
|
+
return handler(ctx);
|
|
418
|
+
}
|
|
419
|
+
return next();
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
**Methods that receive accumulated derives** require two steps. TypeScript cannot infer generic method signatures when `TMethods` is nested inside the return type of `createComposer`, so use `defineComposerMethods` first — its return type is directly `TMethods`, which preserves generic signatures. Then pass `typeof methods` as the 3rd type argument.
|
|
427
|
+
|
|
428
|
+
Use `ComposerLike<TThis>` as an F-bounded constraint so that `this.on(...)` is fully typed and returns `TThis` — no casts needed.
|
|
429
|
+
|
|
430
|
+
**Pattern: `this: TThis` + `ContextOf<TThis>` — zero annotation at the call site:**
|
|
431
|
+
|
|
432
|
+
```ts
|
|
433
|
+
import { createComposer, defineComposerMethods, eventTypes } from "@gramio/composer";
|
|
434
|
+
import type { ComposerLike, ContextOf, Middleware } from "@gramio/composer";
|
|
435
|
+
|
|
436
|
+
const methods = defineComposerMethods({
|
|
437
|
+
command<TThis extends ComposerLike<TThis>>(
|
|
438
|
+
this: TThis,
|
|
439
|
+
name: string,
|
|
440
|
+
handler: Middleware<MessageCtx & ContextOf<TThis>>,
|
|
441
|
+
): TThis {
|
|
442
|
+
const inner: Middleware<MessageCtx & ContextOf<TThis>> = (ctx, next) => {
|
|
443
|
+
if (ctx.text === `/${name}`) return handler(ctx, next);
|
|
444
|
+
return next();
|
|
445
|
+
};
|
|
446
|
+
return this.on("message", inner);
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
const { Composer } = createComposer<BaseCtx, { message: MessageCtx }, typeof methods>({
|
|
451
|
+
discriminator: (ctx) => ctx.updateType,
|
|
452
|
+
methods,
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
// Derives flow into the handler automatically — no annotation needed:
|
|
456
|
+
new Composer()
|
|
457
|
+
.derive(() => ({ user: { id: 1, name: "Alice" } }))
|
|
458
|
+
.command("start", (ctx, next) => {
|
|
459
|
+
ctx.user.id; // ✅ typed — inferred from ContextOf<TThis>
|
|
460
|
+
ctx.text; // ✅ string | undefined — from MessageCtx
|
|
461
|
+
return next();
|
|
462
|
+
});
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
#### `ContextOf<T>` — extract the current context type
|
|
466
|
+
|
|
467
|
+
Extracts `TOut` from a Composer or EventComposer instance type. Used as `ContextOf<TThis>` in custom method signatures to automatically capture all accumulated derives at the call site.
|
|
468
|
+
|
|
469
|
+
```ts
|
|
470
|
+
import type { ContextOf } from "@gramio/composer";
|
|
471
|
+
|
|
472
|
+
// From a plain Composer:
|
|
473
|
+
type Ctx = ContextOf<Composer<{ a: number }, { a: number; b: string }>>;
|
|
474
|
+
// Ctx = { a: number; b: string }
|
|
475
|
+
|
|
476
|
+
// In a custom method — TThis is inferred from the caller instance:
|
|
477
|
+
command<TThis extends ComposerLike<TThis>>(
|
|
478
|
+
this: TThis,
|
|
479
|
+
handler: Middleware<ContextOf<TThis>>,
|
|
480
|
+
): TThis
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
#### `ComposerLike<T>` — minimal structural type for `this` constraints
|
|
484
|
+
|
|
485
|
+
A minimal interface `{ on(event: any, handler: any): T }` used as an F-bounded constraint on `TThis`. Makes `this.on(...)` fully typed and return `TThis` without casts.
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
import type { ComposerLike } from "@gramio/composer";
|
|
489
|
+
|
|
490
|
+
// Constraint in a custom method:
|
|
491
|
+
command<TThis extends ComposerLike<TThis>>(this: TThis, ...): TThis {
|
|
492
|
+
return this.on("message", inner); // returns TThis — no `as TThis` needed
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
|
|
308
496
|
### `EventQueue`
|
|
309
497
|
|
|
310
498
|
Concurrent event queue with graceful shutdown.
|
|
@@ -324,6 +512,88 @@ queue.addBatch(events);
|
|
|
324
512
|
await queue.stop(5000);
|
|
325
513
|
```
|
|
326
514
|
|
|
515
|
+
### Macro System
|
|
516
|
+
|
|
517
|
+
Declarative handler options inspired by [Elysia macros](https://elysiajs.com/patterns/macro.md). Register reusable behaviors (guards, rate-limits, auth) as macros, then activate them via an options object on handler methods.
|
|
518
|
+
|
|
519
|
+
#### `macro(name, definition)` / `macro(definitions)`
|
|
520
|
+
|
|
521
|
+
Register macros on a Composer or EventComposer instance.
|
|
522
|
+
|
|
523
|
+
```ts
|
|
524
|
+
import { Composer, type MacroDef, type ContextCallback } from "@gramio/composer";
|
|
525
|
+
|
|
526
|
+
// Boolean shorthand macro — plain hooks object
|
|
527
|
+
const onlyAdmin: MacroDef<void, {}> = {
|
|
528
|
+
preHandler: (ctx, next) => {
|
|
529
|
+
if (ctx.role !== "admin") return; // stops chain
|
|
530
|
+
return next();
|
|
531
|
+
},
|
|
532
|
+
};
|
|
533
|
+
|
|
534
|
+
// Parameterized macro — function receiving options
|
|
535
|
+
interface ThrottleOptions {
|
|
536
|
+
limit: number;
|
|
537
|
+
window?: number;
|
|
538
|
+
onLimit?: ContextCallback; // ← replaced with actual ctx type at call site
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const throttle: MacroDef<ThrottleOptions, {}> = (opts) => ({
|
|
542
|
+
preHandler: createThrottleMiddleware(opts),
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
// Macro with derive — enriches handler context
|
|
546
|
+
interface AuthDerived { user: { id: number; name: string } }
|
|
547
|
+
|
|
548
|
+
const auth: MacroDef<void, AuthDerived> = {
|
|
549
|
+
derive: async (ctx) => {
|
|
550
|
+
const user = await getUser(ctx.token);
|
|
551
|
+
if (!user) return; // void = stop chain (guard behavior)
|
|
552
|
+
return { user };
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
|
|
556
|
+
const app = new Composer()
|
|
557
|
+
.macro("onlyAdmin", onlyAdmin)
|
|
558
|
+
.macro({ throttle, auth }); // batch registration
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
#### `buildFromOptions(macros, options, handler)`
|
|
562
|
+
|
|
563
|
+
Runtime helper that composes a handler with macro hooks. Used internally by frameworks to wire macros into handler methods.
|
|
564
|
+
|
|
565
|
+
```ts
|
|
566
|
+
import { buildFromOptions } from "@gramio/composer";
|
|
567
|
+
|
|
568
|
+
// Execution order:
|
|
569
|
+
// 1. options.preHandler[] (explicit guards — user controls order)
|
|
570
|
+
// 2. Per-macro in options property order:
|
|
571
|
+
// a. macro.preHandler (guard middleware)
|
|
572
|
+
// b. macro.derive (context enrichment; void = stop chain)
|
|
573
|
+
// 3. Main handler
|
|
574
|
+
const composed = buildFromOptions(
|
|
575
|
+
app["~"].macros,
|
|
576
|
+
{ auth: true, throttle: { limit: 5 } },
|
|
577
|
+
mainHandler,
|
|
578
|
+
);
|
|
579
|
+
```
|
|
580
|
+
|
|
581
|
+
#### Macro Types
|
|
582
|
+
|
|
583
|
+
```ts
|
|
584
|
+
import type {
|
|
585
|
+
MacroDef, // Macro definition (function or hooks object)
|
|
586
|
+
MacroHooks, // { preHandler?, derive? }
|
|
587
|
+
MacroDefinitions, // Record<string, MacroDef<any, any>>
|
|
588
|
+
ContextCallback, // Marker type for context-aware callbacks
|
|
589
|
+
WithCtx, // Recursively replaces ContextCallback with real ctx type
|
|
590
|
+
HandlerOptions, // Builds the options parameter type for handler methods
|
|
591
|
+
DeriveFromOptions, // Collects derive types from activated macros
|
|
592
|
+
MacroOptionType, // Extracts option type from MacroDef
|
|
593
|
+
MacroDeriveType, // Extracts derive return type from MacroDef
|
|
594
|
+
} from "@gramio/composer";
|
|
595
|
+
```
|
|
596
|
+
|
|
327
597
|
### Utilities
|
|
328
598
|
|
|
329
599
|
```ts
|
package/dist/index.cjs
CHANGED
|
@@ -79,7 +79,10 @@ class Composer {
|
|
|
79
79
|
name: void 0,
|
|
80
80
|
seed: void 0,
|
|
81
81
|
errorsDefinitions: {},
|
|
82
|
-
tracer: void 0
|
|
82
|
+
tracer: void 0,
|
|
83
|
+
macros: {},
|
|
84
|
+
/** Phantom type accessor — never set at runtime, used by `ContextOf<T>` */
|
|
85
|
+
Out: void 0
|
|
83
86
|
};
|
|
84
87
|
constructor(options) {
|
|
85
88
|
this["~"].name = options?.name;
|
|
@@ -88,6 +91,14 @@ class Composer {
|
|
|
88
91
|
invalidate() {
|
|
89
92
|
this["~"].compiled = null;
|
|
90
93
|
}
|
|
94
|
+
macro(nameOrDefs, definition) {
|
|
95
|
+
if (typeof nameOrDefs === "string") {
|
|
96
|
+
this["~"].macros[nameOrDefs] = definition;
|
|
97
|
+
} else {
|
|
98
|
+
Object.assign(this["~"].macros, nameOrDefs);
|
|
99
|
+
}
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
91
102
|
decorate(values, options) {
|
|
92
103
|
const mw = (ctx, next) => {
|
|
93
104
|
Object.assign(ctx, values);
|
|
@@ -99,6 +110,7 @@ class Composer {
|
|
|
99
110
|
this.invalidate();
|
|
100
111
|
return this;
|
|
101
112
|
}
|
|
113
|
+
// biome-ignore lint/suspicious/noExplicitAny: overload implementation signature
|
|
102
114
|
use(...middleware) {
|
|
103
115
|
for (const fn of middleware) {
|
|
104
116
|
this["~"].middlewares.push({ fn, scope: "local", type: "use", name: fn.name || void 0 });
|
|
@@ -316,6 +328,7 @@ class Composer {
|
|
|
316
328
|
this["~"].extended.add(key);
|
|
317
329
|
}
|
|
318
330
|
Object.assign(this["~"].errorsDefinitions, other["~"].errorsDefinitions);
|
|
331
|
+
Object.assign(this["~"].macros, other["~"].macros);
|
|
319
332
|
this["~"].onErrors.push(...other["~"].onErrors);
|
|
320
333
|
const pluginName = other["~"].name;
|
|
321
334
|
const isNew = (m) => {
|
|
@@ -475,16 +488,36 @@ class EventQueue {
|
|
|
475
488
|
}
|
|
476
489
|
}
|
|
477
490
|
|
|
491
|
+
function defineComposerMethods(methods) {
|
|
492
|
+
return methods;
|
|
493
|
+
}
|
|
494
|
+
function eventTypes() {
|
|
495
|
+
return void 0;
|
|
496
|
+
}
|
|
478
497
|
function createComposer(config) {
|
|
479
498
|
class EventComposerImpl extends Composer {
|
|
480
|
-
on(
|
|
481
|
-
|
|
499
|
+
on(eventOrFilter, filterOrHandler, handler) {
|
|
500
|
+
if (typeof eventOrFilter === "function") {
|
|
501
|
+
const filter2 = eventOrFilter;
|
|
502
|
+
const actualHandler2 = filterOrHandler;
|
|
503
|
+
const filterLabel = filter2.name || "filter";
|
|
504
|
+
const mw2 = (ctx, next) => {
|
|
505
|
+
if (filter2(ctx)) return actualHandler2(ctx, next);
|
|
506
|
+
return next();
|
|
507
|
+
};
|
|
508
|
+
nameMiddleware(mw2, "on", filterLabel);
|
|
509
|
+
this["~"].middlewares.push({ fn: mw2, scope: "local", type: "on", name: filterLabel });
|
|
510
|
+
this.invalidate();
|
|
511
|
+
return this;
|
|
512
|
+
}
|
|
513
|
+
const events = Array.isArray(eventOrFilter) ? eventOrFilter : [eventOrFilter];
|
|
482
514
|
const eventLabel = events.join("|");
|
|
515
|
+
const actualHandler = handler ?? filterOrHandler;
|
|
516
|
+
const filter = handler ? filterOrHandler : void 0;
|
|
483
517
|
const mw = (ctx, next) => {
|
|
484
|
-
if (events.includes(config.discriminator(ctx)))
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return next();
|
|
518
|
+
if (!events.includes(config.discriminator(ctx))) return next();
|
|
519
|
+
if (filter && !filter(ctx)) return next();
|
|
520
|
+
return actualHandler(ctx, next);
|
|
488
521
|
};
|
|
489
522
|
nameMiddleware(mw, "on", eventLabel);
|
|
490
523
|
this["~"].middlewares.push({ fn: mw, scope: "local", type: "on", name: eventLabel });
|
|
@@ -512,6 +545,19 @@ function createComposer(config) {
|
|
|
512
545
|
return this;
|
|
513
546
|
}
|
|
514
547
|
}
|
|
548
|
+
if (config.methods) {
|
|
549
|
+
for (const [name, fn] of Object.entries(config.methods)) {
|
|
550
|
+
if (name in EventComposerImpl.prototype) {
|
|
551
|
+
throw new Error(`Custom method "${name}" conflicts with built-in method`);
|
|
552
|
+
}
|
|
553
|
+
Object.defineProperty(EventComposerImpl.prototype, name, {
|
|
554
|
+
value: fn,
|
|
555
|
+
writable: true,
|
|
556
|
+
configurable: true,
|
|
557
|
+
enumerable: false
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
}
|
|
515
561
|
return {
|
|
516
562
|
Composer: EventComposerImpl,
|
|
517
563
|
compose,
|
|
@@ -519,10 +565,53 @@ function createComposer(config) {
|
|
|
519
565
|
};
|
|
520
566
|
}
|
|
521
567
|
|
|
568
|
+
function buildFromOptions(macros, options, handler) {
|
|
569
|
+
if (!options) return handler;
|
|
570
|
+
const chain = [];
|
|
571
|
+
const preHandler = options.preHandler;
|
|
572
|
+
if (preHandler) {
|
|
573
|
+
if (Array.isArray(preHandler)) {
|
|
574
|
+
chain.push(...preHandler);
|
|
575
|
+
} else {
|
|
576
|
+
chain.push(preHandler);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
for (const key of Object.keys(options)) {
|
|
580
|
+
if (key === "preHandler") continue;
|
|
581
|
+
const value = options[key];
|
|
582
|
+
if (value === false || value == null) continue;
|
|
583
|
+
const def = macros[key];
|
|
584
|
+
if (!def) continue;
|
|
585
|
+
const hooks = typeof def === "function" ? def(value === true ? void 0 : value) : def;
|
|
586
|
+
if (hooks.preHandler) {
|
|
587
|
+
if (Array.isArray(hooks.preHandler)) {
|
|
588
|
+
chain.push(...hooks.preHandler);
|
|
589
|
+
} else {
|
|
590
|
+
chain.push(hooks.preHandler);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
if (hooks.derive) {
|
|
594
|
+
const deriveFn = hooks.derive;
|
|
595
|
+
chain.push(async (ctx, next) => {
|
|
596
|
+
const derived = await deriveFn(ctx);
|
|
597
|
+
if (derived == null) return;
|
|
598
|
+
Object.assign(ctx, derived);
|
|
599
|
+
return next();
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
chain.push(handler);
|
|
604
|
+
if (chain.length === 1) return chain[0];
|
|
605
|
+
return compose(chain);
|
|
606
|
+
}
|
|
607
|
+
|
|
522
608
|
exports.Composer = Composer;
|
|
523
609
|
exports.EventQueue = EventQueue;
|
|
610
|
+
exports.buildFromOptions = buildFromOptions;
|
|
524
611
|
exports.compose = compose;
|
|
525
612
|
exports.createComposer = createComposer;
|
|
613
|
+
exports.defineComposerMethods = defineComposerMethods;
|
|
614
|
+
exports.eventTypes = eventTypes;
|
|
526
615
|
exports.noopNext = noopNext;
|
|
527
616
|
exports.skip = skip;
|
|
528
617
|
exports.stop = stop;
|