@juo/blocks 1.20.0 → 1.21.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/core.js CHANGED
@@ -234,6 +234,7 @@ function uuid() {
234
234
  );
235
235
  }
236
236
  var AuthenticationErrorReason = /* @__PURE__ */ ((AuthenticationErrorReason2) => {
237
+ AuthenticationErrorReason2["RateLimited"] = "rate-limited";
237
238
  AuthenticationErrorReason2["Expired"] = "expired";
238
239
  AuthenticationErrorReason2["IncorrectCode"] = "wrong-code";
239
240
  AuthenticationErrorReason2["TooManyTries"] = "too-many-tries";
@@ -251,6 +252,17 @@ class AuthenticationError extends Error {
251
252
  this.data = data ?? {};
252
253
  }
253
254
  }
255
+ class RateLimitedError extends AuthenticationError {
256
+ constructor(retryAt) {
257
+ super(
258
+ "rate-limited"
259
+ /* RateLimited */
260
+ );
261
+ /** Epoch milliseconds before which the backend will refuse another login request. */
262
+ __publicField(this, "retryAt");
263
+ this.retryAt = retryAt;
264
+ }
265
+ }
254
266
  const OTP_ERROR_REASONS = {
255
267
  INCORRECT_CODE: AuthenticationErrorReason.IncorrectCode,
256
268
  TOO_MANY_TRIES: AuthenticationErrorReason.TooManyTries,
@@ -272,6 +284,15 @@ function authenticationErrorFromBody(data) {
272
284
  const reason = OTP_ERROR_REASONS[code];
273
285
  return reason == null ? void 0 : new AuthenticationError(reason);
274
286
  }
287
+ function rateLimitErrorFromResponse(res) {
288
+ var _a;
289
+ if (res.status !== 429) {
290
+ return void 0;
291
+ }
292
+ const seconds = (_a = res.headers.get("Retry-After")) == null ? void 0 : _a.trim();
293
+ const delay2 = seconds && /^\d+$/.test(seconds) ? Number(seconds) * 1e3 : 6e4;
294
+ return new RateLimitedError(Date.now() + delay2);
295
+ }
275
296
  function createMockLoginAdapter(opts = {}) {
276
297
  const appUrl = opts.appUrl ?? "https://admin.juo.io";
277
298
  return {
@@ -310,6 +331,8 @@ function createApiLoginAdapter(fetcher, opts) {
310
331
  },
311
332
  body: JSON.stringify(body)
312
333
  });
334
+ const limited = rateLimitErrorFromResponse(res);
335
+ if (limited) throw limited;
313
336
  if (res.status === 403) {
314
337
  const error = authenticationErrorFromBody(await res.json());
315
338
  if (error != null) {
@@ -345,6 +368,8 @@ function createApiLoginAdapter(fetcher, opts) {
345
368
  },
346
369
  body: JSON.stringify(params)
347
370
  });
371
+ const limited = rateLimitErrorFromResponse(res);
372
+ if (limited) throw limited;
348
373
  if (res.status === 403) {
349
374
  const error = authenticationErrorFromBody(await res.json());
350
375
  if (error != null) {
@@ -417,6 +442,12 @@ function loginService(deps) {
417
442
  }
418
443
  function createLoginService(init) {
419
444
  const { adapter, formatError, router, shop, onLoginSuccess } = init;
445
+ function failure(error) {
446
+ return {
447
+ _tag: "Failure",
448
+ error: error instanceof RateLimitedError ? { ...formatError(error), retryAt: error.retryAt } : formatError(error)
449
+ };
450
+ }
420
451
  const accessToken = d(null);
421
452
  const delegatedToken = d(storage(STORAGE_DELEGATED_TOKEN));
422
453
  const refreshToken = d(storage(STORAGE_REFRESH_TOKEN_KEY));
@@ -489,11 +520,11 @@ function createLoginService(init) {
489
520
  lang
490
521
  });
491
522
  if (result._tag === "Failure") {
492
- return { _tag: "Failure", error: formatError(result.error) };
523
+ return failure(result.error);
493
524
  }
494
525
  return { _tag: "Success", data: void 0 };
495
526
  } catch (error) {
496
- return { _tag: "Failure", error: formatError(error) };
527
+ return failure(error);
497
528
  }
498
529
  },
499
530
  async verify(email, code) {
@@ -509,13 +540,13 @@ function createLoginService(init) {
509
540
  code
510
541
  });
511
542
  if (result._tag === "Failure") {
512
- return { _tag: "Failure", error: formatError(result.error) };
543
+ return failure(result.error);
513
544
  }
514
545
  storeTokens(result.data);
515
546
  await onSuccess();
516
547
  return { _tag: "Success", data: void 0 };
517
548
  } catch (error) {
518
- return { _tag: "Failure", error: formatError(error) };
549
+ return failure(error);
519
550
  }
520
551
  }
521
552
  }
@@ -535,13 +566,13 @@ function createLoginService(init) {
535
566
  try {
536
567
  const result = await adapter.authenticate({ code });
537
568
  if (result._tag === "Failure") {
538
- return { _tag: "Failure", error: formatError(result.error) };
569
+ return failure(result.error);
539
570
  }
540
571
  storeTokens(result.data);
541
572
  await onSuccess();
542
573
  return { _tag: "Success", data: void 0 };
543
574
  } catch (error) {
544
- return { _tag: "Failure", error: formatError(error) };
575
+ return failure(error);
545
576
  }
546
577
  }
547
578
  },
@@ -3725,8 +3756,7 @@ const MOCK_FLOWS = {
3725
3756
  actionType: "offer_pause",
3726
3757
  actionConfig: {
3727
3758
  headline: "Need a break?",
3728
- description: "Pause your subscription for up to 30 days instead of cancelling.",
3729
- pauseDurationDays: 30
3759
+ description: "Pause your subscription for up to 30 days instead of cancelling."
3730
3760
  },
3731
3761
  availableResponses: [
3732
3762
  { type: "accepted", label: "Pause subscription" },
@@ -4157,6 +4187,7 @@ export {
4157
4187
  OrdersServiceContext,
4158
4188
  P as PAGE_ROOT_SLOT,
4159
4189
  ProductServiceContext,
4190
+ RateLimitedError,
4160
4191
  RouterServiceContext,
4161
4192
  SUPPORTED_UI_LANGUAGES,
4162
4193
  SUPPORTED_UI_LANGUAGE_OPTIONS,
@@ -4246,6 +4277,7 @@ export {
4246
4277
  a1 as projectToCss,
4247
4278
  p as projectToGridStack,
4248
4279
  a2 as provideContext,
4280
+ rateLimitErrorFromResponse,
4249
4281
  a3 as readingOrder,
4250
4282
  e as reconcileLayout,
4251
4283
  a4 as rectsOverlap,
@@ -1 +1 @@
1
- {"version":3,"file":"MockWorkflowApiAdapter.d.ts","sourceRoot":"","sources":["../../src/flows/MockWorkflowApiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAKnB,MAAM,mBAAmB,CAAC;AA4L3B;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,IAAI,kBAAkB,CA0GjE"}
1
+ {"version":3,"file":"MockWorkflowApiAdapter.d.ts","sourceRoot":"","sources":["../../src/flows/MockWorkflowApiAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAKnB,MAAM,mBAAmB,CAAC;AA2L3B;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,IAAI,kBAAkB,CA0GjE"}
@@ -1,5 +1,5 @@
1
1
  import { ApiFetcher, Result } from '../../utils';
2
- import { AuthenticationError } from './errors';
2
+ import { AuthenticationError, RateLimitedError } from './errors';
3
3
  export type AuthTokens = {
4
4
  access_token: string;
5
5
  refresh_token: string;
@@ -33,6 +33,13 @@ export interface LoginAdapter {
33
33
  * and translating it to a code is the backend's job.
34
34
  */
35
35
  export declare function authenticationErrorFromBody(data: unknown): AuthenticationError | undefined;
36
+ /**
37
+ * Turn a `429` from the auth endpoints into a `RateLimitedError`, or
38
+ * `undefined` for any other status. `Retry-After` is read as delay-seconds; a
39
+ * missing or unparseable header falls back to a minute so the form can't stay
40
+ * blocked forever.
41
+ */
42
+ export declare function rateLimitErrorFromResponse(res: Pick<Response, "status" | "headers">): RateLimitedError | undefined;
36
43
  export declare function createMockLoginAdapter(opts?: {
37
44
  appUrl?: string;
38
45
  }): LoginAdapter;
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/services/login/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAA6B,MAAM,UAAU,CAAC;AAE1E,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,MAAM,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAE1B,YAAY,CACV,MAAM,EACF;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC/B;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAChB;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,GAC3B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAE/B,eAAe,CAAC,MAAM,EAAE;QACtB,UAAU,EAAE,UAAU,GAAG,eAAe,CAAC;QACzC,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,MAAM,CAAC;CACZ;AAQD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,OAAO,GACZ,mBAAmB,GAAG,SAAS,CAoBjC;AAED,wBAAgB,sBAAsB,CACpC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,YAAY,CAwBd;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C,YAAY,CAoGd"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/services/login/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,mBAAmB,EAEnB,gBAAgB,EACjB,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,UAAU,GAAG;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,iBAAiB,CAAC,MAAM,EAAE;QACxB,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IAE1B,YAAY,CACV,MAAM,EACF;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAC/B;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAChB;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,GAC3B,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IAE/B,eAAe,CAAC,MAAM,EAAE;QACtB,UAAU,EAAE,UAAU,GAAG,eAAe,CAAC;QACzC,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;KACd,GAAG,MAAM,CAAC;CACZ;AAQD;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,OAAO,GACZ,mBAAmB,GAAG,SAAS,CAoBjC;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,GAAG,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC,GACxC,gBAAgB,GAAG,SAAS,CAU9B;AAED,wBAAgB,sBAAsB,CACpC,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAA;CAAO,GAC7B,YAAY,CAwBd;AAED,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,UAAU,EACnB,IAAI,EAAE;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAC3C,YAAY,CA0Gd"}
@@ -6,6 +6,7 @@
6
6
  * which is what lets the adapter's code lookup be a plain table.
7
7
  */
8
8
  export declare enum AuthenticationErrorReason {
9
+ RateLimited = "rate-limited",
9
10
  Expired = "expired",
10
11
  IncorrectCode = "wrong-code",
11
12
  TooManyTries = "too-many-tries",
@@ -18,4 +19,9 @@ export declare class AuthenticationError extends Error {
18
19
  data: Record<string, unknown>;
19
20
  constructor(reason: AuthenticationErrorReason, data?: Record<string, unknown>);
20
21
  }
22
+ export declare class RateLimitedError extends AuthenticationError {
23
+ /** Epoch milliseconds before which the backend will refuse another login request. */
24
+ readonly retryAt: number;
25
+ constructor(retryAt: number);
26
+ }
21
27
  //# sourceMappingURL=errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/services/login/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,oBAAY,yBAAyB;IACnC,OAAO,YAAY;IACnB,aAAa,eAAe;IAC5B,YAAY,mBAAmB;IAC/B,OAAO,aAAa;IACpB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,MAAM,EAAE,yBAAyB,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAG5B,MAAM,EAAE,yBAAyB,EACjC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAMjC"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/services/login/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,oBAAY,yBAAyB;IACnC,WAAW,iBAAiB;IAC5B,OAAO,YAAY;IACnB,aAAa,eAAe;IAC5B,YAAY,mBAAmB;IAC/B,OAAO,aAAa;IACpB,OAAO,YAAY;IACnB,OAAO,YAAY;CACpB;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IAC5C,MAAM,EAAE,yBAAyB,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAG5B,MAAM,EAAE,yBAAyB,EACjC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAMjC;AAED,qBAAa,gBAAiB,SAAQ,mBAAmB;IACvD,qFAAqF;IACrF,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAEb,OAAO,EAAE,MAAM;CAI5B"}
@@ -21,6 +21,6 @@ export declare const LoginServiceContext: {
21
21
  __context__: LoginService;
22
22
  };
23
23
  export { loginService, createLoginService, type LoginServiceDependencies, type ErrorFormatter, } from './loginService';
24
- export { AuthenticationError, AuthenticationErrorReason } from './errors';
25
- export { type LoginAdapter, type AuthTokens, authenticationErrorFromBody, createApiLoginAdapter, createMockLoginAdapter, } from './adapter';
24
+ export { AuthenticationError, AuthenticationErrorReason, RateLimitedError, } from './errors';
25
+ export { type LoginAdapter, type AuthTokens, authenticationErrorFromBody, rateLimitErrorFromResponse, createApiLoginAdapter, createMockLoginAdapter, } from './adapter';
26
26
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/login/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,KAAK,YAAY,GAAG;IAClB,YAAY,EAAE;QACZ,KAAK,EAAE;YACL,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;SAC1E,CAAC;KACH,CAAC;IAEF,MAAM,EAAE;QACN,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;QAC5E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;KAC3D,CAAC;IAEF,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEnC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAElD,iBAAiB,CACf,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;IAEvC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,mBAAmB;;CACe,CAAC;AAEhD,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAE1E,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,2BAA2B,EAC3B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/login/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,OAAO,EAAE,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AAExD,KAAK,YAAY,GAAG;IAClB,YAAY,EAAE;QACZ,KAAK,EAAE;YACL,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;YAC1D,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;SAC1E,CAAC;KACH,CAAC;IAEF,MAAM,EAAE;QACN,KAAK,CAAC,QAAQ,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;QAC5E,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;KAC3D,CAAC;IAEF,eAAe,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC;IAEjC,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAEnC,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAElD,iBAAiB,CACf,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;IAEvC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;CAC3B,CAAC;AAEF,eAAO,MAAM,mBAAmB;;CACe,CAAC;AAEhD,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,GACpB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,2BAA2B,EAC3B,0BAA0B,EAC1B,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,WAAW,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"loginService.d.ts","sourceRoot":"","sources":["../../../src/services/login/loginService.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAKxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAE1E,UAAU,aAAa;IACrB,IAAI,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IACtE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AA0BD,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,KAAK;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,cAAc,CAAC;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,WAAW,EAAE,cAAc,CAAC;IAC5B,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;;;;;;;;;;;;;;;;EAmBA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,wBAAwB;;;;;;;;;;;;;;;;EAsNhE"}
1
+ {"version":3,"file":"loginService.d.ts","sourceRoot":"","sources":["../../../src/services/login/loginService.ts"],"names":[],"mappings":"AAEA,OAAO,EAA4B,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAKxE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EACL,mBAAmB,EACnB,yBAAyB,EAE1B,MAAM,UAAU,CAAC;AAElB,UAAU,aAAa;IACrB,IAAI,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI,CAAC;IACtE,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,CAAC;CAChB;AA0BD,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,OAAO,GAAG,mBAAmB,KAAK;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,yBAAyB,CAAC;CACpC,CAAC;AAEF,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,YAAY,CAAC;IACtB,WAAW,EAAE,cAAc,CAAC;IAC5B,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE,WAAW,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IACjC,WAAW,EAAE,cAAc,CAAC;IAC5B,SAAS,EAAE,MAAM,MAAM,CAAC;IACxB,MAAM,EAAE,aAAa,CAAC;IACtB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACtC;;;;;;;;;;;;;;;;EAmBA;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,wBAAwB;;;;;;;;;;;;;;;;EAkOhE"}
@@ -27,6 +27,11 @@ export type ErrorDetails = {
27
27
  * Values are `AuthenticationErrorReason` members.
28
28
  */
29
29
  reason?: string;
30
+ /**
31
+ * Epoch milliseconds before which the backend will refuse another login
32
+ * request. Present when `reason` is rate-limited.
33
+ */
34
+ retryAt?: number;
30
35
  };
31
36
  export type Image = {
32
37
  url: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,CAAC,CAAC,GAAG,OAAO,IAAI;IACjC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,IAAI;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAEF,MAAM,MAAM,MAAM,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEpE,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AACjF,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/utils/index.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,OAAO,CAAC,CAAC,GAAG,OAAO,IAAI;IACjC,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,IAAI;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,CAAC,CAAC;CACT,CAAC;AAEF,MAAM,MAAM,MAAM,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,GAAG,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEpE,MAAM,WAAW,QAAQ;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa,CAAC,CAAC;IAC9B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;;GAIG;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AACjF,cAAc,UAAU,CAAC"}
@@ -3,4 +3,5 @@ export * from './signals';
3
3
  export * from './target';
4
4
  export * from './context';
5
5
  export * from './bridge';
6
+ export * from './useLoginCooldown';
6
7
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/vue/main.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/vue/main.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,UAAU,CAAC;AACzB,cAAc,oBAAoB,CAAC"}
@@ -0,0 +1,7 @@
1
+ import { MaybeRefOrGetter } from 'vue';
2
+ export declare function useLoginCooldown(deadline: MaybeRefOrGetter<number | null>): {
3
+ remainingSeconds: import('vue').ComputedRef<number>;
4
+ remainingTime: import('vue').ComputedRef<string>;
5
+ isBlocked: import('vue').ComputedRef<boolean>;
6
+ };
7
+ //# sourceMappingURL=useLoginCooldown.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLoginCooldown.d.ts","sourceRoot":"","sources":["../../src/vue/useLoginCooldown.ts"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,gBAAgB,EACtB,MAAM,KAAK,CAAC;AAEb,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,MAAM,GAAG,IAAI,CAAC;;;;EAsCzE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useLoginCooldown.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLoginCooldown.test.d.ts","sourceRoot":"","sources":["../../src/vue/useLoginCooldown.test.ts"],"names":[],"mappings":""}
package/dist/vue.js CHANGED
@@ -346,6 +346,39 @@ function useBridge(options) {
346
346
  });
347
347
  return { isEditorMode: isEditorMode.value };
348
348
  }
349
+ function useLoginCooldown(deadline) {
350
+ const retryAt = computed(() => toValue(deadline) ?? 0);
351
+ const now = ref(Date.now());
352
+ const remainingSeconds = computed(
353
+ () => Math.max(0, Math.ceil((retryAt.value - now.value) / 1e3))
354
+ );
355
+ const remainingTime = computed(() => {
356
+ const minutes = Math.floor(remainingSeconds.value / 60);
357
+ const seconds = remainingSeconds.value % 60;
358
+ return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`;
359
+ });
360
+ const isBlocked = computed(() => remainingSeconds.value > 0);
361
+ let timer;
362
+ function stop() {
363
+ clearInterval(timer);
364
+ timer = void 0;
365
+ }
366
+ watch(
367
+ retryAt,
368
+ (value) => {
369
+ stop();
370
+ now.value = Date.now();
371
+ if (value <= now.value) return;
372
+ timer = setInterval(() => {
373
+ now.value = Date.now();
374
+ if (!isBlocked.value) stop();
375
+ }, 250);
376
+ },
377
+ { immediate: true, flush: "sync" }
378
+ );
379
+ onScopeDispose(stop);
380
+ return { remainingSeconds, remainingTime, isBlocked };
381
+ }
349
382
  export {
350
383
  ContextProvider,
351
384
  createVueCeRenderer,
@@ -357,6 +390,7 @@ export {
357
390
  useBlockType,
358
391
  useBridge,
359
392
  useContext,
393
+ useLoginCooldown,
360
394
  useProvideContext,
361
395
  useProvideContextProvider,
362
396
  useRegisterTranslationKeys,
@@ -1 +1 @@
1
- {"version":3,"file":"InlineText.d.ts","sourceRoot":"","sources":["../../../src/web-components/runtime/InlineText.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,OAAO,CAAC,OAAO,CAAC,CAAa;IAE7B,iBAAiB;IAsCjB,oBAAoB;CAIrB"}
1
+ {"version":3,"file":"InlineText.d.ts","sourceRoot":"","sources":["../../../src/web-components/runtime/InlineText.ts"],"names":[],"mappings":"AAIA,MAAM,CAAC,OAAO,OAAO,UAAW,SAAQ,WAAW;IACjD,OAAO,CAAC,OAAO,CAAC,CAAa;IAE7B,iBAAiB;IAuCjB,oBAAoB;CAIrB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=InlineText.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InlineText.test.d.ts","sourceRoot":"","sources":["../../../src/web-components/runtime/InlineText.test.ts"],"names":[],"mappings":""}
@@ -12,6 +12,7 @@ class InlineText extends HTMLElement {
12
12
  connectedCallback() {
13
13
  if (!this.style.display) this.style.display = "inline-block";
14
14
  if (!this.style.overflowWrap) this.style.overflowWrap = "anywhere";
15
+ if (!this.style.whiteSpace) this.style.whiteSpace = "pre-wrap";
15
16
  const key = this.getAttribute("prop");
16
17
  if (!key) return;
17
18
  let blockEl = this.closest("[data-block-type]");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@juo/blocks",
3
3
  "private": false,
4
- "version": "1.20.0",
4
+ "version": "1.21.0",
5
5
  "type": "module",
6
6
  "files": [
7
7
  "dist"