@crowdedkingdoms/crowdyjs 15.5.0 → 15.6.0-dev.1
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/MIGRATION.md +65 -0
- package/README.md +63 -44
- package/dist/default-origin.d.ts +6 -6
- package/dist/default-origin.d.ts.map +1 -1
- package/dist/default-origin.js +6 -6
- package/dist/domains/auth.d.ts +28 -6
- package/dist/domains/auth.d.ts.map +1 -1
- package/dist/domains/auth.js +18 -6
- package/dist/domains/portal.d.ts +97 -13
- package/dist/domains/portal.d.ts.map +1 -1
- package/dist/domains/portal.js +128 -11
- package/dist/generated/graphql.d.ts +17 -13
- package/dist/generated/graphql.d.ts.map +1 -1
- package/dist/generated/graphql.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/kit/npcs.d.ts.map +1 -1
- package/dist/kit/social.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/domains/portal.js
CHANGED
|
@@ -13,15 +13,29 @@
|
|
|
13
13
|
* client whose token store holds that game's app token.
|
|
14
14
|
*
|
|
15
15
|
* Browser handoff (cross-origin) is an OAuth2 Authorization-Code + PKCE flow:
|
|
16
|
-
* 1. game origin: `
|
|
17
|
-
* `/authorize` page carrying a PKCE
|
|
18
|
-
*
|
|
16
|
+
* 1. game origin: `signIn()` (or the lower-level `beginEntry()`) -> redirect
|
|
17
|
+
* the player to Studio's hosted `/authorize` page carrying a PKCE
|
|
18
|
+
* challenge + the game's redirect URI.
|
|
19
|
+
* 2. Studio origin (holds the session): `handleAuthorizeRequest()` ->
|
|
19
20
|
* mints a one-time code and redirects back to the game.
|
|
20
|
-
* 3. game origin: `
|
|
21
|
-
* it kept locally) for an app
|
|
21
|
+
* 3. game origin: `handleSignInCallback()` (or `completeEntry()`) ->
|
|
22
|
+
* exchanges the code (+ the PKCE verifier it kept locally) for an app
|
|
23
|
+
* token and stores it.
|
|
22
24
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
+
* THIS IS THE ONLY WAY A BROWSER GAME ON ITS OWN DOMAIN SIGNS A PLAYER IN
|
|
26
|
+
* (ck-api v1.88.0, 2026-09-08). The direct sign-in mutations behind
|
|
27
|
+
* `client.auth.login` / `register` / magic link / social are served only to
|
|
28
|
+
* first-party origins (Studio, the Crowdy Games host); from any other browser
|
|
29
|
+
* origin they are refused with `HOSTED_SIGN_IN_REQUIRED`
|
|
30
|
+
* ({@link isHostedSignInRequiredError}). The reason is the player's password:
|
|
31
|
+
* a form on a customer's domain that collects it is indistinguishable, to the
|
|
32
|
+
* platform and to the player, from a phishing page. Hosted sign-in means the
|
|
33
|
+
* password is only ever typed into Studio and the game receives a token
|
|
34
|
+
* confined to itself.
|
|
35
|
+
*
|
|
36
|
+
* Native / first-party / non-browser callers (no `Origin` header) can skip
|
|
37
|
+
* the redirect dance: sign in with `client.auth.*` and call
|
|
38
|
+
* `mintAppToken(appId)` directly with the session token.
|
|
25
39
|
*/
|
|
26
40
|
import { parse } from 'graphql';
|
|
27
41
|
import { generatePkcePair, generateState } from '../pkce.js';
|
|
@@ -65,6 +79,43 @@ const AuthorizeAppDocument = parse(`mutation AuthorizeApp($input: AuthorizeAppIn
|
|
|
65
79
|
const RevokeAppAuthorizationDocument = parse(`mutation RevokeAppAuthorization($appId: BigInt!) { revokeAppAuthorization(appId: $appId) }`);
|
|
66
80
|
const MyAuthorizedAppsDocument = parse(`query MyAuthorizedApps { myAuthorizedApps { grantId appId appName scopes status grantedAt revokedAt } }`);
|
|
67
81
|
const SetAppClientSettingsDocument = parse(`mutation SetAppClientSettings($input: SetAppClientSettingsInput!) { setAppClientSettings(input: $input) { appId appName trusted consentRequired } }`);
|
|
82
|
+
/**
|
|
83
|
+
* The hosted sign-in page for the tier a GraphQL endpoint belongs to.
|
|
84
|
+
*
|
|
85
|
+
* The CK API answers on `ck.<tier>.crowdedkingdoms.com` and Studio on
|
|
86
|
+
* `studio.<tier>.crowdedkingdoms.com` (infra-control-plane `dns-tier.ts`,
|
|
87
|
+
* `CK_STUDIO_PUBLIC_HOST_BY_TIER`), so the first label is the only difference;
|
|
88
|
+
* locally the API is on :3000 and Studio on :3001. Anything else has no
|
|
89
|
+
* convention to lean on and the caller must pass `authorizeUrl`.
|
|
90
|
+
*/
|
|
91
|
+
export function defaultHostedSignInUrl(graphqlEndpoint) {
|
|
92
|
+
const url = new URL(graphqlEndpoint);
|
|
93
|
+
const host = url.hostname.toLowerCase();
|
|
94
|
+
if (host === 'localhost' || host === '127.0.0.1') {
|
|
95
|
+
return `${url.protocol}//${host}:3001/authorize`;
|
|
96
|
+
}
|
|
97
|
+
if (host.startsWith('ck.')) {
|
|
98
|
+
return `https://studio.${host.slice('ck.'.length)}/authorize`;
|
|
99
|
+
}
|
|
100
|
+
throw new Error(`Cannot derive the hosted sign-in page from ${graphqlEndpoint}; pass authorizeUrl explicitly (the tier's Studio origin + /authorize).`);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* `HOSTED_SIGN_IN_REQUIRED`: a direct sign-in mutation (`auth.login`,
|
|
104
|
+
* `auth.register`, magic link, social, reset...) was called from a browser
|
|
105
|
+
* origin that is not first-party. The remedy is {@link PortalAPI.signIn}.
|
|
106
|
+
* ck-api v1.88.0.
|
|
107
|
+
*/
|
|
108
|
+
export function isHostedSignInRequiredError(error) {
|
|
109
|
+
const e = error;
|
|
110
|
+
const codes = [
|
|
111
|
+
e?.code,
|
|
112
|
+
e?.extensions?.code,
|
|
113
|
+
...(e?.graphQLErrors ?? []).map((g) => g.extensions?.code),
|
|
114
|
+
];
|
|
115
|
+
if (codes.includes('HOSTED_SIGN_IN_REQUIRED'))
|
|
116
|
+
return true;
|
|
117
|
+
return /HOSTED_SIGN_IN_REQUIRED|only available to first-party/i.test(typeof e?.message === 'string' ? e.message : '');
|
|
118
|
+
}
|
|
68
119
|
export class PortalAPI {
|
|
69
120
|
constructor(api, session, pkceStore = new BrowserSessionPkceStore()) {
|
|
70
121
|
this.api = api;
|
|
@@ -154,11 +205,58 @@ export class PortalAPI {
|
|
|
154
205
|
});
|
|
155
206
|
return data.setAppClientSettings;
|
|
156
207
|
}
|
|
208
|
+
// ----- Hosted sign-in (the browser game's whole flow, two calls) -----------
|
|
209
|
+
/**
|
|
210
|
+
* Send the player to Studio's hosted sign-in and come back with an app token.
|
|
211
|
+
*
|
|
212
|
+
* ```ts
|
|
213
|
+
* // on the "Sign in with Crowded Kingdoms" button:
|
|
214
|
+
* await client.portal.signIn({ appId, redirectUri: `${location.origin}/auth/callback` });
|
|
215
|
+
*
|
|
216
|
+
* // on /auth/callback (or unconditionally on boot -- it is a no-op without ?code=):
|
|
217
|
+
* const entered = await client.portal.handleSignInCallback();
|
|
218
|
+
* if (entered) start(entered); // client now holds an app-scoped token
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
221
|
+
* This is `beginEntry()` with the hosted page defaulted to the tier's Studio
|
|
222
|
+
* and the navigation done for you. It is the ONLY sign-in a browser game on
|
|
223
|
+
* its own domain can use: `client.auth.login` and friends are refused from a
|
|
224
|
+
* non-first-party origin with `HOSTED_SIGN_IN_REQUIRED`. Resolves to the URL
|
|
225
|
+
* navigated to (useful when `navigate: false`).
|
|
226
|
+
*/
|
|
227
|
+
async signIn(params) {
|
|
228
|
+
const authorizeUrl = params.authorizeUrl ?? defaultHostedSignInUrl(this.api.getEndpoint());
|
|
229
|
+
const url = await this.beginEntry({
|
|
230
|
+
appId: params.appId,
|
|
231
|
+
authorizeUrl,
|
|
232
|
+
redirectUri: params.redirectUri,
|
|
233
|
+
state: params.state,
|
|
234
|
+
});
|
|
235
|
+
const loc = globalThis
|
|
236
|
+
.location;
|
|
237
|
+
if (params.navigate !== false && typeof loc?.assign === 'function') {
|
|
238
|
+
loc.assign(url);
|
|
239
|
+
}
|
|
240
|
+
return url;
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* The return leg of {@link signIn}: exchange the `?code=` Studio sent back
|
|
244
|
+
* for an app-scoped token and store it on this client. Returns null when the
|
|
245
|
+
* URL carries no code, so it is safe to call on every boot. In a browser it
|
|
246
|
+
* also strips `code` and `state` from the address bar, so a reload does not
|
|
247
|
+
* try to spend a code that has already been used (a replay is refused).
|
|
248
|
+
*/
|
|
249
|
+
async handleSignInCallback(search) {
|
|
250
|
+
const result = await this.completeEntry(search);
|
|
251
|
+
if (result && search === undefined)
|
|
252
|
+
stripCodeFromLocation();
|
|
253
|
+
return result;
|
|
254
|
+
}
|
|
157
255
|
// ----- Browser PKCE redirect helpers -------------------------------------
|
|
158
256
|
/**
|
|
159
257
|
* Destination-game side, step 1: generate a PKCE pair, persist the verifier,
|
|
160
|
-
* and return the
|
|
161
|
-
* `window.location.assign(url)`.
|
|
258
|
+
* and return the hosted sign-in URL to navigate to. The caller does
|
|
259
|
+
* `window.location.assign(url)`. Prefer {@link signIn}, which does both.
|
|
162
260
|
*/
|
|
163
261
|
async beginEntry(params) {
|
|
164
262
|
const state = params.state ?? generateState();
|
|
@@ -173,9 +271,11 @@ export class PortalAPI {
|
|
|
173
271
|
return url.toString();
|
|
174
272
|
}
|
|
175
273
|
/**
|
|
176
|
-
*
|
|
274
|
+
* Studio (identity) side: handle an incoming `/authorize` request. Reads the
|
|
177
275
|
* game's params from the URL, mints a code with the session token, and returns
|
|
178
|
-
* the URL to redirect the player back to (carrying `code` + `state`).
|
|
276
|
+
* the URL to redirect the player back to (carrying `code` + `state`). Studio's
|
|
277
|
+
* own `/authorize` page is the production caller; a self-hosted identity page
|
|
278
|
+
* may use it too.
|
|
179
279
|
*/
|
|
180
280
|
async handleAuthorizeRequest(search, options) {
|
|
181
281
|
const params = new URLSearchParams(search ?? defaultSearch());
|
|
@@ -234,3 +334,20 @@ function defaultSearch() {
|
|
|
234
334
|
const loc = globalThis.location;
|
|
235
335
|
return loc?.search ?? '';
|
|
236
336
|
}
|
|
337
|
+
/** Remove `code` and `state` from the address bar without a navigation. */
|
|
338
|
+
function stripCodeFromLocation() {
|
|
339
|
+
const g = globalThis;
|
|
340
|
+
if (!g.location?.href || typeof g.history?.replaceState !== 'function')
|
|
341
|
+
return;
|
|
342
|
+
try {
|
|
343
|
+
const url = new URL(g.location.href);
|
|
344
|
+
if (!url.searchParams.has('code'))
|
|
345
|
+
return;
|
|
346
|
+
url.searchParams.delete('code');
|
|
347
|
+
url.searchParams.delete('state');
|
|
348
|
+
g.history.replaceState(null, '', url.toString());
|
|
349
|
+
}
|
|
350
|
+
catch {
|
|
351
|
+
// Not a parseable location (tests, exotic embeds): nothing to strip.
|
|
352
|
+
}
|
|
353
|
+
}
|
|
@@ -5116,17 +5116,17 @@ export type Mutation = {
|
|
|
5116
5116
|
cancelSharedSubscription: AppSharedSubscription;
|
|
5117
5117
|
/** Captures an approved PayPal order after the hosted checkout redirects back, completes the checkout (wallet credit / access grant), and returns the updated Checkout. PayPal webhooks remain a backup for idempotent reconciliation if they arrive later. Requires an authenticated user who owns the checkout. */
|
|
5118
5118
|
capturePaypalCheckout: Checkout;
|
|
5119
|
-
/** Changes the authenticated user's password after verifying the current password. Requires a valid session token. Returns true on success. Refuses with extensions.code INVALID_CURRENT_PASSWORD (403) when the current password is wrong, and PASSWORD_NOT_SET (409) when the account has no password to change — use setInitialPassword for that, which needs only the session. NEITHER of those means the session is invalid, and neither is UNAUTHENTICATED: both were until v1.60.0, so a client that signs the user out on UNAUTHENTICATED was signing them out over a typo.
|
|
5119
|
+
/** Changes the authenticated user's password after verifying the current password. Requires a valid session token. Returns true on success. Refuses with extensions.code INVALID_CURRENT_PASSWORD (403) when the current password is wrong, and PASSWORD_NOT_SET (409) when the account has no password to change — use setInitialPassword for that, which needs only the session. NEITHER of those means the session is invalid, and neither is UNAUTHENTICATED: both were until v1.60.0, so a client that signs the user out on UNAUTHENTICATED was signing them out over a typo. Every OTHER session for the account is revoked (with the app tokens minted from it); the session that made this call stays valid, so the client does not need to sign in again. */
|
|
5120
5120
|
changePassword: Scalars['Boolean']['output'];
|
|
5121
5121
|
/** Self-service: the authenticated caller claims access to an app via its free, open-by-default tier. Requires authentication only (no org membership needed). ENTITLEMENT CHANGE: grants the free default tier as a 'system' grant and notifies the game API. Idempotent: returns the existing row if already granted, and never overrides a prior revoke. Errors if the app has no free default tier or is archived. */
|
|
5122
5122
|
claimFreeAppAccess: AppUserAccess;
|
|
5123
|
-
/** Claim one currently unclaimed chunk as a new player-owned grid. Requires an ordinary app-scoped player token and active app access, but never manage_apps. The app's policy must be SELF_CLAIM. The server validates the app's grid assignment and peer-overlap rules, then atomically creates a one-chunk grid, assigns current-user ownership, grants access/update_voxel_data/use_voice_chat/teleport plus player-code keys
|
|
5123
|
+
/** Claim one currently unclaimed chunk as a new player-owned grid. Requires an ordinary app-scoped player token and active app access, but never manage_apps. The app's policy must be SELF_CLAIM. The server validates the app's grid assignment and peer-overlap rules, then atomically creates a one-chunk grid, assigns current-user ownership, grants access/update_voxel_data/use_voice_chat/teleport plus the player-code keys and use_video_chat where the caller's tier already carries them, and materializes the effective ACL. Conflicts and policy denials throw GraphQL errors; no partial grid, ownership, or grant rows remain. */
|
|
5124
5124
|
claimGridChunk: ChunkClaimResult;
|
|
5125
5125
|
/** Claim grid ownership under the app's claim policy (D4, server-authorized — no client manage_apps involved). SELF_CLAIM assigns ownership immediately; APPROVAL creates a pending request for designated approvers; INVITE requires a standing invite (consumed on use); MARKETPLACE_ONLY refuses (ownership arrives only via grid purchase, P4b). The grid must exist and have no current owner; game rules gate who may attempt a claim. */
|
|
5126
5126
|
claimGridOwnership: GridClaimResult;
|
|
5127
5127
|
/** Remove an app's compute allowance, returning it to observation against the platform reference allowance. Returns true if an allowance was removed. Requires app-admin ('manage_apps'). */
|
|
5128
5128
|
clearAppComputeBudget: Scalars['Boolean']['output'];
|
|
5129
|
-
/** Complete a magic-link sign-in with the emailed token; returns a session AuthResponse. Public (the token authorizes the call); throws if invalid/expired/used. */
|
|
5129
|
+
/** Complete a magic-link sign-in with the emailed token; returns a session AuthResponse. Public (the token authorizes the call); throws if invalid/expired/used. First-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise). */
|
|
5130
5130
|
completeLoginLink: AuthResponse;
|
|
5131
5131
|
/** Delete a compute module and (via cascade) its versions, triggers, and lease. Run history is retained for auditing. Returns true when a module was deleted. Requires the org 'manage_compute' permission. */
|
|
5132
5132
|
computeDeleteModule: Scalars['Boolean']['output'];
|
|
@@ -5148,7 +5148,7 @@ export type Mutation = {
|
|
|
5148
5148
|
computeUpsertModule: WasmModule;
|
|
5149
5149
|
/** Bind a trigger to a compute module: a tick loop (tickHz, clamped by policy), an event subscription (model or compute events), or a client-invokable export (with an optional invoke policy). Requires the org 'manage_compute' permission. */
|
|
5150
5150
|
computeUpsertTrigger: WasmModuleTrigger;
|
|
5151
|
-
/** Confirms a user email address using the token from the confirmation email (also enables password sign-in for the account). Returns true on success, false if the token is invalid or expired. Public (the token authorizes the call). */
|
|
5151
|
+
/** Confirms a user email address using the token from the confirmation email (also enables password sign-in for the account). Returns true on success, false if the token is invalid or expired. Public (the token authorizes the call); first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise). */
|
|
5152
5152
|
confirmEmail: Scalars['Boolean']['output'];
|
|
5153
5153
|
/** Open the UDP proxy session for this game token (idempotent: returns the existing status if one is already open). Binds a socket and selects the game server with the fewest clients on first open. Optional: send mutations and udpNotifications also create a session lazily when none exists. To force a fresh socket, call disconnectUdpProxy first. */
|
|
5154
5154
|
connectUdpProxy: UdpProxyConnectionStatus;
|
|
@@ -5364,7 +5364,7 @@ export type Mutation = {
|
|
|
5364
5364
|
leaveTeam: Scalars['Boolean']['output'];
|
|
5365
5365
|
/** Link an additional federated identity (from a socialLoginStart callback) to the signed-in account. Requires a session token; throws if the identity is already linked to another account. */
|
|
5366
5366
|
linkIdentity: UserIdentity;
|
|
5367
|
-
/** Authenticates with email + password and starts a new session. Returns an AuthResponse whose `token` must be sent on subsequent requests as `Authorization: Bearer <token>`. Public (no auth required); throws on invalid credentials. If the account also has another verified sign-in method, the password must first be email-confirmed. */
|
|
5367
|
+
/** Authenticates with email + password and starts a new session. Returns an AuthResponse whose `token` must be sent on subsequent requests as `Authorization: Bearer <token>`. Public (no auth required); throws on invalid credentials. If the account also has another verified sign-in method, the password must first be email-confirmed. First-party origins only: a browser page on any other origin is refused with HOSTED_SIGN_IN_REQUIRED (403) and must use the hosted sign-in redirect (CrowdyJS portal.signIn) instead; requests with no Origin header (native clients, scripts) are unaffected. Rate-limited per address and per client. */
|
|
5368
5368
|
login: AuthResponse;
|
|
5369
5369
|
/** Ends the current session by deleting the game_token that authenticated this request; other devices stay logged in. An identity session logout also cascades to (revokes) every app token it minted. Returns false if no token was resolved. */
|
|
5370
5370
|
logout: Scalars['Boolean']['output'];
|
|
@@ -5408,7 +5408,7 @@ export type Mutation = {
|
|
|
5408
5408
|
refreshAppToken: AppTokenResponse;
|
|
5409
5409
|
/** Request a refund of a paid acquisition (P4b). Allowed only within the refund window and before meaningful use (first install/fetch voids it), capped per buyer; a successful refund credits the wallet, reverses the ledger split, claws back the seller balance, revokes the acquisition, and drains installs. Returns cents refunded. */
|
|
5410
5410
|
refundPlayerCodeAcquisition: Scalars['Int']['output'];
|
|
5411
|
-
/** Registers a new email + password account: creates the (initially unconfirmed) account, emails a confirmation link, and returns an AuthResponse with a session `token` for immediate use (send as `Authorization: Bearer <token>`). If an account already exists for the email
|
|
5411
|
+
/** Registers a new email + password account: creates the (initially unconfirmed) account, emails a confirmation link, and returns an AuthResponse with a session `token` for immediate use (send as `Authorization: Bearer <token>`). If an account already exists for the email it is refused with extensions.code EMAIL_ALREADY_REGISTERED (409) and no session is returned: an account that already has a password is left exactly as it was (sign in, or use the emailed reset), and only a password-less account (created via magic link/social) gets the password attached pending email confirmation. It is a routine outcome rather than a fault, and it reached clients as INTERNAL_SERVER_ERROR before v1.60.0, which is why several of them match it by its message text. Public; first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise); rate-limited. */
|
|
5412
5412
|
register: AuthResponse;
|
|
5413
5413
|
/** OPERATOR ONLY. Reverses a retirement: the organization returns to status 'active', deleted_at is cleared, and the tombstone records who put it back and why rather than being deleted. Its apps are LEFT ARCHIVED — un-archiving is archiveApp's inverse and belongs to whoever decides which apps should serve traffic again. Refuses an organization that is not currently retired. */
|
|
5414
5414
|
reinstateOrganization: OrgRetirementType;
|
|
@@ -5424,9 +5424,9 @@ export type Mutation = {
|
|
|
5424
5424
|
removeTeamMember: Scalars['Boolean']['output'];
|
|
5425
5425
|
/** Renew a RENT acquisition (or extend a TIME_LIMITED window): a wallet charge on the same acquisition that pushes its expiry out. A drained install resumes on the next scheduler pass without re-consent. Paid modes only (P4b). */
|
|
5426
5426
|
renewPlayerCodeAcquisition: PlayerCodeAcquisition;
|
|
5427
|
-
/** Passwordless: email a one-time magic sign-in link to the address (creates the account on first sign-in). Always reports sent=true (no account enumeration). Public. */
|
|
5427
|
+
/** Passwordless: email a one-time magic sign-in link to the address (creates the account on first sign-in). Always reports sent=true (no account enumeration). Public; first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise); rate-limited. */
|
|
5428
5428
|
requestLoginLink: RequestLoginLinkResult;
|
|
5429
|
-
/** Starts the password-reset flow by emailing a reset link to the address. Always returns true regardless of whether the email exists (prevents account enumeration). The reset link is also the ownership-proven way an existing passwordless account adds a password. Public. */
|
|
5429
|
+
/** Starts the password-reset flow by emailing a reset link to the address. Always returns true regardless of whether the email exists (prevents account enumeration). The reset link is also the ownership-proven way an existing passwordless account adds a password. Public; first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise); rate-limited. */
|
|
5430
5430
|
requestPasswordReset: Scalars['Boolean']['output'];
|
|
5431
5431
|
/** Pay out the calling player's payable balance to their Connect account (D6 minimum, 7-day delay, reserves enforced). Returns cents paid. */
|
|
5432
5432
|
requestSellerPayout: Scalars['Int']['output'];
|
|
@@ -5434,9 +5434,9 @@ export type Mutation = {
|
|
|
5434
5434
|
requestToJoinChannel: GroupMember;
|
|
5435
5435
|
/** Request to join a request-only team (creates a pending membership a manager can approve via addTeamMember). Behaves identically to joinTeam; named for request-policy UIs. */
|
|
5436
5436
|
requestToJoinTeam: GroupMember;
|
|
5437
|
-
/** Re-sends the email-confirmation link. Always returns true regardless of whether the account exists or is already confirmed (prevents enumeration); the email is only sent for existing unconfirmed accounts. Public. */
|
|
5437
|
+
/** Re-sends the email-confirmation link. Always returns true regardless of whether the account exists or is already confirmed (prevents enumeration); the email is only sent for existing unconfirmed accounts. Public; first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise); rate-limited. */
|
|
5438
5438
|
resendConfirmationEmail: Scalars['Boolean']['output'];
|
|
5439
|
-
/** Completes a password reset using the reset token and a new password. Returns true on success; throws if the token is invalid or expired. Public (the token authorizes the call).
|
|
5439
|
+
/** Completes a password reset using the reset token and a new password. Returns true on success; throws if the token is invalid or expired. Public (the token authorizes the call); first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise). EVERY existing session for the account is revoked (and every app token minted from one): a reset is what an owner does after losing control of the account, so whoever held a session loses it. The user signs in again with the new password. */
|
|
5440
5440
|
resetPassword: Scalars['Boolean']['output'];
|
|
5441
5441
|
/** OPERATOR ONLY. Retires an organization: sets organizations.status to 'retired', stamps deleted_at, archives its apps, and writes a tombstone to org_retirements. RETIREMENT IS A STATE, NOT A DELETION — no wallet_transactions, org_billing_waivers, app_shared_usage_charges or other ledger row is altered or removed, so an auditor can still reconstruct exactly what this organization spent, with the same joins as before, indefinitely. There is no purge and no retention window, by decision rather than by omission: org ledger rows are retained forever (operator decision, 2026-08-21), so retirement is only ever a state. A retired organization's remaining wallet balance is FROZEN indefinitely by the same decision — held, not refunded and not forfeited — and the amount is recorded on the tombstone. AFTER RETIREMENT the org's API tokens stop authenticating, its members lose every org permission (super admins excepted, so this is reversible), and it is excluded from the caller's organization list — but it is still readable by id and slug, because a retired org that answers like a missing one is worse than one that says what it is. Refuses unless expectedSlug matches the org named by orgId, and refuses an organization holding money unless acknowledgeFrozenBalance is passed. Reverse it with reinstateOrganization. */
|
|
5442
5442
|
retireOrganization: OrgRetirementType;
|
|
@@ -5536,9 +5536,9 @@ export type Mutation = {
|
|
|
5536
5536
|
setTeamPolicy: AppGroupPolicy;
|
|
5537
5537
|
/** Begins vaulting a card for off-session auto-billing. Returns a Stripe SetupIntent client secret the browser confirms; no charge is made here. Requires the 'manage_billing' org permission. */
|
|
5538
5538
|
setupSharedPaymentMethod: PaymentMethodSetup;
|
|
5539
|
-
/** Complete a federated sign-in from the provider callback (code + state). Returns a session AuthResponse, creating/linking the account by provider identity. Public. */
|
|
5539
|
+
/** Complete a federated sign-in from the provider callback (code + state). Returns a session AuthResponse, creating/linking the account by provider identity. Public; first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise). */
|
|
5540
5540
|
socialLoginComplete: AuthResponse;
|
|
5541
|
-
/** Begin a federated (social) sign-in: returns an authorizeUrl to redirect the user to and an opaque state to round-trip back to socialLoginComplete. Public. */
|
|
5541
|
+
/** Begin a federated (social) sign-in: returns an authorizeUrl to redirect the user to and an opaque state to round-trip back to socialLoginComplete. Public; first-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise). */
|
|
5542
5542
|
socialLoginStart: SocialLoginStart;
|
|
5543
5543
|
/** Earn-to-mod: convert payable seller balance into the player wallet without a provider round-trip (07 §4.1). Returns cents credited. */
|
|
5544
5544
|
spendPayoutBalanceToWallet: Scalars['Int']['output'];
|
|
@@ -7745,7 +7745,7 @@ export type Query = {
|
|
|
7745
7745
|
channelRoles: Array<GroupRole>;
|
|
7746
7746
|
/** List all active channels in an app (not just the caller's). */
|
|
7747
7747
|
channels: Array<Group>;
|
|
7748
|
-
/** Email-first adaptive login: check whether the account has password sign-in enabled. Public
|
|
7748
|
+
/** Email-first adaptive login: check whether the account has password sign-in enabled. Public. It answers true only for an account that has a password, so it does reveal that such an account exists; unknown addresses and password-less accounts both answer false. First-party origins only (HOSTED_SIGN_IN_REQUIRED otherwise); rate-limited per address and per client. */
|
|
7749
7749
|
checkAuthMethod: AuthMethodResult;
|
|
7750
7750
|
/** Cross-tenant payments audit across all users, orgs, and apps (newest first), with optional filtering. Restricted to super admins; requests from non-super-admins are rejected. For a caller's own history use `myCheckouts` instead. */
|
|
7751
7751
|
checkouts: CheckoutsPage;
|
|
@@ -11553,6 +11553,10 @@ export type AppQuery = {
|
|
|
11553
11553
|
runtimeStatus: string;
|
|
11554
11554
|
runtimeDenialReason: string | null;
|
|
11555
11555
|
gameApiUrl: string | null;
|
|
11556
|
+
launchUrl: string | null;
|
|
11557
|
+
redirectUris: Array<string>;
|
|
11558
|
+
isTrusted: boolean;
|
|
11559
|
+
clientType: string;
|
|
11556
11560
|
createdAt: string;
|
|
11557
11561
|
updatedAt: string;
|
|
11558
11562
|
org: {
|