@monixlite/grammy-scenes 1.2.2 → 1.4.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 +116 -47
- package/package.json +1 -1
- package/src/index.js +104 -37
package/README.md
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# @monixlite/grammy-scenes
|
|
2
2
|
|
|
3
|
-
A lightweight
|
|
4
|
-
|
|
3
|
+
A lightweight finite state machine (FSM) engine for Telegram bots built on grammY. The library provides declarative, step-based scenes with deterministic transitions, session-backed state, and minimal runtime overhead.
|
|
4
|
+
|
|
5
|
+
The goal of the library is to stay simple and predictable: no hidden magic, no implicit state, and no tight coupling to bot logic. Everything is explicit and driven by return values.
|
|
5
6
|
|
|
6
7
|
---
|
|
7
8
|
|
|
@@ -11,29 +12,30 @@ The library provides step-based scenes with declarative definitions, session-bac
|
|
|
11
12
|
npm install @monixlite/grammy-scenes
|
|
12
13
|
```
|
|
13
14
|
|
|
14
|
-
## Requirements
|
|
15
|
-
|
|
16
|
-
* Node.js >= 16
|
|
17
|
-
* grammY >= 1.19
|
|
18
|
-
|
|
19
15
|
---
|
|
20
16
|
|
|
21
17
|
## Core Concepts
|
|
22
18
|
|
|
23
|
-
|
|
19
|
+
### Scene
|
|
20
|
+
|
|
21
|
+
A scene is a logical flow (for example: `auth`, `profile_edit`, `checkout`).
|
|
24
22
|
|
|
25
|
-
|
|
23
|
+
### Step
|
|
26
24
|
|
|
27
|
-
|
|
25
|
+
A step is a single state inside a scene. Steps are ordered in the same order they are declared.
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
### FSM
|
|
28
|
+
|
|
29
|
+
Internally, the library builds a finite state machine:
|
|
30
|
+
|
|
31
|
+
* scene title → ordered step list
|
|
32
|
+
* step → action handlers
|
|
31
33
|
|
|
32
34
|
---
|
|
33
35
|
|
|
34
36
|
## Basic Usage
|
|
35
37
|
|
|
36
|
-
```
|
|
38
|
+
```js
|
|
37
39
|
const { Bot, session } = require("grammy");
|
|
38
40
|
const { Scenes } = require("@monixlite/grammy-scenes");
|
|
39
41
|
|
|
@@ -55,7 +57,7 @@ const scenes = new Scenes([
|
|
|
55
57
|
step: "phone",
|
|
56
58
|
},
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
action: {
|
|
59
61
|
enter: async (ctx) => {
|
|
60
62
|
await ctx.reply("• Enter your phone number:");
|
|
61
63
|
},
|
|
@@ -72,7 +74,7 @@ const scenes = new Scenes([
|
|
|
72
74
|
step: "confirm",
|
|
73
75
|
},
|
|
74
76
|
|
|
75
|
-
|
|
77
|
+
action: {
|
|
76
78
|
enter: async (ctx) => {
|
|
77
79
|
const { phone } = ctx.session;
|
|
78
80
|
|
|
@@ -88,6 +90,7 @@ const scenes = new Scenes([
|
|
|
88
90
|
text: "• Yes",
|
|
89
91
|
callback_data: "confirm:yes",
|
|
90
92
|
},
|
|
93
|
+
|
|
91
94
|
{
|
|
92
95
|
text: "• No",
|
|
93
96
|
callback_data: "confirm:no",
|
|
@@ -98,27 +101,28 @@ const scenes = new Scenes([
|
|
|
98
101
|
});
|
|
99
102
|
},
|
|
100
103
|
|
|
101
|
-
|
|
104
|
+
callbacks: [
|
|
102
105
|
{
|
|
103
|
-
match: /^confirm:yes$/,
|
|
104
|
-
handler: async (ctx) => {
|
|
105
|
-
|
|
106
|
+
match: /^confirm:(yes|no)$/,
|
|
107
|
+
handler: async (ctx, match) => {
|
|
108
|
+
await ctx.answerCallbackQuery();
|
|
106
109
|
|
|
107
110
|
|
|
108
|
-
|
|
109
|
-
|
|
111
|
+
switch (match[1]) {
|
|
112
|
+
case "yes": {
|
|
113
|
+
const { phone } = ctx.session;
|
|
110
114
|
|
|
111
115
|
|
|
112
|
-
|
|
113
|
-
},
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
match: /^confirm:no$/,
|
|
117
|
-
handler: async (ctx) => {
|
|
118
|
-
await ctx.answerCallbackQuery();
|
|
116
|
+
await ctx.reply(`• Thank you! Your phone number ${phone} has been saved.`);
|
|
119
117
|
|
|
120
118
|
|
|
121
|
-
|
|
119
|
+
return "stop";
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
default: {
|
|
123
|
+
return "^phone";
|
|
124
|
+
};
|
|
125
|
+
};
|
|
122
126
|
},
|
|
123
127
|
},
|
|
124
128
|
],
|
|
@@ -126,7 +130,7 @@ const scenes = new Scenes([
|
|
|
126
130
|
},
|
|
127
131
|
],
|
|
128
132
|
], {
|
|
129
|
-
"
|
|
133
|
+
["inline_keyboard:scene:cancel"]: {
|
|
130
134
|
enabled: true,
|
|
131
135
|
|
|
132
136
|
component: {
|
|
@@ -150,7 +154,7 @@ bot.callbackQuery("scene:cancel", async (ctx) => {
|
|
|
150
154
|
|
|
151
155
|
|
|
152
156
|
bot.command("start", async (ctx) => {
|
|
153
|
-
await scenes.enter(ctx, {
|
|
157
|
+
return await scenes.enter(ctx, {
|
|
154
158
|
title: "auth",
|
|
155
159
|
step: "phone",
|
|
156
160
|
});
|
|
@@ -166,20 +170,24 @@ bot.start();
|
|
|
166
170
|
|
|
167
171
|
## Scene Definition
|
|
168
172
|
|
|
169
|
-
```
|
|
173
|
+
```js
|
|
170
174
|
{
|
|
171
175
|
scene: {
|
|
172
176
|
title: string,
|
|
173
177
|
step: string,
|
|
174
178
|
},
|
|
175
179
|
|
|
176
|
-
|
|
180
|
+
action: {
|
|
177
181
|
enter?: (ctx) => Promise<void>,
|
|
182
|
+
|
|
178
183
|
update?: (ctx) => Promise<SceneResult | void>,
|
|
179
184
|
message?: (ctx) => Promise<SceneResult | void>,
|
|
180
|
-
|
|
185
|
+
callbacks?: Array<{
|
|
181
186
|
match: RegExp,
|
|
182
|
-
handler: (
|
|
187
|
+
handler: (
|
|
188
|
+
ctx,
|
|
189
|
+
match: RegExpMatchArray,
|
|
190
|
+
) => Promise<SceneResult | void>,
|
|
183
191
|
}>,
|
|
184
192
|
},
|
|
185
193
|
}
|
|
@@ -202,7 +210,7 @@ Executed on step entry.
|
|
|
202
210
|
|
|
203
211
|
Executed on any update while the step is active.
|
|
204
212
|
|
|
205
|
-
* Runs before `message` and `
|
|
213
|
+
* Runs before `message` and `callbacks`
|
|
206
214
|
* Suitable for guards, timeouts, media
|
|
207
215
|
* Return value controls navigation
|
|
208
216
|
|
|
@@ -210,7 +218,7 @@ Executed on any update while the step is active.
|
|
|
210
218
|
|
|
211
219
|
Triggered on text messages during the step.
|
|
212
220
|
|
|
213
|
-
###
|
|
221
|
+
### callbacks[]
|
|
214
222
|
|
|
215
223
|
CallbackQuery handlers matched by regexp.
|
|
216
224
|
The handler return value follows standard transition rules.
|
|
@@ -232,7 +240,7 @@ Behavior:
|
|
|
232
240
|
|
|
233
241
|
### Manual Termination
|
|
234
242
|
|
|
235
|
-
```
|
|
243
|
+
```js
|
|
236
244
|
ctx.session.scene = null;
|
|
237
245
|
```
|
|
238
246
|
|
|
@@ -240,7 +248,7 @@ ctx.session.scene = null;
|
|
|
240
248
|
|
|
241
249
|
## Middleware
|
|
242
250
|
|
|
243
|
-
```
|
|
251
|
+
```js
|
|
244
252
|
bot.use(session(...));
|
|
245
253
|
bot.use(scenes.middleware);
|
|
246
254
|
```
|
|
@@ -273,7 +281,7 @@ Transitions are driven by callback return values.
|
|
|
273
281
|
|
|
274
282
|
Scene termination sets:
|
|
275
283
|
|
|
276
|
-
```
|
|
284
|
+
```js
|
|
277
285
|
ctx.session.scene = null;
|
|
278
286
|
```
|
|
279
287
|
|
|
@@ -281,14 +289,40 @@ ctx.session.scene = null;
|
|
|
281
289
|
|
|
282
290
|
## Options
|
|
283
291
|
|
|
284
|
-
|
|
292
|
+
Options are passed to the `Scenes` constructor and stored internally.
|
|
285
293
|
|
|
286
|
-
|
|
294
|
+
```js
|
|
295
|
+
const scenes = new Scenes(scenes, options, DEV);
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### action:enter:redefinition:reply
|
|
299
|
+
|
|
300
|
+
Controls whether `ctx.reply` is temporarily patched during `enter`.
|
|
287
301
|
|
|
302
|
+
When enabled, all `ctx.reply` calls inside `enter`:
|
|
303
|
+
|
|
304
|
+
* preserve existing inline keyboards
|
|
305
|
+
* automatically append the cancel button (if enabled)
|
|
306
|
+
|
|
307
|
+
```js
|
|
308
|
+
{
|
|
309
|
+
["action:enter:redefinition:reply"]: {
|
|
310
|
+
enabled: true,
|
|
311
|
+
},
|
|
312
|
+
}
|
|
288
313
|
```
|
|
314
|
+
|
|
315
|
+
Disable this option if you want full manual control over `ctx.reply` behavior inside `enter`.
|
|
316
|
+
|
|
317
|
+
### inline_keyboard:scene:cancel
|
|
318
|
+
|
|
319
|
+
Automatically injects a cancel button into **all `enter` replies**.
|
|
320
|
+
|
|
321
|
+
```js
|
|
289
322
|
{
|
|
290
|
-
"
|
|
323
|
+
["inline_keyboard:scene:cancel"]: {
|
|
291
324
|
enabled: true,
|
|
325
|
+
|
|
292
326
|
component: {
|
|
293
327
|
text: "Cancel",
|
|
294
328
|
callback_data: "scene:cancel",
|
|
@@ -299,21 +333,56 @@ Injects a cancel button into all `enter` replies.
|
|
|
299
333
|
|
|
300
334
|
Notes:
|
|
301
335
|
|
|
302
|
-
* Only affects `enter`
|
|
303
|
-
*
|
|
336
|
+
* Only affects `enter(ctx)` replies
|
|
337
|
+
* Does not implement cancellation logic
|
|
338
|
+
* The handler for `scene:cancel` is user-defined
|
|
339
|
+
|
|
340
|
+
---
|
|
341
|
+
|
|
342
|
+
## Accessing Options at Runtime
|
|
343
|
+
|
|
344
|
+
Options passed to `Scenes` are stored internally and can be accessed via `scenes.option(name)`.
|
|
345
|
+
|
|
346
|
+
This is useful when you need to reuse option configuration (for example, the cancel button component) outside of the scene lifecycle.
|
|
347
|
+
|
|
348
|
+
Example:
|
|
349
|
+
|
|
350
|
+
```js
|
|
351
|
+
const cancel = scenes.option("inline_keyboard:scene:cancel");
|
|
352
|
+
|
|
353
|
+
if (cancel?.enabled) {
|
|
354
|
+
console.log(cancel.component);
|
|
355
|
+
// { text: 'Cancel', callback_data: 'scene:cancel' }
|
|
356
|
+
};
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
Notes:
|
|
360
|
+
|
|
361
|
+
* Returns `null` if the option is not defined
|
|
362
|
+
* The returned object is not cloned; treat it as read-only
|
|
304
363
|
|
|
305
364
|
---
|
|
306
365
|
|
|
307
366
|
## API
|
|
308
367
|
|
|
309
|
-
### new Scenes(scenes, options)
|
|
368
|
+
### new Scenes(scenes, options, DEV?)
|
|
310
369
|
|
|
311
|
-
Creates a scene manager
|
|
370
|
+
Creates a scene manager backed by `ScenesMiddleware`.
|
|
312
371
|
|
|
313
372
|
Exports:
|
|
314
373
|
|
|
315
374
|
* `scenes.middleware`
|
|
316
375
|
* `scenes.enter(ctx, scene)`
|
|
376
|
+
* `scenes.option(name)`
|
|
377
|
+
|
|
378
|
+
### DEV mode
|
|
379
|
+
|
|
380
|
+
When `DEV` is set to `true`, the middleware prints warnings to the console if:
|
|
381
|
+
|
|
382
|
+
* a transition points to a missing step
|
|
383
|
+
* there is no next / previous step in a scene
|
|
384
|
+
|
|
385
|
+
This mode is intended for development and debugging only.
|
|
317
386
|
|
|
318
387
|
---
|
|
319
388
|
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -1,59 +1,72 @@
|
|
|
1
1
|
class ScenesMiddleware {
|
|
2
2
|
constructor({
|
|
3
3
|
scenes,
|
|
4
|
-
options
|
|
4
|
+
options,
|
|
5
|
+
DEV,
|
|
5
6
|
}) {
|
|
6
7
|
this.scenes = scenes;
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
|
|
10
|
+
this.map = {}; {
|
|
9
11
|
for (const group of scenes) {
|
|
10
12
|
for (const item of group) {
|
|
11
13
|
const { title, step } = item.scene;
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
if (!this.
|
|
15
|
-
this.
|
|
16
|
+
if (!this.map[title]) {
|
|
17
|
+
this.map[title] = {
|
|
18
|
+
order: [],
|
|
19
|
+
steps: {},
|
|
20
|
+
};
|
|
16
21
|
};
|
|
17
22
|
|
|
18
23
|
|
|
19
|
-
this.
|
|
24
|
+
this.map[title].order.push(step);
|
|
25
|
+
this.map[title].steps[step] = item;
|
|
20
26
|
};
|
|
21
27
|
};
|
|
22
28
|
};
|
|
23
29
|
|
|
24
30
|
|
|
25
31
|
this.options = {
|
|
26
|
-
["
|
|
32
|
+
["action:enter:redefinition:reply"]: {
|
|
33
|
+
enabled: true,
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
["inline_keyboard:scene:cancel"]: {
|
|
27
37
|
enabled: true,
|
|
28
38
|
|
|
29
39
|
component: {
|
|
30
|
-
text: "
|
|
40
|
+
text: "Cancel",
|
|
31
41
|
callback_data: "scene:cancel",
|
|
32
42
|
},
|
|
33
43
|
},
|
|
34
44
|
|
|
35
45
|
...options,
|
|
36
46
|
};
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
this.DEV = DEV;
|
|
37
50
|
};
|
|
38
51
|
|
|
39
52
|
|
|
40
53
|
scene = (scene) => {
|
|
41
|
-
return this.
|
|
54
|
+
return (this.map?.[scene?.title]?.steps?.[scene?.step] ?? null);
|
|
42
55
|
};
|
|
43
56
|
|
|
44
57
|
step = (scene, dir) => {
|
|
45
|
-
const
|
|
46
|
-
if (!
|
|
58
|
+
const entry = this.map?.[scene?.title]; {
|
|
59
|
+
if (!entry) return null;
|
|
47
60
|
};
|
|
48
61
|
|
|
49
62
|
|
|
50
|
-
const index =
|
|
63
|
+
const index = entry.order.indexOf(scene?.step); {
|
|
51
64
|
if (index == -1) return null;
|
|
52
65
|
};
|
|
53
66
|
|
|
54
67
|
|
|
55
|
-
if (dir == "next") return (
|
|
56
|
-
if (dir == "prev") return (
|
|
68
|
+
if (dir == "next") return (entry.order[(index + 1)] ?? null);
|
|
69
|
+
if (dir == "prev") return (entry.order[(index - 1)] ?? null);
|
|
57
70
|
|
|
58
71
|
|
|
59
72
|
return null;
|
|
@@ -65,31 +78,42 @@ class ScenesMiddleware {
|
|
|
65
78
|
|
|
66
79
|
|
|
67
80
|
const current = this.scene(scene); {
|
|
68
|
-
if (!current?.
|
|
81
|
+
if (!current?.action?.enter) return;
|
|
69
82
|
};
|
|
70
83
|
|
|
71
84
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
if (!this.options?.["action:enter:redefinition:reply"]?.enabled) return await current.action.enter(ctx);
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
const original = {
|
|
89
|
+
reply: ((typeof (ctx.reply) == "function") ? ctx.reply.bind(ctx) : null),
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
try {
|
|
93
|
+
if (original?.reply) ctx.reply = (text, extra = {}) => {
|
|
94
|
+
const keyboard = [
|
|
95
|
+
...(extra.reply_markup?.inline_keyboard || []),
|
|
96
|
+
]; {
|
|
97
|
+
if (this.options?.["inline_keyboard:scene:cancel"]?.enabled) keyboard.push([
|
|
98
|
+
this.options?.["inline_keyboard:scene:cancel"]?.component,
|
|
77
99
|
]);
|
|
78
100
|
};
|
|
79
101
|
|
|
80
102
|
|
|
81
|
-
return original(text, {
|
|
103
|
+
return original.reply(text, {
|
|
82
104
|
...extra,
|
|
83
105
|
reply_markup: {
|
|
106
|
+
...(extra?.reply_markup || {}),
|
|
84
107
|
inline_keyboard: keyboard,
|
|
85
108
|
},
|
|
86
109
|
});
|
|
87
110
|
};
|
|
88
|
-
};
|
|
89
111
|
|
|
90
112
|
|
|
91
|
-
|
|
92
|
-
|
|
113
|
+
await current.action.enter(ctx);
|
|
114
|
+
} finally {
|
|
115
|
+
if (original?.reply) ctx.reply = original.reply;
|
|
116
|
+
};
|
|
93
117
|
};
|
|
94
118
|
|
|
95
119
|
handle = async (ctx, scene, result) => {
|
|
@@ -103,7 +127,17 @@ class ScenesMiddleware {
|
|
|
103
127
|
const step = this.step({
|
|
104
128
|
title: scene.title,
|
|
105
129
|
step: scene.step,
|
|
106
|
-
}, "prev");
|
|
130
|
+
}, "prev"); {
|
|
131
|
+
if (!step) {
|
|
132
|
+
if (this.DEV) {
|
|
133
|
+
console.warn(`[grammy-scenes] No previous step found for scene "${scene.title}" (current step: "${scene.step}")`);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
ctx.session.scene = null;
|
|
138
|
+
return;
|
|
139
|
+
};
|
|
140
|
+
};
|
|
107
141
|
|
|
108
142
|
|
|
109
143
|
return await this.enter(ctx, {
|
|
@@ -116,7 +150,17 @@ class ScenesMiddleware {
|
|
|
116
150
|
const step = this.step({
|
|
117
151
|
title: scene.title,
|
|
118
152
|
step: scene.step,
|
|
119
|
-
}, "next");
|
|
153
|
+
}, "next"); {
|
|
154
|
+
if (!step) {
|
|
155
|
+
if (this.DEV) {
|
|
156
|
+
console.warn(`[grammy-scenes] No next step found for scene "${scene.title}" (current step: "${scene.step}")`);
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
ctx.session.scene = null;
|
|
161
|
+
return;
|
|
162
|
+
};
|
|
163
|
+
};
|
|
120
164
|
|
|
121
165
|
|
|
122
166
|
return await this.enter(ctx, {
|
|
@@ -139,6 +183,11 @@ class ScenesMiddleware {
|
|
|
139
183
|
|
|
140
184
|
const transition = String(result).match(/^\^(.*)$/); {
|
|
141
185
|
if (!transition || !transition?.[1]) {
|
|
186
|
+
if (this.DEV) {
|
|
187
|
+
console.warn(`[grammy-scenes] Invalid transition result:`, result);
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
|
|
142
191
|
ctx.session.scene = null;
|
|
143
192
|
return;
|
|
144
193
|
};
|
|
@@ -167,56 +216,74 @@ class ScenesMiddleware {
|
|
|
167
216
|
|
|
168
217
|
|
|
169
218
|
const current = this.scene(scene); {
|
|
170
|
-
if (!current?.
|
|
219
|
+
if (!current?.action) return next();
|
|
171
220
|
};
|
|
172
221
|
|
|
173
222
|
|
|
174
|
-
if (ctx?.update && current.
|
|
175
|
-
const result = await current.
|
|
223
|
+
if (ctx?.update && current.action?.update) {
|
|
224
|
+
const result = await current.action.update(ctx);
|
|
176
225
|
|
|
177
226
|
return await this.handle(ctx, scene, result);
|
|
178
227
|
};
|
|
179
228
|
|
|
180
229
|
|
|
181
|
-
if (ctx?.message && current.
|
|
230
|
+
if (ctx?.message && current.action?.message) {
|
|
182
231
|
if (ctx?.message?.caption) ctx.message.text ??= ctx.message.caption;
|
|
183
232
|
|
|
184
233
|
|
|
185
|
-
const result = await current.
|
|
234
|
+
const result = await current.action.message(ctx);
|
|
186
235
|
|
|
187
236
|
return await this.handle(ctx, scene, result);
|
|
188
237
|
};
|
|
189
238
|
|
|
190
239
|
|
|
191
|
-
if (ctx?.callbackQuery && current.callbacks
|
|
240
|
+
if (ctx?.callbackQuery && current.action?.callbacks) {
|
|
241
|
+
if (!Array.isArray(current.action.callbacks)) return next();
|
|
242
|
+
|
|
243
|
+
|
|
192
244
|
const data = ctx.callbackQuery.data;
|
|
193
245
|
|
|
194
246
|
|
|
195
|
-
for (const q of current.callbacks
|
|
196
|
-
if (q
|
|
197
|
-
const result = await q.handler(ctx);
|
|
247
|
+
for (const q of current.action.callbacks) {
|
|
248
|
+
if (!(q?.match instanceof RegExp) || !q?.handler) continue;
|
|
198
249
|
|
|
199
|
-
|
|
250
|
+
|
|
251
|
+
const match = data.match(q.match); {
|
|
252
|
+
if (!match) continue;
|
|
200
253
|
};
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
const result = await q.handler(ctx, match);
|
|
257
|
+
|
|
258
|
+
return await this.handle(ctx, scene, result);
|
|
201
259
|
};
|
|
202
260
|
};
|
|
203
261
|
|
|
204
262
|
|
|
205
263
|
return next();
|
|
206
264
|
};
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
option = (option) => {
|
|
268
|
+
return (this.options?.[option] ?? null);
|
|
269
|
+
};
|
|
207
270
|
};
|
|
208
271
|
|
|
209
272
|
|
|
210
273
|
class Scenes {
|
|
211
|
-
constructor(scenes, options = {}) {
|
|
274
|
+
constructor(scenes = [], options = {}, DEV = false) {
|
|
212
275
|
this.instance = new ScenesMiddleware({
|
|
213
276
|
scenes: scenes,
|
|
214
277
|
options: options,
|
|
278
|
+
DEV: DEV,
|
|
215
279
|
});
|
|
216
280
|
|
|
217
281
|
|
|
218
|
-
this.middleware = this.instance.middleware;
|
|
219
282
|
this.enter = this.instance.enter.bind(this.instance);
|
|
283
|
+
this.middleware = this.instance.middleware.bind(this.instance);
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
this.option = this.instance.option.bind(this.instance);
|
|
220
287
|
};
|
|
221
288
|
};
|
|
222
289
|
|