@vestiarion/sdk 0.1.0 → 0.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.
package/README.md CHANGED
@@ -12,10 +12,11 @@ It has no dependencies, and runs on Node 20 or later, Deno, Bun and edge runtime
12
12
  ## Install
13
13
 
14
14
  ```bash
15
- npm install https://www.vestiarion.xyz/sdk/vestiarion-sdk-0.1.0.tgz
15
+ npm install @vestiarion/sdk
16
16
  ```
17
17
 
18
- pnpm, Yarn and Bun take the same URL. The package is ESM only.
18
+ pnpm, Yarn and Bun install it the same way. The package is ESM only. Each version is also served as a tarball, such as
19
+ `https://www.vestiarion.xyz/sdk/vestiarion-sdk-0.2.0.tgz`, which `npm install` takes as it is.
19
20
 
20
21
  ## Read
21
22
 
@@ -38,9 +39,10 @@ workspace.
38
39
 
39
40
  ## Add records
40
41
 
41
- A key with write access ("Can also add counterparties and invoices") adds counterparties and invoices.
42
+ A key with write access ("Can also add records") adds counterparties, invoices, milestones and payee links.
42
43
  - The agent decides each invoice as it decides one typed into the console, with every guardrail.
43
- - An address added this way waits for a person in the workspace to confirm it.
44
+ - A milestone starts pending. GitHub verifies it once its pull request is merged, or a person does; then the agent decides.
45
+ - An address added this way, or entered by a payee through a link, waits for a person in the workspace to confirm it.
44
46
 
45
47
  ```ts
46
48
  const counterparty = await vestiarion.counterparties.create(
@@ -55,6 +57,20 @@ const invoice = await vestiarion.invoices.create(
55
57
 
56
58
  Pass your own record's id as `idempotencyKey`, so that your own retries cannot add it twice either.
57
59
 
60
+ Pay a contributor when their pull request merges, and ask a new payee for their address:
61
+
62
+ ```ts
63
+ const milestone = await vestiarion.milestones.create(
64
+ { contractorId: contractor.id, title: "CSV export for EURC", amount: "150.00", verificationSource: "https://github.com/acme/app/pull/42" },
65
+ { idempotencyKey: "pr-acme-app-42" }
66
+ );
67
+ const link = await vestiarion.payeeLinks.create({ counterpartyId: contractor.id });
68
+ // Send link.url to the payee: it is shown only in this answer.
69
+ ```
70
+
71
+ A payee link takes no `idempotencyKey`: the API keeps only the link's hash, so a repeat makes a new link and the unused
72
+ one stops working.
73
+
58
74
  ## Errors and retries
59
75
 
60
76
  Every failure is a `VestiarionError`. It carries:
@@ -68,7 +84,8 @@ A request is retried twice by default after:
68
84
  - a `500`, `502`, `503` or `504`;
69
85
  - a timeout or a network failure.
70
86
 
71
- A write always carries an `Idempotency-Key`, so retrying it never adds the record twice.
87
+ A write carries an `Idempotency-Key`, so retrying it never adds the record twice. A payee link is the exception: a retry
88
+ makes a new link, which replaces the first.
72
89
 
73
90
  ## Webhooks
74
91
 
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type FetchLike } from "./http.js";
2
- import type { Counterparty, CounterpartyDetail, CreateCounterpartyInput, CreateInvoiceInput, Insights, Invoice, LedgerEntry, LedgerVerification, ListCounterpartiesParams, ListInvoicesParams, ListLedgerEntriesParams, ListMilestonesParams, Milestone, Page, Status, Treasury } from "./types.js";
2
+ import type { Counterparty, CounterpartyDetail, CreateCounterpartyInput, CreateInvoiceInput, CreateMilestoneInput, CreatePayeeLinkInput, Insights, Invoice, LedgerEntry, LedgerVerification, ListCounterpartiesParams, ListInvoicesParams, ListLedgerEntriesParams, ListMilestonesParams, Milestone, Page, PayeeLink, Status, Treasury } from "./types.js";
3
3
  /** Where the API is, unless `baseUrl` says otherwise. */
4
4
  export declare const DEFAULT_BASE_URL = "https://www.vestiarion.xyz";
5
5
  export interface VestiarionOptions {
@@ -49,7 +49,16 @@ export declare class Vestiarion {
49
49
  get(id: string): Promise<CounterpartyDetail>;
50
50
  create(input: CreateCounterpartyInput, options?: WriteOptions): Promise<Counterparty>;
51
51
  };
52
- readonly milestones: Collection<Milestone, ListMilestonesParams>;
52
+ readonly milestones: Collection<Milestone, ListMilestonesParams> & {
53
+ create(input: CreateMilestoneInput, options?: WriteOptions): Promise<Milestone>;
54
+ };
55
+ /**
56
+ * One-time links where a payee enters the address they are paid at. A link's `url` is in the answer only: send it to
57
+ * the payee. Making one revokes the payee's unused link, so a retry gives a working link too.
58
+ */
59
+ readonly payeeLinks: {
60
+ create(input: CreatePayeeLinkInput): Promise<PayeeLink>;
61
+ };
53
62
  readonly treasury: {
54
63
  get(): Promise<Treasury>;
55
64
  };
package/dist/client.js CHANGED
@@ -48,6 +48,11 @@ export class Vestiarion {
48
48
  invoices;
49
49
  counterparties;
50
50
  milestones;
51
+ /**
52
+ * One-time links where a payee enters the address they are paid at. A link's `url` is in the answer only: send it to
53
+ * the payee. Making one revokes the payee's unused link, so a retry gives a working link too.
54
+ */
55
+ payeeLinks;
51
56
  treasury;
52
57
  insights;
53
58
  constructor(options) {
@@ -84,7 +89,14 @@ export class Vestiarion {
84
89
  get: (id) => one(`/api/v1/counterparties/${encodeURIComponent(id)}`),
85
90
  create: (input, write) => add("/api/v1/counterparties", input, write),
86
91
  };
87
- this.milestones = collection(transport, "/api/v1/milestones");
92
+ this.milestones = {
93
+ ...collection(transport, "/api/v1/milestones"),
94
+ create: (input, write) => add("/api/v1/milestones", input, write),
95
+ };
96
+ // The API keeps no outcome for a payee link's key, so none is sent (write API part 2, W3).
97
+ this.payeeLinks = {
98
+ create: (input) => transport.request({ method: "POST", path: "/api/v1/payee-links", body: input, idempotencyKey: null }).then((answer) => answer.data),
99
+ };
88
100
  this.treasury = { get: () => one("/api/v1/treasury") };
89
101
  this.insights = { get: () => one("/api/v1/insights") };
90
102
  }
package/dist/http.d.ts CHANGED
@@ -29,7 +29,8 @@ export interface RequestSpec {
29
29
  path: string;
30
30
  query?: object;
31
31
  body?: unknown;
32
- idempotencyKey?: string;
32
+ /** A write's key: the caller's, or a fresh one when left out. `null` sends none, for a write that keeps no outcome for one. */
33
+ idempotencyKey?: string | null;
33
34
  }
34
35
  export interface Transport {
35
36
  request<T>(spec: RequestSpec): Promise<T>;
package/dist/http.js CHANGED
@@ -121,8 +121,10 @@ export function createTransport(options) {
121
121
  let body;
122
122
  if (spec.method === "POST") {
123
123
  headers["content-type"] = "application/json";
124
- // Every write carries a key, so retrying it can never add the record twice (R6).
125
- headers["idempotency-key"] = spec.idempotencyKey ?? uuid();
124
+ // Every write that keeps an outcome for a key carries one, so retrying it can never add the record twice (R6). A
125
+ // payee link keeps none, since that would store the link: a retry makes a new link, which replaces the first.
126
+ if (spec.idempotencyKey !== null)
127
+ headers["idempotency-key"] = spec.idempotencyKey ?? uuid();
126
128
  body = JSON.stringify(spec.body ?? {});
127
129
  }
128
130
  for (let attempt = 0;; attempt += 1) {
package/dist/types.d.ts CHANGED
@@ -174,6 +174,16 @@ export interface Milestone {
174
174
  } | null;
175
175
  createdAt: string;
176
176
  }
177
+ /** A one-time link for a payee to enter the address they are paid at. That address waits for a person in the workspace to confirm it before the agent pays to it. */
178
+ export interface PayeeLink {
179
+ /** The link's own id. It is not the link: that is `url`. */
180
+ id: string;
181
+ counterpartyId: string;
182
+ /** The one-time page where the payee enters their address. It is in this answer only: Vestiarion keeps just its hash. Send it to the payee yourself. */
183
+ url: string;
184
+ /** When the link stops working, 7 days after it was made. It also stops once the payee has used it, or a newer link replaces it. */
185
+ expiresAt: string;
186
+ }
177
187
  /** The workspace's accounts, reserve, obligations, latest forecast and recent treasury moves. */
178
188
  export interface Treasury {
179
189
  accounts: Array<{
@@ -335,6 +345,22 @@ export interface CreateCounterpartyInput {
335
345
  /** Where it is emailed once a payment to it is confirmed. */
336
346
  noticeEmail?: string;
337
347
  }
348
+ /** A milestone to add. It starts pending: the agent pays it only once it is verified, by GitHub or by a person, and only after its own checks. */
349
+ export interface CreateMilestoneInput {
350
+ /** The `id` of the contractor or vendor to pay, from `GET /api/v1/counterparties` or from the answer that added it. A client is not paid for milestones. */
351
+ contractorId: string;
352
+ /** What was delivered, 3 to 160 characters. */
353
+ title: string;
354
+ /** What the work is paid, in USDC, with at most 6 decimal places. A decimal string such as `"250.00"` keeps it exact; a number is read the same way. */
355
+ amount: string | number;
356
+ /** A link to the delivered work: https, up to 500 characters. A GitHub pull request (`https://github.com/<owner>/<repo>/pull/<number>`) is checked by the agent, which verifies the milestone once it is merged. Any other link is evidence for the person who verifies the milestone on Contractors. */
357
+ verificationSource?: string;
358
+ }
359
+ /** Who the link is for. */
360
+ export interface CreatePayeeLinkInput {
361
+ /** The `id` of the vendor or contractor who is to enter the address they are paid at. A client gets no link: the agent never pays one. */
362
+ counterpartyId: string;
363
+ }
338
364
  /** The query parameters of `list-ledger-entries`. */
339
365
  export interface ListLedgerEntriesParams {
340
366
  /** How many items to return. Defaults to 50; a larger value is capped at 200. */
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** This package's version, sent in every request's `User-Agent`. tests/sdk-package.test.ts holds it to package.json. */
2
- export declare const VERSION = "0.1.0";
2
+ export declare const VERSION = "0.2.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** This package's version, sent in every request's `User-Agent`. tests/sdk-package.test.ts holds it to package.json. */
2
- export const VERSION = "0.1.0";
2
+ export const VERSION = "0.2.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vestiarion/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "A typed client for the Vestiarion API and its signed webhooks.",
5
5
  "license": "MIT",
6
6
  "type": "module",