@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 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 has no automatic retry
5
- policy, persistence, telemetry, Node-only runtime import, or Cloudflare binding.
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 message = await whooshBang.createMessage(
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 terminal summary as the same tagged union the
60
- machine stream and signed customer events carry, and returns nothing once the
61
- typed answer has left its retention window — `answered` and `answered_at`
62
- survive that deletion, the answer does not.
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.30` carries contracts `1.0.0-rc.21`: a project
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
- account is waiting on.
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 | Meaning |
201
- | --- | --- |
202
- | `WhooshBangValidationError` | Caller input broke a documented bound. No request was made. Extends `TypeError`. |
203
- | `WhooshBangContractError` | A request or response did not match the pinned contract. |
204
- | `WhooshBangProblemError` | The API returned Problem Details, with `code`, `status`, `diagnosticId`, `retryable`, and `retryAt` preserved. |
205
- | `WhooshBangProtocolError` | The response was not a usable WhooshBang response. |
206
- | `WhooshBangTransportError` | The request could not be completed. |
207
- | `WhooshBangAbortError` | The caller's `AbortSignal` fired. |
208
- | `WhooshBangTimeoutError` | The caller's `timeoutMs` elapsed. |
209
-
210
- The last three carry `outcomeUnknown`: a cancelled or failed side effect may
211
- already have been applied, and the client never resolves that on your behalf.
212
-
213
- Use a verified human-session token with the same client to call `getAccount`,
214
- `listProjects`, `createProject`, `getProject`, `listApiCredentials`,
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 an error with
229
- `outcomeUnknown: true`; the caller decides whether a new logical operation is
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 through 300000 milliseconds).
236
- When only one is supplied it reaches `fetch` unchanged. Neither adds a retry:
237
- they bound waiting, and neither claims a server outcome after cancellation.
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, bounded polling, raw-body webhook
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.16`. Successful payloads are
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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"}
@@ -1,5 +1,5 @@
1
1
  const SWITCHES = new Set([
2
- "account",
2
+ "organization",
3
3
  "help",
4
4
  "json",
5
5
  "version",
@@ -1 +1 @@
1
- {"version":3,"file":"arguments.js","sourceRoot":"","sources":["../../src/cli/arguments.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"arguments.js","sourceRoot":"","sources":["../../src/cli/arguments.ts"],"names":[],"mappings":"AAkBA,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC;IACvB,cAAc;IACd,MAAM;IACN,MAAM;IACN,SAAS;IACT,kBAAkB;IAClB,KAAK;IACL,WAAW;IACX,eAAe;IACf,UAAU;IACV,QAAQ;IACR,WAAW;IACX,MAAM;IACN,SAAS;IACT,KAAK;CACN,CAAC,CAAC;AAEH;;;;;;;;;GASG;AACH,MAAM,OAAO,GAAgC,IAAI,GAAG,CAAC;IACnD,CAAC,GAAG,EAAE,YAAY,CAAC;IACnB,CAAC,GAAG,EAAE,aAAa,CAAC;IACpB,CAAC,KAAK,EAAE,aAAa,CAAC;IACtB,CAAC,GAAG,EAAE,KAAK,CAAC;IACZ,CAAC,GAAG,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,SAAS,CAAC;IAChB,CAAC,GAAG,EAAE,YAAY,CAAC;IACnB,CAAC,GAAG,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,KAAK,CAAC;CACb,CAAC,CAAC;AAEH,SAAS,SAAS,CAAC,IAAY;IAC7B,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAuB;IACpD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE7C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC,EAAE,CAAC;QACpD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAW,CAAC;QACpC,yEAAyE;QACzE,0EAA0E;QAC1E,mBAAmB;QACnB,MAAM,KAAK,GACT,KAAK,CAAC,MAAM,GAAG,CAAC;YAChB,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC;YACrB,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC;YACvB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACvB,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAClB,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,IAAI,GAAG,SAAS,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;QACrE,IAAI,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;YAClB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YACzB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC;YAC3D,SAAS;QACX,CAAC;QACD,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC7B,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,oEAAoE;YACpE,yEAAyE;YACzE,aAAa;YACb,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACnB,SAAS;QACX,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QACxB,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QAC1D,KAAK,IAAI,CAAC,CAAC;IACb,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,OAAO,UAAW,SAAQ,KAAK;IACnC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,MAAM,UAAU,QAAQ,CACtB,MAAuB,EACvB,IAAY,EACZ,IAAY;IAEZ,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACvC,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9C,MAAM,IAAI,UAAU,CAAC,KAAK,IAAI,iBAAiB,IAAI,GAAG,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -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;