riftri 0.3.5 → 0.4.1

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
@@ -67,9 +67,16 @@ The interactive `riftri setup` terminal UI is behind an off-by-default `tui`
67
67
  feature; add `--features tui` to build it in (the published binaries do). The
68
68
  default build keeps `setup` working through plain text prompts.
69
69
 
70
- The npm launcher is a separate distribution channel. It is not currently
71
- available because the initial publication is incomplete; use the verified
72
- standalone installer or GitHub release assets for now.
70
+ Or install through npm, which fetches the binary for your platform:
71
+
72
+ ```sh
73
+ npm install --global riftri
74
+ ```
75
+
76
+ > **Windows on ARM64 is not yet available through npm.** Its platform package
77
+ > is held up in registry review, so npm installs there resolve without a
78
+ > binary. Use the PowerShell installer above until that clears. Every other
79
+ > platform — macOS, Linux, and Windows x64 — installs normally.
73
80
 
74
81
  ## Quick start
75
82
 
@@ -155,9 +162,11 @@ $ sudo riftri overlayfs install-helper
155
162
  ```
156
163
 
157
164
  **Riftri fails closed.** When a checkout cannot be reproduced exactly — custom
158
- filters, sparse checkout, submodules, external attributes, checkout hooks, or non-canonical
159
- [Git LFS](docs/git-lfs.md) setups — it stops before changing anything rather
160
- than silently falling back to a full copy. See
165
+ filters, a repository's own sparse-checkout profile, submodules, external
166
+ attributes, checkout hooks, or non-canonical [Git LFS](docs/git-lfs.md) setups
167
+ — it stops before changing anything rather than silently falling back to a full
168
+ copy. Sparse worktrees are supported when you ask for them explicitly, with
169
+ [`--sparse-dir` cone selection](docs/sparse-checkout.md). See
161
170
  [how Riftri stays safe](docs/safety.md).
162
171
 
163
172
  ## Common commands
package/bin/riftri.js CHANGED
@@ -50,22 +50,18 @@ if (process.platform !== "win32") {
50
50
  }
51
51
  }
52
52
 
53
- function releaseSignals() {
54
- for (const signal of ignoredSignals) {
55
- process.removeListener(signal, ignoreSignal);
56
- }
57
- for (const signal of forwardedSignals) {
58
- process.removeListener(signal, forwardSignal);
59
- }
60
- }
53
+ // The handlers deliberately stay installed until the process exits. Removing
54
+ // the last listener for a signal restores its default disposition, so a SIGINT
55
+ // still in flight — the common case, since the terminal sent it to the whole
56
+ // foreground group — would kill the launcher in the window before
57
+ // `process.exit`, losing the command's real status. `process.exit` does not
58
+ // need them removed.
61
59
 
62
60
  child.on("error", (error) => {
63
- releaseSignals();
64
61
  fail(`could not start the native executable at ${binary}: ${error.message}`);
65
62
  });
66
63
 
67
64
  child.on("exit", (code, signal) => {
68
- releaseSignals();
69
65
  if (signal !== null) {
70
66
  const exitCode = signalExitCode(signal);
71
67
  if (exitCode === null) {
@@ -0,0 +1,179 @@
1
+ /** Programmatic API for the Riftri CLI. */
2
+
3
+ export const EXIT_SUCCESS: 0;
4
+ export const EXIT_OPERATIONAL: 1;
5
+ export const EXIT_USAGE: 2;
6
+ export const EXIT_POLICY: 3;
7
+
8
+ /** One versioned failure receipt, as emitted by `--json-errors`. */
9
+ export interface FailureReceipt {
10
+ schemaVersion: number;
11
+ outcome: "failed";
12
+ operation: string;
13
+ code: string;
14
+ category: "policy" | "operational";
15
+ message: string;
16
+ phase: string | null;
17
+ cleanup: string;
18
+ recovery: "required" | "not-required" | "retry" | "inspect" | "unknown";
19
+ nextCommand: string | null;
20
+ }
21
+
22
+ export class RiftriError extends Error {
23
+ /**
24
+ * Always a number. A process killed by a signal reports 128 plus the signal
25
+ * number, the convention native `riftri exec` uses for its own children.
26
+ */
27
+ readonly exitCode: number;
28
+ /** The signal that killed the process, or null if it exited on its own. */
29
+ readonly signal: NodeJS.Signals | null;
30
+ /** The process was killed rather than exiting on its own. */
31
+ readonly wasSignalled: boolean;
32
+ readonly receipt: FailureReceipt | null;
33
+ /** Riftri declined before touching anything; falling back is safe. */
34
+ readonly isPolicyRefusal: boolean;
35
+ /** The request was malformed; never retry it unchanged. */
36
+ readonly isUsageError: boolean;
37
+ /** A live process holds the lock; waiting and retrying is correct. */
38
+ readonly isBusy: boolean;
39
+ /** Run `receipt.nextCommand` before continuing. */
40
+ readonly needsRepair: boolean;
41
+ }
42
+
43
+ export interface DoctorReport {
44
+ project_stage: string;
45
+ operating_system: string;
46
+ architecture: string;
47
+ /** Whether a usable copy-on-write backend exists here. */
48
+ cow_backend_active: boolean;
49
+ /** Whether this repository has opted in with `enable()`. */
50
+ repository_enabled: boolean;
51
+ git_shim_active: boolean;
52
+ destination_readiness: {
53
+ destination: string;
54
+ status: string;
55
+ backend: string | null;
56
+ copy_on_write: boolean;
57
+ };
58
+ storage_capabilities: unknown[];
59
+ [key: string]: unknown;
60
+ }
61
+
62
+ export interface AddReport {
63
+ schema_version: number;
64
+ backend: string;
65
+ destination: string;
66
+ destination_native_hex: string;
67
+ commit: string;
68
+ tree: string;
69
+ base_path: string;
70
+ /** False when this add materialized a new immutable base. */
71
+ reused_base: boolean;
72
+ journal_path: string;
73
+ native_path_encoding: string;
74
+ }
75
+
76
+ export interface ManagedWorktree {
77
+ path: string;
78
+ path_native_hex: string;
79
+ repository: string;
80
+ head: string;
81
+ branch: string | null;
82
+ detached: boolean;
83
+ backend: string;
84
+ base_path: string;
85
+ /** Filesystem-accounted bytes this worktree actually occupies. */
86
+ allocated_bytes: number;
87
+ /** Bytes the same content would occupy without sharing. */
88
+ logical_bytes: number;
89
+ locked_reason: string | null;
90
+ prunable_reason: string | null;
91
+ }
92
+
93
+ export interface WorktreeInventory {
94
+ schema_version: number;
95
+ state_directory: string;
96
+ worktrees: ManagedWorktree[];
97
+ native_path_encoding: string;
98
+ }
99
+
100
+ export interface StatusReport {
101
+ schema_version: number;
102
+ state_directory: string;
103
+ bases: unknown[];
104
+ worktrees: unknown[];
105
+ operations: unknown[];
106
+ diagnostic_issues: unknown[];
107
+ total_allocated_bytes: number;
108
+ total_logical_bytes: number;
109
+ [key: string]: unknown;
110
+ }
111
+
112
+ export interface RepairReport {
113
+ schema_version: number;
114
+ scanned: number;
115
+ active: number;
116
+ errors: unknown[];
117
+ [key: string]: unknown;
118
+ }
119
+
120
+ export interface AddOptions {
121
+ /** Create and check out a new branch. */
122
+ branch?: string;
123
+ /** Create a detached worktree instead of a branch. */
124
+ detach?: boolean;
125
+ /** Commit-ish to start from; defaults to HEAD. */
126
+ revision?: string;
127
+ /** Cone-mode sparse directories to include. */
128
+ sparseDir?: string[];
129
+ }
130
+
131
+ export interface RiftriOptions {
132
+ /** Working directory for every command. Defaults to `process.cwd()`. */
133
+ repository?: string;
134
+ /** Explicit executable; defaults to the installed platform binary. */
135
+ binary?: string;
136
+ /** Passed as `--state-dir` where the command supports it. */
137
+ stateDir?: string;
138
+ }
139
+
140
+ export class Riftri {
141
+ constructor(options?: RiftriOptions);
142
+ repository: string;
143
+
144
+ /** Inspect Git and storage. Creates nothing. */
145
+ doctor(options?: { destination?: string }): Promise<DoctorReport>;
146
+ backends(path?: string): Promise<unknown>;
147
+ /**
148
+ * True when this destination can be optimized.
149
+ *
150
+ * Resolves `false` only when Riftri answers the question: no active
151
+ * copy-on-write backend, a destination Riftri reports as `blocked`, or an
152
+ * outright refusal. A missing or non-executable binary, an unreadable
153
+ * repository, or output that is not JSON rejects instead.
154
+ *
155
+ * A `needs-activation` destination is optimizable: `worktree.add` works
156
+ * without `enable()`, which only gates Git interception.
157
+ */
158
+ isOptimizable(destination?: string): Promise<boolean>;
159
+
160
+ enable(): Promise<null>;
161
+ disable(): Promise<null>;
162
+
163
+ status(): Promise<StatusReport>;
164
+ repair(): Promise<RepairReport>;
165
+ gc(options?: { apply?: boolean }): Promise<unknown>;
166
+
167
+ /** Escape hatch: run any command and parse its report. */
168
+ run(args: string[], options?: { json?: boolean; cwd?: string }): Promise<unknown>;
169
+
170
+ readonly worktree: {
171
+ add(destination: string, options?: AddOptions): Promise<AddReport>;
172
+ list(options?: { allStates?: boolean }): Promise<WorktreeInventory>;
173
+ /** Refuses a dirty worktree unless `force` is set. */
174
+ remove(destination: string, options?: { force?: boolean }): Promise<unknown>;
175
+ move(source: string, destination: string): Promise<unknown>;
176
+ compact(destination: string): Promise<unknown>;
177
+ prune(): Promise<unknown>;
178
+ };
179
+ }
package/lib/client.js ADDED
@@ -0,0 +1,289 @@
1
+ "use strict";
2
+
3
+ // Programmatic API for the Riftri CLI.
4
+ //
5
+ // The binary is the implementation; this is a typed, promise-returning wrapper
6
+ // around it. Every method runs one command, returns its parsed `--json` report,
7
+ // and throws a RiftriError carrying the parsed `--json-errors` receipt on
8
+ // failure. Nothing here reimplements Riftri behaviour.
9
+
10
+ const { spawn } = require("node:child_process");
11
+ const { resolveBinary } = require("./platform.js");
12
+ const { signalExitCode } = require("./signals.js");
13
+
14
+ /** Process exit codes. Documented in docs/cli.md. */
15
+ const EXIT_SUCCESS = 0;
16
+ const EXIT_OPERATIONAL = 1;
17
+ const EXIT_USAGE = 2;
18
+ const EXIT_POLICY = 3;
19
+
20
+ /** Commands that accept `--json`. The rest print human text on success. */
21
+ const REPORTING = new Set([
22
+ "doctor",
23
+ "backends",
24
+ "status",
25
+ "repair",
26
+ "gc",
27
+ "worktree",
28
+ ]);
29
+
30
+ class RiftriError extends Error {
31
+ constructor(exitCode, receipt, stderr, signal = null) {
32
+ super(
33
+ receipt?.message ||
34
+ stderr.trim() ||
35
+ (signal ? `riftri terminated by ${signal}` : `riftri exited ${exitCode}`),
36
+ );
37
+ this.name = "RiftriError";
38
+ // Always a number. A signalled process reports 128 + the signal number,
39
+ // the convention native `riftri exec` already uses for its own children.
40
+ this.exitCode = exitCode;
41
+ this.signal = signal;
42
+ this.receipt = receipt ?? null;
43
+ }
44
+
45
+ /** The process was killed rather than exiting on its own. */
46
+ get wasSignalled() {
47
+ return this.signal !== null;
48
+ }
49
+
50
+ /** Riftri declined before touching anything. Falling back is safe. */
51
+ get isPolicyRefusal() {
52
+ return this.exitCode === EXIT_POLICY;
53
+ }
54
+
55
+ /** The request itself was malformed. Never retry unchanged. */
56
+ get isUsageError() {
57
+ return this.exitCode === EXIT_USAGE;
58
+ }
59
+
60
+ /** A live process holds the lock; unlike a refusal, waiting helps. */
61
+ get isBusy() {
62
+ return this.receipt?.code === "worktree-busy";
63
+ }
64
+
65
+ /** Run `nextCommand` (usually `riftri repair`) before continuing. */
66
+ get needsRepair() {
67
+ return this.receipt?.recovery === "required";
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Place Riftri's own flags before any `--`. Appending them instead puts them
73
+ * inside the payload `riftri exec` hands to the child, which then fails on a
74
+ * flag meant for Riftri.
75
+ */
76
+ function withRiftriFlags(args, flags) {
77
+ if (flags.length === 0) return [...args];
78
+ const boundary = args.indexOf("--");
79
+ if (boundary === -1) return [...args, ...flags];
80
+ return [...args.slice(0, boundary), ...flags, ...args.slice(boundary)];
81
+ }
82
+
83
+ function flagsFor(options) {
84
+ const flags = [];
85
+ for (const [key, value] of Object.entries(options)) {
86
+ if (value === undefined || value === null || value === false) continue;
87
+ const flag = `--${key.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`)}`;
88
+ if (value === true) flags.push(flag);
89
+ else if (Array.isArray(value)) for (const item of value) flags.push(flag, String(item));
90
+ else flags.push(flag, String(value));
91
+ }
92
+ return flags;
93
+ }
94
+
95
+ class Riftri {
96
+ /**
97
+ * @param {object} [options]
98
+ * @param {string} [options.repository] working directory for every command
99
+ * @param {string} [options.binary] explicit riftri executable
100
+ * @param {string} [options.stateDir] passed as --state-dir where supported
101
+ */
102
+ constructor(options = {}) {
103
+ this.repository = options.repository ?? process.cwd();
104
+ this.binary = options.binary ?? null;
105
+ this.stateDir = options.stateDir ?? null;
106
+ this.worktree = {
107
+ add: this.#worktreeAdd.bind(this),
108
+ list: this.#worktreeList.bind(this),
109
+ remove: this.#worktreeRemove.bind(this),
110
+ move: this.#worktreeMove.bind(this),
111
+ compact: this.#worktreeCompact.bind(this),
112
+ prune: this.#worktreePrune.bind(this),
113
+ };
114
+ }
115
+
116
+ /** Resolve the executable: explicit, else the installed platform package. */
117
+ #executable() {
118
+ if (this.binary) return this.binary;
119
+ this.binary = resolveBinary();
120
+ return this.binary;
121
+ }
122
+
123
+ /**
124
+ * Run one command and resolve with its parsed report, or null when the
125
+ * command does not support `--json`.
126
+ */
127
+ run(args, { json = REPORTING.has(args[0]), cwd = this.repository } = {}) {
128
+ const flags = json ? ["--json", "--json-errors"] : ["--json-errors"];
129
+ return new Promise((resolve, reject) => {
130
+ let child;
131
+ try {
132
+ child = spawn(this.#executable(), withRiftriFlags(args, flags), {
133
+ cwd,
134
+ stdio: ["ignore", "pipe", "pipe"],
135
+ });
136
+ } catch (error) {
137
+ reject(error); // no installed binary for this platform
138
+ return;
139
+ }
140
+ let stdout = "";
141
+ let stderr = "";
142
+ child.stdout.on("data", (chunk) => (stdout += chunk));
143
+ child.stderr.on("data", (chunk) => (stderr += chunk));
144
+ child.on("error", reject);
145
+ child.on("close", (exitCode, signal) => {
146
+ // A signalled child reports a null exit code. Translate it rather than
147
+ // letting `null` reach callers through a field typed as a number.
148
+ if (signal) {
149
+ const code = signalExitCode(signal) ?? EXIT_OPERATIONAL;
150
+ reject(new RiftriError(code, null, stderr, signal));
151
+ return;
152
+ }
153
+ if (exitCode === EXIT_SUCCESS) {
154
+ if (!json || !stdout.trim()) {
155
+ resolve(null);
156
+ return;
157
+ }
158
+ try {
159
+ resolve(JSON.parse(stdout));
160
+ } catch (error) {
161
+ // Thrown from a close handler this would be uncatchable and would
162
+ // take the host process down, so it has to reject instead.
163
+ reject(
164
+ new RiftriError(
165
+ EXIT_OPERATIONAL,
166
+ null,
167
+ `riftri ${args.join(" ")} returned output that is not JSON: ` +
168
+ `${error.message}\n${stdout.slice(0, 2000)}`,
169
+ ),
170
+ );
171
+ }
172
+ return;
173
+ }
174
+ let receipt = null;
175
+ try {
176
+ receipt = JSON.parse(stderr);
177
+ } catch {
178
+ // Usage errors come from the argument parser and carry no receipt.
179
+ }
180
+ reject(new RiftriError(exitCode, receipt, stderr));
181
+ });
182
+ });
183
+ }
184
+
185
+ #stateFlag() {
186
+ return this.stateDir ? ["--state-dir", this.stateDir] : [];
187
+ }
188
+
189
+ /** Inspect Git and storage without creating anything. */
190
+ doctor(options = {}) {
191
+ return this.run(["doctor", ...flagsFor(options)]);
192
+ }
193
+
194
+ /** Probe storage backends for a destination volume. */
195
+ backends(pathOrOptions = {}) {
196
+ const target = typeof pathOrOptions === "string" ? [pathOrOptions] : [];
197
+ return this.run(["backends", ...target]);
198
+ }
199
+
200
+ /**
201
+ * True when this destination can get an optimized worktree.
202
+ *
203
+ * `false` is an answer from Riftri: either the report says no backend is
204
+ * active, or Riftri refused this destination outright. A broken
205
+ * installation, an unreadable repository, or any other unexpected failure
206
+ * rejects instead, so a caller can tell "not supported here" apart from
207
+ * "this client cannot run at all".
208
+ */
209
+ async isOptimizable(destination) {
210
+ try {
211
+ const report = await this.doctor(destination ? { destination } : {});
212
+ if (!report?.cow_backend_active) return false;
213
+ // cow_backend_active is a fact about the volume, not about this
214
+ // destination. Outside a Git repository it is still true while
215
+ // `worktree add` cannot possibly succeed, so the readiness verdict
216
+ // decides. `needs-activation` stays optimizable: the explicit interface
217
+ // works without `enable`, which only gates Git interception.
218
+ const readiness = report.destination_readiness?.status;
219
+ return readiness === undefined || readiness !== "blocked";
220
+ } catch (error) {
221
+ if (error instanceof RiftriError && error.isPolicyRefusal) return false;
222
+ throw error;
223
+ }
224
+ }
225
+
226
+ /** Opt one repository in. Required before interception applies. */
227
+ enable() {
228
+ return this.run(["enable"], { json: false });
229
+ }
230
+
231
+ disable() {
232
+ return this.run(["disable"], { json: false });
233
+ }
234
+
235
+ /** Bases, reference counts, storage use, and pending operations. */
236
+ status() {
237
+ return this.run(["status", ...this.#stateFlag()]);
238
+ }
239
+
240
+ /** Resume or roll back interrupted operations. Safe to run repeatedly. */
241
+ repair() {
242
+ return this.run(["repair", ...this.#stateFlag()]);
243
+ }
244
+
245
+ /** Plan base collection; pass `{ apply: true }` to delete. */
246
+ gc({ apply = false } = {}) {
247
+ const args = ["gc", ...this.#stateFlag()];
248
+ if (apply) args.push("--apply", "--yes");
249
+ return this.run(args);
250
+ }
251
+
252
+ #worktreeAdd(destination, options = {}) {
253
+ const { revision, ...rest } = options;
254
+ const args = ["worktree", "add", destination];
255
+ if (revision) args.push(revision);
256
+ return this.run([...args, ...flagsFor(rest), ...this.#stateFlag()]);
257
+ }
258
+
259
+ #worktreeList(options = {}) {
260
+ return this.run(["worktree", "list", ...flagsFor(options), ...this.#stateFlag()]);
261
+ }
262
+
263
+ #worktreeRemove(destination, { force = false } = {}) {
264
+ const args = ["worktree", "remove", destination];
265
+ if (force) args.push("--force", "--yes");
266
+ return this.run([...args, ...this.#stateFlag()]);
267
+ }
268
+
269
+ #worktreeMove(source, destination) {
270
+ return this.run(["worktree", "move", source, destination, ...this.#stateFlag()]);
271
+ }
272
+
273
+ #worktreeCompact(destination) {
274
+ return this.run(["worktree", "compact", destination, ...this.#stateFlag()]);
275
+ }
276
+
277
+ #worktreePrune() {
278
+ return this.run(["worktree", "prune", ...this.#stateFlag()]);
279
+ }
280
+ }
281
+
282
+ module.exports = {
283
+ Riftri,
284
+ RiftriError,
285
+ EXIT_SUCCESS,
286
+ EXIT_OPERATIONAL,
287
+ EXIT_USAGE,
288
+ EXIT_POLICY,
289
+ };
package/lib/platform.js CHANGED
@@ -14,6 +14,15 @@ const PLATFORM_PACKAGES = Object.freeze({
14
14
  "win32-x64": "riftri-win32-x64",
15
15
  });
16
16
 
17
+ // Native packages the registry has refused, mapped to the route that does work.
18
+ // A name only belongs here while it is genuinely absent from npm; remove the
19
+ // entry in the release that first publishes it.
20
+ const UNPUBLISHED_PACKAGES = Object.freeze({
21
+ "riftri-win32-arm64":
22
+ "install it with the PowerShell installer instead: " +
23
+ "https://riftri.dev/docs/installation",
24
+ });
25
+
17
26
  function detectLinuxLibc(report = process.report) {
18
27
  if (!report || typeof report.getReport !== "function") {
19
28
  return "unknown";
@@ -102,8 +111,13 @@ function resolveBinary(options = {}) {
102
111
  try {
103
112
  packageJson = require.resolve(`${packageName}/package.json`);
104
113
  } catch {
114
+ // Reinstalling cannot help when the package was never published, so say so
115
+ // instead of sending the user around the same loop.
116
+ const unpublished = UNPUBLISHED_PACKAGES[packageName];
105
117
  throw new Error(
106
- `the optional native package ${packageName} is missing; reinstall without --omit=optional, or build Riftri from source`,
118
+ unpublished
119
+ ? `${packageName} is not published to npm, so riftri cannot run on this platform through npm; ${unpublished}`
120
+ : `the optional native package ${packageName} is missing; reinstall without --omit=optional, or build Riftri from source`,
107
121
  );
108
122
  }
109
123
 
@@ -125,6 +139,7 @@ function resolveBinary(options = {}) {
125
139
 
126
140
  module.exports = {
127
141
  PLATFORM_PACKAGES,
142
+ UNPUBLISHED_PACKAGES,
128
143
  assertCompatiblePackageManifest,
129
144
  binaryName,
130
145
  detectLinuxLibc,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "riftri",
3
- "version": "0.3.5",
3
+ "version": "0.4.1",
4
4
  "description": "Experimental lightweight Git workspaces for parallel development",
5
5
  "license": "MIT",
6
6
  "author": "Assistant UI contributors",
@@ -18,14 +18,14 @@
18
18
  "node": ">=18.18"
19
19
  },
20
20
  "optionalDependencies": {
21
- "riftri-darwin-arm64": "0.3.5",
22
- "riftri-darwin-x64": "0.3.5",
23
- "riftri-linux-arm64-gnu": "0.3.5",
24
- "riftri-linux-arm64-musl": "0.3.5",
25
- "riftri-linux-x64-gnu": "0.3.5",
26
- "riftri-linux-x64-musl": "0.3.5",
27
- "riftri-win32-arm64": "0.3.5",
28
- "riftri-win32-x64": "0.3.5"
21
+ "riftri-darwin-arm64": "0.4.1",
22
+ "riftri-darwin-x64": "0.4.1",
23
+ "riftri-linux-arm64-gnu": "0.4.1",
24
+ "riftri-linux-arm64-musl": "0.4.1",
25
+ "riftri-linux-x64-gnu": "0.4.1",
26
+ "riftri-linux-x64-musl": "0.4.1",
27
+ "riftri-win32-arm64": "0.4.1",
28
+ "riftri-win32-x64": "0.4.1"
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
@@ -45,5 +45,14 @@
45
45
  "publishConfig": {
46
46
  "access": "public",
47
47
  "provenance": true
48
+ },
49
+ "main": "lib/client.js",
50
+ "types": "lib/client.d.ts",
51
+ "exports": {
52
+ ".": {
53
+ "types": "./lib/client.d.ts",
54
+ "require": "./lib/client.js",
55
+ "default": "./lib/client.js"
56
+ }
48
57
  }
49
58
  }