@casual-simulation/aux-common 2.0.16 → 2.0.21

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.
Files changed (49) hide show
  1. package/aux-format-2/AuxStateHelpers.d.ts +11 -1
  2. package/aux-format-2/AuxStateHelpers.js +20 -5
  3. package/aux-format-2/AuxStateHelpers.js.map +1 -1
  4. package/aux-format-2/AuxWeaveHelpers.d.ts +0 -1
  5. package/aux-format-2/AuxWeaveHelpers.js +1 -15
  6. package/aux-format-2/AuxWeaveHelpers.js.map +1 -1
  7. package/aux-format-2/AuxWeaveReducer.js +6 -6
  8. package/aux-format-2/AuxWeaveReducer.js.map +1 -1
  9. package/bots/Bot.d.ts +46 -0
  10. package/bots/Bot.js +6 -0
  11. package/bots/Bot.js.map +1 -1
  12. package/bots/BotCalculations.d.ts +29 -1
  13. package/bots/BotCalculations.js +94 -14
  14. package/bots/BotCalculations.js.map +1 -1
  15. package/bots/BotEvents.d.ts +30 -1
  16. package/bots/BotEvents.js +8 -0
  17. package/bots/BotEvents.js.map +1 -1
  18. package/bots/test/BotCalculationContextTests.js +11 -4
  19. package/bots/test/BotCalculationContextTests.js.map +1 -1
  20. package/package.json +5 -3
  21. package/partitions/MemoryPartition.js +23 -6
  22. package/partitions/MemoryPartition.js.map +1 -1
  23. package/partitions/RemoteYjsPartition.js +2 -2
  24. package/partitions/RemoteYjsPartition.js.map +1 -1
  25. package/partitions/YjsPartition.js +2 -2
  26. package/partitions/YjsPartition.js.map +1 -1
  27. package/partitions/test/PartitionTests.js +93 -19
  28. package/partitions/test/PartitionTests.js.map +1 -1
  29. package/runtime/AuxGlobalContext.d.ts +2 -2
  30. package/runtime/AuxGlobalContext.js.map +1 -1
  31. package/runtime/AuxLibrary.d.ts +29 -2
  32. package/runtime/AuxLibrary.js +391 -17
  33. package/runtime/AuxLibrary.js.map +1 -1
  34. package/runtime/AuxLibraryDefinitions.def +243 -8
  35. package/runtime/AuxRuntime.d.ts +3 -0
  36. package/runtime/AuxRuntime.js +31 -10
  37. package/runtime/AuxRuntime.js.map +1 -1
  38. package/runtime/RuntimeBot.d.ts +6 -0
  39. package/runtime/RuntimeBot.js +123 -6
  40. package/runtime/RuntimeBot.js.map +1 -1
  41. package/runtime/Transpiler.js +3 -3
  42. package/runtime/Transpiler.js.map +1 -1
  43. package/runtime/Utils.d.ts +6 -0
  44. package/runtime/Utils.js +30 -0
  45. package/runtime/Utils.js.map +1 -1
  46. package/runtime/test/TestScriptBotFactory.js +6 -3
  47. package/runtime/test/TestScriptBotFactory.js.map +1 -1
  48. package/test/YjsTestHelpers.js.map +1 -1
  49. package/yjs/YjsHelpers.d.ts +1 -1
@@ -11,8 +11,8 @@ import { DEBUG_STRING, debugStringifyFunction, } from './AuxGlobalContext';
11
11
  import { hasValue, trimTag, isBot, BOT_SPACE_TAG, toast as toastMessage, showJoinCode as calcShowJoinCode, requestFullscreen, exitFullscreen, html as htmlMessage, hideHtml as hideHtmlMessage, setClipboard as calcSetClipboard, tweenTo as calcTweenTo, showChat as calcShowChat, hideChat as calcHideChat, runScript, enableAR as calcEnableAR, disableAR as calcDisableAR, enableVR as calcEnableVR, disableVR as calcDisableVR, showUploadAuxFile as calcShowUploadAuxFile, openQRCodeScanner as calcOpenQRCodeScanner, showQRCode as calcShowQRCode, openBarcodeScanner as calcOpenBarcodeScanner, showBarcode as calcShowBarcode, importAUX as calcImportAUX, showInputForTag as calcShowInputForTag, showInput as calcShowInput, replaceDragBot as calcReplaceDragBot, goToDimension as calcGoToDimension, goToURL as calcGoToURL, openURL as calcOpenURL, checkout as calcCheckout, playSound as calcPlaySound, bufferSound as calcBufferSound, cancelSound as calcCancelSound, setupServer as calcSetupServer, shell as calcShell, backupToGithub as calcBackupToGithub, backupAsDownload as calcBackupAsDownload, finishCheckout as calcFinishCheckout, markHistory as calcMarkHistory, browseHistory as calcBrowseHistory, restoreHistoryMark as calcRestoreHistoryMark, loadFile as calcLoadFile, saveFile as calcSaveFile, reject as calcReject, localFormAnimation as calcLocalFormAnimation, webhook as calcWebhook, superShout as calcSuperShout, share as calcShare, registerPrefix as calcRegisterPrefix, createCertificate as calcCreateCertificate, signTag as calcSignTag, revokeCertificate as calcRevokeCertificate, localPositionTween as calcLocalPositionTween, localRotationTween as calcLocalRotationTween, showUploadFiles as calcShowUploadFiles, download, loadSimulation, unloadSimulation, getUploadState, addState, getPortalTag, KNOWN_PORTALS, openConsole, tagsOnBot, getOriginalObject, getBotSpace, trimEvent, CREATE_ACTION_NAME, CREATE_ANY_ACTION_NAME, DESTROY_ACTION_NAME, ORIGINAL_OBJECT, unlockSpace, getRemoteCount, getServers, getRemotes, action, getServerStatuses, setSpacePassword, exportGpioPin, unexportGpioPin, setGpioPin, getGpioPin, rpioInitPin, rpioExitPin, rpioOpenPin, rpioModePin, rpioReadPin, rpioReadSequencePin, rpioWritePin, rpioWriteSequencePin, rpioReadpadPin, rpioWritepadPin, rpioPudPin, rpioPollPin, rpioClosePin, rpioI2CBeginPin, rpioI2CSetSlaveAddressPin, rpioI2CSetBaudRatePin, rpioI2CSetClockDividerPin, rpioI2CReadPin, rpioI2CWritePin,
12
12
  // rpioI2CReadRegisterRestartPin,
13
13
  // rpioI2CWriteReadRestartPin,
14
- rpioI2CEndPin, rpioPWMSetClockDividerPin, rpioPWMSetRangePin, rpioPWMSetDataPin, rpioSPIBeginPin, rpioSPIChipSelectPin, rpioSPISetCSPolarityPin, rpioSPISetClockDividerPin, rpioSPISetDataModePin, rpioSPITransferPin, rpioSPIWritePin, rpioSPIEndPin, serialConnectPin, serialOpenPin, serialStreamPin, serialUpdatePin, serialWritePin, serialReadPin, serialClosePin, serialFlushPin, serialDrainPin, serialPausePin, serialResumePin, calculateAnchorPoint, calculateAnchorPointOffset, getBotPosition as calcGetBotPosition, isRuntimeBot, SET_TAG_MASK_SYMBOL, CLEAR_TAG_MASKS_SYMBOL, getBotScale, EDIT_TAG_SYMBOL, EDIT_TAG_MASK_SYMBOL, circleWipe, addDropSnap as calcAddDropSnap, animateToPosition, beginAudioRecording as calcBeginAudioRecording, endAudioRecording as calcEndAudioRecording, beginRecording as calcBeginRecording, endRecording as calcEndRecording, speakText as calcSpeakText, getVoices as calcGetVoices, getGeolocation as calcGetGeolocation, cancelAnimation, disablePOV, enablePOV, enableCustomDragging as calcEnableCustomDragging, MINI_PORTAL, registerCustomApp, setAppOutput, unregisterCustomApp, requestAuthData as calcRequestAuthData, createBot, defineGlobalBot as calcDefineGlobalBot, TEMPORARY_BOT_PARTITION_ID, publishRecord as calcPublishRecord, DEFAULT_RECORD_SPACE, getRecords as calcGetRecords, requestPermanentAuthToken as calcRequestPermanentAuthToken, deleteRecord, } from '../bots';
15
- import { sortBy, every } from 'lodash';
14
+ rpioI2CEndPin, rpioPWMSetClockDividerPin, rpioPWMSetRangePin, rpioPWMSetDataPin, rpioSPIBeginPin, rpioSPIChipSelectPin, rpioSPISetCSPolarityPin, rpioSPISetClockDividerPin, rpioSPISetDataModePin, rpioSPITransferPin, rpioSPIWritePin, rpioSPIEndPin, serialConnectPin, serialOpenPin, serialStreamPin, serialUpdatePin, serialWritePin, serialReadPin, serialClosePin, serialFlushPin, serialDrainPin, serialPausePin, serialResumePin, calculateAnchorPoint, calculateAnchorPointOffset, getBotPosition as calcGetBotPosition, isRuntimeBot, SET_TAG_MASK_SYMBOL, CLEAR_TAG_MASKS_SYMBOL, getBotScale, EDIT_TAG_SYMBOL, EDIT_TAG_MASK_SYMBOL, circleWipe, addDropSnap as calcAddDropSnap, animateToPosition, beginAudioRecording as calcBeginAudioRecording, endAudioRecording as calcEndAudioRecording, beginRecording as calcBeginRecording, endRecording as calcEndRecording, speakText as calcSpeakText, getVoices as calcGetVoices, getGeolocation as calcGetGeolocation, cancelAnimation, disablePOV, enablePOV, enableCustomDragging as calcEnableCustomDragging, MINI_PORTAL, registerCustomApp, setAppOutput, unregisterCustomApp, requestAuthData as calcRequestAuthData, createBot, defineGlobalBot as calcDefineGlobalBot, TEMPORARY_BOT_PARTITION_ID, publishRecord as calcPublishRecord, DEFAULT_RECORD_SPACE, getRecords as calcGetRecords, requestPermanentAuthToken as calcRequestPermanentAuthToken, deleteRecord, convertToString, GET_TAG_MASKS_SYMBOL, isBotLink, parseBotLink, createBotLink, convertGeolocationToWhat3Words as calcConvertGeolocationToWhat3Words, } from '../bots';
15
+ import { sortBy, every, cloneDeep, union, isEqual, flatMap } from 'lodash';
16
16
  import { remote as calcRemote, } from '@casual-simulation/causal-trees';
17
17
  import { RanOutOfEnergyError } from './AuxResults';
18
18
  import '../polyfill/Array.first.polyfill';
@@ -22,7 +22,7 @@ import { sha256 as hashSha256, sha512 as hashSha512, hmac } from 'hash.js';
22
22
  import stableStringify from '@casual-simulation/fast-json-stable-stringify';
23
23
  import { encrypt as realEncrypt, decrypt as realDecrypt, keypair as realKeypair, sign as realSign, verify as realVerify, asymmetricKeypair as realAsymmetricKeypair, asymmetricEncrypt as realAsymmetricEncrypt, asymmetricDecrypt as realAsymmetricDecrypt, isAsymmetricKeypair, isAsymmetricEncrypted, isEncrypted, } from '@casual-simulation/crypto';
24
24
  import { tagValueHash } from '../aux-format-2/AuxOpTypes';
25
- import { convertToString, del, insert, isTagEdit, preserve, } from '../aux-format-2';
25
+ import { apply, del, insert, isTagEdit, preserve } from '../aux-format-2';
26
26
  import { Euler, Vector3, Plane, Ray, } from '@casual-simulation/three';
27
27
  import mime from 'mime';
28
28
  import TWEEN from '@tweenjs/tween.js';
@@ -31,12 +31,94 @@ import './BlobPolyfill';
31
31
  import { Fragment, h } from 'preact';
32
32
  import htm from 'htm';
33
33
  import { fromByteArray, toByteArray } from 'base64-js';
34
+ import expect, { iterableEquality } from '@casual-simulation/expect';
34
35
  const _html = htm.bind(h);
35
36
  const html = ((...args) => {
36
37
  return _html(...args);
37
38
  });
38
39
  html.h = h;
39
40
  html.f = Fragment;
41
+ /**
42
+ * The status codes that should be used to retry web requests.
43
+ */
44
+ const DEFUALT_RETRY_STATUS_CODES = [
45
+ 408,
46
+ 429,
47
+ 500,
48
+ 502,
49
+ 503,
50
+ 504,
51
+ 0, // Network Failure / CORS
52
+ ];
53
+ /**
54
+ * The time to wait until another web request retry unless specified by the webhook options.
55
+ * Defaults to 3 seconds.
56
+ */
57
+ const DEFAULT_RETRY_AFTER_MS = 3 * 1000;
58
+ /**
59
+ * The maximum amount of time to wait before giving up on a set of requests.
60
+ * Defaults to 1 minute.
61
+ */
62
+ const MAX_RETRY_AFTER_MS = 60 * 60 * 1000;
63
+ /**
64
+ * The maximum number of times that a web request should be retried for.
65
+ */
66
+ const MAX_RETRY_COUNT = 10;
67
+ const botsEquality = function (first, second) {
68
+ if (isRuntimeBot(first) && isRuntimeBot(second)) {
69
+ expect(getBotSnapshot(first)).toEqual(getBotSnapshot(second));
70
+ return true;
71
+ }
72
+ return undefined;
73
+ };
74
+ expect.extend({
75
+ toEqual(received, expected) {
76
+ // Copied from https://github.com/facebook/jest/blob/7bb400c373a6f90ba956dd25fe24ee4d4788f41e/packages/expect/src/matchers.ts#L580
77
+ // Added the testBots matcher to make testing against bots easier.
78
+ const matcherName = 'toEqual';
79
+ const options = {
80
+ comment: 'deep equality',
81
+ isNot: this.isNot,
82
+ promise: this.promise,
83
+ };
84
+ const pass = this.equals(received, expected, [
85
+ botsEquality,
86
+ iterableEquality,
87
+ ]);
88
+ const message = pass
89
+ ? () => this.utils.matcherHint(matcherName, undefined, undefined, options) +
90
+ '\n\n' +
91
+ `Expected: not ${this.utils.printExpected(expected)}\n` +
92
+ (this.utils.stringify(expected) !==
93
+ this.utils.stringify(received)
94
+ ? `Received: ${this.utils.printReceived(received)}`
95
+ : '')
96
+ : () => this.utils.matcherHint(matcherName, undefined, undefined, options) +
97
+ '\n\n' +
98
+ this.utils.printDiffOrStringify(expected, received, 'Expected', 'Received', this.expand !== false);
99
+ // Passing the actual and expected objects so that a custom reporter
100
+ // could access them, for example in order to display a custom visual diff,
101
+ // or create a different error message
102
+ return { actual: received, expected, message, name: matcherName, pass };
103
+ },
104
+ });
105
+ function getBotSnapshot(bot) {
106
+ var _a;
107
+ let b = {
108
+ id: bot.id,
109
+ space: bot.space,
110
+ tags: typeof bot.tags.toJSON === 'function'
111
+ ? bot.tags.toJSON()
112
+ : bot.tags,
113
+ };
114
+ let masks = isRuntimeBot(bot)
115
+ ? bot[GET_TAG_MASKS_SYMBOL]()
116
+ : cloneDeep((_a = bot.masks) !== null && _a !== void 0 ? _a : {});
117
+ if (Object.keys(masks).length > 0) {
118
+ b.masks = masks;
119
+ }
120
+ return b;
121
+ }
40
122
  /**
41
123
  * Creates a library that includes the default functions and APIs.
42
124
  * @param context The global context that should be used.
@@ -48,6 +130,14 @@ export function createDefaultLibrary(context) {
48
130
  };
49
131
  const webhookFunc = makeMockableFunction(webhook, 'webhook');
50
132
  webhookFunc.post = makeMockableFunction(webhook.post, 'webhook.post');
133
+ const shoutImpl = shout;
134
+ const shoutProxy = new Proxy(shoutImpl, {
135
+ get(target, name, reciever) {
136
+ return (arg) => {
137
+ return shout(name, arg);
138
+ };
139
+ },
140
+ });
51
141
  return {
52
142
  api: {
53
143
  getBots,
@@ -57,6 +147,10 @@ export function createDefaultLibrary(context) {
57
147
  getBotPosition,
58
148
  getID,
59
149
  getJSON,
150
+ getFormattedJSON,
151
+ getSnapshot,
152
+ diffSnapshots,
153
+ applyDiffToSnapshot,
60
154
  getTag,
61
155
  setTag,
62
156
  setTagMask,
@@ -71,9 +165,12 @@ export function createDefaultLibrary(context) {
71
165
  subtractMods,
72
166
  destroy,
73
167
  changeState,
168
+ getLink: createBotLinkApi,
169
+ getBotLinks,
170
+ updateBotLinks,
74
171
  superShout,
75
172
  priorityShout,
76
- shout,
173
+ shout: shoutProxy,
77
174
  whisper,
78
175
  byTag,
79
176
  byID,
@@ -107,6 +204,7 @@ export function createDefaultLibrary(context) {
107
204
  clearWatchPortal,
108
205
  assert,
109
206
  assertEqual,
207
+ expect,
110
208
  html,
111
209
  os: {
112
210
  sleep,
@@ -199,12 +297,16 @@ export function createDefaultLibrary(context) {
199
297
  publishRecord: makeMockableFunction(publishRecord, 'os.publishRecord'),
200
298
  getRecords: makeMockableFunction(getRecords, 'os.getRecords'),
201
299
  destroyRecord: makeMockableFunction(destroyRecord, 'os.destroyRecord'),
300
+ convertGeolocationToWhat3Words,
202
301
  setupInst: setupServer,
203
302
  remotes,
204
303
  instances: servers,
205
304
  remoteCount: serverRemoteCount,
206
305
  totalRemoteCount: totalRemoteCount,
207
306
  instStatuses: serverStatuses,
307
+ get vars() {
308
+ return context.global;
309
+ },
208
310
  },
209
311
  portal: {
210
312
  registerPrefix,
@@ -472,11 +574,14 @@ export function createDefaultLibrary(context) {
472
574
  * @param second The second value to test.
473
575
  */
474
576
  function assertEqual(first, second) {
475
- const json = getPrettyJSON(getAssertionValue(first));
476
- const json2 = getPrettyJSON(getAssertionValue(second));
477
- if (json !== json2) {
478
- throw new Error(`Assertion failed.\n\nExpected: ${json2}\nReceived: ${json}`);
479
- }
577
+ expect(first).toEqual(second);
578
+ // const json = getFormattedJSON(getAssertionValue(first));
579
+ // const json2 = getFormattedJSON(getAssertionValue(second));
580
+ // if (json !== json2) {
581
+ // throw new Error(
582
+ // `Assertion failed.\n\nExpected: ${json2}\nReceived: ${json}`
583
+ // );
584
+ // }
480
585
  }
481
586
  /**
482
587
  * Gets a list of all the bots.
@@ -646,10 +751,37 @@ export function createDefaultLibrary(context) {
646
751
  };
647
752
  }
648
753
  else if (hasValue(filter)) {
649
- return (bot) => {
650
- let val = bot.tags[tag];
651
- return hasValue(val) && filter === val;
652
- };
754
+ if (isBotLink(filter)) {
755
+ const ids = parseBotLink(filter);
756
+ if (ids.length === 0) {
757
+ return (bot) => {
758
+ let val = bot.tags[tag];
759
+ return val === filter;
760
+ };
761
+ }
762
+ else if (ids.length === 1) {
763
+ return (bot) => {
764
+ let val = bot.tags[tag];
765
+ return (ids[0] === val ||
766
+ (isBotLink(val) &&
767
+ parseBotLink(val).some((id) => id === ids[0])));
768
+ };
769
+ }
770
+ else {
771
+ return (bot) => {
772
+ let val = bot.tags[tag];
773
+ const valIds = parseBotLink(val);
774
+ return (!!valIds &&
775
+ ids.every((id1) => valIds.some((id2) => id1 === id2)));
776
+ };
777
+ }
778
+ }
779
+ else {
780
+ return (bot) => {
781
+ let val = bot.tags[tag];
782
+ return hasValue(val) && filter === val;
783
+ };
784
+ }
653
785
  }
654
786
  else if (filter === null) {
655
787
  return (bot) => {
@@ -922,15 +1054,123 @@ export function createDefaultLibrary(context) {
922
1054
  return stableStringify(data);
923
1055
  }
924
1056
  /**
925
- * Gets JSON for the given data.
1057
+ * Gets formatted JSON for the given data.
926
1058
  * @param data The data.
927
1059
  */
928
- function getPrettyJSON(data) {
1060
+ function getFormattedJSON(data) {
929
1061
  if (hasValue(data === null || data === void 0 ? void 0 : data[ORIGINAL_OBJECT])) {
930
1062
  return stableStringify(data[ORIGINAL_OBJECT], { space: 2 });
931
1063
  }
932
1064
  return stableStringify(data, { space: 2 });
933
1065
  }
1066
+ /**
1067
+ * Gets a snapshot of the data that the bots contain.
1068
+ * This is useful for getting all the tags and masks that are attached to the given bots.
1069
+ * @param bots The array of bots to get the snapshot for.
1070
+ */
1071
+ function getSnapshot(bots) {
1072
+ var _a;
1073
+ if (!Array.isArray(bots)) {
1074
+ return getSnapshot([bots]);
1075
+ }
1076
+ let state = {};
1077
+ for (let bot of bots) {
1078
+ let b = (state[bot.id] = {
1079
+ id: bot.id,
1080
+ tags: Object.assign({}, (typeof bot.tags.toJSON === 'function'
1081
+ ? bot.tags.toJSON()
1082
+ : bot.tags)),
1083
+ });
1084
+ if (bot.space) {
1085
+ b.space = bot.space;
1086
+ }
1087
+ let masks = isRuntimeBot(bot)
1088
+ ? bot[GET_TAG_MASKS_SYMBOL]()
1089
+ : cloneDeep((_a = bot.masks) !== null && _a !== void 0 ? _a : {});
1090
+ if (Object.keys(masks).length > 0) {
1091
+ b.masks = masks;
1092
+ }
1093
+ }
1094
+ return state;
1095
+ }
1096
+ /**
1097
+ * Calculates the difference between the two given snapshots.
1098
+ * @param first The first snapshot.
1099
+ * @param second The second snapshot.
1100
+ */
1101
+ function diffSnapshots(first, second) {
1102
+ const allIds = union(Object.keys(first), Object.keys(second));
1103
+ let diff = {};
1104
+ for (let id of allIds) {
1105
+ const inFirst = id in first;
1106
+ const inSecond = id in second;
1107
+ if (inFirst && inSecond) {
1108
+ // possibly updated
1109
+ const firstBot = first[id];
1110
+ const secondBot = second[id];
1111
+ if (firstBot && secondBot) {
1112
+ let botDiff = {};
1113
+ let tagsDiff = diffTags(firstBot.tags, secondBot.tags);
1114
+ if (!!tagsDiff) {
1115
+ botDiff.tags = tagsDiff;
1116
+ }
1117
+ const firstBotMasks = firstBot.masks || {};
1118
+ const secondBotMasks = secondBot.masks || {};
1119
+ let masksDiff = {};
1120
+ let hasMasksDiff = false;
1121
+ const allMaskSpaces = union(Object.keys(firstBotMasks), Object.keys(secondBotMasks));
1122
+ for (let space of allMaskSpaces) {
1123
+ const firstMasks = firstBotMasks[space] || {};
1124
+ const secondMasks = secondBotMasks[space] || {};
1125
+ let tagsDiff = diffTags(firstMasks, secondMasks);
1126
+ if (!!tagsDiff) {
1127
+ hasMasksDiff = true;
1128
+ masksDiff[space] = tagsDiff;
1129
+ }
1130
+ }
1131
+ if (hasMasksDiff) {
1132
+ botDiff.masks = masksDiff;
1133
+ }
1134
+ if (!!tagsDiff || hasMasksDiff) {
1135
+ diff[id] = botDiff;
1136
+ }
1137
+ }
1138
+ }
1139
+ else if (inFirst) {
1140
+ // deleted
1141
+ diff[id] = null;
1142
+ }
1143
+ else if (inSecond) {
1144
+ // added
1145
+ diff[id] = second[id];
1146
+ }
1147
+ }
1148
+ return diff;
1149
+ function diffTags(firstTags, secondTags) {
1150
+ let tagsDiff = {};
1151
+ let hasTagsDiff = false;
1152
+ const allTags = union(Object.keys(firstTags), Object.keys(secondTags));
1153
+ for (let tag of allTags) {
1154
+ const firstValue = firstTags[tag];
1155
+ const secondValue = secondTags[tag];
1156
+ if (!isEqual(firstValue, secondValue)) {
1157
+ // updated, deleted, or added
1158
+ hasTagsDiff = true;
1159
+ tagsDiff[tag] = hasValue(secondValue) ? secondValue : null;
1160
+ }
1161
+ }
1162
+ return hasTagsDiff ? tagsDiff : null;
1163
+ }
1164
+ }
1165
+ /**
1166
+ * Applies the given delta to the given snapshot and returns the result.
1167
+ * This is essentially the opposite of diffSnapshots().
1168
+ * @param snapshot The snapshot that the diff should be applied to.
1169
+ * @param diff The delta that should be applied to the snapshot.
1170
+ */
1171
+ function applyDiffToSnapshot(snapshot, diff) {
1172
+ return apply(snapshot, diff);
1173
+ }
934
1174
  // Actions
935
1175
  /**
936
1176
  * Shows a toast message to the user.
@@ -1917,6 +2157,15 @@ export function createDefaultLibrary(context) {
1917
2157
  const event = deleteRecord(token, address, space, task.taskId);
1918
2158
  return addAsyncAction(task, event);
1919
2159
  }
2160
+ /**
2161
+ * Converts the given geolocation to a what3words (https://what3words.com/) address.
2162
+ * @param location The latitude and longitude that should be converted to a 3 word address.
2163
+ */
2164
+ function convertGeolocationToWhat3Words(location) {
2165
+ const task = context.createTask();
2166
+ const event = calcConvertGeolocationToWhat3Words(location, task.taskId);
2167
+ return addAsyncAction(task, event);
2168
+ }
1920
2169
  /**
1921
2170
  * Sends an event to the server to setup a new instance if it does not exist.
1922
2171
  * @param inst The instance.
@@ -2718,8 +2967,46 @@ export function createDefaultLibrary(context) {
2718
2967
  * @param options The options that should be used to send the webhook.
2719
2968
  */
2720
2969
  function webhook(options) {
2970
+ if (options.retryCount > 0) {
2971
+ return _retryWebhook(options);
2972
+ }
2973
+ else {
2974
+ return _webhook(options);
2975
+ }
2976
+ }
2977
+ function _retryWebhook(options) {
2978
+ var _a, _b, _c, _d;
2979
+ return __awaiter(this, void 0, void 0, function* () {
2980
+ const retryCount = Math.min(options.retryCount, MAX_RETRY_COUNT);
2981
+ const timeToWait = Math.max(0, Math.min((_a = options.retryAfterMs) !== null && _a !== void 0 ? _a : DEFAULT_RETRY_AFTER_MS, MAX_RETRY_AFTER_MS));
2982
+ const statusCodes = (_b = options.retryStatusCodes) !== null && _b !== void 0 ? _b : DEFUALT_RETRY_STATUS_CODES;
2983
+ let retries = 0;
2984
+ while (true) {
2985
+ try {
2986
+ return yield _webhook(options);
2987
+ }
2988
+ catch (err) {
2989
+ if (retries >= retryCount) {
2990
+ throw err;
2991
+ }
2992
+ else if (!statusCodes.includes((_d = (_c = err.response) === null || _c === void 0 ? void 0 : _c.status) !== null && _d !== void 0 ? _d : 0)) {
2993
+ throw err;
2994
+ }
2995
+ yield sleep(timeToWait);
2996
+ retries += 1;
2997
+ }
2998
+ }
2999
+ });
3000
+ }
3001
+ function _webhook(options) {
2721
3002
  const task = context.createTask();
2722
- const event = calcWebhook(options, task.taskId);
3003
+ const event = calcWebhook({
3004
+ method: options.method,
3005
+ url: options.url,
3006
+ responseShout: options.responseShout,
3007
+ data: options.data,
3008
+ headers: options.headers,
3009
+ }, task.taskId);
2723
3010
  return addAsyncAction(task, event);
2724
3011
  }
2725
3012
  /**
@@ -4172,7 +4459,7 @@ export function createDefaultLibrary(context) {
4172
4459
  destroyChildren(id);
4173
4460
  }
4174
4461
  function destroyChildren(id) {
4175
- const children = getBots('creator', id);
4462
+ const children = getBots(byTag('creator', createBotLink([id])));
4176
4463
  for (let child of children) {
4177
4464
  destroyBot(child);
4178
4465
  }
@@ -4198,6 +4485,93 @@ export function createDefaultLibrary(context) {
4198
4485
  }
4199
4486
  whisper(bot, `${groupName}${stateName}OnEnter`, arg);
4200
4487
  }
4488
+ /**
4489
+ * Creates a tag value that can be used to link to the given bots.
4490
+ * @param bots The bots that the link should point to.
4491
+ */
4492
+ function createBotLinkApi(...bots) {
4493
+ let targets = flatMap(bots);
4494
+ let result = [];
4495
+ for (let t of targets) {
4496
+ if (isBot(t)) {
4497
+ result.push(t.id);
4498
+ }
4499
+ else {
4500
+ let links = parseBotLink(t);
4501
+ if (links) {
4502
+ result.push(...links);
4503
+ }
4504
+ else {
4505
+ result.push(t);
4506
+ }
4507
+ }
4508
+ }
4509
+ return createBotLink(result);
4510
+ }
4511
+ /**
4512
+ * Gets the list of bot links that are stored in this bot's tags.
4513
+ * @param bot The bot to get the links for.
4514
+ */
4515
+ function getBotLinks(bot) {
4516
+ let links = [];
4517
+ for (let tag of Object.keys(bot.tags)) {
4518
+ const val = bot.tags[tag];
4519
+ const ids = parseBotLink(val);
4520
+ if (ids) {
4521
+ links.push({
4522
+ tag,
4523
+ botIDs: ids,
4524
+ });
4525
+ }
4526
+ }
4527
+ return links;
4528
+ }
4529
+ /**
4530
+ * Updates all the links in the given bot using the given ID map.
4531
+ * Useful if you know that the links in the given bot are outdated and you know which IDs map to the new IDs.
4532
+ * @param bot The bot to update.
4533
+ * @param idMap The map of old IDs to new IDs that should be used.
4534
+ */
4535
+ function updateBotLinks(bot, idMap) {
4536
+ let map;
4537
+ if (idMap instanceof Map) {
4538
+ map = idMap;
4539
+ }
4540
+ else if (typeof idMap === 'object') {
4541
+ map = new Map();
4542
+ for (let key in idMap) {
4543
+ const newId = idMap[key];
4544
+ if (typeof newId === 'string') {
4545
+ map.set(key, newId);
4546
+ }
4547
+ else if (isBot(newId)) {
4548
+ map.set(key, newId.id);
4549
+ }
4550
+ }
4551
+ }
4552
+ else {
4553
+ return;
4554
+ }
4555
+ for (let tag of Object.keys(bot.tags)) {
4556
+ const val = bot.tags[tag];
4557
+ const ids = parseBotLink(val);
4558
+ if (ids) {
4559
+ const mapped = ids.map((id) => {
4560
+ if (map.has(id)) {
4561
+ const newId = map.get(id);
4562
+ if (typeof newId === 'string') {
4563
+ return newId;
4564
+ }
4565
+ else if (isBot(newId)) {
4566
+ return newId.id;
4567
+ }
4568
+ }
4569
+ return id;
4570
+ });
4571
+ bot.tags[tag] = createBotLink(mapped);
4572
+ }
4573
+ }
4574
+ }
4201
4575
  /**
4202
4576
  * Shouts the given event to every bot in every loaded simulation.
4203
4577
  * @param eventName The name of the event to shout.