lecodes-cli 0.18.2 → 0.19.2

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