@bitmagic/cli 0.1.83-dev.0 → 0.1.83-dev.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.
- package/README.md +70 -1
- package/assets/skills/publishing-to-steam/SKILL.md +218 -0
- package/dist/assets/vxl-summary.d.ts +6 -20
- package/dist/assets/vxl-summary.js +9 -100
- package/dist/assets/vxl-summary.js.map +1 -1
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/check.js +5 -0
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/forge.js +1 -1
- package/dist/commands/forge.js.map +1 -1
- package/dist/commands/import.js +2 -4
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.js +0 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/ios.js +88 -136
- package/dist/commands/ios.js.map +1 -1
- package/dist/commands/promote.js +2 -4
- package/dist/commands/promote.js.map +1 -1
- package/dist/commands/publish.js +0 -1
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/steam-build.d.ts +67 -0
- package/dist/commands/steam-build.js +286 -0
- package/dist/commands/steam-build.js.map +1 -0
- package/dist/commands/steam-portal.d.ts +1 -0
- package/dist/commands/steam-portal.js +319 -0
- package/dist/commands/steam-portal.js.map +1 -0
- package/dist/commands/steam-upload.d.ts +37 -0
- package/dist/commands/steam-upload.js +172 -0
- package/dist/commands/steam-upload.js.map +1 -0
- package/dist/commands/steam.d.ts +1 -0
- package/dist/commands/steam.js +502 -0
- package/dist/commands/steam.js.map +1 -0
- package/dist/commands/verify.js +6 -0
- package/dist/commands/verify.js.map +1 -1
- package/dist/editor/asset-viewer.js +5 -4
- package/dist/editor/asset-viewer.js.map +1 -1
- package/dist/editor/game-info-panel.d.ts +2 -2
- package/dist/editor/game-info-panel.js +1 -2
- package/dist/editor/game-info-panel.js.map +1 -1
- package/dist/editor/game-info.d.ts +1 -3
- package/dist/editor/game-info.js +1 -2
- package/dist/editor/game-info.js.map +1 -1
- package/dist/editor/server.js +2 -3
- package/dist/editor/server.js.map +1 -1
- package/dist/editor/shell-page.js +9 -7
- package/dist/editor/shell-page.js.map +1 -1
- package/dist/forge/browser-host.d.ts +6 -12
- package/dist/forge/browser-host.js +5 -11
- package/dist/forge/browser-host.js.map +1 -1
- package/dist/ios/native-overrides.d.ts +27 -0
- package/dist/ios/native-overrides.js +87 -0
- package/dist/ios/native-overrides.js.map +1 -0
- package/dist/ios/record-validate.d.ts +1 -33
- package/dist/ios/record-validate.js +2 -100
- package/dist/ios/record-validate.js.map +1 -1
- package/dist/ios/store-bridge.d.ts +26 -0
- package/dist/ios/store-bridge.js +224 -0
- package/dist/ios/store-bridge.js.map +1 -0
- package/dist/ios/storekit-config.d.ts +15 -0
- package/dist/ios/storekit-config.js +106 -0
- package/dist/ios/storekit-config.js.map +1 -0
- package/dist/ios/xcode-template.d.ts +12 -3
- package/dist/ios/xcode-template.js +158 -27
- package/dist/ios/xcode-template.js.map +1 -1
- package/dist/package/collect-urls.d.ts +1 -1
- package/dist/package/collect-urls.js +10 -2
- package/dist/package/collect-urls.js.map +1 -1
- package/dist/package/offline-payload.d.ts +35 -0
- package/dist/package/offline-payload.js +142 -0
- package/dist/package/offline-payload.js.map +1 -0
- package/dist/package/packaged-check.d.ts +39 -0
- package/dist/package/packaged-check.js +104 -0
- package/dist/package/packaged-check.js.map +1 -0
- package/dist/package/transform.d.ts +4 -4
- package/dist/package/transform.js +3 -3
- package/dist/project/context.d.ts +7 -2
- package/dist/project/context.js +1 -2
- package/dist/project/context.js.map +1 -1
- package/dist/project/published-three.d.ts +14 -0
- package/dist/project/published-three.js +93 -0
- package/dist/project/published-three.js.map +1 -0
- package/dist/publish/bundle.d.ts +1 -1
- package/dist/publish/bundle.js +8 -3
- package/dist/publish/bundle.js.map +1 -1
- package/dist/publish/platform.d.ts +1 -1
- package/dist/publish/platform.js +1 -1
- package/dist/publish/source-zip.d.ts +0 -1
- package/dist/publish/source-zip.js +1 -2
- package/dist/publish/source-zip.js.map +1 -1
- package/dist/scaffold/aliases.js +8 -5
- package/dist/scaffold/aliases.js.map +1 -1
- package/dist/scaffold/engine-download.d.ts +1 -1
- package/dist/scaffold/engine-download.js +1 -1
- package/dist/scaffold/export-zip.d.ts +0 -1
- package/dist/scaffold/export-zip.js +3 -6
- package/dist/scaffold/export-zip.js.map +1 -1
- package/dist/scaffold/import-project.js +1 -1
- package/dist/scaffold/project-export.js +0 -1
- package/dist/scaffold/project-export.js.map +1 -1
- package/dist/scaffold/project-files.d.ts +0 -1
- package/dist/scaffold/project-files.js +8 -5
- package/dist/scaffold/project-files.js.map +1 -1
- package/dist/scaffold/project.d.ts +15 -4
- package/dist/scaffold/project.js +30 -13
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/skill-files.js +3 -1
- package/dist/scaffold/skill-files.js.map +1 -1
- package/dist/scaffold/steam-skill-files.d.ts +2 -0
- package/dist/scaffold/steam-skill-files.js +7 -0
- package/dist/scaffold/steam-skill-files.js.map +1 -0
- package/dist/scaffold/suggest-template.js +0 -1
- package/dist/scaffold/suggest-template.js.map +1 -1
- package/dist/scaffold/unity-skill-files.js +1 -1
- package/dist/steam/art-generate.d.ts +55 -0
- package/dist/steam/art-generate.js +173 -0
- package/dist/steam/art-generate.js.map +1 -0
- package/dist/steam/art-render.d.ts +25 -0
- package/dist/steam/art-render.js +157 -0
- package/dist/steam/art-render.js.map +1 -0
- package/dist/steam/art-use.d.ts +20 -0
- package/dist/steam/art-use.js +84 -0
- package/dist/steam/art-use.js.map +1 -0
- package/dist/steam/art.d.ts +98 -0
- package/dist/steam/art.js +243 -0
- package/dist/steam/art.js.map +1 -0
- package/dist/steam/check.d.ts +21 -0
- package/dist/steam/check.js +113 -0
- package/dist/steam/check.js.map +1 -0
- package/dist/steam/config.d.ts +57 -0
- package/dist/steam/config.js +145 -0
- package/dist/steam/config.js.map +1 -0
- package/dist/steam/electron-cache.d.ts +11 -0
- package/dist/steam/electron-cache.js +105 -0
- package/dist/steam/electron-cache.js.map +1 -0
- package/dist/steam/fuses.d.ts +16 -0
- package/dist/steam/fuses.js +75 -0
- package/dist/steam/fuses.js.map +1 -0
- package/dist/steam/icon-files.d.ts +16 -0
- package/dist/steam/icon-files.js +70 -0
- package/dist/steam/icon-files.js.map +1 -0
- package/dist/steam/image-info.d.ts +16 -0
- package/dist/steam/image-info.js +89 -0
- package/dist/steam/image-info.js.map +1 -0
- package/dist/steam/launch-check.d.ts +12 -0
- package/dist/steam/launch-check.js +101 -0
- package/dist/steam/launch-check.js.map +1 -0
- package/dist/steam/media.d.ts +48 -0
- package/dist/steam/media.js +151 -0
- package/dist/steam/media.js.map +1 -0
- package/dist/steam/package-macos.d.ts +52 -0
- package/dist/steam/package-macos.js +268 -0
- package/dist/steam/package-macos.js.map +1 -0
- package/dist/steam/package-windows.d.ts +24 -0
- package/dist/steam/package-windows.js +84 -0
- package/dist/steam/package-windows.js.map +1 -0
- package/dist/steam/paths.d.ts +37 -0
- package/dist/steam/paths.js +45 -0
- package/dist/steam/paths.js.map +1 -0
- package/dist/steam/playtest-shots.d.ts +18 -0
- package/dist/steam/playtest-shots.js +83 -0
- package/dist/steam/playtest-shots.js.map +1 -0
- package/dist/steam/portal-recipes.d.ts +25 -0
- package/dist/steam/portal-recipes.js +61 -0
- package/dist/steam/portal-recipes.js.map +1 -0
- package/dist/steam/portal.d.ts +58 -0
- package/dist/steam/portal.js +340 -0
- package/dist/steam/portal.js.map +1 -0
- package/dist/steam/raster.d.ts +59 -0
- package/dist/steam/raster.js +285 -0
- package/dist/steam/raster.js.map +1 -0
- package/dist/steam/shell-files.d.ts +17 -0
- package/dist/steam/shell-files.js +268 -0
- package/dist/steam/shell-files.js.map +1 -0
- package/dist/steam/specs.d.ts +61 -0
- package/dist/steam/specs.js +103 -0
- package/dist/steam/specs.js.map +1 -0
- package/dist/steam/steampipe.d.ts +46 -0
- package/dist/steam/steampipe.js +170 -0
- package/dist/steam/steampipe.js.map +1 -0
- package/dist/steam/store-copy.d.ts +89 -0
- package/dist/steam/store-copy.js +221 -0
- package/dist/steam/store-copy.js.map +1 -0
- package/dist/steam/tags.d.ts +9 -0
- package/dist/steam/tags.js +49 -0
- package/dist/steam/tags.js.map +1 -0
- package/dist/steam/trailer.d.ts +34 -0
- package/dist/steam/trailer.js +141 -0
- package/dist/steam/trailer.js.map +1 -0
- package/dist/verify/browser.d.ts +11 -0
- package/dist/verify/browser.js +195 -152
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/classify.d.ts +11 -2
- package/dist/verify/classify.js +16 -4
- package/dist/verify/classify.js.map +1 -1
- package/dist/verify/mobile-parity.d.ts +1 -1
- package/dist/verify/mobile-parity.js +1 -1
- package/package.json +5 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser-host.js","sourceRoot":"","sources":["../../src/forge/browser-host.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAkC,MAAM,iBAAiB,CAAC;AAE3E,OAAO,EAAE,aAAa,EAAiB,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EACL,wBAAwB,GAIzB,MAAM,gBAAgB,CAAC;AAExB,kFAAkF;AAClF,MAAM,cAAc,GAAG,yBAAyB,CAAC;AAEjD,2FAA2F;AAC3F,MAAM,8BAA8B,GAAG,OAAO,CAAC;AAE/C;;;;;GAKG;AACH,MAAM,iCAAiC,GAAG,MAAM,CAAC;AAoBjD;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB,EAAE,MAAc;IAC3D,OAAO,oBAAoB,QAAQ,2BAA2B,kBAAkB,CAAC,MAAM,CAAC,EAAE,CAAC;AAC7F,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAuC,EACvC,QAAmB;IAEnB,OAAO,CAAC,wBAAwB,EAAE,GAAG,aAAa,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,gFAAgF;AAChF,SAAS,YAAY,CAAC,IAAY,EAAE,IAAY,EAAE,GAAW;IAC3D,8FAA8F;IAC9F,8FAA8F;IAC9F,gFAAgF;IAChF,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,cAAc,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;IAC3D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,QAAQ,CAChB,MAAM,IAAI,OAAO,IAAI,wBAAwB,GAAG,6CAA6C,CAC9F,CAAC;IACJ,CAAC;IACD,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAAC,GAAG,IAAI,mDAAmD,CAAC,CAAC;IACjF,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC3E,MAAM,IAAI,QAAQ,CAAC,GAAG,IAAI,8BAA8B,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,MAAiC,CAAC;AAC3C,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"browser-host.js","sourceRoot":"","sources":["../../src/forge/browser-host.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export declare const CUSTOM_GROUP_DIR = "Custom";
|
|
2
|
+
export declare const STOREKIT_FILE_NAME = "StoreKit.storekit";
|
|
3
|
+
export interface NativeOverrides {
|
|
4
|
+
/** Paths relative to `ios/Sources/`, forward-slashed and sorted, so the pbxproj is stable. */
|
|
5
|
+
swiftFiles: string[];
|
|
6
|
+
/** Class names found adopting `BitmagicNativeExtension`, in file order. */
|
|
7
|
+
extensionClasses: string[];
|
|
8
|
+
/** Whether `ios/StoreKit.storekit` exists. */
|
|
9
|
+
hasStoreKitConfig: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface NativePaths {
|
|
12
|
+
/** `ios/` at the project root. */
|
|
13
|
+
nativeDir: string;
|
|
14
|
+
/** `ios/Sources/` — creator Swift. */
|
|
15
|
+
sourcesDir: string;
|
|
16
|
+
/** `ios/StoreKit.storekit`. */
|
|
17
|
+
storeKitPath: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function nativePaths(root: string): NativePaths;
|
|
20
|
+
export declare function findExtensionClasses(swiftSource: string): string[];
|
|
21
|
+
/** What the project's `ios/` folder holds. A project without one gets the plain shell. */
|
|
22
|
+
export declare function readNativeOverrides(root: string): NativeOverrides;
|
|
23
|
+
/**
|
|
24
|
+
* Copy the creator's files into the generated project. `Custom/` is replaced wholesale, so a
|
|
25
|
+
* Swift file deleted from `ios/Sources/` leaves the app too.
|
|
26
|
+
*/
|
|
27
|
+
export declare function copyNativeOverrides(root: string, projectDir: string, overrides: NativeOverrides): void;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The creator-owned half of the iOS app (§13 of docs/ios-export-design.md): the project's `ios/`
|
|
3
|
+
* folder. Everything under `.bitmagic/ios/` is regenerated on every build, so native code a
|
|
4
|
+
* creator writes lives here instead — in the project, committed with the game — and each build
|
|
5
|
+
* copies it into the generated project:
|
|
6
|
+
*
|
|
7
|
+
* ios/Sources/ every .swift file, at any depth, is compiled into the app; a class
|
|
8
|
+
* adopting `BitmagicNativeExtension` is installed into the game view
|
|
9
|
+
* (see NATIVE_EXTENSION_SWIFT in xcode-template.ts)
|
|
10
|
+
* ios/StoreKit.storekit Xcode's local StoreKit test catalogue, wired into the scheme's Run
|
|
11
|
+
* action so purchases work in the Simulator with no App Store account
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
import { CliError } from '../errors.js';
|
|
16
|
+
export const CUSTOM_GROUP_DIR = 'Custom';
|
|
17
|
+
export const STOREKIT_FILE_NAME = 'StoreKit.storekit';
|
|
18
|
+
export function nativePaths(root) {
|
|
19
|
+
const nativeDir = path.join(root, 'ios');
|
|
20
|
+
return {
|
|
21
|
+
nativeDir,
|
|
22
|
+
sourcesDir: path.join(nativeDir, 'Sources'),
|
|
23
|
+
storeKitPath: path.join(nativeDir, STOREKIT_FILE_NAME),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function listSwift(dir, base, out) {
|
|
27
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
28
|
+
const full = path.join(dir, entry.name);
|
|
29
|
+
if (entry.isDirectory())
|
|
30
|
+
listSwift(full, base, out);
|
|
31
|
+
else if (entry.isFile() && entry.name.endsWith('.swift')) {
|
|
32
|
+
out.push(path.relative(base, full).split(path.sep).join('/'));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A class that adopts the protocol, as declared: `class Name: BitmagicNativeExtension`, or with
|
|
38
|
+
* a superclass or further protocols in the list. Comments are stripped first so an example in a
|
|
39
|
+
* comment is not installed. A struct cannot adopt it (the protocol is class-bound).
|
|
40
|
+
*/
|
|
41
|
+
const EXTENSION_CLASS = /\bclass\s+([A-Za-z_][A-Za-z0-9_]*)\s*:\s*[^{]*\bBitmagicNativeExtension\b/g;
|
|
42
|
+
export function findExtensionClasses(swiftSource) {
|
|
43
|
+
const code = swiftSource.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/[^\n]*/g, '');
|
|
44
|
+
return [...code.matchAll(EXTENSION_CLASS)].map((match) => match[1]);
|
|
45
|
+
}
|
|
46
|
+
/** What the project's `ios/` folder holds. A project without one gets the plain shell. */
|
|
47
|
+
export function readNativeOverrides(root) {
|
|
48
|
+
const paths = nativePaths(root);
|
|
49
|
+
const swiftFiles = [];
|
|
50
|
+
if (fs.existsSync(paths.sourcesDir))
|
|
51
|
+
listSwift(paths.sourcesDir, paths.sourcesDir, swiftFiles);
|
|
52
|
+
swiftFiles.sort();
|
|
53
|
+
// The names go into the pbxproj as quoted strings; refuse what would need escaping there.
|
|
54
|
+
const unsafe = swiftFiles.find((rel) => /["\\\n]/.test(rel));
|
|
55
|
+
if (unsafe !== undefined) {
|
|
56
|
+
throw new CliError(`Rename ios/Sources/${unsafe}: a file name may not contain quotes or backslashes.`, 3);
|
|
57
|
+
}
|
|
58
|
+
const extensionClasses = [];
|
|
59
|
+
for (const rel of swiftFiles) {
|
|
60
|
+
extensionClasses.push(...findExtensionClasses(fs.readFileSync(path.join(paths.sourcesDir, rel), 'utf-8')));
|
|
61
|
+
}
|
|
62
|
+
const duplicate = extensionClasses.find((name, i) => extensionClasses.indexOf(name) !== i);
|
|
63
|
+
if (duplicate !== undefined) {
|
|
64
|
+
throw new CliError(`ios/Sources declares the class ${duplicate} twice.`, 3);
|
|
65
|
+
}
|
|
66
|
+
return { swiftFiles, extensionClasses, hasStoreKitConfig: fs.existsSync(paths.storeKitPath) };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Copy the creator's files into the generated project. `Custom/` is replaced wholesale, so a
|
|
70
|
+
* Swift file deleted from `ios/Sources/` leaves the app too.
|
|
71
|
+
*/
|
|
72
|
+
export function copyNativeOverrides(root, projectDir, overrides) {
|
|
73
|
+
const paths = nativePaths(root);
|
|
74
|
+
const customDir = path.join(projectDir, CUSTOM_GROUP_DIR);
|
|
75
|
+
fs.rmSync(customDir, { recursive: true, force: true });
|
|
76
|
+
for (const rel of overrides.swiftFiles) {
|
|
77
|
+
const target = path.join(customDir, ...rel.split('/'));
|
|
78
|
+
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
79
|
+
fs.copyFileSync(path.join(paths.sourcesDir, ...rel.split('/')), target);
|
|
80
|
+
}
|
|
81
|
+
const storeKitTarget = path.join(projectDir, STOREKIT_FILE_NAME);
|
|
82
|
+
if (overrides.hasStoreKitConfig)
|
|
83
|
+
fs.copyFileSync(paths.storeKitPath, storeKitTarget);
|
|
84
|
+
else
|
|
85
|
+
fs.rmSync(storeKitTarget, { force: true });
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=native-overrides.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-overrides.js","sourceRoot":"","sources":["../../src/ios/native-overrides.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,33 +1 @@
|
|
|
1
|
-
|
|
2
|
-
import { type VerifyResult } from '../verify/classify.js';
|
|
3
|
-
import type { MobileOrientation } from '../project/platform-declaration.js';
|
|
4
|
-
/** Long enough for a forged level's worth of local loads; adaptive settle exits earlier. */
|
|
5
|
-
export declare const PACKAGED_CHECK_SETTLE_MS = 25000;
|
|
6
|
-
/**
|
|
7
|
-
* Online-only endpoints the packaged game may talk to when a network exists, and must live
|
|
8
|
-
* without when one does not. Never vendored, aborted during both passes.
|
|
9
|
-
*/
|
|
10
|
-
export declare function isOnlineOnlyUrl(url: string): boolean;
|
|
11
|
-
export interface PackagedCheckOptions {
|
|
12
|
-
payloadDir: string;
|
|
13
|
-
/** Where console.log / screenshot.png land — one subdirectory per pass. */
|
|
14
|
-
artifactDir: string;
|
|
15
|
-
mode: 'record' | 'offline';
|
|
16
|
-
/**
|
|
17
|
-
* The renderer this pass boots, pinned with `?renderer=`. A device on iOS 26 or later runs
|
|
18
|
-
* WebGPU; older iOS and the Simulator run the classic WebGL renderer. For WebGPU the run also
|
|
19
|
-
* probes the backend, so a headless Chrome that silently fell back to WebGL2 fails the pass
|
|
20
|
-
* instead of passing it on the wrong renderer.
|
|
21
|
-
*/
|
|
22
|
-
renderer: RendererChoice;
|
|
23
|
-
orientation?: MobileOrientation;
|
|
24
|
-
settleMs?: number;
|
|
25
|
-
}
|
|
26
|
-
export interface PackagedCheckOutcome {
|
|
27
|
-
result: VerifyResult;
|
|
28
|
-
/** Remote, non-online-only URLs the game requested — the vendoring gap. */
|
|
29
|
-
misses: string[];
|
|
30
|
-
consolePath: string;
|
|
31
|
-
screenshotPath: string;
|
|
32
|
-
}
|
|
33
|
-
export declare function runPackagedCheck(options: PackagedCheckOptions): Promise<PackagedCheckOutcome>;
|
|
1
|
+
export * from '../package/packaged-check.js';
|
|
@@ -1,101 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
*
|
|
3
|
-
* headless system Chrome as an iPhone, `?platform=mobile` — in two modes, once per renderer a
|
|
4
|
-
* device can boot (`ios build` runs each mode on WebGPU and on WebGL).
|
|
5
|
-
*
|
|
6
|
-
* - `record`: remote requests are allowed through and every one on a non-denylisted host is
|
|
7
|
-
* reported as a miss for the caller to vendor. This is the empirical net under the static
|
|
8
|
-
* collector: whatever the running game actually fetches, however the URL was constructed.
|
|
9
|
-
* - `offline`: every remote request is aborted. The run must still pass `classifyVerifyRun`
|
|
10
|
-
* (the same "did the game render" verdict verify and the publish smoke check use) with zero
|
|
11
|
-
* remote asset requests — that is the design's acceptance criterion for "no internet needed".
|
|
12
|
-
*
|
|
13
|
-
* Denylisted endpoints are the online-only features the engine already degrades without
|
|
14
|
-
* (game-server, play-auth, play sessions, fonts, analytics) — aborted in BOTH passes so the
|
|
15
|
-
* record run observes the same world the device will.
|
|
16
|
-
*/
|
|
17
|
-
import * as fs from 'fs';
|
|
18
|
-
import * as path from 'path';
|
|
19
|
-
import { browserLaunchPlan } from '../browser-gl.js';
|
|
20
|
-
import { runHeadlessCheck } from '../verify/browser.js';
|
|
21
|
-
import { DEFAULT_START_INTERACTION } from '../verify/start.js';
|
|
22
|
-
import { classifyVerifyRun } from '../verify/classify.js';
|
|
23
|
-
import { mobileContextOptions } from '../verify/device.js';
|
|
24
|
-
import { startPayloadServer } from './local-server.js';
|
|
25
|
-
/** Long enough for a forged level's worth of local loads; adaptive settle exits earlier. */
|
|
26
|
-
export const PACKAGED_CHECK_SETTLE_MS = 25_000;
|
|
27
|
-
/**
|
|
28
|
-
* Online-only endpoints the packaged game may talk to when a network exists, and must live
|
|
29
|
-
* without when one does not. Never vendored, aborted during both passes.
|
|
30
|
-
*/
|
|
31
|
-
export function isOnlineOnlyUrl(url) {
|
|
32
|
-
let parsed;
|
|
33
|
-
try {
|
|
34
|
-
parsed = new URL(url);
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
const host = parsed.hostname;
|
|
40
|
-
return (host.includes('game-server')
|
|
41
|
-
|| parsed.pathname.endsWith('/play-auth.html')
|
|
42
|
-
|| host === 'fonts.googleapis.com'
|
|
43
|
-
|| host === 'fonts.gstatic.com'
|
|
44
|
-
|| host === 'esm.sh'
|
|
45
|
-
|| host === 'esm.run'
|
|
46
|
-
|| host.endsWith('.sentry.io')
|
|
47
|
-
|| host.endsWith('google-analytics.com')
|
|
48
|
-
|| host.endsWith('googletagmanager.com')
|
|
49
|
-
// Local dev services (api-server 3002, game-server 1666, agent 4111, lid relay 8765) —
|
|
50
|
-
// NOT the payload server, whose exact origin is allowed before this predicate runs.
|
|
51
|
-
|| host === 'localhost'
|
|
52
|
-
|| host === '127.0.0.1');
|
|
53
|
-
}
|
|
54
|
-
export async function runPackagedCheck(options) {
|
|
55
|
-
fs.mkdirSync(options.artifactDir, { recursive: true });
|
|
56
|
-
const consolePath = path.join(options.artifactDir, 'console.log');
|
|
57
|
-
const screenshotPath = path.join(options.artifactDir, 'screenshot.png');
|
|
58
|
-
const server = await startPayloadServer(options.payloadDir);
|
|
59
|
-
const misses = new Set();
|
|
60
|
-
const routes = async (context) => {
|
|
61
|
-
await context.route('**/*', (route) => {
|
|
62
|
-
const url = route.request().url();
|
|
63
|
-
if (url.startsWith(server.origin)) {
|
|
64
|
-
void route.continue();
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
if (isOnlineOnlyUrl(url)) {
|
|
68
|
-
void route.abort('internetdisconnected');
|
|
69
|
-
return;
|
|
70
|
-
}
|
|
71
|
-
misses.add(url);
|
|
72
|
-
if (options.mode === 'record')
|
|
73
|
-
void route.continue();
|
|
74
|
-
else
|
|
75
|
-
void route.abort('internetdisconnected');
|
|
76
|
-
});
|
|
77
|
-
};
|
|
78
|
-
try {
|
|
79
|
-
const observations = await runHeadlessCheck({
|
|
80
|
-
url: `${server.origin}/index.html?platform=mobile&renderer=${options.renderer}`,
|
|
81
|
-
screenshotPath,
|
|
82
|
-
consolePath,
|
|
83
|
-
settleMs: options.settleMs ?? PACKAGED_CHECK_SETTLE_MS,
|
|
84
|
-
startInteraction: DEFAULT_START_INTERACTION,
|
|
85
|
-
launch: browserLaunchPlan(process.env, options.renderer),
|
|
86
|
-
requestedRenderer: options.renderer,
|
|
87
|
-
device: mobileContextOptions(options.orientation ?? 'portrait'),
|
|
88
|
-
routes,
|
|
89
|
-
});
|
|
90
|
-
return {
|
|
91
|
-
result: classifyVerifyRun(observations),
|
|
92
|
-
misses: [...misses],
|
|
93
|
-
consolePath,
|
|
94
|
-
screenshotPath,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
finally {
|
|
98
|
-
await server.close();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
1
|
+
// Compatibility entry point for the iOS packager.
|
|
2
|
+
export * from '../package/packaged-check.js';
|
|
101
3
|
//# sourceMappingURL=record-validate.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"record-validate.js","sourceRoot":"","sources":["../../src/ios/record-validate.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"record-validate.js","sourceRoot":"","sources":["../../src/ios/record-validate.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAClD,cAAc,8BAA8B,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell's App Store bridge (§13 of docs/ios-export-design.md): StoreKit 2 behind one
|
|
3
|
+
* `WKScriptMessageHandlerWithReply`, so the page's `postMessage` returns a promise and no request
|
|
4
|
+
* ids are needed. The engine side is `game/src/engine/store/NativeStoreBackend.ts`; the message
|
|
5
|
+
* shapes below are the contract between them, so change both or neither.
|
|
6
|
+
*
|
|
7
|
+
* Ops (page → app, each answered once):
|
|
8
|
+
* status { canMakePayments }
|
|
9
|
+
* products { ids } → { products, missing }
|
|
10
|
+
* purchase { productId, quantity?, appAccountToken? }
|
|
11
|
+
* → { status: purchased | pending | cancelled | unverified | failed, transaction?, reason? }
|
|
12
|
+
* unfinished {} → { transactions }
|
|
13
|
+
* finish { transactionId } → { finished }
|
|
14
|
+
* Event (app → page): a `bmstore:transaction` window event whenever StoreKit delivers a purchase
|
|
15
|
+
* outside a purchase() call. It carries nothing; the page answers it by asking for `unfinished`,
|
|
16
|
+
* so a page that was not listening yet loses nothing.
|
|
17
|
+
*
|
|
18
|
+
* A verified purchase is never finished here. The engine records the credit first and then asks
|
|
19
|
+
* for `finish`; until then StoreKit keeps re-delivering the transaction, which is what makes an
|
|
20
|
+
* app killed between payment and crediting safe.
|
|
21
|
+
*
|
|
22
|
+
* Same literal rules as xcode-template.ts: no Swift `\(...)` interpolation, no backticks.
|
|
23
|
+
*/
|
|
24
|
+
export declare const STORE_HANDLER_NAME = "bmStore";
|
|
25
|
+
export declare const STORE_TRANSACTION_EVENT = "bmstore:transaction";
|
|
26
|
+
export declare const STORE_BRIDGE_SWIFT = "import Foundation\nimport StoreKit\nimport WebKit\n\n/// App Store purchases for the game page. See the ops table in the CLI's store-bridge.ts.\nfinal class StoreBridge: NSObject, WKScriptMessageHandlerWithReply {\n static let handlerName = \"bmStore\"\n static let transactionEvent = \"bmstore:transaction\"\n\n weak var webView: WKWebView?\n private var updatesTask: Task<Void, Never>?\n /// Verified, not yet finished transactions, by id \u2014 so finish never has to search.\n private var pending: [UInt64: Transaction] = [:]\n\n deinit {\n updatesTask?.cancel()\n }\n\n /// Listen for purchases that complete outside a purchase() call: Ask to Buy approvals,\n /// payment-method fixes, a purchase interrupted by the app being killed. Apple's guidance is\n /// to start this at launch, before any purchase, or such transactions can be missed.\n @MainActor\n func start() {\n updatesTask = Task { [weak self] in\n for await result in Transaction.updates {\n self?.received(result)\n }\n }\n }\n\n @MainActor\n private func received(_ result: VerificationResult<Transaction>) {\n guard case .verified(let transaction) = result else { return }\n pending[transaction.id] = transaction\n let script = \"window.dispatchEvent(new CustomEvent('\" + Self.transactionEvent + \"'))\"\n webView?.evaluateJavaScript(script, completionHandler: nil)\n }\n\n func userContentController(\n _ userContentController: WKUserContentController,\n didReceive message: WKScriptMessage,\n replyHandler: @escaping (Any?, String?) -> Void\n ) {\n guard let body = message.body as? [String: Any], let op = body[\"op\"] as? String else {\n replyHandler(nil, \"A store request needs an op.\")\n return\n }\n Task { @MainActor in\n do {\n let reply = try await self.handle(op: op, body: body)\n replyHandler(reply, nil)\n } catch {\n replyHandler(nil, String(describing: error))\n }\n }\n }\n\n @MainActor\n private func handle(op: String, body: [String: Any]) async throws -> Any {\n switch op {\n case \"status\":\n return [\"canMakePayments\": AppStore.canMakePayments]\n case \"products\":\n let ids = (body[\"ids\"] as? [String]) ?? []\n let products = try await Product.products(for: ids)\n let found = Set(products.map { $0.id })\n return [\n \"products\": products.map { Self.productInfo($0) },\n \"missing\": ids.filter { !found.contains($0) },\n ]\n case \"purchase\":\n return try await purchase(body)\n case \"unfinished\":\n var transactions: [[String: Any]] = []\n for await result in Transaction.unfinished {\n guard case .verified(let transaction) = result else { continue }\n pending[transaction.id] = transaction\n transactions.append(Self.transactionInfo(transaction, jws: result.jwsRepresentation))\n }\n return [\"transactions\": transactions]\n case \"finish\":\n guard let text = body[\"transactionId\"] as? String, let id = UInt64(text) else {\n throw StoreBridgeError.badRequest(\"finish needs a transactionId string\")\n }\n return [\"finished\": await finish(id)]\n default:\n throw StoreBridgeError.badRequest(\"Unknown store op \" + op)\n }\n }\n\n @MainActor\n private func purchase(_ body: [String: Any]) async throws -> Any {\n guard let productId = body[\"productId\"] as? String else {\n throw StoreBridgeError.badRequest(\"purchase needs a productId\")\n }\n guard let product = try await Product.products(for: [productId]).first else {\n return [\n \"status\": \"failed\",\n \"reason\": \"The App Store did not return the product \" + productId + \".\",\n ]\n }\n var options: Set<Product.PurchaseOption> = []\n if let quantity = body[\"quantity\"] as? Int, quantity > 1 {\n options.insert(.quantity(quantity))\n }\n if let token = body[\"appAccountToken\"] as? String, let uuid = UUID(uuidString: token) {\n options.insert(.appAccountToken(uuid))\n }\n let result = try await product.purchase(options: options)\n switch result {\n case .success(let verification):\n switch verification {\n case .verified(let transaction):\n pending[transaction.id] = transaction\n return [\n \"status\": \"purchased\",\n \"transaction\": Self.transactionInfo(transaction, jws: verification.jwsRepresentation),\n ]\n case .unverified(_, let error):\n return [\"status\": \"unverified\", \"reason\": String(describing: error)]\n }\n case .userCancelled:\n return [\"status\": \"cancelled\"]\n case .pending:\n return [\"status\": \"pending\"]\n @unknown default:\n return [\"status\": \"failed\", \"reason\": \"StoreKit returned an unknown purchase result.\"]\n }\n }\n\n @MainActor\n private func finish(_ id: UInt64) async -> Bool {\n if let transaction = pending.removeValue(forKey: id) {\n await transaction.finish()\n return true\n }\n for await result in Transaction.unfinished {\n if case .verified(let transaction) = result, transaction.id == id {\n await transaction.finish()\n return true\n }\n }\n return false\n }\n\n private static func typeName(_ type: Product.ProductType) -> String {\n if type == .consumable { return \"consumable\" }\n if type == .nonConsumable { return \"nonConsumable\" }\n if type == .autoRenewable { return \"autoRenewable\" }\n if type == .nonRenewable { return \"nonRenewable\" }\n return \"other\"\n }\n\n private static func productInfo(_ product: Product) -> [String: Any] {\n return [\n \"id\": product.id,\n \"type\": typeName(product.type),\n \"displayName\": product.displayName,\n \"description\": product.description,\n \"displayPrice\": product.displayPrice,\n \"price\": NSDecimalNumber(decimal: product.price).doubleValue,\n \"currencyCode\": product.priceFormatStyle.currencyCode,\n ]\n }\n\n /// Ids go to the page as strings: a transaction id is a UInt64 and need not fit a double.\n private static func transactionInfo(_ transaction: Transaction, jws: String) -> [String: Any] {\n var info: [String: Any] = [\n \"transactionId\": String(transaction.id),\n \"originalTransactionId\": String(transaction.originalID),\n \"productId\": transaction.productID,\n \"productType\": typeName(transaction.productType),\n \"quantity\": transaction.purchasedQuantity,\n \"purchaseDate\": transaction.purchaseDate.timeIntervalSince1970 * 1000,\n \"revoked\": transaction.revocationDate != nil,\n \"jws\": jws,\n ]\n if #available(iOS 16.0, *) {\n info[\"environment\"] = transaction.environment.rawValue\n }\n if let token = transaction.appAccountToken {\n info[\"appAccountToken\"] = token.uuidString\n }\n return info\n }\n}\n\nenum StoreBridgeError: Error, CustomStringConvertible {\n case badRequest(String)\n\n var description: String {\n switch self {\n case .badRequest(let message):\n return message\n }\n }\n}\n";
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shell's App Store bridge (§13 of docs/ios-export-design.md): StoreKit 2 behind one
|
|
3
|
+
* `WKScriptMessageHandlerWithReply`, so the page's `postMessage` returns a promise and no request
|
|
4
|
+
* ids are needed. The engine side is `game/src/engine/store/NativeStoreBackend.ts`; the message
|
|
5
|
+
* shapes below are the contract between them, so change both or neither.
|
|
6
|
+
*
|
|
7
|
+
* Ops (page → app, each answered once):
|
|
8
|
+
* status { canMakePayments }
|
|
9
|
+
* products { ids } → { products, missing }
|
|
10
|
+
* purchase { productId, quantity?, appAccountToken? }
|
|
11
|
+
* → { status: purchased | pending | cancelled | unverified | failed, transaction?, reason? }
|
|
12
|
+
* unfinished {} → { transactions }
|
|
13
|
+
* finish { transactionId } → { finished }
|
|
14
|
+
* Event (app → page): a `bmstore:transaction` window event whenever StoreKit delivers a purchase
|
|
15
|
+
* outside a purchase() call. It carries nothing; the page answers it by asking for `unfinished`,
|
|
16
|
+
* so a page that was not listening yet loses nothing.
|
|
17
|
+
*
|
|
18
|
+
* A verified purchase is never finished here. The engine records the credit first and then asks
|
|
19
|
+
* for `finish`; until then StoreKit keeps re-delivering the transaction, which is what makes an
|
|
20
|
+
* app killed between payment and crediting safe.
|
|
21
|
+
*
|
|
22
|
+
* Same literal rules as xcode-template.ts: no Swift `\(...)` interpolation, no backticks.
|
|
23
|
+
*/
|
|
24
|
+
export const STORE_HANDLER_NAME = 'bmStore';
|
|
25
|
+
export const STORE_TRANSACTION_EVENT = 'bmstore:transaction';
|
|
26
|
+
export const STORE_BRIDGE_SWIFT = `import Foundation
|
|
27
|
+
import StoreKit
|
|
28
|
+
import WebKit
|
|
29
|
+
|
|
30
|
+
/// App Store purchases for the game page. See the ops table in the CLI's store-bridge.ts.
|
|
31
|
+
final class StoreBridge: NSObject, WKScriptMessageHandlerWithReply {
|
|
32
|
+
static let handlerName = "${STORE_HANDLER_NAME}"
|
|
33
|
+
static let transactionEvent = "${STORE_TRANSACTION_EVENT}"
|
|
34
|
+
|
|
35
|
+
weak var webView: WKWebView?
|
|
36
|
+
private var updatesTask: Task<Void, Never>?
|
|
37
|
+
/// Verified, not yet finished transactions, by id — so finish never has to search.
|
|
38
|
+
private var pending: [UInt64: Transaction] = [:]
|
|
39
|
+
|
|
40
|
+
deinit {
|
|
41
|
+
updatesTask?.cancel()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/// Listen for purchases that complete outside a purchase() call: Ask to Buy approvals,
|
|
45
|
+
/// payment-method fixes, a purchase interrupted by the app being killed. Apple's guidance is
|
|
46
|
+
/// to start this at launch, before any purchase, or such transactions can be missed.
|
|
47
|
+
@MainActor
|
|
48
|
+
func start() {
|
|
49
|
+
updatesTask = Task { [weak self] in
|
|
50
|
+
for await result in Transaction.updates {
|
|
51
|
+
self?.received(result)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@MainActor
|
|
57
|
+
private func received(_ result: VerificationResult<Transaction>) {
|
|
58
|
+
guard case .verified(let transaction) = result else { return }
|
|
59
|
+
pending[transaction.id] = transaction
|
|
60
|
+
let script = "window.dispatchEvent(new CustomEvent('" + Self.transactionEvent + "'))"
|
|
61
|
+
webView?.evaluateJavaScript(script, completionHandler: nil)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func userContentController(
|
|
65
|
+
_ userContentController: WKUserContentController,
|
|
66
|
+
didReceive message: WKScriptMessage,
|
|
67
|
+
replyHandler: @escaping (Any?, String?) -> Void
|
|
68
|
+
) {
|
|
69
|
+
guard let body = message.body as? [String: Any], let op = body["op"] as? String else {
|
|
70
|
+
replyHandler(nil, "A store request needs an op.")
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
Task { @MainActor in
|
|
74
|
+
do {
|
|
75
|
+
let reply = try await self.handle(op: op, body: body)
|
|
76
|
+
replyHandler(reply, nil)
|
|
77
|
+
} catch {
|
|
78
|
+
replyHandler(nil, String(describing: error))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
@MainActor
|
|
84
|
+
private func handle(op: String, body: [String: Any]) async throws -> Any {
|
|
85
|
+
switch op {
|
|
86
|
+
case "status":
|
|
87
|
+
return ["canMakePayments": AppStore.canMakePayments]
|
|
88
|
+
case "products":
|
|
89
|
+
let ids = (body["ids"] as? [String]) ?? []
|
|
90
|
+
let products = try await Product.products(for: ids)
|
|
91
|
+
let found = Set(products.map { $0.id })
|
|
92
|
+
return [
|
|
93
|
+
"products": products.map { Self.productInfo($0) },
|
|
94
|
+
"missing": ids.filter { !found.contains($0) },
|
|
95
|
+
]
|
|
96
|
+
case "purchase":
|
|
97
|
+
return try await purchase(body)
|
|
98
|
+
case "unfinished":
|
|
99
|
+
var transactions: [[String: Any]] = []
|
|
100
|
+
for await result in Transaction.unfinished {
|
|
101
|
+
guard case .verified(let transaction) = result else { continue }
|
|
102
|
+
pending[transaction.id] = transaction
|
|
103
|
+
transactions.append(Self.transactionInfo(transaction, jws: result.jwsRepresentation))
|
|
104
|
+
}
|
|
105
|
+
return ["transactions": transactions]
|
|
106
|
+
case "finish":
|
|
107
|
+
guard let text = body["transactionId"] as? String, let id = UInt64(text) else {
|
|
108
|
+
throw StoreBridgeError.badRequest("finish needs a transactionId string")
|
|
109
|
+
}
|
|
110
|
+
return ["finished": await finish(id)]
|
|
111
|
+
default:
|
|
112
|
+
throw StoreBridgeError.badRequest("Unknown store op " + op)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
@MainActor
|
|
117
|
+
private func purchase(_ body: [String: Any]) async throws -> Any {
|
|
118
|
+
guard let productId = body["productId"] as? String else {
|
|
119
|
+
throw StoreBridgeError.badRequest("purchase needs a productId")
|
|
120
|
+
}
|
|
121
|
+
guard let product = try await Product.products(for: [productId]).first else {
|
|
122
|
+
return [
|
|
123
|
+
"status": "failed",
|
|
124
|
+
"reason": "The App Store did not return the product " + productId + ".",
|
|
125
|
+
]
|
|
126
|
+
}
|
|
127
|
+
var options: Set<Product.PurchaseOption> = []
|
|
128
|
+
if let quantity = body["quantity"] as? Int, quantity > 1 {
|
|
129
|
+
options.insert(.quantity(quantity))
|
|
130
|
+
}
|
|
131
|
+
if let token = body["appAccountToken"] as? String, let uuid = UUID(uuidString: token) {
|
|
132
|
+
options.insert(.appAccountToken(uuid))
|
|
133
|
+
}
|
|
134
|
+
let result = try await product.purchase(options: options)
|
|
135
|
+
switch result {
|
|
136
|
+
case .success(let verification):
|
|
137
|
+
switch verification {
|
|
138
|
+
case .verified(let transaction):
|
|
139
|
+
pending[transaction.id] = transaction
|
|
140
|
+
return [
|
|
141
|
+
"status": "purchased",
|
|
142
|
+
"transaction": Self.transactionInfo(transaction, jws: verification.jwsRepresentation),
|
|
143
|
+
]
|
|
144
|
+
case .unverified(_, let error):
|
|
145
|
+
return ["status": "unverified", "reason": String(describing: error)]
|
|
146
|
+
}
|
|
147
|
+
case .userCancelled:
|
|
148
|
+
return ["status": "cancelled"]
|
|
149
|
+
case .pending:
|
|
150
|
+
return ["status": "pending"]
|
|
151
|
+
@unknown default:
|
|
152
|
+
return ["status": "failed", "reason": "StoreKit returned an unknown purchase result."]
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
@MainActor
|
|
157
|
+
private func finish(_ id: UInt64) async -> Bool {
|
|
158
|
+
if let transaction = pending.removeValue(forKey: id) {
|
|
159
|
+
await transaction.finish()
|
|
160
|
+
return true
|
|
161
|
+
}
|
|
162
|
+
for await result in Transaction.unfinished {
|
|
163
|
+
if case .verified(let transaction) = result, transaction.id == id {
|
|
164
|
+
await transaction.finish()
|
|
165
|
+
return true
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return false
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
private static func typeName(_ type: Product.ProductType) -> String {
|
|
172
|
+
if type == .consumable { return "consumable" }
|
|
173
|
+
if type == .nonConsumable { return "nonConsumable" }
|
|
174
|
+
if type == .autoRenewable { return "autoRenewable" }
|
|
175
|
+
if type == .nonRenewable { return "nonRenewable" }
|
|
176
|
+
return "other"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private static func productInfo(_ product: Product) -> [String: Any] {
|
|
180
|
+
return [
|
|
181
|
+
"id": product.id,
|
|
182
|
+
"type": typeName(product.type),
|
|
183
|
+
"displayName": product.displayName,
|
|
184
|
+
"description": product.description,
|
|
185
|
+
"displayPrice": product.displayPrice,
|
|
186
|
+
"price": NSDecimalNumber(decimal: product.price).doubleValue,
|
|
187
|
+
"currencyCode": product.priceFormatStyle.currencyCode,
|
|
188
|
+
]
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/// Ids go to the page as strings: a transaction id is a UInt64 and need not fit a double.
|
|
192
|
+
private static func transactionInfo(_ transaction: Transaction, jws: String) -> [String: Any] {
|
|
193
|
+
var info: [String: Any] = [
|
|
194
|
+
"transactionId": String(transaction.id),
|
|
195
|
+
"originalTransactionId": String(transaction.originalID),
|
|
196
|
+
"productId": transaction.productID,
|
|
197
|
+
"productType": typeName(transaction.productType),
|
|
198
|
+
"quantity": transaction.purchasedQuantity,
|
|
199
|
+
"purchaseDate": transaction.purchaseDate.timeIntervalSince1970 * 1000,
|
|
200
|
+
"revoked": transaction.revocationDate != nil,
|
|
201
|
+
"jws": jws,
|
|
202
|
+
]
|
|
203
|
+
if #available(iOS 16.0, *) {
|
|
204
|
+
info["environment"] = transaction.environment.rawValue
|
|
205
|
+
}
|
|
206
|
+
if let token = transaction.appAccountToken {
|
|
207
|
+
info["appAccountToken"] = token.uuidString
|
|
208
|
+
}
|
|
209
|
+
return info
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
enum StoreBridgeError: Error, CustomStringConvertible {
|
|
214
|
+
case badRequest(String)
|
|
215
|
+
|
|
216
|
+
var description: String {
|
|
217
|
+
switch self {
|
|
218
|
+
case .badRequest(let message):
|
|
219
|
+
return message
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
`;
|
|
224
|
+
//# sourceMappingURL=store-bridge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-bridge.js","sourceRoot":"","sources":["../../src/ios/store-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,CAAC;AAC5C,MAAM,CAAC,MAAM,uBAAuB,GAAG,qBAAqB,CAAC;AAE7D,MAAM,CAAC,MAAM,kBAAkB,GAAG;;;;;;gCAMF,kBAAkB;qCACb,uBAAuB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8L3D,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface StoreKitProductInput {
|
|
2
|
+
id: string;
|
|
3
|
+
/** Test price, e.g. "0.99". Defaults to 0.99. */
|
|
4
|
+
price?: string;
|
|
5
|
+
/** Test display name. Defaults to the id. */
|
|
6
|
+
name?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface StoreKitUpsertResult {
|
|
9
|
+
created: boolean;
|
|
10
|
+
added: string[];
|
|
11
|
+
alreadyListed: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare function validateProductId(id: string): void;
|
|
14
|
+
/** Add the consumables the file does not list yet; create the file when there is none. */
|
|
15
|
+
export declare function upsertStoreKitConsumables(filePath: string, products: readonly StoreKitProductInput[]): StoreKitUpsertResult;
|