@antelopejs/dms-frontend 0.7.0 → 0.7.1

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/help.js CHANGED
@@ -63,7 +63,7 @@ exports.ENVIRONMENT_VARIABLES = [
63
63
  ],
64
64
  [
65
65
  "DMS_BACKEND_WAKE_BUDGET_MS",
66
- "How long a GET or HEAD waits out a backend's cold-start 503, in ms (default: 30000; 0: never)",
66
+ "How long a GET or HEAD waits out a waking or starting backend's 503, in ms (default: 30000; 0: never)",
67
67
  ],
68
68
  ["DMS_CLIENT_BASE_URL", "Public frontend URL used in links and e-mails"],
69
69
  ["NO_COLOR", "Same as --no-color"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@antelopejs/dms-frontend",
3
- "version": "0.7.0",
3
+ "version": "0.7.1",
4
4
  "description": "Frontend-agnostic loader for AntelopeJS DMS, shipping the Vue 3 renderer (Vite, Inertia, SSR)",
5
5
  "keywords": [
6
6
  "antelope",
@@ -4,7 +4,7 @@
4
4
  // allows.
5
5
 
6
6
  import { GENERIC_ERROR_MESSAGE } from "./auth/backend.mjs";
7
- import { isColdStart } from "./backend-wake.mjs";
7
+ import { isWaitable } from "./backend-wake.mjs";
8
8
  import { isFrontendVisit, redirectFrontendVisit } from "./inertia.mjs";
9
9
 
10
10
  // What a JSON caller gets when the backend fails: the generic i18n key of the
@@ -47,7 +47,7 @@ async function refusalCode(response) {
47
47
 
48
48
  /** The error for a failed backend response, with its typed code if any. */
49
49
  export async function backendResponseError(response) {
50
- const retryAfter = isColdStart(response)
50
+ const retryAfter = isWaitable(response)
51
51
  ? (response.headers.get("retry-after") ?? undefined)
52
52
  : undefined;
53
53
  return new BackendResponseError(
@@ -1,14 +1,14 @@
1
- // Waiting out a backend the platform is waking from sleep.
1
+ // Waiting out a backend that is waking from sleep or still starting.
2
2
  //
3
3
  // A platform that scales the backend to zero answers the first requests with
4
4
  // a 503 while it starts a replica, marked with the cold-start header and a
5
- // Retry-After in whole seconds: all three make the signal, and a 503 missing
6
- // one of them goes out as it came. The header is a reserved resume
7
- // convention, not a proof of where the answer came from: a backend may set it
8
- // too, and then gets the same bounded wait. Only a GET or a HEAD is repeated;
9
- // a POST, PATCH or DELETE is sent once.
5
+ // Retry-After in whole seconds; a DMS backend that listens before it is ready
6
+ // answers 503 with a Retry-After too, without the header. A 503 with a
7
+ // Retry-After in whole seconds is the signal, with or without the header; a
8
+ // 503 without one goes out as it came. Only a GET or a HEAD is repeated; a
9
+ // POST, PATCH or DELETE is sent once.
10
10
  //
11
- // The wait starts with the first cold-start answer and lasts at most the
11
+ // The wait starts with the first waitable answer and lasts at most the
12
12
  // budget, the repeated requests included: one still waiting for its answer
13
13
  // when the budget runs out is aborted, and no request starts after it. It
14
14
  // ends at once when the client leaves.
@@ -18,7 +18,7 @@
18
18
 
19
19
  import { setTimeout as sleep } from "node:timers/promises";
20
20
 
21
- /** Header marking a 503 as a cold start to wait out. */
21
+ /** Header a platform marks the 503 of a backend it is waking with. */
22
22
  export const COLD_START_HEADER = "x-antelope-cold-start";
23
23
  const COLD_START_VALUE = "1";
24
24
  const UNAVAILABLE = 503;
@@ -34,12 +34,14 @@ const MS_PER_SECOND = 1_000;
34
34
  const WHOLE_SECONDS = /^\d+$/;
35
35
  const WHOLE_MILLISECONDS = /^\d+$/;
36
36
 
37
- /** Whether `response` is a 503 marked as a cold start, with its Retry-After. */
38
- export function isColdStart(response) {
37
+ /**
38
+ * Whether `response` is a 503 asking to come back after a Retry-After in
39
+ * whole seconds: a platform waking the backend (with the cold-start header)
40
+ * or a backend still starting.
41
+ */
42
+ export function isWaitable(response) {
39
43
  return (
40
- response.status === UNAVAILABLE &&
41
- response.headers.get(COLD_START_HEADER) === COLD_START_VALUE &&
42
- WHOLE_SECONDS.test(retryAfter(response))
44
+ response.status === UNAVAILABLE && WHOLE_SECONDS.test(retryAfter(response))
43
45
  );
44
46
  }
45
47
 
@@ -57,7 +59,7 @@ export function wakeBudgetMs(env = process.env) {
57
59
  return WHOLE_MILLISECONDS.test(raw) ? Number(raw) : DEFAULT_BUDGET_MS;
58
60
  }
59
61
 
60
- /** The pause a cold-start answer's Retry-After asks for, kept between bounds. */
62
+ /** The pause a waitable answer's Retry-After asks for, kept between bounds. */
61
63
  function retryDelayMs(response) {
62
64
  return Math.min(
63
65
  MAX_DELAY_MS,
@@ -66,17 +68,15 @@ function retryDelayMs(response) {
66
68
  }
67
69
 
68
70
  /**
69
- * What the client gets once the budget is spent: the last cold-start answer's
70
- * status and headers, without the body its pause already discarded.
71
+ * What the client gets once the budget is spent: the last waitable answer's
72
+ * status, Retry-After and cold-start header if it had one, without the body
73
+ * its pause already discarded.
71
74
  */
72
75
  function expired(cold) {
73
- return new Response(null, {
74
- status: UNAVAILABLE,
75
- headers: {
76
- [COLD_START_HEADER]: COLD_START_VALUE,
77
- "retry-after": retryAfter(cold),
78
- },
79
- });
76
+ const headers = { "retry-after": retryAfter(cold) };
77
+ if (cold.headers.get(COLD_START_HEADER) === COLD_START_VALUE)
78
+ headers[COLD_START_HEADER] = COLD_START_VALUE;
79
+ return new Response(null, { status: UNAVAILABLE, headers });
80
80
  }
81
81
 
82
82
  /**
@@ -102,9 +102,9 @@ async function repeat(url, init, deadline, now) {
102
102
  }
103
103
 
104
104
  /**
105
- * `fetch`, waiting out cold-start answers to a GET or a HEAD until `budgetMs`
105
+ * `fetch`, waiting out waitable 503s to a GET or a HEAD until `budgetMs`
106
106
  * from the first of them is spent or MAX_REPEATS repeats are sent. Then the
107
- * caller gets a cold-start 503 with its `Retry-After` to relay. The client
107
+ * caller gets the 503 with its `Retry-After` to relay. The client
108
108
  * leaving, through `init.signal`, ends the wait with its AbortError.
109
109
  */
110
110
  export async function fetchWaking(
@@ -116,7 +116,7 @@ export async function fetchWaking(
116
116
  let response = await fetch(url, init);
117
117
  if (!RETRYABLE_METHODS.has(method)) return response;
118
118
  const deadline = now() + budgetMs;
119
- for (let repeats = 0; isColdStart(response); repeats += 1) {
119
+ for (let repeats = 0; isWaitable(response); repeats += 1) {
120
120
  const cold = response;
121
121
  const delay = retryDelayMs(cold);
122
122
  if (repeats >= MAX_REPEATS || now() + delay > deadline) return cold;