@casual-simulation/aux-runtime 3.10.5 → 4.0.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@casual-simulation/aux-runtime",
3
- "version": "3.10.5",
3
+ "version": "4.0.0",
4
4
  "description": "Runtime for AUX projects",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -25,23 +25,23 @@
25
25
  "access": "public"
26
26
  },
27
27
  "dependencies": {
28
- "@casual-simulation/aux-common": "^3.10.5",
29
- "@casual-simulation/aux-records": "^3.10.5",
30
- "@casual-simulation/crypto": "^3.10.3",
28
+ "@casual-simulation/aux-common": "^4.0.0",
29
+ "@casual-simulation/aux-records": "^4.0.0",
30
+ "@casual-simulation/crypto": "^4.0.0",
31
31
  "@casual-simulation/engine262": "0.0.1-4de2170374e22761996e46eb1362f4496ee57f8f",
32
32
  "@casual-simulation/error-stack-parser": "^2.0.7",
33
- "@casual-simulation/expect": "^3.10.3",
34
- "@casual-simulation/fast-json-stable-stringify": "^3.10.3",
35
- "@casual-simulation/js-interpreter": "^3.10.3",
36
- "@casual-simulation/stacktrace": "^3.1.11",
33
+ "@casual-simulation/expect": "^4.0.0",
34
+ "@casual-simulation/fast-json-stable-stringify": "^4.0.0",
35
+ "@casual-simulation/js-interpreter": "^4.0.0",
36
+ "@casual-simulation/stacktrace": "^4.0.0",
37
37
  "@casual-simulation/three": "^0.140.3",
38
+ "@sveltejs/acorn-typescript": "1.0.8",
38
39
  "@tweenjs/tween.js": "18.6.0",
39
40
  "@types/estraverse": "5.1.7",
40
41
  "@types/seedrandom": "3.0.8",
41
42
  "@types/uuid": "10.0.0",
42
43
  "acorn": "8.11.3",
43
44
  "acorn-jsx": "5.3.2",
44
- "acorn-typescript": "1.4.13",
45
45
  "astring": "1.7.5",
46
46
  "axios": "1.7.7",
47
47
  "base64-js": "^1.5.1",
@@ -475,7 +475,7 @@ export interface WatchBotTimer {
475
475
  /**
476
476
  * The function that should be called when the bot changes.
477
477
  */
478
- handler: () => void | Generator<InterpreterStop, any, InterpreterContinuation>;
478
+ handler: (bot: RuntimeBot, changedTags: string[], botId: string) => void | Generator<InterpreterStop, any, InterpreterContinuation>;
479
479
  }
480
480
  /**
481
481
  * Inserts the given bot into the global context.
@@ -2230,6 +2230,7 @@ export declare function createDefaultLibrary(context: AuxGlobalContext): {
2230
2230
  (bot: string | Bot | RuntimeBot | (string | Bot | RuntimeBot)[]): void;
2231
2231
  [INTERPRETABLE_FUNCTION]: (bot: string | Bot | RuntimeBot | (string | Bot | RuntimeBot)[]) => Generator<any, void, any>;
2232
2232
  };
2233
+ _watchBot: (bot: (Bot | string)[] | Bot | string, handler: (bot: Bot, changedTags: string[], botId: string) => void) => RuntimeBot | RuntimeBot[];
2233
2234
  _changeState: (bot: Bot, stateName: string, groupName?: string) => void;
2234
2235
  changeState: {
2235
2236
  (bot: Bot, stateName: string, groupName?: string): void;
@@ -2317,8 +2318,6 @@ export declare function createDefaultLibrary(context: AuxGlobalContext): {
2317
2318
  clearWatchBot: (id: number) => void;
2318
2319
  clearWatchPortal: (id: number) => void;
2319
2320
  assert: (condition: boolean, message?: string) => void;
2320
- assertEqual: (first: any, second: any) => void;
2321
- expect: import("@casual-simulation/expect/src/types").Expect;
2322
2321
  html: HtmlFunction;
2323
2322
  /**
2324
2323
  * Gets the config bot (formerly known as the player bot).
@@ -2385,6 +2384,10 @@ export declare function createDefaultLibrary(context: AuxGlobalContext): {
2385
2384
  showJoinCode: (inst?: string, dimension?: string) => ShowJoinCodeAction;
2386
2385
  requestFullscreenMode: () => RequestFullscreenAction;
2387
2386
  exitFullscreenMode: () => ExitFullscreenAction;
2387
+ promptToInstallPWA: () => Promise<{
2388
+ outcome: "accepted" | "dismissed";
2389
+ platform: string;
2390
+ }>;
2388
2391
  hideLoadingScreen: () => Promise<void>;
2389
2392
  showHtml: (html: string) => ShowHtmlAction;
2390
2393
  hideHtml: () => HideHtmlAction;
@@ -3043,7 +3046,7 @@ export declare function createDefaultLibrary(context: AuxGlobalContext): {
3043
3046
  setTimeout: (options: TagSpecificApiOptions) => (handler: (...args: any[]) => void | Generator<InterpreterStop, any, InterpreterContinuation>, timeout?: number, ...args: any[]) => number;
3044
3047
  setInterval: (options: TagSpecificApiOptions) => (handler: (...args: any[]) => void | Generator<InterpreterStop, any, InterpreterContinuation>, timeout?: number, ...args: any[]) => number;
3045
3048
  watchPortal: (options: TagSpecificApiOptions) => (portalId: string, handler: () => void | Generator<InterpreterStop, any, InterpreterContinuation>) => number;
3046
- watchBot: (options: TagSpecificApiOptions) => (bot: (Bot | string)[] | Bot | string, handler: () => void | Generator<InterpreterStop, any, InterpreterContinuation>) => number;
3049
+ watchBot: (options: TagSpecificApiOptions) => (bot: (Bot | string)[] | Bot | string, handler: (bot: Bot, changedTags: string[], botId: string) => void | Generator<InterpreterStop, any, InterpreterContinuation>) => number;
3047
3050
  };
3048
3051
  };
3049
3052
  export type DefaultLibrary = ReturnType<typeof createDefaultLibrary>;
@@ -1,5 +1,5 @@
1
1
  import { DEBUG_STRING, debugStringifyFunction } from './AuxGlobalContext';
2
- import { hasValue, trimTag, isBot, BOT_SPACE_TAG, toast as toastMessage, getScriptIssues as scriptIssues, configureTypeChecking as calcConfigureTypeChecking, tip as tipMessage, hideTips as hideTipMessages, showJoinCode as calcShowJoinCode, requestFullscreen, exitFullscreen, html as htmlMessage, hideHtml as hideHtmlMessage, setClipboard as calcSetClipboard, tweenTo as calcTweenTo, showChat as calcShowChat, hideChat as calcHideChat, runScript, getMediaPermission as calcGetMediaPermission, getAverageFrameRate as calcGetAverageFrameRate, enableAR as calcEnableAR, disableAR as calcDisableAR, enableVR as calcEnableVR, disableVR as calcDisableVR, arSupported as calcARSupported, vrSupported as calcVRSupported, showUploadAuxFile as calcShowUploadAuxFile, openQRCodeScanner as calcOpenQRCodeScanner, showQRCode as calcShowQRCode, generateQRCode as calcGenerateQRCode, openBarcodeScanner as calcOpenBarcodeScanner, showBarcode as calcShowBarcode, importAUX as calcImportAUX, showInputForTag as calcShowInputForTag, showInput as calcShowInput, showConfirm as calcShowConfirm, showAlert as calcShowAlert, replaceDragBot as calcReplaceDragBot, goToDimension as calcGoToDimension, goToURL as calcGoToURL, openURL as calcOpenURL, playSound as calcPlaySound, bufferSound as calcBufferSound, cancelSound as calcCancelSound, shell as calcShell, reject as calcReject, localFormAnimation as calcLocalFormAnimation, webhook as calcWebhook, superShout as calcSuperShout, share as calcShare, registerPrefix as calcRegisterPrefix, 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, getRemoteCount, getRemotes, listInstUpdates as calcListInstUpdates, getInstStateFromUpdates as calcGetInstStateFromUpdates, action, calculateAnchorPoint, calculateAnchorPointOffset, getBotPosition as calcGetBotPosition, getBotRotation as calcGetBotRotation, isRuntimeBot, SET_TAG_MASK_SYMBOL, CLEAR_TAG_MASKS_SYMBOL, getBotScale, EDIT_TAG_SYMBOL, EDIT_TAG_MASK_SYMBOL, circleWipe, addDropSnap as calcAddDropSnap, addDropGrid as calcAddDropGrid, 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, signOut as calcSignOut, createBot, defineGlobalBot as calcDefineGlobalBot, TEMPORARY_BOT_PARTITION_ID, convertToString, GET_TAG_MASKS_SYMBOL, isBotLink, parseBotLink, createBotLink, convertGeolocationToWhat3Words as calcConvertGeolocationToWhat3Words, meetCommand as calcMeetCommand, meetFunction as calcMeetFunction, openImageClassifier as calcOpenImageClassifier, classifyImages as calcOpenClassifyImages, isBotDate, DATE_TAG_PREFIX, parseBotDate, realNumberOrDefault, raycastFromCamera as calcRaycastFromCamera, raycastInPortal as calcRaycastInPortal, calculateRayFromCamera as calcCalculateRayFromCamera, bufferFormAddressGltf, startFormAnimation as calcStartFormAnimation, stopFormAnimation as calcStopFormAnimation, listFormAnimations as calcListFormAnimations, calculateStringTagValue, createInitializationUpdate as calcCreateInitalizationUpdate, applyUpdatesToInst as calcApplyUpdatesToInst, configureWakeLock, getWakeLockConfiguration as calcGetWakeLockConfiguration, analyticsRecordEvent as calcAnalyticsRecordEvent, KNOWN_TAGS, isStoredVersion2, getCurrentInstUpdate as calcGetCurrentInstUpdate, openPhotoCamera as calcOpenPhotoCamera, getEasing, enableCollaboration as calcEnableCollaboration, showAccountInfo as calcShowAccountInfo, reportInst as calcReportInst, getRecordsEndpoint as calcGetRecordsEndpoint, ldrawCountAddressBuildSteps as calcLdrawCountAddressBuildSteps, ldrawCountTextBuildSteps as calcLdrawCountTextBuildSteps, calculateViewportCoordinatesFromPosition as calcCalculateViewportCoordinatesFromPosition, calculateScreenCoordinatesFromViewportCoordinates as calcCalculateScreenCoordinatesFromViewportCoordinates, calculateViewportCoordinatesFromScreenCoordinates as calcCalculateViewportCoordinatesFromScreenCoordinates, capturePortalScreenshot as calcCapturePortalScreenshot, createStaticHtml as calcCreateStaticHtmlFromBots, recordLoom, watchLoom, getLoomMetadata, loadSharedDocument, installAuxFile as calcInstallAuxFile, calculateStringListTagValue, calculateScreenCoordinatesFromPosition as calcCalculateScreenCoordinatesFromPosition, addMapLayer as calcAddMapLayer, removeMapLayer as calcRemoveMapLayer, GET_DYNAMIC_LISTENERS_SYMBOL, ADD_BOT_LISTENER_SYMBOL, REMOVE_BOT_LISTENER_SYMBOL, trackConfigBotTags as calcTrackConfigBotTags, } from '@casual-simulation/aux-common/bots';
2
+ import { hasValue, trimTag, isBot, BOT_SPACE_TAG, toast as toastMessage, getScriptIssues as scriptIssues, configureTypeChecking as calcConfigureTypeChecking, tip as tipMessage, hideTips as hideTipMessages, showJoinCode as calcShowJoinCode, requestFullscreen, exitFullscreen, promptToInstallPWA as calcPromptToInstallPWA, html as htmlMessage, hideHtml as hideHtmlMessage, setClipboard as calcSetClipboard, tweenTo as calcTweenTo, showChat as calcShowChat, hideChat as calcHideChat, runScript, getMediaPermission as calcGetMediaPermission, getAverageFrameRate as calcGetAverageFrameRate, enableAR as calcEnableAR, disableAR as calcDisableAR, enableVR as calcEnableVR, disableVR as calcDisableVR, arSupported as calcARSupported, vrSupported as calcVRSupported, showUploadAuxFile as calcShowUploadAuxFile, openQRCodeScanner as calcOpenQRCodeScanner, showQRCode as calcShowQRCode, generateQRCode as calcGenerateQRCode, openBarcodeScanner as calcOpenBarcodeScanner, showBarcode as calcShowBarcode, importAUX as calcImportAUX, showInputForTag as calcShowInputForTag, showInput as calcShowInput, showConfirm as calcShowConfirm, showAlert as calcShowAlert, replaceDragBot as calcReplaceDragBot, goToDimension as calcGoToDimension, goToURL as calcGoToURL, openURL as calcOpenURL, playSound as calcPlaySound, bufferSound as calcBufferSound, cancelSound as calcCancelSound, shell as calcShell, reject as calcReject, localFormAnimation as calcLocalFormAnimation, webhook as calcWebhook, superShout as calcSuperShout, share as calcShare, registerPrefix as calcRegisterPrefix, 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, getRemoteCount, getRemotes, listInstUpdates as calcListInstUpdates, getInstStateFromUpdates as calcGetInstStateFromUpdates, action, calculateAnchorPoint, calculateAnchorPointOffset, getBotPosition as calcGetBotPosition, getBotRotation as calcGetBotRotation, isRuntimeBot, SET_TAG_MASK_SYMBOL, CLEAR_TAG_MASKS_SYMBOL, getBotScale, EDIT_TAG_SYMBOL, EDIT_TAG_MASK_SYMBOL, circleWipe, addDropSnap as calcAddDropSnap, addDropGrid as calcAddDropGrid, 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, signOut as calcSignOut, createBot, defineGlobalBot as calcDefineGlobalBot, TEMPORARY_BOT_PARTITION_ID, convertToString, GET_TAG_MASKS_SYMBOL, isBotLink, parseBotLink, createBotLink, convertGeolocationToWhat3Words as calcConvertGeolocationToWhat3Words, meetCommand as calcMeetCommand, meetFunction as calcMeetFunction, openImageClassifier as calcOpenImageClassifier, classifyImages as calcOpenClassifyImages, isBotDate, DATE_TAG_PREFIX, parseBotDate, realNumberOrDefault, raycastFromCamera as calcRaycastFromCamera, raycastInPortal as calcRaycastInPortal, calculateRayFromCamera as calcCalculateRayFromCamera, bufferFormAddressGltf, startFormAnimation as calcStartFormAnimation, stopFormAnimation as calcStopFormAnimation, listFormAnimations as calcListFormAnimations, calculateStringTagValue, createInitializationUpdate as calcCreateInitalizationUpdate, applyUpdatesToInst as calcApplyUpdatesToInst, configureWakeLock, getWakeLockConfiguration as calcGetWakeLockConfiguration, analyticsRecordEvent as calcAnalyticsRecordEvent, KNOWN_TAGS, isStoredVersion2, getCurrentInstUpdate as calcGetCurrentInstUpdate, openPhotoCamera as calcOpenPhotoCamera, getEasing, enableCollaboration as calcEnableCollaboration, showAccountInfo as calcShowAccountInfo, reportInst as calcReportInst, getRecordsEndpoint as calcGetRecordsEndpoint, ldrawCountAddressBuildSteps as calcLdrawCountAddressBuildSteps, ldrawCountTextBuildSteps as calcLdrawCountTextBuildSteps, calculateViewportCoordinatesFromPosition as calcCalculateViewportCoordinatesFromPosition, calculateScreenCoordinatesFromViewportCoordinates as calcCalculateScreenCoordinatesFromViewportCoordinates, calculateViewportCoordinatesFromScreenCoordinates as calcCalculateViewportCoordinatesFromScreenCoordinates, capturePortalScreenshot as calcCapturePortalScreenshot, createStaticHtml as calcCreateStaticHtmlFromBots, recordLoom, watchLoom, getLoomMetadata, loadSharedDocument, installAuxFile as calcInstallAuxFile, calculateStringListTagValue, calculateScreenCoordinatesFromPosition as calcCalculateScreenCoordinatesFromPosition, addMapLayer as calcAddMapLayer, removeMapLayer as calcRemoveMapLayer, GET_DYNAMIC_LISTENERS_SYMBOL, ADD_BOT_LISTENER_SYMBOL, REMOVE_BOT_LISTENER_SYMBOL, trackConfigBotTags as calcTrackConfigBotTags, } from '@casual-simulation/aux-common/bots';
3
3
  import { aiChat, aiChatStream, aiListChatModels, aiGenerateSkybox, aiGenerateImage, grantRecordPermission as calcGrantRecordPermission, revokeRecordPermission as calcRevokeRecordPermission, listPermissions as calcListPermissions, grantInstAdminPermission as calcGrantInstAdminPermission, grantUserRole as calcGrantUserRole, revokeUserRole as calcRevokeUserRole, grantInstRole as calcGrantInstRole, revokeInstRole as calcRevokeInstRole, listUserStudios as calcListUserStudios, listStudioRecords as calcListStudioRecords, joinRoom as calcJoinRoom, leaveRoom as calcLeaveRoom, setRoomOptions as calcSetRoomOptions, getRoomOptions as calcGetRoomOptions, getRoomTrackOptions as calcGetRoomTrackOptions, setRoomTrackOptions as calcSetRoomTrackOptions, getRoomRemoteOptions as calcGetRoomRemoteOptions, listDataRecord, recordEvent as calcRecordEvent, getEventCount as calcGetEventCount, getFile as calcGetFile, eraseFile as calcEraseFile, getPublicRecordKey as calcGetPublicRecordKey, recordData as calcRecordData, getRecordData, eraseRecordData, recordFile as calcRecordFile, listDataRecordByMarker, aiHumeGetAccessToken, aiSloydGenerateModel, recordWebhook as calcRecordWebhook, getWebhook as calcGetWebhook, listWebhooks as calcListWebhooks, listWebhooksByMarker as calcListWebhooksByMarker, eraseWebhook as calcEraseWebhook, runWebhook as calcRunWebhook, recordNotification as calcRecordNotification, getNotification as calcGetNotification, listNotifications as calcListNotifications, listNotificationsByMarker as calcListNotificationsByMarker, eraseNotification as calcEraseNotification, subscribeToNotification as calcSubscribeToNotification, unsubscribeFromNotification as calcUnsubscribeFromNotification, sendNotification as calcSendNotification, listNotificationSubscriptions as calcListNotificationSubscriptions, listUserNotificationSubscriptions as calcListUserNotificationSubscriptions,
4
4
  // getXpUserMeta,
5
5
  // createXpContract,
@@ -24,7 +24,6 @@ import { Vector3, Vector2, Quaternion, Rotation, } from '@casual-simulation/aux-
24
24
  import { Fragment, h } from 'preact';
25
25
  import htm from 'htm';
26
26
  import { fromByteArray, toByteArray } from 'base64-js';
27
- import expect, { iterableEquality } from '@casual-simulation/expect';
28
27
  import { parseRecordKey, isRecordKey as calcIsRecordKey, } from '@casual-simulation/aux-common';
29
28
  import SeedRandom from 'seedrandom';
30
29
  import { DateTime } from 'luxon';
@@ -89,60 +88,6 @@ const MAX_RETRY_AFTER_MS = 60 * 60 * 1000;
89
88
  */
90
89
  const MAX_RETRY_COUNT = 10;
91
90
  export const GET_RUNTIME = Symbol('get_runtime');
92
- const botsEquality = function (first, second) {
93
- if (isRuntimeBot(first) && isRuntimeBot(second)) {
94
- expect(getBotSnapshot(first)).toEqual(getBotSnapshot(second));
95
- return true;
96
- }
97
- return undefined;
98
- };
99
- expect.extend({
100
- toEqual(received, expected) {
101
- // Copied from https://github.com/facebook/jest/blob/7bb400c373a6f90ba956dd25fe24ee4d4788f41e/packages/expect/src/matchers.ts#L580
102
- // Added the testBots matcher to make testing against bots easier.
103
- const matcherName = 'toEqual';
104
- const options = {
105
- comment: 'deep equality',
106
- isNot: this.isNot,
107
- promise: this.promise,
108
- };
109
- const pass = this.equals(received, expected, [
110
- botsEquality,
111
- iterableEquality,
112
- ]);
113
- const message = pass
114
- ? () => this.utils.matcherHint(matcherName, undefined, undefined, options) +
115
- '\n\n' +
116
- `Expected: not ${this.utils.printExpected(expected)}\n` +
117
- (this.utils.stringify(expected) !==
118
- this.utils.stringify(received)
119
- ? `Received: ${this.utils.printReceived(received)}`
120
- : '')
121
- : () => this.utils.matcherHint(matcherName, undefined, undefined, options) +
122
- '\n\n' +
123
- this.utils.printDiffOrStringify(expected, received, 'Expected', 'Received', this.expand !== false);
124
- // Passing the actual and expected objects so that a custom reporter
125
- // could access them, for example in order to display a custom visual diff,
126
- // or create a different error message
127
- return { actual: received, expected, message, name: matcherName, pass };
128
- },
129
- });
130
- function getBotSnapshot(bot) {
131
- let b = {
132
- id: bot.id,
133
- space: bot.space,
134
- tags: typeof bot.tags.toJSON === 'function'
135
- ? bot.tags.toJSON()
136
- : bot.tags,
137
- };
138
- let masks = isRuntimeBot(bot)
139
- ? bot[GET_TAG_MASKS_SYMBOL]()
140
- : cloneDeep(bot.masks ?? {});
141
- if (Object.keys(masks).length > 0) {
142
- b.masks = masks;
143
- }
144
- return b;
145
- }
146
91
  const DEAD_RECKONING_OFFSET = 50;
147
92
  const DEG_TO_RAD = Math.PI / 180;
148
93
  const RAD_TO_DEG = 180 / Math.PI;
@@ -414,6 +359,7 @@ export function createDefaultLibrary(context) {
414
359
  _create,
415
360
  _destroy,
416
361
  destroy: createInterpretableFunction(destroy),
362
+ _watchBot,
417
363
  _changeState,
418
364
  changeState: createInterpretableFunction(changeState),
419
365
  getLink: createBotLinkApi,
@@ -480,8 +426,6 @@ export function createDefaultLibrary(context) {
480
426
  clearWatchBot,
481
427
  clearWatchPortal,
482
428
  assert,
483
- assertEqual,
484
- expect,
485
429
  html,
486
430
  /**
487
431
  * Gets the config bot (formerly known as the player bot).
@@ -536,6 +480,7 @@ export function createDefaultLibrary(context) {
536
480
  showJoinCode,
537
481
  requestFullscreenMode,
538
482
  exitFullscreenMode,
483
+ promptToInstallPWA,
539
484
  hideLoadingScreen,
540
485
  showHtml,
541
486
  hideHtml,
@@ -1071,20 +1016,20 @@ export function createDefaultLibrary(context) {
1071
1016
  function watchBot() {
1072
1017
  let timerId = 0;
1073
1018
  return (options) => function (bot, handler) {
1074
- let id = timerId++;
1075
- let botIds = Array.isArray(bot)
1019
+ const id = timerId++;
1020
+ const botIds = Array.isArray(bot)
1076
1021
  ? bot.map((b) => getID(b))
1077
1022
  : [getID(bot)];
1078
- const finalHandler = () => {
1023
+ const finalHandler = (bot, changedTags, botId) => {
1079
1024
  try {
1080
- let result = handler();
1025
+ const result = handler(bot, changedTags, botId);
1081
1026
  return wrapGenerator(result);
1082
1027
  }
1083
1028
  catch (err) {
1084
1029
  context.enqueueError(err);
1085
1030
  }
1086
1031
  };
1087
- for (let botId of botIds) {
1032
+ for (const botId of botIds) {
1088
1033
  context.recordBotTimer(options.bot.id, {
1089
1034
  type: 'watch_bot',
1090
1035
  timerId: id,
@@ -1096,6 +1041,37 @@ export function createDefaultLibrary(context) {
1096
1041
  return id;
1097
1042
  };
1098
1043
  }
1044
+ /**
1045
+ * Watches the given bot(s) for changes and calls the given handler function whenever a tag on the bot changes or when the bot is destroyed.
1046
+ *
1047
+ * @param bot The bot or array of bots (or bot IDs) that should be watched for changes.
1048
+ *
1049
+ * @param handler The function that should be called whenever a tag on the bot changes.
1050
+ * The function is called with three arguments: the bot that changed, an array of tag names that changed on the bot, and the ID of the bot.
1051
+ * If the bot is destroyed, then the first argument will be `null` and the second argument will be an empty array.
1052
+ *
1053
+ * @returns The ID of the watcher that was created. This ID can be used to clear the watcher using {@link clearWatchBot}.
1054
+ *
1055
+ * @example Watch this bot for changes
1056
+ * let unwatch = watchBot(thisBot, (bot, changedTags) => {
1057
+ * if (!bot) return; // Bot was destroyed
1058
+ * os.toast("The bot (" + bot.id.slice(0, 6) + ") changed! Changed tags: " + changedTags.join(", "));
1059
+ * });
1060
+ *
1061
+ * @example Watch multiple bots for changes
1062
+ * let unwatch = watchBot(getBots('color', 'red'), (bot, changedTags) => {
1063
+ * if (!bot) return; // Bot was destroyed
1064
+ * os.toast("A red bot (" + bot.id.slice(0, 6) + ") changed! Changed tags: " + changedTags.join(", "));
1065
+ * });
1066
+ *
1067
+ * @dochash actions/data
1068
+ * @docgroup 01-data-actions
1069
+ * @docname watchBot
1070
+ * @docid watchBot
1071
+ */
1072
+ function _watchBot(bot, handler) {
1073
+ return null;
1074
+ }
1099
1075
  function wrapGenerator(result) {
1100
1076
  if (isGenerator(result)) {
1101
1077
  const gen = result;
@@ -1167,6 +1143,24 @@ export function createDefaultLibrary(context) {
1167
1143
  return result;
1168
1144
  }
1169
1145
  }
1146
+ /**
1147
+ * Clears the watcher created by {@link watchBot}.
1148
+ *
1149
+ * @param id The ID of the watcher to clear.
1150
+ *
1151
+ * @example Clear a bot watcher
1152
+ * let unwatch = watchBot(thisBot, (bot, changedTags) => {
1153
+ * os.toast("The bot (" + bot.id.slice(0, 6) + ") changed! Changed tags: " + changedTags.join(", "));
1154
+ * });
1155
+ *
1156
+ * // When you want to clear it
1157
+ * clearWatchBot(unwatch);
1158
+ *
1159
+ * @dochash actions/data
1160
+ * @docgroup 01-data-actions
1161
+ * @docname clearWatchBot
1162
+ * @docid clearWatchBot
1163
+ */
1170
1164
  function clearWatchBot(id) {
1171
1165
  context.cancelAndRemoveTimers(id, 'watch_bot');
1172
1166
  }
@@ -1198,38 +1192,6 @@ export function createDefaultLibrary(context) {
1198
1192
  }
1199
1193
  }
1200
1194
  }
1201
- function getAssertionValue(value) {
1202
- if (value instanceof Error) {
1203
- return value.toString();
1204
- }
1205
- return value;
1206
- }
1207
- /**
1208
- * Verifies that the given values are equal to each other.
1209
- * If they are not, then an error is thrown.
1210
- * This function is useful for automated testing since tests should ideally throw an error if the test fails.
1211
- * It can also be useful to make sure that some important code is only run if a precondition is met.
1212
- *
1213
- * @param first The first value to test.
1214
- * @param second The second value to test.
1215
- *
1216
- * @example Assert that the tag color is "blue"
1217
- * assertEqual(tags.color, "blue");
1218
- *
1219
- * @example Assert that the bot contains some specific tag values
1220
- * assertEqual(tags, {
1221
- * color: "blue",
1222
- * home: true,
1223
- * homeX: 0,
1224
- * homeY: 0
1225
- * });
1226
- *
1227
- * @dochash actions/debuggers
1228
- * @docname assertEqual
1229
- */
1230
- function assertEqual(first, second) {
1231
- expect(first).toEqual(second);
1232
- }
1233
1195
  /**
1234
1196
  * Gets an array of bots that match the given tag and value. The returned array is sorted alphabetically by the {@tag id} tag.
1235
1197
  *
@@ -2610,6 +2572,40 @@ export function createDefaultLibrary(context) {
2610
2572
  function exitFullscreenMode() {
2611
2573
  return addAction(exitFullscreen());
2612
2574
  }
2575
+ /**
2576
+ * Prompts the user to install the Progressive Web App (PWA).
2577
+ *
2578
+ * Returns a promise that resolves with an object containing the user's choice and platform information when they respond to the prompt. Rejects with an error if the feature is not supported (for example, on iOS devices).
2579
+ *
2580
+ * The returned object contains:
2581
+ * - `outcome`: Either "accepted" or "dismissed" based on the user's choice
2582
+ * - `platform`: The platform identifier (empty string if not provided)
2583
+ *
2584
+ * Note that this feature may not be available on all platforms and browsers. In particular:
2585
+ * - iOS Safari does not support the PWA installation prompt
2586
+ * - The prompt can only be triggered in response to user interaction
2587
+ * - The prompt will only be shown if the app meets PWA installability criteria
2588
+ *
2589
+ * @example Prompt the user to install the PWA.
2590
+ * try {
2591
+ * const result = await os.promptToInstallPWA();
2592
+ * if (result.outcome === 'accepted') {
2593
+ * os.toast("Thanks for installing!");
2594
+ * } else {
2595
+ * os.toast("Maybe next time!");
2596
+ * }
2597
+ * } catch (error) {
2598
+ * os.toast("PWA installation is not available: " + error.message);
2599
+ * }
2600
+ *
2601
+ * @dochash actions/os/system
2602
+ * @docname os.promptToInstallPWA
2603
+ */
2604
+ function promptToInstallPWA() {
2605
+ const task = context.createTask();
2606
+ const event = calcPromptToInstallPWA(task.taskId);
2607
+ return addAsyncAction(task, event);
2608
+ }
2613
2609
  /**
2614
2610
  * Hides the loading screen.
2615
2611
  *
@@ -10865,6 +10861,8 @@ export function createDefaultLibrary(context) {
10865
10861
  * If null is provided, then a seed will be chosen in a somewhat unpredictable manner.
10866
10862
  *
10867
10863
  * @example Set the random seed for math.random() and math.randomInt().
10864
+ * import expect from 'expect';
10865
+ *
10868
10866
  * math.setRandomSeed(123);
10869
10867
  *
10870
10868
  * expect(math.randomInt(0, 10)).toBe(9);