lecodes-cli 0.19.0 → 0.20.0

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 (270) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +1069 -396
  3. package/package.json +14 -13
  4. package/runtime/scene-harness.json +1 -1
  5. package/runtime/sdk-types.json +1 -1
  6. package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
  7. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  8. package/runtime/web/assets/basis-C64VHDVD.js +1 -0
  9. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  10. package/runtime/web/assets/createViewerLite-BvHobVhp.js +852 -0
  11. package/runtime/web/assets/draco-BiISTFcR.js +118 -0
  12. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  13. package/runtime/web/assets/index-CfNCCBjN.js +2 -0
  14. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
  15. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
  16. package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
  17. package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
  18. package/runtime/web/embed.html +32 -0
  19. package/runtime/web/embed.js +171 -0
  20. package/src/api.ts +302 -0
  21. package/src/browserAuth.ts +87 -0
  22. package/src/cmgenTool.ts +104 -0
  23. package/src/commands/app.ts +896 -0
  24. package/src/commands/appAndroid.ts +603 -0
  25. package/src/commands/appDesktop.ts +339 -0
  26. package/src/commands/appDesktopMac.ts +470 -0
  27. package/src/commands/appIcon.ts +187 -0
  28. package/src/commands/appShared.ts +542 -0
  29. package/src/commands/appTemplates.ts +907 -0
  30. package/src/commands/appTemplatesAndroid.ts +599 -0
  31. package/src/commands/appTemplatesGradlew.ts +9 -0
  32. package/src/commands/assets.ts +28 -0
  33. package/src/commands/clone.ts +60 -0
  34. package/src/commands/compile.ts +253 -0
  35. package/src/commands/create.ts +59 -0
  36. package/src/commands/design.ts +863 -0
  37. package/src/commands/designTemplates.ts +11 -0
  38. package/src/commands/desktop.ts +127 -0
  39. package/src/commands/dev.ts +236 -0
  40. package/src/commands/diff.ts +59 -0
  41. package/src/commands/init.ts +191 -0
  42. package/src/commands/install.ts +198 -0
  43. package/src/commands/lightmap.ts +290 -0
  44. package/src/commands/link.ts +147 -0
  45. package/src/commands/login.ts +24 -0
  46. package/src/commands/navmesh.ts +225 -0
  47. package/src/commands/pn.ts +305 -0
  48. package/src/commands/projectTemplates.ts +130 -0
  49. package/src/commands/pull.ts +109 -0
  50. package/src/commands/push.ts +173 -0
  51. package/src/commands/render.ts +416 -0
  52. package/src/commands/scene.ts +148 -0
  53. package/src/commands/shaders.ts +157 -0
  54. package/src/commands/status.ts +29 -0
  55. package/src/commands/test.ts +371 -0
  56. package/src/commands/thumbs.ts +178 -0
  57. package/src/commands/types.ts +87 -0
  58. package/src/commands/update.ts +190 -0
  59. package/src/compile/assetIcons.ts +214 -0
  60. package/src/compile/collect.ts +46 -0
  61. package/src/compile/collectLocal.ts +37 -0
  62. package/src/compile/designCompile.ts +104 -0
  63. package/src/compile/fonts.ts +156 -0
  64. package/src/compile/headlessBundle.ts +129 -0
  65. package/src/compile/nativeStack.ts +21 -0
  66. package/src/compile/projectCompile.ts +109 -0
  67. package/src/compile/sceneCompile.ts +113 -0
  68. package/src/compile/screenEntry.ts +127 -0
  69. package/src/compile/shaders.ts +245 -0
  70. package/src/config.ts +42 -0
  71. package/src/designMeta.ts +35 -0
  72. package/src/desktopRenderer.ts +545 -0
  73. package/src/desktopScript.ts +276 -0
  74. package/src/dev/clientTemplates.ts +160 -0
  75. package/src/dev/devServer.ts +333 -0
  76. package/src/dev/webRunner.ts +22 -0
  77. package/src/dev/wsServer.ts +144 -0
  78. package/src/distRoot.ts +20 -0
  79. package/src/ignore.ts +163 -0
  80. package/src/index.ts +494 -0
  81. package/src/lecodes-3d-editor.d.ts +41 -0
  82. package/src/lecodes-assets.d.ts +7 -0
  83. package/src/lecodes-design.d.ts +191 -0
  84. package/src/lecodes-renderer.d.ts +131 -0
  85. package/src/localFiles.ts +144 -0
  86. package/src/manifest.ts +46 -0
  87. package/src/matcTool.ts +137 -0
  88. package/src/peers.ts +40 -0
  89. package/src/project.ts +61 -0
  90. package/src/projectEnv.ts +94 -0
  91. package/src/qrcode-terminal.d.ts +9 -0
  92. package/src/releases.ts +125 -0
  93. package/src/serverDiff.ts +78 -0
  94. package/src/textDiff.ts +103 -0
  95. package/src/types.ts +0 -0
  96. package/src/util.ts +146 -0
  97. package/runtime/sdk/animate/animate.ts +0 -238
  98. package/runtime/sdk/animate/bezier.ts +0 -138
  99. package/runtime/sdk/animate/easings.ts +0 -126
  100. package/runtime/sdk/canvas/Canvas.ts +0 -305
  101. package/runtime/sdk/compile/aspectMacro.ts +0 -86
  102. package/runtime/sdk/compile/assetIconMacro.ts +0 -384
  103. package/runtime/sdk/compile/assetMacro.ts +0 -146
  104. package/runtime/sdk/compile/assetName.ts +0 -50
  105. package/runtime/sdk/compile/bundler.ts +0 -287
  106. package/runtime/sdk/compile/compileProject.ts +0 -129
  107. package/runtime/sdk/compile/detectEntry.ts +0 -128
  108. package/runtime/sdk/compile/fontMacro.ts +0 -459
  109. package/runtime/sdk/compile/fontRegistry.ts +0 -78
  110. package/runtime/sdk/compile/header.ts +0 -67
  111. package/runtime/sdk/compile/index.ts +0 -100
  112. package/runtime/sdk/compile/libraryImports.ts +0 -52
  113. package/runtime/sdk/compile/liteMaterial.ts +0 -247
  114. package/runtime/sdk/compile/sceneEditor.ts +0 -88
  115. package/runtime/sdk/compile/serverSplit.ts +0 -233
  116. package/runtime/sdk/compile/serverTypes.ts +0 -227
  117. package/runtime/sdk/compile/sfnt.ts +0 -98
  118. package/runtime/sdk/compile/shaderSchema.ts +0 -202
  119. package/runtime/sdk/compile/shaderTargets.ts +0 -81
  120. package/runtime/sdk/compile/sourcemap.ts +0 -25
  121. package/runtime/sdk/core/Aspect.ts +0 -568
  122. package/runtime/sdk/core/InspectorUI.ts +0 -212
  123. package/runtime/sdk/core/StateMachine.ts +0 -308
  124. package/runtime/sdk/core/color.ts +0 -66
  125. package/runtime/sdk/core/compWrite.ts +0 -42
  126. package/runtime/sdk/core/events.ts +0 -38
  127. package/runtime/sdk/core/fields.ts +0 -120
  128. package/runtime/sdk/core/registry.ts +0 -23
  129. package/runtime/sdk/core/signals.ts +0 -277
  130. package/runtime/sdk/core/time.ts +0 -84
  131. package/runtime/sdk/g2/Camera2D.ts +0 -40
  132. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  133. package/runtime/sdk/g2/Node2D.ts +0 -267
  134. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  135. package/runtime/sdk/g2/Physics2D.ts +0 -346
  136. package/runtime/sdk/g2/Scene2D.ts +0 -209
  137. package/runtime/sdk/g2/Shape2D.ts +0 -259
  138. package/runtime/sdk/g2/Sprite.ts +0 -89
  139. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  140. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  141. package/runtime/sdk/g2/Texture2D.ts +0 -47
  142. package/runtime/sdk/g2/Tilemap.ts +0 -41
  143. package/runtime/sdk/g2/Tileset.ts +0 -71
  144. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  145. package/runtime/sdk/g2/autotile.ts +0 -433
  146. package/runtime/sdk/g2/cells.ts +0 -91
  147. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  148. package/runtime/sdk/g2/groups2d.ts +0 -106
  149. package/runtime/sdk/g2/loop.ts +0 -50
  150. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  151. package/runtime/sdk/g2/touch.ts +0 -83
  152. package/runtime/sdk/gl/Camera.ts +0 -160
  153. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  154. package/runtime/sdk/gl/CharacterController.ts +0 -238
  155. package/runtime/sdk/gl/Gearbox.ts +0 -212
  156. package/runtime/sdk/gl/Geometry.ts +0 -279
  157. package/runtime/sdk/gl/IK.ts +0 -193
  158. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  159. package/runtime/sdk/gl/Light.ts +0 -107
  160. package/runtime/sdk/gl/Lightmap.ts +0 -256
  161. package/runtime/sdk/gl/Material.ts +0 -260
  162. package/runtime/sdk/gl/Mesh.ts +0 -83
  163. package/runtime/sdk/gl/Model.ts +0 -124
  164. package/runtime/sdk/gl/NavAgent.ts +0 -337
  165. package/runtime/sdk/gl/NavMesh.ts +0 -397
  166. package/runtime/sdk/gl/Node.ts +0 -350
  167. package/runtime/sdk/gl/Noise.ts +0 -30
  168. package/runtime/sdk/gl/Particles.ts +0 -676
  169. package/runtime/sdk/gl/Physics.ts +0 -222
  170. package/runtime/sdk/gl/Plane.ts +0 -53
  171. package/runtime/sdk/gl/Ragdoll.ts +0 -270
  172. package/runtime/sdk/gl/Ray.ts +0 -16
  173. package/runtime/sdk/gl/Scene.ts +0 -486
  174. package/runtime/sdk/gl/Shape.ts +0 -409
  175. package/runtime/sdk/gl/Terrain.ts +0 -1092
  176. package/runtime/sdk/gl/Texture.ts +0 -63
  177. package/runtime/sdk/gl/Trigger.ts +0 -45
  178. package/runtime/sdk/gl/Vehicle.ts +0 -473
  179. package/runtime/sdk/gl/Wheel.ts +0 -240
  180. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  181. package/runtime/sdk/gl/animation/Animator.ts +0 -120
  182. package/runtime/sdk/gl/animation/Layer.ts +0 -31
  183. package/runtime/sdk/gl/animation/Loop.ts +0 -30
  184. package/runtime/sdk/gl/animation/Playback.ts +0 -44
  185. package/runtime/sdk/gl/animation/core.ts +0 -336
  186. package/runtime/sdk/gl/controls.ts +0 -95
  187. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  188. package/runtime/sdk/gl/scenarios.ts +0 -291
  189. package/runtime/sdk/gl/state.ts +0 -6
  190. package/runtime/sdk/gl/terrainMesh.ts +0 -219
  191. package/runtime/sdk/gl/touch.ts +0 -68
  192. package/runtime/sdk/inject.ts +0 -197
  193. package/runtime/sdk/math/Mathf.ts +0 -118
  194. package/runtime/sdk/math/mat4.ts +0 -278
  195. package/runtime/sdk/math/quat.ts +0 -232
  196. package/runtime/sdk/math/vec.ts +0 -255
  197. package/runtime/sdk/net/codec.ts +0 -119
  198. package/runtime/sdk/net/core.ts +0 -384
  199. package/runtime/sdk/net/index.ts +0 -181
  200. package/runtime/sdk/net/replication.ts +0 -622
  201. package/runtime/sdk/plugins/camera.ts +0 -81
  202. package/runtime/sdk/plugins/geolocation.ts +0 -123
  203. package/runtime/sdk/plugins/oauth.ts +0 -61
  204. package/runtime/sdk/plugins/permission.ts +0 -7
  205. package/runtime/sdk/plugins/push.ts +0 -132
  206. package/runtime/sdk/plugins/qr.ts +0 -73
  207. package/runtime/sdk/plugins/service.ts +0 -47
  208. package/runtime/sdk/runtime/app.ts +0 -101
  209. package/runtime/sdk/runtime/appEvents.ts +0 -54
  210. package/runtime/sdk/runtime/channel.ts +0 -61
  211. package/runtime/sdk/runtime/clipboard.ts +0 -20
  212. package/runtime/sdk/runtime/datetime.ts +0 -329
  213. package/runtime/sdk/runtime/device.ts +0 -293
  214. package/runtime/sdk/runtime/fetch.ts +0 -77
  215. package/runtime/sdk/runtime/files.ts +0 -23
  216. package/runtime/sdk/runtime/input.ts +0 -175
  217. package/runtime/sdk/runtime/media.ts +0 -111
  218. package/runtime/sdk/runtime/misc.ts +0 -16
  219. package/runtime/sdk/runtime/net.ts +0 -36
  220. package/runtime/sdk/runtime/rpc.ts +0 -218
  221. package/runtime/sdk/runtime/service.ts +0 -83
  222. package/runtime/sdk/runtime/share.ts +0 -9
  223. package/runtime/sdk/runtime/storage.ts +0 -13
  224. package/runtime/sdk/runtime/touch.ts +0 -76
  225. package/runtime/sdk/scene/defineScene.ts +0 -1353
  226. package/runtime/sdk/scene/editorPlugins.ts +0 -110
  227. package/runtime/sdk/scene/gizmos.ts +0 -148
  228. package/runtime/sdk/scene/grammar.ts +0 -120
  229. package/runtime/sdk/scene/material.ts +0 -188
  230. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  231. package/runtime/sdk/server/auth/global.ts +0 -80
  232. package/runtime/sdk/server/auth/host.ts +0 -318
  233. package/runtime/sdk/server/auth/models.ts +0 -83
  234. package/runtime/sdk/server/auth/types.ts +0 -50
  235. package/runtime/sdk/server/channel.ts +0 -56
  236. package/runtime/sdk/server/context.ts +0 -36
  237. package/runtime/sdk/server/db/defineDb.ts +0 -237
  238. package/runtime/sdk/server/db/fields.ts +0 -132
  239. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  240. package/runtime/sdk/server/db/index.ts +0 -7
  241. package/runtime/sdk/server/db/marci/query.ts +0 -412
  242. package/runtime/sdk/server/db/types.ts +0 -202
  243. package/runtime/sdk/server/errors.ts +0 -12
  244. package/runtime/sdk/server/host.ts +0 -74
  245. package/runtime/sdk/server/inject.ts +0 -13
  246. package/runtime/sdk/server/runtime.ts +0 -133
  247. package/runtime/sdk/server/validate.ts +0 -87
  248. package/runtime/sdk/ui/NativeView.ts +0 -142
  249. package/runtime/sdk/ui/UI.ts +0 -39
  250. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  251. package/runtime/sdk/ui/UIButton.ts +0 -101
  252. package/runtime/sdk/ui/UIContainer.ts +0 -60
  253. package/runtime/sdk/ui/UIImage.ts +0 -83
  254. package/runtime/sdk/ui/UIInput.ts +0 -185
  255. package/runtime/sdk/ui/UIModal.ts +0 -139
  256. package/runtime/sdk/ui/UINode.ts +0 -830
  257. package/runtime/sdk/ui/UIPager.ts +0 -362
  258. package/runtime/sdk/ui/UIPopover.ts +0 -100
  259. package/runtime/sdk/ui/UIScreen.ts +0 -123
  260. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  261. package/runtime/sdk/ui/UISpacer.ts +0 -14
  262. package/runtime/sdk/ui/UITabs.ts +0 -236
  263. package/runtime/sdk/ui/UIText.ts +0 -51
  264. package/runtime/sdk/ui/UIVideo.ts +0 -88
  265. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  266. package/runtime/sdk/ui/UIWidget.ts +0 -127
  267. package/runtime/sdk/ui/fonts.ts +0 -13
  268. package/runtime/sdk/ui/presentable.ts +0 -117
  269. package/runtime/sdk/ui/router.ts +0 -132
  270. package/runtime/sdk/ui/theme.ts +0 -84
@@ -0,0 +1,907 @@
1
+ import { swiftPackageName, type IosPackage } from "./appShared"
2
+
3
+ /* Templates for `lecodes app init ios` / `lecodes app sync` — distilled 1:1 from the
4
+ * hand-built, device-verified reference shell (docs/xcode-app-plan.md). Three ownership
5
+ * tiers: App/ Swift files are USER-owned after generation (init writes once, sync never
6
+ * touches them); Info.plist, the LeCodesRuntime package, Resources/ and the shell README
7
+ * are CLI-owned (sync regenerates); the pbxproj is written once at init — sync only restamps the
8
+ * MARKETING_VERSION / CURRENT_PROJECT_VERSION values (no structural edits, ever). */
9
+
10
+ export interface ShellIdentity {
11
+ displayName: string
12
+ bundleId: string
13
+ teamId: string
14
+ version: string
15
+ buildNumber: number
16
+ }
17
+
18
+ /** Escape a value for a double-quoted pbxproj string. */
19
+ const pbxStr = (s: string) => s.replace(/[\\"]/g, m => "\\" + m)
20
+
21
+ export const pbxprojTemplate = ({ displayName, bundleId, teamId, version, buildNumber }: ShellIdentity): string => {
22
+ const target = `
23
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
24
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
25
+ CODE_SIGN_ENTITLEMENTS = "App/App.entitlements";
26
+ CODE_SIGN_STYLE = Automatic;
27
+ CURRENT_PROJECT_VERSION = ${buildNumber};
28
+ DEVELOPMENT_TEAM = "${pbxStr(teamId)}";
29
+ GENERATE_INFOPLIST_FILE = YES;
30
+ INFOPLIST_FILE = "App/Info.plist";
31
+ INFOPLIST_KEY_CFBundleDisplayName = "${pbxStr(displayName)}";
32
+ INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
33
+ INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
34
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
35
+ INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
36
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
37
+ LD_RUNPATH_SEARCH_PATHS = (
38
+ "$(inherited)",
39
+ "@executable_path/Frameworks",
40
+ );
41
+ MARKETING_VERSION = ${version};
42
+ PRODUCT_BUNDLE_IDENTIFIER = "${pbxStr(bundleId)}";
43
+ PRODUCT_NAME = "$(TARGET_NAME)";
44
+ STRING_CATALOG_GENERATE_SYMBOLS = YES;
45
+ SWIFT_APPROACHABLE_CONCURRENCY = YES;
46
+ SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
47
+ SWIFT_EMIT_LOC_STRINGS = YES;
48
+ SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES;
49
+ SWIFT_VERSION = 5.0;
50
+ TARGETED_DEVICE_FAMILY = "1,2";`
51
+
52
+ const project = (debug: boolean) => `
53
+ ALWAYS_SEARCH_USER_PATHS = NO;
54
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
55
+ CLANG_ANALYZER_NONNULL = YES;
56
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
57
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
58
+ CLANG_ENABLE_MODULES = YES;
59
+ CLANG_ENABLE_OBJC_ARC = YES;
60
+ CLANG_ENABLE_OBJC_WEAK = YES;
61
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
62
+ CLANG_WARN_BOOL_CONVERSION = YES;
63
+ CLANG_WARN_COMMA = YES;
64
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
65
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
66
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
67
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
68
+ CLANG_WARN_EMPTY_BODY = YES;
69
+ CLANG_WARN_ENUM_CONVERSION = YES;
70
+ CLANG_WARN_INFINITE_RECURSION = YES;
71
+ CLANG_WARN_INT_CONVERSION = YES;
72
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
73
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
74
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
75
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
76
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
77
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
78
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
79
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
80
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
81
+ CLANG_WARN_UNREACHABLE_CODE = YES;
82
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
83
+ COPY_PHASE_STRIP = NO;
84
+ DEBUG_INFORMATION_FORMAT = ${debug ? "dwarf" : "\"dwarf-with-dsym\""};
85
+ DEVELOPMENT_TEAM = "${pbxStr(teamId)}";${debug ? "" : `
86
+ ENABLE_NS_ASSERTIONS = NO;`}
87
+ ENABLE_STRICT_OBJC_MSGSEND = YES;${debug ? `
88
+ ENABLE_TESTABILITY = YES;` : ""}
89
+ ENABLE_USER_SCRIPT_SANDBOXING = YES;
90
+ GCC_C_LANGUAGE_STANDARD = gnu17;${debug ? `
91
+ GCC_DYNAMIC_NO_PIC = NO;` : ""}
92
+ GCC_NO_COMMON_BLOCKS = YES;${debug ? `
93
+ GCC_OPTIMIZATION_LEVEL = 0;
94
+ GCC_PREPROCESSOR_DEFINITIONS = (
95
+ "DEBUG=1",
96
+ "$(inherited)",
97
+ );` : ""}
98
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
99
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
100
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
101
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
102
+ GCC_WARN_UNUSED_FUNCTION = YES;
103
+ GCC_WARN_UNUSED_VARIABLE = YES;
104
+ IPHONEOS_DEPLOYMENT_TARGET = 15.6;
105
+ LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
106
+ MTL_ENABLE_DEBUG_INFO = ${debug ? "INCLUDE_SOURCE" : "NO"};
107
+ MTL_FAST_MATH = YES;${debug ? `
108
+ ONLY_ACTIVE_ARCH = YES;` : ""}
109
+ SDKROOT = iphoneos;${debug ? `
110
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
111
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";` : `
112
+ SWIFT_COMPILATION_MODE = wholemodule;
113
+ VALIDATE_PRODUCT = YES;`}`
114
+
115
+ return `// !$*UTF8*$!
116
+ {
117
+ archiveVersion = 1;
118
+ classes = {
119
+ };
120
+ objectVersion = 77;
121
+ objects = {
122
+
123
+ /* Begin PBXBuildFile section */
124
+ B73550812FEBAC710005B4CC /* LeCodesRuntime in Frameworks */ = {isa = PBXBuildFile; productRef = B73550802FEBAC710005B4CC /* LeCodesRuntime */; };
125
+ /* End PBXBuildFile section */
126
+
127
+ /* Begin PBXFileReference section */
128
+ B73550672FEBAC0F0005B4CC /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
129
+ /* End PBXFileReference section */
130
+
131
+ /* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
132
+ B73550792FEBAC100005B4CC /* Exceptions for "App" folder in "App" target */ = {
133
+ isa = PBXFileSystemSynchronizedBuildFileExceptionSet;
134
+ membershipExceptions = (
135
+ App.entitlements,
136
+ Info.plist,
137
+ );
138
+ target = B73550662FEBAC0F0005B4CC /* App */;
139
+ };
140
+ /* End PBXFileSystemSynchronizedBuildFileExceptionSet section */
141
+
142
+ /* Begin PBXFileSystemSynchronizedRootGroup section */
143
+ B73550692FEBAC0F0005B4CC /* App */ = {
144
+ isa = PBXFileSystemSynchronizedRootGroup;
145
+ exceptions = (
146
+ B73550792FEBAC100005B4CC /* Exceptions for "App" folder in "App" target */,
147
+ );
148
+ path = App;
149
+ sourceTree = "<group>";
150
+ };
151
+ /* End PBXFileSystemSynchronizedRootGroup section */
152
+
153
+ /* Begin PBXFrameworksBuildPhase section */
154
+ B73550642FEBAC0F0005B4CC /* Frameworks */ = {
155
+ isa = PBXFrameworksBuildPhase;
156
+ buildActionMask = 2147483647;
157
+ files = (
158
+ B73550812FEBAC710005B4CC /* LeCodesRuntime in Frameworks */,
159
+ );
160
+ runOnlyForDeploymentPostprocessing = 0;
161
+ };
162
+ /* End PBXFrameworksBuildPhase section */
163
+
164
+ /* Begin PBXGroup section */
165
+ B735505E2FEBAC0F0005B4CC = {
166
+ isa = PBXGroup;
167
+ children = (
168
+ B73550692FEBAC0F0005B4CC /* App */,
169
+ B73550682FEBAC0F0005B4CC /* Products */,
170
+ );
171
+ sourceTree = "<group>";
172
+ };
173
+ B73550682FEBAC0F0005B4CC /* Products */ = {
174
+ isa = PBXGroup;
175
+ children = (
176
+ B73550672FEBAC0F0005B4CC /* App.app */,
177
+ );
178
+ name = Products;
179
+ sourceTree = "<group>";
180
+ };
181
+ /* End PBXGroup section */
182
+
183
+ /* Begin PBXNativeTarget section */
184
+ B73550662FEBAC0F0005B4CC /* App */ = {
185
+ isa = PBXNativeTarget;
186
+ buildConfigurationList = B735507A2FEBAC100005B4CC /* Build configuration list for PBXNativeTarget "App" */;
187
+ buildPhases = (
188
+ B73550632FEBAC0F0005B4CC /* Sources */,
189
+ B73550642FEBAC0F0005B4CC /* Frameworks */,
190
+ B73550652FEBAC0F0005B4CC /* Resources */,
191
+ );
192
+ buildRules = (
193
+ );
194
+ dependencies = (
195
+ );
196
+ fileSystemSynchronizedGroups = (
197
+ B73550692FEBAC0F0005B4CC /* App */,
198
+ );
199
+ name = App;
200
+ packageProductDependencies = (
201
+ B73550802FEBAC710005B4CC /* LeCodesRuntime */,
202
+ );
203
+ productName = App;
204
+ productReference = B73550672FEBAC0F0005B4CC /* App.app */;
205
+ productType = "com.apple.product-type.application";
206
+ };
207
+ /* End PBXNativeTarget section */
208
+
209
+ /* Begin PBXProject section */
210
+ B735505F2FEBAC0F0005B4CC /* Project object */ = {
211
+ isa = PBXProject;
212
+ attributes = {
213
+ BuildIndependentTargetsInParallel = 1;
214
+ LastSwiftUpdateCheck = 2650;
215
+ LastUpgradeCheck = 2650;
216
+ TargetAttributes = {
217
+ B73550662FEBAC0F0005B4CC = {
218
+ CreatedOnToolsVersion = 26.5;
219
+ };
220
+ };
221
+ };
222
+ buildConfigurationList = B73550622FEBAC0F0005B4CC /* Build configuration list for PBXProject "App" */;
223
+ developmentRegion = en;
224
+ hasScannedForEncodings = 0;
225
+ knownRegions = (
226
+ en,
227
+ Base,
228
+ );
229
+ mainGroup = B735505E2FEBAC0F0005B4CC;
230
+ minimizedProjectReferenceProxies = 1;
231
+ packageReferences = (
232
+ B73550822FEBAC720005B4CC /* XCLocalSwiftPackageReference "LeCodesRuntime" */,
233
+ );
234
+ preferredProjectObjectVersion = 77;
235
+ productRefGroup = B73550682FEBAC0F0005B4CC /* Products */;
236
+ projectDirPath = "";
237
+ projectRoot = "";
238
+ targets = (
239
+ B73550662FEBAC0F0005B4CC /* App */,
240
+ );
241
+ };
242
+ /* End PBXProject section */
243
+
244
+ /* Begin PBXResourcesBuildPhase section */
245
+ B73550652FEBAC0F0005B4CC /* Resources */ = {
246
+ isa = PBXResourcesBuildPhase;
247
+ buildActionMask = 2147483647;
248
+ files = (
249
+ );
250
+ runOnlyForDeploymentPostprocessing = 0;
251
+ };
252
+ /* End PBXResourcesBuildPhase section */
253
+
254
+ /* Begin PBXSourcesBuildPhase section */
255
+ B73550632FEBAC0F0005B4CC /* Sources */ = {
256
+ isa = PBXSourcesBuildPhase;
257
+ buildActionMask = 2147483647;
258
+ files = (
259
+ );
260
+ runOnlyForDeploymentPostprocessing = 0;
261
+ };
262
+ /* End PBXSourcesBuildPhase section */
263
+
264
+ /* Begin XCBuildConfiguration section */
265
+ B735507B2FEBAC100005B4CC /* Debug */ = {
266
+ isa = XCBuildConfiguration;
267
+ buildSettings = {${target}
268
+ };
269
+ name = Debug;
270
+ };
271
+ B735507C2FEBAC100005B4CC /* Release */ = {
272
+ isa = XCBuildConfiguration;
273
+ buildSettings = {${target}
274
+ };
275
+ name = Release;
276
+ };
277
+ B735507D2FEBAC100005B4CC /* Debug */ = {
278
+ isa = XCBuildConfiguration;
279
+ buildSettings = {${project(true)}
280
+ };
281
+ name = Debug;
282
+ };
283
+ B735507E2FEBAC100005B4CC /* Release */ = {
284
+ isa = XCBuildConfiguration;
285
+ buildSettings = {${project(false)}
286
+ };
287
+ name = Release;
288
+ };
289
+ /* End XCBuildConfiguration section */
290
+
291
+ /* Begin XCConfigurationList section */
292
+ B73550622FEBAC0F0005B4CC /* Build configuration list for PBXProject "App" */ = {
293
+ isa = XCConfigurationList;
294
+ buildConfigurations = (
295
+ B735507D2FEBAC100005B4CC /* Debug */,
296
+ B735507E2FEBAC100005B4CC /* Release */,
297
+ );
298
+ defaultConfigurationIsVisible = 0;
299
+ defaultConfigurationName = Release;
300
+ };
301
+ B735507A2FEBAC100005B4CC /* Build configuration list for PBXNativeTarget "App" */ = {
302
+ isa = XCConfigurationList;
303
+ buildConfigurations = (
304
+ B735507B2FEBAC100005B4CC /* Debug */,
305
+ B735507C2FEBAC100005B4CC /* Release */,
306
+ );
307
+ defaultConfigurationIsVisible = 0;
308
+ defaultConfigurationName = Release;
309
+ };
310
+ /* End XCConfigurationList section */
311
+
312
+ /* Begin XCLocalSwiftPackageReference section */
313
+ B73550822FEBAC720005B4CC /* XCLocalSwiftPackageReference "LeCodesRuntime" */ = {
314
+ isa = XCLocalSwiftPackageReference;
315
+ relativePath = LeCodesRuntime;
316
+ };
317
+ /* End XCLocalSwiftPackageReference section */
318
+
319
+ /* Begin XCSwiftPackageProductDependency section */
320
+ B73550802FEBAC710005B4CC /* LeCodesRuntime */ = {
321
+ isa = XCSwiftPackageProductDependency;
322
+ productName = LeCodesRuntime;
323
+ };
324
+ /* End XCSwiftPackageProductDependency section */
325
+ };
326
+ rootObject = B735505F2FEBAC0F0005B4CC /* Project object */;
327
+ }
328
+ `
329
+ }
330
+
331
+ export const SCHEME_XML = `<?xml version="1.0" encoding="UTF-8"?>
332
+ <Scheme
333
+ LastUpgradeVersion = "2650"
334
+ version = "1.7">
335
+ <BuildAction
336
+ parallelizeBuildables = "YES"
337
+ buildImplicitDependencies = "YES">
338
+ <BuildActionEntries>
339
+ <BuildActionEntry
340
+ buildForTesting = "YES"
341
+ buildForRunning = "YES"
342
+ buildForProfiling = "YES"
343
+ buildForArchiving = "YES"
344
+ buildForAnalyzing = "YES">
345
+ <BuildableReference
346
+ BuildableIdentifier = "primary"
347
+ BlueprintIdentifier = "B73550662FEBAC0F0005B4CC"
348
+ BuildableName = "App.app"
349
+ BlueprintName = "App"
350
+ ReferencedContainer = "container:App.xcodeproj">
351
+ </BuildableReference>
352
+ </BuildActionEntry>
353
+ </BuildActionEntries>
354
+ </BuildAction>
355
+ <TestAction
356
+ buildConfiguration = "Debug"
357
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
358
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
359
+ shouldUseLaunchSchemeArgsEnv = "YES">
360
+ <Testables>
361
+ </Testables>
362
+ </TestAction>
363
+ <LaunchAction
364
+ buildConfiguration = "Debug"
365
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
366
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
367
+ launchStyle = "0"
368
+ useCustomWorkingDirectory = "NO"
369
+ ignoresPersistentStateOnLaunch = "NO"
370
+ debugDocumentVersioning = "YES"
371
+ debugServiceExtension = "internal"
372
+ allowLocationSimulation = "YES">
373
+ <BuildableProductRunnable
374
+ runnableDebuggingMode = "0">
375
+ <BuildableReference
376
+ BuildableIdentifier = "primary"
377
+ BlueprintIdentifier = "B73550662FEBAC0F0005B4CC"
378
+ BuildableName = "App.app"
379
+ BlueprintName = "App"
380
+ ReferencedContainer = "container:App.xcodeproj">
381
+ </BuildableReference>
382
+ </BuildableProductRunnable>
383
+ </LaunchAction>
384
+ <ProfileAction
385
+ buildConfiguration = "Release"
386
+ shouldUseLaunchSchemeArgsEnv = "YES"
387
+ savedToolIdentifier = ""
388
+ useCustomWorkingDirectory = "NO"
389
+ debugDocumentVersioning = "YES">
390
+ <BuildableProductRunnable
391
+ runnableDebuggingMode = "0">
392
+ <BuildableReference
393
+ BuildableIdentifier = "primary"
394
+ BlueprintIdentifier = "B73550662FEBAC0F0005B4CC"
395
+ BuildableName = "App.app"
396
+ BlueprintName = "App"
397
+ ReferencedContainer = "container:App.xcodeproj">
398
+ </BuildableReference>
399
+ </BuildableProductRunnable>
400
+ </ProfileAction>
401
+ <AnalyzeAction
402
+ buildConfiguration = "Debug">
403
+ </AnalyzeAction>
404
+ <ArchiveAction
405
+ buildConfiguration = "Release"
406
+ revealArchiveInOrganizer = "YES">
407
+ </ArchiveAction>
408
+ </Scheme>
409
+ `
410
+
411
+ export const APP_DELEGATE = `//
412
+ // AppDelegate.swift
413
+ // Generated by lecodes app init — yours to edit.
414
+ //
415
+
416
+ import UIKit
417
+ import LeCodesRuntime
418
+
419
+ @main
420
+ class AppDelegate: UIResponder, UIApplicationDelegate {
421
+
422
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
423
+ // Plugin hook surface: installs the notification-center delegate (must be in
424
+ // place before the app finishes launching) and buffers launch-time events until
425
+ // a plugin that needs them registers. Inert if no installed plugin consumes it.
426
+ LeCodesAppHooks.install()
427
+ return true
428
+ }
429
+
430
+ // APNs token callbacks → the plugin hook surface (consumed by the push plugin).
431
+ func application(_ application: UIApplication,
432
+ didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
433
+ LeCodesAppHooks.remoteNotificationsToken(deviceToken)
434
+ }
435
+
436
+ func application(_ application: UIApplication,
437
+ didFailToRegisterForRemoteNotificationsWithError error: Error) {
438
+ LeCodesAppHooks.remoteNotificationsError(error)
439
+ }
440
+
441
+ // MARK: UISceneSession Lifecycle
442
+
443
+ func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
444
+ return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
445
+ }
446
+ }
447
+ `
448
+
449
+ export const SCENE_DELEGATE = `//
450
+ // SceneDelegate.swift
451
+ // Generated by lecodes app init — yours to edit.
452
+ //
453
+
454
+ import UIKit
455
+ import LeCodesRuntime
456
+
457
+ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
458
+
459
+ var window: UIWindow?
460
+
461
+ func scene(
462
+ _ scene: UIScene,
463
+ willConnectTo session: UISceneSession,
464
+ options connectionOptions: UIScene.ConnectionOptions
465
+ ) {
466
+ guard let windowScene = (scene as? UIWindowScene) else { return }
467
+
468
+ // Cold-started by a notification tap: buffered by the hooks; a plugin that
469
+ // consumes it (push) drains the buffer when it registers — before the JS world
470
+ // boots, because makeKeyAndVisible() runs registerBundledPlugins via viewDidLoad.
471
+ if let response = connectionOptions.notificationResponse {
472
+ LeCodesAppHooks.coldStartNotification(response)
473
+ }
474
+
475
+ let window = UIWindow(windowScene: windowScene)
476
+ window.rootViewController = ViewController()
477
+ self.window = window
478
+ window.makeKeyAndVisible()
479
+ }
480
+ }
481
+ `
482
+
483
+ export const VIEW_CONTROLLER = `//
484
+ // ViewController.swift
485
+ // Generated by lecodes app init — yours to edit.
486
+ //
487
+ // Runs the bundled app.js (produced by \`lecodes app sync\`) offline: everything the
488
+ // script needs is provided by the SDK core (_creatorUI / _creatorUtils / console)
489
+ // plus the plugins registered below via LeCodesRuntime.
490
+ //
491
+
492
+ import UIKit
493
+ import LeCodesRuntime
494
+
495
+ class ViewController: LeCodesViewController {
496
+
497
+ override func viewDidLoad() {
498
+ super.viewDidLoad()
499
+
500
+ let lang = Locale.preferredLanguages.first.map { String($0.prefix(2)) } ?? "en"
501
+ engine.setLanguage(lang)
502
+
503
+ // \`// preload:\` assets (images, fonts) resolve out of the app bundle.
504
+ engine.setFetchLocal { path in
505
+ let ns = path as NSString
506
+ let ext = ns.pathExtension
507
+ let name = ns.deletingPathExtension
508
+ guard let url = Bundle.main.url(forResource: name, withExtension: ext.isEmpty ? nil : ext) else {
509
+ return nil
510
+ }
511
+ return try? Data(contentsOf: url)
512
+ }
513
+
514
+ registerBundledPlugins(in: engine)
515
+
516
+ // OTA (native, no JS involved): start the check FIRST — even a bundle that crashes
517
+ // on boot can never block its own fix — then run the newest local bundle (a
518
+ // downloaded update, or the embedded app.js). New code applies on the next launch.
519
+ // Configured via app.json "update" (→ Info.plist LeCodesUpdateURL); offline: no-op.
520
+ LeCodesUpdater.checkForUpdate()
521
+ guard let code = LeCodesUpdater.activeBundle() else {
522
+ print("LeCodes: bundled app.js is missing — run \`lecodes app sync\`")
523
+ return
524
+ }
525
+ engine.run(code)
526
+ }
527
+ }
528
+ `
529
+
530
+ /** A JSON value as plist XML — plugin manifests carry plist-typed JSON (push's
531
+ * `{"aps-environment": "development"}`, associated-domains arrays, …). */
532
+ const plistValue = (value: unknown, indent: string): string => {
533
+ if (typeof value === "string") return `<string>${value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")}</string>`
534
+ if (typeof value === "boolean") return value ? "<true/>" : "<false/>"
535
+ if (typeof value === "number") return Number.isInteger(value) ? `<integer>${value}</integer>` : `<real>${value}</real>`
536
+ if (Array.isArray(value)) {
537
+ if (value.length === 0) return "<array/>"
538
+ return `<array>\n${value.map(v => `${indent}\t${plistValue(v, indent + "\t")}`).join("\n")}\n${indent}</array>`
539
+ }
540
+ if (value && typeof value === "object") {
541
+ const entries = Object.entries(value as Record<string, unknown>)
542
+ if (entries.length === 0) return "<dict/>"
543
+ return `<dict>\n${entries.map(([k, v]) => `${indent}\t<key>${k}</key>\n${indent}\t${plistValue(v, indent + "\t")}`).join("\n")}\n${indent}</dict>`
544
+ }
545
+ return "<string></string>"
546
+ }
547
+
548
+ /** App/App.entitlements — CLI-owned: the capabilities the installed plugins' manifests declare
549
+ * (push's aps-environment, …), sorted for stable diffs. Referenced by CODE_SIGN_ENTITLEMENTS
550
+ * in the pbxproj; Xcode's automatic signing enables the matching App ID capabilities from it
551
+ * (and archive/export rewrites values like aps-environment to their distribution form). */
552
+ export const entitlementsTemplate = (entries: Record<string, unknown>): string => {
553
+ const keys = Object.keys(entries).sort()
554
+ const body = keys.length === 0 ? "" :
555
+ keys.map(k => ` <key>${k}</key>\n ${plistValue(entries[k], "\t")}`).join("\n") + "\n"
556
+ return `<?xml version="1.0" encoding="UTF-8"?>
557
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
558
+ <plist version="1.0">
559
+ <dict>
560
+ ${body}</dict>
561
+ </plist>
562
+ `
563
+ }
564
+
565
+ /** Info.plist — CLI-owned: the scene manifest plus usage-description keys merged from the
566
+ * installed plugins' manifests (sorted for stable diffs), plus the native OTA source
567
+ * (app.json "update" → LeCodesUpdater reads this key at launch). */
568
+ export const infoPlistTemplate = (pluginKeys: Record<string, string>, updateUrl?: string): string => {
569
+ const xml = (s: string) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
570
+ const keys = Object.keys(pluginKeys).sort()
571
+ const pluginBlock = keys.length === 0 ? "" : `
572
+ <!-- lecodes:plugins — merged from installed plugin manifests by \`lecodes app sync\` -->
573
+ ${keys.map(k => ` <key>${k}</key>\n <string>${pluginKeys[k]}</string>`).join("\n")}`
574
+ const updateBlock = !updateUrl ? "" : `
575
+ <!-- lecodes:update — native OTA source (app.json "update"); LeCodesUpdater checks it at launch -->
576
+ <key>LeCodesUpdateURL</key>
577
+ <string>${xml(updateUrl)}</string>`
578
+ return `<?xml version="1.0" encoding="UTF-8"?>
579
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
580
+ <plist version="1.0">
581
+ <dict>
582
+ <!-- Standard-encryption-only (HTTPS/TLS): declares export-compliance exemption
583
+ so App Store Connect skips the "Missing Compliance" prompt on every build.
584
+ Flip to <true/> only if the app adds its own non-exempt cryptography. -->
585
+ <key>ITSAppUsesNonExemptEncryption</key>
586
+ <false/>
587
+ <key>UIApplicationSceneManifest</key>
588
+ <dict>
589
+ <key>UIApplicationSupportsMultipleScenes</key>
590
+ <false/>
591
+ <key>UISceneConfigurations</key>
592
+ <dict>
593
+ <key>UIWindowSceneSessionRoleApplication</key>
594
+ <array>
595
+ <dict>
596
+ <key>UISceneConfigurationName</key>
597
+ <string>Default Configuration</string>
598
+ <key>UISceneDelegateClassName</key>
599
+ <string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
600
+ </dict>
601
+ </array>
602
+ </dict>
603
+ </dict>${pluginBlock}${updateBlock}
604
+ </dict>
605
+ </plist>
606
+ `
607
+ }
608
+
609
+ export const LAUNCH_STORYBOARD = `<?xml version="1.0" encoding="UTF-8" standalone="no"?>
610
+ <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
611
+ <dependencies>
612
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
613
+ <capability name="Safe area layout guides" minToolsVersion="9.0"/>
614
+ <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
615
+ </dependencies>
616
+ <scenes>
617
+ <!--View Controller-->
618
+ <scene sceneID="EHf-IW-A2E">
619
+ <objects>
620
+ <viewController id="01J-lp-oVM" sceneMemberID="viewController">
621
+ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
622
+ <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
623
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
624
+ <color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
625
+ <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
626
+ </view>
627
+ </viewController>
628
+ <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
629
+ </objects>
630
+ <point key="canvasLocation" x="53" y="375"/>
631
+ </scene>
632
+ </scenes>
633
+ </document>
634
+ `
635
+
636
+ export const ASSETS_ROOT_JSON = `{
637
+ "info" : {
638
+ "author" : "xcode",
639
+ "version" : 1
640
+ }
641
+ }
642
+ `
643
+
644
+ export const ACCENT_COLOR_JSON = `{
645
+ "colors" : [
646
+ {
647
+ "idiom" : "universal"
648
+ }
649
+ ],
650
+ "info" : {
651
+ "author" : "xcode",
652
+ "version" : 1
653
+ }
654
+ }
655
+ `
656
+
657
+ export const APP_ICON_JSON = `{
658
+ "images" : [
659
+ {
660
+ "idiom" : "universal",
661
+ "platform" : "ios",
662
+ "size" : "1024x1024"
663
+ },
664
+ {
665
+ "appearances" : [
666
+ {
667
+ "appearance" : "luminosity",
668
+ "value" : "dark"
669
+ }
670
+ ],
671
+ "idiom" : "universal",
672
+ "platform" : "ios",
673
+ "size" : "1024x1024"
674
+ },
675
+ {
676
+ "appearances" : [
677
+ {
678
+ "appearance" : "luminosity",
679
+ "value" : "tinted"
680
+ }
681
+ ],
682
+ "idiom" : "universal",
683
+ "platform" : "ios",
684
+ "size" : "1024x1024"
685
+ }
686
+ ],
687
+ "info" : {
688
+ "author" : "xcode",
689
+ "version" : 1
690
+ }
691
+ }
692
+ `
693
+
694
+ export interface RuntimePlugin {
695
+ /** Swift module / target name (manifest ios.module), e.g. "LeCodesCamera". */
696
+ module: string
697
+ /** Type exposing `static func register(in: LeCodesEngine)` (manifest ios.register). */
698
+ register: string
699
+ /** Plugin id, for the generated comments. */
700
+ id: string
701
+ /** Swift packages the target links (manifest ios.packages). */
702
+ packages?: IosPackage[]
703
+ }
704
+
705
+ /** The SDK binary products (all vend the same `LeCodesSDK` module): core = UI only,
706
+ * 2d = + Creator2D, 3d = + CreatorGL/filament with Jolt physics (AR included), full = everything. */
707
+ export type SdkVariant = "core" | "2d" | "3d" | "full"
708
+
709
+ /** LeCodesRuntime/Package.swift — the CLI-owned dependency graph: the SDK variant + one
710
+ * vendored target per installed plugin.
711
+ *
712
+ * `sdkVersion` is always an exact release and is pinned with `.exact(…)`: the CLI also
713
+ * downloads that version's LeCodesSDKResources.bundle (filament assets) into App/Resources/,
714
+ * so letting SwiftPM drift to a newer release on its own would pair mismatched halves of one
715
+ * SDK. Moving the version is the CLI's job — `lecodes app update` (or app.json ios.sdk). */
716
+ export const runtimePackageTemplate = (sdkVersion: string, variant: SdkVariant, plugins: RuntimePlugin[]): string => {
717
+ const product = `LeCodesSDK-${variant}`
718
+ // Third-party Swift packages (manifest ios.packages), one `.package(…)` per url. Two plugins
719
+ // naming the same url must agree on the pin — SwiftPM has one resolution per package.
720
+ const packages = new Map<string, IosPackage>()
721
+ for (const p of plugins) {
722
+ for (const pkg of p.packages ?? []) {
723
+ const prior = packages.get(pkg.url)
724
+ if (prior && (prior.exact !== pkg.exact || prior.from !== pkg.from)) {
725
+ throw new Error(`Plugin "${p.id}" pins ${pkg.url} at ${pkg.exact ?? `from ${pkg.from}`} but another plugin pins ${prior.exact ?? `from ${prior.from}`} — the shell can link one version.`)
726
+ }
727
+ packages.set(pkg.url, pkg)
728
+ }
729
+ }
730
+ const packageDecls = [...packages.values()].map(pkg =>
731
+ `\n .package(url: "${pkg.url}", ${pkg.exact ? `exact: "${pkg.exact}"` : `from: "${pkg.from}"`}),`).join("")
732
+ const pluginTargets = plugins.map(p => {
733
+ const deps = [
734
+ `.product(name: "${product}", package: "lecodes-ios-sdk")`,
735
+ ...(p.packages ?? []).flatMap(pkg => pkg.products.map(name => `.product(name: "${name}", package: "${swiftPackageName(pkg.url)}")`)),
736
+ ]
737
+ return `
738
+ // plugin: ${p.id} (vendored — see plugins.lock.json)
739
+ .target(
740
+ name: "${p.module}",
741
+ dependencies: [${deps.join(", ")}],
742
+ path: "Sources/${p.module}"
743
+ ),`
744
+ }).join("")
745
+ const runtimeDeps = [`.product(name: "${product}", package: "lecodes-ios-sdk")`, ...plugins.map(p => `"${p.module}"`)]
746
+ return `// swift-tools-version:5.9
747
+ //
748
+ // GENERATED by lecodes — regenerated by \`lecodes app sync\`; do not edit.
749
+ // Owns the app's native dependency graph: the LeCodesSDK variant (auto-picked from what
750
+ // the compiled bundle uses, or forced via app.json ios.variant), the exact SDK release
751
+ // (app.json ios.sdk — move it with \`lecodes app update\`) and one vendored target per
752
+ // installed plugin (see plugins.lock.json).
753
+
754
+ import PackageDescription
755
+
756
+ let package = Package(
757
+ name: "LeCodesRuntime",
758
+ platforms: [.iOS(.v15)],
759
+ products: [
760
+ .library(name: "LeCodesRuntime", targets: ["LeCodesRuntime"]),
761
+ ],
762
+ dependencies: [
763
+ .package(url: "https://github.com/letary/lecodes-ios-sdk.git", exact: "${sdkVersion}"),${packageDecls}
764
+ ],
765
+ targets: [
766
+ .target(
767
+ name: "LeCodesRuntime",
768
+ dependencies: [
769
+ ${runtimeDeps.join(",\n ")},
770
+ ],
771
+ path: "Sources/LeCodesRuntime"
772
+ ),${pluginTargets}
773
+ ]
774
+ )
775
+ `
776
+ }
777
+
778
+ /** Sources/LeCodesRuntime/LeCodesRuntime.swift — SDK re-export + plugin registration +
779
+ * (server-backed projects, SDK ≥ 1.6.0) the baked project identity. Baking it here — in a
780
+ * CLI-owned file that runs BEFORE \`engine.run(code)\` — means every shell picks identity up
781
+ * from a plain \`lecodes app sync\`, with no edits to the user-owned ViewController. */
782
+ export const runtimeSwiftTemplate = (plugins: RuntimePlugin[], projectUuid?: string): string => {
783
+ const imports = plugins.map(p => `import ${p.module}\n`).join("")
784
+ const identity = !projectUuid ? "" : ` // World identity: this shell IS the project — the engine attributes the boot world so
785
+ // identity-gated services (push registration) see the app's uuid from its first
786
+ // synchronous statement. Baked from .lecodes/manifest.json by \`lecodes app sync\`.
787
+ engine.bootProjectUuid = "${projectUuid}"
788
+ `
789
+ const calls = plugins.map(p => ` ${p.register}.register(in: engine)\n`).join("")
790
+ const body = identity + calls
791
+ return `//
792
+ // LeCodesRuntime.swift
793
+ // GENERATED by lecodes — regenerated by \`lecodes app sync\`; do not edit.
794
+ //
795
+ // Re-exports the SDK (so app code needs only \`import LeCodesRuntime\`) and registers
796
+ // every plugin installed in app.json.
797
+ //
798
+
799
+ @_exported import LeCodesSDK
800
+ ${imports}
801
+ public func registerBundledPlugins(in engine: LeCodesEngine) {
802
+ ${body === "" ? "\n" : body}}
803
+ `
804
+ }
805
+
806
+ /** ios/README.md — orientation for whoever opens the shell: what this folder is, who owns
807
+ * which files, and the daily workflow. CLI-owned: init writes it, sync regenerates it. */
808
+ export const IOS_README = `# ios/ — native Xcode shell
809
+
810
+ Generated by \`lecodes app init ios\` for the LeCodes project one folder up. The app runs the
811
+ compiled bundle in \`App/Resources/app.js\` — fully offline, nothing fetched at launch. This
812
+ folder is ignored by the platform (\`ios/\` in \`.lecodesignore\`) and is never pushed to LeCodes.
813
+
814
+ ## Daily workflow
815
+
816
+ Edit the app's code at the project root (\`main.ts\` …), then:
817
+
818
+ \`\`\`
819
+ lecodes app sync # recompile app.js + assets into the shell, vendor plugins
820
+ lecodes app open # open App.xcodeproj in Xcode — build & run from there
821
+ \`\`\`
822
+
823
+ \`lecodes app doctor\` preflights builds; \`lecodes app build ios\` archives and exports an
824
+ .ipa; \`lecodes app upload ios\` sends the archive to App Store Connect.
825
+
826
+ ## SDK version
827
+
828
+ \`../app.json\` \`ios.sdk\` is the exact \`lecodes-ios-sdk\` release this shell links —
829
+ \`lecodes app init\` pinned the newest one at the time, so builds stay reproducible.
830
+
831
+ \`\`\`
832
+ lecodes app update # → newest SDK release + newest plugin tags, then sync
833
+ lecodes app update --check # what's available, writes nothing
834
+ lecodes app update --sdk 1.3.0
835
+ \`\`\`
836
+
837
+ Set \`"sdk": "latest"\` instead of a version to re-resolve on every sync (needs network;
838
+ offline it keeps whatever the shell already links). The pin is exact rather than a range
839
+ because the CLI also downloads that release's filament resources bundle into
840
+ \`App/Resources/\` — the two halves of one SDK must not drift apart.
841
+
842
+ ## Who owns what
843
+
844
+ Yours — \`lecodes app sync\` never touches these:
845
+
846
+ - \`App/*.swift\` (AppDelegate, SceneDelegate, ViewController) — customize freely, add files.
847
+ App-local native code goes here too: add a Swift file that calls
848
+ \`engine.registerCallback\` / \`registerService\`, and invoke it from ViewController.
849
+ - \`App/Assets.xcassets/\` — the app icon. Easy path: set \`app.json\` \`"icon"\` to a square
850
+ ≥1024px image and every sync renders AppIcon.png from it (flattened over \`"iconBackground"\`,
851
+ default white — App Store icons must be opaque). Or leave the key unset and drop a
852
+ 1024×1024 PNG into AppIcon.appiconset/ yourself — sync never touches it then.
853
+ - \`App/Base.lproj/LaunchScreen.storyboard\`
854
+
855
+ CLI-owned — regenerated on every sync, hand edits are overwritten:
856
+
857
+ - \`App/Resources/\` — the compiled app.js + every asset it references (fonts included), so
858
+ the app runs fully offline. Globs in app.json \`"resources": {"remote": [...]}\` opt heavy
859
+ assets (videos, big models) back into streaming from the server instead.
860
+ - \`App/Info.plist\` — template + the installed plugins' usage keys
861
+ - \`App/App.entitlements\` — the capabilities the installed plugins declare (e.g. push)
862
+ - \`LeCodesRuntime/\` — local Swift package: the LeCodesSDK dependency, vendored plugin
863
+ sources (Sources/<Module>/), and the registerBundledPlugins entry point
864
+ - the version values inside \`App.xcodeproj/project.pbxproj\` (from app.json)
865
+ - this README
866
+
867
+ ## SDK variant
868
+
869
+ Sync links the smallest SDK product that covers what the compiled bundle actually uses:
870
+ \`core\` (UI only), \`2d\` (+ 2D engine with physics), \`3d\` (+ 3D/AR engine with physics,
871
+ ~17 MB heavier), or \`full\` (everything). Adding your first 3D scene or 2D game to the
872
+ project switches it automatically on the next sync; set \`ios.variant\` in \`../app.json\`
873
+ to pin one instead.
874
+
875
+ ## Over-the-air updates
876
+
877
+ With \`"update": true\` in \`../app.json\` (server-backed projects; or
878
+ \`"update": {"url": "https://…/app.js"}\` for any static hosting), the app checks that URL
879
+ at launch — natively, before any JS runs, so a broken bundle can never block its own fix.
880
+ Unchanged code costs a ~0-byte 304; new code downloads in the background and runs on the
881
+ NEXT launch. Offline it's a silent no-op — the app always launches from a local bundle.
882
+ An App Store update always beats a cached OTA bundle. JS only: adding/removing native
883
+ plugins still needs a store release.
884
+
885
+ Shipping an update is just \`lecodes push --publish\` — no rebuild, no store review:
886
+ running apps download the new code on their next launch and run it on the launch after.
887
+
888
+ Shells generated before OTA support have a \`ViewController.swift\` that reads app.js from
889
+ \`Bundle.main\` directly, so downloaded updates would never run — and since \`App/*.swift\`
890
+ is yours, sync won't rewrite it (\`lecodes app doctor\` flags this). Replace the
891
+ \`Bundle.main\` load with:
892
+
893
+ \`\`\`swift
894
+ LeCodesUpdater.checkForUpdate()
895
+ guard let code = LeCodesUpdater.activeBundle() else {
896
+ print("LeCodes: bundled app.js is missing — run \`lecodes app sync\`")
897
+ return
898
+ }
899
+ engine.run(code)
900
+ \`\`\`
901
+
902
+ ## Config
903
+
904
+ Identity lives in \`../app.json\` (name, version, plugins, ios.bundleId / teamId / variant /
905
+ buildNumber) — edit it there, then re-run \`lecodes app sync\`. Plugins are managed with
906
+ \`lecodes app plugin add <id | github:owner/repo[@tag]>\` and pinned in \`../plugins.lock.json\`.
907
+ `