Skip to content

Commit a6102bd

Browse files
authored
merge: squarecloud/dev for Square Cloud API v2
Dev
2 parents 81a9a67 + 8fb2893 commit a6102bd

File tree

19 files changed

+705
-219
lines changed

19 files changed

+705
-219
lines changed

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
engine-strict=true

package.json

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@squarecloud/api",
3-
"version": "2.2.2",
4-
"description": "A NodeJS Wrapper for the SquareCloud API",
3+
"version": "3.0.0",
4+
"description": "A NodeJS wrapper for Square Cloud API",
55
"main": "lib/index.js",
66
"types": "lib/index.d.ts",
77
"scripts": {
@@ -13,15 +13,22 @@
1313
"square",
1414
"squarecloud",
1515
"api",
16-
"typescript"
16+
"typescript",
17+
"app",
18+
"bot",
19+
"website",
20+
"host"
1721
],
1822
"author": {
1923
"name": "bluey#0012",
20-
"url": "https://discord.com/users/702529018410303640"
24+
"url": "https://github.com/bluee-js"
2125
},
2226
"license": "MIT",
27+
"engines": {
28+
"node": ">=19.0.0"
29+
},
2330
"devDependencies": {
24-
"@types/node": "^18.15.11",
31+
"@types/node": "^18.15.13",
2532
"ts-node-dev": "^2.0.0",
2633
"typescript": "^4.9.5"
2734
},

src/assertions.ts

Lines changed: 17 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,48 @@
11
import z from 'zod';
2-
import { SquareCloudAPIError } from './ApiManager';
2+
import SquareCloudAPIError from './structures/error';
3+
4+
const stringSchema = z.coerce.string();
5+
const booleanSchema = z.coerce.boolean();
6+
const pathLikeSchema = z.string().or(z.instanceof(Buffer));
37

48
export function validateString(
59
value: any,
610
code?: string,
7-
starts: string = ''
11+
starts?: string
812
): asserts value is string {
913
if (starts) {
1014
validateString(starts);
1115
}
1216

13-
handleParser(
14-
() => z.string().parse(value),
15-
'Expect string, got ' + typeof value,
16-
code
17-
);
17+
handleParser(stringSchema, value, 'string', code);
1818
}
1919

2020
export function validateBoolean(
2121
value: any,
2222
code?: string
2323
): asserts value is boolean {
24-
handleParser(
25-
() => z.boolean().parse(value),
26-
'Expect boolean, got ' + typeof value,
27-
code
28-
);
24+
handleParser(booleanSchema, value, 'boolean', code);
2925
}
3026

3127
export function validatePathLike(
3228
value: any,
3329
code?: string
3430
): asserts value is string | Buffer {
35-
handleParser(
36-
() => {
37-
z.string()
38-
.or(z.custom((value) => value instanceof Buffer))
39-
.parse(value);
40-
},
41-
'Expect string or Buffer, got ' + typeof value,
42-
code
43-
);
31+
handleParser(pathLikeSchema, value, 'string or Buffer', code);
4432
}
4533

46-
function handleParser(func: any, message: string, code?: string) {
34+
function handleParser(
35+
schema: z.Schema,
36+
value: any,
37+
expect: string,
38+
code?: string
39+
) {
4740
try {
48-
func();
41+
schema.parse(value);
4942
} catch {
5043
throw new SquareCloudAPIError(
5144
code ? `INVALID_${code}` : 'VALIDATION_ERROR',
52-
message
45+
`Expect ${expect}, got ${typeof value}`
5346
);
5447
}
5548
}

src/index.ts

Lines changed: 24 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,39 @@
1-
import FormData from 'form-data';
2-
import { readFile } from 'fs/promises';
3-
import { ApiManager, SquareCloudAPIError } from './ApiManager';
4-
import { validatePathLike, validateString } from './Assertions';
5-
import { Application } from './structures/Application';
6-
import { FullUser, User } from './structures/User';
7-
import { Options } from './typings';
8-
9-
class SquareCloudAPI {
1+
import { validateString } from './assertions';
2+
import APIManager from './managers/api';
3+
import ApplicationManager from './managers/application';
4+
import ExperimentalManager from './managers/experimental';
5+
import UserManager from './managers/user';
6+
import { APIOptions } from './types';
7+
8+
export class SquareCloudAPI {
109
static apiInfo = {
11-
version: 'v1',
10+
latestVersion: 'v2',
1211
baseUrl: 'https://api.squarecloud.app/',
1312
};
1413

15-
private apiManager: ApiManager;
16-
private experimental?: boolean;
14+
private apiManager: APIManager;
15+
16+
/** Use experimental features */
17+
public experimental?: ExperimentalManager;
18+
/** The applications manager */
19+
public applications: ApplicationManager;
20+
/** The users manager */
21+
public users: UserManager;
1722

1823
/**
1924
* Creates an API instance
2025
*
2126
* @param apiKey - Your API Token (generate at [Square Cloud Dashboard](https://squarecloud.app/dashboard))
22-
* @param options.customApiManager - Custom API manager. Just use if you know what you are doing!
2327
* @param options.experimental - Whether to enable experimental features
2428
*/
25-
constructor(apiKey: string, options?: Options) {
29+
constructor(apiKey: string, options?: APIOptions) {
2630
validateString(apiKey, 'API_KEY');
2731

28-
this.apiManager = new ApiManager(apiKey);
29-
this.experimental = Boolean(options?.experimental);
30-
}
31-
32-
/**
33-
* Gets a user's informations
34-
*
35-
* @param userId - The user ID, if not provided it will get your own information
36-
*/
37-
async getUser(): Promise<FullUser>;
38-
async getUser(userId: string): Promise<User>;
39-
async getUser(userId?: string): Promise<User> {
40-
if (userId) {
41-
validateString(userId, 'USER_ID');
42-
}
43-
44-
const data = await this.apiManager.user(userId);
45-
const hasAccess = data.user.email && data.user.email !== 'Access denied';
46-
47-
return new (hasAccess ? FullUser : User)(this.apiManager, data);
48-
}
49-
50-
/**
51-
* Returns an application that you can manage or get information
52-
*
53-
* @param appId - The application ID, you must own the application
54-
*/
55-
async getApplication(appId: string): Promise<Application> {
56-
validateString(appId, 'APP_ID');
57-
58-
const data = await this.apiManager.user();
59-
const applications = data.applications || [];
60-
const applicaton = applications.find((app) => app.id === appId);
61-
62-
if (!applicaton) {
63-
throw new SquareCloudAPIError('APP_NOT_FOUND');
64-
}
65-
66-
return new Application(this.apiManager, applicaton);
67-
}
68-
69-
/**
70-
* Upload a new application to Square Cloud
71-
*
72-
* - Don't forget the [configuration file](https://config.squarecloud.app/).
73-
* - This only accepts .zip files.
74-
*
75-
* - Tip: use this to get an absolute path.
76-
* ```ts
77-
* require('path').join(__dirname, 'fileName')
78-
* ```
79-
*
80-
* @param file - Buffer or absolute path to the file
81-
*
82-
* @returns The uploaded application ID
83-
*/
84-
async uploadApplication(file: string | Buffer) {
85-
validatePathLike(file, 'UPLOAD_DATA');
86-
87-
if (typeof file === 'string') {
88-
file = await readFile(file);
89-
}
90-
91-
const formData = new FormData();
92-
formData.append('file', file, { filename: 'app.zip' });
93-
94-
const data = await this.apiManager.fetch('upload', {
95-
method: 'POST',
96-
body: formData.getBuffer(),
97-
headers: formData.getHeaders(),
98-
});
99-
100-
return <string>data?.app?.id;
101-
}
102-
103-
/**
104-
* @experimental
105-
* Use the new Square Cloud experimental AI feature.
106-
* **May have bugs.**
107-
*
108-
* @param question - The question you want to be answered :)
109-
* @param prompt - Optional context or previous messages
110-
*/
111-
async askAi(question: string, prompt?: string): Promise<string | undefined> {
112-
if (!this.experimental) {
113-
return;
114-
}
115-
116-
const data = await this.apiManager.fetch(
117-
'ai',
118-
{
119-
method: 'POST',
120-
body: JSON.stringify({ question, prompt }),
121-
},
122-
'experimental'
123-
);
124-
125-
return data?.response;
32+
this.apiManager = new APIManager(apiKey);
33+
this.experimental = options?.experimental
34+
? new ExperimentalManager(this.apiManager)
35+
: undefined;
36+
this.applications = new ApplicationManager(this.apiManager);
37+
this.users = new UserManager(this.apiManager);
12638
}
12739
}
128-
129-
module.exports = Object.assign(SquareCloudAPI, {
130-
default: SquareCloudAPI,
131-
ApiManager,
132-
Application,
133-
FullUser,
134-
User,
135-
});
136-
137-
export default SquareCloudAPI;
138-
export * from './typings';
139-
export { ApiManager, Application, FullUser, User };
140-

src/managers/api.ts

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import SquareCloudAPIError from '../structures/error';
2+
import { APIRootPath, APIVersion, UserResponse } from '../types';
3+
import { APIResponse } from '../types';
4+
5+
export default class APIManager {
6+
public readonly baseUrl = 'https://api.squarecloud.app';
7+
8+
constructor(readonly apiKey: string) {}
9+
10+
user(userId?: string): Promise<APIResponse<UserResponse>> {
11+
return this.fetch('user' + (userId ? `/${userId}` : ''));
12+
}
13+
14+
application(
15+
path: string,
16+
appId: string,
17+
options?: RequestInit | 'GET' | 'POST' | 'DELETE'
18+
): Promise<APIResponse> {
19+
if (typeof options === 'string') {
20+
options = {
21+
method: options,
22+
};
23+
}
24+
25+
return this.fetch(`apps/${appId}/${path}`, options);
26+
}
27+
28+
async fetch(
29+
path: string,
30+
options: RequestInit = {},
31+
version: APIVersion<1 | 2> = 'v2',
32+
rootPath?: APIRootPath
33+
): Promise<APIResponse> {
34+
options = {
35+
...options,
36+
method: options.method || 'GET',
37+
headers: { ...(options.headers || {}), Authorization: this.apiKey },
38+
};
39+
40+
const res = await fetch(
41+
`${this.baseUrl}/${version}${rootPath ? `/${rootPath}` : ''}/${path}`,
42+
options
43+
).catch((err) => {
44+
throw new SquareCloudAPIError(err.code);
45+
});
46+
const data = await res.json();
47+
48+
if (!data || data.status === 'error' || !res.ok) {
49+
throw new SquareCloudAPIError(data?.code || 'COMMON_ERROR');
50+
}
51+
52+
return data;
53+
}
54+
}

src/managers/application.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import FormData from 'form-data';
2+
import { readFile } from 'fs/promises';
3+
import { validatePathLike, validateString } from '../assertions';
4+
import Application from '../structures/application';
5+
import Collection from '../structures/collection';
6+
import { FullUser } from '../structures/user';
7+
import { APIResponse, UploadedApplicationResponse } from '../types';
8+
import APIManager from './api';
9+
10+
export default class ApplicationManager {
11+
constructor(private readonly apiManager: APIManager) {}
12+
13+
/**
14+
* If the ID is provided, it will return an application that you can manage or get information
15+
* If the ID is not provided, it will return a collection of applications
16+
*
17+
* @param appId - The application ID, you must own the application
18+
*/
19+
async get(): Promise<Collection<string, Application> | undefined>;
20+
async get(appId: string): Promise<Application | undefined>;
21+
async get(
22+
appId?: string
23+
): Promise<Application | Collection<string, Application> | undefined> {
24+
const { response } = await this.apiManager.user();
25+
if (!response) {
26+
return;
27+
}
28+
29+
const { applications } = new FullUser(this.apiManager, response);
30+
31+
if (appId) {
32+
validateString(appId, 'APP_ID');
33+
return applications.get(appId);
34+
}
35+
return applications;
36+
}
37+
38+
/**
39+
* Uploads an application
40+
*
41+
* @param file - The zip file path or Buffer
42+
* @returns The uploaded application data
43+
*/
44+
async create(file: string | Buffer) {
45+
validatePathLike(file, 'COMMIT_DATA');
46+
47+
if (typeof file === 'string') {
48+
file = await readFile(file);
49+
}
50+
51+
const formData = new FormData();
52+
formData.append('file', file, { filename: 'app.zip' });
53+
54+
const data = <APIResponse<UploadedApplicationResponse>>(
55+
await this.apiManager.fetch('apps/upload', {
56+
method: 'POST',
57+
body: formData.getBuffer(),
58+
headers: formData.getHeaders(),
59+
})
60+
);
61+
62+
return data?.response?.app;
63+
}
64+
}

0 commit comments

Comments
 (0)