@playcademy/better-auth 0.0.1-alpha.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/README.md +57 -0
- package/dist/client.d.ts +69 -0
- package/dist/client.js +278 -0
- package/dist/fetch.d.ts +10 -0
- package/dist/server.d.ts +92 -0
- package/dist/server.js +20655 -0
- package/dist/utils.d.ts +44 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @playcademy/better-auth
|
|
2
|
+
|
|
3
|
+
Better Auth integration for Playcademy games with automatic platform/standalone mode detection and Safari Storage Access API support.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add @playcademy/better-auth better-auth
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Server
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { betterAuth } from 'better-auth'
|
|
17
|
+
|
|
18
|
+
import { playcademy } from '@playcademy/better-auth/server'
|
|
19
|
+
|
|
20
|
+
export const auth = betterAuth({
|
|
21
|
+
database: drizzleAdapter(db, { provider: 'sqlite' }),
|
|
22
|
+
plugins: [playcademy()],
|
|
23
|
+
advanced: {
|
|
24
|
+
defaultCookieAttributes: {
|
|
25
|
+
sameSite: 'none',
|
|
26
|
+
secure: true,
|
|
27
|
+
path: '/',
|
|
28
|
+
partitioned: true, // CHIPS for Chrome/Edge
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Client
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { createAuthClient } from 'better-auth/react'
|
|
38
|
+
|
|
39
|
+
import { playcademy } from '@playcademy/better-auth/client'
|
|
40
|
+
|
|
41
|
+
const auth = createAuthClient({
|
|
42
|
+
plugins: [playcademy()],
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Features
|
|
47
|
+
|
|
48
|
+
- **Automatic mode detection**: Seamlessly switches between platform (iframe) and standalone modes
|
|
49
|
+
- **Cookie-based auth**: Uses HttpOnly cookies for security
|
|
50
|
+
- **CHIPS support**: Partitioned cookies for Chrome/Edge in iframes
|
|
51
|
+
- **Safari support**: Automatic Storage Access API prompts for Safari users
|
|
52
|
+
- **Zero configuration**: Works out of the box with sensible defaults
|
|
53
|
+
|
|
54
|
+
## Requirements
|
|
55
|
+
|
|
56
|
+
- **HTTPS required**: Both platform and game must use HTTPS (Safari requirement)
|
|
57
|
+
- **Storage Access API**: Safari users see a one-time prompt to allow cookies in iframes
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playcademy Auth Client Plugin
|
|
3
|
+
*
|
|
4
|
+
* Handles platform/standalone auth automatically:
|
|
5
|
+
* - Platform mode: Uses cookies with automatic Storage Access API for Safari
|
|
6
|
+
* - Standalone mode: Uses cookies
|
|
7
|
+
*
|
|
8
|
+
* Safari in iframes requires Storage Access API - this plugin handles it automatically!
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { createAuthClient } from 'better-auth/react'
|
|
13
|
+
* import { playcademy } from '@playcademy/better-auth/client'
|
|
14
|
+
*
|
|
15
|
+
* // That's it! Safari Storage Access API handled automatically
|
|
16
|
+
* const auth = createAuthClient({
|
|
17
|
+
* plugins: [playcademy()]
|
|
18
|
+
* })
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { playcademy as playcademyServerPlugin } from './server';
|
|
22
|
+
export interface PlaycademyClientOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Automatically show storage access prompt for Safari users
|
|
25
|
+
*
|
|
26
|
+
* When true (default), Safari users in iframes will see an automatic
|
|
27
|
+
* prompt to grant storage access (required for cookies in Safari).
|
|
28
|
+
*
|
|
29
|
+
* Set to false if you want to handle Safari storage access yourself.
|
|
30
|
+
*
|
|
31
|
+
* @default true
|
|
32
|
+
*/
|
|
33
|
+
safari?: {
|
|
34
|
+
autoPrompt?: boolean;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Playcademy client plugin for Better Auth
|
|
39
|
+
*
|
|
40
|
+
* Automatically handles:
|
|
41
|
+
* - Platform vs standalone mode detection
|
|
42
|
+
* - Token exchange for platform mode
|
|
43
|
+
* - Safari Storage Access API (auto-prompt)
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* // Default: Auto-handles everything including Safari
|
|
48
|
+
* const auth = createAuthClient({
|
|
49
|
+
* plugins: [playcademy()]
|
|
50
|
+
* })
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // Custom: Disable auto Safari prompt
|
|
56
|
+
* const auth = createAuthClient({
|
|
57
|
+
* plugins: [
|
|
58
|
+
* playcademy({
|
|
59
|
+
* safari: { autoPrompt: false }
|
|
60
|
+
* })
|
|
61
|
+
* ]
|
|
62
|
+
* })
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export declare function playcademy(options?: PlaycademyClientOptions): {
|
|
66
|
+
id: "playcademy-client";
|
|
67
|
+
$InferServerPlugin: ReturnType<typeof playcademyServerPlugin>;
|
|
68
|
+
fetchPlugins: import("@better-fetch/fetch").BetterFetchPlugin[];
|
|
69
|
+
};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, {
|
|
5
|
+
get: all[name],
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
set: (newValue) => all[name] = () => newValue
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
// src/utils.ts
|
|
13
|
+
function isPlatformMode() {
|
|
14
|
+
if (typeof window === "undefined")
|
|
15
|
+
return false;
|
|
16
|
+
if (window.self === window.top)
|
|
17
|
+
return false;
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
function isSafari() {
|
|
21
|
+
if (typeof window === "undefined")
|
|
22
|
+
return false;
|
|
23
|
+
const ua = navigator.userAgent;
|
|
24
|
+
return ua.includes("Safari") && !ua.includes("Chrome") && !ua.includes("Chromium");
|
|
25
|
+
}
|
|
26
|
+
function getPlatformToken() {
|
|
27
|
+
if (typeof window === "undefined")
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
return window.PLAYCADEMY?.token || null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function hasStorageAccessAPI() {
|
|
36
|
+
return typeof document !== "undefined" && "hasStorageAccess" in document;
|
|
37
|
+
}
|
|
38
|
+
async function needsStorageAccess() {
|
|
39
|
+
if (!isPlatformMode())
|
|
40
|
+
return false;
|
|
41
|
+
if (!isSafari())
|
|
42
|
+
return false;
|
|
43
|
+
if (!hasStorageAccessAPI())
|
|
44
|
+
return false;
|
|
45
|
+
try {
|
|
46
|
+
const hasAccess = await document.hasStorageAccess();
|
|
47
|
+
return !hasAccess;
|
|
48
|
+
} catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function requestStorageAccess() {
|
|
53
|
+
if (!hasStorageAccessAPI())
|
|
54
|
+
return false;
|
|
55
|
+
try {
|
|
56
|
+
await document.requestStorageAccess();
|
|
57
|
+
return true;
|
|
58
|
+
} catch {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/fetch.ts
|
|
64
|
+
async function ensureStorageAccess() {
|
|
65
|
+
if (!isSafari())
|
|
66
|
+
return true;
|
|
67
|
+
const needs = await needsStorageAccess();
|
|
68
|
+
if (needs) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
await document.requestStorageAccess();
|
|
73
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
74
|
+
return true;
|
|
75
|
+
} catch (err) {
|
|
76
|
+
console.error("[Playcademy Auth] Safari Storage Access activation failed:", err);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
var exchangeComplete = false;
|
|
81
|
+
var exchangePromise = null;
|
|
82
|
+
var TOKEN_POLL_INTERVAL_MS = 50;
|
|
83
|
+
var TOKEN_POLL_MAX_DURATION_MS = 3000;
|
|
84
|
+
async function waitForToken() {
|
|
85
|
+
const startTime = Date.now();
|
|
86
|
+
return new Promise((resolve) => {
|
|
87
|
+
const checkToken = () => {
|
|
88
|
+
const token = getPlatformToken();
|
|
89
|
+
if (token) {
|
|
90
|
+
resolve(token);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (Date.now() - startTime >= TOKEN_POLL_MAX_DURATION_MS) {
|
|
94
|
+
resolve(null);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
setTimeout(checkToken, TOKEN_POLL_INTERVAL_MS);
|
|
98
|
+
};
|
|
99
|
+
checkToken();
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function playcademyExchangePlugin(_opts) {
|
|
103
|
+
return {
|
|
104
|
+
id: "playcademy-exchange",
|
|
105
|
+
name: "Playcademy Exchange",
|
|
106
|
+
async init(url, options) {
|
|
107
|
+
if (exchangeComplete)
|
|
108
|
+
return { url, options };
|
|
109
|
+
if (exchangePromise) {
|
|
110
|
+
await exchangePromise;
|
|
111
|
+
return { url, options };
|
|
112
|
+
}
|
|
113
|
+
exchangePromise = (async () => {
|
|
114
|
+
const token = await waitForToken();
|
|
115
|
+
if (!token)
|
|
116
|
+
return;
|
|
117
|
+
const canProceed = await ensureStorageAccess();
|
|
118
|
+
if (!canProceed) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
await fetch("/api/auth/playcademy", {
|
|
122
|
+
method: "POST",
|
|
123
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
124
|
+
credentials: "include"
|
|
125
|
+
});
|
|
126
|
+
exchangeComplete = true;
|
|
127
|
+
})();
|
|
128
|
+
await exchangePromise;
|
|
129
|
+
return { url, options };
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/client.ts
|
|
135
|
+
var OVERLAY_ID = "playcademy-storage-access-overlay";
|
|
136
|
+
function removeOverlay() {
|
|
137
|
+
const overlay = document.getElementById(OVERLAY_ID);
|
|
138
|
+
if (overlay) {
|
|
139
|
+
overlay.remove();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function injectStorageAccessOverlay() {
|
|
143
|
+
if (document.getElementById(OVERLAY_ID))
|
|
144
|
+
return;
|
|
145
|
+
if (!document.body) {
|
|
146
|
+
document.addEventListener("DOMContentLoaded", injectStorageAccessOverlay);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const overlay = document.createElement("div");
|
|
150
|
+
overlay.id = OVERLAY_ID;
|
|
151
|
+
overlay.innerHTML = `
|
|
152
|
+
<style>
|
|
153
|
+
#${OVERLAY_ID} {
|
|
154
|
+
position: fixed;
|
|
155
|
+
top: 0;
|
|
156
|
+
left: 0;
|
|
157
|
+
width: 100%;
|
|
158
|
+
height: 100%;
|
|
159
|
+
background: rgba(0, 0, 0, 0.8);
|
|
160
|
+
display: flex;
|
|
161
|
+
align-items: center;
|
|
162
|
+
justify-content: center;
|
|
163
|
+
z-index: 999999;
|
|
164
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
#${OVERLAY_ID}-modal {
|
|
168
|
+
background: white;
|
|
169
|
+
border-radius: 12px;
|
|
170
|
+
padding: 32px;
|
|
171
|
+
max-width: 400px;
|
|
172
|
+
text-align: center;
|
|
173
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
#${OVERLAY_ID}-title {
|
|
177
|
+
font-size: 20px;
|
|
178
|
+
font-weight: 600;
|
|
179
|
+
margin: 0 0 12px 0;
|
|
180
|
+
color: #1a1a1a;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#${OVERLAY_ID}-message {
|
|
184
|
+
font-size: 14px;
|
|
185
|
+
line-height: 1.5;
|
|
186
|
+
margin: 0 0 24px 0;
|
|
187
|
+
color: #666;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
#${OVERLAY_ID}-button {
|
|
191
|
+
background: #007aff;
|
|
192
|
+
color: white;
|
|
193
|
+
border: none;
|
|
194
|
+
border-radius: 8px;
|
|
195
|
+
padding: 12px 24px;
|
|
196
|
+
font-size: 16px;
|
|
197
|
+
font-weight: 500;
|
|
198
|
+
cursor: pointer;
|
|
199
|
+
transition: background 0.2s;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
#${OVERLAY_ID}-button:hover {
|
|
203
|
+
background: #0051d5;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
#${OVERLAY_ID}-button:active {
|
|
207
|
+
transform: scale(0.98);
|
|
208
|
+
}
|
|
209
|
+
</style>
|
|
210
|
+
|
|
211
|
+
<div id="${OVERLAY_ID}-modal">
|
|
212
|
+
<h2 id="${OVERLAY_ID}-title">Enable Authentication</h2>
|
|
213
|
+
<p id="${OVERLAY_ID}-message">
|
|
214
|
+
This game needs to store authentication data.<br>
|
|
215
|
+
Click below to continue.
|
|
216
|
+
</p>
|
|
217
|
+
<button id="${OVERLAY_ID}-button">Continue</button>
|
|
218
|
+
</div>
|
|
219
|
+
`;
|
|
220
|
+
const button = overlay.querySelector(`#${OVERLAY_ID}-button`);
|
|
221
|
+
if (button) {
|
|
222
|
+
button.addEventListener("click", async () => {
|
|
223
|
+
if (hasStorageAccessAPI()) {
|
|
224
|
+
try {
|
|
225
|
+
const hasAccess = await document.hasStorageAccess();
|
|
226
|
+
if (hasAccess) {
|
|
227
|
+
removeOverlay();
|
|
228
|
+
window.location.reload();
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
} catch {}
|
|
232
|
+
}
|
|
233
|
+
const granted = await requestStorageAccess();
|
|
234
|
+
if (granted) {
|
|
235
|
+
removeOverlay();
|
|
236
|
+
window.location.reload();
|
|
237
|
+
} else {
|
|
238
|
+
const message = overlay.querySelector(`#${OVERLAY_ID}-message`);
|
|
239
|
+
if (message) {
|
|
240
|
+
message.textContent = "Storage access was denied. Please enable cookies in your browser settings to continue.";
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
document.body.appendChild(overlay);
|
|
246
|
+
}
|
|
247
|
+
async function handleStorageAccess(options) {
|
|
248
|
+
if (!options.autoPrompt)
|
|
249
|
+
return;
|
|
250
|
+
if (!isPlatformMode() || !isSafari())
|
|
251
|
+
return;
|
|
252
|
+
const needs = await needsStorageAccess();
|
|
253
|
+
if (needs) {
|
|
254
|
+
injectStorageAccessOverlay();
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
function playcademy(options) {
|
|
258
|
+
const opts = {
|
|
259
|
+
safari: {
|
|
260
|
+
autoPrompt: options?.safari?.autoPrompt ?? true
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
if (typeof window !== "undefined" && opts.safari.autoPrompt) {
|
|
264
|
+
if (isPlatformMode() && isSafari()) {
|
|
265
|
+
setTimeout(() => {
|
|
266
|
+
handleStorageAccess(opts.safari);
|
|
267
|
+
}, 0);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
id: "playcademy-client",
|
|
272
|
+
$InferServerPlugin: {},
|
|
273
|
+
fetchPlugins: [playcademyExchangePlugin()]
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
export {
|
|
277
|
+
playcademy
|
|
278
|
+
};
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { BetterFetchPlugin } from '@better-fetch/fetch';
|
|
2
|
+
/**
|
|
3
|
+
* Better Auth fetch plugin - exchanges platform token on first request
|
|
4
|
+
*
|
|
5
|
+
* Uses polling strategy (similar to bootIframe handshake) to wait for SDK initialization.
|
|
6
|
+
* This keeps Better Auth completely decoupled from our SDK.
|
|
7
|
+
*/
|
|
8
|
+
export declare function playcademyExchangePlugin(_opts?: {
|
|
9
|
+
baseURL: string;
|
|
10
|
+
}): BetterFetchPlugin;
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Playcademy plugin for Better Auth
|
|
3
|
+
*
|
|
4
|
+
* This plugin provides:
|
|
5
|
+
* - Platform user schema field (playcademyUserId)
|
|
6
|
+
* - Token exchange endpoint (POST /api/auth/playcademy)
|
|
7
|
+
* - Automatic account linking between platform and standalone modes
|
|
8
|
+
* - Cross-site cookie support with CHIPS
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { betterAuth } from 'better-auth'
|
|
13
|
+
* import { playcademy } from '@playcademy/better-auth/server'
|
|
14
|
+
*
|
|
15
|
+
* export const auth = betterAuth({
|
|
16
|
+
* database: drizzleAdapter(db, { provider: 'sqlite' }),
|
|
17
|
+
* plugins: [playcademy()],
|
|
18
|
+
* advanced: {
|
|
19
|
+
* defaultCookieAttributes: {
|
|
20
|
+
* sameSite: 'none',
|
|
21
|
+
* secure: true,
|
|
22
|
+
* path: '/',
|
|
23
|
+
* partitioned: true, // CHIPS for Chrome/Edge
|
|
24
|
+
* },
|
|
25
|
+
* },
|
|
26
|
+
* })
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare const playcademy: () => {
|
|
30
|
+
id: "playcademy";
|
|
31
|
+
schema: {
|
|
32
|
+
user: {
|
|
33
|
+
fields: {
|
|
34
|
+
playcademyUserId: {
|
|
35
|
+
type: "string";
|
|
36
|
+
unique: true;
|
|
37
|
+
required: false;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
endpoints: {
|
|
43
|
+
/**
|
|
44
|
+
* Platform Token Exchange Endpoint
|
|
45
|
+
*
|
|
46
|
+
* POST /api/auth/playcademy
|
|
47
|
+
*
|
|
48
|
+
* Exchanges a Playcademy platform JWT for a Better Auth session.
|
|
49
|
+
* Used when games are launched from the platform (iframe mode).
|
|
50
|
+
*
|
|
51
|
+
* Flow:
|
|
52
|
+
* 1. Verify platform JWT with platform API
|
|
53
|
+
* 2. Find or create user (with account linking)
|
|
54
|
+
* 3. Check if already authenticated
|
|
55
|
+
* 4. Create new session if needed
|
|
56
|
+
* 5. Set cross-site compatible cookie (CHIPS)
|
|
57
|
+
*/
|
|
58
|
+
playcademyAuth: {
|
|
59
|
+
<AsResponse extends boolean = false, ReturnHeaders extends boolean = false>(inputCtx_0?: ({
|
|
60
|
+
body?: undefined;
|
|
61
|
+
} & {
|
|
62
|
+
method?: "POST" | undefined;
|
|
63
|
+
} & {
|
|
64
|
+
query?: Record<string, any> | undefined;
|
|
65
|
+
} & {
|
|
66
|
+
params?: Record<string, any>;
|
|
67
|
+
} & {
|
|
68
|
+
request?: Request;
|
|
69
|
+
} & {
|
|
70
|
+
headers?: HeadersInit;
|
|
71
|
+
} & {
|
|
72
|
+
asResponse?: boolean;
|
|
73
|
+
returnHeaders?: boolean;
|
|
74
|
+
use?: import("better-call").Middleware[];
|
|
75
|
+
path?: string;
|
|
76
|
+
} & {
|
|
77
|
+
asResponse?: AsResponse | undefined;
|
|
78
|
+
returnHeaders?: ReturnHeaders | undefined;
|
|
79
|
+
}) | undefined): Promise<[AsResponse] extends [true] ? Response : [ReturnHeaders] extends [true] ? {
|
|
80
|
+
headers: Headers;
|
|
81
|
+
response: Response;
|
|
82
|
+
} : Response>;
|
|
83
|
+
options: {
|
|
84
|
+
method: "POST";
|
|
85
|
+
requireHeaders: false;
|
|
86
|
+
} & {
|
|
87
|
+
use: any[];
|
|
88
|
+
};
|
|
89
|
+
path: "/playcademy";
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
};
|