Play rules verified against the live Policy Center / Play Console Help pages (checked 2026-10-11, cited in cli/lib/scan/play-rules.ts): restricted permissions and their declarations (background location, photo / video, All files access, AccessibilityService, REQUEST_INSTALL_PACKAGES, Health Connect, full-screen intent, VpnService), prominent disclosure and the in-app privacy policy, Data safety vs SDK disclosures, Families (certified ad SDKs, AD_ID, location), AD_ID on targetSdk 33+, 16 KB page sizes (Unity, React Native, NDK, AGP, prebuilt .so), testing for new personal accounts, Play metadata, ads at launch, payments outside Play Billing, impersonation. Detection reads manifest components, accessibility configs, strings.xml, native toolchain versions and a local build's merged manifest. New fixtures and tests; README table per store.
73 lines
3.4 KiB
TypeScript
73 lines
3.4 KiB
TypeScript
// storebound config of the demo app "Tidy Lists" (fictional, made for this repo) to show the app profile and the app
|
|
// screenshot style. Placeholder ids and URLs only: nothing here points at a real store record. See README.md.
|
|
import type { ShipConfig } from '../../cli/lib/config';
|
|
|
|
const config: ShipConfig = {
|
|
app: {
|
|
name: 'Tidy Lists',
|
|
profile: 'app',
|
|
copyright: '2026 Example Studio',
|
|
privacyPolicyUrl: 'https://example.com/privacy',
|
|
termsUrl: 'https://example.com/terms',
|
|
supportUrl: 'https://example.com/support',
|
|
website: 'https://example.com',
|
|
contactEmail: 'support@example.com',
|
|
},
|
|
accounts: { enabled: true, deletionUrl: 'https://example.com/delete-account', inApp: 'Settings > Account > Delete account' },
|
|
reviewAccess: { signInRequired: true, instructions: 'Sign in with the demo account; the Groceries list is shared with a second demo user.' },
|
|
languages: [
|
|
{ id: 'en', play: 'en-US', apple: 'en-US' },
|
|
{ id: 'tr', play: 'tr-TR', apple: 'tr' },
|
|
],
|
|
play: { packageName: 'com.example.tidylists' },
|
|
apple: { bundleId: 'com.example.tidylists' },
|
|
// Play Data safety answers (`storebound play data-safety --dry` prints the CSV Play imports). Lists sync to the account.
|
|
dataSafety: {
|
|
collectsData: true,
|
|
encryptedInTransit: true,
|
|
deletionRequest: true,
|
|
accountCreation: ['PSL_ACM_USER_ID_PASSWORD'],
|
|
data: {
|
|
PSL_EMAIL: { shared: false, collect: ['functionality', 'account'] },
|
|
PSL_USER_ACCOUNT: { shared: false, collect: ['functionality', 'account'] },
|
|
PSL_USER_GENERATED_CONTENT: { shared: false, collect: ['functionality'] },
|
|
PSL_CRASH_LOGS: { shared: false, collect: ['analytics'] },
|
|
},
|
|
},
|
|
// `storebound scan` findings that are true for this fictional demo and accepted on purpose (README.md)
|
|
scan: {
|
|
ignore: [
|
|
{ rule: 'demo-account', reason: 'fictional demo app without a store record: there is no backend to sign in to, so no App Review demo account' },
|
|
{ rule: 'play-app-access', reason: 'fictional demo app without a store record: no Play App access credentials' },
|
|
{ rule: 'play-package-name', reason: 'fictional demo app: com.example.* is the repo placeholder and is never uploaded' },
|
|
{ rule: 'play-testing-requirement', reason: 'fictional demo app without a Play developer account, so there is no account type to set' },
|
|
],
|
|
},
|
|
renders: 'store/renders',
|
|
shots: {
|
|
style: 'app',
|
|
copy: 'store/shots/copy.json',
|
|
frames: 'store/shots/frames',
|
|
out: 'store/renders',
|
|
scenes: [
|
|
{ id: 'lists', palette: 'mist', frames: [{ src: 'lists.png' }] },
|
|
{ id: 'check', palette: 'mint', frames: [{ src: 'list.png' }] },
|
|
{ id: 'share', palette: 'sky', frames: [{ src: 'share.png' }] },
|
|
{ id: 'account', palette: 'slate', frames: [{ src: 'account.png' }] },
|
|
],
|
|
feature: { frame: 'lists.png', palette: 'mist' },
|
|
capture: {
|
|
source: 'web',
|
|
baseUrl: 'http://127.0.0.1:8741',
|
|
query: 'lang={lang}',
|
|
shots: {
|
|
lists: { path: '/?screen=lists', waitFor: 'window.app && app.ready', delayMs: 100, device: 'both' },
|
|
list: { path: '/?screen=list', waitFor: 'window.app && app.ready', delayMs: 100, device: 'both' },
|
|
share: { path: '/?screen=share', waitFor: 'window.app && app.ready', delayMs: 100, device: 'both' },
|
|
account: { path: '/?screen=account', waitFor: 'window.app && app.ready', delayMs: 100, device: 'both' },
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
export default config;
|