@whooshbang/sdk 1.0.0-rc.30 → 1.0.0-rc.32
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/README.md +246 -32
- package/dist/browser-confirmation.d.ts +24 -0
- package/dist/browser-confirmation.js +140 -0
- package/dist/browser-confirmation.js.map +1 -0
- package/dist/cli/arguments.js +1 -1
- package/dist/cli/arguments.js.map +1 -1
- package/dist/cli/browser-confirmation.d.ts +18 -0
- package/dist/cli/browser-confirmation.js +152 -0
- package/dist/cli/browser-confirmation.js.map +1 -0
- package/dist/cli/commands.js +177 -67
- package/dist/cli/commands.js.map +1 -1
- package/dist/cli/config.d.ts +1 -1
- package/dist/cli/config.js +1 -1
- package/dist/cli/main.js +10 -6
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/selectors.js +6 -6
- package/dist/cli/selectors.js.map +1 -1
- package/dist/cli/session.d.ts +3 -3
- package/dist/cli/session.js +3 -3
- package/dist/client.d.ts +169 -15
- package/dist/client.js +421 -60
- package/dist/client.js.map +1 -1
- package/dist/errors.d.ts +41 -17
- package/dist/errors.js +46 -32
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/interactions.d.ts +36 -6
- package/dist/interactions.js +74 -20
- package/dist/interactions.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# `@whooshbang/sdk`
|
|
2
2
|
|
|
3
3
|
Fetch-injected, ESM-only client and opt-in browser recipient surface for the
|
|
4
|
-
release-candidate WhooshBang V1 contract. The package
|
|
5
|
-
|
|
4
|
+
release-candidate WhooshBang V1 contract. The package never retries a side
|
|
5
|
+
effect, and has no persistence, telemetry, Node-only runtime import, or
|
|
6
|
+
Cloudflare binding. The one thing it does retry is a read inside the composed
|
|
7
|
+
wait — see [awaiting an answer](#awaiting-an-answer).
|
|
6
8
|
|
|
7
9
|
Install the current release candidate from the public npm registry:
|
|
8
10
|
|
|
@@ -19,7 +21,7 @@ const whooshBang = new WhooshBangClient({
|
|
|
19
21
|
fetch,
|
|
20
22
|
});
|
|
21
23
|
|
|
22
|
-
const
|
|
24
|
+
const sent = await whooshBang.createMessage(
|
|
23
25
|
{
|
|
24
26
|
to: { subscriber_id: "customer_123" },
|
|
25
27
|
content: { type: "text", text: "A deployment is waiting." },
|
|
@@ -32,6 +34,19 @@ const message = await whooshBang.createMessage(
|
|
|
32
34
|
},
|
|
33
35
|
{ idempotencyKey: "deploy-approval-987", timeoutMs: 10_000 },
|
|
34
36
|
);
|
|
37
|
+
switch (sent.outcome) {
|
|
38
|
+
case "accepted":
|
|
39
|
+
console.log(sent.message.id);
|
|
40
|
+
break;
|
|
41
|
+
case "subscriber_unbound":
|
|
42
|
+
// customer_123 revoked their channel, or never finished subscribing.
|
|
43
|
+
// Nothing was sent; give them a new subscription link.
|
|
44
|
+
break;
|
|
45
|
+
case "preferred_binding_unavailable":
|
|
46
|
+
// The channel they chose as preferred is paused or revoked, and the
|
|
47
|
+
// service never picks another for them. Nothing was sent.
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
35
50
|
```
|
|
36
51
|
|
|
37
52
|
## Interaction builders
|
|
@@ -56,10 +71,106 @@ of 30. A wait that expires is a successful current-state read, not an error, so
|
|
|
56
71
|
a listener-less consumer simply reads again. Inspection never acknowledges,
|
|
57
72
|
consumes, retries, or replays anything.
|
|
58
73
|
|
|
59
|
-
`interactionAnswer` reads a
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
`interactionAnswer` reads a summary as a tagged outcome the caller branches
|
|
75
|
+
on, never as a value that might be missing. `outcome` is the interaction's own
|
|
76
|
+
state — `open`, `expired`, `cancelled`, `retired` — except that `answered`
|
|
77
|
+
splits in two: `answered` carries the typed answer as the same union the
|
|
78
|
+
machine stream and signed customer events use, and `answer_not_retained` says
|
|
79
|
+
the person answered but the typed answer has left its retention window —
|
|
80
|
+
`answered` and `answered_at` survive that deletion, the answer does not. A
|
|
81
|
+
summary whose `answer` disagrees with its declared `type`, or whose
|
|
82
|
+
`answer_retained` contradicts the presence of `answer`, is a
|
|
83
|
+
`WhooshBangContractError`, never "no answer". See
|
|
84
|
+
[outcomes and exceptions](#outcomes-and-exceptions).
|
|
85
|
+
|
|
86
|
+
`cancelMessage` and `cancelSubscriptionLink` return a tagged outcome for the
|
|
87
|
+
same reason: the race between cancelling and the provider call is inherent, so
|
|
88
|
+
a correct caller cancels and learns which side won. `cancelled` carries the
|
|
89
|
+
cancelled resource; `cancellation_too_late` carries the message's current
|
|
90
|
+
state, or the link's Problem Details, and never claims provider-side deletion.
|
|
91
|
+
|
|
92
|
+
## Awaiting an answer
|
|
93
|
+
|
|
94
|
+
One server hold caps at 30 seconds and an interaction stays open for up to 24
|
|
95
|
+
hours, so waiting on a person means reading again while the interaction is
|
|
96
|
+
`open`. `awaitInteraction` is that loop, shipped once:
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
const read = await whooshBang.awaitInteraction(interactionId, {
|
|
100
|
+
patienceMs: 15 * 60_000,
|
|
101
|
+
});
|
|
102
|
+
switch (read.outcome) {
|
|
103
|
+
case "answered":
|
|
104
|
+
console.log(read.answer.value);
|
|
105
|
+
break;
|
|
106
|
+
case "answer_not_retained":
|
|
107
|
+
case "expired":
|
|
108
|
+
case "cancelled":
|
|
109
|
+
case "retired":
|
|
110
|
+
break;
|
|
111
|
+
case "open":
|
|
112
|
+
// Fifteen minutes passed and nobody answered. The interaction is still
|
|
113
|
+
// open; await it again, or move on.
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
It resolves with the same tagged outcome `interactionAnswer` produces, with
|
|
119
|
+
one reading added: `open` means the patience bound elapsed first. `patienceMs`
|
|
120
|
+
(0 through 86400000) bounds the whole wait and is deliberately not
|
|
121
|
+
`timeoutMs`, which bounds one HTTP call and cannot express a fifteen-minute
|
|
122
|
+
wait; each read inside the wait derives its own deadline from `wait` (the
|
|
123
|
+
per-hold seconds, default 30) plus a margin, so a stalled connection fails one
|
|
124
|
+
read rather than the wait. Without `patienceMs` the wait ends when the
|
|
125
|
+
interaction does. Nothing ordinary throws: nobody answering in time is a
|
|
126
|
+
result of asking a person a question, not a failure. Your own `signal` firing
|
|
127
|
+
still raises `WhooshBangTransportError` with reason `abort`, because that is
|
|
128
|
+
your decision, not the wait's outcome.
|
|
129
|
+
|
|
130
|
+
`ask` sends the question and waits, in one call:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
const asked = await whooshBang.ask(
|
|
134
|
+
{
|
|
135
|
+
to: { subscriber_id: "customer_123" },
|
|
136
|
+
content: { type: "text", text: "A deployment is waiting." },
|
|
137
|
+
interaction: confirmInteraction({
|
|
138
|
+
prompt: "Approve the synthetic deployment?",
|
|
139
|
+
expiresInSeconds: 900,
|
|
140
|
+
}),
|
|
141
|
+
},
|
|
142
|
+
{ idempotencyKey: "deploy-approval-987", patienceMs: 15 * 60_000 },
|
|
143
|
+
);
|
|
144
|
+
if (asked.outcome === "answered") {
|
|
145
|
+
console.log(asked.message.id, asked.answer.value);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Every interaction member also carries the accepted `message`. The send's two
|
|
150
|
+
refusals — `subscriber_unbound` and `preferred_binding_unavailable` — are the
|
|
151
|
+
same outcomes `createMessage` returns, carrying `problem` and nothing else,
|
|
152
|
+
because no message exists. A request without an interaction is refused locally
|
|
153
|
+
before anything is sent.
|
|
154
|
+
|
|
155
|
+
**The wait retries its reads; the send is never retried.** A read has no side
|
|
156
|
+
effect, so a failed read is re-issued: a transport failure, a `5xx` or `429`
|
|
157
|
+
that arrived without a Problem body (the edge in front of the service during a
|
|
158
|
+
deploy), or a problem the service marks retryable. The pause between attempts
|
|
159
|
+
backs off 1, 2, 4, 8, then 16 seconds, or lasts until `retry_at` when the
|
|
160
|
+
service names one that is later, capped at five minutes. Up to five consecutive
|
|
161
|
+
retries; a successful read resets the count, and the sixth consecutive failure
|
|
162
|
+
ends the wait by throwing that error. A non-retryable problem, or any other
|
|
163
|
+
protocol or contract failure, ends the wait by throwing at once. A read that
|
|
164
|
+
comes back `open` in under a second is followed by the rest of that second, so
|
|
165
|
+
a server that does not hold — `wait: 0`, or long polling switched off — is
|
|
166
|
+
polled once a second, never in a tight loop. `patienceMs` is when the wait
|
|
167
|
+
stops issuing reads: a read already in flight at the bound still runs to its
|
|
168
|
+
own deadline. `getInteraction` and `getMessage` called directly still make
|
|
169
|
+
exactly one request. The send inside `ask` is a side effect and follows the
|
|
170
|
+
side-effect rule: one request, a transport failure raises
|
|
171
|
+
`WhooshBangOutcomeUnknownError`, and repeating the call with the same
|
|
172
|
+
`idempotencyKey` replays the original message rather than asking twice — which
|
|
173
|
+
is also how a wait that failed after the send is resumed.
|
|
63
174
|
|
|
64
175
|
## Endpoint lifecycle and webhook verification
|
|
65
176
|
|
|
@@ -175,12 +286,12 @@ evaluates DOM globals. These subpaths are published in
|
|
|
175
286
|
canonical red fallback while leaving every configured customer name, logo, and
|
|
176
287
|
accent authoritative.
|
|
177
288
|
|
|
178
|
-
`@whooshbang/sdk@1.0.0-rc.
|
|
289
|
+
`@whooshbang/sdk@1.0.0-rc.32` carries contracts `1.0.0-rc.23`: a project
|
|
179
290
|
credential holding the opt-in `projects:read` / `projects:write` scopes can now
|
|
180
291
|
read and write its own project's recipient configuration, an origin may be
|
|
181
292
|
written in any spelling that means the same origin, and
|
|
182
293
|
`WhooshBangProblemError.pendingInvitation` names the rollout gate an uninvited
|
|
183
|
-
|
|
294
|
+
organization is waiting on.
|
|
184
295
|
|
|
185
296
|
## Machine clients
|
|
186
297
|
|
|
@@ -195,23 +306,116 @@ revoking the original second.
|
|
|
195
306
|
The machine stream is proved against the deterministic C0-05 contract mock.
|
|
196
307
|
The production machine-stream service is a separate story.
|
|
197
308
|
|
|
309
|
+
## Outcomes and exceptions
|
|
310
|
+
|
|
311
|
+
**An outcome a correct caller is expected to handle in normal operation is a
|
|
312
|
+
value, not an exception. Its members are distinguished by an `outcome`
|
|
313
|
+
discriminator drawn from the contract's own vocabulary, and data that is real
|
|
314
|
+
in only one member appears in only that member.** An exception is reserved for
|
|
315
|
+
what the caller cannot handle at the call site: invalid input refused locally,
|
|
316
|
+
transport failure, a protocol or contract violation, and a refusal that means
|
|
317
|
+
the integration is wrong.
|
|
318
|
+
|
|
319
|
+
The test is not "did it succeed". It is: _would a correct, finished
|
|
320
|
+
integration meet this in the course of normal operation?_ A person declining a
|
|
321
|
+
confirmation is normal. A credential without the required scope is not.
|
|
322
|
+
|
|
323
|
+
Every method whose outcomes are values has its own union; there is no shared
|
|
324
|
+
result type and no global outcome enum. An exhaustive `switch` over `outcome`
|
|
325
|
+
compiles with no `default`, and a member added later is a compile error at
|
|
326
|
+
every call site rather than a silent fallthrough.
|
|
327
|
+
|
|
328
|
+
| Method | `outcome` | Carries | Means |
|
|
329
|
+
| --------------------------------------------- | ----------------------------------------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
330
|
+
| `interactionAnswer` | `answered` | `answer`, `summary` | A person answered and the typed answer is retained. |
|
|
331
|
+
| | `answer_not_retained` | `summary` | Answered; the typed answer has left its retention window. `answered_at` survives. |
|
|
332
|
+
| | `open` | `summary` | Nobody has answered yet. |
|
|
333
|
+
| | `expired` | `summary` | The interaction reached its server-side expiry unanswered. |
|
|
334
|
+
| | `cancelled` | `summary` | Cancelled before an answer. |
|
|
335
|
+
| | `retired` | `summary` | Superseded under the first-terminal-writer rule. |
|
|
336
|
+
| `createMessage`, `createMessageInEnvironment` | `accepted` | `message` | The service accepted the send; `message` is its current state. |
|
|
337
|
+
| | `subscriber_unbound` | `problem` | No active binding reaches the subscriber on this notifier. Nothing was sent; usually they have to subscribe again, sometimes your routing changed. |
|
|
338
|
+
| | `preferred_binding_unavailable` | `problem` | The channel the subscriber chose as preferred is paused or revoked, and the service never picks a fallback. Nothing was sent; they have to choose again or subscribe again. |
|
|
339
|
+
| `awaitInteraction` | as `interactionAnswer` | `summary`, `answer` on `answered` | The same members; `open` means the patience bound elapsed and the interaction can be awaited again. |
|
|
340
|
+
| `ask` | as `awaitInteraction` | plus `message` | Every interaction member also carries the accepted message. |
|
|
341
|
+
| | `subscriber_unbound`, `preferred_binding_unavailable` | `problem` | The send's own refusals, exactly as `createMessage` returns them. Nothing was sent, so there is no message. |
|
|
342
|
+
| `cancelMessage` | `cancelled` | `message` | Cancellation took effect before a worker owned the provider call. |
|
|
343
|
+
| | `cancellation_too_late` | `message` | A worker already owned the provider call. `message` is the current state; no provider-side deletion is claimed. |
|
|
344
|
+
| `cancelSubscriptionLink` | `cancelled` | `link` | The pending link is cancelled and can no longer create a binding. |
|
|
345
|
+
| | `cancellation_too_late` | `problem` | The link had already left `pending` — activated, expired, failed, or revoked. Read the link to learn which. |
|
|
346
|
+
|
|
347
|
+
Two outcomes the rule names are already values and need no union. An
|
|
348
|
+
equivalent idempotent replay returns the original resource with the original
|
|
349
|
+
status, indistinguishable from the first acceptance by design; only a
|
|
350
|
+
different body under the same key is refused. A machine-event acknowledgement
|
|
351
|
+
that is replayed or arrives out of order returns normally:
|
|
352
|
+
`acknowledgement_status` says `recorded` or `existing`, `advanced` says whether
|
|
353
|
+
the committed cursor moved, and `committed_cursor` says where it is. Only a
|
|
354
|
+
different disposition or cursor for an already-acknowledged event is refused,
|
|
355
|
+
and that is `idempotency_conflict`.
|
|
356
|
+
|
|
357
|
+
### Classification of every published problem code
|
|
358
|
+
|
|
359
|
+
Every `ProblemCode` the service publishes appears here exactly once. An
|
|
360
|
+
_outcome_ never throws from the method that owns it. An _exception_ arrives as
|
|
361
|
+
`WhooshBangProblemError` with `code`, `status`, `diagnosticId`, `retryable`,
|
|
362
|
+
and `retryAt` preserved. `test/problems.test.ts` holds this table to the code:
|
|
363
|
+
a code marked outcome is returned by the method that owns it, and every other
|
|
364
|
+
code throws from that same method.
|
|
365
|
+
|
|
366
|
+
| Code | Side | Why |
|
|
367
|
+
| --------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
368
|
+
| `cancellation_too_late` | Outcome | The race is inherent: a correct caller cancels and learns it was too late. `cancelSubscriptionLink` receives it as a problem and returns it; `cancelMessage` receives the same fact as `cancellation_effective: false` on a success, as the contract documents, so the problem code arriving there is outside the documented response and throws. |
|
|
369
|
+
| `authentication_required` | Exception | No credential reached the service. The integration's configuration is wrong. |
|
|
370
|
+
| `credential_invalid` | Exception | The credential is malformed, revoked, or past its rotation overlap. Only a replacement credential fixes it. |
|
|
371
|
+
| `scope_forbidden` | Exception | The credential lacks a scope, or the organization is not yet invited (`pendingInvitation`). No branch at the call site grants either. |
|
|
372
|
+
| `environment_mismatch` | Exception | A credential reached across the test/live boundary. |
|
|
373
|
+
| `idempotency_key_required` | Exception | The client sends a key on every side effect, so receiving this means the request the service saw is not the one the client sent. |
|
|
374
|
+
| `idempotency_conflict` | Exception | Same key, different body — including the stale row-version, claimed-identity, revoked-client, and acknowledgement cursor or disposition refusals that reuse the code. The fix is a new request, never a retry. |
|
|
375
|
+
| `project_slug_conflict` | Exception | The slug is already taken in this organization. Refused input, handled as `request_invalid` is: choose another. |
|
|
376
|
+
| `request_invalid` | Exception | The request broke a documented bound. `fieldErrors` names each field. |
|
|
377
|
+
| `replay_unavailable` | Exception | A customer-event replay was refused by the attempt's state. The caller waits for a terminal attempt or accepts that an aged-out payload is gone; there is nothing to branch on. |
|
|
378
|
+
| `subscriber_unbound` | Outcome | On a send, the person's channel state: they revoked their channel or never finished subscribing, so the branch is to invite them again. The service uses the same code when a `connection_id` does not route to the subscriber or the connection behind them was archived, which an invitation does not fix; `problem.diagnostic_id` tells support which. `createMessage` and `createMessageInEnvironment` return it. `createMachineClient` still throws it: a machine client for an unbound subscriber is setup that is wrong. |
|
|
379
|
+
| `recipient_configuration_invalid` | Exception | The owner's recipient-experience configuration is not usable as submitted or as stored. |
|
|
380
|
+
| `recipient_origin_forbidden` | Exception | The presenting browser origin is not registered for the session. |
|
|
381
|
+
| `return_destination_not_found` | Exception | The return-destination key is not configured. |
|
|
382
|
+
| `recipient_capability_invalid` | Exception | The session's capability is not live. Only the host backend can mint another; this is the browser surface's `credential_invalid`. |
|
|
383
|
+
| `recipient_handoff_invalid` | Exception | The provider handoff is unavailable or belongs to another session. |
|
|
384
|
+
| `preferred_binding_conflict` | Exception | A stale row version. Re-read the session and send a new request. |
|
|
385
|
+
| `preferred_binding_unavailable` | Outcome | On a send, the subscriber's own channel state: the channel they chose as preferred is paused or revoked, and the service never picks a fallback. The same user event as `subscriber_unbound`, so `createMessage` and `createMessageInEnvironment` return it. The recipient-session client's `setPreferredBinding` still throws it: there it is a choice the service refused, and the panel offers the choice again. |
|
|
386
|
+
| `subscriber_conflict` | Exception | A create for a subscriber that exists. Use the update and membership operations. |
|
|
387
|
+
| `recipient_group_conflict` | Exception | The group's state, revision, or a ceiling refused the operation. |
|
|
388
|
+
| `recipient_group_archived` | Exception | Archive is terminal. The caller needs a different group. |
|
|
389
|
+
| `resource_not_found` | Exception | The identifier is wrong or outside the credential's reach; absent and out-of-scope look identical on purpose. |
|
|
390
|
+
| `resource_expired` | Exception | Published but not produced by the service today: a resource's expiry reaches the caller as its own `status` or `state`, which is already a value. The recipient-session element treats it, with `recipient_capability_invalid`, as a session that has ended. |
|
|
391
|
+
| `credential_organization_mismatch` | Exception | An organization credential reached a project outside its organization. |
|
|
392
|
+
| `endpoint_rotation_conflict` | Exception | A prior overlap is still active. The caller waits for `retryAt`, as for rate limiting. |
|
|
393
|
+
| `rate_limited` | Exception | Wait until `retryAt`. The caller's response is to wait, not to branch. |
|
|
394
|
+
| `privacy_operation_in_progress` | Exception | Export, erasure, or organization closure still has in-flight work to drain or a provider cleanup to retry. Wait until `retryAt`, then repeat the same operation. |
|
|
395
|
+
| `provider_retryable` | Exception | The provider failed before acceptance. The service retries where a retry is scheduled; the caller waits. |
|
|
396
|
+
| `provider_terminal` | Exception | The provider refused permanently. Fix the binding or the content and send a new message. |
|
|
397
|
+
| `provider_outcome_unknown` | Exception | The provider may already have acted. Read the resource state before deciding anything. |
|
|
398
|
+
|
|
198
399
|
## Errors
|
|
199
400
|
|
|
200
|
-
| Class
|
|
201
|
-
|
|
|
202
|
-
| `WhooshBangValidationError`
|
|
203
|
-
| `WhooshBangContractError`
|
|
204
|
-
| `WhooshBangProblemError`
|
|
205
|
-
| `WhooshBangProtocolError`
|
|
206
|
-
| `WhooshBangTransportError`
|
|
207
|
-
| `
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
401
|
+
| Class | Meaning |
|
|
402
|
+
| ------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
403
|
+
| `WhooshBangValidationError` | Caller input broke a documented bound. No request was made. Extends `TypeError`. |
|
|
404
|
+
| `WhooshBangContractError` | A request or response did not match the pinned contract — including an interaction summary whose answer fields contradict each other, and a cancellation response that does not say whether it took effect. |
|
|
405
|
+
| `WhooshBangProblemError` | The API returned Problem Details that the method called does not hand back as an outcome, with `code`, `status`, `diagnosticId`, `retryable`, and `retryAt` preserved. |
|
|
406
|
+
| `WhooshBangProtocolError` | The response was not a usable WhooshBang response. |
|
|
407
|
+
| `WhooshBangTransportError` | The client has no server answer and there is nothing to reconcile: the request never reached the service, or it has no side effect. Repeating it is safe. `reason` says why: `abort`, `timeout` (with `timeoutMs`), or `network`. |
|
|
408
|
+
| `WhooshBangOutcomeUnknownError` | The client has no server answer and a side effect may already have been applied. Repeat it only under its original idempotency key, or read the resource first. Same `reason`. |
|
|
409
|
+
|
|
410
|
+
The last two split on the one distinction that changes what a caller may do
|
|
411
|
+
next, and the split is by class so a caller cannot read "may have applied" as
|
|
412
|
+
"nothing happened" without saying so. Which of the three transport failures
|
|
413
|
+
occurred is data on both, not a class of its own: an abort, a deadline, or a
|
|
414
|
+
network failure changes nothing about whether the side effect landed. The
|
|
415
|
+
client never resolves an unknown outcome on your behalf.
|
|
416
|
+
|
|
417
|
+
Use a verified human-session token with the same client to call `getOrganization`,
|
|
418
|
+
`listProjects`, `createProject`, `getProject`, `updateProject`, `listApiCredentials`,
|
|
215
419
|
`createApiCredential`, `rotateApiCredential`, and `revokeApiCredential`.
|
|
216
420
|
Credential create and rotate calls intentionally have no response-replay key:
|
|
217
421
|
their complete bearer is a one-time response and cannot be retrieved later.
|
|
@@ -225,16 +429,18 @@ machine administration, and acknowledgement side-effects require an explicit
|
|
|
225
429
|
idempotency key. Credential
|
|
226
430
|
lifecycle create/rotate/revoke calls follow their one-time-reveal or intrinsic
|
|
227
431
|
repeat-safe contract instead. The client sends each call exactly once. A
|
|
228
|
-
transport failure on a side effect raises
|
|
229
|
-
|
|
230
|
-
appropriate. With a caller key, an explicit retry returns the original result
|
|
432
|
+
transport failure on a side effect raises `WhooshBangOutcomeUnknownError`;
|
|
433
|
+
the caller decides whether a new logical operation is appropriate. With a caller key, an explicit retry returns the original result
|
|
231
434
|
and creates no duplicate.
|
|
232
435
|
|
|
233
436
|
## Cancellation and deadlines
|
|
234
437
|
|
|
235
|
-
Every method accepts `signal` and `timeoutMs` (1
|
|
236
|
-
When only one is supplied it reaches `fetch`
|
|
237
|
-
|
|
438
|
+
Every method that makes one request accepts `signal` and `timeoutMs` (1
|
|
439
|
+
through 300000 milliseconds). When only one is supplied it reaches `fetch`
|
|
440
|
+
unchanged. Both bound waiting on one request, and neither claims a server
|
|
441
|
+
outcome after cancellation. `awaitInteraction` has no `timeoutMs`: it takes
|
|
442
|
+
`patienceMs` for the whole wait and derives each read's deadline from `wait`;
|
|
443
|
+
`ask`'s `timeoutMs` bounds the send only. Both still take `signal`.
|
|
238
444
|
|
|
239
445
|
## Optional logging
|
|
240
446
|
|
|
@@ -248,7 +454,7 @@ than arriving as a log record.
|
|
|
248
454
|
|
|
249
455
|
## Examples
|
|
250
456
|
|
|
251
|
-
Runnable scripts for the interactive send,
|
|
457
|
+
Runnable scripts for the interactive send, the composed wait, raw-body webhook
|
|
252
458
|
verification, endpoint lifecycle, and machine bootstrap flows live in the
|
|
253
459
|
repository under `packages/sdk-js/examples/`. They are deliberately not part of
|
|
254
460
|
the published tarball, which carries only `dist` and this README. From a
|
|
@@ -262,7 +468,15 @@ node packages/sdk-js/examples/send-interactive-message.mjs
|
|
|
262
468
|
|
|
263
469
|
## Compatibility
|
|
264
470
|
|
|
471
|
+
The organization rename in SDK `1.0.0-rc.32` is a clean cut. Update old
|
|
472
|
+
`getAccount` and credential lifecycle calls to their organization equivalents,
|
|
473
|
+
response fields to `organization_id`, and CLI tenant options to `--organization`.
|
|
474
|
+
An older SDK can reject a response before application code reads it, including
|
|
475
|
+
project-scoped calls whose caller ignores the tenant field. Use the matching
|
|
476
|
+
contracts and mock release and follow the
|
|
477
|
+
[upgrade guide](../../docs/onboarding/organization-upgrade.mdx).
|
|
478
|
+
|
|
265
479
|
The exact runtime dependency is
|
|
266
|
-
`@whooshbang/contracts@1.0.0-rc.
|
|
480
|
+
`@whooshbang/contracts@1.0.0-rc.23`. Successful payloads are
|
|
267
481
|
validated with its packaged validators, while additive response fields remain
|
|
268
482
|
available on the returned object.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Browser-safe result encryption. No dashboard authority or readable credential
|
|
2
|
+
* is persisted by the handoff: only the invoking CLI holds the private key. */
|
|
3
|
+
export interface CredentialConfirmation {
|
|
4
|
+
apiOrigin: string;
|
|
5
|
+
organizationId: string;
|
|
6
|
+
method: "GET" | "POST";
|
|
7
|
+
path: string;
|
|
8
|
+
body?: unknown;
|
|
9
|
+
publicKey: JsonWebKey;
|
|
10
|
+
redirectUri: string;
|
|
11
|
+
state: string;
|
|
12
|
+
expiresAt: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function parseCredentialConfirmation(value: string): CredentialConfirmation;
|
|
15
|
+
export declare function encryptCredentialResult(publicKey: JsonWebKey, state: string, result: unknown): Promise<string>;
|
|
16
|
+
export declare function decryptCredentialResult(privateKey: CryptoKey, state: string, encrypted: string): Promise<unknown>;
|
|
17
|
+
export declare function credentialConfirmationDigest(request: CredentialConfirmation): Promise<string>;
|
|
18
|
+
export interface ConfirmedCredentialResult {
|
|
19
|
+
requestDigest: string;
|
|
20
|
+
status: number;
|
|
21
|
+
body: unknown;
|
|
22
|
+
denied?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function validConfirmedCredentialResult(request: CredentialConfirmation, value: ConfirmedCredentialResult): boolean;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { validateProblem, validateCreateOrganizationCredentialRequest, validateCreateApiCredentialRequest, validateRotateOrganizationCredentialRequest, validateRotateApiCredentialRequest, validateOrganizationCredentialCollection, validateApiCredentialCollection, validateOrganizationCredentialSecretReveal, validateApiCredentialSecretReveal, validateOrganizationCredentialRotation, validateApiCredentialRotation, validateOrganizationCredential, validateApiCredential, } from "@whooshbang/contracts";
|
|
2
|
+
function encoded(bytes) {
|
|
3
|
+
return btoa(Array.from(new Uint8Array(bytes), (byte) => String.fromCharCode(byte)).join(""))
|
|
4
|
+
.replaceAll("+", "-")
|
|
5
|
+
.replaceAll("/", "_")
|
|
6
|
+
.replaceAll("=", "");
|
|
7
|
+
}
|
|
8
|
+
function decoded(value) {
|
|
9
|
+
return Uint8Array.from(atob(value.replaceAll("-", "+").replaceAll("_", "/")), (c) => c.charCodeAt(0));
|
|
10
|
+
}
|
|
11
|
+
export function parseCredentialConfirmation(value) {
|
|
12
|
+
const parsed = JSON.parse(value);
|
|
13
|
+
const callback = new URL(parsed.redirectUri);
|
|
14
|
+
const api = new URL(parsed.apiOrigin);
|
|
15
|
+
if (api.origin !== parsed.apiOrigin ||
|
|
16
|
+
api.username ||
|
|
17
|
+
api.password ||
|
|
18
|
+
(api.protocol !== "https:" &&
|
|
19
|
+
!(api.protocol === "http:" &&
|
|
20
|
+
["127.0.0.1", "localhost"].includes(api.hostname))))
|
|
21
|
+
throw new Error("Invalid API origin.");
|
|
22
|
+
if (callback.protocol !== "http:" ||
|
|
23
|
+
callback.hostname !== "127.0.0.1" ||
|
|
24
|
+
!callback.port ||
|
|
25
|
+
callback.pathname !== "/callback" ||
|
|
26
|
+
callback.username ||
|
|
27
|
+
callback.password ||
|
|
28
|
+
callback.search ||
|
|
29
|
+
callback.hash ||
|
|
30
|
+
!/^[A-Za-z0-9_-]{32,128}$/u.test(parsed.state) ||
|
|
31
|
+
!Number.isFinite(parsed.expiresAt) ||
|
|
32
|
+
parsed.expiresAt <= Date.now() ||
|
|
33
|
+
parsed.expiresAt > Date.now() + 300_000 ||
|
|
34
|
+
typeof parsed.organizationId !== "string" ||
|
|
35
|
+
parsed.organizationId.length > 100 ||
|
|
36
|
+
!["GET", "POST"].includes(parsed.method) ||
|
|
37
|
+
!/^\/v1\/(?:organization|projects\/[A-Za-z0-9_-]+\/environments\/[A-Za-z0-9_-]+)\/credentials(?:\/[A-Za-z0-9_-]+\/(?:rotate|revoke))?$/u.test(parsed.path) ||
|
|
38
|
+
(parsed.method === "GET" &&
|
|
39
|
+
(parsed.path.endsWith("/rotate") ||
|
|
40
|
+
parsed.path.endsWith("/revoke") ||
|
|
41
|
+
parsed.body !== undefined)) ||
|
|
42
|
+
parsed.publicKey.kty !== "RSA" ||
|
|
43
|
+
parsed.publicKey.e !== "AQAB" ||
|
|
44
|
+
typeof parsed.publicKey.n !== "string" ||
|
|
45
|
+
parsed.publicKey.n.length !== 342 ||
|
|
46
|
+
parsed.publicKey.d !== undefined) {
|
|
47
|
+
throw new Error("Invalid or expired credential confirmation.");
|
|
48
|
+
}
|
|
49
|
+
const organization = parsed.path.startsWith("/v1/organization/");
|
|
50
|
+
const validBody = parsed.method === "GET" || parsed.path.endsWith("/revoke")
|
|
51
|
+
? parsed.body === undefined
|
|
52
|
+
: parsed.path.endsWith("/rotate")
|
|
53
|
+
? organization
|
|
54
|
+
? validateRotateOrganizationCredentialRequest(parsed.body)
|
|
55
|
+
: validateRotateApiCredentialRequest(parsed.body)
|
|
56
|
+
: organization
|
|
57
|
+
? validateCreateOrganizationCredentialRequest(parsed.body)
|
|
58
|
+
: validateCreateApiCredentialRequest(parsed.body);
|
|
59
|
+
if (!validBody ||
|
|
60
|
+
Object.keys(parsed).some((key) => ![
|
|
61
|
+
"apiOrigin",
|
|
62
|
+
"organizationId",
|
|
63
|
+
"method",
|
|
64
|
+
"path",
|
|
65
|
+
"body",
|
|
66
|
+
"publicKey",
|
|
67
|
+
"redirectUri",
|
|
68
|
+
"state",
|
|
69
|
+
"expiresAt",
|
|
70
|
+
].includes(key)))
|
|
71
|
+
throw new Error("Invalid credential operation.");
|
|
72
|
+
return parsed;
|
|
73
|
+
}
|
|
74
|
+
export async function encryptCredentialResult(publicKey, state, result) {
|
|
75
|
+
const rsa = await crypto.subtle.importKey("jwk", publicKey, { name: "RSA-OAEP", hash: "SHA-256" }, false, ["encrypt"]);
|
|
76
|
+
const aes = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt"]);
|
|
77
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
78
|
+
const ciphertext = await crypto.subtle.encrypt({ name: "AES-GCM", iv, additionalData: new TextEncoder().encode(state) }, aes, new TextEncoder().encode(JSON.stringify(result)));
|
|
79
|
+
const wrapped = await crypto.subtle.encrypt("RSA-OAEP", rsa, await crypto.subtle.exportKey("raw", aes));
|
|
80
|
+
return [encoded(wrapped), encoded(iv.buffer), encoded(ciphertext)].join(".");
|
|
81
|
+
}
|
|
82
|
+
export async function decryptCredentialResult(privateKey, state, encrypted) {
|
|
83
|
+
const parts = encrypted.split(".");
|
|
84
|
+
const [wrapped, iv, ciphertext] = parts;
|
|
85
|
+
if (parts.length !== 3 ||
|
|
86
|
+
!wrapped ||
|
|
87
|
+
!iv ||
|
|
88
|
+
!ciphertext ||
|
|
89
|
+
encrypted.length > 131072)
|
|
90
|
+
throw new Error("Invalid credential result.");
|
|
91
|
+
const rawKey = await crypto.subtle.decrypt("RSA-OAEP", privateKey, decoded(wrapped));
|
|
92
|
+
const aes = await crypto.subtle.importKey("raw", rawKey, "AES-GCM", false, [
|
|
93
|
+
"decrypt",
|
|
94
|
+
]);
|
|
95
|
+
return JSON.parse(new TextDecoder().decode(await crypto.subtle.decrypt({
|
|
96
|
+
name: "AES-GCM",
|
|
97
|
+
iv: decoded(iv),
|
|
98
|
+
additionalData: new TextEncoder().encode(state),
|
|
99
|
+
}, aes, decoded(ciphertext))));
|
|
100
|
+
}
|
|
101
|
+
export async function credentialConfirmationDigest(request) {
|
|
102
|
+
return encoded(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(JSON.stringify([
|
|
103
|
+
request.apiOrigin,
|
|
104
|
+
request.organizationId,
|
|
105
|
+
request.method,
|
|
106
|
+
request.path,
|
|
107
|
+
request.body ?? null,
|
|
108
|
+
request.publicKey.n,
|
|
109
|
+
request.redirectUri,
|
|
110
|
+
request.state,
|
|
111
|
+
request.expiresAt,
|
|
112
|
+
]))));
|
|
113
|
+
}
|
|
114
|
+
export function validConfirmedCredentialResult(request, value) {
|
|
115
|
+
if (value.denied === true)
|
|
116
|
+
return value.status === 403;
|
|
117
|
+
if (value.status >= 400)
|
|
118
|
+
return validateProblem(value.body) && value.body.status === value.status;
|
|
119
|
+
const organization = request.path.startsWith("/v1/organization/");
|
|
120
|
+
if (request.method === "GET")
|
|
121
|
+
return (value.status === 200 &&
|
|
122
|
+
(organization
|
|
123
|
+
? validateOrganizationCredentialCollection(value.body)
|
|
124
|
+
: validateApiCredentialCollection(value.body)));
|
|
125
|
+
if (request.path.endsWith("/revoke"))
|
|
126
|
+
return (value.status === 200 &&
|
|
127
|
+
(organization
|
|
128
|
+
? validateOrganizationCredential(value.body)
|
|
129
|
+
: validateApiCredential(value.body)));
|
|
130
|
+
if (request.path.endsWith("/rotate"))
|
|
131
|
+
return (value.status === 201 &&
|
|
132
|
+
(organization
|
|
133
|
+
? validateOrganizationCredentialRotation(value.body)
|
|
134
|
+
: validateApiCredentialRotation(value.body)));
|
|
135
|
+
return (value.status === 201 &&
|
|
136
|
+
(organization
|
|
137
|
+
? validateOrganizationCredentialSecretReveal(value.body)
|
|
138
|
+
: validateApiCredentialSecretReveal(value.body)));
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=browser-confirmation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-confirmation.js","sourceRoot":"","sources":["../src/browser-confirmation.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/arguments.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arguments.js","sourceRoot":"","sources":["../../src/cli/arguments.ts"],"names":[],"mappings":"AAkBA,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,
|
|
1
|
+
{"version":3,"file":"arguments.js","sourceRoot":"","sources":["../../src/cli/arguments.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { WhooshBangClient } from "../client.js";
|
|
2
|
+
import { type ConfirmedCredentialResult, type CredentialConfirmation } from "../browser-confirmation.js";
|
|
3
|
+
/** No reusable session crosses this boundary. State possession authenticates
|
|
4
|
+
* the callback; the ephemeral private key protects the one-time result. Neither
|
|
5
|
+
* is persisted, and other web origins cannot read the dashboard URL fragment. */
|
|
6
|
+
export declare function confirmCredentialOperation(options: {
|
|
7
|
+
request: Pick<CredentialConfirmation, "apiOrigin" | "organizationId" | "method" | "path" | "body">;
|
|
8
|
+
confirmationUrl: string;
|
|
9
|
+
openUrl: (url: string) => Promise<void>;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}): Promise<ConfirmedCredentialResult>;
|
|
12
|
+
export declare function credentialConfirmationClient(options: {
|
|
13
|
+
origin: string;
|
|
14
|
+
organization: () => Promise<{
|
|
15
|
+
id: string;
|
|
16
|
+
}>;
|
|
17
|
+
openUrl: (url: string) => Promise<void>;
|
|
18
|
+
}): WhooshBangClient;
|