maplibre-gl 3.5.0 → 3.5.2

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/src/util/actor.ts CHANGED
@@ -1,9 +1,57 @@
1
- import {isWorker, isSafari} from './util';
2
- import {serialize, deserialize} from './web_worker_transfer';
1
+ import {isWorker} from './util';
2
+ import {serialize, deserialize, Serialized} from './web_worker_transfer';
3
3
  import {ThrottledInvoker} from './throttled_invoker';
4
4
 
5
5
  import type {Transferable} from '../types/transferable';
6
6
  import type {Cancelable} from '../types/cancelable';
7
+ import type {WorkerSource} from '../source/worker_source';
8
+ import type {OverscaledTileID} from '../source/tile_id';
9
+ import type {Callback} from '../types/callback';
10
+ import type {StyleGlyph} from '../style/style_glyph';
11
+
12
+ export interface ActorTarget {
13
+ addEventListener: typeof window.addEventListener;
14
+ removeEventListener: typeof window.removeEventListener;
15
+ postMessage: typeof window.postMessage;
16
+ terminate?: () => void;
17
+ }
18
+
19
+ export interface WorkerSourceProvider {
20
+ getWorkerSource(mapId: string | number, sourceType: string, sourceName: string): WorkerSource;
21
+ }
22
+
23
+ export interface GlyphsProvider {
24
+ getGlyphs(mapId: string, params: {
25
+ stacks: {[_: string]: Array<number>};
26
+ source: string;
27
+ tileID: OverscaledTileID;
28
+ type: string;
29
+ },
30
+ callback: Callback<{[_: string]: {[_: number]: StyleGlyph}}>
31
+ );
32
+ }
33
+
34
+ export type MessageType = '<response>' | '<cancel>' |
35
+ 'geojson.getClusterExpansionZoom' | 'geojson.getClusterChildren' | 'geojson.getClusterLeaves' | 'geojson.loadData' |
36
+ 'removeSource' | 'loadWorkerSource' | 'loadDEMTile' | 'removeDEMTile' |
37
+ 'removeTile' | 'reloadTile' | 'abortTile' | 'loadTile' | 'getTile' |
38
+ 'getGlyphs' | 'getImages' | 'setImages' |
39
+ 'syncRTLPluginState' | 'setReferrer' | 'setLayers' | 'updateLayers';
40
+
41
+ export type MessageData = {
42
+ id: string;
43
+ type: MessageType;
44
+ data?: Serialized;
45
+ targetMapId?: string | number | null;
46
+ mustQueue?: boolean;
47
+ error?: Serialized | null;
48
+ hasCallback?: boolean;
49
+ sourceMapId: string | number | null;
50
+ }
51
+
52
+ export type Message = {
53
+ data: MessageData;
54
+ }
7
55
 
8
56
  /**
9
57
  * An implementation of the [Actor design pattern](http://en.wikipedia.org/wiki/Actor_model)
@@ -12,36 +60,30 @@ import type {Cancelable} from '../types/cancelable';
12
60
  * owned by the styles
13
61
  */
14
62
  export class Actor {
15
- target: any;
16
- parent: any;
17
- mapId: string | null;
18
- callbacks: {
19
- number: any;
20
- };
63
+ target: ActorTarget;
64
+ parent: WorkerSourceProvider | GlyphsProvider;
65
+ mapId: string | number | null;
66
+ callbacks: { [x: number]: Function};
21
67
  name: string;
22
- tasks: {
23
- number: any;
24
- };
25
- taskQueue: Array<number>;
26
- cancelCallbacks: {
27
- number: Cancelable;
28
- };
68
+ tasks: { [x: number]: MessageData };
69
+ taskQueue: Array<string>;
70
+ cancelCallbacks: { [x: number]: () => void };
29
71
  invoker: ThrottledInvoker;
30
- globalScope: any;
72
+ globalScope: ActorTarget;
31
73
 
32
74
  /**
33
75
  * @param target - The target
34
76
  * @param parent - The parent
35
77
  * @param mapId - A unique identifier for the Map instance using this Actor.
36
78
  */
37
- constructor(target: any, parent: any, mapId?: string) {
79
+ constructor(target: ActorTarget, parent: WorkerSourceProvider | GlyphsProvider, mapId?: string | number) {
38
80
  this.target = target;
39
81
  this.parent = parent;
40
82
  this.mapId = mapId;
41
- this.callbacks = {} as { number: any };
42
- this.tasks = {} as { number: any };
83
+ this.callbacks = {};
84
+ this.tasks = {};
43
85
  this.taskQueue = [];
44
- this.cancelCallbacks = {} as { number: Cancelable };
86
+ this.cancelCallbacks = {};
45
87
  this.invoker = new ThrottledInvoker(this.process);
46
88
  this.target.addEventListener('message', this.receive, false);
47
89
  this.globalScope = isWorker() ? target : window;
@@ -55,7 +97,7 @@ export class Actor {
55
97
  * @param targetMapId - A particular mapId to which to send this message.
56
98
  */
57
99
  send(
58
- type: string,
100
+ type: MessageType,
59
101
  data: unknown,
60
102
  callback?: Function | null,
61
103
  targetMapId?: string | null,
@@ -69,8 +111,8 @@ export class Actor {
69
111
  if (callback) {
70
112
  this.callbacks[id] = callback;
71
113
  }
72
- const buffers: Array<Transferable> = isSafari(this.globalScope) ? undefined : [];
73
- this.target.postMessage({
114
+ const buffers: Array<Transferable> = [];
115
+ const message: MessageData = {
74
116
  id,
75
117
  type,
76
118
  hasCallback: !!callback,
@@ -78,31 +120,27 @@ export class Actor {
78
120
  mustQueue,
79
121
  sourceMapId: this.mapId,
80
122
  data: serialize(data, buffers)
81
- }, buffers);
123
+ };
124
+
125
+ this.target.postMessage(message, {transfer: buffers});
82
126
  return {
83
127
  cancel: () => {
84
128
  if (callback) {
85
129
  // Set the callback to null so that it never fires after the request is aborted.
86
130
  delete this.callbacks[id];
87
131
  }
88
- this.target.postMessage({
132
+ const cancelMessage: MessageData = {
89
133
  id,
90
134
  type: '<cancel>',
91
135
  targetMapId,
92
136
  sourceMapId: this.mapId
93
- });
137
+ };
138
+ this.target.postMessage(cancelMessage);
94
139
  }
95
140
  };
96
141
  }
97
142
 
98
- receive = (message: {
99
- data: {
100
- id: number;
101
- type: string;
102
- data: unknown;
103
- targetMapId?: string | null;
104
- mustQueue: boolean;
105
- };}) => {
143
+ receive = (message: Message) => {
106
144
  const data = message.data;
107
145
  const id = data.id;
108
146
 
@@ -164,7 +202,7 @@ export class Actor {
164
202
  this.processTask(id, task);
165
203
  };
166
204
 
167
- processTask(id: number, task: any) {
205
+ processTask(id: string, task: MessageData) {
168
206
  if (task.type === '<response>') {
169
207
  // The done() function in the counterpart has been called, and we are now
170
208
  // firing the callback in the originating actor, if there is one.
@@ -180,30 +218,31 @@ export class Actor {
180
218
  }
181
219
  } else {
182
220
  let completed = false;
183
- const buffers: Array<Transferable> = isSafari(this.globalScope) ? undefined : [];
221
+ const buffers: Array<Transferable> = [];
184
222
  const done = task.hasCallback ? (err: Error, data?: any) => {
185
223
  completed = true;
186
224
  delete this.cancelCallbacks[id];
187
- this.target.postMessage({
225
+ const responseMessage: MessageData = {
188
226
  id,
189
227
  type: '<response>',
190
228
  sourceMapId: this.mapId,
191
229
  error: err ? serialize(err) : null,
192
230
  data: serialize(data, buffers)
193
- }, buffers);
231
+ };
232
+ this.target.postMessage(responseMessage, {transfer: buffers});
194
233
  } : (_) => {
195
234
  completed = true;
196
235
  };
197
236
 
198
- let callback = null;
199
- const params = (deserialize(task.data) as any);
237
+ let callback: Cancelable = null;
238
+ const params = deserialize(task.data);
200
239
  if (this.parent[task.type]) {
201
240
  // task.type == 'loadTile', 'removeTile', etc.
202
241
  callback = this.parent[task.type](task.sourceMapId, params, done);
203
- } else if (this.parent.getWorkerSource) {
242
+ } else if ('getWorkerSource' in this.parent) {
204
243
  // task.type == sourcetype.method
205
244
  const keys = task.type.split('.');
206
- const scope = (this.parent as any).getWorkerSource(task.sourceMapId, keys[0], params.source);
245
+ const scope = this.parent.getWorkerSource(task.sourceMapId, keys[0], (params as any).source);
207
246
  callback = scope[keys[1]](params, done);
208
247
  } else {
209
248
  // No function was found.
@@ -1,3 +1,4 @@
1
+ import {Actor} from './actor';
1
2
  import {Dispatcher} from './dispatcher';
2
3
  import {workerFactory} from './web_worker';
3
4
  import {WorkerPool} from './worker_pool';
@@ -16,7 +17,7 @@ describe('Dispatcher', () => {
16
17
  }
17
18
  } as any as WorkerPool;
18
19
 
19
- const dispatcher = new Dispatcher(workerPool, {}, mapId);
20
+ const dispatcher = new Dispatcher(workerPool, {} as any, mapId);
20
21
  expect(dispatcher.actors.map((actor) => { return actor.target; })).toEqual(workers);
21
22
  dispatcher.remove();
22
23
  expect(dispatcher.actors).toHaveLength(0);
@@ -41,7 +42,7 @@ describe('Dispatcher', () => {
41
42
  }
42
43
  } as any as WorkerPool;
43
44
 
44
- let dispatcher = new Dispatcher(workerPool, {}, mapId);
45
+ let dispatcher = new Dispatcher(workerPool, {} as any, mapId);
45
46
  expect(dispatcher.actors.map((actor) => { return actor.target; })).toEqual(workers);
46
47
 
47
48
  // Remove dispatcher, but map is not disposed (During style change)
@@ -50,7 +51,7 @@ describe('Dispatcher', () => {
50
51
  expect(releaseCalled).toHaveLength(0);
51
52
 
52
53
  // Create new instance of dispatcher
53
- dispatcher = new Dispatcher(workerPool, {}, mapId);
54
+ dispatcher = new Dispatcher(workerPool, {} as any, mapId);
54
55
  expect(dispatcher.actors.map((actor) => { return actor.target; })).toEqual(workers);
55
56
  dispatcher.remove(true); // mapRemoved = true
56
57
  expect(dispatcher.actors).toHaveLength(0);
@@ -61,14 +62,12 @@ describe('Dispatcher', () => {
61
62
  test('#remove destroys actors', () => {
62
63
  const actorsRemoved = [];
63
64
  const mapId = 1;
64
- function Actor() {
65
- this.remove = function() { actorsRemoved.push(this); };
66
- }
67
- jest.spyOn(Dispatcher, 'Actor').mockImplementation(Actor as any);
65
+ const spy = jest.fn().mockImplementation(() => { actorsRemoved.push(this); });
66
+ Actor.prototype.remove = spy;
68
67
  WorkerPool.workerCount = 4;
69
68
 
70
69
  const workerPool = new WorkerPool();
71
- const dispatcher = new Dispatcher(workerPool, {}, mapId);
70
+ const dispatcher = new Dispatcher(workerPool, {} as any, mapId);
72
71
  dispatcher.remove();
73
72
  expect(actorsRemoved).toHaveLength(4);
74
73
  });
@@ -1,5 +1,5 @@
1
1
  import {asyncAll} from './util';
2
- import {Actor} from './actor';
2
+ import {Actor, GlyphsProvider, MessageType} from './actor';
3
3
 
4
4
  import type {WorkerPool} from './worker_pool';
5
5
  import type {WorkerSource} from '../source/worker_source'; /* eslint-disable-line */ // this is used for the docs' import
@@ -11,14 +11,9 @@ export class Dispatcher {
11
11
  workerPool: WorkerPool;
12
12
  actors: Array<Actor>;
13
13
  currentActor: number;
14
- id: number;
14
+ id: string | number;
15
15
 
16
- // exposed to allow stubbing in unit tests
17
- static Actor: {
18
- new (...args: any): Actor;
19
- };
20
-
21
- constructor(workerPool: WorkerPool, parent: any, mapId: number) {
16
+ constructor(workerPool: WorkerPool, parent: GlyphsProvider, mapId: string | number) {
22
17
  this.workerPool = workerPool;
23
18
  this.actors = [];
24
19
  this.currentActor = 0;
@@ -26,7 +21,7 @@ export class Dispatcher {
26
21
  const workers = this.workerPool.acquire(mapId);
27
22
  for (let i = 0; i < workers.length; i++) {
28
23
  const worker = workers[i];
29
- const actor = new Dispatcher.Actor(worker, parent, mapId);
24
+ const actor = new Actor(worker, parent, mapId);
30
25
  actor.name = `Worker ${i}`;
31
26
  this.actors.push(actor);
32
27
  }
@@ -36,7 +31,7 @@ export class Dispatcher {
36
31
  /**
37
32
  * Broadcast a message to all Workers.
38
33
  */
39
- broadcast(type: string, data: unknown, cb?: (...args: any[]) => any) {
34
+ broadcast(type: MessageType, data: unknown, cb?: (...args: any[]) => any) {
40
35
  cb = cb || function () {};
41
36
  asyncAll(this.actors, (actor, done) => {
42
37
  actor.send(type, data, done);
@@ -58,5 +53,3 @@ export class Dispatcher {
58
53
  if (mapRemoved) this.workerPool.release(this.id);
59
54
  }
60
55
  }
61
-
62
- Dispatcher.Actor = Actor;
@@ -2,21 +2,6 @@ import {config} from './config';
2
2
 
3
3
  import type {WorkerSource} from '../source/worker_source';
4
4
 
5
- export type MessageListener = (
6
- a: {
7
- data: any;
8
- target: any;
9
- }
10
- ) => unknown;
11
-
12
- // The main thread interface. Provided by Worker in a browser environment,
13
- export interface WorkerInterface {
14
- addEventListener(type: 'message', listener: MessageListener): void;
15
- removeEventListener(type: 'message', listener: MessageListener): void;
16
- postMessage(message: any): void;
17
- terminate(): void;
18
- }
19
-
20
5
  export interface WorkerGlobalScopeInterface {
21
6
  importScripts(...urls: Array<string>): void;
22
7
  registerWorkerSource: (
@@ -172,10 +172,9 @@ export function serialize(input: unknown, transferables?: Array<Transferable> |
172
172
 
173
173
  if (!klass.serialize) {
174
174
  for (const key in input) {
175
- // any cast due to https://github.com/facebook/flow/issues/5393
176
- if (!(input as any).hasOwnProperty(key)) continue; // eslint-disable-line no-prototype-builtins
175
+ if (!input.hasOwnProperty(key)) continue; // eslint-disable-line no-prototype-builtins
177
176
  if (registry[name].omit.indexOf(key) >= 0) continue;
178
- const property = (input as any)[key];
177
+ const property = input[key];
179
178
  properties[key] = registry[name].shallow.indexOf(key) >= 0 ?
180
179
  property :
181
180
  serialize(property, transferables);
@@ -184,7 +183,7 @@ export function serialize(input: unknown, transferables?: Array<Transferable> |
184
183
  properties.message = input.message;
185
184
  }
186
185
  } else {
187
- if (transferables && properties as any === transferables[transferables.length - 1]) {
186
+ if (transferables && properties === transferables[transferables.length - 1]) {
188
187
  throw new Error('statically serialized object won\'t survive transfer of $name property');
189
188
  }
190
189
  }
@@ -226,7 +225,7 @@ export function deserialize(input: Serialized): unknown {
226
225
  }
227
226
 
228
227
  if (typeof input === 'object') {
229
- const name = (input as any).$name || 'Object';
228
+ const name = input.$name || 'Object';
230
229
  if (!registry[name]) {
231
230
  throw new Error(`can't deserialize unregistered class ${name}`);
232
231
  }
@@ -1,7 +1,7 @@
1
1
  import {workerFactory} from './web_worker';
2
- import type {WorkerInterface} from './web_worker';
3
2
  import {browser} from './browser';
4
3
  import {isSafari} from './util';
4
+ import {ActorTarget} from './actor';
5
5
 
6
6
  export const PRELOAD_POOL_ID = 'mapboxgl_preloaded_worker_pool';
7
7
 
@@ -14,13 +14,13 @@ export class WorkerPool {
14
14
  active: {
15
15
  [_ in number | string]: boolean;
16
16
  };
17
- workers: Array<WorkerInterface>;
17
+ workers: Array<ActorTarget>;
18
18
 
19
19
  constructor() {
20
20
  this.active = {};
21
21
  }
22
22
 
23
- acquire(mapId: number | string): Array<WorkerInterface> {
23
+ acquire(mapId: number | string): Array<ActorTarget> {
24
24
  if (!this.workers) {
25
25
  // Lazily look up the value of mapboxgl.workerCount so that
26
26
  // client code has had a chance to set it.