@arkstack/common 0.14.19 → 0.14.20
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/faker.d.ts +9 -0
- package/dist/faker.js +20 -0
- package/dist/index.d.ts +32 -4
- package/dist/index.js +2 -1
- package/dist/{utils-DEflBOXp.js → system-DUaI4u99.js} +1 -434
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils-Df3nH1sG.js +437 -0
- package/package.json +9 -4
- /package/dist/{helpers-CfQxt_q2.d.ts → helpers-BrQ0B-EX.d.ts} +0 -0
package/dist/faker.d.ts
ADDED
package/dist/faker.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { v as configLoader } from "./system-DUaI4u99.js";
|
|
2
|
+
import * as fakerLocales from "@faker-js/faker";
|
|
3
|
+
import { Faker } from "@faker-js/faker";
|
|
4
|
+
import { pictwoImage } from "@pictwo/faker";
|
|
5
|
+
//#region src/faker.ts
|
|
6
|
+
const config = globalThis.config ?? ((key, def) => configLoader.resolve(key, def));
|
|
7
|
+
const fallbackLocale = "en";
|
|
8
|
+
const resolveLocale = () => {
|
|
9
|
+
const locale = config("app.locale", fallbackLocale);
|
|
10
|
+
if (typeof locale === "string" && locale in fakerLocales && typeof fakerLocales[locale] === "object") return fakerLocales[locale];
|
|
11
|
+
return fakerLocales[fallbackLocale];
|
|
12
|
+
};
|
|
13
|
+
const fakerInstance = new Faker({ locale: [resolveLocale()].filter(Boolean) });
|
|
14
|
+
const { faker: _, ...image } = fakerInstance.image;
|
|
15
|
+
const faker = {
|
|
16
|
+
...fakerInstance,
|
|
17
|
+
image: Object.assign({}, image, pictwoImage())
|
|
18
|
+
};
|
|
19
|
+
//#endregion
|
|
20
|
+
export { faker };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference path="./app.d.ts" />
|
|
2
|
-
import { a as abortIf, c as initializeGlobalContext, d as Hash, f as Encryption, i as abort, l as isClass, n as ModelConstructor, o as assertFound, r as ModelRegistry, s as getModel, t as AbstractModelConstructor, u as perPage } from "./helpers-
|
|
2
|
+
import { a as abortIf, c as initializeGlobalContext, d as Hash, f as Encryption, i as abort, l as isClass, n as ModelConstructor, o as assertFound, r as ModelRegistry, s as getModel, t as AbstractModelConstructor, u as perPage } from "./helpers-BrQ0B-EX.js";
|
|
3
3
|
import { JitiOptions, JitiResolveOptions } from "jiti";
|
|
4
4
|
import { Arkstack } from "@arkstack/contract";
|
|
5
5
|
import pino from "pino";
|
|
@@ -170,6 +170,9 @@ declare class Logger {
|
|
|
170
170
|
static console(): typeof Console;
|
|
171
171
|
}
|
|
172
172
|
//#endregion
|
|
173
|
+
//#region src/locales.d.ts
|
|
174
|
+
declare const locales: readonly ["af_ZA", "ar", "az", "bn_BD", "cs_CZ", "cy", "da", "de", "de_AT", "de_CH", "dv", "el", "en", "en_AU", "en_AU_ocker", "en_BORK", "en_CA", "en_GB", "en_GH", "en_HK", "en_IE", "en_IN", "en_NG", "en_US", "en_ZA", "eo", "es", "es_MX", "fa", "fi", "fr", "fr_BE", "fr_CA", "fr_CH", "fr_LU", "fr_SN", "he", "hr", "hu", "hy", "id_ID", "it", "ja", "ka_GE", "ko", "ku_ckb", "ku_kmr_latin", "lv", "mk", "mn_MN_cyrl", "nb_NO", "ne", "nl", "nl_BE", "pl", "pt_BR", "pt_PT", "ro", "ro_MD", "ru", "sk", "sl_SI", "sr_RS_latin", "sv", "ta_IN", "th", "tr", "uk", "ur", "uz_UZ_latin", "vi", "yo_NG", "zh_CN", "zh_TW", "zu_ZA"];
|
|
175
|
+
//#endregion
|
|
173
176
|
//#region src/types.d.ts
|
|
174
177
|
interface ConfigRegistry {}
|
|
175
178
|
/**
|
|
@@ -192,6 +195,11 @@ interface EnvRegistry {
|
|
|
192
195
|
APP_URL: string;
|
|
193
196
|
APP_HOST: string;
|
|
194
197
|
APP_PORT: number;
|
|
198
|
+
APP_DEBUG: boolean;
|
|
199
|
+
APP_TIMEZONE: string;
|
|
200
|
+
APP_LOCALE: typeof locales[number];
|
|
201
|
+
APP_FALLBACK_LOCALE: typeof locales[number];
|
|
202
|
+
APP_FAKER_LOCALE: typeof locales[number];
|
|
195
203
|
NODE_ENV: 'development' | 'production' | 'test';
|
|
196
204
|
PORT: number;
|
|
197
205
|
HOST: string;
|
|
@@ -200,6 +208,8 @@ interface EnvRegistry {
|
|
|
200
208
|
OUTPUT_DIR_DEV: string;
|
|
201
209
|
CONFIG_PATH: string;
|
|
202
210
|
TUNNEL: boolean;
|
|
211
|
+
NGROK_AUTHTOKEN: string;
|
|
212
|
+
NGROK_DOMAIN: string;
|
|
203
213
|
FILESYSTEM_DISK: string;
|
|
204
214
|
CACHE_STORE: string;
|
|
205
215
|
CACHE_PREFIX: string;
|
|
@@ -237,7 +247,25 @@ interface EnvRegistry {
|
|
|
237
247
|
AWS_URL: string;
|
|
238
248
|
AWS_ENDPOINT: string;
|
|
239
249
|
}
|
|
240
|
-
/**
|
|
250
|
+
/**
|
|
251
|
+
* App Confifuration
|
|
252
|
+
*/
|
|
253
|
+
interface AppConfig {
|
|
254
|
+
env: string;
|
|
255
|
+
key: string;
|
|
256
|
+
url: string;
|
|
257
|
+
host: string;
|
|
258
|
+
name: string;
|
|
259
|
+
frontend_url: string;
|
|
260
|
+
debug: boolean;
|
|
261
|
+
timezone: string;
|
|
262
|
+
locale: typeof locales[number];
|
|
263
|
+
fallback_locale: typeof locales[number];
|
|
264
|
+
faker_locale: typeof locales[number];
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Known environment variable names.
|
|
268
|
+
*/
|
|
241
269
|
type EnvKey = keyof EnvRegistry & string;
|
|
242
270
|
/** The registered type for a known key, or `string` for an unknown one. */
|
|
243
271
|
type EnvLookup<K extends string> = [K] extends [EnvKey] ? EnvRegistry[K] : string;
|
|
@@ -274,7 +302,7 @@ interface LoggerLog {
|
|
|
274
302
|
*/
|
|
275
303
|
type EnvReturn<X, K extends string, D> = [X] extends [never] ? [D] extends [undefined] ? EnvLookup<K> : EnvLookup<K> | D : [D] extends [undefined] ? X : X | D;
|
|
276
304
|
interface GlobalEnv {
|
|
277
|
-
<X = never, D = undefined, K extends string =
|
|
305
|
+
<X = never, D = undefined, K extends (keyof EnvRegistry | (string & {})) = keyof EnvRegistry>(env: K, defaultValue?: D): EnvReturn<X, K, D>;
|
|
278
306
|
}
|
|
279
307
|
type ConfigShape = keyof ConfigRegistry extends never ? Record<string, any> : ConfigRegistry;
|
|
280
308
|
interface GlobalConfig {
|
|
@@ -742,4 +770,4 @@ declare class ConfigLoader {
|
|
|
742
770
|
*/
|
|
743
771
|
declare const configLoader: ConfigLoader;
|
|
744
772
|
//#endregion
|
|
745
|
-
export { AbstractModelConstructor, AppException, ArkstackErrorPayload, ArkstackErrorShape, CONFIG_KEY, ConfigLoader, ConfigRegistry, ConfigShape, DotPath, DotPathValue, Encryption, EnvKey, EnvLoader, EnvLookup, EnvRegistry, EnvReturn, ErrorHandler, Exception, FileImporter, GlobalConfig, GlobalEnv, Hash, Hook, HookArgs, HookFor, HookName, HookPos, HookPositions, HookRegistry, IHook, Logger, LoggerChalk, LoggerLog, LoggerParseSignature, MergedConfig, ModelConstructor, ModelRegistry, Primitive, PublishEntry, PublishFilter, PublishGroup, Publisher, RequestException, UnionToIntersection, abort, abortIf, appKey, appUrl, assertFound, bindGracefulShutdown, bootWithDetectedPort, config, configLoader, createErrorPayload, discoverCommands, env, envLoader, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, initializeGlobalContext, interopDefault, isClass, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, rebuildOutput, renderError, resolveRuntimeDir, resolveRuntimeModule, serializeError, shouldHideStack, shouldLogError, toErrorShape, toOutputPath };
|
|
773
|
+
export { AbstractModelConstructor, AppConfig, AppException, ArkstackErrorPayload, ArkstackErrorShape, CONFIG_KEY, ConfigLoader, ConfigRegistry, ConfigShape, DotPath, DotPathValue, Encryption, EnvKey, EnvLoader, EnvLookup, EnvRegistry, EnvReturn, ErrorHandler, Exception, FileImporter, GlobalConfig, GlobalEnv, Hash, Hook, HookArgs, HookFor, HookName, HookPos, HookPositions, HookRegistry, IHook, Logger, LoggerChalk, LoggerLog, LoggerParseSignature, MergedConfig, ModelConstructor, ModelRegistry, Primitive, PublishEntry, PublishFilter, PublishGroup, Publisher, RequestException, UnionToIntersection, abort, abortIf, appKey, appUrl, assertFound, bindGracefulShutdown, bootWithDetectedPort, config, configLoader, createErrorPayload, discoverCommands, env, envLoader, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, initializeGlobalContext, interopDefault, isClass, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, rebuildOutput, renderError, resolveRuntimeDir, resolveRuntimeModule, serializeError, shouldHideStack, shouldLogError, toErrorShape, toOutputPath };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { _ as ConfigLoader, a as env, c as nodeEnv, d as resolveRuntimeDir, f as resolveRuntimeModule, g as CONFIG_KEY, h as envLoader, i as discoverCommands, l as outputDir, m as EnvLoader, n as appUrl, o as importFile, p as toOutputPath, r as config, s as interopDefault, t as appKey, u as rebuildOutput, v as configLoader } from "./system-DUaI4u99.js";
|
|
2
|
+
import { _ as Hash, c as abortIf, d as initializeGlobalContext, f as isClass, g as Exception, h as AppException, l as assertFound, m as RequestException, p as perPage, s as abort, u as getModel, v as Encryption } from "./utils-Df3nH1sG.js";
|
|
2
3
|
import { Hook as Hook$1 } from "@arkstack/foundry";
|
|
3
4
|
import { Arkstack } from "@arkstack/contract";
|
|
4
5
|
import { str } from "@h3ravel/support";
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
2
1
|
import { createJiti } from "jiti";
|
|
3
2
|
import { existsSync, readdirSync } from "fs";
|
|
4
3
|
import { Arkstack } from "@arkstack/contract";
|
|
@@ -10,9 +9,6 @@ import { config } from "dotenv";
|
|
|
10
9
|
import { pathToFileURL } from "node:url";
|
|
11
10
|
import { rm } from "node:fs/promises";
|
|
12
11
|
import { spawn } from "node:child_process";
|
|
13
|
-
import { Secret, TOTP } from "otpauth";
|
|
14
|
-
import { compare, genSalt, hash } from "bcryptjs";
|
|
15
|
-
import { getUserConfig } from "arkormx";
|
|
16
12
|
//#region src/ConfigLoader.ts
|
|
17
13
|
const CONFIG_KEY = Symbol("globalConfig");
|
|
18
14
|
globalThis[CONFIG_KEY] = {};
|
|
@@ -476,433 +472,4 @@ const interopDefault = (imp) => {
|
|
|
476
472
|
return typeof imp === "function" ? imp : imp.default;
|
|
477
473
|
};
|
|
478
474
|
//#endregion
|
|
479
|
-
|
|
480
|
-
var Encryption = class {
|
|
481
|
-
static algorithm = "aes-256-gcm";
|
|
482
|
-
static getKey() {
|
|
483
|
-
const secret = appKey("TWO_FACTOR_ENCRYPTION_KEY");
|
|
484
|
-
if (!secret) throw new Error("APP_KEY is required to use two-factor authentication. Run `ark key:generate`.");
|
|
485
|
-
return createHash("sha256").update(secret).digest();
|
|
486
|
-
}
|
|
487
|
-
static encrypt(value) {
|
|
488
|
-
const iv = randomBytes(12);
|
|
489
|
-
const cipher = createCipheriv(this.algorithm, this.getKey(), iv);
|
|
490
|
-
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
491
|
-
return [
|
|
492
|
-
iv,
|
|
493
|
-
cipher.getAuthTag(),
|
|
494
|
-
ciphertext
|
|
495
|
-
].map((part) => part.toString("base64url")).join(":");
|
|
496
|
-
}
|
|
497
|
-
static decrypt(payload) {
|
|
498
|
-
const [iv, authTag, ciphertext] = payload.split(":");
|
|
499
|
-
if (!iv || !authTag || !ciphertext) throw new Error("Invalid encrypted payload format");
|
|
500
|
-
const decipher = createDecipheriv(this.algorithm, this.getKey(), Buffer.from(iv, "base64url"));
|
|
501
|
-
decipher.setAuthTag(Buffer.from(authTag, "base64url"));
|
|
502
|
-
return Buffer.concat([decipher.update(Buffer.from(ciphertext, "base64url")), decipher.final()]).toString("utf8");
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
//#endregion
|
|
506
|
-
//#region src/utils/hash.ts
|
|
507
|
-
var Hash = class {
|
|
508
|
-
/**
|
|
509
|
-
* Hash a value using bcrypt
|
|
510
|
-
*
|
|
511
|
-
* @param value
|
|
512
|
-
* @returns
|
|
513
|
-
*/
|
|
514
|
-
static async make(value) {
|
|
515
|
-
return await hash(value, await genSalt(10));
|
|
516
|
-
}
|
|
517
|
-
/**
|
|
518
|
-
* Verify a value against a hashed value
|
|
519
|
-
*
|
|
520
|
-
* @param value
|
|
521
|
-
* @param hashedValue
|
|
522
|
-
* @returns
|
|
523
|
-
*/
|
|
524
|
-
static async verify(value, hashedValue) {
|
|
525
|
-
return await compare(value, hashedValue);
|
|
526
|
-
}
|
|
527
|
-
/**
|
|
528
|
-
* Generate a one-time password (OTP) using TOTP algorithm
|
|
529
|
-
*
|
|
530
|
-
* @param digits The number of digits for the OTP, default is 6.
|
|
531
|
-
* @param label A label to identify the OTP, can be an email or phone number.
|
|
532
|
-
* @param period Interval of time for which a token is valid, in seconds.
|
|
533
|
-
* @returns
|
|
534
|
-
*/
|
|
535
|
-
static otp(digits = 6, label = "Alice", period = 30) {
|
|
536
|
-
return new TOTP({
|
|
537
|
-
label,
|
|
538
|
-
digits,
|
|
539
|
-
issuer: env$1("APP_NAME", "Roseed"),
|
|
540
|
-
algorithm: "SHA1",
|
|
541
|
-
period,
|
|
542
|
-
secret: "US3WHSG7X5KAPV27VANWKQHF3SH3HULL"
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
static totp(secret, label, issuer = env$1("APP_NAME", "Roseed"), period = 30) {
|
|
546
|
-
return new TOTP({
|
|
547
|
-
issuer,
|
|
548
|
-
label,
|
|
549
|
-
algorithm: "SHA1",
|
|
550
|
-
digits: 6,
|
|
551
|
-
period,
|
|
552
|
-
secret: Secret.fromBase32(secret)
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
};
|
|
556
|
-
//#endregion
|
|
557
|
-
//#region src/Exceptions/Exception.ts
|
|
558
|
-
var Exception = class extends Error {
|
|
559
|
-
name;
|
|
560
|
-
constructor(message, options) {
|
|
561
|
-
super(message, options);
|
|
562
|
-
this.name = "Exception";
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
//#endregion
|
|
566
|
-
//#region src/Exceptions/AppException.ts
|
|
567
|
-
var AppException = class extends Exception {
|
|
568
|
-
errors = void 0;
|
|
569
|
-
statusCode;
|
|
570
|
-
constructor(message, statusCode = 400, options) {
|
|
571
|
-
super(message, options);
|
|
572
|
-
this.statusCode = statusCode;
|
|
573
|
-
}
|
|
574
|
-
};
|
|
575
|
-
//#endregion
|
|
576
|
-
//#region src/Exceptions/RequestException.ts
|
|
577
|
-
var RequestException = class RequestException extends AppException {
|
|
578
|
-
statusCode;
|
|
579
|
-
constructor(message, statusCode = 400, options) {
|
|
580
|
-
super(message, statusCode, options);
|
|
581
|
-
this.statusCode = statusCode;
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Asserts that a value is not null or undefined.
|
|
585
|
-
*
|
|
586
|
-
* @param value
|
|
587
|
-
* @param message
|
|
588
|
-
* @param code
|
|
589
|
-
* @throws {RequestException} Throws if the value is null or undefined.
|
|
590
|
-
*/
|
|
591
|
-
static assertFound(value, message, code = 404) {
|
|
592
|
-
if (!value) throw new RequestException(message, code);
|
|
593
|
-
}
|
|
594
|
-
/**
|
|
595
|
-
* Asserts that a value is not null or undefined.
|
|
596
|
-
*
|
|
597
|
-
* @param value
|
|
598
|
-
* @param message
|
|
599
|
-
* @param code
|
|
600
|
-
* @throws {RequestException} Throws if the value is null or undefined.
|
|
601
|
-
* @deprecated Use assertFound instead
|
|
602
|
-
*/
|
|
603
|
-
static assertNotEmpty(value, message, code = 404) {
|
|
604
|
-
return this.assertFound(value, message, code);
|
|
605
|
-
}
|
|
606
|
-
/**
|
|
607
|
-
* Asserts that a boolean condition is true.
|
|
608
|
-
*
|
|
609
|
-
* @param boolean
|
|
610
|
-
* @param message
|
|
611
|
-
* @param code
|
|
612
|
-
* @throws {RequestException} Throws if the boolean condition is true.
|
|
613
|
-
*/
|
|
614
|
-
static abortIf(boolean, message, code) {
|
|
615
|
-
if (boolean) throw new RequestException(message, code);
|
|
616
|
-
}
|
|
617
|
-
};
|
|
618
|
-
//#endregion
|
|
619
|
-
//#region src/utils/helpers.ts
|
|
620
|
-
/**
|
|
621
|
-
* Checks and asserts if target is a class
|
|
622
|
-
*
|
|
623
|
-
* @param target
|
|
624
|
-
* @returns
|
|
625
|
-
*/
|
|
626
|
-
const isClass = (target) => {
|
|
627
|
-
return typeof target === "function" && /^class\s/.test(Function.prototype.toString.call(target));
|
|
628
|
-
};
|
|
629
|
-
/**
|
|
630
|
-
* Determine the number of items to return per page based on the provided query parameters.
|
|
631
|
-
*
|
|
632
|
-
* @param query
|
|
633
|
-
* @returns
|
|
634
|
-
*/
|
|
635
|
-
const perPage = (query) => {
|
|
636
|
-
const requestedPerPage = Number(query.limit ?? query.perPage ?? 15);
|
|
637
|
-
return Number.isFinite(requestedPerPage) && requestedPerPage > 0 ? Math.min(requestedPerPage, 50) : 15;
|
|
638
|
-
};
|
|
639
|
-
async function getModel(modelName) {
|
|
640
|
-
const resolveModelExport = (module, modelName) => {
|
|
641
|
-
if (!isModelModule(module)) return module;
|
|
642
|
-
return module.default ?? module[modelName] ?? module;
|
|
643
|
-
};
|
|
644
|
-
const isModelModule = (value) => typeof value === "object" && value !== null;
|
|
645
|
-
const modelPath = getUserConfig().paths?.models || "./src/models";
|
|
646
|
-
const model = resolveModelExport(await importFile(resolveRuntimeModule(path.join(path.isAbsolute(modelPath) ? modelPath : path.join(Arkstack.rootDir(), modelPath), modelName))), path.basename(modelName, path.extname(modelName)));
|
|
647
|
-
if (typeof model !== "function") throw new Error(`Model "${modelName}" not found`);
|
|
648
|
-
return model;
|
|
649
|
-
}
|
|
650
|
-
const initializeGlobalContext = async ({ Request, Response, Session } = {}) => {
|
|
651
|
-
try {
|
|
652
|
-
const { Request: Req, Response: Res, Session: Ses } = await import("@arkstack/http");
|
|
653
|
-
Session ??= new Ses();
|
|
654
|
-
Request ??= new Req();
|
|
655
|
-
Response ??= new Res();
|
|
656
|
-
} catch {
|
|
657
|
-
Session ??= new class {}();
|
|
658
|
-
Request ??= new class {}();
|
|
659
|
-
Response ??= new class {}();
|
|
660
|
-
}
|
|
661
|
-
globalThis.session ??= () => Session;
|
|
662
|
-
globalThis.request ??= () => Request;
|
|
663
|
-
globalThis.response ??= () => Response;
|
|
664
|
-
};
|
|
665
|
-
/**
|
|
666
|
-
* Thows to abort the current request
|
|
667
|
-
*
|
|
668
|
-
* @param message
|
|
669
|
-
* @param code
|
|
670
|
-
* @throws {RequestException}
|
|
671
|
-
*/
|
|
672
|
-
const abort = (message = "Request Aborted", code = 404) => {
|
|
673
|
-
RequestException.abortIf(true, message, code);
|
|
674
|
-
};
|
|
675
|
-
/**
|
|
676
|
-
* Asserts that a boolean condition is true.
|
|
677
|
-
*
|
|
678
|
-
* @param boolean
|
|
679
|
-
* @param message
|
|
680
|
-
* @param code
|
|
681
|
-
* @throws {RequestException} Throws if the boolean condition is true.
|
|
682
|
-
*/
|
|
683
|
-
const abortIf = (boolean, message = "Request Aborted", code = 404) => {
|
|
684
|
-
RequestException.abortIf(boolean, message, code);
|
|
685
|
-
};
|
|
686
|
-
/**
|
|
687
|
-
* Asserts that a value is not null or undefined.
|
|
688
|
-
*
|
|
689
|
-
* @param value
|
|
690
|
-
* @param message
|
|
691
|
-
* @param code
|
|
692
|
-
* @throws {RequestException} Throws if the value is null or undefined.
|
|
693
|
-
*/
|
|
694
|
-
const assertFound = (value, message, code = 404) => {
|
|
695
|
-
if (!value) throw new RequestException(message, code);
|
|
696
|
-
};
|
|
697
|
-
//#endregion
|
|
698
|
-
//#region src/utils/traits.ts
|
|
699
|
-
/**
|
|
700
|
-
* CRC32 implementation in TypeScript, adapted from https://stackoverflow.com/a/18639999
|
|
701
|
-
* Note: This implementation is not cryptographically secure and is only used for generating
|
|
702
|
-
* unique identifiers for traits based on their factory function's string representation.
|
|
703
|
-
*/
|
|
704
|
-
const crcTable = [];
|
|
705
|
-
for (let n = 0; n < 256; n++) {
|
|
706
|
-
let c = n;
|
|
707
|
-
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
708
|
-
crcTable[n] = c;
|
|
709
|
-
}
|
|
710
|
-
const crc32 = (str) => {
|
|
711
|
-
let crc = -1;
|
|
712
|
-
for (let i = 0; i < str.length; i++) crc = crc >>> 8 ^ crcTable[(crc ^ str.charCodeAt(i)) & 255];
|
|
713
|
-
return (crc ^ -1) >>> 0;
|
|
714
|
-
};
|
|
715
|
-
const isCons = (fn) => typeof fn === "function" && !!fn.prototype && !!fn.prototype.constructor;
|
|
716
|
-
const isArkormModelInstance = (value) => typeof value === "object" && value !== null && typeof value.constructor === "function" && typeof value.getAttribute === "function" && typeof value.setAttribute === "function";
|
|
717
|
-
const isTypeFactory = (fn) => typeof fn === "function" && !fn.prototype && fn.length === 0;
|
|
718
|
-
/**
|
|
719
|
-
* API: generate trait (technical implementation)
|
|
720
|
-
*
|
|
721
|
-
* @param args
|
|
722
|
-
*/
|
|
723
|
-
function trait(...args) {
|
|
724
|
-
const factory = args.length === 2 ? args[1] : args[0];
|
|
725
|
-
const superTraits = args.length === 2 ? args[0] : void 0;
|
|
726
|
-
return {
|
|
727
|
-
id: crc32(factory.toString()),
|
|
728
|
-
symbol: Symbol("trait"),
|
|
729
|
-
factory,
|
|
730
|
-
superTraits
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
/**
|
|
734
|
-
* utility function: add an additional invisible property to an object
|
|
735
|
-
*
|
|
736
|
-
* @param cons
|
|
737
|
-
* @param field
|
|
738
|
-
* @param value
|
|
739
|
-
* @returns
|
|
740
|
-
*/
|
|
741
|
-
const extendProperties = (cons, field, value) => Object.defineProperty(cons, field, {
|
|
742
|
-
value,
|
|
743
|
-
enumerable: false,
|
|
744
|
-
writable: false
|
|
745
|
-
});
|
|
746
|
-
const traitMethodRegistry = Symbol("trait-method-registry");
|
|
747
|
-
const cloneMethodRegistry = (target) => {
|
|
748
|
-
const registry = target[traitMethodRegistry];
|
|
749
|
-
return new Map([...registry?.entries() ?? []].map(([name, methods]) => [name, [...methods]]));
|
|
750
|
-
};
|
|
751
|
-
const registerMethodScope = (target, base, ignored) => {
|
|
752
|
-
const registry = cloneMethodRegistry(base);
|
|
753
|
-
for (const name of Reflect.ownKeys(target)) {
|
|
754
|
-
if (ignored.has(name)) continue;
|
|
755
|
-
const method = Object.getOwnPropertyDescriptor(target, name)?.value;
|
|
756
|
-
if (typeof method !== "function") continue;
|
|
757
|
-
const methods = registry.get(name);
|
|
758
|
-
const previous = base?.[name];
|
|
759
|
-
if (methods) {
|
|
760
|
-
if (methods.at(-1) !== method) methods.push(method);
|
|
761
|
-
registry.set(name, methods);
|
|
762
|
-
} else if (typeof previous === "function" && previous !== method) registry.set(name, [previous, method]);
|
|
763
|
-
}
|
|
764
|
-
Object.defineProperty(target, traitMethodRegistry, {
|
|
765
|
-
configurable: false,
|
|
766
|
-
enumerable: false,
|
|
767
|
-
value: registry,
|
|
768
|
-
writable: false
|
|
769
|
-
});
|
|
770
|
-
};
|
|
771
|
-
/**
|
|
772
|
-
* Registers conflicting trait methods
|
|
773
|
-
*
|
|
774
|
-
* @param classInstance
|
|
775
|
-
* @param baseClass
|
|
776
|
-
*/
|
|
777
|
-
const registerTraitMethods = (classInstance, baseClass) => {
|
|
778
|
-
registerMethodScope(classInstance.prototype, baseClass.prototype, new Set(["constructor"]));
|
|
779
|
-
registerMethodScope(classInstance, baseClass, new Set([
|
|
780
|
-
"length",
|
|
781
|
-
"name",
|
|
782
|
-
"prototype",
|
|
783
|
-
"arguments",
|
|
784
|
-
"caller"
|
|
785
|
-
]));
|
|
786
|
-
};
|
|
787
|
-
/**
|
|
788
|
-
* Return every trait implementation for a method, bound to the supplied
|
|
789
|
-
* instance or class. Methods are ordered from the base implementation to the
|
|
790
|
-
* currently active trait implementation.
|
|
791
|
-
*
|
|
792
|
-
* @param target
|
|
793
|
-
* @param name
|
|
794
|
-
* @returns
|
|
795
|
-
*/
|
|
796
|
-
const getTraitMethods = (target, name) => {
|
|
797
|
-
return (((typeof target === "function" ? target : Object.getPrototypeOf(target))?.[traitMethodRegistry])?.get(name) ?? []).map((method) => method.bind(target));
|
|
798
|
-
};
|
|
799
|
-
/**
|
|
800
|
-
* Invoke every trait implementation for a method in registration order.
|
|
801
|
-
*
|
|
802
|
-
* @param target
|
|
803
|
-
* @param name
|
|
804
|
-
* @param args
|
|
805
|
-
* @returns
|
|
806
|
-
*/
|
|
807
|
-
const callTraitMethods = (target, name, ...args) => {
|
|
808
|
-
const methods = getTraitMethods(target, name);
|
|
809
|
-
if (methods.length === 0) {
|
|
810
|
-
console.warn(`No conflicting trait methods found for "${String(name)}".`);
|
|
811
|
-
return [];
|
|
812
|
-
}
|
|
813
|
-
return methods.map((method) => method(...args));
|
|
814
|
-
};
|
|
815
|
-
/**
|
|
816
|
-
* utility function: get raw trait
|
|
817
|
-
*
|
|
818
|
-
* @param x
|
|
819
|
-
* @returns
|
|
820
|
-
*/
|
|
821
|
-
const rawTrait = (x) => isTypeFactory(x) ? x() : x;
|
|
822
|
-
/**
|
|
823
|
-
* utility function: derive a trait
|
|
824
|
-
*
|
|
825
|
-
* @param trait$
|
|
826
|
-
* @param baseClass
|
|
827
|
-
* @param derived
|
|
828
|
-
* @returns
|
|
829
|
-
*/
|
|
830
|
-
const deriveTrait = (trait$, baseClass, derived) => {
|
|
831
|
-
const trait = rawTrait(trait$);
|
|
832
|
-
if (trait === void 0 || trait === null || typeof trait.id !== "number") throw new Error("use(): received an undefined or invalid trait. This usually means a circular import — the trait module had not finished initializing when use() ran. Avoid importing models at the top level of trait modules, or break the import cycle.");
|
|
833
|
-
let classInstance = baseClass;
|
|
834
|
-
if (!derived.has(trait.id)) {
|
|
835
|
-
derived.set(trait.id, true);
|
|
836
|
-
if (trait.superTraits !== void 0) for (const superTrait of reverseTraitList(trait.superTraits)) classInstance = deriveTrait(superTrait, classInstance, derived);
|
|
837
|
-
const base = classInstance;
|
|
838
|
-
classInstance = trait.factory(classInstance);
|
|
839
|
-
registerTraitMethods(classInstance, base);
|
|
840
|
-
extendProperties(classInstance, "id", crc32(trait.factory.toString()));
|
|
841
|
-
extendProperties(classInstance, trait.symbol, true);
|
|
842
|
-
}
|
|
843
|
-
return classInstance;
|
|
844
|
-
};
|
|
845
|
-
/**
|
|
846
|
-
* utility function: get reversed trait list
|
|
847
|
-
*
|
|
848
|
-
* @param traits
|
|
849
|
-
* @returns
|
|
850
|
-
*/
|
|
851
|
-
const reverseTraitList = (traits) => traits.slice().reverse();
|
|
852
|
-
function use(...args) {
|
|
853
|
-
const withMethodHelpers = args[0] === true;
|
|
854
|
-
const traits = withMethodHelpers ? args.slice(1) : args;
|
|
855
|
-
if (traits.length === 0) throw new Error("invalid number of parameters (expected one or more traits)");
|
|
856
|
-
let classInstance;
|
|
857
|
-
let lot;
|
|
858
|
-
const last = traits[traits.length - 1];
|
|
859
|
-
if (isCons(last) && !isTypeFactory(last)) {
|
|
860
|
-
classInstance = last;
|
|
861
|
-
lot = traits.slice(0, -1);
|
|
862
|
-
} else if (isArkormModelInstance(last)) {
|
|
863
|
-
classInstance = last.constructor;
|
|
864
|
-
lot = traits.slice(0, -1);
|
|
865
|
-
} else {
|
|
866
|
-
classInstance = class ROOT {};
|
|
867
|
-
lot = traits;
|
|
868
|
-
}
|
|
869
|
-
const derived = /* @__PURE__ */ new Map();
|
|
870
|
-
for (const trait of reverseTraitList(lot)) classInstance = deriveTrait(trait, classInstance, derived);
|
|
871
|
-
if (withMethodHelpers) classInstance = class TraitMethodEnabled extends classInstance {
|
|
872
|
-
getTraitMethods(name) {
|
|
873
|
-
return getTraitMethods(this, name);
|
|
874
|
-
}
|
|
875
|
-
callTraitMethods(name, ...args) {
|
|
876
|
-
return callTraitMethods(this, name, ...args);
|
|
877
|
-
}
|
|
878
|
-
static getTraitMethods(name) {
|
|
879
|
-
return getTraitMethods(this, name);
|
|
880
|
-
}
|
|
881
|
-
static callTraitMethods(name, ...args) {
|
|
882
|
-
return callTraitMethods(this, name, ...args);
|
|
883
|
-
}
|
|
884
|
-
};
|
|
885
|
-
return classInstance;
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* API: type guard for checking whether class instance is derived from a trait
|
|
889
|
-
*
|
|
890
|
-
* @param instance
|
|
891
|
-
* @param trait
|
|
892
|
-
* @returns
|
|
893
|
-
*/
|
|
894
|
-
function uses(instance, trait) {
|
|
895
|
-
if (typeof instance !== "object" || instance === null) return false;
|
|
896
|
-
let obj = instance;
|
|
897
|
-
if (isCons(trait) && !isTypeFactory(trait)) return instance instanceof trait;
|
|
898
|
-
const idTrait = (isTypeFactory(trait) ? trait() : trait)["id"];
|
|
899
|
-
while (obj) {
|
|
900
|
-
if (Object.hasOwn(obj, "constructor")) {
|
|
901
|
-
if ((obj.constructor["id"] ?? 0) === idTrait) return true;
|
|
902
|
-
}
|
|
903
|
-
obj = Object.getPrototypeOf(obj);
|
|
904
|
-
}
|
|
905
|
-
return false;
|
|
906
|
-
}
|
|
907
|
-
//#endregion
|
|
908
|
-
export { resolveRuntimeModule as A, env$1 as C, outputDir as D, nodeEnv as E, ConfigLoader as F, configLoader as I, EnvLoader as M, envLoader as N, rebuildOutput as O, CONFIG_KEY as P, discoverCommands as S, interopDefault as T, Hash as _, use as a, appUrl as b, abortIf as c, initializeGlobalContext as d, isClass as f, Exception as g, AppException as h, trait as i, toOutputPath as j, resolveRuntimeDir as k, assertFound as l, RequestException as m, crc32 as n, uses as o, perPage as p, getTraitMethods as r, abort as s, callTraitMethods as t, getModel as u, Encryption as v, importFile as w, config$1 as x, appKey as y };
|
|
475
|
+
export { ConfigLoader as _, env$1 as a, nodeEnv as c, resolveRuntimeDir as d, resolveRuntimeModule as f, CONFIG_KEY as g, envLoader as h, discoverCommands as i, outputDir as l, EnvLoader as m, appUrl as n, importFile as o, toOutputPath as p, config$1 as r, interopDefault as s, appKey as t, rebuildOutput as u, configLoader as v };
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as abortIf, c as initializeGlobalContext, d as Hash, f as Encryption, i as abort, l as isClass, n as ModelConstructor, o as assertFound, r as ModelRegistry, s as getModel, t as AbstractModelConstructor, u as perPage } from "../helpers-
|
|
1
|
+
import { a as abortIf, c as initializeGlobalContext, d as Hash, f as Encryption, i as abort, l as isClass, n as ModelConstructor, o as assertFound, r as ModelRegistry, s as getModel, t as AbstractModelConstructor, u as perPage } from "../helpers-BrQ0B-EX.js";
|
|
2
2
|
import { Model } from "arkormx";
|
|
3
3
|
|
|
4
4
|
//#region src/utils/traits.d.ts
|
package/dist/utils/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { _ as Hash, a as use, c as abortIf, d as initializeGlobalContext, f as isClass, i as trait, l as assertFound, n as crc32, o as uses, p as perPage, r as getTraitMethods, s as abort, t as callTraitMethods, u as getModel, v as Encryption } from "../utils-
|
|
1
|
+
import { _ as Hash, a as use, c as abortIf, d as initializeGlobalContext, f as isClass, i as trait, l as assertFound, n as crc32, o as uses, p as perPage, r as getTraitMethods, s as abort, t as callTraitMethods, u as getModel, v as Encryption } from "../utils-Df3nH1sG.js";
|
|
2
2
|
export { Encryption, Hash, abort, abortIf, assertFound, callTraitMethods, crc32, getModel, getTraitMethods, initializeGlobalContext, isClass, perPage, trait, use, uses };
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { a as env, f as resolveRuntimeModule, o as importFile, t as appKey } from "./system-DUaI4u99.js";
|
|
2
|
+
import { createCipheriv, createDecipheriv, createHash, randomBytes } from "node:crypto";
|
|
3
|
+
import { Arkstack } from "@arkstack/contract";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { Secret, TOTP } from "otpauth";
|
|
6
|
+
import { compare, genSalt, hash } from "bcryptjs";
|
|
7
|
+
import { getUserConfig } from "arkormx";
|
|
8
|
+
//#region src/utils/encryption.ts
|
|
9
|
+
var Encryption = class {
|
|
10
|
+
static algorithm = "aes-256-gcm";
|
|
11
|
+
static getKey() {
|
|
12
|
+
const secret = appKey("TWO_FACTOR_ENCRYPTION_KEY");
|
|
13
|
+
if (!secret) throw new Error("APP_KEY is required to use two-factor authentication. Run `ark key:generate`.");
|
|
14
|
+
return createHash("sha256").update(secret).digest();
|
|
15
|
+
}
|
|
16
|
+
static encrypt(value) {
|
|
17
|
+
const iv = randomBytes(12);
|
|
18
|
+
const cipher = createCipheriv(this.algorithm, this.getKey(), iv);
|
|
19
|
+
const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
|
|
20
|
+
return [
|
|
21
|
+
iv,
|
|
22
|
+
cipher.getAuthTag(),
|
|
23
|
+
ciphertext
|
|
24
|
+
].map((part) => part.toString("base64url")).join(":");
|
|
25
|
+
}
|
|
26
|
+
static decrypt(payload) {
|
|
27
|
+
const [iv, authTag, ciphertext] = payload.split(":");
|
|
28
|
+
if (!iv || !authTag || !ciphertext) throw new Error("Invalid encrypted payload format");
|
|
29
|
+
const decipher = createDecipheriv(this.algorithm, this.getKey(), Buffer.from(iv, "base64url"));
|
|
30
|
+
decipher.setAuthTag(Buffer.from(authTag, "base64url"));
|
|
31
|
+
return Buffer.concat([decipher.update(Buffer.from(ciphertext, "base64url")), decipher.final()]).toString("utf8");
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
//#endregion
|
|
35
|
+
//#region src/utils/hash.ts
|
|
36
|
+
var Hash = class {
|
|
37
|
+
/**
|
|
38
|
+
* Hash a value using bcrypt
|
|
39
|
+
*
|
|
40
|
+
* @param value
|
|
41
|
+
* @returns
|
|
42
|
+
*/
|
|
43
|
+
static async make(value) {
|
|
44
|
+
return await hash(value, await genSalt(10));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Verify a value against a hashed value
|
|
48
|
+
*
|
|
49
|
+
* @param value
|
|
50
|
+
* @param hashedValue
|
|
51
|
+
* @returns
|
|
52
|
+
*/
|
|
53
|
+
static async verify(value, hashedValue) {
|
|
54
|
+
return await compare(value, hashedValue);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Generate a one-time password (OTP) using TOTP algorithm
|
|
58
|
+
*
|
|
59
|
+
* @param digits The number of digits for the OTP, default is 6.
|
|
60
|
+
* @param label A label to identify the OTP, can be an email or phone number.
|
|
61
|
+
* @param period Interval of time for which a token is valid, in seconds.
|
|
62
|
+
* @returns
|
|
63
|
+
*/
|
|
64
|
+
static otp(digits = 6, label = "Alice", period = 30) {
|
|
65
|
+
return new TOTP({
|
|
66
|
+
label,
|
|
67
|
+
digits,
|
|
68
|
+
issuer: env("APP_NAME", "Roseed"),
|
|
69
|
+
algorithm: "SHA1",
|
|
70
|
+
period,
|
|
71
|
+
secret: "US3WHSG7X5KAPV27VANWKQHF3SH3HULL"
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
static totp(secret, label, issuer = env("APP_NAME", "Roseed"), period = 30) {
|
|
75
|
+
return new TOTP({
|
|
76
|
+
issuer,
|
|
77
|
+
label,
|
|
78
|
+
algorithm: "SHA1",
|
|
79
|
+
digits: 6,
|
|
80
|
+
period,
|
|
81
|
+
secret: Secret.fromBase32(secret)
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/Exceptions/Exception.ts
|
|
87
|
+
var Exception = class extends Error {
|
|
88
|
+
name;
|
|
89
|
+
constructor(message, options) {
|
|
90
|
+
super(message, options);
|
|
91
|
+
this.name = "Exception";
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
//#endregion
|
|
95
|
+
//#region src/Exceptions/AppException.ts
|
|
96
|
+
var AppException = class extends Exception {
|
|
97
|
+
errors = void 0;
|
|
98
|
+
statusCode;
|
|
99
|
+
constructor(message, statusCode = 400, options) {
|
|
100
|
+
super(message, options);
|
|
101
|
+
this.statusCode = statusCode;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/Exceptions/RequestException.ts
|
|
106
|
+
var RequestException = class RequestException extends AppException {
|
|
107
|
+
statusCode;
|
|
108
|
+
constructor(message, statusCode = 400, options) {
|
|
109
|
+
super(message, statusCode, options);
|
|
110
|
+
this.statusCode = statusCode;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Asserts that a value is not null or undefined.
|
|
114
|
+
*
|
|
115
|
+
* @param value
|
|
116
|
+
* @param message
|
|
117
|
+
* @param code
|
|
118
|
+
* @throws {RequestException} Throws if the value is null or undefined.
|
|
119
|
+
*/
|
|
120
|
+
static assertFound(value, message, code = 404) {
|
|
121
|
+
if (!value) throw new RequestException(message, code);
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Asserts that a value is not null or undefined.
|
|
125
|
+
*
|
|
126
|
+
* @param value
|
|
127
|
+
* @param message
|
|
128
|
+
* @param code
|
|
129
|
+
* @throws {RequestException} Throws if the value is null or undefined.
|
|
130
|
+
* @deprecated Use assertFound instead
|
|
131
|
+
*/
|
|
132
|
+
static assertNotEmpty(value, message, code = 404) {
|
|
133
|
+
return this.assertFound(value, message, code);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Asserts that a boolean condition is true.
|
|
137
|
+
*
|
|
138
|
+
* @param boolean
|
|
139
|
+
* @param message
|
|
140
|
+
* @param code
|
|
141
|
+
* @throws {RequestException} Throws if the boolean condition is true.
|
|
142
|
+
*/
|
|
143
|
+
static abortIf(boolean, message, code) {
|
|
144
|
+
if (boolean) throw new RequestException(message, code);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
//#endregion
|
|
148
|
+
//#region src/utils/helpers.ts
|
|
149
|
+
/**
|
|
150
|
+
* Checks and asserts if target is a class
|
|
151
|
+
*
|
|
152
|
+
* @param target
|
|
153
|
+
* @returns
|
|
154
|
+
*/
|
|
155
|
+
const isClass = (target) => {
|
|
156
|
+
return typeof target === "function" && /^class\s/.test(Function.prototype.toString.call(target));
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Determine the number of items to return per page based on the provided query parameters.
|
|
160
|
+
*
|
|
161
|
+
* @param query
|
|
162
|
+
* @returns
|
|
163
|
+
*/
|
|
164
|
+
const perPage = (query) => {
|
|
165
|
+
const requestedPerPage = Number(query.limit ?? query.perPage ?? 15);
|
|
166
|
+
return Number.isFinite(requestedPerPage) && requestedPerPage > 0 ? Math.min(requestedPerPage, 50) : 15;
|
|
167
|
+
};
|
|
168
|
+
async function getModel(modelName) {
|
|
169
|
+
const resolveModelExport = (module, modelName) => {
|
|
170
|
+
if (!isModelModule(module)) return module;
|
|
171
|
+
return module.default ?? module[modelName] ?? module;
|
|
172
|
+
};
|
|
173
|
+
const isModelModule = (value) => typeof value === "object" && value !== null;
|
|
174
|
+
const modelPath = getUserConfig().paths?.models || "./src/models";
|
|
175
|
+
const model = resolveModelExport(await importFile(resolveRuntimeModule(path.join(path.isAbsolute(modelPath) ? modelPath : path.join(Arkstack.rootDir(), modelPath), modelName))), path.basename(modelName, path.extname(modelName)));
|
|
176
|
+
if (typeof model !== "function") throw new Error(`Model "${modelName}" not found`);
|
|
177
|
+
return model;
|
|
178
|
+
}
|
|
179
|
+
const initializeGlobalContext = async ({ Request, Response, Session } = {}) => {
|
|
180
|
+
try {
|
|
181
|
+
const { Request: Req, Response: Res, Session: Ses } = await import("@arkstack/http");
|
|
182
|
+
Session ??= new Ses();
|
|
183
|
+
Request ??= new Req();
|
|
184
|
+
Response ??= new Res();
|
|
185
|
+
} catch {
|
|
186
|
+
Session ??= new class {}();
|
|
187
|
+
Request ??= new class {}();
|
|
188
|
+
Response ??= new class {}();
|
|
189
|
+
}
|
|
190
|
+
globalThis.session ??= () => Session;
|
|
191
|
+
globalThis.request ??= () => Request;
|
|
192
|
+
globalThis.response ??= () => Response;
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Thows to abort the current request
|
|
196
|
+
*
|
|
197
|
+
* @param message
|
|
198
|
+
* @param code
|
|
199
|
+
* @throws {RequestException}
|
|
200
|
+
*/
|
|
201
|
+
const abort = (message = "Request Aborted", code = 404) => {
|
|
202
|
+
RequestException.abortIf(true, message, code);
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Asserts that a boolean condition is true.
|
|
206
|
+
*
|
|
207
|
+
* @param boolean
|
|
208
|
+
* @param message
|
|
209
|
+
* @param code
|
|
210
|
+
* @throws {RequestException} Throws if the boolean condition is true.
|
|
211
|
+
*/
|
|
212
|
+
const abortIf = (boolean, message = "Request Aborted", code = 404) => {
|
|
213
|
+
RequestException.abortIf(boolean, message, code);
|
|
214
|
+
};
|
|
215
|
+
/**
|
|
216
|
+
* Asserts that a value is not null or undefined.
|
|
217
|
+
*
|
|
218
|
+
* @param value
|
|
219
|
+
* @param message
|
|
220
|
+
* @param code
|
|
221
|
+
* @throws {RequestException} Throws if the value is null or undefined.
|
|
222
|
+
*/
|
|
223
|
+
const assertFound = (value, message, code = 404) => {
|
|
224
|
+
if (!value) throw new RequestException(message, code);
|
|
225
|
+
};
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/utils/traits.ts
|
|
228
|
+
/**
|
|
229
|
+
* CRC32 implementation in TypeScript, adapted from https://stackoverflow.com/a/18639999
|
|
230
|
+
* Note: This implementation is not cryptographically secure and is only used for generating
|
|
231
|
+
* unique identifiers for traits based on their factory function's string representation.
|
|
232
|
+
*/
|
|
233
|
+
const crcTable = [];
|
|
234
|
+
for (let n = 0; n < 256; n++) {
|
|
235
|
+
let c = n;
|
|
236
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
|
|
237
|
+
crcTable[n] = c;
|
|
238
|
+
}
|
|
239
|
+
const crc32 = (str) => {
|
|
240
|
+
let crc = -1;
|
|
241
|
+
for (let i = 0; i < str.length; i++) crc = crc >>> 8 ^ crcTable[(crc ^ str.charCodeAt(i)) & 255];
|
|
242
|
+
return (crc ^ -1) >>> 0;
|
|
243
|
+
};
|
|
244
|
+
const isCons = (fn) => typeof fn === "function" && !!fn.prototype && !!fn.prototype.constructor;
|
|
245
|
+
const isArkormModelInstance = (value) => typeof value === "object" && value !== null && typeof value.constructor === "function" && typeof value.getAttribute === "function" && typeof value.setAttribute === "function";
|
|
246
|
+
const isTypeFactory = (fn) => typeof fn === "function" && !fn.prototype && fn.length === 0;
|
|
247
|
+
/**
|
|
248
|
+
* API: generate trait (technical implementation)
|
|
249
|
+
*
|
|
250
|
+
* @param args
|
|
251
|
+
*/
|
|
252
|
+
function trait(...args) {
|
|
253
|
+
const factory = args.length === 2 ? args[1] : args[0];
|
|
254
|
+
const superTraits = args.length === 2 ? args[0] : void 0;
|
|
255
|
+
return {
|
|
256
|
+
id: crc32(factory.toString()),
|
|
257
|
+
symbol: Symbol("trait"),
|
|
258
|
+
factory,
|
|
259
|
+
superTraits
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* utility function: add an additional invisible property to an object
|
|
264
|
+
*
|
|
265
|
+
* @param cons
|
|
266
|
+
* @param field
|
|
267
|
+
* @param value
|
|
268
|
+
* @returns
|
|
269
|
+
*/
|
|
270
|
+
const extendProperties = (cons, field, value) => Object.defineProperty(cons, field, {
|
|
271
|
+
value,
|
|
272
|
+
enumerable: false,
|
|
273
|
+
writable: false
|
|
274
|
+
});
|
|
275
|
+
const traitMethodRegistry = Symbol("trait-method-registry");
|
|
276
|
+
const cloneMethodRegistry = (target) => {
|
|
277
|
+
const registry = target[traitMethodRegistry];
|
|
278
|
+
return new Map([...registry?.entries() ?? []].map(([name, methods]) => [name, [...methods]]));
|
|
279
|
+
};
|
|
280
|
+
const registerMethodScope = (target, base, ignored) => {
|
|
281
|
+
const registry = cloneMethodRegistry(base);
|
|
282
|
+
for (const name of Reflect.ownKeys(target)) {
|
|
283
|
+
if (ignored.has(name)) continue;
|
|
284
|
+
const method = Object.getOwnPropertyDescriptor(target, name)?.value;
|
|
285
|
+
if (typeof method !== "function") continue;
|
|
286
|
+
const methods = registry.get(name);
|
|
287
|
+
const previous = base?.[name];
|
|
288
|
+
if (methods) {
|
|
289
|
+
if (methods.at(-1) !== method) methods.push(method);
|
|
290
|
+
registry.set(name, methods);
|
|
291
|
+
} else if (typeof previous === "function" && previous !== method) registry.set(name, [previous, method]);
|
|
292
|
+
}
|
|
293
|
+
Object.defineProperty(target, traitMethodRegistry, {
|
|
294
|
+
configurable: false,
|
|
295
|
+
enumerable: false,
|
|
296
|
+
value: registry,
|
|
297
|
+
writable: false
|
|
298
|
+
});
|
|
299
|
+
};
|
|
300
|
+
/**
|
|
301
|
+
* Registers conflicting trait methods
|
|
302
|
+
*
|
|
303
|
+
* @param classInstance
|
|
304
|
+
* @param baseClass
|
|
305
|
+
*/
|
|
306
|
+
const registerTraitMethods = (classInstance, baseClass) => {
|
|
307
|
+
registerMethodScope(classInstance.prototype, baseClass.prototype, new Set(["constructor"]));
|
|
308
|
+
registerMethodScope(classInstance, baseClass, new Set([
|
|
309
|
+
"length",
|
|
310
|
+
"name",
|
|
311
|
+
"prototype",
|
|
312
|
+
"arguments",
|
|
313
|
+
"caller"
|
|
314
|
+
]));
|
|
315
|
+
};
|
|
316
|
+
/**
|
|
317
|
+
* Return every trait implementation for a method, bound to the supplied
|
|
318
|
+
* instance or class. Methods are ordered from the base implementation to the
|
|
319
|
+
* currently active trait implementation.
|
|
320
|
+
*
|
|
321
|
+
* @param target
|
|
322
|
+
* @param name
|
|
323
|
+
* @returns
|
|
324
|
+
*/
|
|
325
|
+
const getTraitMethods = (target, name) => {
|
|
326
|
+
return (((typeof target === "function" ? target : Object.getPrototypeOf(target))?.[traitMethodRegistry])?.get(name) ?? []).map((method) => method.bind(target));
|
|
327
|
+
};
|
|
328
|
+
/**
|
|
329
|
+
* Invoke every trait implementation for a method in registration order.
|
|
330
|
+
*
|
|
331
|
+
* @param target
|
|
332
|
+
* @param name
|
|
333
|
+
* @param args
|
|
334
|
+
* @returns
|
|
335
|
+
*/
|
|
336
|
+
const callTraitMethods = (target, name, ...args) => {
|
|
337
|
+
const methods = getTraitMethods(target, name);
|
|
338
|
+
if (methods.length === 0) {
|
|
339
|
+
console.warn(`No conflicting trait methods found for "${String(name)}".`);
|
|
340
|
+
return [];
|
|
341
|
+
}
|
|
342
|
+
return methods.map((method) => method(...args));
|
|
343
|
+
};
|
|
344
|
+
/**
|
|
345
|
+
* utility function: get raw trait
|
|
346
|
+
*
|
|
347
|
+
* @param x
|
|
348
|
+
* @returns
|
|
349
|
+
*/
|
|
350
|
+
const rawTrait = (x) => isTypeFactory(x) ? x() : x;
|
|
351
|
+
/**
|
|
352
|
+
* utility function: derive a trait
|
|
353
|
+
*
|
|
354
|
+
* @param trait$
|
|
355
|
+
* @param baseClass
|
|
356
|
+
* @param derived
|
|
357
|
+
* @returns
|
|
358
|
+
*/
|
|
359
|
+
const deriveTrait = (trait$, baseClass, derived) => {
|
|
360
|
+
const trait = rawTrait(trait$);
|
|
361
|
+
if (trait === void 0 || trait === null || typeof trait.id !== "number") throw new Error("use(): received an undefined or invalid trait. This usually means a circular import — the trait module had not finished initializing when use() ran. Avoid importing models at the top level of trait modules, or break the import cycle.");
|
|
362
|
+
let classInstance = baseClass;
|
|
363
|
+
if (!derived.has(trait.id)) {
|
|
364
|
+
derived.set(trait.id, true);
|
|
365
|
+
if (trait.superTraits !== void 0) for (const superTrait of reverseTraitList(trait.superTraits)) classInstance = deriveTrait(superTrait, classInstance, derived);
|
|
366
|
+
const base = classInstance;
|
|
367
|
+
classInstance = trait.factory(classInstance);
|
|
368
|
+
registerTraitMethods(classInstance, base);
|
|
369
|
+
extendProperties(classInstance, "id", crc32(trait.factory.toString()));
|
|
370
|
+
extendProperties(classInstance, trait.symbol, true);
|
|
371
|
+
}
|
|
372
|
+
return classInstance;
|
|
373
|
+
};
|
|
374
|
+
/**
|
|
375
|
+
* utility function: get reversed trait list
|
|
376
|
+
*
|
|
377
|
+
* @param traits
|
|
378
|
+
* @returns
|
|
379
|
+
*/
|
|
380
|
+
const reverseTraitList = (traits) => traits.slice().reverse();
|
|
381
|
+
function use(...args) {
|
|
382
|
+
const withMethodHelpers = args[0] === true;
|
|
383
|
+
const traits = withMethodHelpers ? args.slice(1) : args;
|
|
384
|
+
if (traits.length === 0) throw new Error("invalid number of parameters (expected one or more traits)");
|
|
385
|
+
let classInstance;
|
|
386
|
+
let lot;
|
|
387
|
+
const last = traits[traits.length - 1];
|
|
388
|
+
if (isCons(last) && !isTypeFactory(last)) {
|
|
389
|
+
classInstance = last;
|
|
390
|
+
lot = traits.slice(0, -1);
|
|
391
|
+
} else if (isArkormModelInstance(last)) {
|
|
392
|
+
classInstance = last.constructor;
|
|
393
|
+
lot = traits.slice(0, -1);
|
|
394
|
+
} else {
|
|
395
|
+
classInstance = class ROOT {};
|
|
396
|
+
lot = traits;
|
|
397
|
+
}
|
|
398
|
+
const derived = /* @__PURE__ */ new Map();
|
|
399
|
+
for (const trait of reverseTraitList(lot)) classInstance = deriveTrait(trait, classInstance, derived);
|
|
400
|
+
if (withMethodHelpers) classInstance = class TraitMethodEnabled extends classInstance {
|
|
401
|
+
getTraitMethods(name) {
|
|
402
|
+
return getTraitMethods(this, name);
|
|
403
|
+
}
|
|
404
|
+
callTraitMethods(name, ...args) {
|
|
405
|
+
return callTraitMethods(this, name, ...args);
|
|
406
|
+
}
|
|
407
|
+
static getTraitMethods(name) {
|
|
408
|
+
return getTraitMethods(this, name);
|
|
409
|
+
}
|
|
410
|
+
static callTraitMethods(name, ...args) {
|
|
411
|
+
return callTraitMethods(this, name, ...args);
|
|
412
|
+
}
|
|
413
|
+
};
|
|
414
|
+
return classInstance;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* API: type guard for checking whether class instance is derived from a trait
|
|
418
|
+
*
|
|
419
|
+
* @param instance
|
|
420
|
+
* @param trait
|
|
421
|
+
* @returns
|
|
422
|
+
*/
|
|
423
|
+
function uses(instance, trait) {
|
|
424
|
+
if (typeof instance !== "object" || instance === null) return false;
|
|
425
|
+
let obj = instance;
|
|
426
|
+
if (isCons(trait) && !isTypeFactory(trait)) return instance instanceof trait;
|
|
427
|
+
const idTrait = (isTypeFactory(trait) ? trait() : trait)["id"];
|
|
428
|
+
while (obj) {
|
|
429
|
+
if (Object.hasOwn(obj, "constructor")) {
|
|
430
|
+
if ((obj.constructor["id"] ?? 0) === idTrait) return true;
|
|
431
|
+
}
|
|
432
|
+
obj = Object.getPrototypeOf(obj);
|
|
433
|
+
}
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
//#endregion
|
|
437
|
+
export { Hash as _, use as a, abortIf as c, initializeGlobalContext as d, isClass as f, Exception as g, AppException as h, trait as i, assertFound as l, RequestException as m, crc32 as n, uses as o, perPage as p, getTraitMethods as r, abort as s, callTraitMethods as t, getModel as u, Encryption as v };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arkstack/common",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core utilities, primitives, and shared infrastructure for the Arkstack ecosystem.",
|
|
6
6
|
"homepage": "https://arkstack.toneflix.net",
|
|
@@ -28,23 +28,28 @@
|
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
30
|
".": "./dist/index.js",
|
|
31
|
+
"./faker": "./dist/faker.js",
|
|
31
32
|
"./utils": "./dist/utils/index.js",
|
|
32
33
|
"./package.json": "./package.json"
|
|
33
34
|
},
|
|
34
35
|
"dependencies": {
|
|
35
|
-
"
|
|
36
|
+
"@pictwo/faker": "^1.1.0",
|
|
36
37
|
"bcryptjs": "^3.0.3",
|
|
37
38
|
"chalk": "^5.6.2",
|
|
38
39
|
"detect-port": "^2.1.0",
|
|
39
40
|
"dotenv": "^17.4.2",
|
|
41
|
+
"jiti": "^2.7.0",
|
|
40
42
|
"otpauth": "^9.5.1",
|
|
41
43
|
"pino": "^10.3.1"
|
|
42
44
|
},
|
|
43
45
|
"peerDependencies": {
|
|
44
46
|
"@h3ravel/support": "^2.2.0",
|
|
45
47
|
"arkormx": "^2.10.1",
|
|
46
|
-
"@arkstack/
|
|
47
|
-
"@arkstack/
|
|
48
|
+
"@arkstack/contract": "^0.14.20",
|
|
49
|
+
"@arkstack/foundry": "^0.14.20"
|
|
50
|
+
},
|
|
51
|
+
"optionalDependencies": {
|
|
52
|
+
"@faker-js/faker": "^10.4.0"
|
|
48
53
|
},
|
|
49
54
|
"scripts": {
|
|
50
55
|
"build": "tsdown --config-loader unrun",
|
|
File without changes
|