@mymehq/sdk 4.2.0 → 4.4.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.
@@ -35,6 +35,11 @@ interface TokenProvider {
35
35
  onSignOut(handler: () => void): () => void;
36
36
  /** Force a sign-out — clears local storage, fires onSignOut handlers. */
37
37
  signOut(): Promise<void>;
38
+ /** Force a token refresh and return the new access token. Same single-flight
39
+ * semantics as `getAccessToken()`. Consumers calling on a 401 response
40
+ * should use this rather than waiting for the next `getAccessToken()` to
41
+ * hit the proactive window. */
42
+ refresh(): Promise<string>;
38
43
  }
39
44
 
40
45
  /**
@@ -128,7 +128,9 @@ var StoredTokenProvider = class {
128
128
  }
129
129
  return this.refresh();
130
130
  }
131
- /** Single-flight refresh — concurrent callers await the same promise. */
131
+ /** Single-flight refresh — concurrent callers await the same promise.
132
+ * Public so consumers can force a refresh on 401 responses (RFC 6750
133
+ * invalid_token) without waiting for the proactive-refresh window. */
132
134
  async refresh() {
133
135
  if (this.inflightRefresh) return this.inflightRefresh;
134
136
  if (!this.cache) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { MergeStrategy, ConflictSnapshot, MergePolicy, ItemState, Tier, Item, CreateItemInput, PaginatedResult, ItemWithMetadata, Version, Edge, Metadata, SearchResult, CreateEdgeInput, EdgeTypeSchema, TypeSchema, CreateKeyInput, ApiKey, UpdateKeyInput, CreateWebhookInput, Webhook, UpdateWebhookInput, WebhookDelivery, TenantConfig } from '@mymehq/shared';
1
+ import { MergeStrategy, ConflictSnapshot, MergePolicy, ItemState, Tier, Item, CreateItemInput, PaginatedResult, ItemWithMetadata, Version, Edge, Metadata, SearchResult, CreateEdgeInput, EdgeTypeSchema, TypeSchema, CreateKeyInput, ApiKey, UpdateKeyInput, CreateWebhookInput, Webhook, UpdateWebhookInput, WebhookDelivery, ConnectionUninstallResult, TenantConfig } from '@mymehq/shared';
2
2
  export { ApiKey, ConflictSnapshot, CreateItemInput, CreateKeyInput, Item, ItemState, MergePolicy, MergeStrategy, Metadata, PaginatedResult, SearchResult, TypeSchema, UpdateKeyInput, Version } from '@mymehq/shared';
3
3
 
4
4
  /**
@@ -352,6 +352,29 @@ declare class MymeClient {
352
352
  * targets with the new item as source. */
353
353
  edges?: Record<string, string[]>;
354
354
  }) => Promise<Item>;
355
+ /**
356
+ * Create-or-update an item, surfacing whether the server resolved as a
357
+ * fresh create (HTTP 201) or a natural-key match update (HTTP 200).
358
+ *
359
+ * `POST /items` accepts a `(source, source_id)` pair as a stable
360
+ * natural key (T-038): a second POST with the same pair updates the
361
+ * existing row in place rather than 409ing. The wire shape returned
362
+ * is `{ item }` regardless — the only signal of "created vs updated"
363
+ * is the HTTP status code, which `transport.request` consumes
364
+ * internally. This method threads the status out so callers can
365
+ * surface the distinction (e.g. CLI `Created.` vs `Updated
366
+ * (natural-key match).`).
367
+ *
368
+ * Use `items.create` when the caller does not need the distinction —
369
+ * the wire shape is identical.
370
+ */
371
+ upsert: (input: CreateItemInput & {
372
+ /** Atomic edges payload, same semantics as `items.create`. */
373
+ edges?: Record<string, string[]>;
374
+ }) => Promise<{
375
+ item: Item;
376
+ created: boolean;
377
+ }>;
355
378
  get: (id: string) => Promise<Item>;
356
379
  list: (filters?: ListFilters) => Promise<PaginatedResult<Item>>;
357
380
  listWithMetadata: (filters?: Omit<ListFilters, "include">) => Promise<PaginatedResult<ItemWithMetadata>>;
@@ -526,6 +549,33 @@ declare class MymeClient {
526
549
  limit?: number;
527
550
  }) => Promise<WebhookDelivery[]>;
528
551
  };
552
+ /**
553
+ * Connection management. The `system.connection` items themselves are
554
+ * still managed via `client.items` (list, get, transition); this
555
+ * namespace adds the orchestrated lifecycle operations that don't fit
556
+ * the generic items surface — specifically `uninstall`, which requires
557
+ * a multi-step server-side teardown across credentials, tokens, and
558
+ * inbound subscriptions.
559
+ *
560
+ * Convenience filters for listing connections (by `integration_ref`,
561
+ * `state`, etc.) live on `client.items.list({ type: "system.connection",
562
+ * ... })`. The CLI's `my connections` tree wraps both.
563
+ */
564
+ readonly connections: {
565
+ /**
566
+ * Orchestrated uninstall of an `external-service-connector`
567
+ * connection. Revokes runtime credentials, deletes upstream OAuth
568
+ * tokens, revokes active leased tokens, disables inbound webhook
569
+ * subscriptions, transitions the system.connection state to
570
+ * `revoked`, and emits a `system.activity` row. Audit-logged.
571
+ *
572
+ * Idempotent at the artefact level — revoking already-revoked
573
+ * tokens is a no-op — but rejects with 400 when the connection
574
+ * itself is already in state `revoked`. Admin-only; tenant admins
575
+ * can uninstall connections in their own tenant scope.
576
+ */
577
+ uninstall: (id: string) => Promise<ConnectionUninstallResult>;
578
+ };
529
579
  /** Tenant-scoped configuration. Controls feed-tier retention per type
530
580
  * and the three optional schema-enforcement levers (TSC42 §5). All
531
581
  * endpoints are admin-only. */
package/dist/index.js CHANGED
@@ -85,15 +85,41 @@ var HttpTransport = class {
85
85
  return `Bearer ${token}`;
86
86
  }
87
87
  async request(method, path, options) {
88
+ const { data } = await this.requestWithStatus(method, path, options);
89
+ return data;
90
+ }
91
+ /**
92
+ * Like {@link request}, but additionally surfaces the HTTP response
93
+ * status so callers can branch on 200 vs 201 (or any other 2xx). Used
94
+ * by `client.items.upsert` to distinguish a fresh create (201) from a
95
+ * natural-key match update (200) — `request<T>` consumes the status
96
+ * internally so this sibling exists to thread it back out.
97
+ *
98
+ * Error behaviour matches {@link request}: non-2xx responses throw the
99
+ * appropriate typed `MymeError` subclass via `throwForError`, never
100
+ * resolve. 204 No Content resolves with `data: undefined as T` and
101
+ * `status: 204`.
102
+ *
103
+ * Internal — not part of the public SDK surface. The exported `MymeClient`
104
+ * keeps callers at the namespace-method level (`client.items.upsert`,
105
+ * etc.) so the transport's status-passing remains an implementation
106
+ * detail.
107
+ */
108
+ // T is used by the caller to type the response body, mirroring `request<T>`.
109
+ // The rule's "single use" heuristic doesn't account for callers explicitly
110
+ // providing the type arg — see `requestWithStatus<{ item: Item }>` in
111
+ // `client.items.upsert`.
112
+ // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-parameters
113
+ async requestWithStatus(method, path, options) {
88
114
  const response = await this.rawRequest(method, path, options);
89
115
  if (response.status === 204) {
90
- return void 0;
116
+ return { data: void 0, status: 204 };
91
117
  }
92
118
  const body = await this.parseJson(response);
93
119
  if (!response.ok) {
94
120
  this.throwForError(response.status, body);
95
121
  }
96
- return body;
122
+ return { data: body, status: response.status };
97
123
  }
98
124
  /**
99
125
  * Like request(), but returns the conflict response instead of throwing
@@ -359,6 +385,26 @@ var MymeClient = class {
359
385
  );
360
386
  return res.item;
361
387
  },
388
+ /**
389
+ * Create-or-update an item, surfacing whether the server resolved as a
390
+ * fresh create (HTTP 201) or a natural-key match update (HTTP 200).
391
+ *
392
+ * `POST /items` accepts a `(source, source_id)` pair as a stable
393
+ * natural key (T-038): a second POST with the same pair updates the
394
+ * existing row in place rather than 409ing. The wire shape returned
395
+ * is `{ item }` regardless — the only signal of "created vs updated"
396
+ * is the HTTP status code, which `transport.request` consumes
397
+ * internally. This method threads the status out so callers can
398
+ * surface the distinction (e.g. CLI `Created.` vs `Updated
399
+ * (natural-key match).`).
400
+ *
401
+ * Use `items.create` when the caller does not need the distinction —
402
+ * the wire shape is identical.
403
+ */
404
+ upsert: async (input) => {
405
+ const { data, status } = await this.transport.requestWithStatus("POST", "/items", { body: input });
406
+ return { item: data.item, created: status === 201 };
407
+ },
362
408
  get: async (id) => {
363
409
  const res = await this.transport.request(
364
410
  "GET",
@@ -835,6 +881,39 @@ var MymeClient = class {
835
881
  return res.deliveries;
836
882
  }
837
883
  };
884
+ // ---- Connections ----
885
+ /**
886
+ * Connection management. The `system.connection` items themselves are
887
+ * still managed via `client.items` (list, get, transition); this
888
+ * namespace adds the orchestrated lifecycle operations that don't fit
889
+ * the generic items surface — specifically `uninstall`, which requires
890
+ * a multi-step server-side teardown across credentials, tokens, and
891
+ * inbound subscriptions.
892
+ *
893
+ * Convenience filters for listing connections (by `integration_ref`,
894
+ * `state`, etc.) live on `client.items.list({ type: "system.connection",
895
+ * ... })`. The CLI's `my connections` tree wraps both.
896
+ */
897
+ connections = {
898
+ /**
899
+ * Orchestrated uninstall of an `external-service-connector`
900
+ * connection. Revokes runtime credentials, deletes upstream OAuth
901
+ * tokens, revokes active leased tokens, disables inbound webhook
902
+ * subscriptions, transitions the system.connection state to
903
+ * `revoked`, and emits a `system.activity` row. Audit-logged.
904
+ *
905
+ * Idempotent at the artefact level — revoking already-revoked
906
+ * tokens is a no-op — but rejects with 400 when the connection
907
+ * itself is already in state `revoked`. Admin-only; tenant admins
908
+ * can uninstall connections in their own tenant scope.
909
+ */
910
+ uninstall: async (id) => {
911
+ return this.transport.request(
912
+ "POST",
913
+ `/connections/${id}/uninstall`
914
+ );
915
+ }
916
+ };
838
917
  // ---- Tenants (admin) ----
839
918
  /** Tenant-scoped configuration. Controls feed-tier retention per type
840
919
  * and the three optional schema-enforcement levers (TSC42 §5). All
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mymehq/sdk",
3
- "version": "4.2.0",
3
+ "version": "4.4.0",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "registry": "https://registry.npmjs.org",
@@ -20,7 +20,7 @@
20
20
  "dist"
21
21
  ],
22
22
  "dependencies": {
23
- "@mymehq/shared": "4.2.0"
23
+ "@mymehq/shared": "4.4.0"
24
24
  },
25
25
  "devDependencies": {
26
26
  "@types/node": "^22.0.0",