@limrun/api 0.28.1 → 0.28.3

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 (48) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/client.d.mts.map +1 -1
  3. package/client.d.ts.map +1 -1
  4. package/client.js +11 -0
  5. package/client.js.map +1 -1
  6. package/client.mjs +11 -0
  7. package/client.mjs.map +1 -1
  8. package/folder-sync-ignore.d.mts +2 -0
  9. package/folder-sync-ignore.d.mts.map +1 -1
  10. package/folder-sync-ignore.d.ts +2 -0
  11. package/folder-sync-ignore.d.ts.map +1 -1
  12. package/folder-sync-ignore.js +53 -2
  13. package/folder-sync-ignore.js.map +1 -1
  14. package/folder-sync-ignore.mjs +53 -2
  15. package/folder-sync-ignore.mjs.map +1 -1
  16. package/folder-sync.d.mts +9 -0
  17. package/folder-sync.d.mts.map +1 -1
  18. package/folder-sync.d.ts +9 -0
  19. package/folder-sync.d.ts.map +1 -1
  20. package/folder-sync.js +43 -7
  21. package/folder-sync.js.map +1 -1
  22. package/folder-sync.mjs +43 -7
  23. package/folder-sync.mjs.map +1 -1
  24. package/ios-client.d.mts.map +1 -1
  25. package/ios-client.d.ts.map +1 -1
  26. package/ios-client.js +21 -20
  27. package/ios-client.js.map +1 -1
  28. package/ios-client.mjs +21 -20
  29. package/ios-client.mjs.map +1 -1
  30. package/package.json +1 -1
  31. package/resources/xcode-instances-helpers.d.mts +8 -0
  32. package/resources/xcode-instances-helpers.d.mts.map +1 -1
  33. package/resources/xcode-instances-helpers.d.ts +8 -0
  34. package/resources/xcode-instances-helpers.d.ts.map +1 -1
  35. package/resources/xcode-instances-helpers.js +46 -1
  36. package/resources/xcode-instances-helpers.js.map +1 -1
  37. package/resources/xcode-instances-helpers.mjs +47 -2
  38. package/resources/xcode-instances-helpers.mjs.map +1 -1
  39. package/src/client.ts +12 -0
  40. package/src/folder-sync-ignore.ts +64 -2
  41. package/src/folder-sync.ts +60 -8
  42. package/src/ios-client.ts +21 -20
  43. package/src/resources/xcode-instances-helpers.ts +62 -3
  44. package/src/version.ts +1 -1
  45. package/version.d.mts +1 -1
  46. package/version.d.ts +1 -1
  47. package/version.js +1 -1
  48. package/version.mjs +1 -1
@@ -3,7 +3,7 @@ import path from 'path';
3
3
  import crypto from 'crypto';
4
4
  import { XcodeInstances as GeneratedXcodeInstances } from "./xcode-instances.mjs";
5
5
  import { exec } from "../exec-client.mjs";
6
- import { syncFolder as syncFolderImpl } from "../folder-sync.mjs";
6
+ import { syncFolder as syncFolderImpl, } from "../folder-sync.mjs";
7
7
  import { createIgnoreFn } from "../folder-sync-ignore.mjs";
8
8
  import { nodeProxyTransport } from "../internal/proxy-transport.mjs";
9
9
  function createLogger(logLevel) {
@@ -23,6 +23,38 @@ function createLogger(logLevel) {
23
23
  }
24
24
  };
25
25
  }
26
+ function normalizeWorkspaceRelativePath(remotePath) {
27
+ if (remotePath === '' ||
28
+ remotePath.startsWith('/') ||
29
+ remotePath.includes('\\') ||
30
+ remotePath.includes('\0')) {
31
+ throw new Error(`invalid sandbox home path from server: ${remotePath}`);
32
+ }
33
+ const parts = remotePath.split('/');
34
+ if (parts.some((part) => part === '' || part === '.' || part === '..')) {
35
+ throw new Error(`invalid sandbox home path from server: ${remotePath}`);
36
+ }
37
+ return parts.join('/');
38
+ }
39
+ async function fetchSandboxInfo(apiUrl, token) {
40
+ const res = await nodeProxyTransport.fetch(`${apiUrl}/info`, {
41
+ method: 'GET',
42
+ headers: {
43
+ Authorization: `Bearer ${token}`,
44
+ },
45
+ });
46
+ const text = await res.text();
47
+ if (!res.ok) {
48
+ throw new Error(`GET /info failed: ${res.status} ${text}`);
49
+ }
50
+ const body = JSON.parse(text);
51
+ if (!body.homeDir) {
52
+ throw new Error('GET /info response is missing homeDir');
53
+ }
54
+ return {
55
+ homeDir: normalizeWorkspaceRelativePath(body.homeDir),
56
+ };
57
+ }
26
58
  export class XcodeInstances extends GeneratedXcodeInstances {
27
59
  async createClient(params) {
28
60
  let apiUrl;
@@ -40,6 +72,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
40
72
  }
41
73
  const log = createLogger(params.logLevel ?? 'info');
42
74
  const client = this._client;
75
+ const sandboxInfo = await fetchSandboxInfo(apiUrl, token);
43
76
  return {
44
77
  async sync(localCodePath, opts) {
45
78
  const resolvedPath = path.resolve(localCodePath);
@@ -47,6 +80,12 @@ export class XcodeInstances extends GeneratedXcodeInstances {
47
80
  const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
48
81
  const cacheKey = `limsync-cache-${folderName}-${hash}`;
49
82
  const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
83
+ const additionalFiles = opts?.additionalFiles?.map((file) => ({
84
+ localPath: file.localPath,
85
+ remotePath: file.remotePath.startsWith('~/') ?
86
+ `${sandboxInfo.homeDir}/${file.remotePath.slice(2)}`
87
+ : file.remotePath,
88
+ }));
50
89
  const codeSyncOpts = {
51
90
  apiUrl,
52
91
  token,
@@ -54,6 +93,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
54
93
  install: opts?.install ?? true,
55
94
  ignoreFn: await createIgnoreFn(localCodePath, {
56
95
  basisCacheDir,
96
+ log,
57
97
  additional: (relativePath) => {
58
98
  if (relativePath.startsWith('build/') ||
59
99
  relativePath.startsWith('.build/') ||
@@ -85,6 +125,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
85
125
  maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
86
126
  launchMode: 'ForegroundIfRunning',
87
127
  log,
128
+ ...(additionalFiles ? { additionalFiles } : {}),
88
129
  };
89
130
  const result = await syncFolderImpl(localCodePath, codeSyncOpts);
90
131
  if (result.stopWatching) {
@@ -97,7 +138,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
97
138
  command: 'xcodebuild',
98
139
  ...(settings && { xcodebuild: settings }),
99
140
  };
100
- if (options?.upload) {
141
+ if (options?.upload && 'assetName' in options.upload) {
101
142
  const uploadName = options.upload.assetName;
102
143
  const requestPromise = client.assets
103
144
  .getOrCreate({ name: uploadName })
@@ -111,6 +152,10 @@ export class XcodeInstances extends GeneratedXcodeInstances {
111
152
  });
112
153
  return exec(requestPromise, { apiUrl, token, log });
113
154
  }
155
+ if (options?.upload && 'signedUploadUrl' in options.upload && 'signedDownloadUrl' in options.upload) {
156
+ request.signedUploadUrl = options.upload.signedUploadUrl;
157
+ request.additionalMetadata = { signedDownloadUrl: options.upload.signedDownloadUrl };
158
+ }
114
159
  return exec(request, { apiUrl, token, log });
115
160
  },
116
161
  async attachSimulator(simulator) {
@@ -1 +1 @@
1
- {"version":3,"file":"xcode-instances-helpers.mjs","sourceRoot":"","sources":["../src/resources/xcode-instances-helpers.ts"],"names":[],"mappings":"OAAO,EAAE,MAAM,IAAI;OACZ,IAAI,MAAM,MAAM;OAChB,MAAM,MAAM,QAAQ;OAEpB,EAAE,cAAc,IAAI,uBAAuB,EAAsB;OAEjE,EAAE,IAAI,EAA2C;OACjD,EAAE,UAAU,IAAI,cAAc,EAA0B;OACxD,EAAE,cAAc,EAAE;OAClB,EAAE,kBAAkB,EAAE;AAuE7B,SAAS,YAAY,CAAC,QAAkB;IACtC,MAAM,SAAS,GAAG,CAAC,KAAe,EAAE,EAAE;QACpC,MAAM,MAAM,GAAe,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;QACtE,OAAO,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC3D,CAAC,CAAC;IACF,OAAO,CAAC,KAA0C,EAAE,GAAW,EAAE,EAAE;QACjE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;YAAE,OAAO;QAC9B,MAAM,MAAM,GAAG,iBAAiB,CAAC;QACjC,IAAI,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1C,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,OAAO,cAAe,SAAQ,uBAAuB;IACzD,KAAK,CAAC,YAAY,CAAC,MAA+B;QAChD,IAAI,MAAc,CAAC;QACnB,IAAI,KAAa,CAAC;QAClB,IAAI,UAAU,IAAI,MAAM,EAAE,CAAC;YACzB,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;gBACnC,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;YACjE,CAAC;YACD,MAAM,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;YACvC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;YACvB,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QACvB,CAAC;QAED,MAAM,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,CAAC;QACpD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC;QAE5B,OAAO;YACL,KAAK,CAAC,IAAI,CAAC,aAAqB,EAAE,IAAkB;gBAClD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;gBACjD,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;gBAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gBACtF,MAAM,QAAQ,GAAG,iBAAiB,UAAU,IAAI,IAAI,EAAE,CAAC;gBACvD,MAAM,aAAa,GAAG,IAAI,EAAE,aAAa,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,QAAQ,CAAC,CAAC;gBAC9E,MAAM,YAAY,GAAsB;oBACtC,MAAM;oBACN,KAAK;oBACL,IAAI,EAAE,QAAQ;oBACd,OAAO,EAAE,IAAI,EAAE,OAAO,IAAI,IAAI;oBAC9B,QAAQ,EAAE,MAAM,cAAc,CAAC,aAAa,EAAE;wBAC5C,aAAa;wBACb,UAAU,EAAE,CAAC,YAAoB,EAAE,EAAE;4BACnC,IACE,YAAY,CAAC,UAAU,CAAC,QAAQ,CAAC;gCACjC,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC;gCAClC,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC;gCACvC,YAAY,CAAC,UAAU,CAAC,gBAAgB,CAAC;gCACzC,YAAY,CAAC,UAAU,CAAC,sBAAsB,CAAC;gCAC/C,YAAY,CAAC,UAAU,CAAC,eAAe,CAAC,EACxC,CAAC;gCACD,OAAO,IAAI,CAAC;4BACd,CAAC;4BACD,IACE,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC;gCACpC,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC;gCAChC,YAAY,CAAC,UAAU,CAAC,iBAAiB,CAAC,EAC1C,CAAC;gCACD,OAAO,IAAI,CAAC;4BACd,CAAC;4BACD,IAAI,YAAY,CAAC,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC;gCAC1C,OAAO,IAAI,CAAC;4BACd,CAAC;4BACD,IAAI,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gCACpC,OAAO,IAAI,CAAC;4BACd,CAAC;4BACD,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC;gCACjC,OAAO,IAAI,CAAC;4BACd,CAAC;4BACD,OAAO,KAAK,CAAC;wBACf,CAAC;qBACF,CAAC;oBACF,aAAa;oBACb,KAAK,EAAE,IAAI,EAAE,KAAK,IAAI,IAAI;oBAC1B,aAAa,EAAE,IAAI,EAAE,aAAa,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI;oBACrD,UAAU,EAAE,qBAAqB;oBACjC,GAAG;iBACJ,CAAC;gBAEF,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC;gBACjE,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;oBACxB,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC;gBAC/C,CAAC;gBACD,OAAO,EAAE,CAAC;YACZ,CAAC;YAED,UAAU,CAAC,QAA6B,EAAE,OAA2B;gBACnE,MAAM,OAAO,GAAgB;oBAC3B,OAAO,EAAE,YAAY;oBACrB,GAAG,CAAC,QAAQ,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;iBAC1C,CAAC;gBAEF,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;oBACpB,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;oBAC5C,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM;yBACjC,WAAW,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;yBACjC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE;wBACd,OAAO,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;wBAChD,OAAO,CAAC,kBAAkB,GAAG,EAAE,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,EAAE,CAAC;wBAC5E,OAAO,OAAO,CAAC;oBACjB,CAAC,CAAC;yBACD,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;wBACb,MAAM,IAAI,KAAK,CACb,6CAA6C,UAAU,MACrD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GACvC,EAAE,CACH,CAAC;oBACJ,CAAC,CAAC,CAAC;oBACL,OAAO,IAAI,CAAC,cAAc,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;gBACtD,CAAC;gBAED,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;YAC/C,CAAC;YAED,KAAK,CAAC,eAAe,CAAC,SAA0D;gBAC9E,IAAI,SAAiB,CAAC;gBACtB,IAAI,QAAgB,CAAC;gBACrB,IAAI,QAAQ,IAAI,SAAS,EAAE,CAAC;oBAC1B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;wBAC7B,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;oBAC3E,CAAC;oBACD,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC;oBACpC,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC;gBACpC,CAAC;qBAAM,CAAC;oBACN,SAAS,GAAG,SAAS,CAAC,MAAM,CAAC;oBAC7B,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC;gBAC7B,CAAC;gBAED,MAAM,GAAG,GAAG,MAAM,kBAAkB,CAAC,KAAK,CAAC,GAAG,MAAM,YAAY,EAAE;oBAChE,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE;wBACP,cAAc,EAAE,kBAAkB;wBAClC,aAAa,EAAE,UAAU,KAAK,EAAE;qBACjC;oBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC7D,CAAC,CAAC;gBACH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;oBACZ,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;oBAC9B,MAAM,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC,CAAC;gBACnE,CAAC;YACH,CAAC;SACF,CAAC;IACJ,CAAC;CACF"}
1
+ {"version":3,"file":"xcode-instances-helpers.mjs","sourceRoot":"","sources":["../src/resources/xcode-instances-helpers.ts"],"names":[],"mappings":"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"}
package/src/client.ts CHANGED
@@ -198,6 +198,18 @@ export class Limrun {
198
198
  this.fetch = options.fetch ?? Shims.getDefaultFetch();
199
199
  this.#encoder = Opts.FallbackEncoder;
200
200
 
201
+ const customHeadersEnv = readEnv('LIMRUN_CUSTOM_HEADERS');
202
+ if (customHeadersEnv) {
203
+ const parsed: Record<string, string> = {};
204
+ for (const line of customHeadersEnv.split('\n')) {
205
+ const colon = line.indexOf(':');
206
+ if (colon >= 0) {
207
+ parsed[line.substring(0, colon).trim()] = line.substring(colon + 1).trim();
208
+ }
209
+ }
210
+ options.defaultHeaders = { ...parsed, ...options.defaultHeaders };
211
+ }
212
+
201
213
  this._options = options;
202
214
 
203
215
  this.apiKey = apiKey;
@@ -1,12 +1,19 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { execFile } from 'child_process';
4
+ import { promisify } from 'util';
3
5
  import ignorePkg from 'ignore';
4
6
 
7
+ const execFileAsync = promisify(execFile);
8
+
5
9
  export type IgnoreFn = (relativePath: string) => boolean;
6
10
 
11
+ export type IgnoreLogger = (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => void;
12
+
7
13
  export type IgnoreFnOptions = {
8
14
  additional?: IgnoreFn;
9
15
  basisCacheDir: string;
16
+ log?: IgnoreLogger;
10
17
  };
11
18
 
12
19
  function normalizeRelativePath(relativePath: string): string {
@@ -17,9 +24,42 @@ function normalizeRelativePath(relativePath: string): string {
17
24
  .replace(/\/+/g, '/');
18
25
  }
19
26
 
27
+ type GitTrackedSets = {
28
+ tracked: Set<string>;
29
+ prefixes: Set<string>;
30
+ };
31
+
32
+ async function getGitTrackedSets(rootDir: string): Promise<GitTrackedSets | null> {
33
+ try {
34
+ const { stdout } = await execFileAsync('git', ['ls-files', '-z'], {
35
+ cwd: rootDir,
36
+ maxBuffer: 256 * 1024 * 1024,
37
+ });
38
+ const tracked = new Set<string>();
39
+ const prefixes = new Set<string>();
40
+ for (const file of stdout.split('\0')) {
41
+ if (!file) continue;
42
+ tracked.add(file);
43
+ let idx = file.lastIndexOf('/');
44
+ while (idx > 0) {
45
+ prefixes.add(file.substring(0, idx));
46
+ idx = file.lastIndexOf('/', idx - 1);
47
+ }
48
+ }
49
+ return { tracked, prefixes };
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+
20
55
  export async function createIgnoreFn(rootDir: string, options: IgnoreFnOptions): Promise<IgnoreFn> {
21
56
  const rootResolved = path.resolve(rootDir);
22
- const ig = ignorePkg();
57
+ // ignorecase: false matches git's default semantics. The package defaults to true,
58
+ // which silently drops e.g. `Vendor/` when .gitignore says `vendor/` (Ruby convention).
59
+ // allowRelativePaths: true so a `../`-style path doesn't throw mid-sync — treat it as
60
+ // "not ignored" and let it through, since the cost of dropping a needed file is higher
61
+ // than including an unexpected one.
62
+ const ig = ignorePkg({ ignorecase: false, allowRelativePaths: true });
23
63
  const gitignorePath = path.join(rootResolved, '.gitignore');
24
64
  try {
25
65
  const content = await fs.promises.readFile(gitignorePath, 'utf-8');
@@ -36,6 +76,11 @@ export async function createIgnoreFn(rootDir: string, options: IgnoreFnOptions):
36
76
  basisCacheRelative !== '..' &&
37
77
  !basisCacheRelative.startsWith('../');
38
78
 
79
+ const trackedSets = await getGitTrackedSets(rootResolved);
80
+ const log = options.log;
81
+ // Dedupe by .gitignore rule so a single bad rule doesn't spam the log per matched file.
82
+ const warnedRules = new Set<string>();
83
+
39
84
  return (relativePath: string) => {
40
85
  const normalized = normalizeRelativePath(relativePath);
41
86
  if (!normalized) return false;
@@ -59,7 +104,24 @@ export async function createIgnoreFn(rootDir: string, options: IgnoreFnOptions):
59
104
  return true;
60
105
  }
61
106
  if (withoutTrailingSlash.endsWith('.xcconfig')) return false;
62
- if (ig.ignores(normalized)) return true;
107
+ if (ig.ignores(normalized)) {
108
+ if (
109
+ trackedSets &&
110
+ (trackedSets.tracked.has(withoutTrailingSlash) || trackedSets.prefixes.has(withoutTrailingSlash))
111
+ ) {
112
+ const rule = ig.test(normalized).rule?.pattern ?? '<unknown>';
113
+ if (!warnedRules.has(rule)) {
114
+ warnedRules.add(rule);
115
+ const msg = `.gitignore rule '${rule}' is dropping '${withoutTrailingSlash}', which is tracked in git. The remote build will not see this path. Remove or scope the rule if you need it synced.`;
116
+ if (log) {
117
+ log('warn', msg);
118
+ } else {
119
+ console.warn('[FolderSync]', msg);
120
+ }
121
+ }
122
+ }
123
+ return true;
124
+ }
63
125
  if (options.additional?.(normalized)) return true;
64
126
  return false;
65
127
  };
@@ -47,6 +47,11 @@ export type FolderSyncOptions = {
47
47
  * ignoreFn: (path) => !(path.startsWith('src/') || path.endsWith('.json'))
48
48
  */
49
49
  ignoreFn: IgnoreFn;
50
+ /**
51
+ * Extra files to sync into limbuild. These are included
52
+ * in every sync pass but are not watched directly.
53
+ */
54
+ additionalFiles?: AdditionalFileSyncEntry[];
50
55
  };
51
56
 
52
57
  export type SyncFolderResult = {
@@ -56,7 +61,15 @@ export type SyncFolderResult = {
56
61
  stopWatching?: () => void;
57
62
  };
58
63
 
59
- type FileEntry = { path: string; size: number; sha256: string; absPath: string; mode: number };
64
+ export type AdditionalFileSyncEntry = { localPath: string; remotePath: string };
65
+
66
+ type FileEntry = {
67
+ path: string;
68
+ size: number;
69
+ sha256: string;
70
+ absPath: string;
71
+ mode: number;
72
+ };
60
73
 
61
74
  type FolderSyncHttpPayload = {
62
75
  kind: 'delta' | 'full';
@@ -284,6 +297,33 @@ async function walkFiles(root: string, ignoreFn: IgnoreFn): Promise<FileEntry[]>
284
297
  return out;
285
298
  }
286
299
 
300
+ async function collectAdditionalFiles(
301
+ additionalFiles: AdditionalFileSyncEntry[] | undefined,
302
+ ): Promise<FileEntry[]> {
303
+ if (!additionalFiles || additionalFiles.length === 0) {
304
+ return [];
305
+ }
306
+ const out: FileEntry[] = [];
307
+ for (const additionalFile of additionalFiles) {
308
+ const remotePath = additionalFile.remotePath;
309
+ const absPath = path.resolve(additionalFile.localPath);
310
+ const st = await fs.promises.stat(absPath);
311
+ if (!st.isFile()) {
312
+ throw new Error(`additional file localPath must be a file: ${additionalFile.localPath}`);
313
+ }
314
+ const sha256 = await sha256FileHex(absPath);
315
+ out.push({
316
+ path: remotePath,
317
+ size: st.size,
318
+ sha256,
319
+ absPath,
320
+ mode: st.mode & 0o7777,
321
+ });
322
+ }
323
+ out.sort((a, b) => a.path.localeCompare(b.path));
324
+ return out;
325
+ }
326
+
287
327
  // xdelta3 encoder backed by a WASM build of the upstream xdelta3 library.
288
328
  // Produces VCDIFF-compatible patches identical to `xdelta3 -e -s basis target`,
289
329
  // so the server-side decoder continues to apply them without changes.
@@ -421,7 +461,9 @@ async function syncFolderOnce(
421
461
  const maxPatchBytes = opts.maxPatchBytes ?? 4 * 1024 * 1024;
422
462
 
423
463
  const files = await walkFiles(localFolderPath, opts.ignoreFn);
424
- const fileMap = new Map(files.map((f) => [f.path, f]));
464
+ const additionalFiles = await collectAdditionalFiles(opts.additionalFiles);
465
+ const allFiles = [...files, ...additionalFiles].sort((a, b) => a.path.localeCompare(b.path));
466
+ const fileMap = new Map(allFiles.map((f) => [f.path, f]));
425
467
 
426
468
  const syncId = genId('sync');
427
469
  const rootName = path.basename(path.resolve(localFolderPath));
@@ -439,7 +481,7 @@ async function syncFolderOnce(
439
481
  // Build payload list by comparing against local basis cache (single-flight/watch assumes server matches cache).
440
482
  const encodeLimit = concurrencyLimit();
441
483
  const changed: FileEntry[] = [];
442
- for (const f of files) {
484
+ for (const f of allFiles) {
443
485
  const basisPath = cacheGet(opts.basisCacheDir, f.path);
444
486
  if (!fs.existsSync(basisPath)) {
445
487
  changed.push(f);
@@ -489,7 +531,12 @@ async function syncFolderOnce(
489
531
  slog('debug', `full(file): ${f.path} size=${f.size}`);
490
532
  bytesSentFull += f.size;
491
533
  return {
492
- payload: { kind: 'full', path: f.path, targetSha256: f.sha256.toLowerCase(), length: f.size },
534
+ payload: {
535
+ kind: 'full',
536
+ path: f.path,
537
+ targetSha256: f.sha256.toLowerCase(),
538
+ length: f.size,
539
+ },
493
540
  filePath: f.absPath,
494
541
  };
495
542
  });
@@ -499,14 +546,19 @@ async function syncFolderOnce(
499
546
  rootName,
500
547
  install: opts.install,
501
548
  ...(opts.launchMode ? { launchMode: opts.launchMode } : {}),
502
- files: files.map((f) => ({ path: f.path, size: f.size, sha256: f.sha256.toLowerCase(), mode: f.mode })),
549
+ files: allFiles.map((f) => ({
550
+ path: f.path,
551
+ size: f.size,
552
+ sha256: f.sha256.toLowerCase(),
553
+ mode: f.mode,
554
+ })),
503
555
  payloads: encodedPayloads.map((p) => p.payload),
504
556
  };
505
557
  const hasDelta = encodedPayloads.some((p) => p.payload.kind === 'delta');
506
558
  const compression: 'zstd' | 'gzip' | 'identity' = hasDelta ? 'identity' : preferredCompression;
507
559
  slog(
508
560
  'debug',
509
- `sync started files=${files.length}${reason ? ` reason=${reason}` : ''} compression=${compression}`,
561
+ `sync started files=${allFiles.length}${reason ? ` reason=${reason}` : ''} compression=${compression}`,
510
562
  );
511
563
 
512
564
  const sendStart = nowMs();
@@ -583,7 +635,7 @@ async function syncFolderOnce(
583
635
  const totalBytes = bytesSentFull + bytesSentDelta;
584
636
  slog(
585
637
  'debug',
586
- `sync finished files=${files.length} sent=${fmtBytes(totalBytes)} syncWork=${fmtMs(
638
+ `sync finished files=${allFiles.length} sent=${fmtBytes(totalBytes)} syncWork=${fmtMs(
587
639
  syncWorkMs,
588
640
  )} total=${fmtMs(tookMs)}`,
589
641
  );
@@ -595,7 +647,7 @@ async function syncFolderOnce(
595
647
  out.installedBundleId = resp.bundleId;
596
648
  }
597
649
  // Update local cache optimistically: after a successful sync, cache reflects current local tree.
598
- for (const f of files) {
650
+ for (const f of allFiles) {
599
651
  await cachePut(opts.basisCacheDir, f.path, f.absPath);
600
652
  }
601
653
  return out;
package/src/ios-client.ts CHANGED
@@ -1763,31 +1763,32 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
1763
1763
  const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
1764
1764
  const cacheKey = `limsync-cache-${folderName}-${hash}`;
1765
1765
  const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
1766
+ const syncLog: FolderSyncOptions['log'] = (level, msg) => {
1767
+ switch (level) {
1768
+ case 'debug':
1769
+ logger.debug(msg);
1770
+ break;
1771
+ case 'info':
1772
+ logger.info(msg);
1773
+ break;
1774
+ case 'warn':
1775
+ logger.warn(msg);
1776
+ break;
1777
+ case 'error':
1778
+ logger.error(msg);
1779
+ break;
1780
+ default:
1781
+ logger.info(msg);
1782
+ break;
1783
+ }
1784
+ };
1766
1785
  const folderSyncOpts: FolderSyncOptions = {
1767
1786
  apiUrl: options.apiUrl,
1768
1787
  token: options.token,
1769
1788
  udid: cacheKey,
1770
- ignoreFn: await createIgnoreFn(localAppBundlePath, { basisCacheDir }),
1789
+ ignoreFn: await createIgnoreFn(localAppBundlePath, { basisCacheDir, log: syncLog }),
1771
1790
  basisCacheDir,
1772
- log: (level, msg) => {
1773
- switch (level) {
1774
- case 'debug':
1775
- logger.debug(msg);
1776
- break;
1777
- case 'info':
1778
- logger.info(msg);
1779
- break;
1780
- case 'warn':
1781
- logger.warn(msg);
1782
- break;
1783
- case 'error':
1784
- logger.error(msg);
1785
- break;
1786
- default:
1787
- logger.info(msg);
1788
- break;
1789
- }
1790
- },
1791
+ log: syncLog,
1791
1792
  install: opts?.install ?? true,
1792
1793
  maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
1793
1794
  launchMode: opts?.launchMode ?? 'ForegroundIfRunning',
@@ -5,7 +5,11 @@ import crypto from 'crypto';
5
5
  import { XcodeInstances as GeneratedXcodeInstances, type XcodeInstance } from './xcode-instances';
6
6
  import { type IosInstance } from './ios-instances';
7
7
  import { exec, type ExecChildProcess, type ExecRequest } from '../exec-client';
8
- import { syncFolder as syncFolderImpl, type FolderSyncOptions } from '../folder-sync';
8
+ import {
9
+ syncFolder as syncFolderImpl,
10
+ type AdditionalFileSyncEntry,
11
+ type FolderSyncOptions,
12
+ } from '../folder-sync';
9
13
  import { createIgnoreFn } from '../folder-sync-ignore';
10
14
  import { nodeProxyTransport } from '../internal/proxy-transport';
11
15
 
@@ -32,6 +36,10 @@ export type SyncOptions = {
32
36
  * Return true to ignore, false to keep.
33
37
  */
34
38
  ignore?: (relativePath: string) => boolean;
39
+ /**
40
+ * Extra files to sync on every sync pass.
41
+ */
42
+ additionalFiles?: AdditionalFileSyncEntry[];
35
43
  };
36
44
 
37
45
  export type SyncResult = {
@@ -47,7 +55,7 @@ export type XcodeProjectConfig = {
47
55
  };
48
56
 
49
57
  export type XcodeBuildOptions = {
50
- upload?: { assetName: string };
58
+ upload?: { assetName: string } | { signedUploadUrl: string; signedDownloadUrl: string };
51
59
  };
52
60
 
53
61
  export type XcodeClient = {
@@ -94,6 +102,42 @@ function createLogger(logLevel: LogLevel) {
94
102
  };
95
103
  }
96
104
 
105
+ function normalizeWorkspaceRelativePath(remotePath: string): string {
106
+ if (
107
+ remotePath === '' ||
108
+ remotePath.startsWith('/') ||
109
+ remotePath.includes('\\') ||
110
+ remotePath.includes('\0')
111
+ ) {
112
+ throw new Error(`invalid sandbox home path from server: ${remotePath}`);
113
+ }
114
+ const parts = remotePath.split('/');
115
+ if (parts.some((part) => part === '' || part === '.' || part === '..')) {
116
+ throw new Error(`invalid sandbox home path from server: ${remotePath}`);
117
+ }
118
+ return parts.join('/');
119
+ }
120
+
121
+ async function fetchSandboxInfo(apiUrl: string, token: string): Promise<{ homeDir: string }> {
122
+ const res = await nodeProxyTransport.fetch(`${apiUrl}/info`, {
123
+ method: 'GET',
124
+ headers: {
125
+ Authorization: `Bearer ${token}`,
126
+ },
127
+ });
128
+ const text = await res.text();
129
+ if (!res.ok) {
130
+ throw new Error(`GET /info failed: ${res.status} ${text}`);
131
+ }
132
+ const body = JSON.parse(text) as { homeDir?: string };
133
+ if (!body.homeDir) {
134
+ throw new Error('GET /info response is missing homeDir');
135
+ }
136
+ return {
137
+ homeDir: normalizeWorkspaceRelativePath(body.homeDir),
138
+ };
139
+ }
140
+
97
141
  export class XcodeInstances extends GeneratedXcodeInstances {
98
142
  async createClient(params: XcodeCreateClientParams): Promise<XcodeClient> {
99
143
  let apiUrl: string;
@@ -111,6 +155,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
111
155
 
112
156
  const log = createLogger(params.logLevel ?? 'info');
113
157
  const client = this._client;
158
+ const sandboxInfo = await fetchSandboxInfo(apiUrl, token);
114
159
 
115
160
  return {
116
161
  async sync(localCodePath: string, opts?: SyncOptions): Promise<SyncResult> {
@@ -119,6 +164,13 @@ export class XcodeInstances extends GeneratedXcodeInstances {
119
164
  const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
120
165
  const cacheKey = `limsync-cache-${folderName}-${hash}`;
121
166
  const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
167
+ const additionalFiles = opts?.additionalFiles?.map((file) => ({
168
+ localPath: file.localPath,
169
+ remotePath:
170
+ file.remotePath.startsWith('~/') ?
171
+ `${sandboxInfo.homeDir}/${file.remotePath.slice(2)}`
172
+ : file.remotePath,
173
+ }));
122
174
  const codeSyncOpts: FolderSyncOptions = {
123
175
  apiUrl,
124
176
  token,
@@ -126,6 +178,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
126
178
  install: opts?.install ?? true,
127
179
  ignoreFn: await createIgnoreFn(localCodePath, {
128
180
  basisCacheDir,
181
+ log,
129
182
  additional: (relativePath: string) => {
130
183
  if (
131
184
  relativePath.startsWith('build/') ||
@@ -161,6 +214,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
161
214
  maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
162
215
  launchMode: 'ForegroundIfRunning',
163
216
  log,
217
+ ...(additionalFiles ? { additionalFiles } : {}),
164
218
  };
165
219
 
166
220
  const result = await syncFolderImpl(localCodePath, codeSyncOpts);
@@ -176,7 +230,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
176
230
  ...(settings && { xcodebuild: settings }),
177
231
  };
178
232
 
179
- if (options?.upload) {
233
+ if (options?.upload && 'assetName' in options.upload) {
180
234
  const uploadName = options.upload.assetName;
181
235
  const requestPromise = client.assets
182
236
  .getOrCreate({ name: uploadName })
@@ -195,6 +249,11 @@ export class XcodeInstances extends GeneratedXcodeInstances {
195
249
  return exec(requestPromise, { apiUrl, token, log });
196
250
  }
197
251
 
252
+ if (options?.upload && 'signedUploadUrl' in options.upload && 'signedDownloadUrl' in options.upload) {
253
+ request.signedUploadUrl = options.upload.signedUploadUrl;
254
+ request.additionalMetadata = { signedDownloadUrl: options.upload.signedDownloadUrl };
255
+ }
256
+
198
257
  return exec(request, { apiUrl, token, log });
199
258
  },
200
259
 
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.28.1'; // x-release-please-version
1
+ export const VERSION = '0.28.3'; // x-release-please-version
package/version.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.28.1";
1
+ export declare const VERSION = "0.28.3";
2
2
  //# sourceMappingURL=version.d.mts.map
package/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.28.1";
1
+ export declare const VERSION = "0.28.3";
2
2
  //# sourceMappingURL=version.d.ts.map
package/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.VERSION = void 0;
4
- exports.VERSION = '0.28.1'; // x-release-please-version
4
+ exports.VERSION = '0.28.3'; // x-release-please-version
5
5
  //# sourceMappingURL=version.js.map
package/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.28.1'; // x-release-please-version
1
+ export const VERSION = '0.28.3'; // x-release-please-version
2
2
  //# sourceMappingURL=version.mjs.map