@akashic/headless-driver 2.6.3 → 2.7.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.
@@ -1,10 +1,17 @@
1
1
  import type { Permission } from "@akashic/amflow";
2
2
  import { AMFlowClient } from "./amflow/AMFlowClient";
3
+ import type { AMFlowStoreOptions } from "./amflow/AMFlowStore";
3
4
  export declare class AMFlowClientManager {
4
5
  /**
5
6
  * PlayId と AMFlowStore を紐付けるマップ情報。
6
7
  */
7
8
  private storeMap;
9
+ /**
10
+ * 対象の PlayID の オプションを設定する。
11
+ * @param playId PlayID
12
+ * @param options オプション
13
+ */
14
+ setAMFlowStoreOptions(playId: string, options: AMFlowStoreOptions | null): void;
8
15
  /**
9
16
  * 対象の PlayID の AMFlowClient を作成する。
10
17
  * @param playId PlayID
@@ -10,6 +10,18 @@ class AMFlowClientManager {
10
10
  */
11
11
  this.storeMap = new Map();
12
12
  }
13
+ /**
14
+ * 対象の PlayID の オプションを設定する。
15
+ * @param playId PlayID
16
+ * @param options オプション
17
+ */
18
+ setAMFlowStoreOptions(playId, options) {
19
+ let store = this.storeMap.get(playId);
20
+ if (!store) {
21
+ store = this.createAMFlowStore(playId);
22
+ }
23
+ store.setOptions(options);
24
+ }
13
25
  /**
14
26
  * 対象の PlayID の AMFlowClient を作成する。
15
27
  * @param playId PlayID
@@ -1,4 +1,5 @@
1
1
  import type { Permission } from "@akashic/amflow";
2
+ import type { AMFlowStoreOptions } from "./amflow";
2
3
  import type { AMFlowClient } from "./amflow/AMFlowClient";
3
4
  import type { DumpedPlaylog } from "./amflow/types";
4
5
  import type { ContentLocation } from "./Content";
@@ -18,7 +19,7 @@ export declare class PlayManager {
18
19
  * Play を作成する。
19
20
  * @param playLocation パラメータ
20
21
  */
21
- createPlay(playLocation: PlayManagerParameters, playlog?: DumpedPlaylog): Promise<string>;
22
+ createPlay(playLocation: PlayManagerParameters, playlog?: DumpedPlaylog, options?: AMFlowStoreOptions): Promise<string>;
22
23
  /**
23
24
  * Play を削除する。
24
25
  * @param playID PlayID
@@ -24,10 +24,12 @@ class PlayManager {
24
24
  * Play を作成する。
25
25
  * @param playLocation パラメータ
26
26
  */
27
- createPlay(playLocation, playlog) {
27
+ createPlay(playLocation, playlog, options) {
28
28
  return __awaiter(this, void 0, void 0, function* () {
29
29
  const playId = `${this.nextPlayId++}`;
30
30
  this.plays.push(Object.assign({ playId, status: "running", createdAt: Date.now(), lastSuspendedAt: null }, playLocation));
31
+ if (options)
32
+ this.amflowClientManager.setAMFlowStoreOptions(playId, options);
31
33
  if (playlog) {
32
34
  const amflow = this.createAMFlow(playId);
33
35
  amflow.setDumpedPlaylog(playlog);
@@ -15,7 +15,7 @@ export declare class AMFlowClient implements AMFlow {
15
15
  private permission;
16
16
  private tickHandlers;
17
17
  private eventHandlers;
18
- private unconsumedEvents;
18
+ private waitingStartPointRequests;
19
19
  constructor(playId: string, store: AMFlowStore);
20
20
  open(playId: string, callback?: (error: Error | null) => void): void;
21
21
  close(callback?: (error: Error | null) => void): void;
@@ -14,14 +14,39 @@ class AMFlowClient {
14
14
  this.permission = null;
15
15
  this.tickHandlers = [];
16
16
  this.eventHandlers = [];
17
- this.unconsumedEvents = [];
17
+ this.waitingStartPointRequests = [];
18
+ this.handleSendTick = (tick) => {
19
+ this.tickHandlers.forEach((h) => h(tick));
20
+ };
21
+ this.handleSendEvent = (event) => {
22
+ this.eventHandlers.forEach((h) => h(event));
23
+ };
24
+ this.handlePutStartPoint = (startPoint) => {
25
+ this.onPutStartPoint.fire(startPoint);
26
+ if (this.waitingStartPointRequests.length) {
27
+ const remains = [];
28
+ this.waitingStartPointRequests.forEach((req) => {
29
+ const { frame, timestamp } = req.options;
30
+ if ((frame != null && startPoint.frame <= frame) ||
31
+ (timestamp != null && startPoint.timestamp < timestamp) ||
32
+ (frame == null && timestamp == null && startPoint.frame === 0)) {
33
+ req.callback(null, startPoint);
34
+ return;
35
+ }
36
+ if (startPoint.frame === 0) {
37
+ req.callback((0, ErrorFactory_1.createError)("bad_request"));
38
+ return;
39
+ }
40
+ remains.push(req);
41
+ });
42
+ this.waitingStartPointRequests = remains;
43
+ }
44
+ };
18
45
  this.playId = playId;
19
46
  this.store = store;
20
47
  }
21
48
  open(playId, callback) {
22
49
  (0, Logger_1.getSystemLogger)().info("AMFlowClient#open()", playId);
23
- this.store.sendEventTrigger.add(this.handleSendEvent, this);
24
- this.store.sendTickTrigger.add(this.handleSendTick, this);
25
50
  this.store.putStartPointTrigger.add(this.handlePutStartPoint, this);
26
51
  this.state = "open";
27
52
  if (callback) {
@@ -101,6 +126,8 @@ class AMFlowClient {
101
126
  throw (0, ErrorFactory_1.createError)("permission_error", "Permission denied");
102
127
  }
103
128
  this.tickHandlers.push(handler);
129
+ if (this.tickHandlers.length === 1)
130
+ this.store.onTick(this.handleSendTick);
104
131
  }
105
132
  offTick(handler) {
106
133
  if (this.state !== "open") {
@@ -110,6 +137,8 @@ class AMFlowClient {
110
137
  throw (0, ErrorFactory_1.createError)("invalid_status", "Not authenticated");
111
138
  }
112
139
  this.tickHandlers = this.tickHandlers.filter((h) => h !== handler);
140
+ if (this.tickHandlers.length === 0)
141
+ this.store.offTick(this.handleSendTick);
113
142
  }
114
143
  sendEvent(event) {
115
144
  if (this.state !== "open") {
@@ -138,12 +167,8 @@ class AMFlowClient {
138
167
  throw (0, ErrorFactory_1.createError)("permission_error", "Permission denied");
139
168
  }
140
169
  this.eventHandlers.push(handler);
141
- if (0 < this.unconsumedEvents.length) {
142
- this.eventHandlers.forEach((h) => {
143
- this.unconsumedEvents.forEach((ev) => h(ev));
144
- });
145
- this.unconsumedEvents = [];
146
- }
170
+ if (this.eventHandlers.length === 1)
171
+ this.store.onEvent(this.handleSendEvent);
147
172
  }
148
173
  offEvent(handler) {
149
174
  if (this.state !== "open") {
@@ -153,6 +178,8 @@ class AMFlowClient {
153
178
  throw (0, ErrorFactory_1.createError)("invalid_status", "Not authenticated");
154
179
  }
155
180
  this.eventHandlers = this.eventHandlers.filter((h) => h !== handler);
181
+ if (this.eventHandlers.length === 0)
182
+ this.store.offEvent(this.handleSendEvent);
156
183
  }
157
184
  getTickList(optsOrBegin, endOrCallback, callbackOrUndefined) {
158
185
  setImmediate(() => {
@@ -229,12 +256,11 @@ class AMFlowClient {
229
256
  return;
230
257
  }
231
258
  const startPoint = this.store.getStartPoint(opts);
232
- if (startPoint) {
233
- callback(null, startPoint);
234
- }
235
- else {
236
- callback((0, ErrorFactory_1.createError)("runtime_error", "No start point"), undefined);
259
+ if (!startPoint) {
260
+ this.waitingStartPointRequests.push({ options: opts, callback });
261
+ return;
237
262
  }
263
+ callback(null, startPoint);
238
264
  });
239
265
  }
240
266
  /* eslint-disable @typescript-eslint/no-unused-vars */
@@ -258,15 +284,15 @@ class AMFlowClient {
258
284
  return;
259
285
  }
260
286
  if (!this.store.isDestroyed()) {
261
- this.store.sendEventTrigger.remove(this.handleSendEvent, this);
262
- this.store.sendTickTrigger.remove(this.handleSendTick, this);
287
+ this.store.offEvent(this.handleSendEvent);
288
+ this.store.offTick(this.handleSendTick);
263
289
  this.store.putStartPointTrigger.remove(this.handlePutStartPoint, this);
264
290
  }
265
291
  this.store = null;
266
292
  this.permission = null;
267
293
  this.tickHandlers = null;
268
294
  this.eventHandlers = null;
269
- this.unconsumedEvents = null;
295
+ this.waitingStartPointRequests = null;
270
296
  this.onPutStartPoint = null;
271
297
  }
272
298
  isDestroyed() {
@@ -275,18 +301,5 @@ class AMFlowClient {
275
301
  dump() {
276
302
  return this.store.dump();
277
303
  }
278
- handleSendTick(tick) {
279
- this.tickHandlers.forEach((h) => h(tick));
280
- }
281
- handleSendEvent(event) {
282
- if (this.eventHandlers.length <= 0) {
283
- this.unconsumedEvents.push(event);
284
- return;
285
- }
286
- this.eventHandlers.forEach((h) => h(event));
287
- }
288
- handlePutStartPoint(startPoint) {
289
- this.onPutStartPoint.fire(startPoint);
290
- }
291
304
  }
292
305
  exports.AMFlowClient = AMFlowClient;
@@ -2,24 +2,40 @@ import type { GetStartPointOptions, GetTickListOptions, Permission, StartPoint }
2
2
  import type { Event, Tick, TickList } from "@akashic/playlog";
3
3
  import { Trigger } from "@akashic/trigger";
4
4
  import type { DumpedPlaylog } from "./types";
5
+ export interface AMFlowStoreOptions {
6
+ /**
7
+ * 受信されないイベントをバッファリングするか。
8
+ *
9
+ * 真の場合、受信者が一つもない状態で送信されたイベントをバッファに保持する。
10
+ * 保持されたイベントは、最初のイベントハンドラが (AMFlow#onEvent() で) 登録された時、そのハンドラにすべて引き渡される。
11
+ * (二つ目以降のハンドラは受け取れないことに注意)
12
+ */
13
+ preservesUnhandledEvents?: boolean;
14
+ }
5
15
  /**
6
16
  * AMFlow のストア。
7
17
  * 一つのプレーに対して一つ存在する。
8
18
  */
9
19
  export declare class AMFlowStore {
10
20
  playId: string;
21
+ putStartPointTrigger: Trigger<StartPoint>;
11
22
  sendEventTrigger: Trigger<Event>;
12
23
  sendTickTrigger: Trigger<Tick>;
13
- putStartPointTrigger: Trigger<StartPoint>;
14
24
  private permissionMap;
15
25
  private startPoints;
16
26
  private unfilteredTickList;
17
27
  private filteredTickList;
18
28
  private suspended;
29
+ private options;
30
+ private unhandledEvents;
19
31
  constructor(playId: string);
20
32
  authenticate(token: string, revoke?: boolean): Permission;
21
33
  sendTick(tick: Tick): void;
22
34
  sendEvent(event: Event): void;
35
+ onTick(handler: (tick: Tick) => void): void;
36
+ offTick(handler: (tick: Tick) => void): void;
37
+ onEvent(handler: (event: Event) => void): void;
38
+ offEvent(handler: (event: Event) => void): void;
23
39
  getTickList(opts: GetTickListOptions): TickList | null;
24
40
  putStartPoint(startPoint: StartPoint): void;
25
41
  getStartPoint(opts: GetStartPointOptions): StartPoint | null;
@@ -41,6 +57,7 @@ export declare class AMFlowStore {
41
57
  resume(): void;
42
58
  setTickList(tickList: TickList | null): void;
43
59
  setDumpedPlaylog(dumped: DumpedPlaylog): void;
60
+ setOptions(options: AMFlowStoreOptions | null): void;
44
61
  destroy(): void;
45
62
  isDestroyed(): boolean;
46
63
  createPlayToken(permission: Permission): string;
@@ -11,13 +11,16 @@ const ErrorFactory_1 = require("./ErrorFactory");
11
11
  */
12
12
  class AMFlowStore {
13
13
  constructor(playId) {
14
+ this.putStartPointTrigger = new trigger_1.Trigger();
15
+ // 現状外部から参照する必要はないが、互換性のため少なくとも 2.x.x の間は公開する
14
16
  this.sendEventTrigger = new trigger_1.Trigger();
15
17
  this.sendTickTrigger = new trigger_1.Trigger();
16
- this.putStartPointTrigger = new trigger_1.Trigger();
17
18
  this.permissionMap = new Map();
18
19
  this.startPoints = [];
19
20
  this.unfilteredTickList = null;
20
21
  this.filteredTickList = null;
22
+ this.options = null;
23
+ this.unhandledEvents = [];
21
24
  this.playId = playId;
22
25
  this.suspended = false;
23
26
  }
@@ -39,10 +42,32 @@ class AMFlowStore {
39
42
  this.sendTickTrigger.fire(tick);
40
43
  }
41
44
  sendEvent(event) {
45
+ var _a;
42
46
  if (this.isSuspended()) {
43
47
  throw (0, ErrorFactory_1.createError)("bad_request", "Play may be suspended");
44
48
  }
45
- this.sendEventTrigger.fire(this.cloneDeep(event));
49
+ const ev = this.cloneDeep(event);
50
+ if (((_a = this.options) === null || _a === void 0 ? void 0 : _a.preservesUnhandledEvents) && this.sendEventTrigger.length === 0) {
51
+ this.unhandledEvents.push(ev);
52
+ return;
53
+ }
54
+ this.sendEventTrigger.fire(ev);
55
+ }
56
+ onTick(handler) {
57
+ this.sendTickTrigger.add(handler);
58
+ }
59
+ offTick(handler) {
60
+ this.sendTickTrigger.remove(handler);
61
+ }
62
+ onEvent(handler) {
63
+ this.sendEventTrigger.add(handler);
64
+ if (0 < this.unhandledEvents.length) {
65
+ this.unhandledEvents.forEach((ev) => this.sendEventTrigger.fire(ev));
66
+ this.unhandledEvents = [];
67
+ }
68
+ }
69
+ offEvent(handler) {
70
+ this.sendEventTrigger.remove(handler);
46
71
  }
47
72
  getTickList(opts) {
48
73
  if (!(this.unfilteredTickList && this.filteredTickList)) {
@@ -133,6 +158,9 @@ class AMFlowStore {
133
158
  this.setTickList(dumped.tickList);
134
159
  dumped.startPoints.forEach((sp) => this.putStartPoint(sp));
135
160
  }
161
+ setOptions(options) {
162
+ this.options = options;
163
+ }
136
164
  destroy() {
137
165
  if (this.isDestroyed()) {
138
166
  return;
@@ -143,6 +171,7 @@ class AMFlowStore {
143
171
  this.sendTickTrigger = null;
144
172
  this.permissionMap = null;
145
173
  this.startPoints = null;
174
+ this.unhandledEvents = null;
146
175
  this.putStartPointTrigger = null;
147
176
  }
148
177
  isDestroyed() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akashic/headless-driver",
3
- "version": "2.6.3",
3
+ "version": "2.7.0",
4
4
  "description": "A library to execute contents using Akashic Engine headlessly",
5
5
  "main": "lib/index.js",
6
6
  "author": "DWANGO Co., Ltd.",