@elasticpath/plasmic-backend-sdk 0.0.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 +247 -0
- package/dist/client.d.ts +107 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +259 -0
- package/dist/client.js.map +1 -0
- package/dist/generated/index.d.ts +3 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/index.js +4 -0
- package/dist/generated/index.js.map +1 -0
- package/dist/generated/sdk.gen.d.ts +203 -0
- package/dist/generated/sdk.gen.d.ts.map +1 -0
- package/dist/generated/sdk.gen.js +393 -0
- package/dist/generated/sdk.gen.js.map +1 -0
- package/dist/generated/types.gen.d.ts +798 -0
- package/dist/generated/types.gen.d.ts.map +1 -0
- package/dist/generated/types.gen.js +3 -0
- package/dist/generated/types.gen.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -0
- package/dist/src/client.d.ts +80 -0
- package/dist/src/client.d.ts.map +1 -0
- package/dist/src/client.js +176 -0
- package/dist/src/client.js.map +1 -0
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +10 -0
- package/dist/src/index.js.map +1 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# @elasticpath/plasmic-backend-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for Plasmic Admin API with automatic authentication handling.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Type-safe API client generated from OpenAPI specification
|
|
8
|
+
- Automatic CSRF token management
|
|
9
|
+
- Session-based cookie authentication
|
|
10
|
+
- Support for all Plasmic project, workspace, and admin operations
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @elasticpath/plasmic-backend-sdk
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Quick Start
|
|
19
|
+
|
|
20
|
+
```typescript
|
|
21
|
+
import { createPlasmicClient, listProjects, createProject } from '@elasticpath/plasmic-backend-sdk';
|
|
22
|
+
|
|
23
|
+
// Create an authenticated client
|
|
24
|
+
const plasmic = createPlasmicClient('https://studio.plasmic.app');
|
|
25
|
+
|
|
26
|
+
// Authenticate
|
|
27
|
+
await plasmic.authenticate('user@example.com', 'password');
|
|
28
|
+
|
|
29
|
+
// List all projects
|
|
30
|
+
const { data } = await listProjects({
|
|
31
|
+
client: plasmic.client,
|
|
32
|
+
query: { query: 'all' },
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
console.log(data?.projects);
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Authentication
|
|
39
|
+
|
|
40
|
+
The SDK handles session-based authentication with CSRF token management:
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
const plasmic = createPlasmicClient({
|
|
44
|
+
baseUrl: 'https://studio.plasmic.app',
|
|
45
|
+
throwOnError: true, // default: true
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// Login with email/password
|
|
49
|
+
await plasmic.authenticate('user@example.com', 'password');
|
|
50
|
+
|
|
51
|
+
// Check if authenticated
|
|
52
|
+
console.log(plasmic.isAuthenticated); // true
|
|
53
|
+
|
|
54
|
+
// Sign out
|
|
55
|
+
await plasmic.signOut();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Session Validation
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// Check if an existing session is still valid
|
|
62
|
+
const isValid = await plasmic.checkSession();
|
|
63
|
+
if (!isValid) {
|
|
64
|
+
await plasmic.authenticate('user@example.com', 'password');
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## API Reference
|
|
69
|
+
|
|
70
|
+
### Projects
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import {
|
|
74
|
+
listProjects,
|
|
75
|
+
getProject,
|
|
76
|
+
createProject,
|
|
77
|
+
updateProject,
|
|
78
|
+
deleteProject,
|
|
79
|
+
cloneProject,
|
|
80
|
+
} from '@elasticpath/plasmic-backend-sdk';
|
|
81
|
+
|
|
82
|
+
// List all projects
|
|
83
|
+
const { data } = await listProjects({
|
|
84
|
+
client: plasmic.client,
|
|
85
|
+
query: { query: 'all' },
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// Get a specific project
|
|
89
|
+
const { data: project } = await getProject({
|
|
90
|
+
client: plasmic.client,
|
|
91
|
+
path: { projectId: 'project-id' },
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Create a new project
|
|
95
|
+
const { data: newProject } = await createProject({
|
|
96
|
+
client: plasmic.client,
|
|
97
|
+
body: {
|
|
98
|
+
name: 'My New Project',
|
|
99
|
+
workspaceId: 'workspace-id',
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Update a project
|
|
104
|
+
await updateProject({
|
|
105
|
+
client: plasmic.client,
|
|
106
|
+
path: { projectId: 'project-id' },
|
|
107
|
+
body: { name: 'Updated Name' },
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Delete a project
|
|
111
|
+
await deleteProject({
|
|
112
|
+
client: plasmic.client,
|
|
113
|
+
path: { projectId: 'project-id' },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Clone a project
|
|
117
|
+
const { data: cloned } = await cloneProject({
|
|
118
|
+
client: plasmic.client,
|
|
119
|
+
path: { projectId: 'project-id' },
|
|
120
|
+
body: {
|
|
121
|
+
name: 'Copy of Project',
|
|
122
|
+
workspaceId: 'target-workspace-id',
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Workspaces
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
import {
|
|
131
|
+
listWorkspaces,
|
|
132
|
+
getWorkspace,
|
|
133
|
+
createWorkspace,
|
|
134
|
+
updateWorkspace,
|
|
135
|
+
deleteWorkspace,
|
|
136
|
+
} from '@elasticpath/plasmic-backend-sdk';
|
|
137
|
+
|
|
138
|
+
// List all workspaces
|
|
139
|
+
const { data } = await listWorkspaces({ client: plasmic.client });
|
|
140
|
+
|
|
141
|
+
// Get a specific workspace
|
|
142
|
+
const { data: workspace } = await getWorkspace({
|
|
143
|
+
client: plasmic.client,
|
|
144
|
+
path: { workspaceId: 'workspace-id' },
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// Create a new workspace
|
|
148
|
+
const { data: newWorkspace } = await createWorkspace({
|
|
149
|
+
client: plasmic.client,
|
|
150
|
+
body: {
|
|
151
|
+
name: 'My Workspace',
|
|
152
|
+
teamId: 'team-id',
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Admin Operations
|
|
158
|
+
|
|
159
|
+
Admin operations require the user to be listed in the server's admin email configuration.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
import {
|
|
163
|
+
adminListProjects,
|
|
164
|
+
adminCloneProject,
|
|
165
|
+
adminDeleteProject,
|
|
166
|
+
adminRestoreProject,
|
|
167
|
+
adminChangeProjectOwner,
|
|
168
|
+
adminRevertRevision,
|
|
169
|
+
} from '@elasticpath/plasmic-backend-sdk';
|
|
170
|
+
|
|
171
|
+
// List all projects (admin)
|
|
172
|
+
const { data } = await adminListProjects({
|
|
173
|
+
client: plasmic.client,
|
|
174
|
+
body: { ownerId: 'user-id' }, // optional filter
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Clone any project (admin)
|
|
178
|
+
const { data: cloned } = await adminCloneProject({
|
|
179
|
+
client: plasmic.client,
|
|
180
|
+
body: {
|
|
181
|
+
projectId: 'project-id',
|
|
182
|
+
revisionNum: 5, // optional: clone from specific revision
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
// Restore a deleted project
|
|
187
|
+
await adminRestoreProject({
|
|
188
|
+
client: plasmic.client,
|
|
189
|
+
body: { id: 'project-id' },
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
// Change project owner
|
|
193
|
+
await adminChangeProjectOwner({
|
|
194
|
+
client: plasmic.client,
|
|
195
|
+
body: {
|
|
196
|
+
projectId: 'project-id',
|
|
197
|
+
ownerEmail: 'newowner@example.com',
|
|
198
|
+
},
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Revert to a previous revision
|
|
202
|
+
await adminRevertRevision({
|
|
203
|
+
client: plasmic.client,
|
|
204
|
+
body: {
|
|
205
|
+
projectId: 'project-id',
|
|
206
|
+
revision: 10,
|
|
207
|
+
},
|
|
208
|
+
});
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
### Setup
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
# Install dependencies
|
|
217
|
+
pnpm install
|
|
218
|
+
|
|
219
|
+
# Generate SDK from OpenAPI spec
|
|
220
|
+
pnpm generate
|
|
221
|
+
|
|
222
|
+
# Build the package
|
|
223
|
+
pnpm build
|
|
224
|
+
|
|
225
|
+
# Run tests
|
|
226
|
+
pnpm test
|
|
227
|
+
|
|
228
|
+
# Lint code
|
|
229
|
+
pnpm lint
|
|
230
|
+
|
|
231
|
+
# Format code
|
|
232
|
+
pnpm format
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### Regenerating the SDK
|
|
236
|
+
|
|
237
|
+
If the OpenAPI specification changes, regenerate the SDK:
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
pnpm generate
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
This uses [heyapi](https://heyapi.dev/) to generate type-safe API functions from the OpenAPI spec.
|
|
244
|
+
|
|
245
|
+
## License
|
|
246
|
+
|
|
247
|
+
MIT
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plasmic Admin API - Auth Client Wrapper
|
|
3
|
+
*
|
|
4
|
+
* This module wraps the heyapi-generated SDK with session-based authentication
|
|
5
|
+
* handling (CSRF tokens + cookie auth).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { createPlasmicClient } from '@elasticpath/plasmic-backend-sdk';
|
|
9
|
+
* import { listProjects, createProject } from '@elasticpath/plasmic-backend-sdk/generated';
|
|
10
|
+
*
|
|
11
|
+
* const plasmic = createPlasmicClient('https://studio.plasmic.app');
|
|
12
|
+
* await plasmic.authenticate('user@example.com', 'password');
|
|
13
|
+
*
|
|
14
|
+
* const { data } = await listProjects({ client: plasmic.client, query: { query: 'all' } });
|
|
15
|
+
*/
|
|
16
|
+
import { type Client } from '@hey-api/client-fetch';
|
|
17
|
+
export interface PlasmicClientOptions {
|
|
18
|
+
/** Base URL of your Plasmic instance (e.g., 'https://studio.plasmic.app') */
|
|
19
|
+
baseUrl: string;
|
|
20
|
+
/** Optional: throw on HTTP errors (default: true) */
|
|
21
|
+
throwOnError?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export interface AuthenticateOptions {
|
|
24
|
+
email: string;
|
|
25
|
+
password: string;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Creates a Plasmic API client with automatic session-based authentication handling.
|
|
29
|
+
*
|
|
30
|
+
* Features:
|
|
31
|
+
* - Automatic CSRF token management
|
|
32
|
+
* - Cookie credentials included in all requests
|
|
33
|
+
* - Request interceptor for X-CSRF-Token header
|
|
34
|
+
* - Handles CSRF-exempt admin routes
|
|
35
|
+
*/
|
|
36
|
+
export declare function createPlasmicClient(options: PlasmicClientOptions | string): {
|
|
37
|
+
/**
|
|
38
|
+
* The underlying heyapi client instance.
|
|
39
|
+
* Pass this to generated SDK functions.
|
|
40
|
+
*/
|
|
41
|
+
client: Client<Request, Response, unknown, import("@hey-api/client-fetch").RequestOptions<boolean, string>>;
|
|
42
|
+
/**
|
|
43
|
+
* Whether the client is currently authenticated.
|
|
44
|
+
*/
|
|
45
|
+
readonly isAuthenticated: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* Get the current CSRF token (for debugging/advanced use).
|
|
48
|
+
*/
|
|
49
|
+
getCsrfToken(): string | null;
|
|
50
|
+
/**
|
|
51
|
+
* Fetch a fresh CSRF token from the server.
|
|
52
|
+
* Called automatically during authenticate().
|
|
53
|
+
*/
|
|
54
|
+
refreshCsrf(): Promise<string | null>;
|
|
55
|
+
/**
|
|
56
|
+
* Authenticate with email and password.
|
|
57
|
+
* Handles the full CSRF + login flow:
|
|
58
|
+
* 1. Fetch initial CSRF token
|
|
59
|
+
* 2. Login with credentials
|
|
60
|
+
* 3. Refresh CSRF token (important - token changes after login)
|
|
61
|
+
*/
|
|
62
|
+
authenticate(emailOrOptions: string | AuthenticateOptions, password?: string): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Sign out and clear session.
|
|
65
|
+
*/
|
|
66
|
+
signOut(): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Check if the current session is valid by fetching user info.
|
|
69
|
+
*/
|
|
70
|
+
checkSession(): Promise<boolean>;
|
|
71
|
+
/**
|
|
72
|
+
* Check if an error is a CSRF-related error.
|
|
73
|
+
* CSRF errors are 403 responses with specific error messages.
|
|
74
|
+
*/
|
|
75
|
+
isCsrfError(error: unknown): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Execute an API call with automatic CSRF retry.
|
|
78
|
+
* If the call fails due to CSRF token issues, refreshes the token and retries once.
|
|
79
|
+
*
|
|
80
|
+
* Works with hey-api's { data, error } response format.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* const response = await plasmic.withCsrfRetry(() =>
|
|
84
|
+
* updateHostUrl({
|
|
85
|
+
* client: plasmic.client,
|
|
86
|
+
* path: { projectId },
|
|
87
|
+
* body: { hostUrl }
|
|
88
|
+
* })
|
|
89
|
+
* )
|
|
90
|
+
*/
|
|
91
|
+
withCsrfRetry<T>(fn: () => Promise<{
|
|
92
|
+
data?: T;
|
|
93
|
+
error?: unknown;
|
|
94
|
+
}>): Promise<{
|
|
95
|
+
data?: T;
|
|
96
|
+
error?: unknown;
|
|
97
|
+
}>;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Type for the Plasmic client instance returned by createPlasmicClient.
|
|
101
|
+
*/
|
|
102
|
+
export type PlasmicClient = ReturnType<typeof createPlasmicClient>;
|
|
103
|
+
/**
|
|
104
|
+
* Re-export the underlying client type for convenience.
|
|
105
|
+
*/
|
|
106
|
+
export type { Client };
|
|
107
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAwBlE,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM;IAmFtE;;;OAGG;;IAGH;;OAEG;;IAKH;;OAEG;;IAKH;;;OAGG;mBACkB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAe3C;;;;;;OAMG;iCAEe,MAAM,GAAG,mBAAmB,aACjC,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC;IAmChB;;OAEG;eACc,OAAO,CAAC,IAAI,CAAC;IAU9B;;OAEG;oBACmB,OAAO,CAAC,OAAO,CAAC;IAkBtC;;;OAGG;uBACgB,OAAO,GAAG,OAAO;IAkBpC;;;;;;;;;;;;;;OAcG;kBACiB,CAAC,MACf,MAAM,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,GAC/C,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,CAAC,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;EAa5C;AAED;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAEnE;;GAEG;AACH,YAAY,EAAE,MAAM,EAAE,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plasmic Admin API - Auth Client Wrapper
|
|
3
|
+
*
|
|
4
|
+
* This module wraps the heyapi-generated SDK with session-based authentication
|
|
5
|
+
* handling (CSRF tokens + cookie auth).
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { createPlasmicClient } from '@elasticpath/plasmic-backend-sdk';
|
|
9
|
+
* import { listProjects, createProject } from '@elasticpath/plasmic-backend-sdk/generated';
|
|
10
|
+
*
|
|
11
|
+
* const plasmic = createPlasmicClient('https://studio.plasmic.app');
|
|
12
|
+
* await plasmic.authenticate('user@example.com', 'password');
|
|
13
|
+
*
|
|
14
|
+
* const { data } = await listProjects({ client: plasmic.client, query: { query: 'all' } });
|
|
15
|
+
*/
|
|
16
|
+
import { createClient } from '@hey-api/client-fetch';
|
|
17
|
+
// Import generated SDK functions
|
|
18
|
+
import { getCsrfToken, getCurrentUser, login as sdkLogin, logout as sdkLogout, } from './generated/sdk.gen.js';
|
|
19
|
+
/**
|
|
20
|
+
* Admin routes that are exempt from CSRF token requirement.
|
|
21
|
+
* These routes are listed in csrfFreeStaticRoutes in the Plasmic backend.
|
|
22
|
+
*/
|
|
23
|
+
const CSRF_EXEMPT_PATHS = [
|
|
24
|
+
'/admin/delete-project',
|
|
25
|
+
'/admin/restore-project',
|
|
26
|
+
'/admin/clone',
|
|
27
|
+
'/admin/revert-project-revision',
|
|
28
|
+
'/admin/login-as',
|
|
29
|
+
'/admin/deactivate-user',
|
|
30
|
+
'/admin/devflags',
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Creates a Plasmic API client with automatic session-based authentication handling.
|
|
34
|
+
*
|
|
35
|
+
* Features:
|
|
36
|
+
* - Automatic CSRF token management
|
|
37
|
+
* - Cookie credentials included in all requests
|
|
38
|
+
* - Request interceptor for X-CSRF-Token header
|
|
39
|
+
* - Handles CSRF-exempt admin routes
|
|
40
|
+
*/
|
|
41
|
+
export function createPlasmicClient(options) {
|
|
42
|
+
const config = typeof options === 'string' ? { baseUrl: options } : options;
|
|
43
|
+
const { baseUrl, throwOnError = true } = config;
|
|
44
|
+
// Internal state
|
|
45
|
+
let csrfToken = null;
|
|
46
|
+
let isAuthenticated = false;
|
|
47
|
+
// Create the underlying heyapi client
|
|
48
|
+
const client = createClient({
|
|
49
|
+
baseUrl: `${baseUrl.replace(/\/$/, '')}/api/v1`,
|
|
50
|
+
credentials: 'include', // Send cookies with every request
|
|
51
|
+
throwOnError,
|
|
52
|
+
});
|
|
53
|
+
/**
|
|
54
|
+
* Request interceptor: JSON-encode query params for GET/DELETE requests.
|
|
55
|
+
* Plasmic API expects all query parameter values to be JSON-encoded strings.
|
|
56
|
+
* The server uses JSON.parse() on each query param value.
|
|
57
|
+
*
|
|
58
|
+
* Exception: CMSE endpoints read query params directly without JSON.parse(),
|
|
59
|
+
* so we skip JSON encoding for those endpoints.
|
|
60
|
+
*/
|
|
61
|
+
client.interceptors.request.use((request) => {
|
|
62
|
+
if (request.method === 'GET' || request.method === 'DELETE') {
|
|
63
|
+
const url = new URL(request.url);
|
|
64
|
+
// CMSE endpoints don't use parseQueryParams - they read query params directly
|
|
65
|
+
// All other Plasmic endpoints use JSON.parse on query params
|
|
66
|
+
if (url.pathname.includes('/cmse/')) {
|
|
67
|
+
return request;
|
|
68
|
+
}
|
|
69
|
+
url.searchParams.forEach((value, key) => {
|
|
70
|
+
try {
|
|
71
|
+
JSON.parse(value); // Already valid JSON, skip
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
url.searchParams.set(key, JSON.stringify(value));
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
return new Request(url.toString(), request);
|
|
78
|
+
}
|
|
79
|
+
return request;
|
|
80
|
+
});
|
|
81
|
+
/**
|
|
82
|
+
* Request interceptor: Add CSRF token to non-exempt requests
|
|
83
|
+
*/
|
|
84
|
+
client.interceptors.request.use((request) => {
|
|
85
|
+
// Check if this path is CSRF-exempt
|
|
86
|
+
const url = new URL(request.url);
|
|
87
|
+
const isExempt = CSRF_EXEMPT_PATHS.some((exemptPath) => url.pathname.endsWith(exemptPath));
|
|
88
|
+
// Add CSRF token header if we have one and route is not exempt
|
|
89
|
+
if (csrfToken && !isExempt) {
|
|
90
|
+
// Create new Request with CSRF header using clone pattern
|
|
91
|
+
// Pass the original Request as first arg - this properly clones:
|
|
92
|
+
// - Body stream (ReadableStream for PUT/POST)
|
|
93
|
+
// - All other properties (mode, cache, signal, referrer, etc.)
|
|
94
|
+
// Then override only the headers
|
|
95
|
+
const headers = new Headers(request.headers);
|
|
96
|
+
headers.set('X-CSRF-Token', csrfToken);
|
|
97
|
+
return new Request(request, { headers });
|
|
98
|
+
}
|
|
99
|
+
return request;
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* Response interceptor: Handle auth errors
|
|
103
|
+
*/
|
|
104
|
+
client.interceptors.response.use((response) => {
|
|
105
|
+
// If we get a 401, mark as unauthenticated
|
|
106
|
+
if (response.status === 401) {
|
|
107
|
+
isAuthenticated = false;
|
|
108
|
+
csrfToken = null;
|
|
109
|
+
}
|
|
110
|
+
return response;
|
|
111
|
+
});
|
|
112
|
+
return {
|
|
113
|
+
/**
|
|
114
|
+
* The underlying heyapi client instance.
|
|
115
|
+
* Pass this to generated SDK functions.
|
|
116
|
+
*/
|
|
117
|
+
client,
|
|
118
|
+
/**
|
|
119
|
+
* Whether the client is currently authenticated.
|
|
120
|
+
*/
|
|
121
|
+
get isAuthenticated() {
|
|
122
|
+
return isAuthenticated;
|
|
123
|
+
},
|
|
124
|
+
/**
|
|
125
|
+
* Get the current CSRF token (for debugging/advanced use).
|
|
126
|
+
*/
|
|
127
|
+
getCsrfToken() {
|
|
128
|
+
return csrfToken;
|
|
129
|
+
},
|
|
130
|
+
/**
|
|
131
|
+
* Fetch a fresh CSRF token from the server.
|
|
132
|
+
* Called automatically during authenticate().
|
|
133
|
+
*/
|
|
134
|
+
async refreshCsrf() {
|
|
135
|
+
try {
|
|
136
|
+
const { data, error } = await getCsrfToken({ client });
|
|
137
|
+
if (error) {
|
|
138
|
+
console.error('Failed to fetch CSRF token:', error);
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
csrfToken = data?.csrf ?? null;
|
|
142
|
+
return csrfToken;
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
console.error('Failed to fetch CSRF token:', err);
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
/**
|
|
150
|
+
* Authenticate with email and password.
|
|
151
|
+
* Handles the full CSRF + login flow:
|
|
152
|
+
* 1. Fetch initial CSRF token
|
|
153
|
+
* 2. Login with credentials
|
|
154
|
+
* 3. Refresh CSRF token (important - token changes after login)
|
|
155
|
+
*/
|
|
156
|
+
async authenticate(emailOrOptions, password) {
|
|
157
|
+
const email = typeof emailOrOptions === 'string'
|
|
158
|
+
? emailOrOptions
|
|
159
|
+
: emailOrOptions.email;
|
|
160
|
+
const pwd = typeof emailOrOptions === 'string'
|
|
161
|
+
? password
|
|
162
|
+
: emailOrOptions.password;
|
|
163
|
+
// Step 1: Get initial CSRF token
|
|
164
|
+
const initialCsrf = await this.refreshCsrf();
|
|
165
|
+
if (!initialCsrf) {
|
|
166
|
+
throw new Error('Failed to obtain CSRF token');
|
|
167
|
+
}
|
|
168
|
+
// Step 2: Login
|
|
169
|
+
const { error } = await sdkLogin({
|
|
170
|
+
client,
|
|
171
|
+
body: { email, password: pwd },
|
|
172
|
+
});
|
|
173
|
+
if (error) {
|
|
174
|
+
throw new Error(`Login failed: ${error.message || 'Unknown error'}`);
|
|
175
|
+
}
|
|
176
|
+
// Step 3: Refresh CSRF token after login (critical!)
|
|
177
|
+
// The CSRF token changes after authentication
|
|
178
|
+
await this.refreshCsrf();
|
|
179
|
+
isAuthenticated = true;
|
|
180
|
+
},
|
|
181
|
+
/**
|
|
182
|
+
* Sign out and clear session.
|
|
183
|
+
*/
|
|
184
|
+
async signOut() {
|
|
185
|
+
try {
|
|
186
|
+
await sdkLogout({ client });
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Ignore logout errors - we're clearing state anyway
|
|
190
|
+
}
|
|
191
|
+
csrfToken = null;
|
|
192
|
+
isAuthenticated = false;
|
|
193
|
+
},
|
|
194
|
+
/**
|
|
195
|
+
* Check if the current session is valid by fetching user info.
|
|
196
|
+
*/
|
|
197
|
+
async checkSession() {
|
|
198
|
+
try {
|
|
199
|
+
// Call /auth/self to verify the session is actually authenticated
|
|
200
|
+
const { data, error } = await getCurrentUser({ client });
|
|
201
|
+
if (error || !data?.user) {
|
|
202
|
+
isAuthenticated = false;
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
isAuthenticated = true;
|
|
206
|
+
// Refresh CSRF token after successful session check
|
|
207
|
+
await this.refreshCsrf();
|
|
208
|
+
return true;
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
isAuthenticated = false;
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
/**
|
|
216
|
+
* Check if an error is a CSRF-related error.
|
|
217
|
+
* CSRF errors are 403 responses with specific error messages.
|
|
218
|
+
*/
|
|
219
|
+
isCsrfError(error) {
|
|
220
|
+
if (error && typeof error === 'object') {
|
|
221
|
+
const err = error;
|
|
222
|
+
// Check for status code
|
|
223
|
+
if (err.status === 403 || err.statusCode === 403) {
|
|
224
|
+
// Check various properties where the message might be
|
|
225
|
+
const message = String(err.message || err.body || err.detail || err.error || '');
|
|
226
|
+
return (message.includes('CSRF token missing') ||
|
|
227
|
+
message.includes('CSRF token mismatch'));
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return false;
|
|
231
|
+
},
|
|
232
|
+
/**
|
|
233
|
+
* Execute an API call with automatic CSRF retry.
|
|
234
|
+
* If the call fails due to CSRF token issues, refreshes the token and retries once.
|
|
235
|
+
*
|
|
236
|
+
* Works with hey-api's { data, error } response format.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* const response = await plasmic.withCsrfRetry(() =>
|
|
240
|
+
* updateHostUrl({
|
|
241
|
+
* client: plasmic.client,
|
|
242
|
+
* path: { projectId },
|
|
243
|
+
* body: { hostUrl }
|
|
244
|
+
* })
|
|
245
|
+
* )
|
|
246
|
+
*/
|
|
247
|
+
async withCsrfRetry(fn) {
|
|
248
|
+
const result = await fn();
|
|
249
|
+
// Check if this is a CSRF error
|
|
250
|
+
if (result.error && this.isCsrfError(result.error)) {
|
|
251
|
+
// Refresh token and retry once
|
|
252
|
+
await this.refreshCsrf();
|
|
253
|
+
return await fn();
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/generated/index.ts"],"names":[],"mappings":"AACA,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/generated/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,cAAc,WAAW,CAAC;AAC1B,cAAc,aAAa,CAAC"}
|