pxt-core 9.0.6 → 9.0.7

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/built/pxt.js CHANGED
@@ -97876,6 +97876,7 @@ var pxt;
97876
97876
  const AUTH_LOGIN_STATE_KEY = "login-state"; // stored in local storage.
97877
97877
  const AUTH_USER_STATE_KEY = "user-state"; // stored in local storage.
97878
97878
  const X_PXT_TARGET = "x-pxt-target"; // header passed in auth rest calls.
97879
+ const INTERACTIVE_LOGIN_UNTIL = "interactive-login-until"; // hint whether to prompt user or try SSO first.
97879
97880
  let authDisabled = false;
97880
97881
  auth.DEFAULT_USER_PREFERENCES = () => ({
97881
97882
  highContrast: false,
@@ -97901,19 +97902,29 @@ var pxt;
97901
97902
  // Last known auth token state. This is provided as a convenience for legacy methods that cannot be made async.
97902
97903
  // Preference hasAuthTokenAsync() over taking a dependency on this cached value.
97903
97904
  auth.cachedHasAuthToken = false;
97904
- async function getAuthTokenAsync() {
97905
- let token;
97905
+ async function setLocalStorageValueAsync(key, value) {
97906
+ if (!!value)
97907
+ return await pxt.storage.shared.setAsync(AUTH_CONTAINER, key, value);
97908
+ else
97909
+ return await pxt.storage.shared.delAsync(AUTH_CONTAINER, key);
97910
+ }
97911
+ async function getLocalStorageValueAsync(key) {
97906
97912
  try {
97907
- token = await pxt.storage.shared.getAsync(AUTH_CONTAINER, CSRF_TOKEN_KEY);
97913
+ return await pxt.storage.shared.getAsync(AUTH_CONTAINER, key);
97914
+ }
97915
+ catch (_a) {
97916
+ return undefined;
97908
97917
  }
97909
- catch (_a) { }
97918
+ }
97919
+ async function getAuthTokenAsync() {
97920
+ const token = await getLocalStorageValueAsync(CSRF_TOKEN_KEY);
97910
97921
  auth.cachedHasAuthToken = !!token;
97911
97922
  return token;
97912
97923
  }
97913
97924
  auth.getAuthTokenAsync = getAuthTokenAsync;
97914
97925
  async function setAuthTokenAsync(token) {
97915
97926
  auth.cachedHasAuthToken = !!token;
97916
- return await pxt.storage.shared.setAsync(AUTH_CONTAINER, CSRF_TOKEN_KEY, token);
97927
+ return await setLocalStorageValueAsync(CSRF_TOKEN_KEY, token);
97917
97928
  }
97918
97929
  async function hasAuthTokenAsync() {
97919
97930
  return !!(await getAuthTokenAsync());
@@ -97921,7 +97932,7 @@ var pxt;
97921
97932
  auth.hasAuthTokenAsync = hasAuthTokenAsync;
97922
97933
  async function delAuthTokenAsync() {
97923
97934
  auth.cachedHasAuthToken = false;
97924
- return await pxt.storage.shared.delAsync(AUTH_CONTAINER, CSRF_TOKEN_KEY);
97935
+ return await setLocalStorageValueAsync(CSRF_TOKEN_KEY, undefined);
97925
97936
  }
97926
97937
  async function getUserStateAsync() {
97927
97938
  let userState;
@@ -97984,16 +97995,20 @@ var pxt;
97984
97995
  idp,
97985
97996
  persistent
97986
97997
  };
97998
+ // Should the user be prompted to interactively login, or can we try to silently login?
97999
+ const interactiveUntil = parseInt(await getLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL));
98000
+ const interactiveLogin = (interactiveUntil || 0) > Date.now();
97987
98001
  // Redirect to the login endpoint.
97988
98002
  const loginUrl = pxt.Util.stringifyQueryString('/api/auth/login', {
97989
98003
  response_type: "token",
97990
98004
  provider: idp,
97991
98005
  persistent,
97992
- redirect_uri: `${window.location.origin}${window.location.pathname}?authcallback=1&state=${loginState.key}`
98006
+ redirect_uri: `${window.location.origin}${window.location.pathname}?authcallback=1&state=${loginState.key}`,
98007
+ prompt: interactiveLogin ? "select_account" : "silent"
97993
98008
  });
97994
98009
  const apiResult = await this.apiAsync(loginUrl);
97995
98010
  if (apiResult.success) {
97996
- loginState.authCodeVerifier = apiResult.resp.authCodeVerifier;
98011
+ loginState.authCodeVerifier = apiResult.resp.authCodeVerifier; // will be undefined unless configured for the target
97997
98012
  await pxt.storage.shared.setAsync(AUTH_CONTAINER, AUTH_LOGIN_STATE_KEY, loginState);
97998
98013
  pxt.tickEvent('auth.login.start', { 'provider': idp });
97999
98014
  window.location.href = apiResult.resp.loginUrl;
@@ -98012,13 +98027,15 @@ var pxt;
98012
98027
  if (!hasIdentity()) {
98013
98028
  return;
98014
98029
  }
98015
- // Clear local auth state so we can no longer make authenticated requests.
98016
- await this.clearAuthStateAsync();
98017
98030
  await AuthClient.staticLogoutAsync(continuationHash);
98018
98031
  try {
98019
- await this.onSignedOut();
98032
+ await this.onStateCleared();
98020
98033
  }
98021
98034
  catch (_a) { }
98035
+ try {
98036
+ await this.onSignedOut();
98037
+ }
98038
+ catch (_b) { }
98022
98039
  }
98023
98040
  /**
98024
98041
  * Sign out the user and clear the auth token cookie.
@@ -98028,19 +98045,39 @@ var pxt;
98028
98045
  return;
98029
98046
  }
98030
98047
  pxt.tickEvent('auth.logout');
98031
- // Tell backend to clear the http-only auth cookie. Ignore errors here, we want to clear local state even if the backend rejects this request.
98048
+ // Indicate that for the next minute, signin should be interactive.
98049
+ // Use case: SSO signed in with the wrong account. User wants to sign in with a different account.
98050
+ await setLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL, (Date.now() + 60000).toString());
98051
+ continuationHash = continuationHash ? continuationHash.startsWith('#') ? continuationHash : `#${continuationHash}` : "";
98052
+ const clientRedirect = `${window.location.origin}${window.location.pathname}${window.location.search}${continuationHash}`;
98053
+ // Tell backend to clear the http-only auth cookie.
98054
+ let logoutUri = "";
98032
98055
  try {
98033
- await AuthClient.staticApiAsync('/api/auth/logout');
98056
+ const uri = pxt.Util.stringifyQueryString('/api/auth/logout', {
98057
+ redirect_uri: clientRedirect,
98058
+ authcallback: '1'
98059
+ });
98060
+ const apiResult = await AuthClient.staticApiAsync(uri);
98061
+ if (apiResult.success) {
98062
+ logoutUri = apiResult.resp.logoutUrl;
98063
+ }
98064
+ }
98065
+ catch (_a) {
98066
+ // Ignore errors.
98034
98067
  }
98035
- catch (_a) { }
98036
98068
  // Clear local auth state so we can no longer make authenticated requests.
98037
98069
  await delAuthTokenAsync();
98038
98070
  await delUserStateAsync();
98039
- // Redirect to home screen
98040
98071
  if (pxt.BrowserUtils.hasWindow()) {
98041
- const hash = continuationHash ? continuationHash.startsWith('#') ? continuationHash : `#${continuationHash}` : "";
98042
- window.location.href = `${window.location.origin}${window.location.pathname}${window.location.search}${hash}`;
98043
- location.reload();
98072
+ if (logoutUri) {
98073
+ // Redirect to logout endpoint
98074
+ window.location.href = logoutUri;
98075
+ }
98076
+ else {
98077
+ // Redirect to home screen
98078
+ window.location.href = clientRedirect;
98079
+ location.reload();
98080
+ }
98044
98081
  }
98045
98082
  }
98046
98083
  async deleteProfileAsync() {
@@ -98436,6 +98473,8 @@ var pxt;
98436
98473
  // without its cookie-based counterpart. When "Remember me" is false,
98437
98474
  // the cookie is not persisted.
98438
98475
  await setAuthTokenAsync(authToken);
98476
+ // Clear interactive login flag. Next auth request will try silent SSO first.
98477
+ await setLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL, undefined);
98439
98478
  pxt.tickEvent('auth.login.success', { 'provider': loginState.idp });
98440
98479
  } while (false);
98441
98480
  // Clear url parameters and redirect to the callback location.
@@ -155234,6 +155273,13 @@ var pxsim;
155234
155273
  }
155235
155274
  }
155236
155275
  pxsim.Runtime = Runtime;
155276
+ function setParentMuteState(state) {
155277
+ Runtime.postMessage({
155278
+ type: "setmutebuttonstate",
155279
+ state
155280
+ });
155281
+ }
155282
+ pxsim.setParentMuteState = setParentMuteState;
155237
155283
  class PerfCounter {
155238
155284
  constructor(name) {
155239
155285
  this.name = name;
@@ -155906,7 +155952,7 @@ var pxsim;
155906
155952
  }
155907
155953
  }
155908
155954
  handleMessage(msg, source) {
155909
- var _a;
155955
+ var _a, _b, _c;
155910
155956
  switch (msg.type || '') {
155911
155957
  case 'ready': {
155912
155958
  const frameid = msg.frameid;
@@ -155969,6 +156015,9 @@ var pxsim;
155969
156015
  if (this.options.onTopLevelCodeEnd)
155970
156016
  this.options.onTopLevelCodeEnd();
155971
156017
  break;
156018
+ case 'setmutebuttonstate':
156019
+ (_c = (_b = this.options).onMuteButtonStateChange) === null || _c === void 0 ? void 0 : _c.call(_b, msg.state);
156020
+ break;
155972
156021
  default:
155973
156022
  this.postMessage(msg, source);
155974
156023
  break;
@@ -156373,6 +156422,10 @@ var pxsim;
156373
156422
  ctx.resume();
156374
156423
  }
156375
156424
  AudioContextManager.mute = mute;
156425
+ function isMuted() {
156426
+ return _mute;
156427
+ }
156428
+ AudioContextManager.isMuted = isMuted;
156376
156429
  function stopTone() {
156377
156430
  setCurrentToneGain(0);
156378
156431
  _frequency = 0;
@@ -36,6 +36,11 @@ declare namespace pxt.editor {
36
36
  HeaderOnly = "errorListHeader",
37
37
  Expanded = "errorListExpanded"
38
38
  }
39
+ enum MuteState {
40
+ Muted = "muted",
41
+ Unmuted = "unmuted",
42
+ Disabled = "disabled"
43
+ }
39
44
  interface IAppProps {
40
45
  }
41
46
  interface IAppState {
@@ -64,7 +69,7 @@ declare namespace pxt.editor {
64
69
  showParts?: boolean;
65
70
  fullscreen?: boolean;
66
71
  showMiniSim?: boolean;
67
- mute?: boolean;
72
+ mute?: MuteState;
68
73
  embedSimView?: boolean;
69
74
  editorPosition?: {
70
75
  lineNumber: number;
@@ -275,7 +280,7 @@ declare namespace pxt.editor {
275
280
  toggleTrace(intervalSpeed?: number): void;
276
281
  setTrace(enabled: boolean, intervalSpeed?: number): void;
277
282
  toggleMute(): void;
278
- setMute(on: boolean): void;
283
+ setMute(state: MuteState): void;
279
284
  openInstructions(): void;
280
285
  closeFlyout(): void;
281
286
  printCode(): void;
@@ -19,6 +19,12 @@ var pxt;
19
19
  ErrorListState["HeaderOnly"] = "errorListHeader";
20
20
  ErrorListState["Expanded"] = "errorListExpanded";
21
21
  })(ErrorListState = editor.ErrorListState || (editor.ErrorListState = {}));
22
+ let MuteState;
23
+ (function (MuteState) {
24
+ MuteState["Muted"] = "muted";
25
+ MuteState["Unmuted"] = "unmuted";
26
+ MuteState["Disabled"] = "disabled";
27
+ })(MuteState = editor.MuteState || (editor.MuteState = {}));
22
28
  let FilterState;
23
29
  (function (FilterState) {
24
30
  FilterState[FilterState["Hidden"] = 0] = "Hidden";
package/built/pxtlib.js CHANGED
@@ -190,6 +190,7 @@ var pxt;
190
190
  const AUTH_LOGIN_STATE_KEY = "login-state"; // stored in local storage.
191
191
  const AUTH_USER_STATE_KEY = "user-state"; // stored in local storage.
192
192
  const X_PXT_TARGET = "x-pxt-target"; // header passed in auth rest calls.
193
+ const INTERACTIVE_LOGIN_UNTIL = "interactive-login-until"; // hint whether to prompt user or try SSO first.
193
194
  let authDisabled = false;
194
195
  auth.DEFAULT_USER_PREFERENCES = () => ({
195
196
  highContrast: false,
@@ -215,19 +216,29 @@ var pxt;
215
216
  // Last known auth token state. This is provided as a convenience for legacy methods that cannot be made async.
216
217
  // Preference hasAuthTokenAsync() over taking a dependency on this cached value.
217
218
  auth.cachedHasAuthToken = false;
218
- async function getAuthTokenAsync() {
219
- let token;
219
+ async function setLocalStorageValueAsync(key, value) {
220
+ if (!!value)
221
+ return await pxt.storage.shared.setAsync(AUTH_CONTAINER, key, value);
222
+ else
223
+ return await pxt.storage.shared.delAsync(AUTH_CONTAINER, key);
224
+ }
225
+ async function getLocalStorageValueAsync(key) {
220
226
  try {
221
- token = await pxt.storage.shared.getAsync(AUTH_CONTAINER, CSRF_TOKEN_KEY);
227
+ return await pxt.storage.shared.getAsync(AUTH_CONTAINER, key);
228
+ }
229
+ catch (_a) {
230
+ return undefined;
222
231
  }
223
- catch (_a) { }
232
+ }
233
+ async function getAuthTokenAsync() {
234
+ const token = await getLocalStorageValueAsync(CSRF_TOKEN_KEY);
224
235
  auth.cachedHasAuthToken = !!token;
225
236
  return token;
226
237
  }
227
238
  auth.getAuthTokenAsync = getAuthTokenAsync;
228
239
  async function setAuthTokenAsync(token) {
229
240
  auth.cachedHasAuthToken = !!token;
230
- return await pxt.storage.shared.setAsync(AUTH_CONTAINER, CSRF_TOKEN_KEY, token);
241
+ return await setLocalStorageValueAsync(CSRF_TOKEN_KEY, token);
231
242
  }
232
243
  async function hasAuthTokenAsync() {
233
244
  return !!(await getAuthTokenAsync());
@@ -235,7 +246,7 @@ var pxt;
235
246
  auth.hasAuthTokenAsync = hasAuthTokenAsync;
236
247
  async function delAuthTokenAsync() {
237
248
  auth.cachedHasAuthToken = false;
238
- return await pxt.storage.shared.delAsync(AUTH_CONTAINER, CSRF_TOKEN_KEY);
249
+ return await setLocalStorageValueAsync(CSRF_TOKEN_KEY, undefined);
239
250
  }
240
251
  async function getUserStateAsync() {
241
252
  let userState;
@@ -298,16 +309,20 @@ var pxt;
298
309
  idp,
299
310
  persistent
300
311
  };
312
+ // Should the user be prompted to interactively login, or can we try to silently login?
313
+ const interactiveUntil = parseInt(await getLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL));
314
+ const interactiveLogin = (interactiveUntil || 0) > Date.now();
301
315
  // Redirect to the login endpoint.
302
316
  const loginUrl = pxt.Util.stringifyQueryString('/api/auth/login', {
303
317
  response_type: "token",
304
318
  provider: idp,
305
319
  persistent,
306
- redirect_uri: `${window.location.origin}${window.location.pathname}?authcallback=1&state=${loginState.key}`
320
+ redirect_uri: `${window.location.origin}${window.location.pathname}?authcallback=1&state=${loginState.key}`,
321
+ prompt: interactiveLogin ? "select_account" : "silent"
307
322
  });
308
323
  const apiResult = await this.apiAsync(loginUrl);
309
324
  if (apiResult.success) {
310
- loginState.authCodeVerifier = apiResult.resp.authCodeVerifier;
325
+ loginState.authCodeVerifier = apiResult.resp.authCodeVerifier; // will be undefined unless configured for the target
311
326
  await pxt.storage.shared.setAsync(AUTH_CONTAINER, AUTH_LOGIN_STATE_KEY, loginState);
312
327
  pxt.tickEvent('auth.login.start', { 'provider': idp });
313
328
  window.location.href = apiResult.resp.loginUrl;
@@ -326,13 +341,15 @@ var pxt;
326
341
  if (!hasIdentity()) {
327
342
  return;
328
343
  }
329
- // Clear local auth state so we can no longer make authenticated requests.
330
- await this.clearAuthStateAsync();
331
344
  await AuthClient.staticLogoutAsync(continuationHash);
332
345
  try {
333
- await this.onSignedOut();
346
+ await this.onStateCleared();
334
347
  }
335
348
  catch (_a) { }
349
+ try {
350
+ await this.onSignedOut();
351
+ }
352
+ catch (_b) { }
336
353
  }
337
354
  /**
338
355
  * Sign out the user and clear the auth token cookie.
@@ -342,19 +359,39 @@ var pxt;
342
359
  return;
343
360
  }
344
361
  pxt.tickEvent('auth.logout');
345
- // Tell backend to clear the http-only auth cookie. Ignore errors here, we want to clear local state even if the backend rejects this request.
362
+ // Indicate that for the next minute, signin should be interactive.
363
+ // Use case: SSO signed in with the wrong account. User wants to sign in with a different account.
364
+ await setLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL, (Date.now() + 60000).toString());
365
+ continuationHash = continuationHash ? continuationHash.startsWith('#') ? continuationHash : `#${continuationHash}` : "";
366
+ const clientRedirect = `${window.location.origin}${window.location.pathname}${window.location.search}${continuationHash}`;
367
+ // Tell backend to clear the http-only auth cookie.
368
+ let logoutUri = "";
346
369
  try {
347
- await AuthClient.staticApiAsync('/api/auth/logout');
370
+ const uri = pxt.Util.stringifyQueryString('/api/auth/logout', {
371
+ redirect_uri: clientRedirect,
372
+ authcallback: '1'
373
+ });
374
+ const apiResult = await AuthClient.staticApiAsync(uri);
375
+ if (apiResult.success) {
376
+ logoutUri = apiResult.resp.logoutUrl;
377
+ }
378
+ }
379
+ catch (_a) {
380
+ // Ignore errors.
348
381
  }
349
- catch (_a) { }
350
382
  // Clear local auth state so we can no longer make authenticated requests.
351
383
  await delAuthTokenAsync();
352
384
  await delUserStateAsync();
353
- // Redirect to home screen
354
385
  if (pxt.BrowserUtils.hasWindow()) {
355
- const hash = continuationHash ? continuationHash.startsWith('#') ? continuationHash : `#${continuationHash}` : "";
356
- window.location.href = `${window.location.origin}${window.location.pathname}${window.location.search}${hash}`;
357
- location.reload();
386
+ if (logoutUri) {
387
+ // Redirect to logout endpoint
388
+ window.location.href = logoutUri;
389
+ }
390
+ else {
391
+ // Redirect to home screen
392
+ window.location.href = clientRedirect;
393
+ location.reload();
394
+ }
358
395
  }
359
396
  }
360
397
  async deleteProfileAsync() {
@@ -750,6 +787,8 @@ var pxt;
750
787
  // without its cookie-based counterpart. When "Remember me" is false,
751
788
  // the cookie is not persisted.
752
789
  await setAuthTokenAsync(authToken);
790
+ // Clear interactive login flag. Next auth request will try silent SSO first.
791
+ await setLocalStorageValueAsync(INTERACTIVE_LOGIN_UNTIL, undefined);
753
792
  pxt.tickEvent('auth.login.success', { 'provider': loginState.idp });
754
793
  } while (false);
755
794
  // Clear url parameters and redirect to the callback location.
package/built/pxtsim.d.ts CHANGED
@@ -556,6 +556,10 @@ declare namespace pxsim {
556
556
  part: "background-color" | "button-stroke" | "text-color" | "button-fill" | "dpad-fill";
557
557
  color: string;
558
558
  }
559
+ interface SetMuteButtonStateMessage extends SimulatorMessage {
560
+ type: "setmutebuttonstate";
561
+ state: "muted" | "unmuted" | "disabled";
562
+ }
559
563
  namespace multiplayer {
560
564
  type MessageBase = {
561
565
  type: "multiplayer";
@@ -1255,6 +1259,7 @@ declare namespace pxsim {
1255
1259
  cleanScheduledExpired(): void;
1256
1260
  registerUserInteraction(): void;
1257
1261
  }
1262
+ function setParentMuteState(state: "muted" | "unmuted" | "disabled"): void;
1258
1263
  class PerfCounter {
1259
1264
  name: string;
1260
1265
  start: number;
@@ -1279,6 +1284,7 @@ declare namespace pxsim {
1279
1284
  onSimulatorReady?: () => void;
1280
1285
  onSimulatorCommand?: (msg: pxsim.SimulatorCommandMessage) => void;
1281
1286
  onTopLevelCodeEnd?: () => void;
1287
+ onMuteButtonStateChange?: (state: "muted" | "unmuted" | "disabled") => void;
1282
1288
  simUrl?: string;
1283
1289
  stoppedClass?: string;
1284
1290
  invalidatedClass?: string;
@@ -1493,6 +1499,7 @@ declare namespace pxsim {
1493
1499
  function isAudioElementActive(): boolean;
1494
1500
  let soundEventCallback: (ev: "playinstructions" | "muteallchannels", data?: Uint8Array) => void;
1495
1501
  function mute(mute: boolean): void;
1502
+ function isMuted(): boolean;
1496
1503
  function stopAll(): void;
1497
1504
  function stop(): void;
1498
1505
  function onStopAll(handler: () => void): void;
package/built/pxtsim.js CHANGED
@@ -5992,6 +5992,13 @@ var pxsim;
5992
5992
  }
5993
5993
  }
5994
5994
  pxsim.Runtime = Runtime;
5995
+ function setParentMuteState(state) {
5996
+ Runtime.postMessage({
5997
+ type: "setmutebuttonstate",
5998
+ state
5999
+ });
6000
+ }
6001
+ pxsim.setParentMuteState = setParentMuteState;
5995
6002
  class PerfCounter {
5996
6003
  constructor(name) {
5997
6004
  this.name = name;
@@ -6664,7 +6671,7 @@ var pxsim;
6664
6671
  }
6665
6672
  }
6666
6673
  handleMessage(msg, source) {
6667
- var _a;
6674
+ var _a, _b, _c;
6668
6675
  switch (msg.type || '') {
6669
6676
  case 'ready': {
6670
6677
  const frameid = msg.frameid;
@@ -6727,6 +6734,9 @@ var pxsim;
6727
6734
  if (this.options.onTopLevelCodeEnd)
6728
6735
  this.options.onTopLevelCodeEnd();
6729
6736
  break;
6737
+ case 'setmutebuttonstate':
6738
+ (_c = (_b = this.options).onMuteButtonStateChange) === null || _c === void 0 ? void 0 : _c.call(_b, msg.state);
6739
+ break;
6730
6740
  default:
6731
6741
  this.postMessage(msg, source);
6732
6742
  break;
@@ -7131,6 +7141,10 @@ var pxsim;
7131
7141
  ctx.resume();
7132
7142
  }
7133
7143
  AudioContextManager.mute = mute;
7144
+ function isMuted() {
7145
+ return _mute;
7146
+ }
7147
+ AudioContextManager.isMuted = isMuted;
7134
7148
  function stopTone() {
7135
7149
  setCurrentToneGain(0);
7136
7150
  _frequency = 0;