glitch-javascript-sdk 3.10.8 → 3.15.0
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 +7 -1
- package/dist/browser/hosting-runtime.js +12 -2
- package/dist/browser/hosting-runtime.js.map +1 -1
- package/dist/cjs/index.js +871 -6
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/api/FestivalNetworking.d.ts +286 -0
- package/dist/esm/api/Messages.d.ts +4 -1
- package/dist/esm/api/Microtransactions.d.ts +554 -0
- package/dist/esm/api/index.d.ts +2 -0
- package/dist/esm/index.d.ts +8 -0
- package/dist/esm/index.js +761 -4
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/routes/FestivalNetworkingRoute.d.ts +7 -0
- package/dist/esm/routes/MicrotransactionsRoute.d.ts +8 -0
- package/dist/esm/util/MicrotransactionBridge.d.ts +93 -0
- package/dist/esm/util/MicrotransactionOverlay.d.ts +59 -0
- package/dist/esm/util/Requests.d.ts +5 -3
- package/dist/index.d.ts +1003 -5
- package/guides/microtransactions.md +385 -0
- package/package.json +7 -4
- package/src/api/FestivalNetworking.ts +216 -0
- package/src/api/Messages.ts +4 -1
- package/src/api/Microtransactions.ts +509 -0
- package/src/api/index.ts +2 -0
- package/src/index.ts +8 -0
- package/src/routes/FestivalNetworkingRoute.ts +33 -0
- package/src/routes/MicrotransactionsRoute.ts +46 -0
- package/src/util/MicrotransactionBridge.ts +193 -0
- package/src/util/MicrotransactionOverlay.ts +302 -0
- package/src/util/Requests.ts +14 -2
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
# Game microtransactions
|
|
2
|
+
|
|
3
|
+
**Minimum SDK for this guide: `3.15.0`.** Use a confirmed published `3.15.0+`
|
|
4
|
+
release or an approved local package. Registry verification on September 15, 2026
|
|
5
|
+
showed public latest `3.10.8`, which lacks commerce; a plain public npm install at
|
|
6
|
+
that point cannot run this callback/history example. Until `3.15.0` is actually
|
|
7
|
+
published, use only the reviewed local `glitch-javascript-sdk-3.15.0.tgz` for testing.
|
|
8
|
+
Do not change a production dependency to an unpublished version or assume that
|
|
9
|
+
local build success means public publication has happened.
|
|
10
|
+
|
|
11
|
+
The SDK entry point is `Glitch.api.Microtransactions`. All HTTP methods return an
|
|
12
|
+
Axios response whose `response.data.data` contains the typed commerce result.
|
|
13
|
+
Configure the ordinary API base once using the existing Glitch configuration.
|
|
14
|
+
Never put a developer/MCP/install token into a shipped browser game.
|
|
15
|
+
|
|
16
|
+
## Developer setup
|
|
17
|
+
|
|
18
|
+
1. Read `settings`, `readiness`, `providers` and `listProducts` with a signed-in
|
|
19
|
+
title administrator. Start disabled/sandbox. Approvals are server-controlled.
|
|
20
|
+
The **Enable in-game purchases** and **Show ads** switches live only on the
|
|
21
|
+
game's Pricing/monetization page. Microtransactions is for products, media,
|
|
22
|
+
prices, orders and integration—not a second location for those switches.
|
|
23
|
+
2. `uploadMedia(titleId, file, onProgress?)` reuses existing Glitch Media with
|
|
24
|
+
trusted title/actor ownership. Attach returned `id` to `media_ids` or
|
|
25
|
+
`branding.logo_media_id`. No scheduler or social-library post is created.
|
|
26
|
+
3. Create a product draft using `createProduct`. Prices use integer minor units,
|
|
27
|
+
not floating-point money: USD 499 means $4.99; JPY 499 means ¥499. Supported
|
|
28
|
+
currencies are USD/EUR/GBP/CAD/AUD/JPY/BRL/INR/KRW; provider coverage can be
|
|
29
|
+
narrower. Each price is 1–100000 minor units, with country `US` or `*` fallback.
|
|
30
|
+
Provider-specific purchase minima are checked separately (sandbox USD: 50).
|
|
31
|
+
4. Product types are durable, consumable, currency, bundle or pass. Grants have
|
|
32
|
+
a stable key, quantity and kind. Durable quantity is one. Pass requires
|
|
33
|
+
`duration_seconds` between 60 and 31536000, with no active stacking. Existing
|
|
34
|
+
purchased grants cannot be changed in place: create a new SKU. No recurring
|
|
35
|
+
subscriptions, gifts, paid random loot, cash-out or cross-game wallet support.
|
|
36
|
+
5. Configure the exact game origin, regions/currencies, support contact and
|
|
37
|
+
game name/accent/logo with `updateSettings`. The fixed commission is 1200bp
|
|
38
|
+
(12%) of discounted pre-tax subtotal. Actual provider costs are separate.
|
|
39
|
+
Taxes are separate; pending earnings are not a verified available payout.
|
|
40
|
+
6. Run a real approved provider sandbox purchase, verify game delivery/claim,
|
|
41
|
+
then call `verifyIntegration(titleId, {order_id, confirm:true})`. This records
|
|
42
|
+
real evidence, not a self-certified integration checkbox. Launch still needs
|
|
43
|
+
independent seller/tax/provider approvals. MCP confirm alone cannot supply them.
|
|
44
|
+
|
|
45
|
+
Product limits: SKU/grant key 1–100 alphanumeric/underscore/dot/hyphen characters;
|
|
46
|
+
name 255 characters; description 4000; 10 distinct Media UUIDs; 50 prices; 30
|
|
47
|
+
distinct grants; 30 localizations. Settings allow 20 exact origins (255 chars
|
|
48
|
+
each), 100 countries and 20 supported currencies. Branding name maximum 100.
|
|
49
|
+
Server capability schemas remain authoritative.
|
|
50
|
+
|
|
51
|
+
Required product fields are **SKU\***, **Name\***, **Type\***, **Prices\*** and
|
|
52
|
+
**Grants\***; each price needs currency, country and integer minor-unit amount,
|
|
53
|
+
and each grant needs key, quantity and kind. A pass also needs duration in seconds.
|
|
54
|
+
For a pack of **100 Timber** spent building things, choose product type `currency`
|
|
55
|
+
(or `consumable`) and grant `{key:'timber',quantity:100,kind:'consumable'}`. A durable
|
|
56
|
+
grant means lasting ownership and cannot be spent; it is wrong for building Timber.
|
|
57
|
+
|
|
58
|
+
## Hosted checkout and account creation
|
|
59
|
+
|
|
60
|
+
`createCheckoutSession(titleId, {product_id,quantity,country,currency,environment,
|
|
61
|
+
channel:'web',return_origin,nonce})` is anonymous-safe and only opens the purchase
|
|
62
|
+
flow. Use `createMicrotransactionNonce()` to generate the nonce and retain it in
|
|
63
|
+
the game. `hosted_url` points to Glitch's game-branded checkout with the session
|
|
64
|
+
secret in a `#token` fragment, never a query parameter.
|
|
65
|
+
|
|
66
|
+
Use `openMicrotransactionOverlay` to mount an accessible modal iframe inside the
|
|
67
|
+
running game, even if the game is itself embedded. The game document, URL,
|
|
68
|
+
session, canvas and state remain intact. Do not navigate the game away for checkout
|
|
69
|
+
or provide a top-level fallback. The iframe permits payments and controlled
|
|
70
|
+
bank/OAuth verification windows but has no top-navigation sandbox permission.
|
|
71
|
+
When embedding or required verification is unavailable, offer retry/close while
|
|
72
|
+
preserving the game. Retry reloads only the same session, not a new charge.
|
|
73
|
+
|
|
74
|
+
The overlay bounds each document-loading and application-ready wait to 20 seconds
|
|
75
|
+
by default (`frameLoadTimeoutMs`, clamped to 1–60 seconds). A missing iframe load
|
|
76
|
+
or error event cannot leave the player indefinitely at Loading. Timeout displays
|
|
77
|
+
explicit Retry/Close guidance without initiating payment or navigating the game.
|
|
78
|
+
A document `load` only starts the bounded application-ready wait; it is not proof
|
|
79
|
+
that checkout rendered. The hosted page sends
|
|
80
|
+
`{type:'glitch.microtransaction.ready',version:1,title_id,checkout_session_id,nonce}`
|
|
81
|
+
only after its valid-session account/checkout UI is usable. Exact origin/source/
|
|
82
|
+
session/nonce checks protect this signal, and it never grants inventory. Retry
|
|
83
|
+
resets timers; verified readiness and close clear them.
|
|
84
|
+
|
|
85
|
+
The read-only `getCheckoutFramePolicy(titleId,sessionId)` returns only approved
|
|
86
|
+
`frame_ancestors` and expiry; the hosted document uses that server-owned policy.
|
|
87
|
+
It does not expose a checkout token or player data.
|
|
88
|
+
|
|
89
|
+
Inside Glitch, the page uses existing sign-in/registration/OAuth and binds the
|
|
90
|
+
checkout session once to that user. Session methods require
|
|
91
|
+
`{checkoutToken: sessionSecret}` as their last options argument. The existing
|
|
92
|
+
signed-in account JWT stays inside Glitch, not the game. The SDK sends the limited
|
|
93
|
+
session secret only in `X-Checkout-Token` and never temporarily changes global auth.
|
|
94
|
+
|
|
95
|
+
`checkout(titleId, sessionId, {idempotency_key,accept_terms:true}, options)` returns
|
|
96
|
+
an order, quote, provider and UI mode. Stripe uses `ui_mode:'embedded'`, a limited
|
|
97
|
+
`client_secret` and public `publishable_key`; mount Stripe Embedded Checkout
|
|
98
|
+
inside the branded page, never build custom raw card fields. Approved Xsolla
|
|
99
|
+
routes use `ui_mode:'xsolla'` and its official Pay Station flow. Disclose payment
|
|
100
|
+
methods that need external authentication.
|
|
101
|
+
|
|
102
|
+
Stripe 3DS is part of checkout. Exercise challenge success, cancel/failure,
|
|
103
|
+
timeout and reload in the real sandbox. Keep the same order/session/idempotency
|
|
104
|
+
key through authentication. `action_required`, provider authorization, redirect,
|
|
105
|
+
and client completion are not paid/delivered proof. Call
|
|
106
|
+
`reconcileCheckoutSession(titleId, sessionId, options)` to query the original
|
|
107
|
+
attempt; never charge another provider after an uncertain submission.
|
|
108
|
+
|
|
109
|
+
## Verified game/account handoff
|
|
110
|
+
|
|
111
|
+
After verified payment and fulfillment, `createHandoff` returns an event with
|
|
112
|
+
`type:'glitch.microtransaction.updated'`, `version:1`, `title_id`,
|
|
113
|
+
`checkout_session_id`, `order_id`, `nonce`, and a one-time `claim_code` valid for
|
|
114
|
+
two minutes. The hosted page targets the exact approved game origin. It never
|
|
115
|
+
posts the account JWT or a player token.
|
|
116
|
+
|
|
117
|
+
The SDK does the secure return work for you: it checks the actual checkout iframe,
|
|
118
|
+
origin, title, session and nonce, exchanges the one-time claim with Glitch, and
|
|
119
|
+
then calls **your `onVerified` function with the server-verified result**. This is
|
|
120
|
+
where your game connects the player account and displays the purchased inventory.
|
|
121
|
+
You do not need to write your own `message` listener or grant items from a browser
|
|
122
|
+
event. Closing checkout without a successful claim does not call this a purchase.
|
|
123
|
+
|
|
124
|
+
### Minimal working Timber shop
|
|
125
|
+
|
|
126
|
+
Copy this JavaScript module into your game's browser bundle. Call
|
|
127
|
+
`installTimberShop` with the title/product IDs and API/checkout origins shown by
|
|
128
|
+
your game integration settings. For local testing use your approved local origins
|
|
129
|
+
and `allowLocalDevelopment:true`; for production use approved HTTPS endpoints.
|
|
130
|
+
The example creates its own small UI, so no undefined HTML elements or game helper
|
|
131
|
+
functions are required. It never takes the player away from the game.
|
|
132
|
+
|
|
133
|
+
```js
|
|
134
|
+
import Glitch, {
|
|
135
|
+
createMicrotransactionNonce,
|
|
136
|
+
openMicrotransactionOverlay,
|
|
137
|
+
openMicrotransactionRestoreOverlay,
|
|
138
|
+
} from 'glitch-javascript-sdk';
|
|
139
|
+
|
|
140
|
+
export function installTimberShop({ titleId, productId, apiBaseUrl, checkoutOrigin,
|
|
141
|
+
environment = 'sandbox', allowLocalDevelopment = false }) {
|
|
142
|
+
Glitch.util.Requests.setBaseUrl(apiBaseUrl); // Set API location, NOT global auth.
|
|
143
|
+
const api = Glitch.api.Microtransactions;
|
|
144
|
+
const game = { playerId: null, inventory: [], paused: false };
|
|
145
|
+
let playerToken = '', tokenExpiresAt = 0, overlay = null;
|
|
146
|
+
let opening = false, spending = false, pendingUse = null;
|
|
147
|
+
const shop = document.createElement('section');
|
|
148
|
+
const status = document.createElement('p'); status.setAttribute('role', 'status');
|
|
149
|
+
const timber = document.createElement('output');
|
|
150
|
+
const history = document.createElement('pre');
|
|
151
|
+
shop.append(timber, status, history); document.body.append(shop);
|
|
152
|
+
const say = text => { status.textContent = text; };
|
|
153
|
+
|
|
154
|
+
function replaceInventoryInYourGame(entitlements) {
|
|
155
|
+
game.inventory = entitlements; // REPLACE the snapshot. Never add 100 here.
|
|
156
|
+
timber.textContent = 'Timber: ' + (entitlements.find(x => x.key === 'timber')?.balance ?? 0);
|
|
157
|
+
}
|
|
158
|
+
function setGamePaused(paused) { game.paused = paused; }
|
|
159
|
+
function onVerified(result) {
|
|
160
|
+
game.playerId = result.player_id; // Associate the game's profile with this player.
|
|
161
|
+
playerToken = result.player_token; // Memory only; never a URL or global auth token.
|
|
162
|
+
tokenExpiresAt = Date.parse(result.expires_at);
|
|
163
|
+
replaceInventoryInYourGame(result.entitlements); // Already verified by Glitch.
|
|
164
|
+
say('Account connected. Your current inventory is ready.');
|
|
165
|
+
}
|
|
166
|
+
function playerOptions() {
|
|
167
|
+
if (!playerToken || Date.now() >= tokenExpiresAt) {
|
|
168
|
+
throw new Error('Player authentication is required; restore needs an active purchase.');
|
|
169
|
+
}
|
|
170
|
+
return { playerToken }; // Per-request token; never choose a user_id.
|
|
171
|
+
}
|
|
172
|
+
function showError(error) {
|
|
173
|
+
const code = error?.response?.data?.code ?? error?.code ?? error?.response?.data?.message;
|
|
174
|
+
const noRestore = code === 'no_purchases_to_restore'
|
|
175
|
+
|| error?.response?.data?.message === 'No active purchases to restore for this game/account/environment.';
|
|
176
|
+
say(noRestore
|
|
177
|
+
? 'No active purchases to restore. Refunded receipt history still belongs to this account, but no new game token or items can be granted here.'
|
|
178
|
+
: 'Not completed. Retry the same action. If sign-in expired, authenticate again; restore requires an active purchase.');
|
|
179
|
+
}
|
|
180
|
+
async function openShop(restore) {
|
|
181
|
+
if (opening || overlay) return;
|
|
182
|
+
opening = true;
|
|
183
|
+
try {
|
|
184
|
+
const context = { return_origin: window.location.origin,
|
|
185
|
+
nonce: createMicrotransactionNonce(), environment };
|
|
186
|
+
const response = restore
|
|
187
|
+
? await api.createRestoreSession(titleId, context)
|
|
188
|
+
: await api.createCheckoutSession(titleId, { ...context, product_id: productId,
|
|
189
|
+
quantity: 1, country: 'US', currency: 'USD', channel: 'web' });
|
|
190
|
+
const open = restore ? openMicrotransactionRestoreOverlay : openMicrotransactionOverlay;
|
|
191
|
+
overlay = open({ titleId, checkoutOrigin, session: response.data.data,
|
|
192
|
+
allowLocalDevelopment, onVerified,
|
|
193
|
+
onOpen: () => setGamePaused(true),
|
|
194
|
+
onClose: () => { overlay = null; setGamePaused(false); },
|
|
195
|
+
onOrderUpdate: order => say(order ? 'Receipt: ' + order.payment_status : 'No completed receipt yet.'),
|
|
196
|
+
onError: showError });
|
|
197
|
+
} finally { opening = false; }
|
|
198
|
+
}
|
|
199
|
+
async function showMyPurchases(page = 1) {
|
|
200
|
+
const response = await api.listMyPurchases(titleId,
|
|
201
|
+
{ environment, page, per_page: 20 }, playerOptions());
|
|
202
|
+
const data = response.data.data;
|
|
203
|
+
history.textContent = data.purchases.map(purchase => {
|
|
204
|
+
const name = purchase.product.name ?? purchase.product.sku ?? ('Purchase ' + purchase.id);
|
|
205
|
+
return name + '\n' + purchase.grant_usage.map(row => row.key + ': ' + row.usage_status
|
|
206
|
+
+ '; promised ' + row.purchased_quantity + ', granted ' + row.granted_quantity
|
|
207
|
+
+ ', consumed ' + row.consumed_quantity + ', usable ' + row.usable_quantity).join('\n');
|
|
208
|
+
}).join('\n\n') || 'No captured purchases yet.';
|
|
209
|
+
say('History page ' + data.pagination.page + ' of ' + data.pagination.last_page);
|
|
210
|
+
}
|
|
211
|
+
async function useTenTimber() {
|
|
212
|
+
if (spending || game.paused) return;
|
|
213
|
+
const options = playerOptions();
|
|
214
|
+
if (pendingUse && pendingUse.playerId !== game.playerId) {
|
|
215
|
+
throw new Error('Restore the original account before retrying its pending action.');
|
|
216
|
+
}
|
|
217
|
+
// Create ONCE for this gameplay intent. A failed retry keeps this same object.
|
|
218
|
+
pendingUse ??= { playerId: game.playerId, action_id: createMicrotransactionNonce(),
|
|
219
|
+
key: 'timber', quantity: 10 };
|
|
220
|
+
spending = true;
|
|
221
|
+
try {
|
|
222
|
+
await api.consume(titleId, { key: pendingUse.key, quantity: pendingUse.quantity,
|
|
223
|
+
action_id: pendingUse.action_id, environment }, options);
|
|
224
|
+
const current = await api.listEntitlements(titleId, { environment }, options);
|
|
225
|
+
replaceInventoryInYourGame(current.data.data.entitlements);
|
|
226
|
+
pendingUse = null; // Clear only after successful acknowledgement AND refresh.
|
|
227
|
+
say('10 Timber spent once. Inventory refreshed from Glitch.');
|
|
228
|
+
} finally { spending = false; } // Do not clear pendingUse on failure.
|
|
229
|
+
}
|
|
230
|
+
function button(label, action) {
|
|
231
|
+
const element = document.createElement('button');
|
|
232
|
+
element.type = 'button'; element.textContent = label;
|
|
233
|
+
element.onclick = () => void action().catch(showError);
|
|
234
|
+
shop.append(element);
|
|
235
|
+
}
|
|
236
|
+
replaceInventoryInYourGame([]);
|
|
237
|
+
button('Buy 100 Timber', () => openShop(false));
|
|
238
|
+
button('Restore purchases', () => openShop(true));
|
|
239
|
+
button('My purchases', () => showMyPurchases());
|
|
240
|
+
button('Use 10 Timber', useTenTimber); // Explicit gameplay action, NOT a purchase callback.
|
|
241
|
+
return game;
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Call `installTimberShop({titleId: YOUR_TITLE_ID, productId: YOUR_TIMBER_PRODUCT_ID,
|
|
246
|
+
apiBaseUrl: YOUR_API_BASE, checkoutOrigin: YOUR_CHECKOUT_ORIGIN})` from your existing
|
|
247
|
+
game initialization. These four capitalized names describe your configuration;
|
|
248
|
+
replace them with the actual values, not credentials. `environment` is optional
|
|
249
|
+
and defaults to sandbox. The example assumes an eligible US/USD price; choose a
|
|
250
|
+
server-supported country/currency for your real player instead of guessing from IP.
|
|
251
|
+
|
|
252
|
+
**`replaceInventoryInYourGame` and `setGamePaused` are example game functions, not
|
|
253
|
+
SDK APIs.** Their working demo bodies update the displayed Timber and `game`
|
|
254
|
+
object. In your game, replace those bodies with your engine's inventory assignment
|
|
255
|
+
and pause/input/audio controls. Do not replace assignment with `+= productQuantity`:
|
|
256
|
+
callbacks, restores and refreshes can occur more than once. The callback must never
|
|
257
|
+
call `consume`; that state-changing API belongs to an explicit gameplay action.
|
|
258
|
+
|
|
259
|
+
The sample retains `pendingUse` across failed button retries. For reload recovery,
|
|
260
|
+
persist the nonsecret action intent and its account binding in your game's durable
|
|
261
|
+
command queue, then retry the same action ID after sign-in. Never save the player
|
|
262
|
+
token with it, invent a new ID for an uncertain retry, or infer a particular action's
|
|
263
|
+
success from aggregate purchase-history totals. Coordinate actual building creation
|
|
264
|
+
idempotently with that same gameplay action; the demo only spends the resource.
|
|
265
|
+
|
|
266
|
+
The verified result is the actual claim DTO. The helper validates matching
|
|
267
|
+
title/session/order and, on refresh, the same player. It ignores duplicate codes
|
|
268
|
+
even after a network timeout because the server may already have consumed them.
|
|
269
|
+
It does not grant items from window data or continuously poll. A close message
|
|
270
|
+
uses `{type:'glitch.microtransaction.close',version:1,title_id,
|
|
271
|
+
checkout_session_id,nonce}` and must match the same exact origin/source/session.
|
|
272
|
+
The dialog remains mounted until an in-flight claim and inventory callback finish.
|
|
273
|
+
Close before a successful claim can refresh limited receipt status only; show
|
|
274
|
+
pending/restore guidance, never claim that a checkout capability grants inventory.
|
|
275
|
+
|
|
276
|
+
`player_token` lasts 15 minutes and is restricted to one title/player/environment.
|
|
277
|
+
Keep it in memory and pass `{playerToken}` per request to `getOrder`,
|
|
278
|
+
`listEntitlements` and `consume`. Never install it in global `Glitch.util.Session`
|
|
279
|
+
or `Requests.setAuthToken`, log it, or put it in URLs. The SDK's per-request
|
|
280
|
+
headers prevent simultaneous account/checkout requests from overwriting auth.
|
|
281
|
+
|
|
282
|
+
### Inventory, purchase history and usage are different
|
|
283
|
+
|
|
284
|
+
Use `listEntitlements` to replace the current player inventory. Use the optional
|
|
285
|
+
`listMyPurchases(titleId, {environment,page,per_page}, {playerToken})` to show the
|
|
286
|
+
signed-in player's captured purchases and their lot-level usage; it does not grant
|
|
287
|
+
items or let a game pick another `user_id` or `player_id`. A normal user JWT selects
|
|
288
|
+
its own user; a `gl_player` token selects its bound title/player/environment and
|
|
289
|
+
requires the exact approved Origin. MCP and install tokens cannot use this route.
|
|
290
|
+
Admin `listOrders` remains a separate developer reporting API.
|
|
291
|
+
|
|
292
|
+
Read `response.data.data`:
|
|
293
|
+
|
|
294
|
+
- `title_id`, `player_id`, `environment`, `purchases`, and
|
|
295
|
+
`pagination:{page,per_page,total,last_page,has_more_pages}`.
|
|
296
|
+
- `page` defaults to 1 (1–10000), `per_page` to 20 (1–100). Results are ordered by
|
|
297
|
+
`created_at DESC, id DESC`. Pass the next page number when `has_more_pages` is true.
|
|
298
|
+
No cursor or product filter exists. The HTTP API rejects unknown selectors with 422;
|
|
299
|
+
the SDK rejects invalid filter names/ranges before transport.
|
|
300
|
+
- Each purchase includes the immutable product snapshot, payment/fulfillment state,
|
|
301
|
+
`grant_usage`, `has_consumed_grants`, and `has_usable_grants`. Captured purchases
|
|
302
|
+
remain visible after refund, dispute or quarantine; unpaid attempts are excluded.
|
|
303
|
+
Historical snapshots may have null product `sku`, `name`, `type`, or `version`;
|
|
304
|
+
show a receipt-ID fallback rather than inventing a current catalog value.
|
|
305
|
+
- Each grant usage row has `grant_id` (nullable), `key`, `kind`,
|
|
306
|
+
`purchased_quantity`, `granted_quantity`, `acquired_quantity`, `remaining_quantity`,
|
|
307
|
+
`consumed_quantity`, `revoked_quantity`, `refunded_quantity`,
|
|
308
|
+
`unrecoverable_quantity`, `expires_at`, `expired`, `usable_quantity`, `is_used`,
|
|
309
|
+
and `usage_status`.
|
|
310
|
+
|
|
311
|
+
`purchased_quantity` is what the frozen product promised (grant quantity × order
|
|
312
|
+
quantity). `granted_quantity` and its alias `acquired_quantity` are what was actually
|
|
313
|
+
granted. Without a lot, `grant_id` is null and actual granted/remaining/consumed
|
|
314
|
+
quantities are zero even though promised quantity can be positive. Do not grant
|
|
315
|
+
missing items just because purchase history lists the promised amount.
|
|
316
|
+
|
|
317
|
+
For consumables, `consumed_quantity = acquired_quantity - remaining_quantity -
|
|
318
|
+
revoked_quantity`. Refunded quantity is bounded `revoked_quantity +
|
|
319
|
+
unrecoverable_quantity`; **unrecoverable overlaps consumed**, so never subtract it
|
|
320
|
+
twice. Refund recovery is not gameplay use. Durable/pass `is_used` is **null** because
|
|
321
|
+
ownership does not prove gameplay usage; inspect `usable_quantity` and `expired`
|
|
322
|
+
instead. Statuses are `unused`, `partially_used`, `used_up`, `owned`, `expired`,
|
|
323
|
+
`revoked`, `not_delivered`, or `unavailable`. Use the server's fields, not a locally
|
|
324
|
+
invented “used” checkbox or a raw remaining count that ignores expiry/restrictions.
|
|
325
|
+
|
|
326
|
+
After expiry/reload or a lost claim response, call the anonymous-safe
|
|
327
|
+
`createRestoreSession(titleId,{return_origin,nonce,environment})` and pass its
|
|
328
|
+
returned `intent:'restore'` session to `openMicrotransactionRestoreOverlay` with
|
|
329
|
+
the same hooks shown above. The account signs in inside the modal, then Glitch
|
|
330
|
+
selects an owned paid purchase and sends its verified handoff. No original receipt
|
|
331
|
+
ID or account JWT is required from the game, and restore never calls `/checkout`.
|
|
332
|
+
The new session ID is known before authentication and remains strictly pinned.
|
|
333
|
+
Restoring cannot duplicate ownership or recreate spent consumables; fully refunded,
|
|
334
|
+
unpaid or failed purchases cannot issue a paid handoff.
|
|
335
|
+
|
|
336
|
+
Restore can issue a new game token only when this account still has an eligible
|
|
337
|
+
active purchase. An account with only fully refunded items may receive
|
|
338
|
+
`no_purchases_to_restore`; the hosted sign-in/restore UI or the example's error
|
|
339
|
+
handler should explain that clearly. An expired scoped token still requires
|
|
340
|
+
authentication. The owner JWT in a Glitch-authenticated context can read all
|
|
341
|
+
captured history, and an existing valid scoped token can read the same history,
|
|
342
|
+
but this example does not promise a new game token after every refund/expiry.
|
|
343
|
+
Do not bypass that boundary, expose the account JWT to the game, repurchase just
|
|
344
|
+
to obtain history access, or invent a read-only-authentication endpoint.
|
|
345
|
+
|
|
346
|
+
Lower-level `createMicrotransactionBridge` remains available for existing in-game
|
|
347
|
+
iframe implementations; pass the actual iframe's `contentWindow`. Its `refresh()`
|
|
348
|
+
works only after a successful claim. The older JWT-only `restoreHandoff` and
|
|
349
|
+
receipt-pinned `createMicrotransactionRestoreBridge` are specialized trusted
|
|
350
|
+
hosted-account flows, not the default anonymous-game recovery path.
|
|
351
|
+
|
|
352
|
+
## Gameplay, refunds and failures
|
|
353
|
+
|
|
354
|
+
- Use server entitlements, not mutable cloud saves. `consume` atomically spends
|
|
355
|
+
consumable units with a unique gameplay `action_id`; reuse that ID for retries.
|
|
356
|
+
Free local gameplay cannot mint paid balances. Pass expiry is server-authoritative.
|
|
357
|
+
- Keep payment, fulfillment and settlement independent. Paid can coexist with
|
|
358
|
+
pending server delivery. Replaying delivery reuses immutable IDs and never
|
|
359
|
+
grants twice. Provider and game messages are at-least-once delivery.
|
|
360
|
+
- Earnings `transferred_minor` means money transferred to a provider balance,
|
|
361
|
+
not a confirmed bank deposit. Preserve `bank_payout_status` and reserve/reconciliation
|
|
362
|
+
fields; never relabel pending or transferred balances as paid bank payouts.
|
|
363
|
+
- `requestRefund` is an owning account's support request. `refundOrder` requires
|
|
364
|
+
a separately approved financial administrator; pending/unknown is not completed.
|
|
365
|
+
Preserve historical orders and reverse commission proportionately. Refunds
|
|
366
|
+
use the original provider/account, not the currently preferred payment route.
|
|
367
|
+
- Handle HTTP 401/403 for account/scope, 404 for unavailable or cross-title IDs,
|
|
368
|
+
409 for idempotency/state/approval conflicts, 410 for expired sessions/claims,
|
|
369
|
+
422 for invalid inputs/revenue policy, 429 for rate limits and 503 for provider
|
|
370
|
+
coverage. Do not retry a hard decline/fraud block through another provider.
|
|
371
|
+
- Ads-off is an actual per-title delivery policy. The backend rejects removing
|
|
372
|
+
the final working revenue model. Provider outages leave ads off and existing
|
|
373
|
+
ownership intact. Sandbox products do not constitute production monetization.
|
|
374
|
+
|
|
375
|
+
## MCP
|
|
376
|
+
|
|
377
|
+
Use `mcpCapabilities` to discover exact schemas, abilities, approval flags and
|
|
378
|
+
examples. `mcpOperation` always targets the authenticated MCP facade; it never
|
|
379
|
+
uses a game's runtime token. `mcpUploadMedia` uses the same authorized Media
|
|
380
|
+
pipeline. The companion `glitch-mcp` package supplies explicit tools, a
|
|
381
|
+
`glitch://microtransactions/setup` resource, dynamic title schema resources and
|
|
382
|
+
the `glitch_setup_microtransactions` prompt. A model must not auto-approve live
|
|
383
|
+
prices, provider activation, financial actions or disabling the last revenue model.
|
|
384
|
+
Developer MCP read tools do not impersonate players. The self-only runtime purchase
|
|
385
|
+
history API is documented for game code, not exposed as an arbitrary-player MCP tool.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glitch-javascript-sdk",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"description": "Javascript SDK for Glitch",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -9,13 +9,15 @@
|
|
|
9
9
|
"module": "dist/esm/index.js",
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
-
"src"
|
|
12
|
+
"src",
|
|
13
|
+
"guides"
|
|
13
14
|
],
|
|
14
15
|
"types": "dist/index.d.ts",
|
|
15
16
|
"scripts": {
|
|
16
|
-
"test": "node scripts/test-hosting-routes.cjs && node scripts/test-game-advertising-routes.cjs && node scripts/test-game-design-routes.cjs && node scripts/test-discord-media-routes.cjs && node scripts/test-user-email-delivery-routes.cjs && node scripts/test-game-show-ticket-routes.cjs",
|
|
17
|
+
"test": "node scripts/test-hosting-routes.cjs && node scripts/test-game-advertising-routes.cjs && node scripts/test-game-design-routes.cjs && node scripts/test-discord-media-routes.cjs && node scripts/test-user-email-delivery-routes.cjs && node scripts/test-game-show-ticket-routes.cjs && node scripts/test-festival-networking-routes.cjs && node scripts/test-microtransaction-routes.cjs && node scripts/test-microtransaction-overlay.cjs && node scripts/test-microtransaction-tutorial.cjs",
|
|
17
18
|
"build": "rm -Rf dist && rollup -c --bundleConfigAsCjs",
|
|
18
|
-
"build-docs": "rm -rf docs && typedoc --tsconfig tsconfig.json src/"
|
|
19
|
+
"build-docs": "rm -rf docs && typedoc --tsconfig tsconfig.json src/",
|
|
20
|
+
"test:package": "node scripts/test-package-exports.cjs"
|
|
19
21
|
},
|
|
20
22
|
"author": "",
|
|
21
23
|
"license": "ISC",
|
|
@@ -27,6 +29,7 @@
|
|
|
27
29
|
"@types/isomorphic-form-data": "^2.0.4",
|
|
28
30
|
"@types/node": "^20.10.5",
|
|
29
31
|
"get-file-object-from-local-path": "^1.0.2",
|
|
32
|
+
"jsdom": "^26.1.0",
|
|
30
33
|
"rollup": "^4.9.1",
|
|
31
34
|
"rollup-plugin-dts": "^6.1.0",
|
|
32
35
|
"rollup-plugin-node-builtins": "^2.1.2",
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { AxiosProgressEvent, AxiosPromise, AxiosRequestConfig } from 'axios';
|
|
2
|
+
import FestivalNetworkingRoute from '../routes/FestivalNetworkingRoute';
|
|
3
|
+
import Requests from '../util/Requests';
|
|
4
|
+
|
|
5
|
+
export type FestivalPostKind = 'discussion' | 'job' | 'talent';
|
|
6
|
+
export type FestivalPostState = 'active' | 'locked' | 'archived' | 'hidden' | 'deleted' | 'removed' | 'paused' | 'filled' | 'expired' | 'closed';
|
|
7
|
+
export type FestivalApplicationState = 'submitted' | 'viewed' | 'shortlisted' | 'interview' | 'accepted' | 'rejected' | 'withdrawn' | 'closed';
|
|
8
|
+
export type FestivalWorkType = 'full_time' | 'part_time' | 'contract' | 'gig';
|
|
9
|
+
export type FestivalRequestOptions = Pick<AxiosRequestConfig, 'signal' | 'timeout'>;
|
|
10
|
+
|
|
11
|
+
export interface FestivalNetworkingSettings {
|
|
12
|
+
discussions_enabled: boolean;
|
|
13
|
+
jobs_enabled: boolean;
|
|
14
|
+
employer_posts_enabled: boolean;
|
|
15
|
+
talent_posts_enabled: boolean;
|
|
16
|
+
matching_enabled: boolean;
|
|
17
|
+
voting_enabled: boolean;
|
|
18
|
+
comments_enabled: boolean;
|
|
19
|
+
media_enabled: boolean;
|
|
20
|
+
/** Registration or a valid ticket is mandatory; cannot be disabled. */
|
|
21
|
+
require_registration: true;
|
|
22
|
+
public_viewing: false;
|
|
23
|
+
anonymous_enabled: false;
|
|
24
|
+
categories: string[];
|
|
25
|
+
skills: string[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FestivalPostInput {
|
|
29
|
+
kind: FestivalPostKind;
|
|
30
|
+
title: string;
|
|
31
|
+
/** Sanitized HTML from the shared WYSIWYG editor. */
|
|
32
|
+
content: string;
|
|
33
|
+
visibility?: 'public' | 'unlisted' | 'private';
|
|
34
|
+
category?: string | null;
|
|
35
|
+
tags?: string[];
|
|
36
|
+
skills?: string[];
|
|
37
|
+
preferred_skills?: string[];
|
|
38
|
+
job_types?: FestivalWorkType[];
|
|
39
|
+
company?: string | null;
|
|
40
|
+
organization_id?: string | null;
|
|
41
|
+
work_arrangement?: 'remote' | 'onsite' | 'hybrid' | null;
|
|
42
|
+
experience?: 'any' | 'entry' | 'junior' | 'mid' | 'senior' | 'lead' | null;
|
|
43
|
+
location?: string | null;
|
|
44
|
+
availability?: 'immediately' | 'within_30_days' | 'specific_date' | 'flexible' | 'unavailable';
|
|
45
|
+
availability_date?: string | null;
|
|
46
|
+
deadline?: string | null;
|
|
47
|
+
expires_at?: string | null;
|
|
48
|
+
compensation_type?: 'negotiable' | 'yearly' | 'monthly' | 'hourly' | 'flat_fee';
|
|
49
|
+
compensation_min?: number | null;
|
|
50
|
+
compensation_max?: number | null;
|
|
51
|
+
currency?: string | null;
|
|
52
|
+
portfolio_url?: string | null;
|
|
53
|
+
application_method?: 'internal' | 'external';
|
|
54
|
+
application_url?: string | null;
|
|
55
|
+
/** UserMedia IDs returned by uploadMedia, NOT Media IDs or clip-library selections. Max 8. */
|
|
56
|
+
media_ids?: string[];
|
|
57
|
+
public_compensation?: boolean;
|
|
58
|
+
public_location?: boolean;
|
|
59
|
+
public_availability?: boolean;
|
|
60
|
+
public_portfolio?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface FestivalNetworkingFilters {
|
|
64
|
+
kind?: FestivalPostKind;
|
|
65
|
+
view?: 'all' | 'mine' | 'saved' | 'hidden' | 'comments';
|
|
66
|
+
sort?: 'new' | 'hot' | 'top' | 'discussed' | 'compensation' | 'deadline';
|
|
67
|
+
window?: 'today' | 'week' | 'month' | 'festival' | 'all';
|
|
68
|
+
q?: string;
|
|
69
|
+
category?: string;
|
|
70
|
+
skill?: string;
|
|
71
|
+
job_type?: FestivalWorkType;
|
|
72
|
+
arrangement?: 'remote' | 'onsite' | 'hybrid';
|
|
73
|
+
experience?: string;
|
|
74
|
+
location?: string;
|
|
75
|
+
company?: string;
|
|
76
|
+
availability?: string;
|
|
77
|
+
currency?: string;
|
|
78
|
+
compensation_type?: string;
|
|
79
|
+
min_compensation?: number;
|
|
80
|
+
tag?: string;
|
|
81
|
+
author?: string;
|
|
82
|
+
has_comments?: boolean;
|
|
83
|
+
has_portfolio?: boolean;
|
|
84
|
+
media_type?: 'image' | 'video';
|
|
85
|
+
page?: number;
|
|
86
|
+
per_page?: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface FestivalMediaUpload {
|
|
90
|
+
id: string;
|
|
91
|
+
user_media_id: string;
|
|
92
|
+
media_id: string;
|
|
93
|
+
url: string;
|
|
94
|
+
mime_type: string;
|
|
95
|
+
size: number;
|
|
96
|
+
title: string;
|
|
97
|
+
processing_status: 'completed' | 'pending' | 'processing' | 'failed';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface FestivalNetworkingProfile { id: string | null; name: string; }
|
|
101
|
+
export interface FestivalNetworkingResponse<T> {
|
|
102
|
+
data: T;
|
|
103
|
+
message?: string;
|
|
104
|
+
meta?: { current_page: number; last_page: number; total?: number; };
|
|
105
|
+
has_access?: boolean;
|
|
106
|
+
can_manage?: boolean;
|
|
107
|
+
can_moderate?: boolean;
|
|
108
|
+
user?: FestivalNetworkingProfile | null;
|
|
109
|
+
show?: { id: string; name: string; };
|
|
110
|
+
audit?: Array<Record<string, unknown>>;
|
|
111
|
+
}
|
|
112
|
+
export interface FestivalPost {
|
|
113
|
+
id: string;
|
|
114
|
+
game_show_id: string;
|
|
115
|
+
parent_id: string | null;
|
|
116
|
+
kind: FestivalPostKind | 'comment';
|
|
117
|
+
title: string;
|
|
118
|
+
content: string;
|
|
119
|
+
state: FestivalPostState;
|
|
120
|
+
visibility: 'public' | 'unlisted' | 'private';
|
|
121
|
+
details: Partial<FestivalPostInput>;
|
|
122
|
+
author: FestivalNetworkingProfile | null;
|
|
123
|
+
created_at: string;
|
|
124
|
+
updated_at: string;
|
|
125
|
+
score: number;
|
|
126
|
+
my_vote: -1 | 0 | 1;
|
|
127
|
+
saved: boolean;
|
|
128
|
+
is_owner: boolean;
|
|
129
|
+
can_edit: boolean;
|
|
130
|
+
comment_count: number;
|
|
131
|
+
media: Array<{ id: string; user_media_id?: string | null; url: string; mime_type: string; title: string | null; }>;
|
|
132
|
+
my_application?: { id: string; status: FestivalApplicationState; } | null;
|
|
133
|
+
match?: { score: number; reasons: string[]; missing_required_skills: string[]; disclaimer: string; };
|
|
134
|
+
}
|
|
135
|
+
export interface FestivalApplicationInput { message?: string; portfolio?: string[]; }
|
|
136
|
+
export interface FestivalConversation {
|
|
137
|
+
id: string;
|
|
138
|
+
festival_application_id: string;
|
|
139
|
+
can_send: boolean;
|
|
140
|
+
read_only_reason: string | null;
|
|
141
|
+
festival_context: { game_show_id: string; post_id: string | null; title: string; kind: FestivalPostKind; application_status: FestivalApplicationState } | null;
|
|
142
|
+
users: Array<{ id: string | null; display_name: string; avatar: string | null }>;
|
|
143
|
+
messages: Array<{ id: string; thread_id: string; user_id: string | null; message: string; client_message_id: string | null; created_at: string; updated_at: string; user: { id: string | null; display_name: string; avatar: string | null } }>;
|
|
144
|
+
}
|
|
145
|
+
export interface FestivalPreferences { notifications?: boolean; blocked_users?: string[]; blocked_companies?: string[]; }
|
|
146
|
+
export interface FestivalReportInput {
|
|
147
|
+
reason: 'spam' | 'harassment' | 'hate' | 'sexual_content' | 'scam' | 'job_scam' | 'copyright' | 'malicious_link' | 'misleading' | 'other';
|
|
148
|
+
explanation?: string;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Festival-scoped discussions, talent, jobs, moderation and new owned media uploads. */
|
|
152
|
+
export default class FestivalNetworking {
|
|
153
|
+
private static request<T>(name: string, show_id: string, data?: object, ids: Record<string, string> = {}, params?: object, options?: FestivalRequestOptions): AxiosPromise<FestivalNetworkingResponse<T>> {
|
|
154
|
+
const replacements = Object.fromEntries(Object.entries({ show_id, ...ids }).map(([key, id]) => [key, encodeURIComponent(id)]));
|
|
155
|
+
return Requests.processRoute<T>(FestivalNetworkingRoute.routes[name], data, replacements, params, options);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Read enabled tools and the current account's registration/ticket access. */
|
|
159
|
+
static settings<T = FestivalNetworkingSettings>(id: string, options?: FestivalRequestOptions) { return this.request<T>('settings', id, undefined, {}, undefined, options); }
|
|
160
|
+
/** Organizer-only settings update; admission remains mandatory. */
|
|
161
|
+
static updateSettings<T = FestivalNetworkingSettings>(id: string, data: Partial<FestivalNetworkingSettings>) { return this.request<T>('updateSettings', id, data); }
|
|
162
|
+
/** Search posts; compensation comparisons require currency and period. */
|
|
163
|
+
static listPosts<T = FestivalPost[]>(id: string, params?: FestivalNetworkingFilters, options?: FestivalRequestOptions) { return this.request<T>('listPosts', id, undefined, {}, params, options); }
|
|
164
|
+
/** Create a post with rich HTML and optional newly uploaded UserMedia IDs. */
|
|
165
|
+
static createPost<T = FestivalPost>(id: string, data: FestivalPostInput) { return this.request<T>('createPost', id, data); }
|
|
166
|
+
/** Direct links still require admission and content visibility permission. */
|
|
167
|
+
static getPost<T = FestivalPost>(id: string, post_id: string, options?: FestivalRequestOptions) { return this.request<T>('getPost', id, undefined, { post_id }, undefined, options); }
|
|
168
|
+
/** Edit or soft-delete via state; omit media_ids to preserve current attachments. */
|
|
169
|
+
static updatePost<T = FestivalPost>(id: string, post_id: string, data: Partial<FestivalPostInput> & { state?: FestivalPostState }) { return this.request<T>('updatePost', id, data, { post_id }); }
|
|
170
|
+
/** Paginated direct replies; fetch children to expand a thread. */
|
|
171
|
+
static listComments<T = FestivalPost[]>(id: string, post_id: string, params?: { page?: number }, options?: FestivalRequestOptions) { return this.request<T>('listComments', id, undefined, { post_id }, params, options); }
|
|
172
|
+
/** Add a rich-text reply, subject to locking and five-level nesting. */
|
|
173
|
+
static createComment<T = FestivalPost>(id: string, post_id: string, data: { content: string }) { return this.request<T>('createComment', id, data, { post_id }); }
|
|
174
|
+
/** Set, replace, or remove a vote/save/hide idempotently. */
|
|
175
|
+
static setInteraction<T = FestivalPost>(id: string, post_id: string, data: { action: 'vote' | 'saved' | 'hidden'; value: -1 | 0 | 1 }) { return this.request<T>('setInteraction', id, data, { post_id }); }
|
|
176
|
+
/** Apply or express interest once; the original listing is snapshotted. */
|
|
177
|
+
static apply<T = Record<string, unknown>>(id: string, post_id: string, data: FestivalApplicationInput) { return this.request<T>('apply', id, data, { post_id }); }
|
|
178
|
+
/** Discovery matches for the current user's own job/talent listing. */
|
|
179
|
+
static matches<T = FestivalPost[]>(id: string, post_id: string, params?: { page?: number }, options?: FestivalRequestOptions) { return this.request<T>('matches', id, undefined, { post_id }, params, options); }
|
|
180
|
+
/** Report suspicious content privately to festival moderators. */
|
|
181
|
+
static report<T = never>(id: string, post_id: string, data: FestivalReportInput) { return this.request<T>('report', id, data, { post_id }); }
|
|
182
|
+
/** Only the applicant and listing owner receive application records. */
|
|
183
|
+
static applications<T = Array<Record<string, unknown>>>(id: string, params?: { page?: number }, options?: FestivalRequestOptions) { return this.request<T>('applications', id, undefined, {}, params, options); }
|
|
184
|
+
/** Applicant withdrawal or owner-managed status changes. */
|
|
185
|
+
static updateApplication<T = Record<string, unknown>>(id: string, application_id: string, data: { status: Exclude<FestivalApplicationState, 'submitted'> }) { return this.request<T>('updateApplication', id, data, { application_id }); }
|
|
186
|
+
/** Open the application/talent inquiry's private shared-inbox conversation, creating it once for legacy applications. Only the applicant and original listing owner may call this. Use Messages.getThread/sendMessage for subsequent conversation activity. */
|
|
187
|
+
static conversation<T = FestivalConversation>(id: string, application_id: string) { return this.request<T>('conversation', id, {}, { application_id }); }
|
|
188
|
+
/** Moderator-only report queue and audit history. */
|
|
189
|
+
static moderation<T = Array<Record<string, unknown>>>(id: string, params?: { page?: number }, options?: FestivalRequestOptions) { return this.request<T>('moderation', id, undefined, {}, params, options); }
|
|
190
|
+
/** Moderation remains available even while the board is disabled. */
|
|
191
|
+
static moderatePost<T = FestivalPost>(id: string, post_id: string, data: { state?: 'active' | 'hidden' | 'locked' | 'deleted' | 'removed'; remove_media?: true }) { return this.request<T>('moderatePost', id, data, { post_id }); }
|
|
192
|
+
/** Resolve, dismiss or begin reviewing a report. */
|
|
193
|
+
static resolveReport<T = never>(id: string, report_id: string, data: { status: 'under_review' | 'resolved' | 'dismissed' }) { return this.request<T>('resolveReport', id, data, { report_id }); }
|
|
194
|
+
/** Moderator-only participant restrictions. */
|
|
195
|
+
static restrictMember<T = never>(id: string, user_id: string, data: { banned: boolean }) { return this.request<T>('restrictMember', id, data, { user_id }); }
|
|
196
|
+
/** Current user's privacy and notification preferences. */
|
|
197
|
+
static preferences<T = FestivalPreferences>(id: string, options?: FestivalRequestOptions) { return this.request<T>('preferences', id, undefined, {}, undefined, options); }
|
|
198
|
+
/** Set notification opt-out and blocked participants/companies. */
|
|
199
|
+
static updatePreferences<T = FestivalPreferences>(id: string, data: FestivalPreferences) { return this.request<T>('updatePreferences', id, data); }
|
|
200
|
+
/** Uploaded festival attachments only; not the gameplay clip library. */
|
|
201
|
+
static media<T = Array<Record<string, unknown>>>(id: string, params?: { page?: number }, options?: FestivalRequestOptions) { return this.request<T>('media', id, undefined, {}, params, options); }
|
|
202
|
+
/**
|
|
203
|
+
* Upload a new image (10 MB max) or video (100 MB max), using the existing media pipeline.
|
|
204
|
+
* The response ID is an owned UserMedia ID for createPost/updatePost media_ids.
|
|
205
|
+
* @param file New file selected by the attendee; supported images exclude SVG.
|
|
206
|
+
* @param data Which enabled board the upload is for.
|
|
207
|
+
* @param onUploadProgress Transfer progress; 100% may still require conversion before completion.
|
|
208
|
+
*/
|
|
209
|
+
static uploadMedia<T = FestivalMediaUpload>(id: string, file: File | Blob, data: { kind: FestivalPostKind }, onUploadProgress?: (event: AxiosProgressEvent) => void, options?: FestivalRequestOptions): AxiosPromise<FestivalNetworkingResponse<T>> {
|
|
210
|
+
return Requests.uploadFile<T>(FestivalNetworkingRoute.routes.uploadMedia.url.replace('{show_id}', encodeURIComponent(id)), 'media', file, data, undefined, onUploadProgress, options);
|
|
211
|
+
}
|
|
212
|
+
/** Organizations the authenticated user is authorized to represent. */
|
|
213
|
+
static organizations<T = Array<{ id: string; name: string }>>(id: string, options?: FestivalRequestOptions) { return this.request<T>('organizations', id, undefined, {}, undefined, options); }
|
|
214
|
+
/** Organizer-only aggregate participation metrics. */
|
|
215
|
+
static analytics<T = Record<string, unknown>>(id: string, options?: FestivalRequestOptions) { return this.request<T>('analytics', id, undefined, {}, undefined, options); }
|
|
216
|
+
}
|
package/src/api/Messages.ts
CHANGED
|
@@ -17,7 +17,10 @@ class Messages {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
* Send a new message that will be added to a thread
|
|
20
|
+
* Send a new message that will be added to a thread. Festival-scoped threads
|
|
21
|
+
* enforce current admission, blocking and read-only state server-side.
|
|
22
|
+
* Include an optional client_message_id UUID and reuse it when retrying a
|
|
23
|
+
* timed-out request to prevent duplicate messages and notifications.
|
|
21
24
|
*
|
|
22
25
|
* @see https://api.glitch.fun/api/documentation#/Messages/storeMessage
|
|
23
26
|
*
|