@insignia-education/api-sdk-js 0.15.9 → 0.15.11

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.
@@ -1,6 +1,6 @@
1
1
  <?xml version="1.0" encoding="UTF-8"?>
2
- <coverage generated="1784924442218" clover="3.2.0">
3
- <project timestamp="1784924442218" name="All files">
2
+ <coverage generated="1784989496629" clover="3.2.0">
3
+ <project timestamp="1784989496629" name="All files">
4
4
  <metrics statements="120" coveredstatements="92" conditionals="43" coveredconditionals="35" methods="14" coveredmethods="13" elements="177" coveredelements="140" complexity="0" loc="120" ncloc="120" packages="1" files="1" classes="1"/>
5
5
  <file name="Client.js" path="/Users/roquendo/code/insignia-education/api-sdk-js/src/Client.js">
6
6
  <metrics statements="120" coveredstatements="92" conditionals="43" coveredconditionals="35" methods="14" coveredmethods="13"/>
@@ -430,7 +430,7 @@
430
430
  <div class='footer quiet pad2 space-top1 center small'>
431
431
  Code coverage generated by
432
432
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
433
- at 2026-07-24T20:20:42.212Z
433
+ at 2026-07-25T14:24:56.621Z
434
434
  </div>
435
435
  <script src="prettify.js"></script>
436
436
  <script>
@@ -101,7 +101,7 @@
101
101
  <div class='footer quiet pad2 space-top1 center small'>
102
102
  Code coverage generated by
103
103
  <a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
104
- at 2026-07-24T20:20:42.212Z
104
+ at 2026-07-25T14:24:56.621Z
105
105
  </div>
106
106
  <script src="prettify.js"></script>
107
107
  <script>
@@ -274,4 +274,4 @@ header {
274
274
  font-size: 1rem;
275
275
  padding: 0 0.5rem;
276
276
  }
277
- </style></head><body><main class="jesthtml-content"><header><h1 id="title">API SDK Test Report</h1></header><section id="metadata-container"><div id="timestamp">Started: 2026-07-24 17:20:41</div><div id="summary"><div id="suite-summary"><div class="summary-total">Suites (1)</div><div class="summary-passed ">1 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div><div id="test-summary"><div class="summary-total">Tests (22)</div><div class="summary-passed ">22 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div></div></section><details id="suite-1" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/roquendo/code/insignia-education/api-sdk-js/tests/client.test.js</div><div class="suite-time">0.544s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">uses explicit baseUrl when provided</div><div class="test-status">passed</div><div class="test-duration">0.003s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">strips trailing slash from explicit baseUrl</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">uses INSIGNIA_EDUCATION_API_BASE_URL env var when baseUrl is null</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">falls back to https://insigniaeducation.com when baseUrl is null and env var is not set</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">falls back to default when constructed with no arguments</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">explicit baseUrl takes precedence over env var</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">falls back to default when process is undefined</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">get() sends GET with no body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">post() sends POST with body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">post() sends POST with no body when not provided</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">put() sends PUT with body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">put() sends PUT with no body when not provided</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">patch() sends PATCH with body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">patch() sends PATCH with no body when not provided</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">del() sends DELETE with no body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">JSON headers are always sent</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">credentials is always include</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">stores response cookies and sends them on later requests in Node</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns parsed JSON from response</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns null for empty response text</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns null for no-content responses</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns null when json parsing fails because the response is empty</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details></main></body></html>
277
+ </style></head><body><main class="jesthtml-content"><header><h1 id="title">API SDK Test Report</h1></header><section id="metadata-container"><div id="timestamp">Started: 2026-07-25 11:24:55</div><div id="summary"><div id="suite-summary"><div class="summary-total">Suites (1)</div><div class="summary-passed ">1 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div><div id="test-summary"><div class="summary-total">Tests (22)</div><div class="summary-passed ">22 passed</div><div class="summary-failed summary-empty">0 failed</div><div class="summary-pending summary-empty">0 pending</div></div></div></section><details id="suite-1" class="suite-container" open=""><summary class="suite-info"><div class="suite-path">/Users/roquendo/code/insignia-education/api-sdk-js/tests/client.test.js</div><div class="suite-time">0.77s</div></summary><div class="suite-tests"><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">uses explicit baseUrl when provided</div><div class="test-status">passed</div><div class="test-duration">0.004s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">strips trailing slash from explicit baseUrl</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">uses INSIGNIA_EDUCATION_API_BASE_URL env var when baseUrl is null</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">falls back to https://insigniaeducation.com when baseUrl is null and env var is not set</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">falls back to default when constructed with no arguments</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">explicit baseUrl takes precedence over env var</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">baseUrl resolution</div><div class="test-title">falls back to default when process is undefined</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">get() sends GET with no body</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">post() sends POST with body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">post() sends POST with no body when not provided</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">put() sends PUT with body</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">put() sends PUT with no body when not provided</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">patch() sends PATCH with body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">patch() sends PATCH with no body when not provided</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">del() sends DELETE with no body</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">JSON headers are always sent</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">credentials is always include</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">stores response cookies and sends them on later requests in Node</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns parsed JSON from response</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns null for empty response text</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns null for no-content responses</div><div class="test-status">passed</div><div class="test-duration">0.001s</div></div></div><div class="test-result passed"><div class="test-info"><div class="test-suitename">HTTP methods</div><div class="test-title">returns null when json parsing fails because the response is empty</div><div class="test-status">passed</div><div class="test-duration"> </div></div></div></div></details></main></body></html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@insignia-education/api-sdk-js",
3
- "version": "0.15.9",
3
+ "version": "0.15.11",
4
4
  "description": "JavaScript SDK for the Insignia Education API",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -7,6 +7,7 @@ export default class Auth {
7
7
 
8
8
  loginOrRegister(email) { return this.#client.get(`/auth/login-register-check?email=${encodeURIComponent(email)}`); }
9
9
  register(data) { return this.#client.put('/auth/register', data); }
10
+ /** May resolve to { two_factor_required: true } — then call twoFactor({ pin }). */
10
11
  login(data) { return this.#client.post('/auth/login', data); }
11
12
  googleLogin(data) { return this.#client.post('/auth/google', data); }
12
13
  refresh() { return this.#client.post('/auth/refresh'); }
@@ -18,5 +19,6 @@ export default class Auth {
18
19
  magicLinkVerify(data) { return this.#client.post('/auth/magic-link/verify', data); }
19
20
  facebookLogin(data) { return this.#client.post('/auth/facebook', data); }
20
21
  emailVerify(data) { return this.#client.post('/auth/email-verify', data); }
22
+ /** Second login step when two_factor_required: exchange { pin } for a session. */
21
23
  twoFactor(data) { return this.#client.post('/auth/2fa', data); }
22
24
  }
@@ -0,0 +1,19 @@
1
+ export default class Offers {
2
+ #client;
3
+
4
+ constructor(client) {
5
+ this.#client = client;
6
+ }
7
+
8
+ /** Sales-only paginated + title-searchable list when called without an id. */
9
+ get(id = null, { search = null, page = 1, perPage = 15 } = {}) {
10
+ if (id) return this.#client.get(`/offers/${id}`);
11
+ return this.#client.get('/offers', { search, page, per_page: perPage });
12
+ }
13
+ create(data) { return this.#client.put('/offers', data); }
14
+ edit(id, data) { return this.#client.patch(`/offers/${id}`, data); }
15
+ delete(id) { return this.#client.del(`/offers/${id}`); }
16
+
17
+ /** Sales-only: usage stats + full redemption history for one offer. */
18
+ stats(id) { return this.#client.get(`/offers/${id}/stats`); }
19
+ }
@@ -6,4 +6,7 @@ export default class PaymentMethods {
6
6
  }
7
7
 
8
8
  byCurrency(currency_id) { return this.#client.get(`/payment-methods/by-currency/${currency_id}`); }
9
+
10
+ /** All payment methods, unscoped by currency — used by the admin Offer form's payment-method select. */
11
+ getAll() { return this.#client.get('/payment-methods'); }
9
12
  }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * TOTP two-factor management for the authenticated user.
3
+ * The login-time verification step lives on Auth.twoFactor().
4
+ */
5
+ export default class TwoFactor {
6
+ #client;
7
+
8
+ constructor(client) {
9
+ this.#client = client;
10
+ }
11
+
12
+ /** { enabled: boolean } */
13
+ status() { return this.#client.get('/2fa/status'); }
14
+ /** Provision a secret (2FA stays OFF). Returns { secret, otpauth_url } for the QR. */
15
+ setup() { return this.#client.post('/2fa/setup'); }
16
+ /** Confirm a code and turn 2FA on. data: { pin } */
17
+ enable(data) { return this.#client.post('/2fa/enable', data); }
18
+ /** Verify a code and turn 2FA off. data: { pin } */
19
+ disable(data) { return this.#client.post('/2fa/disable', data); }
20
+ }
@@ -58,25 +58,26 @@ export default class Users {
58
58
  delete: (id) => client.del(`${base}/${id}`),
59
59
  verify: (id, data) => client.post(`${base}/${id}/verify`, data),
60
60
  reject: (id, data) => client.post(`${base}/${id}/reject`, data),
61
+ /** Preview pricing (with any auto-applied Offer) for a prospective cart, without creating a payment. */
62
+ quote: (data) => client.post(`${base}/quote`, data),
61
63
  };
62
64
  }
63
65
 
66
+ /** Read-only: points are granted/revoked by internal jobs, not over the API. */
64
67
  points(userId) {
65
68
  const base = `/users/${userId}/points`;
66
69
  const client = this.#client;
67
70
  return {
68
- get: (id = null) => id ? client.get(`${base}/${id}`) : client.get(base),
69
- create: (data) => client.put(base, data),
70
- delete: (id) => client.del(`${base}/${id}`),
71
+ get: (id = null) => id ? client.get(`${base}/${id}`) : client.get(base),
71
72
  };
72
73
  }
73
74
 
75
+ /** Read-only: money moves are created by internal jobs, not over the API. */
74
76
  moneyMoves(userId) {
75
77
  const base = `/users/${userId}/money-moves`;
76
78
  const client = this.#client;
77
79
  return {
78
- get: (id = null) => id ? client.get(`${base}/${id}`) : client.get(base),
79
- create: (data) => client.put(base, data),
80
+ get: (id = null) => id ? client.get(`${base}/${id}`) : client.get(base),
80
81
  };
81
82
  }
82
83
 
@@ -16,6 +16,7 @@ import Hashes from './Hashes.js';
16
16
  import Insignias from './Insignias.js';
17
17
  import Languages from './Languages.js';
18
18
  import MailBlacklist from './MailBlacklist.js';
19
+ import Offers from './Offers.js';
19
20
  import Organizations from './Organizations.js';
20
21
  import PaymentMethods from './PaymentMethods.js';
21
22
  import Payments from './Payments.js';
@@ -25,6 +26,7 @@ import Surveys from './Surveys.js';
25
26
  import Taxes from './Taxes.js';
26
27
  import Teacher from './Teacher.js';
27
28
  import Telegram from './Telegram.js';
29
+ import TwoFactor from './TwoFactor.js';
28
30
  import UserSessionTypes from './UserSessionTypes.js';
29
31
  import UserTypes from './UserTypes.js';
30
32
  import Users from './Users.js';
@@ -60,6 +62,7 @@ export default class InsigniaApiV1 extends InsigniaApi {
60
62
  this.insignias = new Insignias(this);
61
63
  this.languages = new Languages(this);
62
64
  this.mailBlacklist = new MailBlacklist(this);
65
+ this.offers = new Offers(this);
63
66
  this.organizations = new Organizations(this);
64
67
  this.paymentMethods = new PaymentMethods(this);
65
68
  this.payments = new Payments(this);
@@ -69,6 +72,7 @@ export default class InsigniaApiV1 extends InsigniaApi {
69
72
  this.taxes = new Taxes(this);
70
73
  this.teacher = new Teacher(this);
71
74
  this.telegram = new Telegram(this);
75
+ this.twoFactor = new TwoFactor(this);
72
76
  this.userSessionTypes = new UserSessionTypes(this);
73
77
  this.userTypes = new UserTypes(this);
74
78
  this.users = new Users(this);
@@ -0,0 +1,27 @@
1
+ import InsigniaApiV1 from '../../../../src/api/v1/index.js';
2
+
3
+ const api = new InsigniaApiV1(process.env.INSIGNIA_EDUCATION_API_BASE_URL);
4
+
5
+ const login = () => api.auth.login({
6
+ email: process.env.TEST_EMAIL,
7
+ password: process.env.TEST_PASSWORD,
8
+ });
9
+
10
+ describe('api/v1/offers', () => {
11
+ test('get | authenticated', async () => {
12
+ await login();
13
+ await api.offers.get()
14
+ .then(response => {
15
+ response = Object.values(response);
16
+ expect(Array.isArray(response)).toBe(true);
17
+ response.forEach(offer => {
18
+ expect(offer["id"]).toBeDefined();
19
+ expect(offer["title"]).toBeDefined();
20
+ expect(offer["percentage"]).toBeDefined();
21
+ expect(offer["enabled"]).toBeDefined();
22
+ expect(offer["created_at"]).toBeDefined();
23
+ expect(offer["updated_at"]).toBeDefined();
24
+ });
25
+ });
26
+ });
27
+ });