@svar-ui/gantt-data-provider 2.7.2 → 2.8.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.js CHANGED
@@ -201,7 +201,7 @@ var ActionQueue = class {
201
201
  copy = { ...obj };
202
202
  }
203
203
  copy[key] = hasRealID;
204
- } else if (!ignore) {
204
+ } else if (test !== ignore) {
205
205
  return cantSendYet;
206
206
  }
207
207
  }
@@ -316,7 +316,7 @@ var Rest = class extends EventBus {
316
316
  if (data) {
317
317
  req.body = typeof data === "object" ? this.toPayload(data) : data;
318
318
  }
319
- const slash = this._url.charAt(-1) === "/" || url[0] === "/" ? "" : "/";
319
+ const slash = !url || this._url.endsWith("/") || url[0] === "/" ? "" : "/";
320
320
  return fetch(`${this._url}${slash}${url || ""}`, req).then(
321
321
  (res) => res.json()
322
322
  );
@@ -3056,26 +3056,34 @@ function cleanEscapedString(input) {
3056
3056
  return matched[1].replace(doubleQuoteRegExp, "'");
3057
3057
  }
3058
3058
 
3059
+ const TaskID = 1;
3060
+ const LinkID = 2;
3061
+ const AssignmentID = 3;
3062
+
3063
+ const DATE_KEYS = ["start", "end", "base_start", "base_end", "deadline"];
3059
3064
  class RestDataProvider extends Rest {
3060
3065
  changeQueue = [];
3061
3066
  _batchUrl;
3062
3067
  flushTimeout = null;
3068
+ headers = {};
3063
3069
  constructor(url, config) {
3064
3070
  super(url);
3065
3071
  this._batchUrl = config?.batchURL;
3066
3072
  }
3073
+ setHeaders(headers) {
3074
+ this.headers = headers;
3075
+ }
3067
3076
  getHandlers() {
3068
3077
  return {
3069
3078
  "add-task": {
3070
3079
  ignoreID: true,
3071
- handler: async (ev) => {
3072
- ev.task.parent = this.correctID(ev.task.parent);
3073
- return this.sendBatch(`tasks`, "POST", {
3074
- task: ev.task,
3075
- mode: ev.mode,
3076
- target: this.correctID(ev.target)
3077
- });
3078
- }
3080
+ kind: TaskID,
3081
+ handler: async (ev) => this.sendBatch(`tasks`, "POST", {
3082
+ task: ev.task,
3083
+ target: ev.target,
3084
+ mode: ev.mode,
3085
+ index: ev.index
3086
+ })
3079
3087
  },
3080
3088
  "update-task": {
3081
3089
  debounce: 500,
@@ -3090,15 +3098,15 @@ class RestDataProvider extends Rest {
3090
3098
  },
3091
3099
  "copy-task": {
3092
3100
  ignoreID: true,
3093
- handler: async (ev) => {
3094
- return this.sendBatch(`tasks/${ev.source}`, "PUT", {
3095
- operation: "copy",
3096
- id: ev.id,
3097
- target: this.correctID(ev.target),
3098
- mode: ev.mode,
3099
- lazy: !!ev.lazy
3100
- });
3101
- }
3101
+ kind: TaskID,
3102
+ handler: async (ev) => this.sendBatch(`tasks/${ev.source}`, "PUT", {
3103
+ operation: "copy",
3104
+ id: ev.id,
3105
+ target: ev.target,
3106
+ mode: ev.mode,
3107
+ index: ev.index,
3108
+ lazy: !!ev.lazy
3109
+ })
3102
3110
  },
3103
3111
  "move-task": {
3104
3112
  handler: async (ev) => {
@@ -3113,11 +3121,8 @@ class RestDataProvider extends Rest {
3113
3121
  },
3114
3122
  "add-link": {
3115
3123
  ignoreID: true,
3116
- handler: async (ev) => {
3117
- ev.link.source = this.correctID(ev.link.source);
3118
- ev.link.target = this.correctID(ev.link.target);
3119
- return this.sendBatch(`links`, "POST", ev.link);
3120
- }
3124
+ kind: LinkID,
3125
+ handler: async (ev) => this.sendBatch(`links`, "POST", ev.link)
3121
3126
  },
3122
3127
  "update-link": {
3123
3128
  handler: async (ev) => this.sendBatch(`links/${ev.id}`, "PUT", ev.link)
@@ -3127,26 +3132,22 @@ class RestDataProvider extends Rest {
3127
3132
  },
3128
3133
  "add-assignment": {
3129
3134
  ignoreID: true,
3130
- handler: async (ev) => {
3131
- ev.assignment.task = this.correctID(ev.assignment.task);
3132
- return this.sendBatch(`assignments`, "POST", ev.assignment);
3133
- }
3135
+ kind: AssignmentID,
3136
+ handler: async (ev) => this.sendBatch(
3137
+ `assignments`,
3138
+ "POST",
3139
+ ev.assignment
3140
+ )
3134
3141
  },
3135
3142
  "delete-assignment": {
3136
- handler: async (ev) => {
3137
- this.sendBatch(`assignments/${ev.id}`, "DELETE");
3138
- }
3143
+ handler: async (ev) => this.sendBatch(`assignments/${ev.id}`, "DELETE")
3139
3144
  },
3140
3145
  "update-assignment": {
3141
- handler: async (ev) => {
3142
- const { task } = ev.assignment;
3143
- if (task) ev.assignment.task = this.correctID(task);
3144
- this.sendBatch(
3145
- `assignments/${ev.id}`,
3146
- "PUT",
3147
- ev.assignment
3148
- );
3149
- }
3146
+ handler: async (ev) => this.sendBatch(
3147
+ `assignments/${ev.id}`,
3148
+ "PUT",
3149
+ ev.assignment
3150
+ )
3150
3151
  }
3151
3152
  };
3152
3153
  }
@@ -3163,7 +3164,7 @@ class RestDataProvider extends Rest {
3163
3164
  ) : void 0
3164
3165
  ]);
3165
3166
  const tasks = this.parseDates(tasksResponse).map((t) => {
3166
- if (t.parent == id) t.parent = tempId;
3167
+ if (t.parent === id) t.parent = tempId;
3167
3168
  return t;
3168
3169
  });
3169
3170
  const result = { tasks, links };
@@ -3173,10 +3174,16 @@ class RestDataProvider extends Rest {
3173
3174
  }
3174
3175
  parseDates(data) {
3175
3176
  data.forEach((item) => {
3176
- item.start = new Date(item.start);
3177
- if (item.end) item.end = new Date(item.end);
3178
- if (item.base_start) item.base_start = new Date(item.base_start);
3179
- if (item.base_end) item.base_end = new Date(item.base_end);
3177
+ DATE_KEYS.forEach((key) => {
3178
+ if (item[key]) item[key] = new Date(item[key]);
3179
+ });
3180
+ if (item.constraint?.date)
3181
+ item.constraint.date = new Date(item.constraint.date);
3182
+ item.segments?.forEach((segment) => {
3183
+ DATE_KEYS.forEach((key) => {
3184
+ if (segment[key]) segment[key] = new Date(segment[key]);
3185
+ });
3186
+ });
3180
3187
  });
3181
3188
  return data;
3182
3189
  }
@@ -3190,46 +3197,72 @@ class RestDataProvider extends Rest {
3190
3197
  return this.send(url, method, data, customHeaders);
3191
3198
  }
3192
3199
  }
3200
+ resolveId(id, kind) {
3201
+ return this.getQueue().resolve(id, kind);
3202
+ }
3193
3203
  async sendBatchRequest(url, method, data, customHeaders) {
3194
- this.changeQueue.push({ url, method, data });
3195
- if (this.flushTimeout) {
3196
- clearTimeout(this.flushTimeout);
3197
- }
3198
- return new Promise((resolve) => {
3199
- this.flushTimeout = setTimeout(async () => {
3200
- if (this.changeQueue.length > 1) {
3201
- const batchData = this.changeQueue.map((req) => {
3202
- return {
3203
- url: req.url,
3204
- method: req.method,
3205
- data: {
3206
- ...req.data
3207
- }
3208
- };
3209
- });
3210
- this.changeQueue = [];
3211
- const result = await this.send(
3212
- this._batchUrl,
3213
- "POST",
3214
- batchData
3215
- );
3216
- resolve(result);
3217
- } else {
3218
- this.changeQueue = [];
3219
- const result = await this.send(
3220
- url,
3221
- method,
3222
- data,
3223
- customHeaders
3224
- );
3225
- resolve(result);
3226
- }
3227
- }, 10);
3204
+ const request = new Promise((resolve, reject) => {
3205
+ this.changeQueue.push({
3206
+ url,
3207
+ method,
3208
+ data,
3209
+ customHeaders,
3210
+ resolve,
3211
+ reject
3212
+ });
3228
3213
  });
3214
+ if (this.flushTimeout) clearTimeout(this.flushTimeout);
3215
+ this.flushTimeout = setTimeout(() => this.flushBatch(), 10);
3216
+ return request;
3217
+ }
3218
+ async flushBatch() {
3219
+ const queue = this.changeQueue;
3220
+ this.changeQueue = [];
3221
+ this.flushTimeout = null;
3222
+ if (!queue.length) return;
3223
+ if (queue.length === 1) {
3224
+ const [req] = queue;
3225
+ try {
3226
+ req.resolve(
3227
+ await this.send(
3228
+ req.url,
3229
+ req.method,
3230
+ req.data,
3231
+ req.customHeaders
3232
+ )
3233
+ );
3234
+ } catch (error) {
3235
+ req.reject(error);
3236
+ }
3237
+ return;
3238
+ }
3239
+ const batchData = queue.map((req) => ({
3240
+ url: req.url,
3241
+ method: req.method,
3242
+ data: req.data ? { ...req.data } : void 0,
3243
+ customHeaders: req.customHeaders
3244
+ }));
3245
+ try {
3246
+ const result = await this.send(
3247
+ this._batchUrl,
3248
+ "POST",
3249
+ batchData
3250
+ );
3251
+ if (!Array.isArray(result))
3252
+ throw new Error("batch endpoint did not return a list");
3253
+ queue.forEach((req, i) => {
3254
+ const response = result[i];
3255
+ if (response?.error) req.reject(new Error(response.error));
3256
+ else req.resolve(response);
3257
+ });
3258
+ } catch (error) {
3259
+ queue.forEach((req) => req.reject(error));
3260
+ }
3229
3261
  }
3230
3262
  async send(url, method, data, customHeaders = {}) {
3231
3263
  const headers = {
3232
3264
  "Content-Type": "application/json",
3265
+ ...this.headers,
3233
3266
  ...customHeaders
3234
3267
  };
3235
3268
  const req = {
@@ -3239,39 +3272,408 @@ class RestDataProvider extends Rest {
3239
3272
  if (data) {
3240
3273
  req.body = this.getPayload(data);
3241
3274
  }
3242
- return fetch(`${this._url}/${url}`, req).then((res) => {
3243
- return res.json().catch((error) => {
3244
- console.error(error);
3245
- });
3275
+ return fetch(`${this._url}/${url}`, req).then(async (res) => {
3276
+ if (!res.ok) {
3277
+ const text = await res.text().catch(() => "");
3278
+ throw new Error(
3279
+ `request failed with status ${res.status}${text ? `: ${text}` : ""}`
3280
+ );
3281
+ }
3282
+ return res.json();
3246
3283
  });
3247
3284
  }
3248
3285
  getPayload(obj) {
3249
- const transformedObj = this.transformDates(obj);
3250
- return JSON.stringify(transformedObj);
3286
+ return JSON.stringify(this.transformDates(obj));
3251
3287
  }
3252
3288
  transformDates = (item) => {
3253
- if (Array.isArray(item)) {
3254
- return item.map(this.transformDates);
3255
- }
3289
+ if (item instanceof Date) return this.formatDate(item);
3290
+ if (Array.isArray(item)) return item.map(this.transformDates);
3256
3291
  if (typeof item === "object" && item !== null) {
3257
- for (const key in item) {
3258
- const value = item[key];
3259
- if (value instanceof Date) {
3260
- item[key] = this.formatDate(value);
3261
- } else if (typeof value === "object") {
3262
- item[key] = this.transformDates(value);
3263
- }
3264
- }
3292
+ const copy = {};
3293
+ for (const key in item) copy[key] = this.transformDates(item[key]);
3294
+ return copy;
3265
3295
  }
3266
3296
  return item;
3267
3297
  };
3268
- correctID(localId) {
3269
- const tempId = localId;
3270
- const id = this.getQueue().getId(tempId);
3271
- if (localId !== id) return id;
3272
- return localId;
3298
+ }
3299
+
3300
+ const RETRY_MIN = 1e3;
3301
+ const RETRY_MAX = 3e4;
3302
+ class EventStream {
3303
+ config;
3304
+ eventSource = null;
3305
+ connectedSignal = null;
3306
+ // Resolves when eventSource opens
3307
+ wasOpen = false;
3308
+ hasMissedEvents = false;
3309
+ isOnline = false;
3310
+ rejectConnect = null;
3311
+ retryTimer = null;
3312
+ retryDelayMs = RETRY_MIN;
3313
+ isStopped = false;
3314
+ constructor(config) {
3315
+ this.config = config;
3316
+ }
3317
+ connect() {
3318
+ this.isStopped = false;
3319
+ if (this.connectedSignal) return this.connectedSignal;
3320
+ this.connectedSignal = new Promise((resolve, reject) => {
3321
+ this.rejectConnect = reject;
3322
+ this.eventSource = new EventSource(this.getURL(), {
3323
+ withCredentials: this.config.withCredentials
3324
+ });
3325
+ const source = this.eventSource;
3326
+ source.onopen = () => {
3327
+ if (this.eventSource !== source) return;
3328
+ const resync = this.wasOpen || this.hasMissedEvents;
3329
+ this.wasOpen = true;
3330
+ this.hasMissedEvents = false;
3331
+ this.retryDelayMs = RETRY_MIN;
3332
+ this.rejectConnect = null;
3333
+ resolve();
3334
+ this.notify(true);
3335
+ if (resync) this.config.onResync?.();
3336
+ };
3337
+ source.onerror = () => {
3338
+ if (this.eventSource !== source) return;
3339
+ this.notify(false);
3340
+ if (source.readyState !== EventSource.CLOSED) return;
3341
+ this.close();
3342
+ this.hasMissedEvents = true;
3343
+ this.rejectConnect?.(
3344
+ new Error("could not open the event stream")
3345
+ );
3346
+ this.rejectConnect = null;
3347
+ this.scheduleRetry();
3348
+ };
3349
+ source.onmessage = (message) => {
3350
+ if (this.eventSource !== source) return;
3351
+ this.config.onMessage?.(message.data);
3352
+ };
3353
+ });
3354
+ return this.connectedSignal;
3355
+ }
3356
+ disconnect() {
3357
+ this.isStopped = true;
3358
+ this.clearRetry();
3359
+ this.close();
3360
+ this.rejectConnect?.(
3361
+ new Error("disconnected before the stream was open")
3362
+ );
3363
+ this.rejectConnect = null;
3364
+ this.notify(false);
3365
+ }
3366
+ scheduleRetry() {
3367
+ if (this.isStopped || this.retryTimer) return;
3368
+ const delayMs = this.retryDelayMs * (0.5 + Math.random() / 2);
3369
+ this.retryDelayMs = Math.min(this.retryDelayMs * 2, RETRY_MAX);
3370
+ this.retryTimer = setTimeout(() => {
3371
+ this.retryTimer = null;
3372
+ if (this.isStopped) return;
3373
+ this.connect().catch(() => {
3374
+ });
3375
+ }, delayMs);
3376
+ }
3377
+ clearRetry() {
3378
+ if (this.retryTimer) clearTimeout(this.retryTimer);
3379
+ this.retryTimer = null;
3380
+ this.retryDelayMs = RETRY_MIN;
3381
+ }
3382
+ close() {
3383
+ this.eventSource?.close();
3384
+ this.eventSource = null;
3385
+ this.connectedSignal = null;
3386
+ }
3387
+ notify(isOnline) {
3388
+ if (this.isOnline === isOnline) return;
3389
+ this.isOnline = isOnline;
3390
+ try {
3391
+ this.config.onConnectionChange?.(isOnline);
3392
+ } catch (error) {
3393
+ console.error(error);
3394
+ }
3395
+ }
3396
+ getURL() {
3397
+ const { url, params } = this.config;
3398
+ const query = new URLSearchParams(
3399
+ typeof params === "function" ? params() : params
3400
+ ).toString();
3401
+ if (!query) return url;
3402
+ return url + (url.includes("?") ? "&" : "?") + query;
3403
+ }
3404
+ }
3405
+
3406
+ function generateUUID() {
3407
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
3408
+ return crypto.randomUUID();
3409
+ } else {
3410
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (char) => {
3411
+ const num = Math.random() * 16 | 0;
3412
+ const value = char === "x" ? num : num & 3 | 8;
3413
+ return value.toString(16);
3414
+ });
3415
+ }
3416
+ }
3417
+
3418
+ const PARAMS = {
3419
+ eventSource: "remote"
3420
+ };
3421
+ class RemoteEvents {
3422
+ // the host sends it as X-Client-Id, so the server skips this client's own echo
3423
+ clientId = generateUUID();
3424
+ provider;
3425
+ api;
3426
+ eventStream;
3427
+ constructor(provider, config) {
3428
+ this.provider = provider;
3429
+ const { params } = config;
3430
+ this.eventStream = new EventStream({
3431
+ ...config,
3432
+ onMessage: config.onMessage ?? ((msg) => this.processMessage(msg)),
3433
+ params: () => ({
3434
+ ...typeof params === "function" ? params() : params,
3435
+ clientId: this.clientId
3436
+ })
3437
+ });
3438
+ }
3439
+ connect(api) {
3440
+ this.api = api;
3441
+ return this.eventStream.connect();
3442
+ }
3443
+ disconnect() {
3444
+ this.eventStream.disconnect();
3445
+ }
3446
+ processMessage(message) {
3447
+ const event = this.parseMessage(message);
3448
+ if (!event || !this.api) return;
3449
+ try {
3450
+ this.applyEvent(event);
3451
+ } catch (error) {
3452
+ console.error("failed to apply a remote event", error);
3453
+ }
3454
+ }
3455
+ applyEvent(event) {
3456
+ switch (event.type) {
3457
+ case "task":
3458
+ return this.applyEventTask(event);
3459
+ case "link":
3460
+ return this.applyEventLink(event);
3461
+ case "assignment":
3462
+ return this.applyEventAssignment(event);
3463
+ }
3464
+ }
3465
+ // ---------- Task events ----------
3466
+ applyEventTask(event) {
3467
+ const id = this.resolveRemoteId(event.id, TaskID);
3468
+ let task;
3469
+ if (event.data) {
3470
+ const data = [event.data];
3471
+ task = (this.provider.parseDates?.(data) ?? data)[0];
3472
+ }
3473
+ const operation = task?.operation;
3474
+ if (task) {
3475
+ task.id = id;
3476
+ task.parent = this.resolveRemoteId(task.parent, TaskID);
3477
+ if ("target" in task)
3478
+ task.target = this.resolveRemoteId(task.target, TaskID);
3479
+ delete task.open;
3480
+ delete task.operation;
3481
+ }
3482
+ switch (event.action) {
3483
+ case "add":
3484
+ if (this.isTaskLoaded(id)) return;
3485
+ return this.applyEventTaskAdd(id, task);
3486
+ case "update":
3487
+ return this.applyEventTaskUpdate(id, task, operation);
3488
+ case "delete":
3489
+ return this.applyEventTaskDelete(id);
3490
+ }
3491
+ }
3492
+ applyEventTaskAdd(id, task) {
3493
+ if (!task) return;
3494
+ if (!this.canAcceptChild(task?.parent)) return;
3495
+ const hasTarget = this.isTaskLoaded(task?.target);
3496
+ const addTask = { ...task };
3497
+ delete addTask.index;
3498
+ delete addTask.target;
3499
+ delete addTask.mode;
3500
+ this.api.exec("add-task", {
3501
+ id,
3502
+ task: addTask,
3503
+ target: hasTarget ? task?.target : void 0,
3504
+ mode: hasTarget ? task?.mode : void 0,
3505
+ index: task?.index,
3506
+ ...PARAMS
3507
+ });
3508
+ }
3509
+ applyEventTaskUpdate(id, task, operation) {
3510
+ if (!task) return;
3511
+ if (!this.isTaskLoaded(id)) {
3512
+ return this.applyEventTaskAdd(id, task);
3513
+ }
3514
+ delete task.lazy;
3515
+ if (!operation) {
3516
+ this.api.exec("update-task", {
3517
+ id,
3518
+ task,
3519
+ ...PARAMS
3520
+ });
3521
+ } else if (operation === "move") {
3522
+ if (!this.canAcceptChild(task?.parent)) {
3523
+ return this.applyEventTaskDelete(id);
3524
+ }
3525
+ const hasTarget = this.isTaskLoaded(task?.target);
3526
+ this.api.exec("move-task", {
3527
+ id,
3528
+ target: hasTarget ? task?.target : task?.parent,
3529
+ mode: hasTarget ? task?.mode : "child",
3530
+ ...PARAMS
3531
+ });
3532
+ }
3533
+ }
3534
+ applyEventTaskDelete(id) {
3535
+ if (!this.isTaskLoaded(id)) return;
3536
+ this.api.exec("delete-task", {
3537
+ id,
3538
+ ...PARAMS
3539
+ });
3540
+ }
3541
+ // ---------- Link events ----------
3542
+ applyEventLink(event) {
3543
+ const id = this.resolveRemoteId(event.id, LinkID);
3544
+ const link = event.data ? event.data : void 0;
3545
+ if (link) {
3546
+ link.id = id;
3547
+ link.source = this.resolveRemoteId(link.source, TaskID);
3548
+ link.target = this.resolveRemoteId(link.target, TaskID);
3549
+ }
3550
+ switch (event.action) {
3551
+ case "add":
3552
+ return this.applyEventLinkAdd(id, link);
3553
+ case "update":
3554
+ return this.applyEventLinkUpdate(id, link);
3555
+ case "delete":
3556
+ return this.applyEventLinkDelete(id);
3557
+ }
3558
+ }
3559
+ applyEventLinkAdd(id, link) {
3560
+ if (!link) return;
3561
+ if (this.isLinkLoaded(id)) return;
3562
+ if (!this.isTaskLoaded(link.source) || !this.isTaskLoaded(link.target))
3563
+ return;
3564
+ this.api.exec("add-link", {
3565
+ id,
3566
+ link,
3567
+ ...PARAMS
3568
+ });
3569
+ }
3570
+ applyEventLinkUpdate(id, link) {
3571
+ if (!link) return;
3572
+ if (!this.isLinkLoaded(id)) return;
3573
+ this.api.exec("update-link", {
3574
+ id,
3575
+ link,
3576
+ ...PARAMS
3577
+ });
3578
+ }
3579
+ applyEventLinkDelete(id) {
3580
+ if (!this.isLinkLoaded(id)) return;
3581
+ this.api.exec("delete-link", {
3582
+ id,
3583
+ ...PARAMS
3584
+ });
3585
+ }
3586
+ // ---------- Assignment events ----------
3587
+ applyEventAssignment(event) {
3588
+ const id = this.resolveRemoteId(event.id, AssignmentID);
3589
+ const assignment = event.data ? event.data : void 0;
3590
+ if (assignment) {
3591
+ assignment.id = id;
3592
+ assignment.task = this.resolveRemoteId(assignment.task, TaskID);
3593
+ }
3594
+ switch (event.action) {
3595
+ case "add":
3596
+ return this.applyEventAssignmentAdd(id, assignment);
3597
+ case "update":
3598
+ return this.applyEventAssignmentUpdate(id, assignment);
3599
+ case "delete":
3600
+ return this.applyEventAssignmentDelete(id);
3601
+ }
3602
+ }
3603
+ applyEventAssignmentAdd(id, assignment) {
3604
+ if (!assignment) return;
3605
+ if (this.isAssignmentLoaded(id)) return;
3606
+ if (!this.isTaskLoaded(assignment.task)) return;
3607
+ this.api.exec("add-assignment", {
3608
+ id,
3609
+ assignment,
3610
+ ...PARAMS
3611
+ });
3612
+ }
3613
+ applyEventAssignmentUpdate(id, assignment) {
3614
+ if (!assignment) return;
3615
+ if (!this.isAssignmentLoaded(id)) return;
3616
+ this.api.exec("update-assignment", {
3617
+ id,
3618
+ assignment,
3619
+ ...PARAMS
3620
+ });
3621
+ }
3622
+ applyEventAssignmentDelete(id) {
3623
+ if (!this.isAssignmentLoaded(id)) return;
3624
+ this.api.exec("delete-assignment", {
3625
+ id,
3626
+ ...PARAMS
3627
+ });
3628
+ }
3629
+ // ---------- Helpers ----------
3630
+ resolveRemoteId(id, kind) {
3631
+ if (id === null || typeof id === "undefined") return id;
3632
+ const localId = this.provider.resolveId(id, kind) ?? id;
3633
+ if (localId === id) return id;
3634
+ return this.doesItemExist(localId, kind) ? localId : id;
3635
+ }
3636
+ doesItemExist(id, kind) {
3637
+ switch (kind) {
3638
+ case TaskID:
3639
+ return !!this.api.getTask(id);
3640
+ case LinkID:
3641
+ return this.isLinkLoaded(id);
3642
+ case AssignmentID:
3643
+ return this.isAssignmentLoaded(id);
3644
+ default:
3645
+ return true;
3646
+ }
3647
+ }
3648
+ isTaskLoaded(id) {
3649
+ return !id || !!this.api.getTask(id);
3650
+ }
3651
+ canAcceptChild(id) {
3652
+ if (!id) return true;
3653
+ const parent = this.api.getTask(id);
3654
+ return !!parent && !parent.lazy;
3655
+ }
3656
+ isLinkLoaded(id) {
3657
+ return !!this.api.getState().links.byId(id);
3658
+ }
3659
+ isAssignmentLoaded(id) {
3660
+ return !!this.api.getState().assignments?.byId(id);
3661
+ }
3662
+ // ---------- Parsing ----------
3663
+ parseMessage(message) {
3664
+ const clientId = this.clientId;
3665
+ let data;
3666
+ try {
3667
+ data = JSON.parse(message);
3668
+ } catch {
3669
+ return null;
3670
+ }
3671
+ if (data === null || typeof data !== "object" || !("from" in data) || data.from === clientId || !("type" in data) || typeof data.type !== "string" || !("action" in data) || typeof data.action !== "string" || !("id" in data)) {
3672
+ return null;
3673
+ }
3674
+ return data;
3273
3675
  }
3274
3676
  }
3275
3677
 
3276
- export { RestDataProvider };
3678
+ export { AssignmentID, EventStream, LinkID, RemoteEvents, RestDataProvider, TaskID };
3277
3679
  //# sourceMappingURL=index.js.map