@bitmagic/cli 0.1.70-dev.1 → 0.1.70-dev.3
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 +81 -1
- package/assets/skills/publishing-to-poki/SKILL.md +90 -0
- package/dist/cli.d.ts +12 -0
- package/dist/commands/publish.d.ts +16 -0
- package/dist/commands/publish.js +21 -0
- package/dist/commands/publish.js.map +1 -1
- package/dist/ios/collect-urls.d.ts +1 -75
- package/dist/ios/collect-urls.js +2 -219
- package/dist/ios/collect-urls.js.map +1 -1
- package/dist/ios/local-server.d.ts +1 -11
- package/dist/ios/local-server.js +2 -87
- package/dist/ios/local-server.js.map +1 -1
- package/dist/ios/transform.d.ts +4 -46
- package/dist/ios/transform.js +4 -60
- package/dist/ios/transform.js.map +1 -1
- package/dist/ios/vendor.d.ts +1 -29
- package/dist/ios/vendor.js +2 -112
- package/dist/ios/vendor.js.map +1 -1
- package/dist/package/collect-urls.d.ts +75 -0
- package/dist/package/collect-urls.js +220 -0
- package/dist/package/collect-urls.js.map +1 -0
- package/dist/package/local-server.d.ts +13 -0
- package/dist/package/local-server.js +102 -0
- package/dist/package/local-server.js.map +1 -0
- package/dist/package/transform.d.ts +47 -0
- package/dist/package/transform.js +62 -0
- package/dist/package/transform.js.map +1 -0
- package/dist/package/vendor.d.ts +33 -0
- package/dist/package/vendor.js +115 -0
- package/dist/package/vendor.js.map +1 -0
- package/dist/poki/assets.d.ts +25 -0
- package/dist/poki/assets.js +163 -0
- package/dist/poki/assets.js.map +1 -0
- package/dist/poki/check.d.ts +24 -0
- package/dist/poki/check.js +168 -0
- package/dist/poki/check.js.map +1 -0
- package/dist/poki/handoff.d.ts +22 -0
- package/dist/poki/handoff.js +56 -0
- package/dist/poki/handoff.js.map +1 -0
- package/dist/poki/playtest.d.ts +15 -0
- package/dist/poki/playtest.js +45 -0
- package/dist/poki/playtest.js.map +1 -0
- package/dist/poki/publish.d.ts +23 -0
- package/dist/poki/publish.js +247 -0
- package/dist/poki/publish.js.map +1 -0
- package/dist/poki/source-diagnostics.d.ts +14 -0
- package/dist/poki/source-diagnostics.js +40 -0
- package/dist/poki/source-diagnostics.js.map +1 -0
- package/dist/poki/transform.d.ts +6 -0
- package/dist/poki/transform.js +31 -0
- package/dist/poki/transform.js.map +1 -0
- package/dist/publish/bundle.d.ts +7 -1
- package/dist/publish/bundle.js +9 -3
- package/dist/publish/bundle.js.map +1 -1
- package/dist/scaffold/poki-skill-files.d.ts +2 -0
- package/dist/scaffold/poki-skill-files.js +7 -0
- package/dist/scaffold/poki-skill-files.js.map +1 -0
- package/dist/scaffold/project-files.js +1 -1
- package/dist/scaffold/project.js +2 -0
- package/dist/scaffold/project.js.map +1 -1
- package/dist/scaffold/skill-files.js +6 -4
- package/dist/scaffold/skill-files.js.map +1 -1
- package/dist/verify/browser.d.ts +13 -1
- package/dist/verify/browser.js +24 -12
- package/dist/verify/browser.js.map +1 -1
- package/dist/verify/classify.d.ts +2 -0
- package/dist/verify/classify.js +6 -1
- package/dist/verify/classify.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** Only static resources may be downloaded by discovery; never vendor API responses. */
|
|
2
|
+
export declare function isStaticAsset(url: string): boolean;
|
|
3
|
+
export declare function scanAssetUrls(text: string): string[];
|
|
4
|
+
/** HTML resource attributes, excluding dormant URL strings in bundled engine code. */
|
|
5
|
+
export declare function scanHtmlAssets(html: string): string[];
|
|
6
|
+
/** Preserve sibling layout, but never merge distinct query resources or unsafe decoded paths. */
|
|
7
|
+
export declare function pokiAssetPath(url: string): string | null;
|
|
8
|
+
/**
|
|
9
|
+
* Where a BASE url's rewrite points: the same mapping, minus the appended 'index' — the Poki
|
|
10
|
+
* counterpart of vendor.ts's `vendorBasePathFor`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function pokiBasePath(baseUrl: string): string | null;
|
|
13
|
+
/** References inside a sidecar, retained with their original spelling for rewriting. */
|
|
14
|
+
export declare function sidecarReferences(text: string, url: string): Map<string, string>;
|
|
15
|
+
/** Rewrite resource references only; short names must not replace arbitrary CSS or GLTF text. */
|
|
16
|
+
export declare function rewriteSidecar(text: string, url: string, replacements: ReadonlyMap<string, string>): string;
|
|
17
|
+
export interface PackageAssetsOptions {
|
|
18
|
+
urls: Iterable<string>;
|
|
19
|
+
payloadDir: string;
|
|
20
|
+
cacheDir: string;
|
|
21
|
+
fetch: typeof globalThis.fetch;
|
|
22
|
+
log: (message: string) => void;
|
|
23
|
+
}
|
|
24
|
+
/** Walk GLTF/CSS dependencies as well as direct URLs; a missing declared asset is an error. */
|
|
25
|
+
export declare function packageAssets(options: PackageAssetsOptions): Promise<Map<string, string>>;
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { CliError } from '../errors.js';
|
|
5
|
+
import { vendorPathFor, vendorUrls } from '../package/vendor.js';
|
|
6
|
+
import { POKI_SDK_URL } from './transform.js';
|
|
7
|
+
/** Only static resources may be downloaded by discovery; never vendor API responses. */
|
|
8
|
+
export function isStaticAsset(url) {
|
|
9
|
+
if (url === POKI_SDK_URL)
|
|
10
|
+
return false;
|
|
11
|
+
try {
|
|
12
|
+
const parsed = new URL(url);
|
|
13
|
+
return /^https?:$/.test(parsed.protocol) && (/\.(?:glb|gltf|bin|vxl|vwld|spz|splat|ksplat|ply|png|jpe?g|webp|gif|svg|ico|ktx2|hdr|exr|woff2?|ttf|otf|mp3|ogg|opus|wav|mp4|webm|json|js|mjs|wasm|css)(?:\.br|\.gz)?$/i.test(parsed.pathname)
|
|
14
|
+
|| (parsed.hostname === 'fonts.googleapis.com' && /^\/css2?$/.test(parsed.pathname)));
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export function scanAssetUrls(text) {
|
|
21
|
+
return [...new Set((text.match(/https?:\/\/[^\s"'`<>\\)]+/g) ?? [])
|
|
22
|
+
.filter((url) => !url.includes('${') && isStaticAsset(url)))];
|
|
23
|
+
}
|
|
24
|
+
/** HTML resource attributes, excluding dormant URL strings in bundled engine code. */
|
|
25
|
+
export function scanHtmlAssets(html) {
|
|
26
|
+
const markup = html.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, (tag) => tag.slice(0, tag.indexOf('>') + 1));
|
|
27
|
+
return [...new Set([...markup.matchAll(/(?:src|href)=["'](https?:\/\/[^"']+)["']/gi)]
|
|
28
|
+
.map((match) => match[1].replace(/&/g, '&')).filter(isStaticAsset))];
|
|
29
|
+
}
|
|
30
|
+
/** Preserve sibling layout, but never merge distinct query resources or unsafe decoded paths. */
|
|
31
|
+
export function pokiAssetPath(url) {
|
|
32
|
+
const normal = vendorPathFor(url);
|
|
33
|
+
if (normal === null)
|
|
34
|
+
return null;
|
|
35
|
+
const parsed = new URL(url);
|
|
36
|
+
if (parsed.username || parsed.password)
|
|
37
|
+
return null;
|
|
38
|
+
const segments = normal.split('/').map((segment) => segment.replace(/[^a-zA-Z0-9._-]/g, (char) => '~' + char.charCodeAt(0).toString(16)));
|
|
39
|
+
let result = segments.join('/');
|
|
40
|
+
if (parsed.hostname === 'fonts.googleapis.com')
|
|
41
|
+
result += '.css';
|
|
42
|
+
if (parsed.search) {
|
|
43
|
+
const suffix = createHash('sha256').update(parsed.search).digest('hex').slice(0, 16);
|
|
44
|
+
const ext = path.posix.extname(result);
|
|
45
|
+
result = `${ext ? result.slice(0, -ext.length) : result}~${suffix}${ext}`;
|
|
46
|
+
}
|
|
47
|
+
return parsed.protocol === 'http:' ? result.replace(/^vendor\//, 'vendor-http/') : result;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Where a BASE url's rewrite points: the same mapping, minus the appended 'index' — the Poki
|
|
51
|
+
* counterpart of vendor.ts's `vendorBasePathFor`.
|
|
52
|
+
*/
|
|
53
|
+
export function pokiBasePath(baseUrl) {
|
|
54
|
+
const mapped = pokiAssetPath(baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
|
|
55
|
+
return mapped === null ? null : mapped.replace(/\/index$/, '');
|
|
56
|
+
}
|
|
57
|
+
/** Every `uri` string in a GLTF document, in place: the visitor's return value replaces it. */
|
|
58
|
+
function walkGltfUris(value, visit) {
|
|
59
|
+
if (Array.isArray(value)) {
|
|
60
|
+
for (const child of value)
|
|
61
|
+
walkGltfUris(child, visit);
|
|
62
|
+
}
|
|
63
|
+
else if (typeof value === 'object' && value !== null) {
|
|
64
|
+
for (const [key, child] of Object.entries(value)) {
|
|
65
|
+
if (key === 'uri' && typeof child === 'string') {
|
|
66
|
+
const replacement = visit(child);
|
|
67
|
+
if (replacement !== undefined)
|
|
68
|
+
value[key] = replacement;
|
|
69
|
+
}
|
|
70
|
+
else
|
|
71
|
+
walkGltfUris(child, visit);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/** The sidecar kinds whose references are followed and rewritten. */
|
|
76
|
+
function sidecarKind(url) {
|
|
77
|
+
const parsed = new URL(url);
|
|
78
|
+
if (/\.gltf$/i.test(parsed.pathname))
|
|
79
|
+
return 'gltf';
|
|
80
|
+
if (/\.css$/i.test(parsed.pathname) || parsed.hostname === 'fonts.googleapis.com')
|
|
81
|
+
return 'css';
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/** `url(...)` and `@import` references in a stylesheet. Group 1 or 2 holds the reference. */
|
|
85
|
+
const CSS_REFERENCE = /url\(\s*["']?([^\s"')]+)["']?\s*\)|@import\s+["']([^"']+)["']/gi;
|
|
86
|
+
/** References inside a sidecar, retained with their original spelling for rewriting. */
|
|
87
|
+
export function sidecarReferences(text, url) {
|
|
88
|
+
const refs = new Set();
|
|
89
|
+
const kind = sidecarKind(url);
|
|
90
|
+
if (kind === 'gltf') {
|
|
91
|
+
walkGltfUris(JSON.parse(text), (uri) => { refs.add(uri); });
|
|
92
|
+
}
|
|
93
|
+
else if (kind === 'css') {
|
|
94
|
+
for (const match of text.matchAll(CSS_REFERENCE))
|
|
95
|
+
refs.add(match[1] ?? match[2]);
|
|
96
|
+
}
|
|
97
|
+
const result = new Map();
|
|
98
|
+
for (const ref of refs) {
|
|
99
|
+
if (/^(data:|blob:|#)/.test(ref))
|
|
100
|
+
continue;
|
|
101
|
+
const absolute = new URL(ref, url).href;
|
|
102
|
+
if (!/^https?:/.test(absolute))
|
|
103
|
+
throw new CliError(`Unsupported dependency ${ref} in ${url}.`);
|
|
104
|
+
result.set(ref, absolute);
|
|
105
|
+
}
|
|
106
|
+
return result;
|
|
107
|
+
}
|
|
108
|
+
/** Rewrite resource references only; short names must not replace arbitrary CSS or GLTF text. */
|
|
109
|
+
export function rewriteSidecar(text, url, replacements) {
|
|
110
|
+
if (sidecarKind(url) === 'gltf') {
|
|
111
|
+
const data = JSON.parse(text);
|
|
112
|
+
walkGltfUris(data, (uri) => replacements.get(uri));
|
|
113
|
+
return JSON.stringify(data);
|
|
114
|
+
}
|
|
115
|
+
return text.replace(CSS_REFERENCE, (match, urlRef, importRef) => {
|
|
116
|
+
const ref = urlRef ?? importRef;
|
|
117
|
+
if (ref === undefined)
|
|
118
|
+
return match;
|
|
119
|
+
const replacement = replacements.get(ref);
|
|
120
|
+
return replacement === undefined ? match : match.replace(ref, () => replacement);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/** Walk GLTF/CSS dependencies as well as direct URLs; a missing declared asset is an error. */
|
|
124
|
+
export async function packageAssets(options) {
|
|
125
|
+
const entries = new Map();
|
|
126
|
+
let pending = [...new Set(options.urls)].filter((url) => url !== POKI_SDK_URL);
|
|
127
|
+
for (let depth = 0; pending.length > 0; depth++) {
|
|
128
|
+
if (depth >= 12 || entries.size + pending.length > 10_000) {
|
|
129
|
+
throw new CliError('Asset dependency discovery exceeded its limit. Check the reported asset URLs.');
|
|
130
|
+
}
|
|
131
|
+
const result = await vendorUrls({
|
|
132
|
+
...options, urls: pending, fetchImpl: options.fetch, pathForUrl: pokiAssetPath,
|
|
133
|
+
// Correct for mutable URLs too; cache files are diagnostics, not proof of freshness.
|
|
134
|
+
reuseCache: false,
|
|
135
|
+
});
|
|
136
|
+
if (result.failures.length > 0) {
|
|
137
|
+
throw new CliError(`Could not package assets:\n${result.failures.map((f) => ` ${f.url}: ${f.error}`).join('\n')}`);
|
|
138
|
+
}
|
|
139
|
+
for (const [url, rel] of result.entries)
|
|
140
|
+
entries.set(url, rel);
|
|
141
|
+
const next = new Set();
|
|
142
|
+
for (const [url, rel] of result.entries) {
|
|
143
|
+
if (sidecarKind(url) === null)
|
|
144
|
+
continue;
|
|
145
|
+
const file = path.join(options.payloadDir, rel);
|
|
146
|
+
const text = fs.readFileSync(file, 'utf-8');
|
|
147
|
+
const refs = sidecarReferences(text, url);
|
|
148
|
+
const rewrites = new Map();
|
|
149
|
+
for (const [ref, absolute] of refs) {
|
|
150
|
+
const dependency = pokiAssetPath(absolute);
|
|
151
|
+
if (dependency === null)
|
|
152
|
+
throw new CliError(`Cannot package dependency ${absolute}.`);
|
|
153
|
+
rewrites.set(ref, path.posix.relative(path.posix.dirname(rel), dependency) + new URL(absolute).hash);
|
|
154
|
+
if (!entries.has(absolute))
|
|
155
|
+
next.add(absolute);
|
|
156
|
+
}
|
|
157
|
+
fs.writeFileSync(file, rewriteSidecar(text, url, rewrites));
|
|
158
|
+
}
|
|
159
|
+
pending = [...next];
|
|
160
|
+
}
|
|
161
|
+
return entries;
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=assets.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"assets.js","sourceRoot":"","sources":["../../src/poki/assets.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { type VerifyResult } from '../verify/classify.js';
|
|
2
|
+
import type { MobileOrientation } from '../project/platform-declaration.js';
|
|
3
|
+
import { type PokiPlaytest } from './playtest.js';
|
|
4
|
+
export declare const POKI_SDK_STUB = "(() => {\n let playing = false, advertising = false;\n const event = (name) => console.info('BM_POKI_EVENT:' + name);\n window.PokiSDK = {\n init: async () => { event('init'); },\n gameLoadingStart: () => {},\n gameLoadingFinished: () => event('loaded'),\n gameplayStart: () => {\n if (playing || advertising) throw new Error('Poki: invalid gameplayStart');\n playing = true; event('start');\n },\n gameplayStop: () => {\n if (!playing || advertising) throw new Error('Poki: invalid gameplayStop');\n playing = false; event('stop');\n },\n commercialBreak: () => {\n if (playing || advertising) throw new Error('Poki: ad during gameplay');\n advertising = true; event('ad');\n return new Promise(resolve => setTimeout(() => { advertising = false; event('ad-end'); resolve(); }, 150));\n },\n setDebug: () => {}\n };\n})();";
|
|
5
|
+
export interface PokiCheckOptions {
|
|
6
|
+
root: string;
|
|
7
|
+
payloadDir: string;
|
|
8
|
+
artifactDir: string;
|
|
9
|
+
mode: 'record' | 'restricted';
|
|
10
|
+
platform: 'desktop' | 'mobile';
|
|
11
|
+
orientation?: MobileOrientation;
|
|
12
|
+
nested?: boolean;
|
|
13
|
+
settleMs?: number;
|
|
14
|
+
playtest?: PokiPlaytest;
|
|
15
|
+
log: (message: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export interface PokiCheckResult {
|
|
18
|
+
result: VerifyResult;
|
|
19
|
+
misses: string[];
|
|
20
|
+
localFailures: string[];
|
|
21
|
+
sdkEvents: string[];
|
|
22
|
+
nonAssetRequests?: string[];
|
|
23
|
+
}
|
|
24
|
+
export declare function runPokiCheck(options: PokiCheckOptions): Promise<PokiCheckResult>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { browserLaunchPlan } from '../browser-gl.js';
|
|
4
|
+
import { startPayloadServer } from '../package/local-server.js';
|
|
5
|
+
import { runHeadlessCheck } from '../verify/browser.js';
|
|
6
|
+
import { classifyVerifyRun } from '../verify/classify.js';
|
|
7
|
+
import { mobileContextOptions } from '../verify/device.js';
|
|
8
|
+
import { DEFAULT_START_INTERACTION } from '../verify/start.js';
|
|
9
|
+
import { withVerifyLock } from '../verify/lock.js';
|
|
10
|
+
import { POKI_SDK_URL } from './transform.js';
|
|
11
|
+
import { isStaticAsset } from './assets.js';
|
|
12
|
+
import { runPokiPlaytest } from './playtest.js';
|
|
13
|
+
import { readPokiSources, requestSourceFiles } from './source-diagnostics.js';
|
|
14
|
+
// Only the intercepted SDK response uses this stub. It never enters the delivered ZIP.
|
|
15
|
+
export const POKI_SDK_STUB = `(() => {
|
|
16
|
+
let playing = false, advertising = false;
|
|
17
|
+
const event = (name) => console.info('BM_POKI_EVENT:' + name);
|
|
18
|
+
window.PokiSDK = {
|
|
19
|
+
init: async () => { event('init'); },
|
|
20
|
+
gameLoadingStart: () => {},
|
|
21
|
+
gameLoadingFinished: () => event('loaded'),
|
|
22
|
+
gameplayStart: () => {
|
|
23
|
+
if (playing || advertising) throw new Error('Poki: invalid gameplayStart');
|
|
24
|
+
playing = true; event('start');
|
|
25
|
+
},
|
|
26
|
+
gameplayStop: () => {
|
|
27
|
+
if (!playing || advertising) throw new Error('Poki: invalid gameplayStop');
|
|
28
|
+
playing = false; event('stop');
|
|
29
|
+
},
|
|
30
|
+
commercialBreak: () => {
|
|
31
|
+
if (playing || advertising) throw new Error('Poki: ad during gameplay');
|
|
32
|
+
advertising = true; event('ad');
|
|
33
|
+
return new Promise(resolve => setTimeout(() => { advertising = false; event('ad-end'); resolve(); }, 150));
|
|
34
|
+
},
|
|
35
|
+
setDebug: () => {}
|
|
36
|
+
};
|
|
37
|
+
})();`;
|
|
38
|
+
/** Every healthy boot reaches all three, in any order. */
|
|
39
|
+
const REQUIRED_SDK_EVENTS = ['init', 'loaded', 'start'];
|
|
40
|
+
/** Exercise the engine state contract after starting through actual UI, inside its iframe. */
|
|
41
|
+
async function pauseAndResume(frame) {
|
|
42
|
+
await frame.evaluate(async () => {
|
|
43
|
+
/* eslint-disable no-undef -- browser callback */
|
|
44
|
+
const w = window;
|
|
45
|
+
const state = w.gameTemplate?.getGameEngine?.().gameStateManager;
|
|
46
|
+
if (!state || state.getCurrentState() !== 'playing')
|
|
47
|
+
throw new Error('Poki check: game never entered PLAYING.');
|
|
48
|
+
state.setPaused(true, 'manual');
|
|
49
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
50
|
+
state.setPaused(false, 'manual');
|
|
51
|
+
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
52
|
+
if (state.getCurrentState() !== 'playing')
|
|
53
|
+
throw new Error('Poki check: game did not resume after the advertisement.');
|
|
54
|
+
/* eslint-enable no-undef */
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
export async function runPokiCheck(options) {
|
|
58
|
+
return withVerifyLock(options.root, 'publish --target poki', { info: options.log }, async () => {
|
|
59
|
+
fs.mkdirSync(options.artifactDir, { recursive: true });
|
|
60
|
+
const mountPath = options.nested ? '/poki/game/' : '/';
|
|
61
|
+
const sources = readPokiSources(options.root);
|
|
62
|
+
const server = await startPayloadServer(options.payloadDir, { mountPath });
|
|
63
|
+
const misses = new Set();
|
|
64
|
+
const localFailures = new Set();
|
|
65
|
+
const nonAssetRequests = new Set();
|
|
66
|
+
const requests = new Map();
|
|
67
|
+
const recordRequest = (url, method, resourceType, status) => {
|
|
68
|
+
requests.set(`${method} ${url}`, { url, method, resourceType, ...(status === undefined ? {} : { status }), sourceFiles: requestSourceFiles(sources, url) });
|
|
69
|
+
if (new URL(url).origin !== server.origin && (method !== 'GET' || !isStaticAsset(url))) {
|
|
70
|
+
nonAssetRequests.add(`${method} ${url}`);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
const gameUrl = `${server.origin}${mountPath}index.html?platform=${options.platform}&renderer=webgl`;
|
|
74
|
+
const harnessUrl = `${server.origin}/__poki_check__.html`;
|
|
75
|
+
const routes = async (context) => {
|
|
76
|
+
context.on('response', (response) => {
|
|
77
|
+
if (new URL(response.url()).origin === server.origin && response.status() >= 400) {
|
|
78
|
+
const request = response.request();
|
|
79
|
+
recordRequest(response.url(), request.method(), request.resourceType(), response.status());
|
|
80
|
+
const files = requestSourceFiles(sources, response.url());
|
|
81
|
+
localFailures.add(`${response.status()} ${response.url()}${files.length > 0 ? ` — source references: ${files.join(', ')}` : ''}`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
await context.routeWebSocket('**', (socket) => {
|
|
85
|
+
misses.add(socket.url());
|
|
86
|
+
recordRequest(socket.url(), 'GET', 'websocket');
|
|
87
|
+
socket.close();
|
|
88
|
+
});
|
|
89
|
+
await context.route('**/*', async (route) => {
|
|
90
|
+
const request = route.request();
|
|
91
|
+
const url = request.url();
|
|
92
|
+
if (url === POKI_SDK_URL) {
|
|
93
|
+
await route.fulfill({ contentType: 'text/javascript', body: POKI_SDK_STUB });
|
|
94
|
+
}
|
|
95
|
+
else if (url === harnessUrl) {
|
|
96
|
+
await route.fulfill({ contentType: 'text/html', body: `<html><head><meta name="viewport" content="width=device-width,initial-scale=1"></head>`
|
|
97
|
+
+ `<body style="margin:0"><iframe allow="fullscreen; autoplay" src="${gameUrl.replace(/&/g, '&')}" `
|
|
98
|
+
+ 'style="position:fixed;inset:0;width:100%;height:100%;border:0"></iframe></body></html>',
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
else if (new URL(url).origin === server.origin) {
|
|
102
|
+
await route.continue();
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
misses.add(url);
|
|
106
|
+
recordRequest(url, request.method(), request.resourceType());
|
|
107
|
+
if (options.mode === 'record' && request.method() === 'GET' && isStaticAsset(url))
|
|
108
|
+
await route.continue();
|
|
109
|
+
else
|
|
110
|
+
await route.abort('blockedbyclient');
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
const recordOutcome = (outcome) => {
|
|
115
|
+
fs.writeFileSync(path.join(options.artifactDir, 'result.json'), JSON.stringify(outcome, null, 2) + '\n');
|
|
116
|
+
return outcome;
|
|
117
|
+
};
|
|
118
|
+
try {
|
|
119
|
+
const observations = await runHeadlessCheck({
|
|
120
|
+
url: harnessUrl, probeFrameUrlIncludes: `${server.origin}${mountPath}index.html`,
|
|
121
|
+
screenshotPath: path.join(options.artifactDir, 'screenshot.png'),
|
|
122
|
+
consolePath: path.join(options.artifactDir, 'console.log'),
|
|
123
|
+
settleMs: options.settleMs ?? 25_000,
|
|
124
|
+
startInteraction: DEFAULT_START_INTERACTION,
|
|
125
|
+
launch: browserLaunchPlan(process.env, 'webgl'),
|
|
126
|
+
...(options.platform === 'mobile' ? { device: mobileContextOptions(options.orientation ?? 'landscape') } : {}),
|
|
127
|
+
routes, blockServiceWorkers: true,
|
|
128
|
+
afterSettle: async (frame) => {
|
|
129
|
+
if (options.playtest) {
|
|
130
|
+
options.log(`Running ${options.platform} Poki playtest: ${options.playtest.file}`);
|
|
131
|
+
await runPokiPlaytest(options.playtest, { frame, platform: options.platform });
|
|
132
|
+
}
|
|
133
|
+
if (options.mode === 'restricted')
|
|
134
|
+
await pauseAndResume(frame);
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
const sdkEvents = observations.consoleLines.flatMap((line) => {
|
|
138
|
+
const event = /BM_POKI_EVENT:(\S+)/.exec(line);
|
|
139
|
+
return event ? [event[1]] : [];
|
|
140
|
+
});
|
|
141
|
+
const result = classifyVerifyRun(observations, { bundled: true, platform: options.platform });
|
|
142
|
+
if (!REQUIRED_SDK_EVENTS.every((event) => sdkEvents.includes(event))) {
|
|
143
|
+
result.failures.push('The Poki SDK did not initialize, finish loading, and report gameplay.');
|
|
144
|
+
}
|
|
145
|
+
if (options.mode === 'restricted' && !sdkEvents.includes('ad-end')) {
|
|
146
|
+
result.failures.push('The Poki pause/resume advertisement was not observed.');
|
|
147
|
+
}
|
|
148
|
+
result.ok = result.failures.length === 0;
|
|
149
|
+
return recordOutcome({ result, misses: [...misses], localFailures: [...localFailures], sdkEvents, nonAssetRequests: [...nonAssetRequests] });
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
return recordOutcome({
|
|
153
|
+
result: { ok: false, failures: [error instanceof Error ? error.message : String(error)], warnings: [] },
|
|
154
|
+
misses: [...misses], localFailures: [...localFailures], sdkEvents: [],
|
|
155
|
+
nonAssetRequests: [...nonAssetRequests],
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
finally {
|
|
159
|
+
try {
|
|
160
|
+
fs.writeFileSync(path.join(options.artifactDir, 'requests.json'), JSON.stringify([...requests.values()], null, 2) + '\n');
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
await server.close();
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/poki/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { PokiServiceRisk } from './source-diagnostics.js';
|
|
2
|
+
interface PokiHandoff {
|
|
3
|
+
zipPath: string;
|
|
4
|
+
payloadDir: string;
|
|
5
|
+
manifestPath: string;
|
|
6
|
+
checksDir: string;
|
|
7
|
+
sha256: string;
|
|
8
|
+
engineVersion: string;
|
|
9
|
+
cliVersion: string;
|
|
10
|
+
zipBytes: number;
|
|
11
|
+
unpackedBytes: number;
|
|
12
|
+
builtAt: string;
|
|
13
|
+
playtest: {
|
|
14
|
+
file: string;
|
|
15
|
+
sha256: string;
|
|
16
|
+
} | null;
|
|
17
|
+
serviceRisks: PokiServiceRisk[];
|
|
18
|
+
warnings: string[];
|
|
19
|
+
}
|
|
20
|
+
/** Generated separately from UPLOAD-HANDOFF.md so re-export never overwrites manual QA notes. */
|
|
21
|
+
export declare function renderPokiHandoff(result: PokiHandoff): string;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/** Generated separately from UPLOAD-HANDOFF.md so re-export never overwrites manual QA notes. */
|
|
2
|
+
export function renderPokiHandoff(result) {
|
|
3
|
+
return `# Poki export handoff
|
|
4
|
+
|
|
5
|
+
Export complete; upload pending. This command did not upload or release a Poki version.
|
|
6
|
+
|
|
7
|
+
- Built: ${result.builtAt}
|
|
8
|
+
- Engine: ${result.engineVersion}
|
|
9
|
+
- CLI: ${result.cliVersion}
|
|
10
|
+
- ZIP: ${JSON.stringify(result.zipPath)}
|
|
11
|
+
- ZIP SHA-256: ${result.sha256}
|
|
12
|
+
- Upload directory: ${JSON.stringify(result.payloadDir)}
|
|
13
|
+
- Manifest: ${JSON.stringify(result.manifestPath)}
|
|
14
|
+
- Diagnostics: ${JSON.stringify(result.checksDir)}
|
|
15
|
+
- Compressed: ${result.zipBytes} bytes; unpacked: ${result.unpackedBytes} bytes.
|
|
16
|
+
|
|
17
|
+
## What was checked
|
|
18
|
+
|
|
19
|
+
The extracted ZIP passed desktop and mobile **emulation** at root and nested iframe paths,
|
|
20
|
+
with an SDK stub and a pause/ad/resume cycle. Network checks cover only the paths exercised.
|
|
21
|
+
${result.playtest
|
|
22
|
+
? `Game-specific playtest: ${JSON.stringify(result.playtest.file)} (SHA-256 ${result.playtest.sha256}).`
|
|
23
|
+
: 'No game-specific playtest was supplied. Custom menus, chapters, score submission, and later levels may be untested. Use --poki-playtest to exercise them during export.'}
|
|
24
|
+
|
|
25
|
+
Real SDK/Inspector QA, physical-phone QA, upload, moderation, and public release remain unverified.
|
|
26
|
+
|
|
27
|
+
## External-service review
|
|
28
|
+
|
|
29
|
+
${result.serviceRisks.length > 0
|
|
30
|
+
? result.serviceRisks.map((risk) => `- ${risk.file}: ${risk.api}() may use an external service.`).join('\n')
|
|
31
|
+
: 'No known Bitmagic service calls were found by the source scan. This is not proof that the game has no external dependencies.'}
|
|
32
|
+
|
|
33
|
+
Exercise an actual session, chapter transitions, end screens, and leaderboards in Inspector.
|
|
34
|
+
Resolve blocked services with the creator: use an agreed offline design or seek Poki approval.
|
|
35
|
+
Do not silently remove features or claim that no external resources are used from a smoke pass.
|
|
36
|
+
|
|
37
|
+
## Upload and real-device QA
|
|
38
|
+
|
|
39
|
+
When an upload is requested, use the [official Poki CLI](https://github.com/poki/poki-cli).
|
|
40
|
+
Read its upload help and supply the Poki UUID and only the upload directory above.
|
|
41
|
+
Preserve the validated assets by disabling image recompression; keep public release disabled
|
|
42
|
+
unless requested. Initial login opens the system browser, so arrange that browser handoff first.
|
|
43
|
+
|
|
44
|
+
Check the returned version ID, not only the uploader's exit code. Record it and check processing status before retrying an apparently missing
|
|
45
|
+
upload. Ready, moderation approval, and public release are separate states.
|
|
46
|
+
|
|
47
|
+
Test the real SDK using [Poki Inspector](https://developers.poki.com/guide/inspector).
|
|
48
|
+
Open the payload folder, or use the uploaded version's Inspector link. Check folder-selection
|
|
49
|
+
permissions before browser automation; if unavailable, use a manual selection or an already
|
|
50
|
+
authorized hosted upload. An HTTP 200 from a local server does not prove its iframe loaded.
|
|
51
|
+
Inspector Mobile mode requires a connected phone via QR; emulation is not physical-device QA.
|
|
52
|
+
|
|
53
|
+
Keep observed upload/QA results in a separate UPLOAD-HANDOFF.md; this file is regenerated on export.
|
|
54
|
+
${result.warnings.length > 0 ? '\n## Export warnings\n\n' + result.warnings.map((warning) => `- ${warning}`).join('\n') + '\n' : ''}`;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=handoff.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff.js","sourceRoot":"","sources":["../../src/poki/handoff.ts"],"names":[],"mappings":"AAkBA,iGAAiG;AACjG,MAAM,UAAU,iBAAiB,CAAC,MAAmB;IACnD,OAAO;;;;WAIE,MAAM,CAAC,OAAO;YACb,MAAM,CAAC,aAAa;SACvB,MAAM,CAAC,UAAU;SACjB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC;iBACtB,MAAM,CAAC,MAAM;sBACR,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC;cACzC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC;iBAChC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC;gBACjC,MAAM,CAAC,QAAQ,qBAAqB,MAAM,CAAC,aAAa;;;;;;EAMtE,MAAM,CAAC,QAAQ;QACb,CAAC,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,MAAM,CAAC,QAAQ,CAAC,MAAM,IAAI;QACxG,CAAC,CAAC,yKAAyK;;;;;;EAM7K,MAAM,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC;QAC5B,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,GAAG,iCAAiC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QAC5G,CAAC,CAAC,8HAA8H;;;;;;;;;;;;;;;;;;;;;;;EAuBlI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,0BAA0B,GAAG,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACtI,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Frame, Page } from 'playwright-core';
|
|
2
|
+
export interface PokiPlaytestContext {
|
|
3
|
+
frame: Page | Frame;
|
|
4
|
+
platform: 'desktop' | 'mobile';
|
|
5
|
+
}
|
|
6
|
+
export interface PokiPlaytest {
|
|
7
|
+
file: string;
|
|
8
|
+
sha256: string;
|
|
9
|
+
run: (context: PokiPlaytestContext) => Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare function playtestHash(file: string): string;
|
|
12
|
+
/** Explicit opt-in: game-specific UI actions run against every staged/extracted package check. */
|
|
13
|
+
export declare function loadPokiPlaytest(file: string | undefined, cwd: string): Promise<PokiPlaytest | undefined>;
|
|
14
|
+
/** A stalled selector must not hang an export. Closing the browser cancels outstanding actions. */
|
|
15
|
+
export declare function runPokiPlaytest(playtest: PokiPlaytest, context: PokiPlaytestContext, timeoutMs?: number): Promise<void>;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import { pathToFileURL } from 'url';
|
|
5
|
+
import { CliError } from '../errors.js';
|
|
6
|
+
export function playtestHash(file) {
|
|
7
|
+
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
|
8
|
+
}
|
|
9
|
+
/** Explicit opt-in: game-specific UI actions run against every staged/extracted package check. */
|
|
10
|
+
export async function loadPokiPlaytest(file, cwd) {
|
|
11
|
+
if (file === undefined)
|
|
12
|
+
return undefined;
|
|
13
|
+
const resolved = path.resolve(cwd, file);
|
|
14
|
+
if (path.extname(resolved) !== '.mjs' || !fs.statSync(resolved, { throwIfNoEntry: false })?.isFile()) {
|
|
15
|
+
throw new CliError('--poki-playtest must name an existing .mjs file exporting a default async function.', 4);
|
|
16
|
+
}
|
|
17
|
+
const sha256 = playtestHash(resolved);
|
|
18
|
+
let loaded;
|
|
19
|
+
try {
|
|
20
|
+
loaded = await import(`${pathToFileURL(resolved).href}?sha256=${sha256}`);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
throw new CliError(`Cannot load Poki playtest ${resolved}: ${error instanceof Error ? error.message : String(error)}`, 4);
|
|
24
|
+
}
|
|
25
|
+
const run = loaded.default;
|
|
26
|
+
if (typeof run !== 'function')
|
|
27
|
+
throw new CliError('The Poki playtest must export a default async function receiving { frame, platform }.', 4);
|
|
28
|
+
return { file: resolved, sha256, run: async (context) => { await run(context); } };
|
|
29
|
+
}
|
|
30
|
+
/** A stalled selector must not hang an export. Closing the browser cancels outstanding actions. */
|
|
31
|
+
export async function runPokiPlaytest(playtest, context, timeoutMs = 30_000) {
|
|
32
|
+
let timer;
|
|
33
|
+
try {
|
|
34
|
+
await Promise.race([
|
|
35
|
+
Promise.resolve().then(() => playtest.run(context)),
|
|
36
|
+
new Promise((_, reject) => {
|
|
37
|
+
timer = setTimeout(() => reject(new Error(`Poki playtest timed out after ${timeoutMs} ms: ${playtest.file}`)), timeoutMs);
|
|
38
|
+
}),
|
|
39
|
+
]);
|
|
40
|
+
}
|
|
41
|
+
finally {
|
|
42
|
+
clearTimeout(timer);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=playtest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playtest.js","sourceRoot":"","sources":["../../src/poki/playtest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAaxC,MAAM,UAAU,YAAY,CAAC,IAAY;IACvC,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC1E,CAAC;AAED,kGAAkG;AAClG,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,IAAwB,EAAE,GAAW;IAC1E,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;IACzC,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,MAAM,IAAI,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;QACrG,MAAM,IAAI,QAAQ,CAAC,qFAAqF,EAAE,CAAC,CAAC,CAAC;IAC/G,CAAC;IACD,MAAM,MAAM,GAAG,YAAY,CAAC,QAAQ,CAAC,CAAC;IACtC,IAAI,MAA6B,CAAC;IAClC,IAAI,CAAC;QAAC,MAAM,GAAG,MAAM,MAAM,CAAC,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,IAAI,WAAW,MAAM,EAAE,CAA0B,CAAC;IAAC,CAAC;IAC3G,OAAO,KAAK,EAAE,CAAC;QAAC,MAAM,IAAI,QAAQ,CAAC,6BAA6B,QAAQ,KAAK,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;IAAC,CAAC;IAC5I,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC;IAC3B,IAAI,OAAO,GAAG,KAAK,UAAU;QAAE,MAAM,IAAI,QAAQ,CAAC,uFAAuF,EAAE,CAAC,CAAC,CAAC;IAC9I,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,GAAG,MAAM,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACrF,CAAC;AAED,mGAAmG;AACnG,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAsB,EAAE,OAA4B,EAAE,SAAS,GAAG,MAAM;IAC5G,IAAI,KAAgD,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,CAAC;YACjB,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACnD,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,iCAAiC,SAAS,QAAQ,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;YAC5H,CAAC,CAAC;SACH,CAAC,CAAC;IACL,CAAC;YAAS,CAAC;QAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAAC,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Output } from '../output.js';
|
|
2
|
+
import { runBuild } from '../publish/bundle.js';
|
|
3
|
+
import { collectUrls } from '../package/collect-urls.js';
|
|
4
|
+
import type { PokiCheckOptions, PokiCheckResult } from './check.js';
|
|
5
|
+
export interface PokiPublishArgs {
|
|
6
|
+
name?: string;
|
|
7
|
+
output?: string;
|
|
8
|
+
'poki-playtest'?: string;
|
|
9
|
+
force?: boolean;
|
|
10
|
+
json?: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface PokiPublishDeps {
|
|
13
|
+
startDir?: string;
|
|
14
|
+
output?: Output;
|
|
15
|
+
fetch: typeof globalThis.fetch;
|
|
16
|
+
build: typeof runBuild;
|
|
17
|
+
collect: typeof collectUrls;
|
|
18
|
+
check: (options: PokiCheckOptions) => Promise<PokiCheckResult>;
|
|
19
|
+
}
|
|
20
|
+
/** Capability metadata lives in the engine tarball, including locally built engines. */
|
|
21
|
+
export declare function requirePokiCapability(root: string): void;
|
|
22
|
+
export declare function pokiOutputPath(root: string, output: string | undefined, cwd: string): string;
|
|
23
|
+
export declare function runPokiPublish(args: PokiPublishArgs, deps?: PokiPublishDeps): Promise<void>;
|