@gemmein/sdk 0.9.0 → 0.10.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/dist/index.d.cts CHANGED
@@ -2,6 +2,37 @@ export type GemmeinOptions = {
2
2
  appKey: string;
3
3
  apiUrl?: string;
4
4
  tokenStore?: TokenStore;
5
+ /**
6
+ * W10 §1 A — the fetch EVERY request this client makes goes through.
7
+ * Defaults to `globalThis.fetch`, read at CALL time so a polyfill
8
+ * installed after construction still counts. Expo passes `expo/fetch`,
9
+ * whose streaming answers React Native's own fetch cannot give.
10
+ */
11
+ fetch?: typeof fetch;
12
+ /**
13
+ * W10 §1 A — where "is the app in front of the user?" comes from, for
14
+ * `watch()`'s sleep-while-hidden. Absent, it is the browser's
15
+ * `document.visibilityState`, exactly as before. Expo passes an
16
+ * AppState-driven hook.
17
+ */
18
+ visibility?: VisibilityHook;
19
+ /**
20
+ * W10 §1 A — the platform appended to `x-client-info`
21
+ * (`gemmein-sdk/<version> expo-ios`). A report of which build called,
22
+ * never a proof. Cleaned and capped here — see `clientInfoFor`.
23
+ */
24
+ platform?: string;
25
+ };
26
+ /**
27
+ * W10 §1 A — a platform's answer to "is the app in front of the user?".
28
+ * The browser answers it with `document.visibilityState`; React Native
29
+ * answers it with `AppState`. `onChange` registers a listener and returns
30
+ * the function that removes it — one shape, so `watch()` never has to know
31
+ * which platform it is running on.
32
+ */
33
+ export type VisibilityHook = {
34
+ isHidden(): boolean;
35
+ onChange(cb: () => void): () => void;
5
36
  };
6
37
  export type TokenStore = {
7
38
  get(): string | undefined | Promise<string | undefined>;
@@ -101,10 +132,18 @@ export type CurrentUser = {
101
132
  authenticated: true;
102
133
  userId: string;
103
134
  email: string;
135
+ /**
136
+ * W10 — the store account token, when the engine carries one. It is
137
+ * passed through exactly as `GET /auth/current-user` sent it, never
138
+ * synthesised here; an engine that does not send it leaves this
139
+ * absent — read that as "not carried", never as "no account".
140
+ */
141
+ storeAccountToken?: string | null;
104
142
  } | {
105
143
  authenticated: false;
106
144
  userId?: undefined;
107
145
  email?: undefined;
146
+ storeAccountToken?: undefined;
108
147
  };
109
148
  export type AuthSession = {
110
149
  token: string;
@@ -120,13 +159,21 @@ export type AuthSession = {
120
159
  * second module). `scripts/sync-version.mjs` rewrites the literal from
121
160
  * package.json before every build (`prebuild`), and a test pins the two
122
161
  * equal, so a bump can never ship with a stale header. */
123
- export declare const SDK_VERSION = "0.9.0";
162
+ export declare const SDK_VERSION = "0.10.0";
124
163
  /** W9.1 / CLIENT-INFO-1: every request the SDK makes to Gemmein carries
125
164
  * `x-client-info: gemmein-sdk/<version>`. The server records it on the
126
165
  * secret-key usage ledger ("last seen from gemmein-sdk/0.5.0"), so a
127
166
  * misbehaving integration can be attributed to an SDK version from day
128
167
  * one. It is a report, not a proof — any caller can set it. */
129
- export declare const CLIENT_INFO = "gemmein-sdk/0.9.0";
168
+ export declare const CLIENT_INFO = "gemmein-sdk/0.10.0";
169
+ /** W10 §1 A: a mobile entry adds its platform — `gemmein-sdk/<version>
170
+ * expo-ios`. The ledger that records this header caps it at 64 characters
171
+ * and strips control characters (`MAX_CLIENT_LENGTH` / `capClient`,
172
+ * packages/db/src/keyUsageStore.ts), so the value is cleaned and capped
173
+ * HERE: a tag that arrives truncated attributes nothing. Anything outside
174
+ * `[A-Za-z0-9._/-]` collapses to a hyphen, so the label can never carry a
175
+ * newline — or a second space — into the ledger. */
176
+ export declare function clientInfoFor(platform?: string): string;
130
177
  export declare class GemmeinError extends Error {
131
178
  readonly status: number;
132
179
  readonly code: string;
@@ -141,12 +188,19 @@ export declare class GemmeinError extends Error {
141
188
  * plans by name through checkout, the server never hands out the list.
142
189
  */
143
190
  readonly requires?: string;
191
+ /**
192
+ * Present on `network_unreachable` — the fetch implementation's own throw,
193
+ * kept so a bug report can say WHICH transport failure it was. Read it for
194
+ * a log; never branch on it (its shape is the runtime's, not Gemmein's).
195
+ */
196
+ readonly cause?: unknown;
144
197
  constructor(input: {
145
198
  status: number;
146
199
  code: string;
147
200
  message: string;
148
201
  resetAt?: string;
149
202
  requires?: string;
203
+ cause?: unknown;
150
204
  });
151
205
  }
152
206
  export declare class MemoryTokenStore implements TokenStore {
@@ -159,6 +213,16 @@ export declare class BrowserTokenStore implements TokenStore {
159
213
  private readonly key;
160
214
  constructor(appKey: string);
161
215
  get(): string | undefined;
216
+ /**
217
+ * A blocked `localStorage` — Safari's private mode past its quota, a
218
+ * browser set to block site data, a sandboxed iframe whose access throws
219
+ * — throws `secure_store_unavailable` (status 0), the same code the
220
+ * mobile stores answer, with the browser's own error on `cause`.
221
+ * `auth.verifyEmailCode()` carries it to the caller: the session is real
222
+ * (the server minted it), so an app that would rather run than stop
223
+ * catches this one code and rebuilds its client with a
224
+ * `MemoryTokenStore`, which never throws.
225
+ */
162
226
  set(token: string): void;
163
227
  clear(): void;
164
228
  }
@@ -208,6 +272,12 @@ type ClientConfig = {
208
272
  apiUrl: string;
209
273
  appKey: string;
210
274
  tokenStore: TokenStore;
275
+ /** W10 §1 A — always present: the injected fetch, or the lazy default. */
276
+ fetch: typeof fetch;
277
+ /** W10 §1 A — absent in a browser; `watch()` falls back to `document`. */
278
+ visibility?: VisibilityHook | undefined;
279
+ /** W10 §1 A — `CLIENT_INFO`, plus the platform when one was named. */
280
+ clientInfo: string;
211
281
  };
212
282
  export declare class AuthClient {
213
283
  private readonly config;
@@ -217,6 +287,17 @@ export declare class AuthClient {
217
287
  email: string;
218
288
  code: string;
219
289
  }): Promise<AuthSession>;
290
+ /**
291
+ * End this device's session. IDEMPOTENT: signing out of a session that is
292
+ * already gone is the outcome asked for, not a failure.
293
+ *
294
+ * W10 row 9, found by driving the Expo app: the commonest way this
295
+ * round-trip fails is `401 auth_expired` — the owner already signed this
296
+ * person out everywhere from the console, which is the remedy for a
297
+ * stolen phone. The session IS ended; throwing there made an app that did
298
+ * everything right show an error for the thing it asked for. Every other
299
+ * failure still throws, and the token store is cleared either way.
300
+ */
220
301
  logout(): Promise<void>;
221
302
  currentUser(): Promise<CurrentUser>;
222
303
  private request;
@@ -275,7 +356,7 @@ export declare class PurchasesClient {
275
356
  }>>;
276
357
  }
277
358
  /**
278
- * A file reference — `file:01K…`. What `upload()` gives you and what your
359
+ * A file reference — `file:<uuid>`. What `upload()` gives you and what your
279
360
  * record should store.
280
361
  *
281
362
  * Branded so it cannot be mistaken for a URL: `<img src={record.poster}>` is a
@@ -285,6 +366,33 @@ export declare class PurchasesClient {
285
366
  export type FileRef = string & {
286
367
  readonly __gemmeinFileRef: unique symbol;
287
368
  };
369
+ /**
370
+ * W10 §1 A — what `upload()` takes. A browser hands it a `File` or a
371
+ * `Blob`.
372
+ *
373
+ * On a phone the thing a picker returns is a `{ uri, name, type, size }`
374
+ * object, and it reaches the wire two ways (W10 row 9, found by driving
375
+ * the Expo app):
376
+ *
377
+ * * through `@gemmein/sdk/expo` — `createExpoGemmein` turns the picker
378
+ * shape into an `expo-file-system` `File`, which IS a `Blob`, before it
379
+ * reaches here. This is the path an Expo app has: Expo's own fetch (the
380
+ * entry's default, and the only one that can stream an AI answer)
381
+ * refuses a bare picker part with
382
+ * `Unsupported FormDataPart implementation`.
383
+ * * through a client given React Native's own fetch
384
+ * (`gemmein(key, { fetch })`) — RN's `FormData` reads the bytes at
385
+ * `uri` itself, so the part is appended exactly as the picker gave it.
386
+ *
387
+ * Carry the picker's `size` with it — the server refuses a presign that
388
+ * declares nothing (it cannot accept an empty file).
389
+ */
390
+ export type UploadInput = Blob | File | {
391
+ uri: string;
392
+ name?: string;
393
+ type?: string;
394
+ size?: number;
395
+ };
288
396
  /**
289
397
  * Turn a stored file reference into a URL you can actually use.
290
398
  *
@@ -360,9 +468,14 @@ export declare class PaymentsClient {
360
468
  * WHAT is being bought when one product covers many things (e.g. a
361
469
  * license tier across a catalog):
362
470
  * `g.payments.buy("premium license", { item: "beat_37" })`.
363
- * A completed payment writes a receipt record addressed to the buyer in
364
- * the owner's receipts collection; gate downloads/fulfilment on that
365
- * receipt, never on the redirect coming back.
471
+ * Gemmein records every completed payment itself: `g.purchases.mine()` is
472
+ * the buyer's proof, and a product that delivers a file carries
473
+ * `delivery` on it. Gate fulfilment on the purchase (or the entitlement it
474
+ * granted), never on the redirect coming back. A receipts collection is
475
+ * optional, for proof records only.
476
+ *
477
+ * A product sold via a RELAY, or not sold yet, has no Payment Link to
478
+ * open: this answers 409 `product_not_sellable`.
366
479
  */
367
480
  buy(product: string, options?: {
368
481
  item?: string;
@@ -688,7 +801,7 @@ export declare class CollectionClient<T extends Record<string, unknown> = Record
688
801
  }): Promise<GemmeinRecord<T>>;
689
802
  delete(id: string): Promise<void>;
690
803
  /**
691
- * Upload a file and get back a REFERENCE — `file:01K…` — not a URL.
804
+ * Upload a file and get back a REFERENCE — `file:<uuid>` — not a URL.
692
805
  *
693
806
  * Store the reference. It never expires, it is safe to log and export, and
694
807
  * it grants nothing on its own. To show or download the file, call
@@ -715,7 +828,7 @@ export declare class CollectionClient<T extends Record<string, unknown> = Record
715
828
  * There is deliberately no `url` here. A URL that outlives a refund is the
716
829
  * bug this replaced.
717
830
  */
718
- upload(file: Blob | File, options?: {
831
+ upload(file: UploadInput, options?: {
719
832
  name?: string;
720
833
  contentType?: string;
721
834
  for?: string;
@@ -730,6 +843,11 @@ export declare class CollectionClient<T extends Record<string, unknown> = Record
730
843
  export type GemmeinServerOptions = {
731
844
  secretKey: string;
732
845
  apiUrl?: string;
846
+ /** The same seam the client has: a `fetch` to use instead of the global
847
+ * one. Every request this class makes goes through `resolveFetch`, so a
848
+ * transport failure is a `GemmeinError` `network_unreachable` here too
849
+ * (W10 review row 14a — the parity law: one seam, both rails). */
850
+ fetch?: typeof fetch;
733
851
  };
734
852
  /**
735
853
  * Where a grant came from — the KIND only. The gate never returns the
@@ -768,6 +886,13 @@ export type Holdings = {
768
886
  balance: number;
769
887
  } | null;
770
888
  };
889
+ /**
890
+ * The person behind a token or an id. The role you read here is the EFFECTIVE
891
+ * one — account membership (owner/admin) wins over the stored role, so
892
+ * a founder signed into their own app reads `owner` here and not the `member`
893
+ * every person is born as. `verifySession` and `holdings` answer with
894
+ * the same one — one person has one role, whichever door asked for it.
895
+ */
771
896
  export type GatePerson = {
772
897
  id: string;
773
898
  email: string;
@@ -786,6 +911,12 @@ export type InvitedPerson = GatePerson & {
786
911
  export declare class GemmeinServer {
787
912
  private readonly apiUrl;
788
913
  private readonly secretKey;
914
+ /** W10 review row 14a: THE ONE TRANSPORT SEAM, on this rail too. Every
915
+ * call below goes through it, so a refused connection is a typed
916
+ * `network_unreachable` and never a raw `TypeError` — the same law the
917
+ * client rail has held since row 9, and what makes "everything the
918
+ * package throws is a GemmeinError" true of the whole package. */
919
+ private readonly fetch;
789
920
  constructor(options: GemmeinServerOptions);
790
921
  collection(name: string): ServerCollectionClient;
791
922
  /**
@@ -1017,7 +1148,10 @@ declare class ServerCollectionClient {
1017
1148
  private readonly apiUrl;
1018
1149
  private readonly secretKey;
1019
1150
  private readonly name;
1020
- constructor(apiUrl: string, secretKey: string, name: string);
1151
+ /** Handed down from `GemmeinServer` — never resolved again here, so one
1152
+ * client has one transport and a test's injected fetch reaches it. */
1153
+ private readonly fetch;
1154
+ constructor(apiUrl: string, secretKey: string, name: string, fetchImpl: typeof fetch);
1021
1155
  get(id: string): Promise<unknown>;
1022
1156
  list(options?: {
1023
1157
  limit?: number;
package/dist/index.d.ts CHANGED
@@ -2,6 +2,37 @@ export type GemmeinOptions = {
2
2
  appKey: string;
3
3
  apiUrl?: string;
4
4
  tokenStore?: TokenStore;
5
+ /**
6
+ * W10 §1 A — the fetch EVERY request this client makes goes through.
7
+ * Defaults to `globalThis.fetch`, read at CALL time so a polyfill
8
+ * installed after construction still counts. Expo passes `expo/fetch`,
9
+ * whose streaming answers React Native's own fetch cannot give.
10
+ */
11
+ fetch?: typeof fetch;
12
+ /**
13
+ * W10 §1 A — where "is the app in front of the user?" comes from, for
14
+ * `watch()`'s sleep-while-hidden. Absent, it is the browser's
15
+ * `document.visibilityState`, exactly as before. Expo passes an
16
+ * AppState-driven hook.
17
+ */
18
+ visibility?: VisibilityHook;
19
+ /**
20
+ * W10 §1 A — the platform appended to `x-client-info`
21
+ * (`gemmein-sdk/<version> expo-ios`). A report of which build called,
22
+ * never a proof. Cleaned and capped here — see `clientInfoFor`.
23
+ */
24
+ platform?: string;
25
+ };
26
+ /**
27
+ * W10 §1 A — a platform's answer to "is the app in front of the user?".
28
+ * The browser answers it with `document.visibilityState`; React Native
29
+ * answers it with `AppState`. `onChange` registers a listener and returns
30
+ * the function that removes it — one shape, so `watch()` never has to know
31
+ * which platform it is running on.
32
+ */
33
+ export type VisibilityHook = {
34
+ isHidden(): boolean;
35
+ onChange(cb: () => void): () => void;
5
36
  };
6
37
  export type TokenStore = {
7
38
  get(): string | undefined | Promise<string | undefined>;
@@ -101,10 +132,18 @@ export type CurrentUser = {
101
132
  authenticated: true;
102
133
  userId: string;
103
134
  email: string;
135
+ /**
136
+ * W10 — the store account token, when the engine carries one. It is
137
+ * passed through exactly as `GET /auth/current-user` sent it, never
138
+ * synthesised here; an engine that does not send it leaves this
139
+ * absent — read that as "not carried", never as "no account".
140
+ */
141
+ storeAccountToken?: string | null;
104
142
  } | {
105
143
  authenticated: false;
106
144
  userId?: undefined;
107
145
  email?: undefined;
146
+ storeAccountToken?: undefined;
108
147
  };
109
148
  export type AuthSession = {
110
149
  token: string;
@@ -120,13 +159,21 @@ export type AuthSession = {
120
159
  * second module). `scripts/sync-version.mjs` rewrites the literal from
121
160
  * package.json before every build (`prebuild`), and a test pins the two
122
161
  * equal, so a bump can never ship with a stale header. */
123
- export declare const SDK_VERSION = "0.9.0";
162
+ export declare const SDK_VERSION = "0.10.0";
124
163
  /** W9.1 / CLIENT-INFO-1: every request the SDK makes to Gemmein carries
125
164
  * `x-client-info: gemmein-sdk/<version>`. The server records it on the
126
165
  * secret-key usage ledger ("last seen from gemmein-sdk/0.5.0"), so a
127
166
  * misbehaving integration can be attributed to an SDK version from day
128
167
  * one. It is a report, not a proof — any caller can set it. */
129
- export declare const CLIENT_INFO = "gemmein-sdk/0.9.0";
168
+ export declare const CLIENT_INFO = "gemmein-sdk/0.10.0";
169
+ /** W10 §1 A: a mobile entry adds its platform — `gemmein-sdk/<version>
170
+ * expo-ios`. The ledger that records this header caps it at 64 characters
171
+ * and strips control characters (`MAX_CLIENT_LENGTH` / `capClient`,
172
+ * packages/db/src/keyUsageStore.ts), so the value is cleaned and capped
173
+ * HERE: a tag that arrives truncated attributes nothing. Anything outside
174
+ * `[A-Za-z0-9._/-]` collapses to a hyphen, so the label can never carry a
175
+ * newline — or a second space — into the ledger. */
176
+ export declare function clientInfoFor(platform?: string): string;
130
177
  export declare class GemmeinError extends Error {
131
178
  readonly status: number;
132
179
  readonly code: string;
@@ -141,12 +188,19 @@ export declare class GemmeinError extends Error {
141
188
  * plans by name through checkout, the server never hands out the list.
142
189
  */
143
190
  readonly requires?: string;
191
+ /**
192
+ * Present on `network_unreachable` — the fetch implementation's own throw,
193
+ * kept so a bug report can say WHICH transport failure it was. Read it for
194
+ * a log; never branch on it (its shape is the runtime's, not Gemmein's).
195
+ */
196
+ readonly cause?: unknown;
144
197
  constructor(input: {
145
198
  status: number;
146
199
  code: string;
147
200
  message: string;
148
201
  resetAt?: string;
149
202
  requires?: string;
203
+ cause?: unknown;
150
204
  });
151
205
  }
152
206
  export declare class MemoryTokenStore implements TokenStore {
@@ -159,6 +213,16 @@ export declare class BrowserTokenStore implements TokenStore {
159
213
  private readonly key;
160
214
  constructor(appKey: string);
161
215
  get(): string | undefined;
216
+ /**
217
+ * A blocked `localStorage` — Safari's private mode past its quota, a
218
+ * browser set to block site data, a sandboxed iframe whose access throws
219
+ * — throws `secure_store_unavailable` (status 0), the same code the
220
+ * mobile stores answer, with the browser's own error on `cause`.
221
+ * `auth.verifyEmailCode()` carries it to the caller: the session is real
222
+ * (the server minted it), so an app that would rather run than stop
223
+ * catches this one code and rebuilds its client with a
224
+ * `MemoryTokenStore`, which never throws.
225
+ */
162
226
  set(token: string): void;
163
227
  clear(): void;
164
228
  }
@@ -208,6 +272,12 @@ type ClientConfig = {
208
272
  apiUrl: string;
209
273
  appKey: string;
210
274
  tokenStore: TokenStore;
275
+ /** W10 §1 A — always present: the injected fetch, or the lazy default. */
276
+ fetch: typeof fetch;
277
+ /** W10 §1 A — absent in a browser; `watch()` falls back to `document`. */
278
+ visibility?: VisibilityHook | undefined;
279
+ /** W10 §1 A — `CLIENT_INFO`, plus the platform when one was named. */
280
+ clientInfo: string;
211
281
  };
212
282
  export declare class AuthClient {
213
283
  private readonly config;
@@ -217,6 +287,17 @@ export declare class AuthClient {
217
287
  email: string;
218
288
  code: string;
219
289
  }): Promise<AuthSession>;
290
+ /**
291
+ * End this device's session. IDEMPOTENT: signing out of a session that is
292
+ * already gone is the outcome asked for, not a failure.
293
+ *
294
+ * W10 row 9, found by driving the Expo app: the commonest way this
295
+ * round-trip fails is `401 auth_expired` — the owner already signed this
296
+ * person out everywhere from the console, which is the remedy for a
297
+ * stolen phone. The session IS ended; throwing there made an app that did
298
+ * everything right show an error for the thing it asked for. Every other
299
+ * failure still throws, and the token store is cleared either way.
300
+ */
220
301
  logout(): Promise<void>;
221
302
  currentUser(): Promise<CurrentUser>;
222
303
  private request;
@@ -275,7 +356,7 @@ export declare class PurchasesClient {
275
356
  }>>;
276
357
  }
277
358
  /**
278
- * A file reference — `file:01K…`. What `upload()` gives you and what your
359
+ * A file reference — `file:<uuid>`. What `upload()` gives you and what your
279
360
  * record should store.
280
361
  *
281
362
  * Branded so it cannot be mistaken for a URL: `<img src={record.poster}>` is a
@@ -285,6 +366,33 @@ export declare class PurchasesClient {
285
366
  export type FileRef = string & {
286
367
  readonly __gemmeinFileRef: unique symbol;
287
368
  };
369
+ /**
370
+ * W10 §1 A — what `upload()` takes. A browser hands it a `File` or a
371
+ * `Blob`.
372
+ *
373
+ * On a phone the thing a picker returns is a `{ uri, name, type, size }`
374
+ * object, and it reaches the wire two ways (W10 row 9, found by driving
375
+ * the Expo app):
376
+ *
377
+ * * through `@gemmein/sdk/expo` — `createExpoGemmein` turns the picker
378
+ * shape into an `expo-file-system` `File`, which IS a `Blob`, before it
379
+ * reaches here. This is the path an Expo app has: Expo's own fetch (the
380
+ * entry's default, and the only one that can stream an AI answer)
381
+ * refuses a bare picker part with
382
+ * `Unsupported FormDataPart implementation`.
383
+ * * through a client given React Native's own fetch
384
+ * (`gemmein(key, { fetch })`) — RN's `FormData` reads the bytes at
385
+ * `uri` itself, so the part is appended exactly as the picker gave it.
386
+ *
387
+ * Carry the picker's `size` with it — the server refuses a presign that
388
+ * declares nothing (it cannot accept an empty file).
389
+ */
390
+ export type UploadInput = Blob | File | {
391
+ uri: string;
392
+ name?: string;
393
+ type?: string;
394
+ size?: number;
395
+ };
288
396
  /**
289
397
  * Turn a stored file reference into a URL you can actually use.
290
398
  *
@@ -360,9 +468,14 @@ export declare class PaymentsClient {
360
468
  * WHAT is being bought when one product covers many things (e.g. a
361
469
  * license tier across a catalog):
362
470
  * `g.payments.buy("premium license", { item: "beat_37" })`.
363
- * A completed payment writes a receipt record addressed to the buyer in
364
- * the owner's receipts collection; gate downloads/fulfilment on that
365
- * receipt, never on the redirect coming back.
471
+ * Gemmein records every completed payment itself: `g.purchases.mine()` is
472
+ * the buyer's proof, and a product that delivers a file carries
473
+ * `delivery` on it. Gate fulfilment on the purchase (or the entitlement it
474
+ * granted), never on the redirect coming back. A receipts collection is
475
+ * optional, for proof records only.
476
+ *
477
+ * A product sold via a RELAY, or not sold yet, has no Payment Link to
478
+ * open: this answers 409 `product_not_sellable`.
366
479
  */
367
480
  buy(product: string, options?: {
368
481
  item?: string;
@@ -688,7 +801,7 @@ export declare class CollectionClient<T extends Record<string, unknown> = Record
688
801
  }): Promise<GemmeinRecord<T>>;
689
802
  delete(id: string): Promise<void>;
690
803
  /**
691
- * Upload a file and get back a REFERENCE — `file:01K…` — not a URL.
804
+ * Upload a file and get back a REFERENCE — `file:<uuid>` — not a URL.
692
805
  *
693
806
  * Store the reference. It never expires, it is safe to log and export, and
694
807
  * it grants nothing on its own. To show or download the file, call
@@ -715,7 +828,7 @@ export declare class CollectionClient<T extends Record<string, unknown> = Record
715
828
  * There is deliberately no `url` here. A URL that outlives a refund is the
716
829
  * bug this replaced.
717
830
  */
718
- upload(file: Blob | File, options?: {
831
+ upload(file: UploadInput, options?: {
719
832
  name?: string;
720
833
  contentType?: string;
721
834
  for?: string;
@@ -730,6 +843,11 @@ export declare class CollectionClient<T extends Record<string, unknown> = Record
730
843
  export type GemmeinServerOptions = {
731
844
  secretKey: string;
732
845
  apiUrl?: string;
846
+ /** The same seam the client has: a `fetch` to use instead of the global
847
+ * one. Every request this class makes goes through `resolveFetch`, so a
848
+ * transport failure is a `GemmeinError` `network_unreachable` here too
849
+ * (W10 review row 14a — the parity law: one seam, both rails). */
850
+ fetch?: typeof fetch;
733
851
  };
734
852
  /**
735
853
  * Where a grant came from — the KIND only. The gate never returns the
@@ -768,6 +886,13 @@ export type Holdings = {
768
886
  balance: number;
769
887
  } | null;
770
888
  };
889
+ /**
890
+ * The person behind a token or an id. The role you read here is the EFFECTIVE
891
+ * one — account membership (owner/admin) wins over the stored role, so
892
+ * a founder signed into their own app reads `owner` here and not the `member`
893
+ * every person is born as. `verifySession` and `holdings` answer with
894
+ * the same one — one person has one role, whichever door asked for it.
895
+ */
771
896
  export type GatePerson = {
772
897
  id: string;
773
898
  email: string;
@@ -786,6 +911,12 @@ export type InvitedPerson = GatePerson & {
786
911
  export declare class GemmeinServer {
787
912
  private readonly apiUrl;
788
913
  private readonly secretKey;
914
+ /** W10 review row 14a: THE ONE TRANSPORT SEAM, on this rail too. Every
915
+ * call below goes through it, so a refused connection is a typed
916
+ * `network_unreachable` and never a raw `TypeError` — the same law the
917
+ * client rail has held since row 9, and what makes "everything the
918
+ * package throws is a GemmeinError" true of the whole package. */
919
+ private readonly fetch;
789
920
  constructor(options: GemmeinServerOptions);
790
921
  collection(name: string): ServerCollectionClient;
791
922
  /**
@@ -1017,7 +1148,10 @@ declare class ServerCollectionClient {
1017
1148
  private readonly apiUrl;
1018
1149
  private readonly secretKey;
1019
1150
  private readonly name;
1020
- constructor(apiUrl: string, secretKey: string, name: string);
1151
+ /** Handed down from `GemmeinServer` — never resolved again here, so one
1152
+ * client has one transport and a test's injected fetch reaches it. */
1153
+ private readonly fetch;
1154
+ constructor(apiUrl: string, secretKey: string, name: string, fetchImpl: typeof fetch);
1021
1155
  get(id: string): Promise<unknown>;
1022
1156
  list(options?: {
1023
1157
  limit?: number;