@gramio/composer 0.1.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/index.cjs ADDED
@@ -0,0 +1,520 @@
1
+ 'use strict';
2
+
3
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
4
+ function compose(middlewares) {
5
+ if (middlewares.length === 0) {
6
+ return (_, next) => next ? next() : Promise.resolve();
7
+ }
8
+ return (context, next) => {
9
+ let lastIndex = -1;
10
+ function dispatch(i) {
11
+ if (i <= lastIndex) {
12
+ return Promise.reject(new Error("next() called multiple times"));
13
+ }
14
+ lastIndex = i;
15
+ const fn = i < middlewares.length ? middlewares[i] : next;
16
+ if (!fn) {
17
+ return Promise.resolve();
18
+ }
19
+ try {
20
+ return Promise.resolve(fn(context, () => dispatch(i + 1)));
21
+ } catch (error) {
22
+ return Promise.reject(error);
23
+ }
24
+ }
25
+ return dispatch(0);
26
+ };
27
+ }
28
+
29
+ function nameMiddleware(fn, type, handlerName) {
30
+ Object.defineProperty(fn, "name", {
31
+ value: handlerName ? `${type}:${handlerName}` : type,
32
+ configurable: true
33
+ });
34
+ return fn;
35
+ }
36
+ const LIB_DIR = /* @__PURE__ */ (() => {
37
+ try {
38
+ const url = (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href));
39
+ const idx = url.lastIndexOf("/");
40
+ let dir = url.substring(0, idx);
41
+ if (dir.startsWith("file://")) {
42
+ dir = dir.slice(7);
43
+ if (/^\/[A-Za-z]:/.test(dir)) dir = dir.slice(1);
44
+ }
45
+ return dir;
46
+ } catch {
47
+ return "";
48
+ }
49
+ })();
50
+ function cleanErrorStack(error) {
51
+ if (!LIB_DIR || !(error instanceof Error) || !error.stack) return;
52
+ const fwd = LIB_DIR;
53
+ const bwd = LIB_DIR.replace(/\//g, "\\");
54
+ error.stack = error.stack.split("\n").filter((line) => {
55
+ if (!/^\s+at\s/.test(line)) return true;
56
+ return !line.includes(fwd) && !line.includes(bwd);
57
+ }).join("\n");
58
+ }
59
+ const noopNext = () => Promise.resolve();
60
+ const skip = (_, next) => next();
61
+ const stop = () => {
62
+ };
63
+
64
+ function resolveRouteHandler(handler) {
65
+ if (handler instanceof Composer) {
66
+ return compose(handler["~"].middlewares.map((m) => m.fn));
67
+ }
68
+ if (Array.isArray(handler)) {
69
+ return compose(handler);
70
+ }
71
+ return handler;
72
+ }
73
+ class Composer {
74
+ "~" = {
75
+ middlewares: [],
76
+ onErrors: [],
77
+ extended: /* @__PURE__ */ new Set(),
78
+ compiled: null,
79
+ name: void 0,
80
+ seed: void 0,
81
+ errorsDefinitions: {},
82
+ tracer: void 0
83
+ };
84
+ constructor(options) {
85
+ this["~"].name = options?.name;
86
+ this["~"].seed = options?.seed;
87
+ }
88
+ invalidate() {
89
+ this["~"].compiled = null;
90
+ }
91
+ decorate(values, options) {
92
+ const mw = (ctx, next) => {
93
+ Object.assign(ctx, values);
94
+ return next();
95
+ };
96
+ nameMiddleware(mw, "decorate");
97
+ const scope = options?.as ?? "local";
98
+ this["~"].middlewares.push({ fn: mw, scope, type: "decorate" });
99
+ this.invalidate();
100
+ return this;
101
+ }
102
+ use(...middleware) {
103
+ for (const fn of middleware) {
104
+ this["~"].middlewares.push({ fn, scope: "local", type: "use", name: fn.name || void 0 });
105
+ }
106
+ this.invalidate();
107
+ return this;
108
+ }
109
+ derive(handler, options) {
110
+ const handlerName = handler.name || void 0;
111
+ const mw = async (ctx, next) => {
112
+ const result = await handler(ctx);
113
+ Object.assign(ctx, result);
114
+ return next();
115
+ };
116
+ nameMiddleware(mw, "derive", handlerName);
117
+ const scope = options?.as ?? "local";
118
+ this["~"].middlewares.push({ fn: mw, scope, type: "derive", name: handlerName });
119
+ this.invalidate();
120
+ return this;
121
+ }
122
+ guard(predicate, ...middleware) {
123
+ const isGate = middleware.length === 0;
124
+ const predicateName = predicate.name || void 0;
125
+ if (isGate) {
126
+ const mw = async (ctx, next) => {
127
+ if (await predicate(ctx)) return next();
128
+ };
129
+ nameMiddleware(mw, "guard", predicateName);
130
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "guard", name: predicateName });
131
+ } else {
132
+ const chain = compose(middleware);
133
+ const mw = async (ctx, next) => {
134
+ if (await predicate(ctx)) {
135
+ await chain(ctx, noopNext);
136
+ }
137
+ await next();
138
+ };
139
+ nameMiddleware(mw, "guard", predicateName);
140
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "guard", name: predicateName });
141
+ }
142
+ this.invalidate();
143
+ return this;
144
+ }
145
+ branch(predicate, onTrue, onFalse) {
146
+ if (typeof predicate === "boolean") {
147
+ const branchName = predicate ? onTrue.name || void 0 : onFalse?.name || void 0;
148
+ if (predicate) {
149
+ this["~"].middlewares.push({ fn: onTrue, scope: "local", type: "branch", name: branchName });
150
+ } else if (onFalse) {
151
+ this["~"].middlewares.push({ fn: onFalse, scope: "local", type: "branch", name: branchName });
152
+ }
153
+ this.invalidate();
154
+ return this;
155
+ }
156
+ const predicateName = predicate.name || void 0;
157
+ const mw = async (ctx, next) => {
158
+ if (await predicate(ctx)) {
159
+ return onTrue(ctx, next);
160
+ }
161
+ return onFalse ? onFalse(ctx, next) : next();
162
+ };
163
+ nameMiddleware(mw, "branch", predicateName);
164
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "branch", name: predicateName });
165
+ this.invalidate();
166
+ return this;
167
+ }
168
+ route(router, casesOrBuilder, fallback) {
169
+ let resolvedCases;
170
+ let resolvedFallback;
171
+ if (typeof casesOrBuilder === "function") {
172
+ resolvedCases = {};
173
+ const composers = /* @__PURE__ */ new Map();
174
+ let otherwiseMws = [];
175
+ const routeBuilder = {
176
+ on: (key, ...middleware) => {
177
+ const c = new Composer();
178
+ if (middleware.length > 0) c.use(...middleware);
179
+ composers.set(key, c);
180
+ return c;
181
+ },
182
+ otherwise: (...middleware) => {
183
+ otherwiseMws = middleware;
184
+ }
185
+ };
186
+ casesOrBuilder(routeBuilder);
187
+ for (const [key, c] of composers) {
188
+ resolvedCases[key] = compose(c["~"].middlewares.map((m) => m.fn));
189
+ }
190
+ resolvedFallback = otherwiseMws.length > 0 ? compose(otherwiseMws) : void 0;
191
+ } else {
192
+ resolvedCases = {};
193
+ for (const [key, handler] of Object.entries(casesOrBuilder)) {
194
+ if (handler != null) {
195
+ resolvedCases[key] = resolveRouteHandler(handler);
196
+ }
197
+ }
198
+ resolvedFallback = fallback != null ? resolveRouteHandler(fallback) : void 0;
199
+ }
200
+ const routerName = router.name || void 0;
201
+ const mw = async (ctx, next) => {
202
+ const key = await router(ctx);
203
+ if (key != null) {
204
+ const caseHandler = resolvedCases[key];
205
+ if (caseHandler) return caseHandler(ctx, next);
206
+ }
207
+ return resolvedFallback ? resolvedFallback(ctx, next) : next();
208
+ };
209
+ nameMiddleware(mw, "route", routerName);
210
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "route", name: routerName });
211
+ this.invalidate();
212
+ return this;
213
+ }
214
+ fork(...middleware) {
215
+ const chain = compose(middleware);
216
+ const mw = (ctx, next) => {
217
+ Promise.resolve().then(() => {
218
+ try {
219
+ const result = chain(ctx, noopNext);
220
+ if (result && typeof result.catch === "function") {
221
+ result.catch(() => {
222
+ });
223
+ }
224
+ } catch {
225
+ }
226
+ });
227
+ return next();
228
+ };
229
+ nameMiddleware(mw, "fork");
230
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "fork" });
231
+ this.invalidate();
232
+ return this;
233
+ }
234
+ tap(...middleware) {
235
+ const chain = compose(middleware);
236
+ const mw = async (ctx, next) => {
237
+ await chain(ctx, noopNext);
238
+ return next();
239
+ };
240
+ nameMiddleware(mw, "tap");
241
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "tap" });
242
+ this.invalidate();
243
+ return this;
244
+ }
245
+ lazy(factory) {
246
+ const factoryName = factory.name || void 0;
247
+ const mw = async (ctx, next) => {
248
+ const resolved = await factory(ctx);
249
+ return resolved(ctx, next);
250
+ };
251
+ nameMiddleware(mw, "lazy", factoryName);
252
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "lazy", name: factoryName });
253
+ this.invalidate();
254
+ return this;
255
+ }
256
+ onError(handler) {
257
+ this["~"].onErrors.push(handler);
258
+ this.invalidate();
259
+ return this;
260
+ }
261
+ // ─── Conditional Registration ───
262
+ when(condition, fn) {
263
+ if (condition) {
264
+ const temp = new this.constructor();
265
+ fn(temp);
266
+ for (const mw of temp["~"].middlewares) {
267
+ this["~"].middlewares.push(mw);
268
+ }
269
+ Object.assign(this["~"].errorsDefinitions, temp["~"].errorsDefinitions);
270
+ this["~"].onErrors.push(...temp["~"].onErrors);
271
+ for (const key of temp["~"].extended) {
272
+ this["~"].extended.add(key);
273
+ }
274
+ }
275
+ this.invalidate();
276
+ return this;
277
+ }
278
+ // ─── Error Registration ───
279
+ error(kind, errorClass) {
280
+ this["~"].errorsDefinitions[kind] = errorClass;
281
+ return this;
282
+ }
283
+ // ─── Scope System ───
284
+ as(scope) {
285
+ for (const entry of this["~"].middlewares) {
286
+ if (scope === "global" || entry.scope === "local") {
287
+ entry.scope = scope;
288
+ }
289
+ }
290
+ this.invalidate();
291
+ return this;
292
+ }
293
+ // ─── Composition Methods ───
294
+ group(fn) {
295
+ const group = new Composer();
296
+ fn(group);
297
+ const chain = compose(group["~"].middlewares.map((m) => m.fn));
298
+ const mw = async (ctx, next) => {
299
+ const scopedCtx = Object.create(ctx);
300
+ await chain(scopedCtx, noopNext);
301
+ return next();
302
+ };
303
+ nameMiddleware(mw, "group");
304
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "group" });
305
+ this.invalidate();
306
+ return this;
307
+ }
308
+ extend(other) {
309
+ if (other["~"].name) {
310
+ const key = `${other["~"].name}:${JSON.stringify(other["~"].seed ?? null)}`;
311
+ if (this["~"].extended.has(key)) return this;
312
+ this["~"].extended.add(key);
313
+ }
314
+ for (const key of other["~"].extended) {
315
+ this["~"].extended.add(key);
316
+ }
317
+ Object.assign(this["~"].errorsDefinitions, other["~"].errorsDefinitions);
318
+ this["~"].onErrors.push(...other["~"].onErrors);
319
+ const pluginName = other["~"].name;
320
+ const localMws = other["~"].middlewares.filter((m) => m.scope === "local");
321
+ const scopedMws = other["~"].middlewares.filter((m) => m.scope === "scoped");
322
+ const globalMws = other["~"].middlewares.filter((m) => m.scope === "global");
323
+ if (localMws.length > 0) {
324
+ const chain = compose(localMws.map((m) => m.fn));
325
+ const isolated = async (ctx, next) => {
326
+ const scopedCtx = Object.create(ctx);
327
+ await chain(scopedCtx, noopNext);
328
+ return next();
329
+ };
330
+ nameMiddleware(isolated, "extend", pluginName);
331
+ this["~"].middlewares.push({ fn: isolated, scope: "local", type: "extend", name: pluginName, plugin: pluginName });
332
+ }
333
+ for (const mw of scopedMws) {
334
+ this["~"].middlewares.push({ fn: mw.fn, scope: "local", type: mw.type, name: mw.name, plugin: mw.plugin || pluginName });
335
+ }
336
+ for (const mw of globalMws) {
337
+ this["~"].middlewares.push({ fn: mw.fn, scope: "global", type: mw.type, name: mw.name, plugin: mw.plugin || pluginName });
338
+ }
339
+ this.invalidate();
340
+ return this;
341
+ }
342
+ inspect() {
343
+ return this["~"].middlewares.map((m, i) => {
344
+ const info = { index: i, type: m.type, scope: m.scope };
345
+ if (m.name) info.name = m.name;
346
+ if (m.plugin) info.plugin = m.plugin;
347
+ return info;
348
+ });
349
+ }
350
+ trace(handler) {
351
+ this["~"].tracer = handler;
352
+ this.invalidate();
353
+ return this;
354
+ }
355
+ compose() {
356
+ if (!this["~"].compiled) {
357
+ const mws = this["~"].middlewares;
358
+ const tracer = this["~"].tracer;
359
+ const fns = tracer ? mws.map((m, i) => {
360
+ const info = { index: i, type: m.type, scope: m.scope };
361
+ if (m.name) info.name = m.name;
362
+ if (m.plugin) info.plugin = m.plugin;
363
+ const orig = m.fn;
364
+ const traced = async (ctx, next) => {
365
+ const done = tracer(info, ctx);
366
+ try {
367
+ const result = await orig(ctx, next);
368
+ done?.();
369
+ return result;
370
+ } catch (err) {
371
+ done?.(err);
372
+ throw err;
373
+ }
374
+ };
375
+ nameMiddleware(traced, "traced", orig.name || m.name);
376
+ return traced;
377
+ }) : mws.map((m) => m.fn);
378
+ const chain = compose(fns);
379
+ const onErrors = this["~"].onErrors;
380
+ const errorsDefinitions = this["~"].errorsDefinitions;
381
+ this["~"].compiled = (async (ctx, next) => {
382
+ try {
383
+ return await chain(ctx, next);
384
+ } catch (error) {
385
+ cleanErrorStack(error);
386
+ let kind;
387
+ for (const [k, ErrorClass] of Object.entries(errorsDefinitions)) {
388
+ if (error instanceof ErrorClass) {
389
+ kind = k;
390
+ break;
391
+ }
392
+ }
393
+ for (const handler of onErrors) {
394
+ const result = await handler({ error, context: ctx, kind });
395
+ if (result !== void 0) return result;
396
+ }
397
+ console.error("[composer] Unhandled error:", error);
398
+ }
399
+ });
400
+ }
401
+ return this["~"].compiled;
402
+ }
403
+ run(context, next) {
404
+ return this.compose()(context, next ?? noopNext);
405
+ }
406
+ }
407
+
408
+ class EventQueue {
409
+ handler;
410
+ queue = [];
411
+ pendingUpdates = /* @__PURE__ */ new Set();
412
+ active = true;
413
+ idleResolvers = [];
414
+ constructor(handler) {
415
+ this.handler = handler;
416
+ }
417
+ add(event) {
418
+ this.queue.push(event);
419
+ this.process();
420
+ }
421
+ addBatch(events) {
422
+ this.queue.push(...events);
423
+ this.process();
424
+ }
425
+ async stop(timeout = 3e3) {
426
+ this.active = false;
427
+ await Promise.race([
428
+ this.onIdle(),
429
+ new Promise((resolve) => setTimeout(resolve, timeout))
430
+ ]);
431
+ }
432
+ onIdle() {
433
+ if (this.queue.length === 0 && this.pendingUpdates.size === 0) {
434
+ return Promise.resolve();
435
+ }
436
+ return new Promise((resolve) => {
437
+ this.idleResolvers.push(resolve);
438
+ });
439
+ }
440
+ get pending() {
441
+ return this.pendingUpdates.size;
442
+ }
443
+ get queued() {
444
+ return this.queue.length;
445
+ }
446
+ get isActive() {
447
+ return this.active;
448
+ }
449
+ process() {
450
+ while (this.queue.length > 0 && this.active) {
451
+ const event = this.queue.shift();
452
+ const promise = this.handler(event).catch(() => {
453
+ }).then(() => {
454
+ this.pendingUpdates.delete(promise);
455
+ this.checkIdle();
456
+ });
457
+ this.pendingUpdates.add(promise);
458
+ }
459
+ }
460
+ checkIdle() {
461
+ if (this.queue.length === 0 && this.pendingUpdates.size === 0) {
462
+ for (const resolve of this.idleResolvers) {
463
+ resolve();
464
+ }
465
+ this.idleResolvers = [];
466
+ }
467
+ }
468
+ }
469
+
470
+ function createComposer(config) {
471
+ class EventComposerImpl extends Composer {
472
+ on(event, handler) {
473
+ const events = Array.isArray(event) ? event : [event];
474
+ const eventLabel = events.join("|");
475
+ const mw = (ctx, next) => {
476
+ if (events.includes(config.discriminator(ctx))) {
477
+ return handler(ctx, next);
478
+ }
479
+ return next();
480
+ };
481
+ nameMiddleware(mw, "on", eventLabel);
482
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "on", name: eventLabel });
483
+ this.invalidate();
484
+ return this;
485
+ }
486
+ derive(eventOrHandler, handlerOrOptions, maybeOptions) {
487
+ if (typeof eventOrHandler === "function") {
488
+ return super.derive(eventOrHandler, handlerOrOptions);
489
+ }
490
+ const events = Array.isArray(eventOrHandler) ? eventOrHandler : [eventOrHandler];
491
+ const handler = handlerOrOptions;
492
+ const eventLabel = events.join("|");
493
+ const handlerName = handler.name || void 0;
494
+ const deriveName = handlerName ? `${eventLabel}:${handlerName}` : eventLabel;
495
+ const mw = async (ctx, next) => {
496
+ if (events.includes(config.discriminator(ctx))) {
497
+ Object.assign(ctx, await handler(ctx));
498
+ }
499
+ return next();
500
+ };
501
+ nameMiddleware(mw, "derive", deriveName);
502
+ this["~"].middlewares.push({ fn: mw, scope: "local", type: "derive", name: deriveName });
503
+ this.invalidate();
504
+ return this;
505
+ }
506
+ }
507
+ return {
508
+ Composer: EventComposerImpl,
509
+ compose,
510
+ EventQueue
511
+ };
512
+ }
513
+
514
+ exports.Composer = Composer;
515
+ exports.EventQueue = EventQueue;
516
+ exports.compose = compose;
517
+ exports.createComposer = createComposer;
518
+ exports.noopNext = noopNext;
519
+ exports.skip = skip;
520
+ exports.stop = stop;