@ahmedsamirelsaka/react-native-local-release 2.0.0-rc.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 (75) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/CREDENTIALS.md +79 -0
  3. package/LICENSE +21 -0
  4. package/README.md +112 -0
  5. package/SECURITY.md +44 -0
  6. package/dist/cli.d.ts +3 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +326 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/config.d.ts +27 -0
  11. package/dist/config.d.ts.map +1 -0
  12. package/dist/config.js +229 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/credentials.d.ts +27 -0
  15. package/dist/credentials.d.ts.map +1 -0
  16. package/dist/credentials.js +317 -0
  17. package/dist/credentials.js.map +1 -0
  18. package/dist/detect.d.ts +7 -0
  19. package/dist/detect.d.ts.map +1 -0
  20. package/dist/detect.js +326 -0
  21. package/dist/detect.js.map +1 -0
  22. package/dist/fs.d.ts +15 -0
  23. package/dist/fs.d.ts.map +1 -0
  24. package/dist/fs.js +79 -0
  25. package/dist/fs.js.map +1 -0
  26. package/dist/index.d.ts +7 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +6 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/install.d.ts +5 -0
  31. package/dist/install.d.ts.map +1 -0
  32. package/dist/install.js +338 -0
  33. package/dist/install.js.map +1 -0
  34. package/dist/logging.d.ts +15 -0
  35. package/dist/logging.d.ts.map +1 -0
  36. package/dist/logging.js +84 -0
  37. package/dist/logging.js.map +1 -0
  38. package/dist/patch.d.ts +39 -0
  39. package/dist/patch.d.ts.map +1 -0
  40. package/dist/patch.js +352 -0
  41. package/dist/patch.js.map +1 -0
  42. package/dist/paths.d.ts +14 -0
  43. package/dist/paths.d.ts.map +1 -0
  44. package/dist/paths.js +65 -0
  45. package/dist/paths.js.map +1 -0
  46. package/dist/process.d.ts +26 -0
  47. package/dist/process.d.ts.map +1 -0
  48. package/dist/process.js +43 -0
  49. package/dist/process.js.map +1 -0
  50. package/dist/prompt.d.ts +9 -0
  51. package/dist/prompt.d.ts.map +1 -0
  52. package/dist/prompt.js +70 -0
  53. package/dist/prompt.js.map +1 -0
  54. package/dist/types.d.ts +153 -0
  55. package/dist/types.d.ts.map +1 -0
  56. package/dist/types.js +2 -0
  57. package/dist/types.js.map +1 -0
  58. package/docs/ARCHITECTURE.md +25 -0
  59. package/docs/MIGRATION.md +28 -0
  60. package/docs/PUBLISHING.md +128 -0
  61. package/docs/SUPPORT.md +17 -0
  62. package/install.mjs +6 -0
  63. package/package.json +73 -0
  64. package/schema/release.config.schema.json +209 -0
  65. package/template/android/keystore.properties.example +7 -0
  66. package/template/android/proguard-rules.rn-local-release.pro +28 -0
  67. package/template/docs/LOCAL_RELEASE.md +85 -0
  68. package/template/fastlane/.env.example +20 -0
  69. package/template/fastlane/Appfile +10 -0
  70. package/template/fastlane/Fastfile +203 -0
  71. package/template/fastlane/lib/rn_local_release.rb +700 -0
  72. package/template/ios/exportOptions.plist +18 -0
  73. package/template/scripts/print-android-signing-fingerprints.sh +62 -0
  74. package/template/secrets/.gitignore +3 -0
  75. package/template/secrets/README.md +12 -0
package/dist/patch.js ADDED
@@ -0,0 +1,352 @@
1
+ import path from 'node:path';
2
+ import { exists, readText, writeText, backupFile, sha256OfText, sha256OfFile } from './fs.js';
3
+ export const BEGIN = '# >>> rn-local-release';
4
+ export const END = '# <<< rn-local-release';
5
+ export function upsertMarkedBlock(content, block, begin = BEGIN, end = END) {
6
+ const markerStart = content.indexOf(begin);
7
+ const markerEnd = content.indexOf(end);
8
+ const wrapped = `${begin}\n${block.trimEnd()}\n${end}\n`;
9
+ if (markerStart !== -1 && markerEnd !== -1 && markerEnd > markerStart) {
10
+ return (content.slice(0, markerStart) +
11
+ wrapped +
12
+ content.slice(markerEnd + end.length).replace(/^\n/, ''));
13
+ }
14
+ const trimmed = content.endsWith('\n') ? content : `${content}\n`;
15
+ return `${trimmed}\n${wrapped}`;
16
+ }
17
+ export function removeMarkedBlock(content, begin = BEGIN, end = END) {
18
+ const markerStart = content.indexOf(begin);
19
+ const markerEnd = content.indexOf(end);
20
+ if (markerStart === -1 || markerEnd === -1 || markerEnd < markerStart) {
21
+ return content;
22
+ }
23
+ return (content.slice(0, markerStart) + content.slice(markerEnd + end.length).replace(/^\n/, ''));
24
+ }
25
+ export const DEFAULT_GITIGNORE_ENTRIES = [
26
+ '# Local release credentials + store artifacts (never commit)',
27
+ '**/fastlane/.env',
28
+ '**/fastlane/.env.*',
29
+ '!**/fastlane/.env.example',
30
+ '**/fastlane/README.md',
31
+ '**/fastlane/report.xml',
32
+ '*.ipa',
33
+ '*.dSYM.zip',
34
+ '*.aab',
35
+ '*.apk',
36
+ '!android/app/debug.keystore',
37
+ '*.jks',
38
+ '*.keystore',
39
+ 'keystore.properties',
40
+ '**/keystore.properties',
41
+ 'secrets/**',
42
+ '!secrets/.gitignore',
43
+ '!secrets/README.md',
44
+ 'credentials.txt',
45
+ '**/credentials.txt',
46
+ '**/AuthKey_*.p8',
47
+ '**/play-store-*.json',
48
+ '**/google-play-*.json',
49
+ '**/*-service-account*.json',
50
+ 'build/ios/',
51
+ 'build/android/',
52
+ 'build/release-manifest.json',
53
+ 'build/release-manifest-*.json',
54
+ 'build/upload-journal.json',
55
+ 'logs/',
56
+ 'versions/',
57
+ '.rn-local-release/',
58
+ '*.rn-local-release.bak',
59
+ ];
60
+ export function mergeGitignore(content) {
61
+ return upsertMarkedBlock(content || '', DEFAULT_GITIGNORE_ENTRIES.join('\n'));
62
+ }
63
+ export function mergePackageScripts(packageJson, scripts) {
64
+ const next = {
65
+ ...packageJson,
66
+ scripts: { ...(packageJson.scripts || {}) },
67
+ };
68
+ const scriptBag = next.scripts;
69
+ for (const [key, value] of Object.entries(scripts)) {
70
+ scriptBag[key] = value;
71
+ }
72
+ if (!scriptBag.typecheck) {
73
+ scriptBag.typecheck = 'tsc --noEmit';
74
+ }
75
+ return next;
76
+ }
77
+ export function mergeGemfile(content) {
78
+ let next = content;
79
+ if (!/gem ['"]fastlane['"]/.test(next)) {
80
+ next = upsertMarkedBlock(next, "gem 'fastlane', '~> 2.234'");
81
+ }
82
+ return next;
83
+ }
84
+ export function ensureRubyVersion(root, version, { dryRun = false } = {}) {
85
+ const filePath = path.join(root, '.ruby-version');
86
+ if (exists(filePath))
87
+ return { path: filePath, wrote: false };
88
+ if (dryRun)
89
+ return { path: filePath, wrote: false, dryRun: true };
90
+ writeText(filePath, `${version}\n`);
91
+ return { path: filePath, wrote: true };
92
+ }
93
+ const GROOVY_SIGNING_HELPER = `
94
+ // >>> rn-local-release-signing
95
+ def loadKeystoreProperties() {
96
+ def props = new Properties()
97
+ def keystorePropertiesFile = rootProject.file("keystore.properties")
98
+ if (keystorePropertiesFile.exists()) {
99
+ keystorePropertiesFile.withInputStream { props.load(it) }
100
+ }
101
+ return props
102
+ }
103
+
104
+ def releaseSigningProperty(String name) {
105
+ def keystoreProps = loadKeystoreProperties()
106
+ def fromFile = keystoreProps.getProperty(name)
107
+ if (fromFile != null && !fromFile.trim().isEmpty()) {
108
+ return fromFile.trim()
109
+ }
110
+ def fromProject = project.findProperty(name)
111
+ if (fromProject != null && !fromProject.toString().trim().isEmpty()) {
112
+ return fromProject.toString().trim()
113
+ }
114
+ def fromEnv = System.getenv(name)
115
+ if (fromEnv != null && !fromEnv.trim().isEmpty()) {
116
+ return fromEnv.trim()
117
+ }
118
+ return null
119
+ }
120
+
121
+ def releaseStoreFileName = releaseSigningProperty("MYAPP_UPLOAD_STORE_FILE")
122
+ def releaseStorePassword = releaseSigningProperty("MYAPP_UPLOAD_STORE_PASSWORD")
123
+ def releaseKeyAlias = releaseSigningProperty("MYAPP_UPLOAD_KEY_ALIAS")
124
+ def releaseKeyPassword = releaseSigningProperty("MYAPP_UPLOAD_KEY_PASSWORD")
125
+ def hasReleaseSigning =
126
+ releaseStoreFileName != null &&
127
+ releaseStorePassword != null &&
128
+ releaseKeyAlias != null &&
129
+ releaseKeyPassword != null
130
+ // <<< rn-local-release-signing
131
+ `.trim();
132
+ const GROOVY_RELEASE_GUARD = `
133
+ // >>> rn-local-release-guard
134
+ gradle.taskGraph.whenReady { graph ->
135
+ if (hasReleaseSigning) {
136
+ return
137
+ }
138
+ def wantsSignedRelease = graph.allTasks.any { task ->
139
+ def name = task.name
140
+ name == "assembleRelease" ||
141
+ name == "bundleRelease" ||
142
+ name == "packageRelease" ||
143
+ name == "packageReleaseBundle" ||
144
+ name == "signReleaseBundle"
145
+ }
146
+ if (wantsSignedRelease) {
147
+ throw new GradleException(
148
+ "Release signing is not configured. Set MYAPP_UPLOAD_* via android/keystore.properties or environment variables."
149
+ )
150
+ }
151
+ }
152
+ // <<< rn-local-release-guard
153
+ `.trim();
154
+ export function patchAndroidBuildGradleGroovy(content) {
155
+ let next = content;
156
+ if (!next.includes('rn-local-release-signing')) {
157
+ const anchors = [
158
+ /def jscFlavor[^\n]*\n/,
159
+ /apply plugin: "com\.facebook\.react"[^\n]*\n/,
160
+ /android\s*\{\n/,
161
+ ];
162
+ let inserted = false;
163
+ for (const anchor of anchors) {
164
+ if (anchor.test(next)) {
165
+ next = next.replace(anchor, (match) => `${match}\n${GROOVY_SIGNING_HELPER}\n\n`);
166
+ inserted = true;
167
+ break;
168
+ }
169
+ }
170
+ if (!inserted) {
171
+ next = `${GROOVY_SIGNING_HELPER}\n\n${next}`;
172
+ }
173
+ }
174
+ if (/signingConfigs\s*\{[\s\S]*release\s*\{[\s\S]*storeFile\s+file\(MYAPP_UPLOAD_STORE_FILE\)/.test(next)) {
175
+ next = next.replace(/release\s*\{[\s\S]*?storeFile\s+file\(MYAPP_UPLOAD_STORE_FILE\)[\s\S]*?keyPassword\s+MYAPP_UPLOAD_KEY_PASSWORD\s*\}/, `release {
176
+ if (hasReleaseSigning) {
177
+ storeFile file(releaseStoreFileName)
178
+ storePassword releaseStorePassword
179
+ keyAlias releaseKeyAlias
180
+ keyPassword releaseKeyPassword
181
+ }
182
+ }`);
183
+ }
184
+ if (!next.includes('rn-local-release-guard')) {
185
+ if (/\n(dependencies\s*\{)/.test(next)) {
186
+ next = next.replace(/\n(dependencies\s*\{)/, `\n${GROOVY_RELEASE_GUARD}\n\n$1`);
187
+ }
188
+ else {
189
+ next = `${next.trimEnd()}\n\n${GROOVY_RELEASE_GUARD}\n`;
190
+ }
191
+ }
192
+ return next;
193
+ }
194
+ const KTS_SIGNING_HELPER = `
195
+ // >>> rn-local-release-signing
196
+ fun loadKeystoreProperties(): java.util.Properties {
197
+ val props = java.util.Properties()
198
+ val keystorePropertiesFile = rootProject.file("keystore.properties")
199
+ if (keystorePropertiesFile.exists()) {
200
+ keystorePropertiesFile.inputStream().use { props.load(it) }
201
+ }
202
+ return props
203
+ }
204
+
205
+ fun releaseSigningProperty(name: String): String? {
206
+ val fromFile = loadKeystoreProperties().getProperty(name)
207
+ if (!fromFile.isNullOrBlank()) return fromFile.trim()
208
+ val fromProject = project.findProperty(name)?.toString()
209
+ if (!fromProject.isNullOrBlank()) return fromProject.trim()
210
+ val fromEnv = System.getenv(name)
211
+ if (!fromEnv.isNullOrBlank()) return fromEnv.trim()
212
+ return null
213
+ }
214
+
215
+ val releaseStoreFileName = releaseSigningProperty("MYAPP_UPLOAD_STORE_FILE")
216
+ val releaseStorePassword = releaseSigningProperty("MYAPP_UPLOAD_STORE_PASSWORD")
217
+ val releaseKeyAlias = releaseSigningProperty("MYAPP_UPLOAD_KEY_ALIAS")
218
+ val releaseKeyPassword = releaseSigningProperty("MYAPP_UPLOAD_KEY_PASSWORD")
219
+ val hasReleaseSigning =
220
+ !releaseStoreFileName.isNullOrBlank() &&
221
+ !releaseStorePassword.isNullOrBlank() &&
222
+ !releaseKeyAlias.isNullOrBlank() &&
223
+ !releaseKeyPassword.isNullOrBlank()
224
+ // <<< rn-local-release-signing
225
+ `.trim();
226
+ const KTS_RELEASE_GUARD = `
227
+ // >>> rn-local-release-guard
228
+ gradle.taskGraph.whenReady {
229
+ if (hasReleaseSigning) return@whenReady
230
+ val wantsSignedRelease = allTasks.any { task ->
231
+ val name = task.name
232
+ name == "assembleRelease" ||
233
+ name == "bundleRelease" ||
234
+ name == "packageRelease" ||
235
+ name == "packageReleaseBundle" ||
236
+ name == "signReleaseBundle"
237
+ }
238
+ if (wantsSignedRelease) {
239
+ throw GradleException(
240
+ "Release signing is not configured. Set MYAPP_UPLOAD_* via android/keystore.properties or environment variables."
241
+ )
242
+ }
243
+ }
244
+ // <<< rn-local-release-guard
245
+ `.trim();
246
+ export function patchAndroidBuildGradleKts(content) {
247
+ let next = content;
248
+ if (!next.includes('rn-local-release-signing')) {
249
+ if (/android\s*\{/.test(next)) {
250
+ next = next.replace(/android\s*\{/, `${KTS_SIGNING_HELPER}\n\nandroid {`);
251
+ }
252
+ else {
253
+ next = `${KTS_SIGNING_HELPER}\n\n${next}`;
254
+ }
255
+ }
256
+ if (!next.includes('rn-local-release-guard')) {
257
+ next = `${next.trimEnd()}\n\n${KTS_RELEASE_GUARD}\n`;
258
+ }
259
+ return next;
260
+ }
261
+ export function patchAndroidBuildGradle(content, dsl) {
262
+ return dsl === 'kotlin'
263
+ ? patchAndroidBuildGradleKts(content)
264
+ : patchAndroidBuildGradleGroovy(content);
265
+ }
266
+ export function patchGradleProperties(content) {
267
+ // Do not force ABI/R8 changes — only strip accidental passwords from tracked file.
268
+ return content
269
+ .split('\n')
270
+ .filter((line) => !line.startsWith('MYAPP_UPLOAD_STORE_PASSWORD=') &&
271
+ !line.startsWith('MYAPP_UPLOAD_KEY_PASSWORD='))
272
+ .join('\n')
273
+ .replace(/\n*$/, '\n');
274
+ }
275
+ export function applyPlan(projectRoot, actions, dryRun) {
276
+ const managed = [];
277
+ const backups = [];
278
+ try {
279
+ for (const action of actions) {
280
+ if (action.type === 'skip')
281
+ continue;
282
+ const target = action.type === 'write' ? action.path : action.to;
283
+ const absolute = path.isAbsolute(target) ? target : path.join(projectRoot, target);
284
+ const originalHash = exists(absolute) ? sha256OfFile(absolute) : null;
285
+ if (dryRun) {
286
+ managed.push({
287
+ path: path.relative(projectRoot, absolute),
288
+ kind: originalHash ? 'patched' : 'created',
289
+ originalHash,
290
+ installedHash: action.type === 'write' ? sha256OfText(action.contents) : 'dry-run',
291
+ });
292
+ continue;
293
+ }
294
+ const backup = backupFile(absolute);
295
+ backups.push({ path: absolute, backup });
296
+ if (action.type === 'write') {
297
+ writeText(absolute, action.contents, action.mode ?? 0o644);
298
+ }
299
+ else {
300
+ const fromAbs = path.isAbsolute(action.from)
301
+ ? action.from
302
+ : path.join(projectRoot, action.from);
303
+ writeText(absolute, readText(fromAbs), action.mode ?? 0o644);
304
+ }
305
+ managed.push({
306
+ path: path.relative(projectRoot, absolute).split(path.sep).join('/'),
307
+ kind: originalHash ? 'patched' : 'created',
308
+ originalHash,
309
+ installedHash: sha256OfFile(absolute),
310
+ });
311
+ }
312
+ }
313
+ catch (error) {
314
+ for (const item of backups.reverse()) {
315
+ if (item.backup && exists(item.backup)) {
316
+ writeText(item.path, readText(item.backup));
317
+ }
318
+ }
319
+ throw error;
320
+ }
321
+ return managed;
322
+ }
323
+ export function releaseScripts() {
324
+ return {
325
+ 'release:doctor': 'bundle exec fastlane doctor',
326
+ 'release:verify': 'bundle exec fastlane verify',
327
+ 'release:credentials': 'bundle exec fastlane credentials',
328
+ 'release:bump': 'bundle exec fastlane bump_version',
329
+ 'release:prepare': 'bundle exec fastlane prepare',
330
+ 'release:compile': 'bundle exec fastlane compile',
331
+ 'release:upload': 'bundle exec fastlane upload_all',
332
+ 'release:ios:build': 'bundle exec fastlane ios build',
333
+ 'release:ios:beta': 'bundle exec fastlane ios beta',
334
+ 'release:ios:upload': 'bundle exec fastlane ios upload',
335
+ 'release:ios:promote': 'bundle exec fastlane ios promote',
336
+ 'release:android:build': 'bundle exec fastlane android build',
337
+ 'release:android:beta': 'bundle exec fastlane android beta',
338
+ 'release:android:upload': 'bundle exec fastlane android upload',
339
+ 'release:android:promote': 'bundle exec fastlane android promote',
340
+ 'release:build': 'bundle exec fastlane build_all',
341
+ 'release:beta': 'bundle exec fastlane beta_all',
342
+ };
343
+ }
344
+ export function renderTemplate(template, values) {
345
+ return template.replace(/\{\{(\w+)\}\}/g, (_match, key) => {
346
+ if (!(key in values)) {
347
+ throw new Error(`Missing template value: ${key}`);
348
+ }
349
+ return values[key];
350
+ });
351
+ }
352
+ //# sourceMappingURL=patch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patch.js","sourceRoot":"","sources":["../src/patch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Resolve a path and ensure it stays within the project root (no traversal).
3
+ */
4
+ export declare function resolveWithinRoot(projectRoot: string, inputPath: string): string;
5
+ export declare function toPosixRelative(projectRoot: string, absolutePath: string): string;
6
+ export declare function assertSafeRelativePath(relativePath: string): void;
7
+ export declare const TEAM_ID_RE: RegExp;
8
+ export declare const BUNDLE_ID_RE: RegExp;
9
+ export declare const SEMVER_RE: RegExp;
10
+ export declare function assertTeamId(value: string): void;
11
+ export declare function assertBundleId(value: string): void;
12
+ export declare function assertSemver(value: string): void;
13
+ export declare function rejectControlChars(value: string, label: string): void;
14
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAmBhF;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEjF;AAED,wBAAgB,sBAAsB,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAcjE;AAED,eAAO,MAAM,UAAU,QAAqB,CAAC;AAC7C,eAAO,MAAM,YAAY,QAAwD,CAAC;AAClF,eAAO,MAAM,SAAS,QAAyC,CAAC;AAEhE,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIhD;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIlD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAIhD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAIrE"}
package/dist/paths.js ADDED
@@ -0,0 +1,65 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ /**
4
+ * Resolve a path and ensure it stays within the project root (no traversal).
5
+ */
6
+ export function resolveWithinRoot(projectRoot, inputPath) {
7
+ const rootReal = fs.realpathSync(path.resolve(projectRoot));
8
+ const absolute = path.isAbsolute(inputPath)
9
+ ? path.resolve(inputPath)
10
+ : path.resolve(projectRoot, inputPath);
11
+ let resolved = absolute;
12
+ if (fs.existsSync(absolute)) {
13
+ resolved = fs.realpathSync(absolute);
14
+ }
15
+ const relative = path.relative(rootReal, resolved);
16
+ if (relative.startsWith('..') || path.isAbsolute(relative)) {
17
+ throw new Error(`Path escapes project root: ${inputPath}`);
18
+ }
19
+ if (inputPath.includes('\0') || /(^|[\\/])\.\.([\\/]|$)/.test(inputPath)) {
20
+ // Allow absolute paths outside check already handled; relative .. already rejected.
21
+ }
22
+ return resolved;
23
+ }
24
+ export function toPosixRelative(projectRoot, absolutePath) {
25
+ return path.relative(projectRoot, absolutePath).split(path.sep).join('/');
26
+ }
27
+ export function assertSafeRelativePath(relativePath) {
28
+ if (!relativePath || relativePath.trim() !== relativePath) {
29
+ throw new Error(`Invalid path: ${relativePath}`);
30
+ }
31
+ if (path.isAbsolute(relativePath) || relativePath.includes('\0')) {
32
+ throw new Error(`Absolute or invalid path not allowed: ${relativePath}`);
33
+ }
34
+ const normalized = path.posix.normalize(relativePath.replace(/\\/g, '/'));
35
+ if (normalized.startsWith('../') || normalized === '..' || normalized.startsWith('/')) {
36
+ throw new Error(`Path traversal rejected: ${relativePath}`);
37
+ }
38
+ if (!/^[\w./@+-]+$/.test(normalized)) {
39
+ throw new Error(`Path contains unsupported characters: ${relativePath}`);
40
+ }
41
+ }
42
+ export const TEAM_ID_RE = /^[A-Z0-9]{6,12}$/;
43
+ export const BUNDLE_ID_RE = /^[A-Za-z][A-Za-z0-9_-]*(\.[A-Za-z][A-Za-z0-9_-]*)+$/;
44
+ export const SEMVER_RE = /^\d+\.\d+\.\d+([.-][A-Za-z0-9.-]+)?$/;
45
+ export function assertTeamId(value) {
46
+ if (!TEAM_ID_RE.test(value)) {
47
+ throw new Error(`Invalid Apple Team ID: ${value}`);
48
+ }
49
+ }
50
+ export function assertBundleId(value) {
51
+ if (!BUNDLE_ID_RE.test(value)) {
52
+ throw new Error(`Invalid bundle/package identifier: ${value}`);
53
+ }
54
+ }
55
+ export function assertSemver(value) {
56
+ if (!SEMVER_RE.test(value)) {
57
+ throw new Error(`Invalid marketing version (expected SemVer): ${value}`);
58
+ }
59
+ }
60
+ export function rejectControlChars(value, label) {
61
+ if (/[\u0000-\u001f\u007f]/.test(value)) {
62
+ throw new Error(`${label} contains control characters`);
63
+ }
64
+ }
65
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../src/paths.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export type SpawnResult = {
2
+ status: number | null;
3
+ stdout: string;
4
+ stderr: string;
5
+ };
6
+ /**
7
+ * Execute a command without a shell — argv only.
8
+ */
9
+ export declare function runArgv(command: string, args: string[], options?: {
10
+ cwd?: string;
11
+ env?: NodeJS.ProcessEnv;
12
+ timeoutMs?: number;
13
+ }): SpawnResult;
14
+ /**
15
+ * Parse a simple package-manager command into argv.
16
+ * Rejects shell metacharacters. Supports: yarn lint, npm run typecheck, etc.
17
+ */
18
+ export declare function parseTrustedCommand(command: string): {
19
+ command: string;
20
+ args: string[];
21
+ };
22
+ export declare function runTrustedCommand(command: string, options?: {
23
+ cwd?: string;
24
+ env?: NodeJS.ProcessEnv;
25
+ }): SpawnResult;
26
+ //# sourceMappingURL=process.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;GAEG;AACH,wBAAgB,OAAO,CACrB,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GAC1E,WAAW,CAab;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAkBxF;AAED,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAA;CAAO,GACtD,WAAW,CAGb"}
@@ -0,0 +1,43 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ /**
3
+ * Execute a command without a shell — argv only.
4
+ */
5
+ export function runArgv(command, args, options = {}) {
6
+ const result = spawnSync(command, args, {
7
+ cwd: options.cwd,
8
+ env: options.env,
9
+ encoding: 'utf8',
10
+ shell: false,
11
+ timeout: options.timeoutMs,
12
+ });
13
+ return {
14
+ status: result.status,
15
+ stdout: result.stdout?.toString() || '',
16
+ stderr: result.stderr?.toString() || '',
17
+ };
18
+ }
19
+ const ALLOWED_PACKAGE_MANAGERS = new Set(['yarn', 'npm', 'pnpm', 'bundle', 'node']);
20
+ /**
21
+ * Parse a simple package-manager command into argv.
22
+ * Rejects shell metacharacters. Supports: yarn lint, npm run typecheck, etc.
23
+ */
24
+ export function parseTrustedCommand(command) {
25
+ const trimmed = command.trim();
26
+ if (!trimmed) {
27
+ throw new Error('Empty command');
28
+ }
29
+ if (/[;|&`$()<>]/.test(trimmed) || trimmed.includes('\n')) {
30
+ throw new Error(`Refusing shell metacharacters in command: ${trimmed}. Use argv-safe commands only.`);
31
+ }
32
+ const parts = trimmed.split(/\s+/);
33
+ const bin = parts[0];
34
+ if (!bin || !ALLOWED_PACKAGE_MANAGERS.has(bin)) {
35
+ throw new Error(`Command binary not allowlisted: ${bin}. Allowed: ${[...ALLOWED_PACKAGE_MANAGERS].join(', ')}`);
36
+ }
37
+ return { command: bin, args: parts.slice(1) };
38
+ }
39
+ export function runTrustedCommand(command, options = {}) {
40
+ const parsed = parseTrustedCommand(command);
41
+ return runArgv(parsed.command, parsed.args, options);
42
+ }
43
+ //# sourceMappingURL=process.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process.js","sourceRoot":"","sources":["../src/process.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAQ/C;;GAEG;AACH,MAAM,UAAU,OAAO,CACrB,OAAe,EACf,IAAc,EACd,UAAyE,EAAE;IAE3E,MAAM,MAAM,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE;QACtC,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,GAAG,EAAE,OAAO,CAAC,GAAG;QAChB,QAAQ,EAAE,MAAM;QAChB,KAAK,EAAE,KAAK;QACZ,OAAO,EAAE,OAAO,CAAC,SAAS;KAC3B,CAAC,CAAC;IACH,OAAO;QACL,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;QACvC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE;KACxC,CAAC;AACJ,CAAC;AAED,MAAM,wBAAwB,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;AAEpF;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,eAAe,CAAC,CAAC;IACnC,CAAC;IACD,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QAC1D,MAAM,IAAI,KAAK,CACb,6CAA6C,OAAO,gCAAgC,CACrF,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACrB,IAAI,CAAC,GAAG,IAAI,CAAC,wBAAwB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,mCAAmC,GAAG,cAAc,CAAC,GAAG,wBAAwB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC/F,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAe,EACf,UAAqD,EAAE;IAEvD,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;AACvD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import readline from 'node:readline/promises';
2
+ export declare function ask(rl: readline.Interface, question: string, defaultValue?: string): Promise<string>;
3
+ export declare function askYesNo(rl: readline.Interface, question: string, defaultYes?: boolean): Promise<boolean>;
4
+ /**
5
+ * Hidden password prompt — does not echo characters.
6
+ */
7
+ export declare function askSecret(question: string): Promise<string>;
8
+ export declare function createReadline(): readline.Interface;
9
+ //# sourceMappingURL=prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.d.ts","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,wBAAwB,CAAC;AAG9C,wBAAsB,GAAG,CACvB,EAAE,EAAE,QAAQ,CAAC,SAAS,EACtB,QAAQ,EAAE,MAAM,EAChB,YAAY,SAAK,GAChB,OAAO,CAAC,MAAM,CAAC,CAIjB;AAED,wBAAsB,QAAQ,CAC5B,EAAE,EAAE,QAAQ,CAAC,SAAS,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,UAAQ,GACjB,OAAO,CAAC,OAAO,CAAC,CAKlB;AAED;;GAEG;AACH,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA+CjE;AAED,wBAAgB,cAAc,IAAI,QAAQ,CAAC,SAAS,CAEnD"}
package/dist/prompt.js ADDED
@@ -0,0 +1,70 @@
1
+ import readline from 'node:readline/promises';
2
+ import { stdin as input, stdout as output } from 'node:process';
3
+ export async function ask(rl, question, defaultValue = '') {
4
+ const suffix = defaultValue ? ` [${defaultValue}]` : '';
5
+ const answer = (await rl.question(`${question}${suffix}: `)).trim();
6
+ return answer || defaultValue;
7
+ }
8
+ export async function askYesNo(rl, question, defaultYes = false) {
9
+ const hint = defaultYes ? 'Y/n' : 'y/N';
10
+ const answer = (await rl.question(`${question} (${hint}): `)).trim().toLowerCase();
11
+ if (!answer)
12
+ return defaultYes;
13
+ return answer === 'y' || answer === 'yes';
14
+ }
15
+ /**
16
+ * Hidden password prompt — does not echo characters.
17
+ */
18
+ export async function askSecret(question) {
19
+ if (!input.isTTY || !output.isTTY) {
20
+ // Non-TTY fallback: still avoid echoing by reading without local echo where possible.
21
+ const rl = readline.createInterface({ input, output: undefined });
22
+ try {
23
+ return (await rl.question(`${question}: `)).trim();
24
+ }
25
+ finally {
26
+ rl.close();
27
+ }
28
+ }
29
+ return new Promise((resolve, reject) => {
30
+ output.write(`${question}: `);
31
+ const wasRaw = input.isRaw;
32
+ input.setRawMode?.(true);
33
+ let value = '';
34
+ const onData = (chunk) => {
35
+ const str = chunk.toString('utf8');
36
+ for (const char of str) {
37
+ if (char === '\n' || char === '\r' || char === '\u0004') {
38
+ cleanup();
39
+ output.write('\n');
40
+ resolve(value);
41
+ return;
42
+ }
43
+ if (char === '\u0003') {
44
+ cleanup();
45
+ reject(new Error('Cancelled'));
46
+ return;
47
+ }
48
+ if (char === '\u007f' || char === '\b') {
49
+ value = value.slice(0, -1);
50
+ continue;
51
+ }
52
+ value += char;
53
+ }
54
+ };
55
+ const cleanup = () => {
56
+ input.off('data', onData);
57
+ if (typeof wasRaw === 'boolean') {
58
+ input.setRawMode?.(wasRaw);
59
+ }
60
+ else {
61
+ input.setRawMode?.(false);
62
+ }
63
+ };
64
+ input.on('data', onData);
65
+ });
66
+ }
67
+ export function createReadline() {
68
+ return readline.createInterface({ input, output });
69
+ }
70
+ //# sourceMappingURL=prompt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt.js","sourceRoot":"","sources":["../src/prompt.ts"],"names":[],"mappings":"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"}