@msg91comm/m91 0.0.0-stage → 1.2.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.
Files changed (100) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/LICENSE +21 -0
  3. package/README.md +647 -2
  4. package/dist/src/client.d.ts +165 -0
  5. package/dist/src/client.d.ts.map +1 -0
  6. package/dist/src/client.js +221 -0
  7. package/dist/src/client.js.map +1 -0
  8. package/dist/src/exceptions.d.ts +114 -0
  9. package/dist/src/exceptions.d.ts.map +1 -0
  10. package/dist/src/exceptions.js +199 -0
  11. package/dist/src/exceptions.js.map +1 -0
  12. package/dist/src/http.d.ts +71 -0
  13. package/dist/src/http.d.ts.map +1 -0
  14. package/dist/src/http.js +150 -0
  15. package/dist/src/http.js.map +1 -0
  16. package/dist/src/index.d.ts +49 -0
  17. package/dist/src/index.d.ts.map +1 -0
  18. package/dist/src/index.js +45 -0
  19. package/dist/src/index.js.map +1 -0
  20. package/dist/src/integrations/claude-agent-sdk.d.ts +63 -0
  21. package/dist/src/integrations/claude-agent-sdk.d.ts.map +1 -0
  22. package/dist/src/integrations/claude-agent-sdk.js +109 -0
  23. package/dist/src/integrations/claude-agent-sdk.js.map +1 -0
  24. package/dist/src/integrations/google-adk.d.ts +47 -0
  25. package/dist/src/integrations/google-adk.d.ts.map +1 -0
  26. package/dist/src/integrations/google-adk.js +84 -0
  27. package/dist/src/integrations/google-adk.js.map +1 -0
  28. package/dist/src/integrations/index.d.ts +26 -0
  29. package/dist/src/integrations/index.d.ts.map +1 -0
  30. package/dist/src/integrations/index.js +25 -0
  31. package/dist/src/integrations/index.js.map +1 -0
  32. package/dist/src/integrations/langchain.d.ts +94 -0
  33. package/dist/src/integrations/langchain.d.ts.map +1 -0
  34. package/dist/src/integrations/langchain.js +129 -0
  35. package/dist/src/integrations/langchain.js.map +1 -0
  36. package/dist/src/integrations/langgraph.d.ts +79 -0
  37. package/dist/src/integrations/langgraph.d.ts.map +1 -0
  38. package/dist/src/integrations/langgraph.js +114 -0
  39. package/dist/src/integrations/langgraph.js.map +1 -0
  40. package/dist/src/integrations/mastra.d.ts +75 -0
  41. package/dist/src/integrations/mastra.d.ts.map +1 -0
  42. package/dist/src/integrations/mastra.js +109 -0
  43. package/dist/src/integrations/mastra.js.map +1 -0
  44. package/dist/src/integrations/openai-agents.d.ts +61 -0
  45. package/dist/src/integrations/openai-agents.d.ts.map +1 -0
  46. package/dist/src/integrations/openai-agents.js +127 -0
  47. package/dist/src/integrations/openai-agents.js.map +1 -0
  48. package/dist/src/integrations/specs.d.ts +173 -0
  49. package/dist/src/integrations/specs.d.ts.map +1 -0
  50. package/dist/src/integrations/specs.js +217 -0
  51. package/dist/src/integrations/specs.js.map +1 -0
  52. package/dist/src/integrations/vercel-ai.d.ts +74 -0
  53. package/dist/src/integrations/vercel-ai.d.ts.map +1 -0
  54. package/dist/src/integrations/vercel-ai.js +102 -0
  55. package/dist/src/integrations/vercel-ai.js.map +1 -0
  56. package/dist/src/integrations/zod-shapes.d.ts +19 -0
  57. package/dist/src/integrations/zod-shapes.d.ts.map +1 -0
  58. package/dist/src/integrations/zod-shapes.js +44 -0
  59. package/dist/src/integrations/zod-shapes.js.map +1 -0
  60. package/dist/src/models.d.ts +274 -0
  61. package/dist/src/models.d.ts.map +1 -0
  62. package/dist/src/models.js +383 -0
  63. package/dist/src/models.js.map +1 -0
  64. package/dist/src/platform.d.ts +176 -0
  65. package/dist/src/platform.d.ts.map +1 -0
  66. package/dist/src/platform.js +286 -0
  67. package/dist/src/platform.js.map +1 -0
  68. package/dist/src/url-builder.d.ts +65 -0
  69. package/dist/src/url-builder.d.ts.map +1 -0
  70. package/dist/src/url-builder.js +167 -0
  71. package/dist/src/url-builder.js.map +1 -0
  72. package/examples/approval-gate.ts +35 -0
  73. package/examples/basic-alert.ts +53 -0
  74. package/examples/claude-agent-sdk.ts +23 -0
  75. package/examples/deep-agents.ts +31 -0
  76. package/examples/google-adk.ts +39 -0
  77. package/examples/langchain-agent.ts +65 -0
  78. package/examples/langgraph-hitl.ts +70 -0
  79. package/examples/mastra.ts +41 -0
  80. package/examples/openai-agents.ts +33 -0
  81. package/examples/vercel-ai-agent.ts +34 -0
  82. package/examples/webhook-urls.ts +44 -0
  83. package/package.json +135 -4
  84. package/src/client.ts +324 -0
  85. package/src/exceptions.ts +229 -0
  86. package/src/http.ts +174 -0
  87. package/src/index.ts +91 -0
  88. package/src/integrations/claude-agent-sdk.ts +158 -0
  89. package/src/integrations/google-adk.ts +122 -0
  90. package/src/integrations/index.ts +27 -0
  91. package/src/integrations/langchain.ts +207 -0
  92. package/src/integrations/langgraph.ts +142 -0
  93. package/src/integrations/mastra.ts +178 -0
  94. package/src/integrations/openai-agents.ts +173 -0
  95. package/src/integrations/specs.ts +328 -0
  96. package/src/integrations/vercel-ai.ts +151 -0
  97. package/src/integrations/zod-shapes.ts +55 -0
  98. package/src/models.ts +660 -0
  99. package/src/platform.ts +343 -0
  100. package/src/url-builder.ts +225 -0
@@ -0,0 +1,165 @@
1
+ /**
2
+ * The send-link client: raise an alert, read what was decided, close it.
3
+ *
4
+ * A send link is a credential in a URL. It goes in a Grafana webhook field, a
5
+ * cron, a CI job or an agent's environment, and it needs no header, no account
6
+ * and no library — which is why this client is a convenience over one URL
7
+ * rather than the only way in.
8
+ *
9
+ * Two things it does that a bare `fetch` does not, and both are why it exists:
10
+ *
11
+ * - **It checks that somebody was actually reached.** A 201 means the alert was
12
+ * filed, not that a phone rang.
13
+ * - **It reads the decision, not the responses.** Only a SHARED response decides.
14
+ */
15
+ import { type TransportOptions } from './http.js';
16
+ import { type AlertDetail, type AlertResponse, type ChannelInfo, type Decision, type ResponseOption, type Severity } from './models.js';
17
+ /**
18
+ * Below this, polling is a busy loop against a rate-limited endpoint and buys
19
+ * nothing: a human waking up and reaching for a phone is not a sub-second event.
20
+ */
21
+ export declare const MIN_POLL_INTERVAL_MS = 2000;
22
+ export interface SendOptions {
23
+ description?: string;
24
+ /**
25
+ * LOW and MEDIUM deliver at alarm volume through Do Not Disturb. **HIGH and
26
+ * CRITICAL also place a voice call** if nobody answers in 45 seconds, so they
27
+ * are never the default — "I didn't specify" must not mean "ring everyone".
28
+ */
29
+ severity?: Severity;
30
+ /**
31
+ * Your own stable id for *the condition*, not for this occurrence. The same
32
+ * disk filling up is the same `customId` every time. Built from a timestamp
33
+ * or a UUID it defeats deduplication — a check running every minute raises
34
+ * sixty alerts an hour — and leaves nothing stable to close by.
35
+ */
36
+ customId?: string;
37
+ /**
38
+ * Left false, the alert stays open until somebody closes it, even after a
39
+ * SHARED response claims it. Set true when the first person taking it on is
40
+ * genuinely the end of the matter.
41
+ */
42
+ isAutoClose?: boolean;
43
+ responseOptions?: Array<ResponseOption | string>;
44
+ /**
45
+ * Whether the free-text "Something else" reply is offered. Tri-state: leave
46
+ * it unset and the server's default (on) stands.
47
+ */
48
+ other?: boolean;
49
+ /** https links to files the alert is about. URLs, never bytes. */
50
+ attachments?: string[];
51
+ /** Where the outcome is POSTed when somebody answers. */
52
+ callbackUrl?: string;
53
+ }
54
+ export interface WaitOptions {
55
+ timeoutMs?: number;
56
+ pollIntervalMs?: number;
57
+ throwOnTimeout?: boolean;
58
+ }
59
+ export interface ApprovalOptions {
60
+ customId: string;
61
+ description?: string;
62
+ severity?: Severity;
63
+ approveLabel?: string;
64
+ rejectLabel?: string;
65
+ timeoutMs?: number;
66
+ pollIntervalMs?: number;
67
+ /** A timeout returns false. There is deliberately no option making it true. */
68
+ timeoutMeansReject?: boolean;
69
+ }
70
+ export interface ClientOptions extends TransportOptions {
71
+ /**
72
+ * Throw `M91DeliveryError` when a send reaches nobody. On by default, because
73
+ * the silent version of this failure is an alert nobody ever hears about.
74
+ */
75
+ throwOnZeroDelivery?: boolean;
76
+ }
77
+ /** Alerts one channel, through one send link. */
78
+ export declare class M91Client {
79
+ readonly sendLink: string;
80
+ readonly throwOnZeroDelivery: boolean;
81
+ private readonly http;
82
+ private readonly sleep;
83
+ private readonly now;
84
+ constructor(sendLink: string, options?: ClientOptions);
85
+ /** Redacted, always: a token in a log, a trace or a crash report can wake a team. */
86
+ toString(): string;
87
+ private url;
88
+ /**
89
+ * Check the link works, and see which channel it points at.
90
+ *
91
+ * **Raises no alert.** Safe on startup, in a health check, or while somebody
92
+ * is pasting the URL into a settings page — which is the point: a mistyped
93
+ * link otherwise shows "connected" and only reveals itself days later, when
94
+ * an alert silently fails to fire.
95
+ */
96
+ verify(): Promise<ChannelInfo>;
97
+ /** Raise an alert on this channel. */
98
+ send(title: string, options?: SendOptions): Promise<AlertResponse>;
99
+ /**
100
+ * Read one alert back. `null` when there is no such alert on this channel.
101
+ *
102
+ * The id is **either** the `customId` you invented or the `_id` the send
103
+ * returned — a caller knows one of them and should not have to say which.
104
+ */
105
+ getAlert(customIdOrAlertId: string): Promise<AlertDetail | null>;
106
+ /**
107
+ * Wait until somebody takes the alert on, or the clock runs out.
108
+ *
109
+ * Resolves with the SHARED response that claimed it. **PERSONAL responses
110
+ * never end this wait** — a "Seen" is an acknowledgement, not an answer, and
111
+ * treating it as one is the mistake `decision` exists to prevent.
112
+ *
113
+ * Stops early and resolves `null` if the alert is closed without ever being
114
+ * claimed: there is nothing left to wait for.
115
+ */
116
+ waitForDecision(customId: string, options?: WaitOptions): Promise<Decision | null>;
117
+ /**
118
+ * Close an alert this link raised. Always `true` when there is nothing open.
119
+ *
120
+ * A monitor that sees the problem heal closes it itself rather than waiting
121
+ * for a person to wake up and notice.
122
+ *
123
+ * **404 and 403 are success.** "No such alert" and "not yours to close" both
124
+ * mean the alert is not open on this channel, which is the state the caller
125
+ * asked for. Anything else would make every close a branch, and every retry
126
+ * after a dropped response a false failure.
127
+ */
128
+ close(options: {
129
+ customId?: string;
130
+ alertId?: string;
131
+ }): Promise<boolean>;
132
+ /**
133
+ * Ask a human, on their phone, and wait for the answer. `true` = approved.
134
+ *
135
+ * The shape of every human-in-the-loop gate: raise the question, block, act
136
+ * on the answer, close the alert either way. Severity defaults to HIGH here —
137
+ * unlike `send` — because something is *waiting* on the answer, so a phone
138
+ * call after 45 seconds of silence is the point rather than an escalation.
139
+ *
140
+ * Both labels are SHARED: approving and rejecting are each a final answer
141
+ * that settles the question for everybody, and neither is an "I saw it".
142
+ *
143
+ * On timeout with `timeoutMeansReject` (the default) it resolves false —
144
+ * nobody answered, so nothing consequential happens. Set it false to get
145
+ * `M91TimeoutError` instead. There is deliberately no way to make a timeout
146
+ * mean approved.
147
+ */
148
+ requestApproval(title: string, options: ApprovalOptions): Promise<boolean>;
149
+ }
150
+ /**
151
+ * A 2xx that woke nobody is a failure, and only the caller can decide what to do.
152
+ *
153
+ * `delivered` counts phones actually sent to, so this fires when a channel's
154
+ * devices are all stale or its members have not installed the app — a case that
155
+ * answers 201 and looks exactly like success.
156
+ */
157
+ export declare function checkDelivery(alert: AlertResponse, enabled: boolean): void;
158
+ /**
159
+ * Whether one poll ends the wait. Pure, so the rule lives in one place.
160
+ */
161
+ export declare function waitVerdict(detail: AlertDetail | null): {
162
+ stop: boolean;
163
+ decision: Decision | null;
164
+ };
165
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,221 @@
1
+ /**
2
+ * The send-link client: raise an alert, read what was decided, close it.
3
+ *
4
+ * A send link is a credential in a URL. It goes in a Grafana webhook field, a
5
+ * cron, a CI job or an agent's environment, and it needs no header, no account
6
+ * and no library — which is why this client is a convenience over one URL
7
+ * rather than the only way in.
8
+ *
9
+ * Two things it does that a bare `fetch` does not, and both are why it exists:
10
+ *
11
+ * - **It checks that somebody was actually reached.** A 201 means the alert was
12
+ * filed, not that a phone rang.
13
+ * - **It reads the decision, not the responses.** Only a SHARED response decides.
14
+ */
15
+ import { M91DeliveryError, M91ForbiddenError, M91NotFoundError, M91TimeoutError, M91ValidationError, } from './exceptions.js';
16
+ import { Transport, redact } from './http.js';
17
+ import { buildAlertPayload, isApproved, parseAlertDetail, parseAlertResponse, parseChannelInfo, } from './models.js';
18
+ import { normalizeSendLink } from './url-builder.js';
19
+ /**
20
+ * Below this, polling is a busy loop against a rate-limited endpoint and buys
21
+ * nothing: a human waking up and reaching for a phone is not a sub-second event.
22
+ */
23
+ export const MIN_POLL_INTERVAL_MS = 2000;
24
+ /** Alerts one channel, through one send link. */
25
+ export class M91Client {
26
+ sendLink;
27
+ throwOnZeroDelivery;
28
+ http;
29
+ sleep;
30
+ now;
31
+ constructor(sendLink, options = {}) {
32
+ this.sendLink = normalizeSendLink(sendLink);
33
+ this.throwOnZeroDelivery = options.throwOnZeroDelivery !== false;
34
+ this.http = new Transport(options);
35
+ this.sleep = options.sleepImpl ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
36
+ this.now = () => Date.now();
37
+ }
38
+ /** Redacted, always: a token in a log, a trace or a crash report can wake a team. */
39
+ toString() {
40
+ return `M91Client(${redact(this.sendLink)})`;
41
+ }
42
+ url(suffix = '') {
43
+ return this.sendLink + suffix;
44
+ }
45
+ /**
46
+ * Check the link works, and see which channel it points at.
47
+ *
48
+ * **Raises no alert.** Safe on startup, in a health check, or while somebody
49
+ * is pasting the URL into a settings page — which is the point: a mistyped
50
+ * link otherwise shows "connected" and only reveals itself days later, when
51
+ * an alert silently fails to fire.
52
+ */
53
+ async verify() {
54
+ return parseChannelInfo(await this.http.request({ method: 'GET', url: this.url() }));
55
+ }
56
+ /** Raise an alert on this channel. */
57
+ async send(title, options = {}) {
58
+ const payload = buildAlertPayload(title, options);
59
+ const alert = parseAlertResponse(await this.http.request({ method: 'POST', url: this.url(), body: payload }));
60
+ checkDelivery(alert, this.throwOnZeroDelivery);
61
+ return alert;
62
+ }
63
+ /**
64
+ * Read one alert back. `null` when there is no such alert on this channel.
65
+ *
66
+ * The id is **either** the `customId` you invented or the `_id` the send
67
+ * returned — a caller knows one of them and should not have to say which.
68
+ */
69
+ async getAlert(customIdOrAlertId) {
70
+ try {
71
+ const raw = await this.http.request({
72
+ method: 'GET',
73
+ url: this.url(`/alerts/${encodeURIComponent(String(customIdOrAlertId))}`),
74
+ });
75
+ return parseAlertDetail(raw);
76
+ }
77
+ catch (error) {
78
+ if (error instanceof M91NotFoundError)
79
+ return null;
80
+ throw error;
81
+ }
82
+ }
83
+ /**
84
+ * Wait until somebody takes the alert on, or the clock runs out.
85
+ *
86
+ * Resolves with the SHARED response that claimed it. **PERSONAL responses
87
+ * never end this wait** — a "Seen" is an acknowledgement, not an answer, and
88
+ * treating it as one is the mistake `decision` exists to prevent.
89
+ *
90
+ * Stops early and resolves `null` if the alert is closed without ever being
91
+ * claimed: there is nothing left to wait for.
92
+ */
93
+ async waitForDecision(customId, options = {}) {
94
+ const timeoutMs = options.timeoutMs ?? 300_000;
95
+ const requested = options.pollIntervalMs ?? 5000;
96
+ if (requested < MIN_POLL_INTERVAL_MS) {
97
+ console.warn(`[m91] pollIntervalMs=${requested} is below ${MIN_POLL_INTERVAL_MS}. The send link is ` +
98
+ `rate limited to 60 requests a minute and a person cannot answer that fast; ` +
99
+ `using ${MIN_POLL_INTERVAL_MS}ms instead.`);
100
+ }
101
+ const interval = Math.max(requested, MIN_POLL_INTERVAL_MS);
102
+ const deadline = this.now() + timeoutMs;
103
+ for (;;) {
104
+ const verdict = waitVerdict(await this.getAlert(customId));
105
+ if (verdict.stop)
106
+ return verdict.decision;
107
+ const remaining = deadline - this.now();
108
+ if (remaining <= 0) {
109
+ if (options.throwOnTimeout)
110
+ throw new M91TimeoutError(customId, timeoutMs);
111
+ return null;
112
+ }
113
+ await this.sleep(Math.min(interval, remaining));
114
+ }
115
+ }
116
+ /**
117
+ * Close an alert this link raised. Always `true` when there is nothing open.
118
+ *
119
+ * A monitor that sees the problem heal closes it itself rather than waiting
120
+ * for a person to wake up and notice.
121
+ *
122
+ * **404 and 403 are success.** "No such alert" and "not yours to close" both
123
+ * mean the alert is not open on this channel, which is the state the caller
124
+ * asked for. Anything else would make every close a branch, and every retry
125
+ * after a dropped response a false failure.
126
+ */
127
+ async close(options) {
128
+ const { customId, alertId } = options ?? {};
129
+ if (Boolean(customId) === Boolean(alertId)) {
130
+ throw new M91ValidationError(`close() needs exactly one of customId or alertId — got ${customId ? 'both' : 'neither'}`);
131
+ }
132
+ try {
133
+ await this.http.request({
134
+ method: 'POST',
135
+ url: this.url('/close'),
136
+ body: customId ? { customId } : { alertId },
137
+ });
138
+ }
139
+ catch (error) {
140
+ if (error instanceof M91NotFoundError || error instanceof M91ForbiddenError)
141
+ return true;
142
+ throw error;
143
+ }
144
+ return true;
145
+ }
146
+ /**
147
+ * Ask a human, on their phone, and wait for the answer. `true` = approved.
148
+ *
149
+ * The shape of every human-in-the-loop gate: raise the question, block, act
150
+ * on the answer, close the alert either way. Severity defaults to HIGH here —
151
+ * unlike `send` — because something is *waiting* on the answer, so a phone
152
+ * call after 45 seconds of silence is the point rather than an escalation.
153
+ *
154
+ * Both labels are SHARED: approving and rejecting are each a final answer
155
+ * that settles the question for everybody, and neither is an "I saw it".
156
+ *
157
+ * On timeout with `timeoutMeansReject` (the default) it resolves false —
158
+ * nobody answered, so nothing consequential happens. Set it false to get
159
+ * `M91TimeoutError` instead. There is deliberately no way to make a timeout
160
+ * mean approved.
161
+ */
162
+ async requestApproval(title, options) {
163
+ const { customId, description, severity = 'HIGH', approveLabel = 'Approve', rejectLabel = 'Reject', timeoutMs = 300_000, pollIntervalMs = 5000, timeoutMeansReject = true, } = options;
164
+ await this.send(title, {
165
+ description,
166
+ severity,
167
+ customId,
168
+ isAutoClose: false,
169
+ responseOptions: [
170
+ { label: approveLabel, effect: 'SHARED' },
171
+ { label: rejectLabel, effect: 'SHARED' },
172
+ ],
173
+ });
174
+ try {
175
+ const decision = await this.waitForDecision(customId, { timeoutMs, pollIntervalMs });
176
+ if (decision === null) {
177
+ if (!timeoutMeansReject)
178
+ throw new M91TimeoutError(customId, timeoutMs);
179
+ return false;
180
+ }
181
+ return isApproved(decision, approveLabel);
182
+ }
183
+ finally {
184
+ // Answered or not, the question is no longer live. Left open, a rejected
185
+ // deploy keeps somebody's phone in the "needs attention" list for an
186
+ // action that already resolved.
187
+ await this.close({ customId });
188
+ }
189
+ }
190
+ }
191
+ /**
192
+ * A 2xx that woke nobody is a failure, and only the caller can decide what to do.
193
+ *
194
+ * `delivered` counts phones actually sent to, so this fires when a channel's
195
+ * devices are all stale or its members have not installed the app — a case that
196
+ * answers 201 and looks exactly like success.
197
+ */
198
+ export function checkDelivery(alert, enabled) {
199
+ if (!enabled || alert.actuallyReachedSomeone)
200
+ return;
201
+ throw new M91DeliveryError(`The alert was filed but reached nobody: delivered=0, ` +
202
+ `unregistered=${alert.unregistered.length}, noDevice=${alert.noDevice}, ` +
203
+ `pending=${alert.pending}. Nobody's phone rang.`, alert.delivered, alert.unregistered, alert.noDevice, alert.pending);
204
+ }
205
+ /**
206
+ * Whether one poll ends the wait. Pure, so the rule lives in one place.
207
+ */
208
+ export function waitVerdict(detail) {
209
+ // Not visible yet. A send answers before the fan-out finishes, so an
210
+ // immediate read can outrun it; keep waiting rather than calling it gone.
211
+ if (detail === null)
212
+ return { stop: false, decision: null };
213
+ if (detail.decision !== null)
214
+ return { stop: true, decision: detail.decision };
215
+ // Closed with nobody having claimed it — by a monitor that saw the problem
216
+ // heal, or by hand. There is no answer coming.
217
+ if (detail.isClosed)
218
+ return { stop: true, decision: null };
219
+ return { stop: false, decision: null };
220
+ }
221
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Every failure the SDK can throw, and the one place an HTTP response becomes one.
3
+ *
4
+ * Branch on `.code` — a stable string the API contract pins down — never on
5
+ * `.message`, which is written for a person reading a terminal and is expected
6
+ * to change.
7
+ */
8
+ export declare class M91Error extends Error {
9
+ readonly code: string;
10
+ readonly statusCode?: number;
11
+ readonly details?: unknown;
12
+ constructor(message: string, code?: string, statusCode?: number, details?: unknown);
13
+ }
14
+ /**
15
+ * 400/413. The request was wrong, and it is never retried.
16
+ *
17
+ * Also thrown locally, before any HTTP call, when a field breaks a documented
18
+ * limit. Every schema server-side is `.strict()`, so a mistyped field name is a
19
+ * 400 rather than a silent drop — and a round trip to learn that is a round
20
+ * trip wasted.
21
+ */
22
+ export declare class M91ValidationError extends M91Error {
23
+ constructor(message: string, details?: unknown, statusCode?: number, code?: string);
24
+ }
25
+ /** 401. Credential invalid, expired or revoked — nothing is cached server-side. */
26
+ export declare class M91AuthError extends M91Error {
27
+ constructor(message?: string, details?: unknown);
28
+ }
29
+ /** 403. Authenticated, but not allowed. On `close()` this means success. */
30
+ export declare class M91ForbiddenError extends M91Error {
31
+ constructor(message?: string, details?: unknown);
32
+ }
33
+ /**
34
+ * 404. Absent — **or present and not yours**. The API answers the same way to
35
+ * both on purpose, so an id cannot become an oracle for what exists.
36
+ */
37
+ export declare class M91NotFoundError extends M91Error {
38
+ constructor(message?: string, details?: unknown);
39
+ }
40
+ /**
41
+ * 409. The channel has nobody who can actually be woken.
42
+ *
43
+ * `unregistered` is the invited numbers with no M91 account — invited, never
44
+ * installed the app, and unreachable. An ordinary SMS would not pierce Do Not
45
+ * Disturb, would not repeat, and would not escalate.
46
+ */
47
+ export declare class M91ChannelEmptyError extends M91Error {
48
+ readonly unregistered: string[];
49
+ constructor(message: string, unregistered?: string[], details?: unknown);
50
+ }
51
+ /**
52
+ * 429. 60 sends a minute per send link.
53
+ *
54
+ * `retryAfter` always has a value: the server sets `Retry-After` on every 429
55
+ * precisely so a caller does not have to guess, and the usual guess is to retry
56
+ * immediately and forever.
57
+ */
58
+ export declare class M91RateLimitError extends M91Error {
59
+ readonly retryAfter: number;
60
+ constructor(message?: string, retryAfter?: number, details?: unknown);
61
+ }
62
+ /** 5xx. Not the caller's fault. Retried with backoff before it reaches you. */
63
+ export declare class M91ServerError extends M91Error {
64
+ constructor(message?: string, statusCode?: number, details?: unknown);
65
+ }
66
+ /**
67
+ * The request never got an answer — DNS, TLS, connect or read timeout.
68
+ *
69
+ * Not retried. A send that timed out may or may not have raised an alert, and
70
+ * retrying it blindly is how one condition wakes a team four times.
71
+ */
72
+ export declare class M91NetworkError extends M91Error {
73
+ constructor(message?: string, details?: unknown);
74
+ }
75
+ /**
76
+ * The API said 2xx and **nobody's phone rang**.
77
+ *
78
+ * The failure mode integrations actually hit, and it is invisible if you only
79
+ * check the status code. `delivered` counts phones sent to, not the size of the
80
+ * roster: a channel of four whose device tokens are all stale answers 201 with
81
+ * `delivered: 0`.
82
+ *
83
+ * A *deduplicated* send is not this error — `delivered` is 0 because the same
84
+ * alert is already open and already ringing, which is success.
85
+ */
86
+ export declare class M91DeliveryError extends M91Error {
87
+ readonly delivered: number;
88
+ readonly unregistered: string[];
89
+ readonly noDevice: number;
90
+ readonly pending: number;
91
+ constructor(message: string, delivered?: number, unregistered?: string[], noDevice?: number, pending?: number);
92
+ }
93
+ /** `waitForDecision` ran out of time with no SHARED response. Nobody answered. */
94
+ export declare class M91TimeoutError extends M91Error {
95
+ readonly customId: string;
96
+ readonly timeoutMs: number;
97
+ constructor(customId: string, timeoutMs: number);
98
+ }
99
+ /**
100
+ * Turn one HTTP response into either the payload or a thrown error.
101
+ *
102
+ * **Every M91 route answers in an envelope** — `{ success: true, data }` or
103
+ * `{ success: false, error: { code, message, details } }` — and this is the only
104
+ * place the SDK knows that. Everything above this line sees the inner `data`.
105
+ *
106
+ * The error class is chosen by HTTP status rather than by `error.code`, because
107
+ * the status is what the server derives the code from in the first place;
108
+ * keeping them in step in two places is how they drift apart. The body's `code`
109
+ * is still carried onto the error.
110
+ */
111
+ export declare function unwrap(status: number, body: unknown, headers?: Headers | Record<string, string>): unknown;
112
+ /** Seconds from `Retry-After`, or undefined. Header lookups are case-insensitive. */
113
+ export declare function retryAfter(headers?: Headers | Record<string, string>): number | undefined;
114
+ //# sourceMappingURL=exceptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exceptions.d.ts","sourceRoot":"","sources":["../../src/exceptions.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,qBAAa,QAAS,SAAQ,KAAK;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;gBAEf,OAAO,EAAE,MAAM,EAAE,IAAI,SAAc,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAUxF;AAED;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,QAAQ;gBAClC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,UAAU,SAAM,EAAE,IAAI,SAAqB;CAG5F;AAED,mFAAmF;AACnF,qBAAa,YAAa,SAAQ,QAAQ;gBAC5B,OAAO,SAAiB,EAAE,OAAO,CAAC,EAAE,OAAO;CAGxD;AAED,4EAA4E;AAC5E,qBAAa,iBAAkB,SAAQ,QAAQ;gBACjC,OAAO,SAAc,EAAE,OAAO,CAAC,EAAE,OAAO;CAGrD;AAED;;;GAGG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;gBAChC,OAAO,SAAc,EAAE,OAAO,CAAC,EAAE,OAAO;CAGrD;AAED;;;;;;GAMG;AACH,qBAAa,oBAAqB,SAAQ,QAAQ;IAChD,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;gBAEpB,OAAO,EAAE,MAAM,EAAE,YAAY,GAAE,MAAM,EAAO,EAAE,OAAO,CAAC,EAAE,OAAO;CAI5E;AAED;;;;;;GAMG;AACH,qBAAa,iBAAkB,SAAQ,QAAQ;IAC7C,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAEhB,OAAO,SAAiB,EAAE,UAAU,SAAK,EAAE,OAAO,CAAC,EAAE,OAAO;CAIzE;AAED,+EAA+E;AAC/E,qBAAa,cAAe,SAAQ,QAAQ;gBAC9B,OAAO,SAA0B,EAAE,UAAU,SAAM,EAAE,OAAO,CAAC,EAAE,OAAO;CAGnF;AAED;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,QAAQ;gBAC/B,OAAO,SAAkB,EAAE,OAAO,CAAC,EAAE,OAAO;CAGzD;AAED;;;;;;;;;;GAUG;AACH,qBAAa,gBAAiB,SAAQ,QAAQ;IAC5C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAGvB,OAAO,EAAE,MAAM,EACf,SAAS,SAAI,EACb,YAAY,GAAE,MAAM,EAAO,EAC3B,QAAQ,SAAI,EACZ,OAAO,SAAI;CAQd;AAED,kFAAkF;AAClF,qBAAa,eAAgB,SAAQ,QAAQ;IAC3C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;gBAEf,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;CAQhD;AAQD;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CA8BzG;AAOD,qFAAqF;AACrF,wBAAgB,UAAU,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,GAAG,SAAS,CAWzF"}