@limrun/api 0.19.3 → 0.20.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.
Files changed (68) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/client.d.mts +3 -2
  3. package/client.d.mts.map +1 -1
  4. package/client.d.ts +3 -2
  5. package/client.d.ts.map +1 -1
  6. package/client.js +11 -3
  7. package/client.js.map +1 -1
  8. package/client.mjs +11 -3
  9. package/client.mjs.map +1 -1
  10. package/exec-client.d.mts +106 -0
  11. package/exec-client.d.mts.map +1 -0
  12. package/exec-client.d.ts +106 -0
  13. package/exec-client.d.ts.map +1 -0
  14. package/exec-client.js +274 -0
  15. package/exec-client.js.map +1 -0
  16. package/exec-client.mjs +268 -0
  17. package/exec-client.mjs.map +1 -0
  18. package/folder-sync.d.mts +16 -2
  19. package/folder-sync.d.mts.map +1 -1
  20. package/folder-sync.d.ts +16 -2
  21. package/folder-sync.d.ts.map +1 -1
  22. package/folder-sync.js +43 -14
  23. package/folder-sync.js.map +1 -1
  24. package/folder-sync.mjs +43 -13
  25. package/folder-sync.mjs.map +1 -1
  26. package/index.d.mts +2 -0
  27. package/index.d.mts.map +1 -1
  28. package/index.d.ts +2 -0
  29. package/index.d.ts.map +1 -1
  30. package/index.js +5 -1
  31. package/index.js.map +1 -1
  32. package/index.mjs +2 -0
  33. package/index.mjs.map +1 -1
  34. package/internal/parse.d.mts.map +1 -1
  35. package/internal/parse.d.ts.map +1 -1
  36. package/internal/parse.js +5 -0
  37. package/internal/parse.js.map +1 -1
  38. package/internal/parse.mjs +5 -0
  39. package/internal/parse.mjs.map +1 -1
  40. package/ios-client.d.mts +10 -3
  41. package/ios-client.d.mts.map +1 -1
  42. package/ios-client.d.ts +10 -3
  43. package/ios-client.d.ts.map +1 -1
  44. package/ios-client.js +19 -4
  45. package/ios-client.js.map +1 -1
  46. package/ios-client.mjs +18 -3
  47. package/ios-client.mjs.map +1 -1
  48. package/package.json +23 -1
  49. package/sandbox-client.d.mts +124 -0
  50. package/sandbox-client.d.mts.map +1 -0
  51. package/sandbox-client.d.ts +124 -0
  52. package/sandbox-client.d.ts.map +1 -0
  53. package/sandbox-client.js +149 -0
  54. package/sandbox-client.js.map +1 -0
  55. package/sandbox-client.mjs +146 -0
  56. package/sandbox-client.mjs.map +1 -0
  57. package/src/client.ts +17 -5
  58. package/src/exec-client.ts +327 -0
  59. package/src/folder-sync.ts +66 -18
  60. package/src/index.ts +16 -0
  61. package/src/internal/parse.ts +6 -0
  62. package/src/ios-client.ts +35 -5
  63. package/src/sandbox-client.ts +267 -0
  64. package/src/version.ts +1 -1
  65. package/version.d.mts +1 -1
  66. package/version.d.ts +1 -1
  67. package/version.js +1 -1
  68. package/version.mjs +1 -1
@@ -0,0 +1,124 @@
1
+ import { ExecChildProcess } from "./exec-client.js";
2
+ export type LogLevel = 'none' | 'error' | 'warn' | 'info' | 'debug';
3
+ /**
4
+ * Build configuration for xcodebuild command.
5
+ */
6
+ export type XcodeBuildConfig = {
7
+ workspace?: string;
8
+ project?: string;
9
+ scheme?: string;
10
+ };
11
+ /**
12
+ * Simulator connection details for configuring the sandbox.
13
+ */
14
+ export type SimulatorConfig = {
15
+ /** The API URL of the simulator (limulator) */
16
+ apiUrl: string;
17
+ /** Auth token for the simulator. If not provided, uses the sandbox token. */
18
+ token?: string;
19
+ };
20
+ /**
21
+ * Options for syncing source code to the sandbox.
22
+ */
23
+ export type SyncOptions = {
24
+ /**
25
+ * Cache scoping key for delta basis caching. Defaults to 'xcode-sandbox'.
26
+ * This is not sent to the server.
27
+ */
28
+ cacheKey?: string;
29
+ basisCacheDir?: string;
30
+ maxPatchBytes?: number;
31
+ /**
32
+ * If true, watch the folder and re-sync on any changes.
33
+ */
34
+ watch?: boolean;
35
+ log?: (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => void;
36
+ /**
37
+ * Optional filter function to include/exclude files and directories.
38
+ * Called with the relative path from the sync root (using forward slashes).
39
+ * For directories, the path ends with '/'.
40
+ * Return true to include, false to exclude.
41
+ *
42
+ * @example
43
+ * // Exclude build folder
44
+ * filter: (path) => !path.startsWith('build/')
45
+ *
46
+ * @example
47
+ * // Only include source files
48
+ * filter: (path) => path.startsWith('src/') || path.endsWith('.json')
49
+ */
50
+ filter?: (relativePath: string) => boolean;
51
+ };
52
+ /**
53
+ * Result of a sync operation.
54
+ */
55
+ export type SyncResult = {
56
+ /** Present only when watch=true; call to stop watching */
57
+ stopWatching?: () => void;
58
+ };
59
+ /**
60
+ * Client for interacting with a sandboxed Xcode build service.
61
+ */
62
+ export type XCodeSandboxClient = {
63
+ /**
64
+ * Sync source code to the sandbox. In watch mode, keeps syncing on changes.
65
+ * Does NOT trigger builds - call xcodebuild() when ready.
66
+ */
67
+ sync: (localCodePath: string, opts?: SyncOptions) => Promise<SyncResult>;
68
+ /**
69
+ * Trigger xcodebuild on the synced source code.
70
+ * Returns a ChildProcess-like object for streaming output.
71
+ *
72
+ * @example
73
+ * // Stream build output
74
+ * const build = client.xcodebuild();
75
+ * build.stdout.on('data', (line) => console.log(line));
76
+ * const { exitCode } = await build;
77
+ */
78
+ xcodebuild: (opts?: XcodeBuildConfig) => ExecChildProcess;
79
+ };
80
+ export type CreateXCodeSandboxClientOptions = {
81
+ /** The API URL of the Xcode sandbox server */
82
+ apiUrl: string;
83
+ /** Auth token for the sandbox */
84
+ token: string;
85
+ /**
86
+ * Simulator (limulator) connection details. Only needed if the sandbox is not
87
+ * already configured (e.g., when created outside of an iOS instance).
88
+ * When provided, the client will call POST /simulator to set up the connection.
89
+ */
90
+ simulator?: SimulatorConfig;
91
+ /**
92
+ * Controls logging verbosity
93
+ * @default 'info'
94
+ */
95
+ logLevel?: LogLevel;
96
+ };
97
+ /**
98
+ * Creates a client for interacting with a sandboxed Xcode build service.
99
+ *
100
+ * @example
101
+ * // When using an iOS instance (simulator already configured):
102
+ * const client = await createXCodeSandboxClient({
103
+ * apiUrl: instance.status.sandbox.xcode.url,
104
+ * token: apiKey,
105
+ * });
106
+ *
107
+ * // When using a standalone sandbox (need to configure simulator):
108
+ * const client = await createXCodeSandboxClient({
109
+ * apiUrl: 'https://sandbox.example.com',
110
+ * token: 'xxx',
111
+ * simulator: {
112
+ * apiUrl: 'https://limulator.example.com',
113
+ * token: 'yyy', // optional, defaults to sandbox token
114
+ * },
115
+ * });
116
+ *
117
+ * // Sync code and build
118
+ * await client.sync('./my-ios-app', { watch: true });
119
+ * const build = client.xcodebuild();
120
+ * build.stdout.on('data', (line) => console.log('[build]', line));
121
+ * const { exitCode } = await build;
122
+ */
123
+ export declare function createXCodeSandboxClient(options: CreateXCodeSandboxClientOptions): Promise<XCodeSandboxClient>;
124
+ //# sourceMappingURL=sandbox-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-client.d.ts","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":"OACO,EAAQ,gBAAgB,EAAE;AAEjC,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAEpE;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,6EAA6E;IAC7E,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACxE;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC;CAC5C,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;CAC3B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,IAAI,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAEzE;;;;;;;;;OASG;IACH,UAAU,EAAE,CAAC,IAAI,CAAC,EAAE,gBAAgB,KAAK,gBAAgB,CAAC;CAC3D,CAAC;AAEF,MAAM,MAAM,+BAA+B,GAAG;IAC5C,8CAA8C;IAC9C,MAAM,EAAE,MAAM,CAAC;IACf,iCAAiC;IACjC,KAAK,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,+BAA+B,GACvC,OAAO,CAAC,kBAAkB,CAAC,CAoI7B"}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createXCodeSandboxClient = createXCodeSandboxClient;
4
+ const folder_sync_1 = require("./folder-sync.js");
5
+ const exec_client_1 = require("./exec-client.js");
6
+ /**
7
+ * Creates a client for interacting with a sandboxed Xcode build service.
8
+ *
9
+ * @example
10
+ * // When using an iOS instance (simulator already configured):
11
+ * const client = await createXCodeSandboxClient({
12
+ * apiUrl: instance.status.sandbox.xcode.url,
13
+ * token: apiKey,
14
+ * });
15
+ *
16
+ * // When using a standalone sandbox (need to configure simulator):
17
+ * const client = await createXCodeSandboxClient({
18
+ * apiUrl: 'https://sandbox.example.com',
19
+ * token: 'xxx',
20
+ * simulator: {
21
+ * apiUrl: 'https://limulator.example.com',
22
+ * token: 'yyy', // optional, defaults to sandbox token
23
+ * },
24
+ * });
25
+ *
26
+ * // Sync code and build
27
+ * await client.sync('./my-ios-app', { watch: true });
28
+ * const build = client.xcodebuild();
29
+ * build.stdout.on('data', (line) => console.log('[build]', line));
30
+ * const { exitCode } = await build;
31
+ */
32
+ async function createXCodeSandboxClient(options) {
33
+ const logLevel = options.logLevel ?? 'info';
34
+ const logger = {
35
+ debug: (...args) => {
36
+ if (logLevel === 'debug')
37
+ console.log('[XCodeSandbox]', ...args);
38
+ },
39
+ info: (...args) => {
40
+ if (logLevel === 'info' || logLevel === 'debug')
41
+ console.log('[XCodeSandbox]', ...args);
42
+ },
43
+ warn: (...args) => {
44
+ if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
45
+ console.warn('[XCodeSandbox]', ...args);
46
+ },
47
+ error: (...args) => {
48
+ if (logLevel !== 'none')
49
+ console.error('[XCodeSandbox]', ...args);
50
+ },
51
+ };
52
+ const logFn = (level, msg) => {
53
+ switch (level) {
54
+ case 'debug':
55
+ logger.debug(msg);
56
+ break;
57
+ case 'info':
58
+ logger.info(msg);
59
+ break;
60
+ case 'warn':
61
+ logger.warn(msg);
62
+ break;
63
+ case 'error':
64
+ logger.error(msg);
65
+ break;
66
+ default:
67
+ logger.info(msg);
68
+ break;
69
+ }
70
+ };
71
+ // Configure the simulator connection if provided
72
+ if (options.simulator) {
73
+ const cfg = {
74
+ simulatorApiUrl: options.simulator.apiUrl,
75
+ simulatorToken: options.simulator.token ?? options.token,
76
+ };
77
+ const res = await fetch(`${options.apiUrl}/simulator`, {
78
+ method: 'POST',
79
+ headers: {
80
+ 'Content-Type': 'application/json',
81
+ Authorization: `Bearer ${options.token}`,
82
+ },
83
+ body: JSON.stringify(cfg),
84
+ });
85
+ const text = await res.text();
86
+ if (!res.ok) {
87
+ throw new Error(`POST /simulator failed: ${res.status} ${text}`);
88
+ }
89
+ }
90
+ return {
91
+ async sync(localCodePath, opts) {
92
+ const codeSyncOpts = {
93
+ apiUrl: options.apiUrl,
94
+ token: options.token,
95
+ udid: opts?.cacheKey ?? 'xcode-sandbox',
96
+ install: false,
97
+ filter: (relativePath) => {
98
+ if (relativePath.startsWith('build/') ||
99
+ relativePath.startsWith('.build/') ||
100
+ relativePath.startsWith('DerivedData/') ||
101
+ relativePath.startsWith('Index.noindex/') ||
102
+ relativePath.startsWith('ModuleCache.noindex/') ||
103
+ relativePath.startsWith('.index-build/')) {
104
+ return false;
105
+ }
106
+ if (relativePath.startsWith('.swiftpm/') ||
107
+ relativePath.startsWith('Pods/') ||
108
+ relativePath.startsWith('Carthage/Build/')) {
109
+ return false;
110
+ }
111
+ if (relativePath.startsWith('.git/') ||
112
+ relativePath.startsWith('.limsync-cache/') ||
113
+ relativePath === '.DS_Store' ||
114
+ relativePath.endsWith('/.DS_Store')) {
115
+ return false;
116
+ }
117
+ if (relativePath.includes('/xcuserdata/')) {
118
+ return false;
119
+ }
120
+ if (relativePath.includes('.dSYM/')) {
121
+ return false;
122
+ }
123
+ // User-provided filter
124
+ if (opts?.filter && !opts.filter(relativePath)) {
125
+ return false;
126
+ }
127
+ return true;
128
+ },
129
+ ...(opts?.basisCacheDir ? { basisCacheDir: opts.basisCacheDir } : {}),
130
+ ...(opts?.maxPatchBytes !== undefined ? { maxPatchBytes: opts.maxPatchBytes } : {}),
131
+ ...(opts?.watch !== undefined ? { watch: opts.watch } : {}),
132
+ log: opts?.log ?? logFn,
133
+ };
134
+ const result = await (0, folder_sync_1.syncApp)(localCodePath, codeSyncOpts);
135
+ if (result.stopWatching) {
136
+ return { stopWatching: result.stopWatching };
137
+ }
138
+ return {};
139
+ },
140
+ xcodebuild(opts) {
141
+ return (0, exec_client_1.exec)({ command: 'xcodebuild', ...(opts && { xcodebuild: opts }) }, {
142
+ apiUrl: options.apiUrl,
143
+ token: options.token,
144
+ log: logFn,
145
+ });
146
+ },
147
+ };
148
+ }
149
+ //# sourceMappingURL=sandbox-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-client.js","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,146 @@
1
+ import { syncApp as syncFolderImpl } from "./folder-sync.mjs";
2
+ import { exec } from "./exec-client.mjs";
3
+ /**
4
+ * Creates a client for interacting with a sandboxed Xcode build service.
5
+ *
6
+ * @example
7
+ * // When using an iOS instance (simulator already configured):
8
+ * const client = await createXCodeSandboxClient({
9
+ * apiUrl: instance.status.sandbox.xcode.url,
10
+ * token: apiKey,
11
+ * });
12
+ *
13
+ * // When using a standalone sandbox (need to configure simulator):
14
+ * const client = await createXCodeSandboxClient({
15
+ * apiUrl: 'https://sandbox.example.com',
16
+ * token: 'xxx',
17
+ * simulator: {
18
+ * apiUrl: 'https://limulator.example.com',
19
+ * token: 'yyy', // optional, defaults to sandbox token
20
+ * },
21
+ * });
22
+ *
23
+ * // Sync code and build
24
+ * await client.sync('./my-ios-app', { watch: true });
25
+ * const build = client.xcodebuild();
26
+ * build.stdout.on('data', (line) => console.log('[build]', line));
27
+ * const { exitCode } = await build;
28
+ */
29
+ export async function createXCodeSandboxClient(options) {
30
+ const logLevel = options.logLevel ?? 'info';
31
+ const logger = {
32
+ debug: (...args) => {
33
+ if (logLevel === 'debug')
34
+ console.log('[XCodeSandbox]', ...args);
35
+ },
36
+ info: (...args) => {
37
+ if (logLevel === 'info' || logLevel === 'debug')
38
+ console.log('[XCodeSandbox]', ...args);
39
+ },
40
+ warn: (...args) => {
41
+ if (logLevel === 'warn' || logLevel === 'info' || logLevel === 'debug')
42
+ console.warn('[XCodeSandbox]', ...args);
43
+ },
44
+ error: (...args) => {
45
+ if (logLevel !== 'none')
46
+ console.error('[XCodeSandbox]', ...args);
47
+ },
48
+ };
49
+ const logFn = (level, msg) => {
50
+ switch (level) {
51
+ case 'debug':
52
+ logger.debug(msg);
53
+ break;
54
+ case 'info':
55
+ logger.info(msg);
56
+ break;
57
+ case 'warn':
58
+ logger.warn(msg);
59
+ break;
60
+ case 'error':
61
+ logger.error(msg);
62
+ break;
63
+ default:
64
+ logger.info(msg);
65
+ break;
66
+ }
67
+ };
68
+ // Configure the simulator connection if provided
69
+ if (options.simulator) {
70
+ const cfg = {
71
+ simulatorApiUrl: options.simulator.apiUrl,
72
+ simulatorToken: options.simulator.token ?? options.token,
73
+ };
74
+ const res = await fetch(`${options.apiUrl}/simulator`, {
75
+ method: 'POST',
76
+ headers: {
77
+ 'Content-Type': 'application/json',
78
+ Authorization: `Bearer ${options.token}`,
79
+ },
80
+ body: JSON.stringify(cfg),
81
+ });
82
+ const text = await res.text();
83
+ if (!res.ok) {
84
+ throw new Error(`POST /simulator failed: ${res.status} ${text}`);
85
+ }
86
+ }
87
+ return {
88
+ async sync(localCodePath, opts) {
89
+ const codeSyncOpts = {
90
+ apiUrl: options.apiUrl,
91
+ token: options.token,
92
+ udid: opts?.cacheKey ?? 'xcode-sandbox',
93
+ install: false,
94
+ filter: (relativePath) => {
95
+ if (relativePath.startsWith('build/') ||
96
+ relativePath.startsWith('.build/') ||
97
+ relativePath.startsWith('DerivedData/') ||
98
+ relativePath.startsWith('Index.noindex/') ||
99
+ relativePath.startsWith('ModuleCache.noindex/') ||
100
+ relativePath.startsWith('.index-build/')) {
101
+ return false;
102
+ }
103
+ if (relativePath.startsWith('.swiftpm/') ||
104
+ relativePath.startsWith('Pods/') ||
105
+ relativePath.startsWith('Carthage/Build/')) {
106
+ return false;
107
+ }
108
+ if (relativePath.startsWith('.git/') ||
109
+ relativePath.startsWith('.limsync-cache/') ||
110
+ relativePath === '.DS_Store' ||
111
+ relativePath.endsWith('/.DS_Store')) {
112
+ return false;
113
+ }
114
+ if (relativePath.includes('/xcuserdata/')) {
115
+ return false;
116
+ }
117
+ if (relativePath.includes('.dSYM/')) {
118
+ return false;
119
+ }
120
+ // User-provided filter
121
+ if (opts?.filter && !opts.filter(relativePath)) {
122
+ return false;
123
+ }
124
+ return true;
125
+ },
126
+ ...(opts?.basisCacheDir ? { basisCacheDir: opts.basisCacheDir } : {}),
127
+ ...(opts?.maxPatchBytes !== undefined ? { maxPatchBytes: opts.maxPatchBytes } : {}),
128
+ ...(opts?.watch !== undefined ? { watch: opts.watch } : {}),
129
+ log: opts?.log ?? logFn,
130
+ };
131
+ const result = await syncFolderImpl(localCodePath, codeSyncOpts);
132
+ if (result.stopWatching) {
133
+ return { stopWatching: result.stopWatching };
134
+ }
135
+ return {};
136
+ },
137
+ xcodebuild(opts) {
138
+ return exec({ command: 'xcodebuild', ...(opts && { xcodebuild: opts }) }, {
139
+ apiUrl: options.apiUrl,
140
+ token: options.token,
141
+ log: logFn,
142
+ });
143
+ },
144
+ };
145
+ }
146
+ //# sourceMappingURL=sandbox-client.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-client.mjs","sourceRoot":"","sources":["src/sandbox-client.ts"],"names":[],"mappings":"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"}
package/src/client.ts CHANGED
@@ -514,9 +514,14 @@ export class Limrun {
514
514
  getAPIList<Item, PageClass extends Pagination.AbstractPage<Item> = Pagination.AbstractPage<Item>>(
515
515
  path: string,
516
516
  Page: new (...args: any[]) => PageClass,
517
- opts?: RequestOptions,
517
+ opts?: PromiseOrValue<RequestOptions>,
518
518
  ): Pagination.PagePromise<PageClass, Item> {
519
- return this.requestAPIList(Page, { method: 'get', path, ...opts });
519
+ return this.requestAPIList(
520
+ Page,
521
+ opts && 'then' in opts ?
522
+ opts.then((opts) => ({ method: 'get', path, ...opts }))
523
+ : { method: 'get', path, ...opts },
524
+ );
520
525
  }
521
526
 
522
527
  requestAPIList<
@@ -524,7 +529,7 @@ export class Limrun {
524
529
  PageClass extends Pagination.AbstractPage<Item> = Pagination.AbstractPage<Item>,
525
530
  >(
526
531
  Page: new (...args: ConstructorParameters<typeof Pagination.AbstractPage>) => PageClass,
527
- options: FinalRequestOptions,
532
+ options: PromiseOrValue<FinalRequestOptions>,
528
533
  ): Pagination.PagePromise<PageClass, Item> {
529
534
  const request = this.makeRequest(options, null, undefined);
530
535
  return new Pagination.PagePromise<PageClass, Item>(this as any as Limrun, request, Page);
@@ -537,9 +542,10 @@ export class Limrun {
537
542
  controller: AbortController,
538
543
  ): Promise<Response> {
539
544
  const { signal, method, ...options } = init || {};
540
- if (signal) signal.addEventListener('abort', () => controller.abort());
545
+ const abort = this._makeAbort(controller);
546
+ if (signal) signal.addEventListener('abort', abort, { once: true });
541
547
 
542
- const timeout = setTimeout(() => controller.abort(), ms);
548
+ const timeout = setTimeout(abort, ms);
543
549
 
544
550
  const isReadableBody =
545
551
  ((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) ||
@@ -706,6 +712,12 @@ export class Limrun {
706
712
  return headers.values;
707
713
  }
708
714
 
715
+ private _makeAbort(controller: AbortController) {
716
+ // note: we can't just inline this method inside `fetchWithTimeout()` because then the closure
717
+ // would capture all request options, and cause a memory leak.
718
+ return () => controller.abort();
719
+ }
720
+
709
721
  private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): {
710
722
  bodyHeaders: HeadersLike;
711
723
  body: BodyInit | undefined;