Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

classcharts-api

Package Overview
Dependencies
Maintainers
1
Versions
53
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

classcharts-api - npm Package Compare versions

Comparing version 2.9.0 to 2.12.0

esm/_dnt.test_shims.d.ts.map

1

esm/mod.d.ts
export * from "./src/core/studentClient.js";
export * from "./src/core/parentClient.js";
//# sourceMappingURL=mod.d.ts.map

19

esm/src/core/baseClient.d.ts

@@ -5,31 +5,33 @@ import type { ActivityResponse, AnnouncementsResponse, AttendanceResponse, BadgesResponse, BehaviourResponse, ClassChartsResponse, DetentionsResponse, GetActivityOptions, GetAttendanceOptions, GetBehaviourOptions, GetFullActivityOptions, GetHomeworkOptions, GetLessonsOptions, GetStudentInfoResponse, HomeworksResponse, LessonsResponse, PupilFieldsResponse } from "../types.js";

*/
export declare class BaseClient {
export declare abstract class BaseClient {
/**
* @property studentId Currently selected student ID
* Currently selected student ID
*/
studentId: number;
/**
* @property authCookies Cookies used for authentication (set during login and can be empty)
* Cookies used for authentication (set during login and can be empty)
*/
authCookies: Array<string>;
authCookies: string[];
/**
* @property sessionId Session ID used for authentication
* Session ID used for authentication
*/
sessionId: string;
/**
* @property lastPing Last time the sessionId was updated
* Last time the sessionId was updated
*/
lastPing: number;
/**
* @property API_BASE Base API URL, this is different depending on if its called as a parent or student
* Base API URL, this is different depending on if its called as a parent or student
*/
protected API_BASE: string;
/**
* Create a new client with the given API url
* @param API_BASE Base API URL, this is different depending on if its called as a parent or student
*/
constructor(API_BASE: string);
abstract login(): Promise<void>;
/**
* Revalidates the session ID.
*
* This is called automatically when the session ID is older than 3 minutes or when initially using the .login() method
* This is called automatically when the session ID is older than 3 minutes and initially using the .login() method
*/

@@ -118,1 +120,2 @@ getNewSessionId(): Promise<void>;

}
//# sourceMappingURL=baseClient.d.ts.map

@@ -7,2 +7,3 @@ import { PING_INTERVAL } from "../utils/consts.js";

/**
* Create a new client with the given API url
* @param API_BASE Base API URL, this is different depending on if its called as a parent or student

@@ -12,3 +13,3 @@ */

/**
* @property studentId Currently selected student ID
* Currently selected student ID
*/

@@ -22,3 +23,3 @@ Object.defineProperty(this, "studentId", {

/**
* @property authCookies Cookies used for authentication (set during login and can be empty)
* Cookies used for authentication (set during login and can be empty)
*/

@@ -32,3 +33,3 @@ Object.defineProperty(this, "authCookies", {

/**
* @property sessionId Session ID used for authentication
* Session ID used for authentication
*/

@@ -42,3 +43,3 @@ Object.defineProperty(this, "sessionId", {

/**
* @property lastPing Last time the sessionId was updated
* Last time the sessionId was updated
*/

@@ -52,3 +53,3 @@ Object.defineProperty(this, "lastPing", {

/**
* @property API_BASE Base API URL, this is different depending on if its called as a parent or student
* Base API URL, this is different depending on if its called as a parent or student
*/

@@ -67,3 +68,3 @@ Object.defineProperty(this, "API_BASE", {

*
* This is called automatically when the session ID is older than 3 minutes or when initially using the .login() method
* This is called automatically when the session ID is older than 3 minutes and initially using the .login() method
*/

@@ -73,3 +74,3 @@ async getNewSessionId() {

pingFormData.append("include_data", "true");
const pingData = await this.makeAuthedRequest(this.API_BASE + "/ping", {
const pingData = await this.makeAuthedRequest(`${this.API_BASE}/ping`, {
method: "POST",

@@ -91,9 +92,7 @@ body: pingFormData,

*/
async makeAuthedRequest(path, fetchOptions, options) {
if (!this.sessionId)
async makeAuthedRequest(path, fetchOptions, options = { revalidateToken: true }) {
if (!this.sessionId) {
throw new Error("No session ID");
if (!options) {
options = {};
}
if (typeof options?.revalidateToken == "undefined") {
if (typeof options?.revalidateToken === "undefined") {
options.revalidateToken = true;

@@ -105,3 +104,3 @@ }

Cookie: this?.authCookies?.join(";") ?? [],
Authorization: "Basic " + this.sessionId,
Authorization: `Basic ${this.sessionId}`,
"User-Agent": "classcharts-api https://github.com/classchartsapi/classcharts-api-js",

@@ -117,3 +116,3 @@ ...fetchOptions.headers,

const request = await fetch(path, requestOptions);
// deno-lint-ignore no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny:
let responseJSON;

@@ -124,5 +123,5 @@ try {

catch {
throw new Error("Error parsing JSON. Returned response: " + (await request.text()));
throw new Error(`Error parsing JSON. Returned response: ${await request.text()}`);
}
if (responseJSON.success == 0) {
if (responseJSON.success === 0) {
throw new Error(responseJSON.error);

@@ -139,3 +138,3 @@ }

body.append("include_data", "true");
return await this.makeAuthedRequest(this.API_BASE + "/ping", {
return await this.makeAuthedRequest(`${this.API_BASE}/ping`, {
method: "POST",

@@ -158,3 +157,3 @@ body: body,

options?.last_id && params.append("last_id", options?.last_id);
return await this.makeAuthedRequest(this.API_BASE + "/activity/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/activity/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -203,3 +202,3 @@ });

options?.to && params.append("to", options?.to);
return await this.makeAuthedRequest(this.API_BASE + "/behaviour/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/behaviour/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -220,3 +219,3 @@ });

options?.to && params.append("to", String(options?.to));
return await this.makeAuthedRequest(this.API_BASE + "/homeworks/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/homeworks/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -231,7 +230,8 @@ });

async getLessons(options) {
if (!options?.date)
if (!options?.date) {
throw new Error("No date specified");
}
const params = new URLSearchParams();
params.append("date", String(options?.date));
return await this.makeAuthedRequest(this.API_BASE + "/timetable/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/timetable/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -245,3 +245,3 @@ });

async getBadges() {
return await this.makeAuthedRequest(this.API_BASE + "/eventbadges/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/eventbadges/${this.studentId}`, {
method: "GET",

@@ -255,3 +255,3 @@ });

async getAnnouncements() {
return await this.makeAuthedRequest(this.API_BASE + "/announcements/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/announcements/${this.studentId}`, {
method: "GET",

@@ -265,3 +265,3 @@ });

async getDetentions() {
return await this.makeAuthedRequest(this.API_BASE + "/detentions/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/detentions/${this.studentId}`, {
method: "GET",

@@ -279,7 +279,3 @@ });

options?.to && params.append("to", options?.to);
return await this.makeAuthedRequest(this.API_BASE +
"/attendance/" +
this.studentId +
"?" +
params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/attendance/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -293,3 +289,3 @@ });

async getPupilFields() {
return await this.makeAuthedRequest(this.API_BASE + "/customfields/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/customfields/${this.studentId}`, {
method: "GET",

@@ -296,0 +292,0 @@ });

import type { ChangePasswordResponse, GetPupilsResponse } from "../types.js";
import { BaseClient } from "./baseClient.js";
/**
* Parent Client
* Parent Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { ParentClient } from "classcharts-api";
* const client = new ParentClient("username", "password");
* await client.login();
* ```
*/

@@ -39,1 +47,2 @@ export declare class ParentClient extends BaseClient {

}
//# sourceMappingURL=parentClient.d.ts.map

@@ -5,3 +5,11 @@ import { BaseClient } from "./baseClient.js";

/**
* Parent Client
* Parent Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { ParentClient } from "classcharts-api";
* const client = new ParentClient("username", "password");
* await client.login();
* ```
*/

@@ -27,3 +35,2 @@ export class ParentClient extends BaseClient {

});
// @ts-expect-error Init in .login
Object.defineProperty(this, "pupils", {

@@ -37,2 +44,3 @@ enumerable: true,

this.password = String(password);
this.pupils = [];
}

@@ -43,6 +51,8 @@ /**

async login() {
if (!this.email)
if (!this.email) {
throw new Error("Email not provided");
if (!this.password)
}
if (!this.password) {
throw new Error("Password not provided");
}
const formData = new URLSearchParams();

@@ -57,3 +67,3 @@ formData.append("_method", "POST");

});
const response = await fetch(BASE_URL + "/parent/login", {
const response = await fetch(`${BASE_URL}/parent/login`, {
method: "POST",

@@ -64,3 +74,3 @@ body: formData,

});
if (response.status != 302 || !response.headers.has("set-cookie")) {
if (response.status !== 302 || !response.headers.has("set-cookie")) {
await response.body?.cancel(); // Make deno tests happy by closing the body, unsure whether this is needed for the actual library

@@ -72,7 +82,8 @@ throw new Error("Unauthenticated: ClassCharts didn't return authentication cookies");

const sessionCookies = parseCookies(cookies);
const sessionID = JSON.parse(String(sessionCookies["parent_session_credentials"]));
const sessionID = JSON.parse(String(sessionCookies.parent_session_credentials));
this.sessionId = sessionID.session_id;
this.pupils = await this.getPupils();
if (!this.pupils)
if (!this.pupils) {
throw new Error("Account has no pupils attached");
}
this.studentId = this.pupils[0].id;

@@ -85,3 +96,3 @@ }

async getPupils() {
const response = await this.makeAuthedRequest(this.API_BASE + "/pupils", {
const response = await this.makeAuthedRequest(`${this.API_BASE}/pupils`, {
method: "GET",

@@ -98,8 +109,9 @@ });

selectPupil(pupilId) {
if (!pupilId)
if (!pupilId) {
throw new Error("No pupil ID specified");
}
const pupils = this.pupils;
for (let i = 0; i < pupils.length; i++) {
const pupil = pupils[i];
if (pupil.id == pupilId) {
if (pupil.id === pupilId) {
this.studentId = pupil.id;

@@ -122,3 +134,3 @@ return;

formData.append("repeat", newPassword);
return (await this.makeAuthedRequest(this.API_BASE + "/password", {
return await this.makeAuthedRequest(`${this.API_BASE}/password`, {
method: "POST",

@@ -129,4 +141,4 @@ body: formData,

},
}));
});
}
}
import { BaseClient } from "./baseClient.js";
import { GetStudentCodeOptions, GetStudentCodeResponse, RewardPurchaseResponse, RewardsResponse } from "../types.js";
import type { GetStudentCodeOptions, GetStudentCodeResponse, RewardPurchaseResponse, RewardsResponse } from "../types.js";
/**
* Student Client
* Student Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { StudentClient } from "classcharts-api";
* // Date of birth MUST in the format DD/MM/YYYY
* const client = new StudentClient("classchartsCode", "01/01/2000");
* await client.login();
* ```
*/
export declare class StudentClient extends BaseClient {
/**
* @property studentCode ClassCharts student code
* ClassCharts student code
*/
private studentCode;
/**
* @property dateOfBirth Student's date of birth
* Student's date of birth
*/

@@ -43,1 +52,2 @@ private dateOfBirth;

}
//# sourceMappingURL=studentClient.d.ts.map

@@ -5,3 +5,12 @@ import { API_BASE_STUDENT, BASE_URL } from "../utils/consts.js";

/**
* Student Client
* Student Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { StudentClient } from "classcharts-api";
* // Date of birth MUST in the format DD/MM/YYYY
* const client = new StudentClient("classchartsCode", "01/01/2000");
* await client.login();
* ```
*/

@@ -16,3 +25,3 @@ export class StudentClient extends BaseClient {

/**
* @property studentCode ClassCharts student code
* ClassCharts student code
*/

@@ -26,3 +35,3 @@ Object.defineProperty(this, "studentCode", {

/**
* @property dateOfBirth Student's date of birth
* Student's date of birth
*/

@@ -42,4 +51,5 @@ Object.defineProperty(this, "dateOfBirth", {

async login() {
if (!this.studentCode)
if (!this.studentCode) {
throw new Error("Student Code not provided");
}
const formData = new URLSearchParams();

@@ -51,3 +61,3 @@ formData.append("_method", "POST");

formData.append("recaptcha-token", "no-token-available");
const request = await fetch(BASE_URL + "/student/login", {
const request = await fetch(`${BASE_URL}/student/login`, {
method: "POST",

@@ -57,3 +67,3 @@ body: formData,

});
if (request.status != 302 || !request.headers.has("set-cookie")) {
if (request.status !== 302 || !request.headers.has("set-cookie")) {
await request.body?.cancel(); // Make deno tests happy by closing the body, unsure whether this is needed for the actual library

@@ -65,3 +75,3 @@ throw new Error("Unauthenticated: ClassCharts didn't return authentication cookies");

const sessionCookies = parseCookies(cookies);
const sessionID = JSON.parse(String(sessionCookies["student_session_credentials"]));
const sessionID = JSON.parse(String(sessionCookies.student_session_credentials));
this.sessionId = sessionID.session_id;

@@ -77,5 +87,5 @@ await this.getNewSessionId();

async getRewards() {
return (await this.makeAuthedRequest(this.API_BASE + "/rewards/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/rewards/${this.studentId}`, {
method: "GET",
}));
});
}

@@ -88,6 +98,6 @@ /**

async purchaseReward(itemId) {
return (await this.makeAuthedRequest(this.API_BASE + "/purchase/" + itemId, {
return await this.makeAuthedRequest(`${this.API_BASE}/purchase/${itemId}`, {
method: "POST",
body: `pupil_id=${this.studentId}`,
}));
});
}

@@ -101,3 +111,3 @@ /**

async getStudentCode(options) {
const data = await this.makeAuthedRequest(this.API_BASE + "/getcode", {
const data = await this.makeAuthedRequest(`${this.API_BASE}/getcode`, {
method: "POST",

@@ -104,0 +114,0 @@ body: JSON.stringify({

@@ -73,9 +73,9 @@ /**

export interface BehaviourResponseData {
timeline: Array<BehaviourTimelinePoint>;
timeline: BehaviourTimelinePoint[];
positive_reasons: Record<string, number>;
negative_reasons: Record<string, number>;
other_positive: Array<string>;
other_negative: Array<string>;
other_positive_count: Array<Record<string, number>>;
other_negative_count: Array<Record<string, number>>;
other_positive: string[];
other_negative: string[];
other_positive_count: Record<string, number>[];
other_negative_count: Record<string, number>[];
}

@@ -104,4 +104,4 @@ export interface BehaviourResponseMeta {

id: number;
type: string;
polarity: string;
type: "detention" | "notice" | "attendance_event" | "question" | "event" | "behaviour";
polarity: "positive" | "blank" | "negative" | null;
reason: string;

@@ -113,11 +113,11 @@ score: number;

border_color: string | null;
custom_class: string | null;
custom_class: "notice-color" | "colour-orange" | "colour-blue" | "colour-purple" | "colour-green" | null;
};
pupil_name: string;
lesson_name: string | null;
teacher_name: string;
teacher_name: string | null;
room_name: string | null;
note: string | null;
_can_delete: boolean;
badges: string | undefined;
badges: string;
detention_date: string | null;

@@ -128,3 +128,3 @@ detention_time: string | null;

}
export type ActivityResponseData = Array<ActivityPoint>;
export type ActivityResponseData = ActivityPoint[];
interface ActivityResponseMeta {

@@ -156,3 +156,3 @@ start_date: string;

}
export interface ValidatedHomeworkAttachment {
export interface TeacherValidatedHomeworkAttachment {
id: number;

@@ -163,2 +163,15 @@ file_name: string;

}
export interface TeacherValidatedHomeworkLink {
link: string;
validated_link: string;
}
export interface StudentHomeworkAttachment {
id: number;
file_name: string;
file: string;
validated_file: string;
teacher_note: string;
teacher_homework_attachments: TeacherValidatedHomeworkAttachment[];
can_delete: boolean;
}
export interface Homework {

@@ -181,15 +194,16 @@ lesson: string;

state: "not_completed" | "late" | "completed" | null;
mark: unknown | null;
mark: string | null;
mark_relative: number;
ticked: "yes" | "no";
allow_attachments: boolean;
allow_marking_completed: boolean;
first_seen_date: string | null;
last_seen_date: string | null;
attachments: Array<unknown>;
attachments: StudentHomeworkAttachment[];
has_feedback: boolean;
};
validated_links: Array<unknown>;
validated_attachments: Array<ValidatedHomeworkAttachment>;
validated_links: TeacherValidatedHomeworkLink[];
validated_attachments: TeacherValidatedHomeworkAttachment[];
}
export type HomeworksResponseData = Array<Homework>;
export type HomeworksResponseData = Homework[];
export interface HomeworksResponseMeta {

@@ -216,5 +230,7 @@ start_date: string;

teacher_name: string;
teacher_id: string;
lesson_name: string;
subject_name: string;
is_alternative_lesson: boolean;
is_break?: boolean;
period_name: string;

@@ -272,6 +288,8 @@ period_number: string;

created_date: string;
pupil_badges: Array<PupilEvent>;
pupil_badges: {
pupil_event: PupilEvent;
}[];
icon_url: string;
}
export type BadgesResponseData = Array<Badge>;
export type BadgesResponseData = Badge[];
export type BadgesResponseMeta = [];

@@ -303,3 +321,3 @@ export type BadgesResponse = ClassChartsResponse<BadgesResponseData, BadgesResponseMeta>;

name: string;
};
} | null;
} | null;

@@ -317,5 +335,5 @@ lesson_pupil_behaviour: {

name: string;
};
} | null;
}
export type DetentionsData = Array<Detention>;
export type DetentionsData = Detention[];
export interface DetentionsMeta {

@@ -325,2 +343,16 @@ detention_alias_plural: string;

export type DetentionsResponse = ClassChartsResponse<DetentionsData, DetentionsMeta>;
export interface AnnouncementConsent {
consent_given: "yes" | "no";
comment: string | null;
parent_name: string;
}
export interface AnnouncementPupilConsent {
pupil: {
id: string;
first_name: string;
last_name: string;
};
can_change_consent: boolean;
consent: AnnouncementConsent | null;
}
export interface Announcement {

@@ -336,7 +368,7 @@ id: number;

timestamp: string;
attachments: Array<{
attachments: {
filename: string;
url: string;
}>;
for_pupils: Array<unknown>;
}[];
for_pupils: string[];
comment_visibility: string;

@@ -349,7 +381,6 @@ allow_comments: "yes" | "no";

can_change_consent: boolean;
consent: unknown | null;
pupil_consents: Array<unknown>;
consent: AnnouncementConsent | null;
pupil_consents: AnnouncementPupilConsent[];
}
export type AnnouncementsResponse = ClassChartsResponse<Array<Announcement>, [
]>;
export type AnnouncementsResponse = ClassChartsResponse<Announcement[], []>;
export interface Pupil extends Student {

@@ -374,3 +405,3 @@ school_name: string;

}
export type GetPupilsResponse = Array<Pupil>;
export type GetPupilsResponse = Pupil[];
export interface GetFullActivityOptions {

@@ -398,3 +429,3 @@ /**

code: string;
status: "present" | "ignore";
status: "yes" | "present" | "ignore" | "no" | "absent" | "excused" | "late";
late_minutes: number | string;

@@ -405,4 +436,4 @@ lesson_name?: string;

export interface AttendanceMeta {
dates: Array<string>;
sessions: Array<string>;
dates: string[];
sessions: string[];
start_date: string;

@@ -427,3 +458,3 @@ end_date: string;

purchased: boolean;
purchased_count: string | number;
purchased_count: string | 0;
price_balance_difference: number;

@@ -444,3 +475,3 @@ }[];

note: string;
fields: Array<{
fields: {
id: number;

@@ -450,3 +481,3 @@ name: string;

value: string;
}>;
}[];
}

@@ -467,1 +498,2 @@ export type PupilFieldsResponse = ClassChartsResponse<PupilFieldsData, []>;

export {};
//# sourceMappingURL=types.d.ts.map
export declare const BASE_URL = "https://www.classcharts.com";
export declare const API_BASE_STUDENT: string;
export declare const API_BASE_PARENT: string;
export declare const API_BASE_STUDENT = "https://www.classcharts.com/apiv2student";
export declare const API_BASE_PARENT = "https://www.classcharts.com/apiv2parent";
export declare const PING_INTERVAL: number;
//# sourceMappingURL=consts.d.ts.map

@@ -7,2 +7,2 @@ /**

export declare function parseCookies(input: string): Record<string, unknown>;
export declare function leftTrim(str: string): string;
//# sourceMappingURL=utils.d.ts.map

@@ -11,8 +11,5 @@ /**

const cookieSplit = cookie.split(";")[0].split("=");
output[leftTrim(decodeURIComponent(cookieSplit[0]))] = decodeURIComponent(cookieSplit[1]);
output[decodeURIComponent(cookieSplit[0]).trimStart()] = decodeURIComponent(cookieSplit[1]);
}
return output;
}
export function leftTrim(str) {
return str.replace(/^\s+/g, "");
}
{
"module": "./esm/mod.js",
"main": "./script/mod.js",
"name": "classcharts-api",
"version": "2.9.0",
"author": {
"name": "James Cook",
"email": "james@jaminit.co.uk"
},
"version": "2.12.0",
"description": "A Typescript wrapper for getting information from the ClassCharts API",
"license": "ISC",
"keywords": [

@@ -18,5 +11,7 @@ "node",

],
"bugs": {
"url": "https://github.com/classchartsapi/classcharts-api-js/issues"
"author": {
"name": "James Cook",
"email": "james@jaminit.co.uk"
},
"homepage": "https://classchartsapi.github.io/classcharts-api-js/",
"repository": {

@@ -26,7 +21,8 @@ "type": "git",

},
"homepage": "https://classchartsapi.github.io/classcharts-api-js/",
"engines": {
"node": ">=18"
"license": "ISC",
"bugs": {
"url": "https://github.com/classchartsapi/classcharts-api-js/issues"
},
"sideEffects": false,
"main": "./script/mod.js",
"module": "./esm/mod.js",
"exports": {

@@ -45,7 +41,12 @@ ".": {

},
"engines": {
"node": ">=20"
},
"sideEffects": false,
"devDependencies": {
"@types/node": "^18.11.9",
"@types/node": "^20.9.0",
"picocolors": "^1.0.0",
"@deno/shim-deno": "~0.16.1"
}
"@deno/shim-deno": "~0.18.0"
},
"_generatedBy": "dnt@dev"
}

@@ -46,4 +46,3 @@ <p align="center">

- Node.js 18 or newer
\
- Node.js 20 or newer\
OR

@@ -297,2 +296,19 @@ - Deno

console.log(detentions);
/**
{
"success": 1,
"data": [
{
"id": 12345678,
"attended": "no",
"date": "2024-03-28T00:00:00+00:00",
...
}
],
"meta": {
"detention_alias_plural": "detentions"
}
}
*/
```

@@ -348,2 +364,2 @@

await client.changePassword("oldPassword", "newPassword");
```
```
export * from "./src/core/studentClient.js";
export * from "./src/core/parentClient.js";
//# sourceMappingURL=mod.d.ts.map

@@ -5,31 +5,33 @@ import type { ActivityResponse, AnnouncementsResponse, AttendanceResponse, BadgesResponse, BehaviourResponse, ClassChartsResponse, DetentionsResponse, GetActivityOptions, GetAttendanceOptions, GetBehaviourOptions, GetFullActivityOptions, GetHomeworkOptions, GetLessonsOptions, GetStudentInfoResponse, HomeworksResponse, LessonsResponse, PupilFieldsResponse } from "../types.js";

*/
export declare class BaseClient {
export declare abstract class BaseClient {
/**
* @property studentId Currently selected student ID
* Currently selected student ID
*/
studentId: number;
/**
* @property authCookies Cookies used for authentication (set during login and can be empty)
* Cookies used for authentication (set during login and can be empty)
*/
authCookies: Array<string>;
authCookies: string[];
/**
* @property sessionId Session ID used for authentication
* Session ID used for authentication
*/
sessionId: string;
/**
* @property lastPing Last time the sessionId was updated
* Last time the sessionId was updated
*/
lastPing: number;
/**
* @property API_BASE Base API URL, this is different depending on if its called as a parent or student
* Base API URL, this is different depending on if its called as a parent or student
*/
protected API_BASE: string;
/**
* Create a new client with the given API url
* @param API_BASE Base API URL, this is different depending on if its called as a parent or student
*/
constructor(API_BASE: string);
abstract login(): Promise<void>;
/**
* Revalidates the session ID.
*
* This is called automatically when the session ID is older than 3 minutes or when initially using the .login() method
* This is called automatically when the session ID is older than 3 minutes and initially using the .login() method
*/

@@ -118,1 +120,2 @@ getNewSessionId(): Promise<void>;

}
//# sourceMappingURL=baseClient.d.ts.map

@@ -10,2 +10,3 @@ "use strict";

/**
* Create a new client with the given API url
* @param API_BASE Base API URL, this is different depending on if its called as a parent or student

@@ -15,3 +16,3 @@ */

/**
* @property studentId Currently selected student ID
* Currently selected student ID
*/

@@ -25,3 +26,3 @@ Object.defineProperty(this, "studentId", {

/**
* @property authCookies Cookies used for authentication (set during login and can be empty)
* Cookies used for authentication (set during login and can be empty)
*/

@@ -35,3 +36,3 @@ Object.defineProperty(this, "authCookies", {

/**
* @property sessionId Session ID used for authentication
* Session ID used for authentication
*/

@@ -45,3 +46,3 @@ Object.defineProperty(this, "sessionId", {

/**
* @property lastPing Last time the sessionId was updated
* Last time the sessionId was updated
*/

@@ -55,3 +56,3 @@ Object.defineProperty(this, "lastPing", {

/**
* @property API_BASE Base API URL, this is different depending on if its called as a parent or student
* Base API URL, this is different depending on if its called as a parent or student
*/

@@ -70,3 +71,3 @@ Object.defineProperty(this, "API_BASE", {

*
* This is called automatically when the session ID is older than 3 minutes or when initially using the .login() method
* This is called automatically when the session ID is older than 3 minutes and initially using the .login() method
*/

@@ -76,3 +77,3 @@ async getNewSessionId() {

pingFormData.append("include_data", "true");
const pingData = await this.makeAuthedRequest(this.API_BASE + "/ping", {
const pingData = await this.makeAuthedRequest(`${this.API_BASE}/ping`, {
method: "POST",

@@ -94,9 +95,7 @@ body: pingFormData,

*/
async makeAuthedRequest(path, fetchOptions, options) {
if (!this.sessionId)
async makeAuthedRequest(path, fetchOptions, options = { revalidateToken: true }) {
if (!this.sessionId) {
throw new Error("No session ID");
if (!options) {
options = {};
}
if (typeof options?.revalidateToken == "undefined") {
if (typeof options?.revalidateToken === "undefined") {
options.revalidateToken = true;

@@ -108,3 +107,3 @@ }

Cookie: this?.authCookies?.join(";") ?? [],
Authorization: "Basic " + this.sessionId,
Authorization: `Basic ${this.sessionId}`,
"User-Agent": "classcharts-api https://github.com/classchartsapi/classcharts-api-js",

@@ -120,3 +119,3 @@ ...fetchOptions.headers,

const request = await fetch(path, requestOptions);
// deno-lint-ignore no-explicit-any
// biome-ignore lint/suspicious/noExplicitAny:
let responseJSON;

@@ -127,5 +126,5 @@ try {

catch {
throw new Error("Error parsing JSON. Returned response: " + (await request.text()));
throw new Error(`Error parsing JSON. Returned response: ${await request.text()}`);
}
if (responseJSON.success == 0) {
if (responseJSON.success === 0) {
throw new Error(responseJSON.error);

@@ -142,3 +141,3 @@ }

body.append("include_data", "true");
return await this.makeAuthedRequest(this.API_BASE + "/ping", {
return await this.makeAuthedRequest(`${this.API_BASE}/ping`, {
method: "POST",

@@ -161,3 +160,3 @@ body: body,

options?.last_id && params.append("last_id", options?.last_id);
return await this.makeAuthedRequest(this.API_BASE + "/activity/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/activity/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -206,3 +205,3 @@ });

options?.to && params.append("to", options?.to);
return await this.makeAuthedRequest(this.API_BASE + "/behaviour/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/behaviour/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -223,3 +222,3 @@ });

options?.to && params.append("to", String(options?.to));
return await this.makeAuthedRequest(this.API_BASE + "/homeworks/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/homeworks/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -234,7 +233,8 @@ });

async getLessons(options) {
if (!options?.date)
if (!options?.date) {
throw new Error("No date specified");
}
const params = new URLSearchParams();
params.append("date", String(options?.date));
return await this.makeAuthedRequest(this.API_BASE + "/timetable/" + this.studentId + "?" + params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/timetable/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -248,3 +248,3 @@ });

async getBadges() {
return await this.makeAuthedRequest(this.API_BASE + "/eventbadges/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/eventbadges/${this.studentId}`, {
method: "GET",

@@ -258,3 +258,3 @@ });

async getAnnouncements() {
return await this.makeAuthedRequest(this.API_BASE + "/announcements/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/announcements/${this.studentId}`, {
method: "GET",

@@ -268,3 +268,3 @@ });

async getDetentions() {
return await this.makeAuthedRequest(this.API_BASE + "/detentions/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/detentions/${this.studentId}`, {
method: "GET",

@@ -282,7 +282,3 @@ });

options?.to && params.append("to", options?.to);
return await this.makeAuthedRequest(this.API_BASE +
"/attendance/" +
this.studentId +
"?" +
params.toString(), {
return await this.makeAuthedRequest(`${this.API_BASE}/attendance/${this.studentId}?${params.toString()}`, {
method: "GET",

@@ -296,3 +292,3 @@ });

async getPupilFields() {
return await this.makeAuthedRequest(this.API_BASE + "/customfields/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/customfields/${this.studentId}`, {
method: "GET",

@@ -299,0 +295,0 @@ });

import type { ChangePasswordResponse, GetPupilsResponse } from "../types.js";
import { BaseClient } from "./baseClient.js";
/**
* Parent Client
* Parent Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { ParentClient } from "classcharts-api";
* const client = new ParentClient("username", "password");
* await client.login();
* ```
*/

@@ -39,1 +47,2 @@ export declare class ParentClient extends BaseClient {

}
//# sourceMappingURL=parentClient.d.ts.map

@@ -8,3 +8,11 @@ "use strict";

/**
* Parent Client
* Parent Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { ParentClient } from "classcharts-api";
* const client = new ParentClient("username", "password");
* await client.login();
* ```
*/

@@ -30,3 +38,2 @@ class ParentClient extends baseClient_js_1.BaseClient {

});
// @ts-expect-error Init in .login
Object.defineProperty(this, "pupils", {

@@ -40,2 +47,3 @@ enumerable: true,

this.password = String(password);
this.pupils = [];
}

@@ -46,6 +54,8 @@ /**

async login() {
if (!this.email)
if (!this.email) {
throw new Error("Email not provided");
if (!this.password)
}
if (!this.password) {
throw new Error("Password not provided");
}
const formData = new URLSearchParams();

@@ -60,3 +70,3 @@ formData.append("_method", "POST");

});
const response = await fetch(consts_js_1.BASE_URL + "/parent/login", {
const response = await fetch(`${consts_js_1.BASE_URL}/parent/login`, {
method: "POST",

@@ -67,3 +77,3 @@ body: formData,

});
if (response.status != 302 || !response.headers.has("set-cookie")) {
if (response.status !== 302 || !response.headers.has("set-cookie")) {
await response.body?.cancel(); // Make deno tests happy by closing the body, unsure whether this is needed for the actual library

@@ -75,7 +85,8 @@ throw new Error("Unauthenticated: ClassCharts didn't return authentication cookies");

const sessionCookies = (0, utils_js_1.parseCookies)(cookies);
const sessionID = JSON.parse(String(sessionCookies["parent_session_credentials"]));
const sessionID = JSON.parse(String(sessionCookies.parent_session_credentials));
this.sessionId = sessionID.session_id;
this.pupils = await this.getPupils();
if (!this.pupils)
if (!this.pupils) {
throw new Error("Account has no pupils attached");
}
this.studentId = this.pupils[0].id;

@@ -88,3 +99,3 @@ }

async getPupils() {
const response = await this.makeAuthedRequest(this.API_BASE + "/pupils", {
const response = await this.makeAuthedRequest(`${this.API_BASE}/pupils`, {
method: "GET",

@@ -101,8 +112,9 @@ });

selectPupil(pupilId) {
if (!pupilId)
if (!pupilId) {
throw new Error("No pupil ID specified");
}
const pupils = this.pupils;
for (let i = 0; i < pupils.length; i++) {
const pupil = pupils[i];
if (pupil.id == pupilId) {
if (pupil.id === pupilId) {
this.studentId = pupil.id;

@@ -125,3 +137,3 @@ return;

formData.append("repeat", newPassword);
return (await this.makeAuthedRequest(this.API_BASE + "/password", {
return await this.makeAuthedRequest(`${this.API_BASE}/password`, {
method: "POST",

@@ -132,5 +144,5 @@ body: formData,

},
}));
});
}
}
exports.ParentClient = ParentClient;
import { BaseClient } from "./baseClient.js";
import { GetStudentCodeOptions, GetStudentCodeResponse, RewardPurchaseResponse, RewardsResponse } from "../types.js";
import type { GetStudentCodeOptions, GetStudentCodeResponse, RewardPurchaseResponse, RewardsResponse } from "../types.js";
/**
* Student Client
* Student Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { StudentClient } from "classcharts-api";
* // Date of birth MUST in the format DD/MM/YYYY
* const client = new StudentClient("classchartsCode", "01/01/2000");
* await client.login();
* ```
*/
export declare class StudentClient extends BaseClient {
/**
* @property studentCode ClassCharts student code
* ClassCharts student code
*/
private studentCode;
/**
* @property dateOfBirth Student's date of birth
* Student's date of birth
*/

@@ -43,1 +52,2 @@ private dateOfBirth;

}
//# sourceMappingURL=studentClient.d.ts.map

@@ -8,3 +8,12 @@ "use strict";

/**
* Student Client
* Student Client.
* See {@link BaseClient} for all shared methods.
*
* @example
* ```ts
* import { StudentClient } from "classcharts-api";
* // Date of birth MUST in the format DD/MM/YYYY
* const client = new StudentClient("classchartsCode", "01/01/2000");
* await client.login();
* ```
*/

@@ -19,3 +28,3 @@ class StudentClient extends baseClient_js_1.BaseClient {

/**
* @property studentCode ClassCharts student code
* ClassCharts student code
*/

@@ -29,3 +38,3 @@ Object.defineProperty(this, "studentCode", {

/**
* @property dateOfBirth Student's date of birth
* Student's date of birth
*/

@@ -45,4 +54,5 @@ Object.defineProperty(this, "dateOfBirth", {

async login() {
if (!this.studentCode)
if (!this.studentCode) {
throw new Error("Student Code not provided");
}
const formData = new URLSearchParams();

@@ -54,3 +64,3 @@ formData.append("_method", "POST");

formData.append("recaptcha-token", "no-token-available");
const request = await fetch(consts_js_1.BASE_URL + "/student/login", {
const request = await fetch(`${consts_js_1.BASE_URL}/student/login`, {
method: "POST",

@@ -60,3 +70,3 @@ body: formData,

});
if (request.status != 302 || !request.headers.has("set-cookie")) {
if (request.status !== 302 || !request.headers.has("set-cookie")) {
await request.body?.cancel(); // Make deno tests happy by closing the body, unsure whether this is needed for the actual library

@@ -68,3 +78,3 @@ throw new Error("Unauthenticated: ClassCharts didn't return authentication cookies");

const sessionCookies = (0, utils_js_1.parseCookies)(cookies);
const sessionID = JSON.parse(String(sessionCookies["student_session_credentials"]));
const sessionID = JSON.parse(String(sessionCookies.student_session_credentials));
this.sessionId = sessionID.session_id;

@@ -80,5 +90,5 @@ await this.getNewSessionId();

async getRewards() {
return (await this.makeAuthedRequest(this.API_BASE + "/rewards/" + this.studentId, {
return await this.makeAuthedRequest(`${this.API_BASE}/rewards/${this.studentId}`, {
method: "GET",
}));
});
}

@@ -91,6 +101,6 @@ /**

async purchaseReward(itemId) {
return (await this.makeAuthedRequest(this.API_BASE + "/purchase/" + itemId, {
return await this.makeAuthedRequest(`${this.API_BASE}/purchase/${itemId}`, {
method: "POST",
body: `pupil_id=${this.studentId}`,
}));
});
}

@@ -104,3 +114,3 @@ /**

async getStudentCode(options) {
const data = await this.makeAuthedRequest(this.API_BASE + "/getcode", {
const data = await this.makeAuthedRequest(`${this.API_BASE}/getcode`, {
method: "POST",

@@ -107,0 +117,0 @@ body: JSON.stringify({

@@ -73,9 +73,9 @@ /**

export interface BehaviourResponseData {
timeline: Array<BehaviourTimelinePoint>;
timeline: BehaviourTimelinePoint[];
positive_reasons: Record<string, number>;
negative_reasons: Record<string, number>;
other_positive: Array<string>;
other_negative: Array<string>;
other_positive_count: Array<Record<string, number>>;
other_negative_count: Array<Record<string, number>>;
other_positive: string[];
other_negative: string[];
other_positive_count: Record<string, number>[];
other_negative_count: Record<string, number>[];
}

@@ -104,4 +104,4 @@ export interface BehaviourResponseMeta {

id: number;
type: string;
polarity: string;
type: "detention" | "notice" | "attendance_event" | "question" | "event" | "behaviour";
polarity: "positive" | "blank" | "negative" | null;
reason: string;

@@ -113,11 +113,11 @@ score: number;

border_color: string | null;
custom_class: string | null;
custom_class: "notice-color" | "colour-orange" | "colour-blue" | "colour-purple" | "colour-green" | null;
};
pupil_name: string;
lesson_name: string | null;
teacher_name: string;
teacher_name: string | null;
room_name: string | null;
note: string | null;
_can_delete: boolean;
badges: string | undefined;
badges: string;
detention_date: string | null;

@@ -128,3 +128,3 @@ detention_time: string | null;

}
export type ActivityResponseData = Array<ActivityPoint>;
export type ActivityResponseData = ActivityPoint[];
interface ActivityResponseMeta {

@@ -156,3 +156,3 @@ start_date: string;

}
export interface ValidatedHomeworkAttachment {
export interface TeacherValidatedHomeworkAttachment {
id: number;

@@ -163,2 +163,15 @@ file_name: string;

}
export interface TeacherValidatedHomeworkLink {
link: string;
validated_link: string;
}
export interface StudentHomeworkAttachment {
id: number;
file_name: string;
file: string;
validated_file: string;
teacher_note: string;
teacher_homework_attachments: TeacherValidatedHomeworkAttachment[];
can_delete: boolean;
}
export interface Homework {

@@ -181,15 +194,16 @@ lesson: string;

state: "not_completed" | "late" | "completed" | null;
mark: unknown | null;
mark: string | null;
mark_relative: number;
ticked: "yes" | "no";
allow_attachments: boolean;
allow_marking_completed: boolean;
first_seen_date: string | null;
last_seen_date: string | null;
attachments: Array<unknown>;
attachments: StudentHomeworkAttachment[];
has_feedback: boolean;
};
validated_links: Array<unknown>;
validated_attachments: Array<ValidatedHomeworkAttachment>;
validated_links: TeacherValidatedHomeworkLink[];
validated_attachments: TeacherValidatedHomeworkAttachment[];
}
export type HomeworksResponseData = Array<Homework>;
export type HomeworksResponseData = Homework[];
export interface HomeworksResponseMeta {

@@ -216,5 +230,7 @@ start_date: string;

teacher_name: string;
teacher_id: string;
lesson_name: string;
subject_name: string;
is_alternative_lesson: boolean;
is_break?: boolean;
period_name: string;

@@ -272,6 +288,8 @@ period_number: string;

created_date: string;
pupil_badges: Array<PupilEvent>;
pupil_badges: {
pupil_event: PupilEvent;
}[];
icon_url: string;
}
export type BadgesResponseData = Array<Badge>;
export type BadgesResponseData = Badge[];
export type BadgesResponseMeta = [];

@@ -303,3 +321,3 @@ export type BadgesResponse = ClassChartsResponse<BadgesResponseData, BadgesResponseMeta>;

name: string;
};
} | null;
} | null;

@@ -317,5 +335,5 @@ lesson_pupil_behaviour: {

name: string;
};
} | null;
}
export type DetentionsData = Array<Detention>;
export type DetentionsData = Detention[];
export interface DetentionsMeta {

@@ -325,2 +343,16 @@ detention_alias_plural: string;

export type DetentionsResponse = ClassChartsResponse<DetentionsData, DetentionsMeta>;
export interface AnnouncementConsent {
consent_given: "yes" | "no";
comment: string | null;
parent_name: string;
}
export interface AnnouncementPupilConsent {
pupil: {
id: string;
first_name: string;
last_name: string;
};
can_change_consent: boolean;
consent: AnnouncementConsent | null;
}
export interface Announcement {

@@ -336,7 +368,7 @@ id: number;

timestamp: string;
attachments: Array<{
attachments: {
filename: string;
url: string;
}>;
for_pupils: Array<unknown>;
}[];
for_pupils: string[];
comment_visibility: string;

@@ -349,7 +381,6 @@ allow_comments: "yes" | "no";

can_change_consent: boolean;
consent: unknown | null;
pupil_consents: Array<unknown>;
consent: AnnouncementConsent | null;
pupil_consents: AnnouncementPupilConsent[];
}
export type AnnouncementsResponse = ClassChartsResponse<Array<Announcement>, [
]>;
export type AnnouncementsResponse = ClassChartsResponse<Announcement[], []>;
export interface Pupil extends Student {

@@ -374,3 +405,3 @@ school_name: string;

}
export type GetPupilsResponse = Array<Pupil>;
export type GetPupilsResponse = Pupil[];
export interface GetFullActivityOptions {

@@ -398,3 +429,3 @@ /**

code: string;
status: "present" | "ignore";
status: "yes" | "present" | "ignore" | "no" | "absent" | "excused" | "late";
late_minutes: number | string;

@@ -405,4 +436,4 @@ lesson_name?: string;

export interface AttendanceMeta {
dates: Array<string>;
sessions: Array<string>;
dates: string[];
sessions: string[];
start_date: string;

@@ -427,3 +458,3 @@ end_date: string;

purchased: boolean;
purchased_count: string | number;
purchased_count: string | 0;
price_balance_difference: number;

@@ -444,3 +475,3 @@ }[];

note: string;
fields: Array<{
fields: {
id: number;

@@ -450,3 +481,3 @@ name: string;

value: string;
}>;
}[];
}

@@ -467,1 +498,2 @@ export type PupilFieldsResponse = ClassChartsResponse<PupilFieldsData, []>;

export {};
//# sourceMappingURL=types.d.ts.map
export declare const BASE_URL = "https://www.classcharts.com";
export declare const API_BASE_STUDENT: string;
export declare const API_BASE_PARENT: string;
export declare const API_BASE_STUDENT = "https://www.classcharts.com/apiv2student";
export declare const API_BASE_PARENT = "https://www.classcharts.com/apiv2parent";
export declare const PING_INTERVAL: number;
//# sourceMappingURL=consts.d.ts.map

@@ -7,2 +7,2 @@ /**

export declare function parseCookies(input: string): Record<string, unknown>;
export declare function leftTrim(str: string): string;
//# sourceMappingURL=utils.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.leftTrim = exports.parseCookies = void 0;
exports.parseCookies = void 0;
/**

@@ -14,3 +14,3 @@ * Parse cookies from string

const cookieSplit = cookie.split(";")[0].split("=");
output[leftTrim(decodeURIComponent(cookieSplit[0]))] = decodeURIComponent(cookieSplit[1]);
output[decodeURIComponent(cookieSplit[0]).trimStart()] = decodeURIComponent(cookieSplit[1]);
}

@@ -20,5 +20,1 @@ return output;

exports.parseCookies = parseCookies;
function leftTrim(str) {
return str.replace(/^\s+/g, "");
}
exports.leftTrim = leftTrim;
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc