@limrun/api 0.28.2 → 0.28.4

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 (53) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/exec-client.d.mts +6 -0
  3. package/exec-client.d.mts.map +1 -1
  4. package/exec-client.d.ts +6 -0
  5. package/exec-client.d.ts.map +1 -1
  6. package/exec-client.js.map +1 -1
  7. package/exec-client.mjs.map +1 -1
  8. package/folder-sync.d.mts +9 -0
  9. package/folder-sync.d.mts.map +1 -1
  10. package/folder-sync.d.ts +9 -0
  11. package/folder-sync.d.ts.map +1 -1
  12. package/folder-sync.js +43 -7
  13. package/folder-sync.js.map +1 -1
  14. package/folder-sync.mjs +43 -7
  15. package/folder-sync.mjs.map +1 -1
  16. package/internal/types.d.mts +6 -6
  17. package/internal/types.d.mts.map +1 -1
  18. package/internal/types.d.ts +6 -6
  19. package/internal/types.d.ts.map +1 -1
  20. package/internal/utils/log.d.mts.map +1 -1
  21. package/internal/utils/log.d.ts.map +1 -1
  22. package/internal/utils/log.js +2 -0
  23. package/internal/utils/log.js.map +1 -1
  24. package/internal/utils/log.mjs +2 -0
  25. package/internal/utils/log.mjs.map +1 -1
  26. package/ios-client.d.mts +2 -0
  27. package/ios-client.d.mts.map +1 -1
  28. package/ios-client.d.ts +2 -0
  29. package/ios-client.d.ts.map +1 -1
  30. package/ios-client.js +1 -1
  31. package/ios-client.js.map +1 -1
  32. package/ios-client.mjs +1 -1
  33. package/ios-client.mjs.map +1 -1
  34. package/package.json +1 -1
  35. package/resources/xcode-instances-helpers.d.mts +12 -2
  36. package/resources/xcode-instances-helpers.d.mts.map +1 -1
  37. package/resources/xcode-instances-helpers.d.ts +12 -2
  38. package/resources/xcode-instances-helpers.d.ts.map +1 -1
  39. package/resources/xcode-instances-helpers.js +42 -2
  40. package/resources/xcode-instances-helpers.js.map +1 -1
  41. package/resources/xcode-instances-helpers.mjs +43 -3
  42. package/resources/xcode-instances-helpers.mjs.map +1 -1
  43. package/src/exec-client.ts +6 -0
  44. package/src/folder-sync.ts +60 -8
  45. package/src/internal/types.ts +6 -8
  46. package/src/internal/utils/log.ts +2 -0
  47. package/src/ios-client.ts +12 -5
  48. package/src/resources/xcode-instances-helpers.ts +65 -5
  49. package/src/version.ts +1 -1
  50. package/version.d.mts +1 -1
  51. package/version.d.ts +1 -1
  52. package/version.js +1 -1
  53. package/version.mjs +1 -1
@@ -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;
@@ -40,7 +40,6 @@ type OverloadedParameters<T> =
40
40
  : T extends (...args: infer A) => unknown ? A
41
41
  : never;
42
42
 
43
- /* eslint-disable */
44
43
  /**
45
44
  * These imports attempt to get types from a parent package's dependencies.
46
45
  * Unresolved bare specifiers can trigger [automatic type acquisition][1] in some projects, which
@@ -63,19 +62,18 @@ type OverloadedParameters<T> =
63
62
  *
64
63
  * [1]: https://www.typescriptlang.org/tsconfig/#typeAcquisition
65
64
  */
66
- /** @ts-ignore For users with \@types/node */
65
+ /** @ts-ignore For users with \@types/node */ /* prettier-ignore */
67
66
  type UndiciTypesRequestInit = NotAny<import('../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../node_modules/undici-types/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/undici-types/index.d.ts').RequestInit>;
68
- /** @ts-ignore For users with undici */
67
+ /** @ts-ignore For users with undici */ /* prettier-ignore */
69
68
  type UndiciRequestInit = NotAny<import('../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../node_modules/undici/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/undici/index.d.ts').RequestInit>;
70
- /** @ts-ignore For users with \@types/bun */
69
+ /** @ts-ignore For users with \@types/bun */ /* prettier-ignore */
71
70
  type BunRequestInit = globalThis.FetchRequestInit;
72
- /** @ts-ignore For users with node-fetch@2 */
71
+ /** @ts-ignore For users with node-fetch@2 */ /* prettier-ignore */
73
72
  type NodeFetch2RequestInit = NotAny<import('../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/@types/node-fetch/index.d.ts').RequestInit>;
74
- /** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */
73
+ /** @ts-ignore For users with node-fetch@3, doesn't need file extension because types are at ./@types/index.d.ts */ /* prettier-ignore */
75
74
  type NodeFetch3RequestInit = NotAny<import('../node_modules/node-fetch').RequestInit> | NotAny<import('../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../../node_modules/node-fetch').RequestInit> | NotAny<import('../../../../../../../../../../node_modules/node-fetch').RequestInit>;
76
- /** @ts-ignore For users who use Deno */
75
+ /** @ts-ignore For users who use Deno */ /* prettier-ignore */
77
76
  type FetchRequestInit = NonNullable<OverloadedParameters<typeof fetch>[1]>;
78
- /* eslint-enable */
79
77
 
80
78
  type RequestInits =
81
79
  | NotAny<UndiciTypesRequestInit>
@@ -107,6 +107,8 @@ export const formatRequestDetails = (details: {
107
107
  name,
108
108
  (
109
109
  name.toLowerCase() === 'authorization' ||
110
+ name.toLowerCase() === 'api-key' ||
111
+ name.toLowerCase() === 'x-api-key' ||
110
112
  name.toLowerCase() === 'cookie' ||
111
113
  name.toLowerCase() === 'set-cookie'
112
114
  ) ?
package/src/ios-client.ts CHANGED
@@ -259,6 +259,8 @@ export type CommandResult = {
259
259
  export type AppInstallationOptions = {
260
260
  /** MD5 hash for caching - if provided and matches cached version, skips download */
261
261
  md5?: string;
262
+ /** Client-side timeout for app installation. Defaults to 120 seconds. */
263
+ timeoutMs?: number;
262
264
  /**
263
265
  * Launch mode after installation:
264
266
  * - 'ForegroundIfRunning': Bring to foreground if already running, otherwise launch
@@ -1670,11 +1672,16 @@ export async function createInstanceClient(options: InstanceClientOptions): Prom
1670
1672
  };
1671
1673
 
1672
1674
  const installApp = (url: string, options?: AppInstallationOptions): Promise<AppInstallationResult> => {
1673
- return sendRequest<AppInstallationResult>('appInstallation', {
1674
- url,
1675
- md5: options?.md5,
1676
- launchMode: options?.launchMode,
1677
- });
1675
+ return sendRequest<AppInstallationResult>(
1676
+ 'appInstallation',
1677
+ {
1678
+ url,
1679
+ md5: options?.md5,
1680
+ launchMode: options?.launchMode,
1681
+ },
1682
+ undefined,
1683
+ options?.timeoutMs ?? 120_000,
1684
+ );
1678
1685
  };
1679
1686
 
1680
1687
  const setOrientation = (orientation: 'Portrait' | 'Landscape'): Promise<void> => {
@@ -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 = {
@@ -43,11 +51,18 @@ export type XcodeProjectConfig = {
43
51
  workspace?: string;
44
52
  project?: string;
45
53
  scheme?: string;
46
- sdk?: 'iphonesimulator' | 'iphoneos';
54
+ sdk?: 'iphonesimulator' | 'iphoneos' | 'watchsimulator' | 'watchos';
55
+ };
56
+
57
+ export type XcodeSigningConfig = {
58
+ certificateP12Base64?: string;
59
+ certificatePassword?: string;
60
+ provisioningProfileBase64?: string;
47
61
  };
48
62
 
49
63
  export type XcodeBuildOptions = {
50
- upload?: { assetName: string } | { signedUploadUrl: string; signedDownloadUrl: string };
64
+ upload?: { assetName: string } | { signedUploadUrl: string };
65
+ signing?: XcodeSigningConfig;
51
66
  };
52
67
 
53
68
  export type XcodeClient = {
@@ -94,6 +109,42 @@ function createLogger(logLevel: LogLevel) {
94
109
  };
95
110
  }
96
111
 
112
+ function normalizeWorkspaceRelativePath(remotePath: string): string {
113
+ if (
114
+ remotePath === '' ||
115
+ remotePath.startsWith('/') ||
116
+ remotePath.includes('\\') ||
117
+ remotePath.includes('\0')
118
+ ) {
119
+ throw new Error(`invalid sandbox home path from server: ${remotePath}`);
120
+ }
121
+ const parts = remotePath.split('/');
122
+ if (parts.some((part) => part === '' || part === '.' || part === '..')) {
123
+ throw new Error(`invalid sandbox home path from server: ${remotePath}`);
124
+ }
125
+ return parts.join('/');
126
+ }
127
+
128
+ async function fetchSandboxInfo(apiUrl: string, token: string): Promise<{ homeDir: string }> {
129
+ const res = await nodeProxyTransport.fetch(`${apiUrl}/info`, {
130
+ method: 'GET',
131
+ headers: {
132
+ Authorization: `Bearer ${token}`,
133
+ },
134
+ });
135
+ const text = await res.text();
136
+ if (!res.ok) {
137
+ throw new Error(`GET /info failed: ${res.status} ${text}`);
138
+ }
139
+ const body = JSON.parse(text) as { homeDir?: string };
140
+ if (!body.homeDir) {
141
+ throw new Error('GET /info response is missing homeDir');
142
+ }
143
+ return {
144
+ homeDir: normalizeWorkspaceRelativePath(body.homeDir),
145
+ };
146
+ }
147
+
97
148
  export class XcodeInstances extends GeneratedXcodeInstances {
98
149
  async createClient(params: XcodeCreateClientParams): Promise<XcodeClient> {
99
150
  let apiUrl: string;
@@ -111,6 +162,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
111
162
 
112
163
  const log = createLogger(params.logLevel ?? 'info');
113
164
  const client = this._client;
165
+ const sandboxInfo = await fetchSandboxInfo(apiUrl, token);
114
166
 
115
167
  return {
116
168
  async sync(localCodePath: string, opts?: SyncOptions): Promise<SyncResult> {
@@ -119,6 +171,13 @@ export class XcodeInstances extends GeneratedXcodeInstances {
119
171
  const hash = crypto.createHash('sha1').update(resolvedPath).digest('hex').slice(0, 8);
120
172
  const cacheKey = `limsync-cache-${folderName}-${hash}`;
121
173
  const basisCacheDir = opts?.basisCacheDir ?? path.join(os.tmpdir(), cacheKey);
174
+ const additionalFiles = opts?.additionalFiles?.map((file) => ({
175
+ localPath: file.localPath,
176
+ remotePath:
177
+ file.remotePath.startsWith('~/') ?
178
+ `${sandboxInfo.homeDir}/${file.remotePath.slice(2)}`
179
+ : file.remotePath,
180
+ }));
122
181
  const codeSyncOpts: FolderSyncOptions = {
123
182
  apiUrl,
124
183
  token,
@@ -162,6 +221,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
162
221
  maxPatchBytes: opts?.maxPatchBytes ?? 4 * 1024 * 1024,
163
222
  launchMode: 'ForegroundIfRunning',
164
223
  log,
224
+ ...(additionalFiles ? { additionalFiles } : {}),
165
225
  };
166
226
 
167
227
  const result = await syncFolderImpl(localCodePath, codeSyncOpts);
@@ -175,6 +235,7 @@ export class XcodeInstances extends GeneratedXcodeInstances {
175
235
  const request: ExecRequest = {
176
236
  command: 'xcodebuild',
177
237
  ...(settings && { xcodebuild: settings }),
238
+ ...(options?.signing && { signing: options.signing }),
178
239
  };
179
240
 
180
241
  if (options?.upload && 'assetName' in options.upload) {
@@ -196,9 +257,8 @@ export class XcodeInstances extends GeneratedXcodeInstances {
196
257
  return exec(requestPromise, { apiUrl, token, log });
197
258
  }
198
259
 
199
- if (options?.upload && 'signedUploadUrl' in options.upload && 'signedDownloadUrl' in options.upload) {
260
+ if (options?.upload && 'signedUploadUrl' in options.upload) {
200
261
  request.signedUploadUrl = options.upload.signedUploadUrl;
201
- request.additionalMetadata = { signedDownloadUrl: options.upload.signedDownloadUrl };
202
262
  }
203
263
 
204
264
  return exec(request, { apiUrl, token, log });
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const VERSION = '0.28.2'; // x-release-please-version
1
+ export const VERSION = '0.28.4'; // x-release-please-version
package/version.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.28.2";
1
+ export declare const VERSION = "0.28.4";
2
2
  //# sourceMappingURL=version.d.mts.map
package/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const VERSION = "0.28.2";
1
+ export declare const VERSION = "0.28.4";
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.2'; // x-release-please-version
4
+ exports.VERSION = '0.28.4'; // x-release-please-version
5
5
  //# sourceMappingURL=version.js.map
package/version.mjs CHANGED
@@ -1,2 +1,2 @@
1
- export const VERSION = '0.28.2'; // x-release-please-version
1
+ export const VERSION = '0.28.4'; // x-release-please-version
2
2
  //# sourceMappingURL=version.mjs.map