yungle-client 0.3.0 → 0.3.2

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
@@ -85,12 +85,12 @@ refused. No public URL? Create the endpoint with `url: null` and poll `listWebho
85
85
 
86
86
  Your vault and end-to-end encrypted transfers, because their keys are derived in
87
87
  the client and never sent to Yungle. See
88
- <https://yungle.co/developers/unsupported>.
88
+ <https://docs.yungle.co/unsupported>.
89
89
 
90
90
  ## Related
91
91
 
92
92
  - [`yungle-cli`](https://www.npmjs.com/package/yungle-cli) — resumable uploads from your terminal
93
93
  - [`yungle-mcp`](https://www.npmjs.com/package/yungle-mcp) — connect Claude or Cursor to Yungle
94
- - [API reference](https://yungle.co/developers/reference?ref=npm) · [OpenAPI spec](https://yungle.co/api/v1/openapi.json)
94
+ - [API reference](https://docs.yungle.co/api-reference?ref=npm) · [OpenAPI spec](https://yungle.co/api/v1/openapi.json)
95
95
 
96
96
  MIT
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { Collection, CollectionFile, CollectionSummary, Contact, ContactInput, DownloadEvent, DownloadLinks, ImportInput, ImportStatus, Submission, UploadRequest, UploadRequestInput, FileInput, Folder, Guest, Me, PulledWebhookEvent, RecipientStatus, Transfer, TransferFile, TransferSummary, UploadTargets, WebhookDelivery, WebhookEndpoint, WebhookEventType } from './types.js';
1
+ import type { Collection, CollectionFile, CollectionSummary, Contact, ContactInput, DownloadEvent, DownloadLinks, ImportInput, ImportStatus, Submission, UploadRequest, UploadRequestInput, FileInput, Folder, Guest, Me, Referral, PulledWebhookEvent, RecipientStatus, Transfer, TransferFile, TransferSummary, UploadTargets, WebhookDelivery, WebhookEndpoint, WebhookEventType } from './types.js';
2
2
  import { type RenewOutcome as Renewal } from './upload-token.js';
3
3
  import { type UploadFileOptions } from './upload.js';
4
4
  export { chunkSize, uploadFile, UploadError, type UploadFileOptions } from './upload.js';
@@ -106,6 +106,8 @@ export declare class YungleClient {
106
106
  uploadUrl: string;
107
107
  }>;
108
108
  me(): Promise<Me>;
109
+ /** Your invite code and link, and the free months it has earned. */
110
+ getReferral(): Promise<Referral>;
109
111
  /**
110
112
  * One page of transfers, newest first — 100 by default, up to 500. Pass the
111
113
  * returned `nextCursor` back as `cursor` for the next page; it is null on the
package/dist/index.mjs CHANGED
@@ -220,7 +220,7 @@ function pageQuery(page, extra = {}) {
220
220
  const q = params.toString();
221
221
  return q ? `?${q}` : "";
222
222
  }
223
- var CLIENT_VERSION = true ? "0.3.0" : "dev";
223
+ var CLIENT_VERSION = true ? "0.3.2" : "dev";
224
224
  var YungleClient = class {
225
225
  baseUrl;
226
226
  apiKey;
@@ -260,6 +260,10 @@ var YungleClient = class {
260
260
  me() {
261
261
  return this.request("GET", "/me");
262
262
  }
263
+ /** Your invite code and link, and the free months it has earned. */
264
+ getReferral() {
265
+ return this.request("GET", "/me/referral");
266
+ }
263
267
  // ── Transfers ─────────────────────────────────────────────────────────────
264
268
  /**
265
269
  * One page of transfers, newest first — 100 by default, up to 500. Pass the
@@ -504,22 +508,31 @@ var YungleClient = class {
504
508
  const safe = method === "GET" || method === "DELETE" || idempotencyKey !== void 0 || opts?.idempotent === true;
505
509
  let attempt = 0;
506
510
  for (; ; ) {
507
- const res = await this.doFetch(`${this.baseUrl}${path}`, {
508
- method,
509
- headers: {
510
- Authorization: `Bearer ${this.apiKey}`,
511
- Accept: "application/json",
512
- "User-Agent": this.userAgent,
513
- ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {},
514
- ...body === void 0 ? {} : { "Content-Type": "application/json" }
515
- },
516
- ...body === void 0 ? {} : { body: JSON.stringify(body) }
517
- });
511
+ let res;
512
+ try {
513
+ res = await this.doFetch(`${this.baseUrl}${path}`, {
514
+ method,
515
+ headers: {
516
+ Authorization: `Bearer ${this.apiKey}`,
517
+ Accept: "application/json",
518
+ "User-Agent": this.userAgent,
519
+ ...idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {},
520
+ ...body === void 0 ? {} : { "Content-Type": "application/json" }
521
+ },
522
+ ...body === void 0 ? {} : { body: JSON.stringify(body) }
523
+ });
524
+ } catch (err) {
525
+ const aborted = err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError");
526
+ if (aborted || !safe || attempt >= this.maxRetries) throw err;
527
+ await sleep(backoffMs(attempt, null));
528
+ attempt++;
529
+ continue;
530
+ }
518
531
  if (res.ok) return await res.json();
519
532
  const error = await toError(res);
520
533
  const mayRetry = error.status === 429 || safe && error.retryable;
521
534
  if (!mayRetry || attempt >= this.maxRetries) throw error;
522
- await sleep(backoffMs(attempt, error));
535
+ await sleep(backoffMs(attempt, error.retryAfterSeconds));
523
536
  attempt++;
524
537
  }
525
538
  }
@@ -555,8 +568,7 @@ function parseRetryAfter(value, now = Date.now()) {
555
568
  const seconds = Math.ceil((at - now) / 1e3);
556
569
  return seconds >= 0 ? seconds : null;
557
570
  }
558
- function backoffMs(attempt, error) {
559
- const stated = error.retryAfterSeconds;
571
+ function backoffMs(attempt, stated) {
560
572
  if (stated !== null) return stated * 1e3;
561
573
  return Math.min(2 ** attempt * 500, 8e3) + Math.floor(Math.random() * 250);
562
574
  }
package/dist/types.d.ts CHANGED
@@ -40,6 +40,23 @@ export interface KeyInfo {
40
40
  /** Whether this credential may make Yungle email anyone. Absent on older servers. */
41
41
  canEmail?: boolean;
42
42
  }
43
+ /** Your invite code and what it has earned (`GET /me/referral`). */
44
+ export interface Referral {
45
+ /** Your invite code. A friend can type it in Settings › Plan. */
46
+ code: string;
47
+ /** Your invite link: the same code, as a page that signs them up. */
48
+ url: string;
49
+ /** Free months earned so far. */
50
+ earnedMonths: number;
51
+ /** Free months still available to earn. */
52
+ remainingMonths: number;
53
+ /** The most free months one person can earn, ever. */
54
+ cap: number;
55
+ /** True once `earnedMonths` reached `cap`. The link still gives friends their month. */
56
+ capped: boolean;
57
+ /** Friends in their free month, or inside the 14 days after their first payment. */
58
+ pending: number;
59
+ }
43
60
  export interface Me {
44
61
  workspace: Workspace;
45
62
  plan: PlanInfo;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yungle-client",
3
- "version": "0.3.0",
3
+ "version": "0.3.2",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -22,7 +22,7 @@
22
22
  "type": "git",
23
23
  "url": "git+https://github.com/heindewilde/yungle-clients.git"
24
24
  },
25
- "homepage": "https://yungle.co/developers/reference?ref=npm",
25
+ "homepage": "https://docs.yungle.co/api-reference?ref=npm",
26
26
  "bugs": {
27
27
  "url": "https://github.com/heindewilde/yungle-clients/issues"
28
28
  },