137 lines
6.7 KiB
TypeScript
137 lines
6.7 KiB
TypeScript
// storebound config for one app (profile "app": a utility / productivity app with an optional account and an
|
|
// analytics SDK, no ads). Every answer below is a starting point: edit it to match your app. Credentials NEVER go
|
|
// here (env vars or .storebound/credentials.json, gitignored). Paths are relative to this file.
|
|
// The type import is optional: it only works when storebound is installed in the project (bun add -d storebound).
|
|
import type { ShipConfig } from 'storebound';
|
|
|
|
const config: ShipConfig = {
|
|
app: {
|
|
name: 'Example App',
|
|
profile: 'app',
|
|
copyright: '2026 Example Studio',
|
|
privacyPolicyUrl: 'https://example.com/privacy',
|
|
// termsUrl: 'https://example.com/terms', // Terms of Use; required once you sell subscriptions
|
|
supportUrl: 'https://example.com/support',
|
|
marketingUrl: 'https://example.com',
|
|
website: 'https://example.com',
|
|
contactEmail: 'support@example.com',
|
|
},
|
|
|
|
// Accounts (account-deletion skill). Remove this block if the app has no sign-up / sign-in at all.
|
|
// Apple 5.1.1(v): deletion must start inside the app. Google Play: in the app AND on a public web page.
|
|
accounts: {
|
|
enabled: true,
|
|
deletionUrl: 'https://example.com/delete-account', // public page, no login needed to request deletion
|
|
inApp: 'Settings > Account > Delete account', // goes into the App Review notes
|
|
// How people sign up: 'username' (a name picked in the app, no password, no email), 'username-password',
|
|
// 'email-password', 'email-link', 'phone', 'oauth', 'other'. `storebound scan` proposes the account data and
|
|
// dataSafety.accountCreation from it; only the 'email-*' methods mean an email address.
|
|
signUp: ['email-password'],
|
|
},
|
|
|
|
// Sign-in for store reviewers (review-access skill). The demo account itself lives ONLY in
|
|
// .storebound/credentials.json ("reviewDemoAccount") or REVIEW_DEMO_USER / REVIEW_DEMO_PASSWORD.
|
|
reviewAccess: {
|
|
signInRequired: true, // false if every feature works without signing in
|
|
instructions: 'Sign in with the demo account. Sample data is already there; nothing needs a second device.',
|
|
},
|
|
|
|
// id = your folder / copy key; play = Play language code(s); apple = App Store Connect locale
|
|
languages: [
|
|
{ id: 'en', play: 'en-US', apple: 'en-US' },
|
|
{ id: 'tr', play: 'tr-TR', apple: 'tr' },
|
|
],
|
|
|
|
play: {
|
|
packageName: 'com.example.app',
|
|
defaultLanguage: 'en-US',
|
|
listing: 'store/listing.md',
|
|
icon: 'store/icons/play-512.png',
|
|
},
|
|
|
|
apple: {
|
|
bundleId: 'com.example.app',
|
|
// name: 'Example: Short Tagline', // App Store name (max 30) when app.name is taken there ("already being used"); default app.name
|
|
listing: 'store/appstore.md',
|
|
// Export compliance (export-compliance skill): decide it, then uncomment. false ONLY if the app uses no encryption
|
|
// beyond the OS (HTTPS through the system, standard sign-in).
|
|
// usesNonExemptEncryption: false,
|
|
// ipad: true,
|
|
info: {
|
|
categories: { primary: 'PRODUCTIVITY', secondary: 'UTILITIES' },
|
|
ageRating: {}, // every content question NONE / false; change what applies (user-generated content, web access ...)
|
|
contentRights: 'DOES_NOT_USE_THIRD_PARTY_CONTENT',
|
|
free: true,
|
|
reviewNotes: [
|
|
'Example App keeps lists and notes in sync across devices. An account is optional on the first screen.',
|
|
'Privacy policy and Terms: Settings > About.',
|
|
].join('\n\n'),
|
|
},
|
|
},
|
|
|
|
// In-app purchases (optional): a one-time "Pro" unlock, for example. Subscriptions: see the subscription-app profile
|
|
// (storebound init --profile subscription-app) or the store-subscriptions skill.
|
|
// products: [{ id: 'com.example.app.pro', type: 'non_consumable', usd: 4.99,
|
|
// listings: { en: { title: 'Pro', description: 'Unlimited lists and themes.' } }, reviewScreenshot: 'store/iap-review/pro.png' }],
|
|
|
|
// Google Play Data safety. Types not listed = not collected. Typical app with accounts + an analytics SDK acting as
|
|
// your processor (collected, not shared). Edit to match what your app and its SDKs really do.
|
|
dataSafety: {
|
|
collectsData: true,
|
|
encryptedInTransit: true,
|
|
// Sign-up methods. Google does not publish these ids: compare with Play Console > App content > Data safety >
|
|
// Export to CSV before the first import. The delete-account URL (PSL_ACCOUNT_DELETION_URL) comes from
|
|
// accounts.deletionUrl; `storebound check` names any other row Play requires.
|
|
accountCreation: ['PSL_ACM_USER_ID_PASSWORD'],
|
|
// Can people delete their data without deleting the account (a data-only page)? true needs partialDeletionUrl.
|
|
// Play's "can users request deletion?" is answered yes by accounts.deletionUrl. Ask the owner.
|
|
partialDeletion: false,
|
|
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_USER_INTERACTION: { shared: false, collect: ['analytics'] },
|
|
PSL_CRASH_LOGS: { shared: false, collect: ['analytics'] },
|
|
PSL_PERFORMANCE_DIAGNOSTICS: { shared: false, collect: ['analytics'] },
|
|
PSL_DEVICE_ID: { shared: false, collect: ['analytics'] },
|
|
},
|
|
},
|
|
|
|
renders: 'store/renders',
|
|
|
|
shots: {
|
|
style: 'app', // calm gradient, device frame, headline + subline (game: sparkles and badges)
|
|
copy: 'store/shots/copy.json',
|
|
frames: 'store/shots/frames', // <lang>/phone/<name>.png, <lang>/tablet/<name>.png
|
|
out: 'store/renders',
|
|
// fonts: { title: 'Inter', body: 'Inter' }, // the app style default
|
|
scenes: [
|
|
{ id: 'home', palette: 'mist', frames: [{ src: 'home.png' }] },
|
|
{ id: 'detail', palette: 'mint', frames: [{ src: 'detail.png' }] },
|
|
{ id: 'share', palette: 'sky', frames: [{ src: 'share.png' }] },
|
|
{ id: 'privacy', palette: 'slate', frames: [{ src: 'settings.png' }] },
|
|
],
|
|
feature: { frame: 'home.png', palette: 'mist' },
|
|
// Raw frames from a web build, the iOS Simulator (ios-sim) or an Android emulator (android-emu): sign in to a demo
|
|
// account with realistic sample data, never real user data (store-screenshots skill).
|
|
capture: {
|
|
baseUrl: 'http://127.0.0.1:5173',
|
|
query: 'lang={lang}&demo=1',
|
|
shots: {
|
|
'home': { path: '/', delayMs: 1000, device: 'both' },
|
|
'detail': { path: '/lists/1', delayMs: 1000, device: 'both' },
|
|
'share': { path: '/lists/1/share', delayMs: 1000, device: 'both' },
|
|
'settings': { path: '/settings', delayMs: 1000, device: 'both' },
|
|
},
|
|
},
|
|
},
|
|
|
|
icons: {
|
|
composite: 'store/icon/icon.png',
|
|
foreground: 'store/icon/foreground.png',
|
|
background: 'store/icon/background.png',
|
|
out: 'store/icons',
|
|
},
|
|
};
|
|
|
|
export default config;
|