@leonxin/meetgames 0.1.14 → 0.1.15

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.
@@ -103,7 +103,14 @@ function sanitizePbxBuildSettings(proj: XcodeProject): void {
103
103
  continue;
104
104
  }
105
105
  if (Array.isArray(raw)) {
106
- settings[key] = raw.map((value) => (typeof value === "string" ? quotePbxStringIfNeeded(value) : value));
106
+ const normalized = raw.map((value) => (typeof value === "string" ? quotePbxStringIfNeeded(value) : value));
107
+ const seen = new Set<string>();
108
+ settings[key] = normalized.filter((value) => {
109
+ const marker = typeof value === "string" ? unquotePbxString(value) : JSON.stringify(value);
110
+ if (seen.has(marker)) return false;
111
+ seen.add(marker);
112
+ return true;
113
+ });
107
114
  }
108
115
  }
109
116
  }
@@ -162,12 +169,15 @@ function ensureResourcesBuildPhase(proj: XcodeProject, targetName: string): void
162
169
  export function addThirdPartyFramework(ctx: PbxContext, relPathFromSrcRoot: string, embed: boolean): void {
163
170
  const file = relPathFromSrcRoot.split(path.sep).join("/");
164
171
  const target = targetKey(ctx.proj, ctx.targetName);
165
- ctx.proj.addFramework(file, {
166
- customFramework: true,
167
- embed,
168
- sign: true,
169
- target,
170
- });
172
+ const basename = path.basename(file);
173
+ const fileRefUuid = ensureFileRef(ctx, file, frameworkFileType(file));
174
+ addBuildFileToPhase(ctx, ensureFrameworksBuildPhase(ctx, target), fileRefUuid, basename, "Frameworks", ["Weak"]);
175
+ if (embed) {
176
+ addBuildFileToPhase(ctx, ensureCopyFilesBuildPhase(ctx, target), fileRefUuid, basename, "Embed Frameworks", [
177
+ "CodeSignOnCopy",
178
+ "RemoveHeadersOnCopy",
179
+ ]);
180
+ }
171
181
  }
172
182
 
173
183
  export function addCopyFile(ctx: PbxContext, relPathFromSrcRoot: string): void {
@@ -202,13 +212,81 @@ function findFileRefUuid(ctx: PbxContext, file: string): string | null {
202
212
  return null;
203
213
  }
204
214
 
215
+ function frameworkFileType(file: string): string {
216
+ return file.endsWith(".xcframework") ? "wrapper.xcframework" : "wrapper.framework";
217
+ }
218
+
219
+ function ensureFileRef(ctx: PbxContext, file: string, lastKnownFileType: string): string {
220
+ const basename = path.basename(file);
221
+ const existing = findFileRefUuid(ctx, file);
222
+ const fileRefSection = objectSection(ctx.proj, "PBXFileReference");
223
+ if (existing) {
224
+ const ref = fileRefSection[existing];
225
+ if (ref && typeof ref === "object") {
226
+ (ref as Record<string, unknown>).lastKnownFileType = lastKnownFileType;
227
+ }
228
+ return existing;
229
+ }
230
+
231
+ const uuid = ctx.proj.generateUuid();
232
+ fileRefSection[uuid] = {
233
+ isa: "PBXFileReference",
234
+ name: `"${basename}"`,
235
+ path: `"${file}"`,
236
+ sourceTree: '"<group>"',
237
+ lastKnownFileType,
238
+ includeInIndex: 0,
239
+ };
240
+ fileRefSection[`${uuid}_comment`] = basename;
241
+ return uuid;
242
+ }
243
+
244
+ function ensureFrameworksBuildPhase(ctx: PbxContext, target: string): Record<string, unknown> {
245
+ try {
246
+ const phase = ctx.proj.pbxFrameworksBuildPhaseObj(target) as Record<string, unknown>;
247
+ phase.files = (phase.files as unknown[]) ?? [];
248
+ return phase;
249
+ } catch {
250
+ ctx.proj.addBuildPhase([], "PBXFrameworksBuildPhase", "Frameworks", target);
251
+ const phase = ctx.proj.pbxFrameworksBuildPhaseObj(target) as Record<string, unknown>;
252
+ phase.files = (phase.files as unknown[]) ?? [];
253
+ return phase;
254
+ }
255
+ }
256
+
257
+ function addBuildFileToPhase(
258
+ ctx: PbxContext,
259
+ phase: Record<string, unknown>,
260
+ fileRefUuid: string,
261
+ basename: string,
262
+ phaseName: string,
263
+ attributes?: string[]
264
+ ): void {
265
+ const files = (phase.files ??= []) as Array<{ value?: string; comment?: string }>;
266
+ if (files.some((file) => file.comment === `${basename} in ${phaseName}`)) return;
267
+
268
+ const buildUuid = ctx.proj.generateUuid();
269
+ const buildFileSection = objectSection(ctx.proj, "PBXBuildFile");
270
+ const buildFile: Record<string, unknown> = {
271
+ isa: "PBXBuildFile",
272
+ fileRef: fileRefUuid,
273
+ fileRef_comment: basename,
274
+ };
275
+ if (attributes?.length) {
276
+ buildFile.settings = { ATTRIBUTES: attributes };
277
+ }
278
+ buildFileSection[buildUuid] = buildFile;
279
+ buildFileSection[`${buildUuid}_comment`] = `${basename} in ${phaseName}`;
280
+ files.push({ value: buildUuid, comment: `${basename} in ${phaseName}` });
281
+ }
282
+
205
283
  function ensureCopyFilesBuildPhase(ctx: PbxContext, target: string): Record<string, unknown> {
206
284
  const section = objectSection(ctx.proj, "PBXCopyFilesBuildPhase");
207
285
  for (const [uuid, raw] of Object.entries(section)) {
208
286
  if (uuid.endsWith("_comment") || !raw || typeof raw !== "object") continue;
209
287
  const phase = raw as Record<string, unknown>;
210
288
  const name = String(phase.name ?? "").replace(/^"|"$/g, "");
211
- if (name === "Copy Files" || section[`${uuid}_comment`] === "Copy Files") {
289
+ if (name === "Embed Frameworks" || section[`${uuid}_comment`] === "Embed Frameworks") {
212
290
  phase.files = (phase.files as unknown[]) ?? [];
213
291
  return phase;
214
292
  }
@@ -221,15 +299,15 @@ function ensureCopyFilesBuildPhase(ctx: PbxContext, target: string): Record<stri
221
299
  dstPath: '""',
222
300
  dstSubfolderSpec: 10,
223
301
  files: [],
224
- name: '"Copy Files"',
302
+ name: '"Embed Frameworks"',
225
303
  runOnlyForDeploymentPostprocessing: 0,
226
304
  };
227
305
  section[uuid] = phase;
228
- section[`${uuid}_comment`] = "Copy Files";
306
+ section[`${uuid}_comment`] = "Embed Frameworks";
229
307
  const native = ctx.proj.pbxNativeTargetSection?.()[target] as { buildPhases?: Array<{ value: string; comment: string }> };
230
308
  native.buildPhases ??= [];
231
309
  if (!native.buildPhases.some((p) => p.value === uuid)) {
232
- native.buildPhases.push({ value: uuid, comment: "Copy Files" });
310
+ native.buildPhases.push({ value: uuid, comment: "Embed Frameworks" });
233
311
  }
234
312
  return phase;
235
313
  }
@@ -289,9 +367,13 @@ export function addSourceOrResourceFile(ctx: PbxContext, relPathFromSrcRoot: str
289
367
  const target = targetKey(ctx.proj, ctx.targetName);
290
368
  if (file.endsWith(".h")) {
291
369
  ctx.proj.addHeaderFile(file, { target });
292
- } else if (file.endsWith(".m") || file.endsWith(".mm") || file.endsWith(".swift")) {
370
+ } else if (file.endsWith(".m") || file.endsWith(".mm") || file.endsWith(".swift") || file.endsWith(".xcdatamodeld")) {
293
371
  try {
294
- ctx.proj.addSourceFile(file, { target });
372
+ if (file.endsWith(".xcdatamodeld")) {
373
+ addSourceFileManually(ctx, file, "wrapper.xcdatamodel");
374
+ } else {
375
+ ctx.proj.addSourceFile(file, { target });
376
+ }
295
377
  } catch (e) {
296
378
  if (!String(e instanceof Error ? e.message : e).includes("path")) throw e;
297
379
  addSourceFileManually(ctx, file);
@@ -327,7 +409,7 @@ function sourceFileType(file: string): string {
327
409
  return "sourcecode.c.objc";
328
410
  }
329
411
 
330
- function addSourceFileManually(ctx: PbxContext, file: string): void {
412
+ function addSourceFileManually(ctx: PbxContext, file: string, lastKnownFileType?: string): void {
331
413
  const basename = path.basename(file);
332
414
  ensureSourcesBuildPhase(ctx.proj, ctx.targetName);
333
415
  const sources = ctx.proj.pbxSourcesBuildPhaseObj(targetKey(ctx.proj, ctx.targetName));
@@ -344,7 +426,7 @@ function addSourceFileManually(ctx: PbxContext, file: string): void {
344
426
  name: `"${basename}"`,
345
427
  path: `"${file}"`,
346
428
  sourceTree: '"<group>"',
347
- lastKnownFileType: sourceFileType(file),
429
+ lastKnownFileType: lastKnownFileType ?? sourceFileType(file),
348
430
  };
349
431
  fileRefSection[`${fileRefUuid}_comment`] = basename;
350
432
  buildFileSection[buildUuid] = {
@@ -394,7 +476,7 @@ function addResourceFileManually(ctx: PbxContext, file: string, lastKnownFileTyp
394
476
  files.push({ value: buildUuid, comment: `${basename} in Resources` });
395
477
  }
396
478
 
397
- export function setBuildSetting(ctx: PbxContext, key: string, value: string): void {
479
+ export function setBuildSetting(ctx: PbxContext, key: string, value: unknown): void {
398
480
  const uuid = targetUuid(ctx.proj, ctx.targetName);
399
481
  if (!uuid) return;
400
482
  const native = ctx.proj.pbxNativeTargetSection?.()[uuid] as { buildConfigurationList?: string };
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  /** Tracks text file originals and pending content for dry-run / apply */
5
5
  export class TextFileStore {
6
6
  private readonly projectRoot: string;
7
- private readonly map = new Map<string, { original: string; current: string }>();
7
+ private readonly map = new Map<string, { original: string; current: string; existed: boolean }>();
8
8
 
9
9
  constructor(projectRoot: string) {
10
10
  this.projectRoot = projectRoot;
@@ -20,7 +20,7 @@ export class TextFileStore {
20
20
  const abs = path.join(this.projectRoot, rel);
21
21
  if (!fs.existsSync(abs)) return "";
22
22
  const content = fs.readFileSync(abs, "utf8");
23
- this.map.set(rel, { original: content, current: content });
23
+ this.map.set(rel, { original: content, current: content, existed: true });
24
24
  return content;
25
25
  }
26
26
 
@@ -32,22 +32,23 @@ export class TextFileStore {
32
32
  return;
33
33
  }
34
34
  let original = "";
35
- if (fs.existsSync(abs)) original = fs.readFileSync(abs, "utf8");
36
- this.map.set(rel, { original, current: content });
35
+ const existed = fs.existsSync(abs);
36
+ if (existed) original = fs.readFileSync(abs, "utf8");
37
+ this.map.set(rel, { original, current: content, existed });
37
38
  }
38
39
 
39
40
  /** Files where current !== original */
40
41
  changedEntries(): Array<{ rel: string; original: string; current: string }> {
41
42
  const out: Array<{ rel: string; original: string; current: string }> = [];
42
43
  for (const [rel, v] of this.map) {
43
- if (v.current !== v.original) out.push({ rel, original: v.original, current: v.current });
44
+ if (!v.existed || v.current !== v.original) out.push({ rel, original: v.original, current: v.current });
44
45
  }
45
46
  return out;
46
47
  }
47
48
 
48
49
  flushToDisk(): void {
49
50
  for (const [rel, v] of this.map) {
50
- if (v.current === v.original) continue;
51
+ if (v.existed && v.current === v.original) continue;
51
52
  const abs = path.join(this.projectRoot, rel);
52
53
  fs.mkdirSync(path.dirname(abs), { recursive: true });
53
54
  fs.writeFileSync(abs, v.current, "utf8");
@@ -53,7 +53,7 @@ describe("meetSdkRemoteConfig", () => {
53
53
  naver: { clientId: "naver-client", secret: "naver-secret", name: "Naver", scheme: "naver-scheme" },
54
54
  apple: {},
55
55
  },
56
- payment: {},
56
+ payment: { appleIap: {} },
57
57
  analytics: {
58
58
  appsflyer: { devKey: "af-key", appleAppId: "1234567890" },
59
59
  firebase: { firebaseUrl: "https://cdn.example/GoogleService-Info.plist", firebaseName: "GoogleService-Info.plist" },
@@ -82,6 +82,7 @@ describe("meetSdkRemoteConfig", () => {
82
82
  devKey: "af-key",
83
83
  appleAppId: "1234567890",
84
84
  });
85
+ expect(parsed!.sdkModules.payment.appleIap).toMatchObject({});
85
86
  });
86
87
 
87
88
  it("does not treat legacy remote field names as valid config", () => {
@@ -50,7 +50,7 @@ function writeNativeSampleRemoteConfig(projectRoot: string): void {
50
50
  apple: {},
51
51
  },
52
52
  payment: {
53
- googleIap: {},
53
+ appleIap: {},
54
54
  },
55
55
  analytics: {
56
56
  appsflyer: {
@@ -156,11 +156,19 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
156
156
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPCore.framework"))).toBe(true);
157
157
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPSDK.framework"))).toBe(true);
158
158
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPGuestSigninPlugin.framework"))).toBe(true);
159
+ expect(fs.existsSync(path.join(tmp, "topSDK", "TOPIAPPayPlugin.framework"))).toBe(true);
160
+ expect(fs.readFileSync(path.join(tmp, "topSDK", "TopSDKInstall.swift"), "utf8")).toBe("");
159
161
  expect(fs.existsSync(path.join(tmp, "topSDK", "TopSDKSource.bundle"))).toBe(true);
160
162
 
161
163
  const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
162
164
  expect(pbx).toContain("TOPCore.framework");
163
165
  expect(pbx).toContain("TOPGuestSigninPlugin.framework");
166
+ expect(pbx).toContain("TOPIAPPayPlugin.framework in Frameworks");
167
+ expect(pbx).toContain("TOPCoreModel.xcdatamodeld in Sources");
168
+ expect(pbx).not.toContain("TOPCoreModel.xcdatamodeld in Resources");
169
+ expect(pbx).not.toContain("FBSDKLoginKit.xcframework in Resources");
170
+ expect(pbx).toContain("LIBRARY_SEARCH_PATHS = \"$(SRCROOT)/topSDK\"");
171
+ expect(pbx).toContain("SWIFT_VERSION = 5.0");
164
172
  expect(pbx).toContain('"-ObjC"');
165
173
  expect(pbx).toContain("libc++.tbd");
166
174
  expect(pbx).not.toMatch(/\n\s+-ObjC,/);
@@ -177,6 +185,8 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios fixture", () => {
177
185
  expect(plist).toContain("<key>APP_ID</key>");
178
186
  expect(plist).toContain("mock-ios-native-sample-app-id");
179
187
  expect(plist).toContain("GuestSignin");
188
+ expect(plist).toContain("IAPPay");
189
+ expect(plist).not.toContain("NSAppTransportSecurity");
180
190
  } finally {
181
191
  fs.rmSync(tmp, { recursive: true, force: true });
182
192
  }
@@ -548,7 +558,7 @@ describe.skipIf(!hasIosProjectFixture)("pipeline ios native-sample parity", () =
548
558
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPGoogleSigninPlugin.framework"))).toBe(true);
549
559
  expect(fs.existsSync(path.join(tmp, "topSDK", "TOPAppleSigninPlugin.framework"))).toBe(true);
550
560
  expect(fs.existsSync(path.join(tmp, "topSDK", "TopSDKSource.bundle"))).toBe(true);
551
- expect(fs.existsSync(path.join(tmp, "native-sample", "TopSDKInstall.swift"))).toBe(true);
561
+ expect(fs.readFileSync(path.join(tmp, "topSDK", "TopSDKInstall.swift"), "utf8")).toBe("");
552
562
 
553
563
  const pbx = fs.readFileSync(path.join(tmp, "native-sample.xcodeproj", "project.pbxproj"), "utf8");
554
564
  expect(pbx).toContain("TOPUIPlugin.framework");