@nauticana/sail 1.1.14 → 1.1.16

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/LICENSE CHANGED
@@ -186,7 +186,7 @@
186
186
  same "printed page" as the copyright notice for easier
187
187
  identification within third-party archives.
188
188
 
189
- Copyright [yyyy] [name of copyright owner]
189
+ Copyright 2026 Mustafa Karli
190
190
 
191
191
  Licensed under the Apache License, Version 2.0 (the "License");
192
192
  you may not use this file except in compliance with the License.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nauticana/sail",
3
- "version": "1.1.14",
3
+ "version": "1.1.16",
4
4
  "description": "Metadata-driven Angular component library for CRUD admin frontends backed by the keel Go backend. Tables, forms, navigation, OTP/2FA/social auth, billing, payout onboarding.",
5
5
  "main": "src/index.ts",
6
6
  "scripts": {
@@ -15,11 +15,36 @@ function asString(value: unknown): string | undefined {
15
15
  return typeof value === 'string' && value ? value : undefined;
16
16
  }
17
17
 
18
+ function statusOf(err: unknown): number | undefined {
19
+ if (!err || typeof err !== 'object') return undefined;
20
+ const s = (err as { status?: unknown }).status;
21
+ return typeof s === 'number' ? s : undefined;
22
+ }
23
+
24
+ /**
25
+ * True when the browser never received a readable response — the network was
26
+ * down, a CORS rule blocked it, or the request was aborted. Angular collapses
27
+ * all three to status 0 with a ProgressEvent body, so this is the only signal
28
+ * available. Use it to offer a retry rather than reporting a backend rejection.
29
+ */
30
+ export function isTransportFailure(err: unknown): boolean {
31
+ return statusOf(err) === 0;
32
+ }
33
+
18
34
  /**
19
35
  * Human-readable message from any sail error shape — SailApiError (BackendService),
20
36
  * raw HttpErrorResponse (direct HttpClient calls), or plain Error.
37
+ *
38
+ * keel answers every rejection with an RFC 7807 body, so an error carrying no
39
+ * `detail` did not come from the API: either nothing reached it (status 0) or
40
+ * something in front of it replied with a body we cannot read. Both used to
41
+ * collapse into the caller's fallback, which reads as a backend rejection and
42
+ * hides the real fault; they are now reported distinctly.
21
43
  */
22
44
  export function errorDetail(err: unknown, fallback: string): string {
45
+ if (isTransportFailure(err)) {
46
+ return 'Cannot reach the server — the network or a CORS rule blocked the response.';
47
+ }
23
48
  const problem = problemOf(err);
24
49
  if (problem) {
25
50
  const message = asString(problem.detail) ?? asString(problem.title) ?? asString(problem.message);
@@ -28,7 +53,8 @@ export function errorDetail(err: unknown, fallback: string): string {
28
53
  if (err instanceof Error && err.message) {
29
54
  return err.message;
30
55
  }
31
- return fallback;
56
+ const status = statusOf(err);
57
+ return status ? `${fallback} (HTTP ${status})` : fallback;
32
58
  }
33
59
 
34
60
  /** Stable machine-readable discriminator from a keel RFC 7807 response. */