@ripwords/myinvois-client 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/dist/index.js ADDED
@@ -0,0 +1,523 @@
1
+
2
+ //#region node_modules/destr/dist/index.mjs
3
+ const suspectProtoRx = /"(?:_|\\u0{2}5[Ff]){2}(?:p|\\u0{2}70)(?:r|\\u0{2}72)(?:o|\\u0{2}6[Ff])(?:t|\\u0{2}74)(?:o|\\u0{2}6[Ff])(?:_|\\u0{2}5[Ff]){2}"\s*:/;
4
+ const suspectConstructorRx = /"(?:c|\\u0063)(?:o|\\u006[Ff])(?:n|\\u006[Ee])(?:s|\\u0073)(?:t|\\u0074)(?:r|\\u0072)(?:u|\\u0075)(?:c|\\u0063)(?:t|\\u0074)(?:o|\\u006[Ff])(?:r|\\u0072)"\s*:/;
5
+ const JsonSigRx = /^\s*["[{]|^\s*-?\d{1,16}(\.\d{1,17})?([Ee][+-]?\d+)?\s*$/;
6
+ function jsonParseTransform(key, value) {
7
+ if (key === "__proto__" || key === "constructor" && value && typeof value === "object" && "prototype" in value) {
8
+ warnKeyDropped(key);
9
+ return;
10
+ }
11
+ return value;
12
+ }
13
+ function warnKeyDropped(key) {
14
+ console.warn(`[destr] Dropping "${key}" key to prevent prototype pollution.`);
15
+ }
16
+ function destr(value, options = {}) {
17
+ if (typeof value !== "string") return value;
18
+ if (value[0] === "\"" && value[value.length - 1] === "\"" && value.indexOf("\\") === -1) return value.slice(1, -1);
19
+ const _value = value.trim();
20
+ if (_value.length <= 9) switch (_value.toLowerCase()) {
21
+ case "true": return true;
22
+ case "false": return false;
23
+ case "undefined": return void 0;
24
+ case "null": return null;
25
+ case "nan": return Number.NaN;
26
+ case "infinity": return Number.POSITIVE_INFINITY;
27
+ case "-infinity": return Number.NEGATIVE_INFINITY;
28
+ }
29
+ if (!JsonSigRx.test(value)) {
30
+ if (options.strict) throw new SyntaxError("[destr] Invalid JSON");
31
+ return value;
32
+ }
33
+ try {
34
+ if (suspectProtoRx.test(value) || suspectConstructorRx.test(value)) {
35
+ if (options.strict) throw new Error("[destr] Possible prototype pollution");
36
+ return JSON.parse(value, jsonParseTransform);
37
+ }
38
+ return JSON.parse(value);
39
+ } catch (error) {
40
+ if (options.strict) throw error;
41
+ return value;
42
+ }
43
+ }
44
+
45
+ //#endregion
46
+ //#region node_modules/ufo/dist/index.mjs
47
+ const r = String.fromCharCode;
48
+ const HASH_RE = /#/g;
49
+ const AMPERSAND_RE = /&/g;
50
+ const SLASH_RE = /\//g;
51
+ const EQUAL_RE = /=/g;
52
+ const PLUS_RE = /\+/g;
53
+ const ENC_CARET_RE = /%5e/gi;
54
+ const ENC_BACKTICK_RE = /%60/gi;
55
+ const ENC_PIPE_RE = /%7c/gi;
56
+ const ENC_SPACE_RE = /%20/gi;
57
+ function encode(text) {
58
+ return encodeURI("" + text).replace(ENC_PIPE_RE, "|");
59
+ }
60
+ function encodeQueryValue(input) {
61
+ return encode(typeof input === "string" ? input : JSON.stringify(input)).replace(PLUS_RE, "%2B").replace(ENC_SPACE_RE, "+").replace(HASH_RE, "%23").replace(AMPERSAND_RE, "%26").replace(ENC_BACKTICK_RE, "`").replace(ENC_CARET_RE, "^").replace(SLASH_RE, "%2F");
62
+ }
63
+ function encodeQueryKey(text) {
64
+ return encodeQueryValue(text).replace(EQUAL_RE, "%3D");
65
+ }
66
+ function decode(text = "") {
67
+ try {
68
+ return decodeURIComponent("" + text);
69
+ } catch {
70
+ return "" + text;
71
+ }
72
+ }
73
+ function decodeQueryKey(text) {
74
+ return decode(text.replace(PLUS_RE, " "));
75
+ }
76
+ function decodeQueryValue(text) {
77
+ return decode(text.replace(PLUS_RE, " "));
78
+ }
79
+ function parseQuery(parametersString = "") {
80
+ const object = /* @__PURE__ */ Object.create(null);
81
+ if (parametersString[0] === "?") parametersString = parametersString.slice(1);
82
+ for (const parameter of parametersString.split("&")) {
83
+ const s = parameter.match(/([^=]+)=?(.*)/) || [];
84
+ if (s.length < 2) continue;
85
+ const key = decodeQueryKey(s[1]);
86
+ if (key === "__proto__" || key === "constructor") continue;
87
+ const value = decodeQueryValue(s[2] || "");
88
+ if (object[key] === void 0) object[key] = value;
89
+ else if (Array.isArray(object[key])) object[key].push(value);
90
+ else object[key] = [object[key], value];
91
+ }
92
+ return object;
93
+ }
94
+ function encodeQueryItem(key, value) {
95
+ if (typeof value === "number" || typeof value === "boolean") value = String(value);
96
+ if (!value) return encodeQueryKey(key);
97
+ if (Array.isArray(value)) return value.map((_value) => `${encodeQueryKey(key)}=${encodeQueryValue(_value)}`).join("&");
98
+ return `${encodeQueryKey(key)}=${encodeQueryValue(value)}`;
99
+ }
100
+ function stringifyQuery(query) {
101
+ return Object.keys(query).filter((k) => query[k] !== void 0).map((k) => encodeQueryItem(k, query[k])).filter(Boolean).join("&");
102
+ }
103
+ const PROTOCOL_STRICT_REGEX = /^[\s\w\0+.-]{2,}:([/\\]{1,2})/;
104
+ const PROTOCOL_REGEX = /^[\s\w\0+.-]{2,}:([/\\]{2})?/;
105
+ const PROTOCOL_RELATIVE_REGEX = /^([/\\]\s*){2,}[^/\\]/;
106
+ const TRAILING_SLASH_RE = /\/$|\/\?|\/#/;
107
+ const JOIN_LEADING_SLASH_RE = /^\.?\//;
108
+ function hasProtocol(inputString, opts = {}) {
109
+ if (typeof opts === "boolean") opts = { acceptRelative: opts };
110
+ if (opts.strict) return PROTOCOL_STRICT_REGEX.test(inputString);
111
+ return PROTOCOL_REGEX.test(inputString) || (opts.acceptRelative ? PROTOCOL_RELATIVE_REGEX.test(inputString) : false);
112
+ }
113
+ function hasTrailingSlash(input = "", respectQueryAndFragment) {
114
+ if (!respectQueryAndFragment) return input.endsWith("/");
115
+ return TRAILING_SLASH_RE.test(input);
116
+ }
117
+ function withoutTrailingSlash(input = "", respectQueryAndFragment) {
118
+ if (!respectQueryAndFragment) return (hasTrailingSlash(input) ? input.slice(0, -1) : input) || "/";
119
+ if (!hasTrailingSlash(input, true)) return input || "/";
120
+ let path = input;
121
+ let fragment = "";
122
+ const fragmentIndex = input.indexOf("#");
123
+ if (fragmentIndex !== -1) {
124
+ path = input.slice(0, fragmentIndex);
125
+ fragment = input.slice(fragmentIndex);
126
+ }
127
+ const [s0, ...s] = path.split("?");
128
+ const cleanPath = s0.endsWith("/") ? s0.slice(0, -1) : s0;
129
+ return (cleanPath || "/") + (s.length > 0 ? `?${s.join("?")}` : "") + fragment;
130
+ }
131
+ function withTrailingSlash(input = "", respectQueryAndFragment) {
132
+ if (!respectQueryAndFragment) return input.endsWith("/") ? input : input + "/";
133
+ if (hasTrailingSlash(input, true)) return input || "/";
134
+ let path = input;
135
+ let fragment = "";
136
+ const fragmentIndex = input.indexOf("#");
137
+ if (fragmentIndex !== -1) {
138
+ path = input.slice(0, fragmentIndex);
139
+ fragment = input.slice(fragmentIndex);
140
+ if (!path) return fragment;
141
+ }
142
+ const [s0, ...s] = path.split("?");
143
+ return s0 + "/" + (s.length > 0 ? `?${s.join("?")}` : "") + fragment;
144
+ }
145
+ function withBase(input, base) {
146
+ if (isEmptyURL(base) || hasProtocol(input)) return input;
147
+ const _base = withoutTrailingSlash(base);
148
+ if (input.startsWith(_base)) return input;
149
+ return joinURL(_base, input);
150
+ }
151
+ function withQuery(input, query) {
152
+ const parsed = parseURL(input);
153
+ const mergedQuery = {
154
+ ...parseQuery(parsed.search),
155
+ ...query
156
+ };
157
+ parsed.search = stringifyQuery(mergedQuery);
158
+ return stringifyParsedURL(parsed);
159
+ }
160
+ function isEmptyURL(url) {
161
+ return !url || url === "/";
162
+ }
163
+ function isNonEmptyURL(url) {
164
+ return url && url !== "/";
165
+ }
166
+ function joinURL(base, ...input) {
167
+ let url = base || "";
168
+ for (const segment of input.filter((url2) => isNonEmptyURL(url2))) if (url) {
169
+ const _segment = segment.replace(JOIN_LEADING_SLASH_RE, "");
170
+ url = withTrailingSlash(url) + _segment;
171
+ } else url = segment;
172
+ return url;
173
+ }
174
+ const protocolRelative = Symbol.for("ufo:protocolRelative");
175
+ function parseURL(input = "", defaultProto) {
176
+ const _specialProtoMatch = input.match(/^[\s\0]*(blob:|data:|javascript:|vbscript:)(.*)/i);
177
+ if (_specialProtoMatch) {
178
+ const [, _proto, _pathname = ""] = _specialProtoMatch;
179
+ return {
180
+ protocol: _proto.toLowerCase(),
181
+ pathname: _pathname,
182
+ href: _proto + _pathname,
183
+ auth: "",
184
+ host: "",
185
+ search: "",
186
+ hash: ""
187
+ };
188
+ }
189
+ if (!hasProtocol(input, { acceptRelative: true })) return defaultProto ? parseURL(defaultProto + input) : parsePath(input);
190
+ const [, protocol = "", auth, hostAndPath = ""] = input.replace(/\\/g, "/").match(/^[\s\0]*([\w+.-]{2,}:)?\/\/([^/@]+@)?(.*)/) || [];
191
+ let [, host = "", path = ""] = hostAndPath.match(/([^#/?]*)(.*)?/) || [];
192
+ if (protocol === "file:") path = path.replace(/\/(?=[A-Za-z]:)/, "");
193
+ const { pathname, search, hash } = parsePath(path);
194
+ return {
195
+ protocol: protocol.toLowerCase(),
196
+ auth: auth ? auth.slice(0, Math.max(0, auth.length - 1)) : "",
197
+ host,
198
+ pathname,
199
+ search,
200
+ hash,
201
+ [protocolRelative]: !protocol
202
+ };
203
+ }
204
+ function parsePath(input = "") {
205
+ const [pathname = "", search = "", hash = ""] = (input.match(/([^#?]*)(\?[^#]*)?(#.*)?/) || []).splice(1);
206
+ return {
207
+ pathname,
208
+ search,
209
+ hash
210
+ };
211
+ }
212
+ function stringifyParsedURL(parsed) {
213
+ const pathname = parsed.pathname || "";
214
+ const search = parsed.search ? (parsed.search.startsWith("?") ? "" : "?") + parsed.search : "";
215
+ const hash = parsed.hash || "";
216
+ const auth = parsed.auth ? parsed.auth + "@" : "";
217
+ const host = parsed.host || "";
218
+ const proto = parsed.protocol || parsed[protocolRelative] ? (parsed.protocol || "") + "//" : "";
219
+ return proto + auth + host + pathname + search + hash;
220
+ }
221
+
222
+ //#endregion
223
+ //#region node_modules/ofetch/dist/shared/ofetch.03887fc3.mjs
224
+ var FetchError = class extends Error {
225
+ constructor(message, opts) {
226
+ super(message, opts);
227
+ this.name = "FetchError";
228
+ if (opts?.cause && !this.cause) this.cause = opts.cause;
229
+ }
230
+ };
231
+ function createFetchError(ctx) {
232
+ const errorMessage = ctx.error?.message || ctx.error?.toString() || "";
233
+ const method = ctx.request?.method || ctx.options?.method || "GET";
234
+ const url = ctx.request?.url || String(ctx.request) || "/";
235
+ const requestStr = `[${method}] ${JSON.stringify(url)}`;
236
+ const statusStr = ctx.response ? `${ctx.response.status} ${ctx.response.statusText}` : "<no response>";
237
+ const message = `${requestStr}: ${statusStr}${errorMessage ? ` ${errorMessage}` : ""}`;
238
+ const fetchError = new FetchError(message, ctx.error ? { cause: ctx.error } : void 0);
239
+ for (const key of [
240
+ "request",
241
+ "options",
242
+ "response"
243
+ ]) Object.defineProperty(fetchError, key, { get() {
244
+ return ctx[key];
245
+ } });
246
+ for (const [key, refKey] of [
247
+ ["data", "_data"],
248
+ ["status", "status"],
249
+ ["statusCode", "status"],
250
+ ["statusText", "statusText"],
251
+ ["statusMessage", "statusText"]
252
+ ]) Object.defineProperty(fetchError, key, { get() {
253
+ return ctx.response && ctx.response[refKey];
254
+ } });
255
+ return fetchError;
256
+ }
257
+ const payloadMethods = new Set(Object.freeze([
258
+ "PATCH",
259
+ "POST",
260
+ "PUT",
261
+ "DELETE"
262
+ ]));
263
+ function isPayloadMethod(method = "GET") {
264
+ return payloadMethods.has(method.toUpperCase());
265
+ }
266
+ function isJSONSerializable(value) {
267
+ if (value === void 0) return false;
268
+ const t = typeof value;
269
+ if (t === "string" || t === "number" || t === "boolean" || t === null) return true;
270
+ if (t !== "object") return false;
271
+ if (Array.isArray(value)) return true;
272
+ if (value.buffer) return false;
273
+ return value.constructor && value.constructor.name === "Object" || typeof value.toJSON === "function";
274
+ }
275
+ const textTypes = /* @__PURE__ */ new Set([
276
+ "image/svg",
277
+ "application/xml",
278
+ "application/xhtml",
279
+ "application/html"
280
+ ]);
281
+ const JSON_RE = /^application\/(?:[\w!#$%&*.^`~-]*\+)?json(;.+)?$/i;
282
+ function detectResponseType(_contentType = "") {
283
+ if (!_contentType) return "json";
284
+ const contentType = _contentType.split(";").shift() || "";
285
+ if (JSON_RE.test(contentType)) return "json";
286
+ if (textTypes.has(contentType) || contentType.startsWith("text/")) return "text";
287
+ return "blob";
288
+ }
289
+ function resolveFetchOptions(request, input, defaults, Headers$1) {
290
+ const headers = mergeHeaders(input?.headers ?? request?.headers, defaults?.headers, Headers$1);
291
+ let query;
292
+ if (defaults?.query || defaults?.params || input?.params || input?.query) query = {
293
+ ...defaults?.params,
294
+ ...defaults?.query,
295
+ ...input?.params,
296
+ ...input?.query
297
+ };
298
+ return {
299
+ ...defaults,
300
+ ...input,
301
+ query,
302
+ params: query,
303
+ headers
304
+ };
305
+ }
306
+ function mergeHeaders(input, defaults, Headers$1) {
307
+ if (!defaults) return new Headers$1(input);
308
+ const headers = new Headers$1(defaults);
309
+ if (input) for (const [key, value] of Symbol.iterator in input || Array.isArray(input) ? input : new Headers$1(input)) headers.set(key, value);
310
+ return headers;
311
+ }
312
+ async function callHooks(context, hooks) {
313
+ if (hooks) if (Array.isArray(hooks)) for (const hook of hooks) await hook(context);
314
+ else await hooks(context);
315
+ }
316
+ const retryStatusCodes = /* @__PURE__ */ new Set([
317
+ 408,
318
+ 409,
319
+ 425,
320
+ 429,
321
+ 500,
322
+ 502,
323
+ 503,
324
+ 504
325
+ ]);
326
+ const nullBodyResponses = /* @__PURE__ */ new Set([
327
+ 101,
328
+ 204,
329
+ 205,
330
+ 304
331
+ ]);
332
+ function createFetch(globalOptions = {}) {
333
+ const { fetch: fetch$1 = globalThis.fetch, Headers: Headers$1 = globalThis.Headers, AbortController: AbortController$1 = globalThis.AbortController } = globalOptions;
334
+ async function onError(context) {
335
+ const isAbort = context.error && context.error.name === "AbortError" && !context.options.timeout || false;
336
+ if (context.options.retry !== false && !isAbort) {
337
+ let retries;
338
+ if (typeof context.options.retry === "number") retries = context.options.retry;
339
+ else retries = isPayloadMethod(context.options.method) ? 0 : 1;
340
+ const responseCode = context.response && context.response.status || 500;
341
+ if (retries > 0 && (Array.isArray(context.options.retryStatusCodes) ? context.options.retryStatusCodes.includes(responseCode) : retryStatusCodes.has(responseCode))) {
342
+ const retryDelay = typeof context.options.retryDelay === "function" ? context.options.retryDelay(context) : context.options.retryDelay || 0;
343
+ if (retryDelay > 0) await new Promise((resolve) => setTimeout(resolve, retryDelay));
344
+ return $fetchRaw(context.request, {
345
+ ...context.options,
346
+ retry: retries - 1
347
+ });
348
+ }
349
+ }
350
+ const error = createFetchError(context);
351
+ if (Error.captureStackTrace) Error.captureStackTrace(error, $fetchRaw);
352
+ throw error;
353
+ }
354
+ const $fetchRaw = async function $fetchRaw2(_request, _options = {}) {
355
+ const context = {
356
+ request: _request,
357
+ options: resolveFetchOptions(_request, _options, globalOptions.defaults, Headers$1),
358
+ response: void 0,
359
+ error: void 0
360
+ };
361
+ if (context.options.method) context.options.method = context.options.method.toUpperCase();
362
+ if (context.options.onRequest) await callHooks(context, context.options.onRequest);
363
+ if (typeof context.request === "string") {
364
+ if (context.options.baseURL) context.request = withBase(context.request, context.options.baseURL);
365
+ if (context.options.query) {
366
+ context.request = withQuery(context.request, context.options.query);
367
+ delete context.options.query;
368
+ }
369
+ if ("query" in context.options) delete context.options.query;
370
+ if ("params" in context.options) delete context.options.params;
371
+ }
372
+ if (context.options.body && isPayloadMethod(context.options.method)) {
373
+ if (isJSONSerializable(context.options.body)) {
374
+ context.options.body = typeof context.options.body === "string" ? context.options.body : JSON.stringify(context.options.body);
375
+ context.options.headers = new Headers$1(context.options.headers || {});
376
+ if (!context.options.headers.has("content-type")) context.options.headers.set("content-type", "application/json");
377
+ if (!context.options.headers.has("accept")) context.options.headers.set("accept", "application/json");
378
+ } else if ("pipeTo" in context.options.body && typeof context.options.body.pipeTo === "function" || typeof context.options.body.pipe === "function") {
379
+ if (!("duplex" in context.options)) context.options.duplex = "half";
380
+ }
381
+ }
382
+ let abortTimeout;
383
+ if (!context.options.signal && context.options.timeout) {
384
+ const controller = new AbortController$1();
385
+ abortTimeout = setTimeout(() => {
386
+ const error = new Error("[TimeoutError]: The operation was aborted due to timeout");
387
+ error.name = "TimeoutError";
388
+ error.code = 23;
389
+ controller.abort(error);
390
+ }, context.options.timeout);
391
+ context.options.signal = controller.signal;
392
+ }
393
+ try {
394
+ context.response = await fetch$1(context.request, context.options);
395
+ } catch (error) {
396
+ context.error = error;
397
+ if (context.options.onRequestError) await callHooks(context, context.options.onRequestError);
398
+ return await onError(context);
399
+ } finally {
400
+ if (abortTimeout) clearTimeout(abortTimeout);
401
+ }
402
+ const hasBody = (context.response.body || context.response._bodyInit) && !nullBodyResponses.has(context.response.status) && context.options.method !== "HEAD";
403
+ if (hasBody) {
404
+ const responseType = (context.options.parseResponse ? "json" : context.options.responseType) || detectResponseType(context.response.headers.get("content-type") || "");
405
+ switch (responseType) {
406
+ case "json": {
407
+ const data = await context.response.text();
408
+ const parseFunction = context.options.parseResponse || destr;
409
+ context.response._data = parseFunction(data);
410
+ break;
411
+ }
412
+ case "stream": {
413
+ context.response._data = context.response.body || context.response._bodyInit;
414
+ break;
415
+ }
416
+ default: context.response._data = await context.response[responseType]();
417
+ }
418
+ }
419
+ if (context.options.onResponse) await callHooks(context, context.options.onResponse);
420
+ if (!context.options.ignoreResponseError && context.response.status >= 400 && context.response.status < 600) {
421
+ if (context.options.onResponseError) await callHooks(context, context.options.onResponseError);
422
+ return await onError(context);
423
+ }
424
+ return context.response;
425
+ };
426
+ const $fetch = async function $fetch2(request, options) {
427
+ const r$1 = await $fetchRaw(request, options);
428
+ return r$1._data;
429
+ };
430
+ $fetch.raw = $fetchRaw;
431
+ $fetch.native = (...args) => fetch$1(...args);
432
+ $fetch.create = (defaultOptions = {}, customGlobalOptions = {}) => createFetch({
433
+ ...globalOptions,
434
+ ...customGlobalOptions,
435
+ defaults: {
436
+ ...globalOptions.defaults,
437
+ ...customGlobalOptions.defaults,
438
+ ...defaultOptions
439
+ }
440
+ });
441
+ return $fetch;
442
+ }
443
+
444
+ //#endregion
445
+ //#region node_modules/ofetch/dist/index.mjs
446
+ const _globalThis = function() {
447
+ if (typeof globalThis !== "undefined") return globalThis;
448
+ if (typeof self !== "undefined") return self;
449
+ if (typeof window !== "undefined") return window;
450
+ if (typeof global !== "undefined") return global;
451
+ throw new Error("unable to locate global object");
452
+ }();
453
+ const fetch = _globalThis.fetch ? (...args) => _globalThis.fetch(...args) : () => Promise.reject(new Error("[ofetch] global.fetch is not supported!"));
454
+ const Headers = _globalThis.Headers;
455
+ const AbortController = _globalThis.AbortController;
456
+ const ofetch = createFetch({
457
+ fetch,
458
+ Headers,
459
+ AbortController
460
+ });
461
+
462
+ //#endregion
463
+ //#region src/utils/MyInvoisClient.ts
464
+ var MyInvoisClient = class {
465
+ baseUrl;
466
+ clientId;
467
+ clientSecret;
468
+ token = "";
469
+ tokenExpiration = new Date();
470
+ constructor(clientId, clientSecret, environment) {
471
+ this.clientId = clientId;
472
+ this.clientSecret = clientSecret;
473
+ if (environment === "sandbox") this.baseUrl = "https://preprod-mytax.hasil.gov.my/";
474
+ else this.baseUrl = "https://mytax.hasil.gov.my";
475
+ }
476
+ async refreshToken() {
477
+ const tokenResponse = await ofetch(`${this.baseUrl}/connect/token`, {
478
+ method: "POST",
479
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
480
+ body: {
481
+ grant_type: "client_credentials",
482
+ client_id: this.clientId,
483
+ client_secret: this.clientSecret,
484
+ scope: "InvoicingAPI"
485
+ }
486
+ });
487
+ this.token = tokenResponse.access_token;
488
+ this.tokenExpiration = new Date(Date.now() + tokenResponse.expires_in * 1e3);
489
+ }
490
+ async getToken() {
491
+ if (this.tokenExpiration < new Date()) await this.refreshToken();
492
+ return this.token;
493
+ }
494
+ async fetch(path, options = {}) {
495
+ const token = await this.getToken();
496
+ return ofetch(`${this.baseUrl}${path}`, {
497
+ ...options,
498
+ headers: {
499
+ ...options.headers,
500
+ Authorization: `Bearer ${token}`
501
+ },
502
+ responseType: "json"
503
+ });
504
+ }
505
+ /**
506
+ * Validates a TIN against a NRIC
507
+ *
508
+ * @param tin
509
+ * @param nric
510
+ * @returns
511
+ */
512
+ async verifyTin(tin, nric) {
513
+ try {
514
+ await this.fetch(`/api/v1.0/taxpayer/validate/${tin}?idType=NRIC&idValue=${nric}`, { method: "GET" });
515
+ return true;
516
+ } catch {
517
+ return false;
518
+ }
519
+ }
520
+ };
521
+
522
+ //#endregion
523
+ export { MyInvoisClient };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@ripwords/myinvois-client",
3
+ "version": "0.0.1",
4
+ "description": "MyInvois client",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "module": "src/index.ts",
8
+ "type": "module",
9
+ "scripts": {
10
+ "build": "rolldown -c rolldown.config.ts",
11
+ "release": "bun run build && npm publish --access public && git push --follow-tags",
12
+ "lint": "oxlint",
13
+ "test": "vitest"
14
+ },
15
+ "devDependencies": {
16
+ "@types/bun": "latest",
17
+ "oxlint": "^0.16.6",
18
+ "prettier": "^3.5.3",
19
+ "vitest": "^3.1.1",
20
+ "rolldown": "^1.0.0-beta.7",
21
+ "rolldown-plugin-dts": "^0.7.0"
22
+ },
23
+ "peerDependencies": {
24
+ "typescript": "^5"
25
+ },
26
+ "dependencies": {
27
+ "ofetch": "^1.4.1"
28
+ }
29
+ }
@@ -0,0 +1,10 @@
1
+ import { defineConfig } from 'rolldown'
2
+ import { dts } from 'rolldown-plugin-dts'
3
+
4
+ export default defineConfig({
5
+ input: 'src/index.ts',
6
+ plugins: [dts()],
7
+ output: {
8
+ dir: 'dist',
9
+ },
10
+ })
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export type * from './types/index.d.ts'
2
+ export * from './utils/MyInvoisClient'
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Represents the allowed classification codes for e-Invoice items.
3
+ * Based on the documentation: https://sdk.myinvois.hasil.gov.my/codes/classification-codes/
4
+ */
5
+ export type ClassificationCode =
6
+ | '001' // Breastfeeding equipment
7
+ | '002' // Child care centres and kindergartens fees
8
+ | '003' // Computer, smartphone or tablet
9
+ | '004' // Consolidated e-Invoice
10
+ | '005' // Construction materials (as specified under Fourth Schedule of the Lembaga Pembangunan Industri Pembinaan Malaysia Act 1994)
11
+ | '006' // Disbursement
12
+ | '007' // Donation
13
+ | '008' // e-Commerce - e-Invoice to buyer / purchaser
14
+ | '009' // e-Commerce - Self-billed e-Invoice to seller, logistics, etc.
15
+ | '010' // Education fees
16
+ | '011' // Goods on consignment (Consignor)
17
+ | '012' // Goods on consignment (Consignee)
18
+ | '013' // Gym membership
19
+ | '014' // Insurance - Education and medical benefits
20
+ | '015' // Insurance - Takaful or life insurance
21
+ | '016' // Interest and financing expenses
22
+ | '017' // Internet subscription
23
+ | '018' // Land and building
24
+ | '019' // Medical examination for learning disabilities and early intervention or rehabilitation treatments of learning disabilities
25
+ | '020' // Medical examination or vaccination expenses
26
+ | '021' // Medical expenses for serious diseases
27
+ | '022' // Others
28
+ | '023' // Petroleum operations (as defined in Petroleum (Income Tax) Act 1967)
29
+ | '024' // Private retirement scheme or deferred annuity scheme
30
+ | '025' // Motor vehicle
31
+ | '026' // Subscription of books / journals / magazines / newspapers / other similar publications
32
+ | '027' // Reimbursement
33
+ | '028' // Rental of motor vehicle
34
+ | '029' // EV charging facilities (Installation, rental, sale / purchase or subscription fees)
35
+ | '030' // Repair and maintenance
36
+ | '031' // Research and development
37
+ | '032' // Foreign income
38
+ | '033' // Self-billed - Betting and gaming
39
+ | '034' // Self-billed - Importation of goods
40
+ | '035' // Self-billed - Importation of services
41
+ | '036' // Self-billed - Others
42
+ | '037' // Self-billed - Monetary payment to agents, dealers or distributors
43
+ | '038' // Sports equipment, rental / entry fees for sports facilities, registration in sports competition or sports training fees imposed by associations / sports clubs / companies registered with the Sports Commissioner or Companies Commission of Malaysia and carrying out sports activities as listed under the Sports Development Act 1997
44
+ | '039' // Supporting equipment for disabled person
45
+ | '040' // Voluntary contribution to approved provident fund
46
+ | '041' // Dental examination or treatment
47
+ | '042' // Fertility treatment
48
+ | '043' // Treatment and home care nursing, daycare centres and residential care centers
49
+ | '044' // Vouchers, gift cards, loyalty points, etc
50
+ | '045' // Self-billed - Non-monetary payment to agents, dealers or distributors
51
+
52
+ /**
53
+ * Enum representing the allowed classification codes with descriptive names.
54
+ * Provides a more readable way to reference classification codes.
55
+ *
56
+ * @example
57
+ * const code = ClassificationCodeEnum.ComputerSmartphoneOrTablet;
58
+ * console.log(code); // Output: "003"
59
+ */
60
+ export enum ClassificationCodeEnum {
61
+ BreastfeedingEquipment = '001',
62
+ ChildCareCentresAndKindergartensFees = '002',
63
+ ComputerSmartphoneOrTablet = '003',
64
+ ConsolidatedEInvoice = '004',
65
+ ConstructionMaterials = '005',
66
+ Disbursement = '006',
67
+ Donation = '007',
68
+ ECommerceEInvoiceToBuyer = '008',
69
+ ECommerceSelfBilledToSellerLogistics = '009',
70
+ EducationFees = '010',
71
+ GoodsOnConsignmentConsignor = '011',
72
+ GoodsOnConsignmentConsignee = '012',
73
+ GymMembership = '013',
74
+ InsuranceEducationMedicalBenefits = '014',
75
+ InsuranceTakafulLife = '015',
76
+ InterestFinancingExpenses = '016',
77
+ InternetSubscription = '017',
78
+ LandAndBuilding = '018',
79
+ MedicalExamLearningDisabilities = '019',
80
+ MedicalExamVaccination = '020',
81
+ MedicalExpensesSeriousDiseases = '021',
82
+ Others = '022',
83
+ PetroleumOperations = '023',
84
+ PrivateRetirementSchemeDeferredAnnuity = '024',
85
+ MotorVehicle = '025',
86
+ SubscriptionBooksJournalsEtc = '026',
87
+ Reimbursement = '027',
88
+ RentalOfMotorVehicle = '028',
89
+ EVChargingFacilities = '029',
90
+ RepairAndMaintenance = '030',
91
+ ResearchAndDevelopment = '031',
92
+ ForeignIncome = '032',
93
+ SelfBilledBettingGaming = '033',
94
+ SelfBilledImportationGoods = '034',
95
+ SelfBilledImportationServices = '035',
96
+ SelfBilledOthers = '036',
97
+ SelfBilledMonetaryPaymentToAgents = '037',
98
+ SportsEquipmentRentalFeesEtc = '038',
99
+ SupportingEquipmentDisabledPerson = '039',
100
+ VoluntaryContributionProvidentFund = '040',
101
+ DentalExamTreatment = '041',
102
+ FertilityTreatment = '042',
103
+ TreatmentHomeCareNursingEtc = '043',
104
+ VouchersGiftCardsLoyaltyPoints = '044',
105
+ SelfBilledNonMonetaryPaymentToAgents = '045',
106
+ }
107
+
108
+ /**
109
+ * Interface representing a classification code entry.
110
+ * Contains the code and its corresponding description.
111
+ */
112
+ export interface Classification {
113
+ code: ClassificationCode
114
+ description: string
115
+ }