bleam 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +23 -4
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +23 -4
- package/dist/{ui-234Plg7Z.d.cts → ui-Bg11tvlc.d.ts} +7 -7
- package/dist/ui.d.ts +1 -1
- package/dist/window.d.ts +1 -1
- package/package.json +4 -1
- package/templates/image-generation/app/index.tsx +1 -1
- package/templates/native/.gitattributes +1 -0
- package/templates/native/App.tsx +26 -0
- package/templates/native/app.json +12 -0
- package/templates/native/index.ts +8 -0
- package/templates/native/ios/.xcode.env +11 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/Flux2Config.swift +288 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/MemoryConfig.swift +233 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/MemoryOptimizationConfig.swift +157 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/ModelRegistry.swift +425 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/QuantizationConfig.swift +138 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/TransformerRepoProfile.swift +59 -0
- package/templates/native/ios/Bleam/AI/Flux2/Configuration/VAEConfig.swift +134 -0
- package/templates/native/ios/Bleam/AI/Flux2/Flux2Core.swift +55 -0
- package/templates/native/ios/Bleam/AI/Flux2/Loading/WeightLoader.swift +1192 -0
- package/templates/native/ios/Bleam/AI/Flux2/Pipeline/FluxImageRunner.swift +1282 -0
- package/templates/native/ios/Bleam/AI/Flux2/Pipeline/LatentUtils.swift +191 -0
- package/templates/native/ios/Bleam/AI/Flux2/Scheduler/FlowMatchEulerScheduler.swift +279 -0
- package/templates/native/ios/Bleam/AI/Flux2/Text/KleinTextEncoder.swift +380 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Attention.swift +379 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Embeddings.swift +159 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2FeedForward.swift +154 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2FusedKernels.swift +198 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2KVCache.swift +36 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Modulation.swift +193 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2ParallelAttention.swift +389 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2RoPE.swift +304 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2SingleBlock.swift +290 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2Transformer.swift +530 -0
- package/templates/native/ios/Bleam/AI/Flux2/Transformer/Flux2TransformerBlock.swift +362 -0
- package/templates/native/ios/Bleam/AI/Flux2/Utils/Flux2Debug.swift +101 -0
- package/templates/native/ios/Bleam/AI/Flux2/Utils/MLXCheckpoint.swift +118 -0
- package/templates/native/ios/Bleam/AI/Flux2/Utils/MemoryManager.swift +201 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/AutoencoderKL.swift +476 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/ResnetBlock.swift +316 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/VAEDecoder.swift +120 -0
- package/templates/native/ios/Bleam/AI/Flux2/VAE/VAEEncoder.swift +136 -0
- package/templates/native/ios/Bleam/AppDelegate.swift +373 -0
- package/templates/native/ios/Bleam/Appearance.swift +62 -0
- package/templates/native/ios/Bleam/Bridging-Header.h +3 -0
- package/templates/native/ios/Bleam/Images.xcassets/AppIcon.appiconset/Contents.json +13 -0
- package/templates/native/ios/Bleam/Images.xcassets/Contents.json +6 -0
- package/templates/native/ios/Bleam/Info.plist +76 -0
- package/templates/native/ios/Bleam/PrivacyInfo.xcprivacy +48 -0
- package/templates/native/ios/Bleam/SceneDelegate.swift +58 -0
- package/templates/native/ios/Bleam/SplashScreen.storyboard +47 -0
- package/templates/native/ios/Bleam/Supporting/Expo.plist +6 -0
- package/templates/native/ios/Bleam/bleam.entitlements +10 -0
- package/templates/native/ios/Bleam.xcodeproj/project.pbxproj +1146 -0
- package/templates/native/ios/Bleam.xcodeproj/xcshareddata/xcschemes/Bleam.xcscheme +88 -0
- package/templates/native/ios/GenerationService/GenerationService.entitlements +10 -0
- package/templates/native/ios/GenerationService/ImageGenerationRunner.swift +591 -0
- package/templates/native/ios/GenerationService/Info.plist +31 -0
- package/templates/native/ios/GenerationService/main.swift +165 -0
- package/templates/native/ios/PlatformHelper/Info.plist +29 -0
- package/templates/native/ios/PlatformHelper/main.swift +335 -0
- package/templates/native/ios/Podfile +198 -0
- package/templates/native/ios/Podfile.lock +2284 -0
- package/templates/native/ios/Podfile.properties.json +5 -0
- package/templates/native/ios/Shared/Generation/GenerationServiceProtocol.swift +12 -0
- package/templates/native/ios/Shared/Generation/GenerationWorkerProtocol.swift +124 -0
- package/templates/native/metro.config.js +6 -0
- package/templates/native/modules/bleam-runtime/BleamRuntime.podspec +14 -0
- package/templates/native/modules/bleam-runtime/expo-module.config.json +8 -0
- package/templates/native/modules/bleam-runtime/ios/AIModule.swift +1258 -0
- package/templates/native/modules/bleam-runtime/ios/GenerationContracts.swift +135 -0
- package/templates/native/modules/bleam-runtime/ios/PlatformModule.swift +313 -0
- package/templates/native/modules/bleam-runtime/package.json +5 -0
- package/templates/native/package.json +39 -0
- package/templates/native/scripts/build/mlx-frameworks.ts +421 -0
- package/templates/native/scripts/start/index.ts +474 -0
- package/templates/native/scripts/start/known-failures.ts +20 -0
- package/templates/native/scripts/start/loader.ts +76 -0
- package/templates/native/scripts/start/native-state.ts +242 -0
- package/templates/native/scripts/start/package.json +4 -0
- package/templates/native/scripts/start/swift-packages.ts +414 -0
- package/templates/native/scripts/start/xcode-formatter.ts +106 -0
- package/templates/native/tsconfig.json +14 -0
- package/templates/native/yarn.lock +3749 -0
- package/dist/ui-BJmXhz9Q.d.ts +0 -92
- package/dist/ui-Dd7SXdbg.d.cts +0 -92
- package/dist/ui-Ds88eETu.d.ts +0 -92
- package/dist/ui-TaqnB5SP.d.ts +0 -92
- package/dist/ui-WQvnXL0W.d.ts +0 -92
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto'
|
|
2
|
+
import {
|
|
3
|
+
existsSync,
|
|
4
|
+
mkdirSync,
|
|
5
|
+
readdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
rmSync,
|
|
8
|
+
writeFileSync,
|
|
9
|
+
} from 'node:fs'
|
|
10
|
+
import path from 'node:path'
|
|
11
|
+
|
|
12
|
+
export type NativeChangeKind = 'none' | 'incremental' | 'structural'
|
|
13
|
+
|
|
14
|
+
type NativeFingerprint = {
|
|
15
|
+
structural: string
|
|
16
|
+
incremental: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type NativeState = {
|
|
20
|
+
version: 1
|
|
21
|
+
fingerprint: NativeFingerprint
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const STATE_FILE_NAME = '.bleam-native-state.json'
|
|
25
|
+
|
|
26
|
+
const STRUCTURAL_EXACT_FILES = new Set([
|
|
27
|
+
'Podfile',
|
|
28
|
+
'Podfile.lock',
|
|
29
|
+
'Podfile.properties.json',
|
|
30
|
+
path.join('Pods', 'Manifest.lock'),
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
const APP_SOURCE_EXTENSIONS = new Set([
|
|
34
|
+
'.c',
|
|
35
|
+
'.cc',
|
|
36
|
+
'.cpp',
|
|
37
|
+
'.entitlements',
|
|
38
|
+
'.h',
|
|
39
|
+
'.hpp',
|
|
40
|
+
'.m',
|
|
41
|
+
'.mm',
|
|
42
|
+
'.plist',
|
|
43
|
+
'.swift',
|
|
44
|
+
'.xcconfig',
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
const IGNORED_SEGMENTS = new Set([
|
|
48
|
+
'.symlinks',
|
|
49
|
+
'Headers',
|
|
50
|
+
'Target Support Files',
|
|
51
|
+
'Vendor',
|
|
52
|
+
'build',
|
|
53
|
+
'xcuserdata',
|
|
54
|
+
])
|
|
55
|
+
|
|
56
|
+
function getStatePath(rootDir: string): string {
|
|
57
|
+
return path.join(rootDir, 'build', STATE_FILE_NAME)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isIgnoredRelativePath(relativePath: string): boolean {
|
|
61
|
+
const segments = relativePath.split(path.sep)
|
|
62
|
+
return segments.some((segment) => IGNORED_SEGMENTS.has(segment))
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isStructuralRelativePath(relativePath: string): boolean {
|
|
66
|
+
if (STRUCTURAL_EXACT_FILES.has(relativePath)) {
|
|
67
|
+
return true
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
relativePath.endsWith(
|
|
72
|
+
path.join('Pods', 'Pods.xcodeproj', 'project.pbxproj'),
|
|
73
|
+
) ||
|
|
74
|
+
relativePath.endsWith(path.join('.xcodeproj', 'project.pbxproj')) ||
|
|
75
|
+
relativePath.endsWith(path.join('.xcworkspace', 'contents.xcworkspacedata'))
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function isAppSourceRelativePath(relativePath: string): boolean {
|
|
80
|
+
if (relativePath.startsWith(`Pods${path.sep}`)) {
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (isStructuralRelativePath(relativePath)) {
|
|
85
|
+
return false
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
relativePath.includes(`${path.sep}.xcodeproj${path.sep}`) ||
|
|
90
|
+
relativePath.includes(`${path.sep}.xcworkspace${path.sep}`)
|
|
91
|
+
) {
|
|
92
|
+
return false
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return APP_SOURCE_EXTENSIONS.has(path.extname(relativePath))
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function classifyNativePath(
|
|
99
|
+
relativePath: string,
|
|
100
|
+
): NativeChangeKind | null {
|
|
101
|
+
if (isIgnoredRelativePath(relativePath)) {
|
|
102
|
+
return null
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (isStructuralRelativePath(relativePath)) {
|
|
106
|
+
return 'structural'
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (isAppSourceRelativePath(relativePath)) {
|
|
110
|
+
return 'incremental'
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return null
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function collectTrackedFiles(
|
|
117
|
+
rootDir: string,
|
|
118
|
+
): Record<NativeChangeKind, string[]> {
|
|
119
|
+
const buckets: Record<NativeChangeKind, string[]> = {
|
|
120
|
+
none: [],
|
|
121
|
+
incremental: [],
|
|
122
|
+
structural: [],
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const iosDir = path.join(rootDir, 'ios')
|
|
126
|
+
if (!existsSync(iosDir)) {
|
|
127
|
+
return buckets
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const walk = (currentDir: string) => {
|
|
131
|
+
for (const entry of readdirSync(currentDir, { withFileTypes: true })) {
|
|
132
|
+
const fullPath = path.join(currentDir, entry.name)
|
|
133
|
+
const relativePath = path.relative(iosDir, fullPath)
|
|
134
|
+
|
|
135
|
+
if (isIgnoredRelativePath(relativePath)) {
|
|
136
|
+
continue
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (entry.isDirectory()) {
|
|
140
|
+
walk(fullPath)
|
|
141
|
+
continue
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!entry.isFile()) {
|
|
145
|
+
continue
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const kind = classifyNativePath(relativePath)
|
|
149
|
+
if (kind) {
|
|
150
|
+
buckets[kind].push(relativePath)
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
walk(iosDir)
|
|
156
|
+
buckets.incremental.sort()
|
|
157
|
+
buckets.structural.sort()
|
|
158
|
+
return buckets
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function hashFiles(rootDir: string, relativePaths: string[]): string {
|
|
162
|
+
const hash = createHash('sha256')
|
|
163
|
+
const iosDir = path.join(rootDir, 'ios')
|
|
164
|
+
|
|
165
|
+
for (const relativePath of relativePaths) {
|
|
166
|
+
hash.update(relativePath)
|
|
167
|
+
hash.update('\0')
|
|
168
|
+
hash.update(readFileSync(path.join(iosDir, relativePath)))
|
|
169
|
+
hash.update('\0')
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return hash.digest('hex')
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function computeNativeFingerprint(rootDir: string): NativeFingerprint {
|
|
176
|
+
const trackedFiles = collectTrackedFiles(rootDir)
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
structural: hashFiles(rootDir, trackedFiles.structural),
|
|
180
|
+
incremental: hashFiles(rootDir, trackedFiles.incremental),
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function readNativeState(rootDir: string): NativeState | null {
|
|
185
|
+
const statePath = getStatePath(rootDir)
|
|
186
|
+
if (!existsSync(statePath)) {
|
|
187
|
+
return null
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
const parsed = JSON.parse(readFileSync(statePath, 'utf8')) as NativeState
|
|
192
|
+
if (
|
|
193
|
+
parsed.version !== 1 ||
|
|
194
|
+
typeof parsed.fingerprint?.structural !== 'string' ||
|
|
195
|
+
typeof parsed.fingerprint?.incremental !== 'string'
|
|
196
|
+
) {
|
|
197
|
+
return null
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return parsed
|
|
201
|
+
} catch {
|
|
202
|
+
return null
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function writeNativeState(
|
|
207
|
+
rootDir: string,
|
|
208
|
+
fingerprint: NativeFingerprint,
|
|
209
|
+
) {
|
|
210
|
+
const statePath = getStatePath(rootDir)
|
|
211
|
+
mkdirSync(path.dirname(statePath), { recursive: true })
|
|
212
|
+
writeFileSync(
|
|
213
|
+
statePath,
|
|
214
|
+
`${JSON.stringify({ version: 1, fingerprint }, null, 2)}\n`,
|
|
215
|
+
'utf8',
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export function clearNativeState(rootDir: string) {
|
|
220
|
+
rmSync(getStatePath(rootDir), { force: true })
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
export function getNativeChangeKind(rootDir: string): NativeChangeKind {
|
|
224
|
+
const previous = readNativeState(rootDir)
|
|
225
|
+
if (!previous) {
|
|
226
|
+
return 'structural'
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const current = computeNativeFingerprint(rootDir)
|
|
230
|
+
if (
|
|
231
|
+
current.structural === previous.fingerprint.structural &&
|
|
232
|
+
current.incremental === previous.fingerprint.incremental
|
|
233
|
+
) {
|
|
234
|
+
return 'none'
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (current.structural !== previous.fingerprint.structural) {
|
|
238
|
+
return 'structural'
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return 'incremental'
|
|
242
|
+
}
|
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
type SwiftPackageRequirement =
|
|
5
|
+
| { kind: 'branch'; branch: string }
|
|
6
|
+
| { kind: 'exactVersion'; version: string }
|
|
7
|
+
| { kind: 'upToNextMajorVersion'; minimumVersion: string }
|
|
8
|
+
|
|
9
|
+
type RemoteSwiftPackageRequirement = {
|
|
10
|
+
identity: string
|
|
11
|
+
repositoryURL: string
|
|
12
|
+
requirement: SwiftPackageRequirement
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type ResolvedPackagePin = {
|
|
16
|
+
identity: string
|
|
17
|
+
location: string
|
|
18
|
+
state: { branch?: string; revision?: string; version?: string }
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type ResolvePackagesPlan = {
|
|
22
|
+
args: string[]
|
|
23
|
+
reason:
|
|
24
|
+
| 'no-remote-dependencies'
|
|
25
|
+
| 'missing-package-resolved'
|
|
26
|
+
| 'missing-resolved-pin'
|
|
27
|
+
| 'requirement-mismatch'
|
|
28
|
+
| 'resolved-remote-packages'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
type XcodeProjectPackageReferences = {
|
|
32
|
+
remote: RemoteSwiftPackageRequirement[]
|
|
33
|
+
localCount: number
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
type XcodeObject = {
|
|
37
|
+
isa?: string
|
|
38
|
+
repositoryURL?: string
|
|
39
|
+
requirement?: Record<string, unknown>
|
|
40
|
+
path?: string
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const PACKAGE_RESOLVED_CANDIDATES = [
|
|
44
|
+
path.join('xcshareddata', 'swiftpm', 'Package.resolved'),
|
|
45
|
+
path.join('xcuserdata', 'swiftpm', 'Package.resolved'),
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
const REMOTE_PACKAGE_PROGRESS_PATTERNS = [
|
|
49
|
+
/(?:fetching|updating|cloning)\s*(?:from\s+)?(?<target>https?:\/\/\S+|git@\S+|\S+\.git|[^\s,]+)$/i,
|
|
50
|
+
/checking out\s+(?<target>[^\s,]+)(?:\s|$)/i,
|
|
51
|
+
/working copy of package ['"](?<target>[^'"]+)['"]/i,
|
|
52
|
+
/package ['"](?<target>[^'"]+)['"]/i,
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
function parseVersion(version: string): number[] | null {
|
|
56
|
+
const match = version.trim().match(/^(\d+)(?:\.(\d+))?(?:\.(\d+))?/)
|
|
57
|
+
if (!match) {
|
|
58
|
+
return null
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return [Number(match[1] ?? 0), Number(match[2] ?? 0), Number(match[3] ?? 0)]
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function compareVersions(left: string, right: string): number {
|
|
65
|
+
const leftParts = parseVersion(left)
|
|
66
|
+
const rightParts = parseVersion(right)
|
|
67
|
+
if (!leftParts || !rightParts) {
|
|
68
|
+
return left.localeCompare(right)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (let index = 0; index < 3; index += 1) {
|
|
72
|
+
const delta = leftParts[index] - rightParts[index]
|
|
73
|
+
if (delta !== 0) {
|
|
74
|
+
return delta
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return 0
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function normalizeIdentity(value: string): string {
|
|
82
|
+
return (
|
|
83
|
+
value
|
|
84
|
+
.trim()
|
|
85
|
+
.replace(/\.git$/i, '')
|
|
86
|
+
.replace(/\/+$/, '')
|
|
87
|
+
.split('/')
|
|
88
|
+
.pop()
|
|
89
|
+
?.toLowerCase() ?? value.trim().toLowerCase()
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function parseRequirement(
|
|
94
|
+
rawRequirement: Record<string, unknown>,
|
|
95
|
+
): SwiftPackageRequirement | null {
|
|
96
|
+
const kind =
|
|
97
|
+
typeof rawRequirement.kind === 'string' ? rawRequirement.kind : null
|
|
98
|
+
if (!kind) {
|
|
99
|
+
return null
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (kind === 'branch' && typeof rawRequirement.branch === 'string') {
|
|
103
|
+
return { kind, branch: rawRequirement.branch }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (kind === 'exactVersion' && typeof rawRequirement.version === 'string') {
|
|
107
|
+
return { kind, version: rawRequirement.version }
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (
|
|
111
|
+
kind === 'upToNextMajorVersion' &&
|
|
112
|
+
typeof rawRequirement.minimumVersion === 'string'
|
|
113
|
+
) {
|
|
114
|
+
return { kind, minimumVersion: rawRequirement.minimumVersion }
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function collectSectionEntries(sectionBody: string): string[] {
|
|
121
|
+
const entries = []
|
|
122
|
+
let cursor = 0
|
|
123
|
+
|
|
124
|
+
while (cursor < sectionBody.length) {
|
|
125
|
+
const start = sectionBody.indexOf('= {', cursor)
|
|
126
|
+
if (start === -1) {
|
|
127
|
+
break
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
let depth = 0
|
|
131
|
+
let end = start
|
|
132
|
+
for (; end < sectionBody.length; end += 1) {
|
|
133
|
+
const char = sectionBody[end]
|
|
134
|
+
if (char === '{') {
|
|
135
|
+
depth += 1
|
|
136
|
+
} else if (char === '}') {
|
|
137
|
+
depth -= 1
|
|
138
|
+
if (depth === 0) {
|
|
139
|
+
const semicolonIndex = sectionBody.indexOf(';', end)
|
|
140
|
+
if (semicolonIndex === -1) {
|
|
141
|
+
break
|
|
142
|
+
}
|
|
143
|
+
entries.push(sectionBody.slice(start + 3, end + 1))
|
|
144
|
+
cursor = semicolonIndex + 1
|
|
145
|
+
break
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (end >= sectionBody.length) {
|
|
151
|
+
break
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return entries
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function parseXcodeObjects(projectContents: string): XcodeObject[] {
|
|
159
|
+
const objectsMatch = projectContents.match(
|
|
160
|
+
/objects = \{([\s\S]*?)\n\s*\};\n\s*rootObject =/,
|
|
161
|
+
)
|
|
162
|
+
if (!objectsMatch) {
|
|
163
|
+
return []
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const objectsBlock = objectsMatch[1]
|
|
167
|
+
const sections = objectsBlock.split(/\/\* Begin /).slice(1)
|
|
168
|
+
const objects: XcodeObject[] = []
|
|
169
|
+
|
|
170
|
+
for (const section of sections) {
|
|
171
|
+
const endIndex = section.indexOf('/* End ')
|
|
172
|
+
const body = endIndex === -1 ? section : section.slice(0, endIndex)
|
|
173
|
+
for (const rawEntry of collectSectionEntries(body)) {
|
|
174
|
+
const isa = rawEntry.match(/\bisa = ([^;]+);/)?.[1]?.trim()
|
|
175
|
+
const repositoryURL = rawEntry
|
|
176
|
+
.match(/\brepositoryURL = ([^;]+);/)?.[1]
|
|
177
|
+
?.trim()
|
|
178
|
+
const localPath = rawEntry.match(/\bpath = ([^;]+);/)?.[1]?.trim()
|
|
179
|
+
const requirementBody = rawEntry.match(
|
|
180
|
+
/\brequirement = \{([\s\S]*?)\n\s*\};/,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
const requirement: Record<string, unknown> = {}
|
|
184
|
+
if (requirementBody) {
|
|
185
|
+
const fieldPattern = /(\w+) = ([^;]+);/g
|
|
186
|
+
let fieldMatch = fieldPattern.exec(requirementBody[1])
|
|
187
|
+
while (fieldMatch) {
|
|
188
|
+
requirement[fieldMatch[1]] = stripPbxString(fieldMatch[2])
|
|
189
|
+
fieldMatch = fieldPattern.exec(requirementBody[1])
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
objects.push({
|
|
194
|
+
isa: isa ? stripPbxString(isa) : undefined,
|
|
195
|
+
repositoryURL: repositoryURL
|
|
196
|
+
? stripPbxString(repositoryURL)
|
|
197
|
+
: undefined,
|
|
198
|
+
path: localPath ? stripPbxString(localPath) : undefined,
|
|
199
|
+
requirement,
|
|
200
|
+
})
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return objects
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function stripPbxString(value: string): string {
|
|
208
|
+
return value.trim().replace(/^"|"$/g, '')
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function readProjectPackageReferences(
|
|
212
|
+
rootDir: string,
|
|
213
|
+
projectName: string,
|
|
214
|
+
): XcodeProjectPackageReferences {
|
|
215
|
+
const projectPath = path.join(
|
|
216
|
+
rootDir,
|
|
217
|
+
'ios',
|
|
218
|
+
`${projectName}.xcodeproj`,
|
|
219
|
+
'project.pbxproj',
|
|
220
|
+
)
|
|
221
|
+
if (!existsSync(projectPath)) {
|
|
222
|
+
return { remote: [], localCount: 0 }
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const contents = readFileSync(projectPath, 'utf8')
|
|
226
|
+
const objects = parseXcodeObjects(contents)
|
|
227
|
+
const remote: RemoteSwiftPackageRequirement[] = []
|
|
228
|
+
let localCount = 0
|
|
229
|
+
|
|
230
|
+
for (const object of objects) {
|
|
231
|
+
if (object.isa === 'XCLocalSwiftPackageReference' && object.path) {
|
|
232
|
+
localCount += 1
|
|
233
|
+
continue
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (
|
|
237
|
+
object.isa !== 'XCRemoteSwiftPackageReference' ||
|
|
238
|
+
!object.repositoryURL ||
|
|
239
|
+
!object.requirement
|
|
240
|
+
) {
|
|
241
|
+
continue
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const requirement = parseRequirement(object.requirement)
|
|
245
|
+
if (!requirement) {
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
remote.push({
|
|
250
|
+
identity: normalizeIdentity(object.repositoryURL),
|
|
251
|
+
repositoryURL: object.repositoryURL,
|
|
252
|
+
requirement,
|
|
253
|
+
})
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
remote.sort((left, right) => left.identity.localeCompare(right.identity))
|
|
257
|
+
return { remote, localCount }
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function findPackageResolvedPath(
|
|
261
|
+
rootDir: string,
|
|
262
|
+
projectName: string,
|
|
263
|
+
): string | null {
|
|
264
|
+
const candidateRoots = [
|
|
265
|
+
path.join(rootDir, 'ios', `${projectName}.xcworkspace`),
|
|
266
|
+
path.join(
|
|
267
|
+
rootDir,
|
|
268
|
+
'ios',
|
|
269
|
+
`${projectName}.xcodeproj`,
|
|
270
|
+
'project.xcworkspace',
|
|
271
|
+
),
|
|
272
|
+
]
|
|
273
|
+
|
|
274
|
+
for (const candidateRoot of candidateRoots) {
|
|
275
|
+
for (const relativePath of PACKAGE_RESOLVED_CANDIDATES) {
|
|
276
|
+
const fullPath = path.join(candidateRoot, relativePath)
|
|
277
|
+
if (existsSync(fullPath)) {
|
|
278
|
+
return fullPath
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return null
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function readResolvedPackagePins(
|
|
287
|
+
rootDir: string,
|
|
288
|
+
projectName: string,
|
|
289
|
+
): ResolvedPackagePin[] | null {
|
|
290
|
+
const packageResolvedPath = findPackageResolvedPath(rootDir, projectName)
|
|
291
|
+
if (!packageResolvedPath) {
|
|
292
|
+
return null
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
try {
|
|
296
|
+
const parsed = JSON.parse(readFileSync(packageResolvedPath, 'utf8')) as {
|
|
297
|
+
pins?: Array<{
|
|
298
|
+
identity?: string
|
|
299
|
+
location?: string
|
|
300
|
+
repositoryURL?: string
|
|
301
|
+
state?: ResolvedPackagePin['state']
|
|
302
|
+
}>
|
|
303
|
+
object?: {
|
|
304
|
+
pins?: Array<{
|
|
305
|
+
identity?: string
|
|
306
|
+
location?: string
|
|
307
|
+
repositoryURL?: string
|
|
308
|
+
state?: ResolvedPackagePin['state']
|
|
309
|
+
}>
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const pins = parsed.pins ?? parsed.object?.pins ?? []
|
|
313
|
+
return pins
|
|
314
|
+
.filter((pin) => typeof pin.state === 'object' && pin.state !== null)
|
|
315
|
+
.map((pin) => ({
|
|
316
|
+
identity: normalizeIdentity(
|
|
317
|
+
pin.identity ?? pin.location ?? pin.repositoryURL ?? '',
|
|
318
|
+
),
|
|
319
|
+
location: pin.location ?? pin.repositoryURL ?? '',
|
|
320
|
+
state: pin.state ?? {},
|
|
321
|
+
}))
|
|
322
|
+
.filter((pin) => pin.identity.length > 0)
|
|
323
|
+
} catch {
|
|
324
|
+
return null
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function requirementSatisfied(
|
|
329
|
+
requirement: SwiftPackageRequirement,
|
|
330
|
+
pin: ResolvedPackagePin,
|
|
331
|
+
): boolean {
|
|
332
|
+
if (requirement.kind === 'branch') {
|
|
333
|
+
return pin.state.branch === requirement.branch
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (requirement.kind === 'exactVersion') {
|
|
337
|
+
return pin.state.version === requirement.version
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (!pin.state.version) {
|
|
341
|
+
return false
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const minimumParts = parseVersion(requirement.minimumVersion)
|
|
345
|
+
const versionParts = parseVersion(pin.state.version)
|
|
346
|
+
if (!minimumParts || !versionParts) {
|
|
347
|
+
return compareVersions(pin.state.version, requirement.minimumVersion) >= 0
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
return (
|
|
351
|
+
versionParts[0] === minimumParts[0] &&
|
|
352
|
+
compareVersions(pin.state.version, requirement.minimumVersion) >= 0
|
|
353
|
+
)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export function getSwiftPackageResolvePlan(
|
|
357
|
+
rootDir: string,
|
|
358
|
+
projectName: string,
|
|
359
|
+
): ResolvePackagesPlan {
|
|
360
|
+
const references = readProjectPackageReferences(rootDir, projectName)
|
|
361
|
+
if (references.remote.length === 0) {
|
|
362
|
+
return {
|
|
363
|
+
args: [],
|
|
364
|
+
reason: 'no-remote-dependencies',
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const pins = readResolvedPackagePins(rootDir, projectName)
|
|
369
|
+
if (!pins) {
|
|
370
|
+
return {
|
|
371
|
+
args: [],
|
|
372
|
+
reason: 'missing-package-resolved',
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const pinsByIdentity = new Map(pins.map((pin) => [pin.identity, pin]))
|
|
377
|
+
for (const remote of references.remote) {
|
|
378
|
+
const pin = pinsByIdentity.get(remote.identity)
|
|
379
|
+
if (!pin) {
|
|
380
|
+
return {
|
|
381
|
+
args: [],
|
|
382
|
+
reason: 'missing-resolved-pin',
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (!requirementSatisfied(remote.requirement, pin)) {
|
|
387
|
+
return {
|
|
388
|
+
args: [],
|
|
389
|
+
reason: 'requirement-mismatch',
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
args: ['-skipPackageUpdates'],
|
|
396
|
+
reason: 'resolved-remote-packages',
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
export function extractPackageProgressLabel(line: string): string | null {
|
|
401
|
+
for (const pattern of REMOTE_PACKAGE_PROGRESS_PATTERNS) {
|
|
402
|
+
const match = line.match(pattern)
|
|
403
|
+
const target = match?.groups?.target?.trim()
|
|
404
|
+
if (!target) {
|
|
405
|
+
continue
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
return normalizeIdentity(target)
|
|
409
|
+
.split(/[\s#?]/)[0]
|
|
410
|
+
.replace(/^["']|["']$/g, '')
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return null
|
|
414
|
+
}
|