@arkstack/common 0.5.2 → 0.5.3
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/README.md +754 -20
- package/dist/app.d.ts +61 -1
- package/dist/faker.d.ts +9 -0
- package/dist/faker.js +20 -0
- package/dist/helpers-BrQ0B-EX.d.ts +109 -0
- package/dist/index.d.ts +662 -49
- package/dist/index.js +129 -275
- package/dist/system-DUaI4u99.js +475 -0
- package/dist/utils/index.d.ts +188 -0
- package/dist/utils/index.js +2 -0
- package/dist/utils-DLifcZWV.js +452 -0
- package/package.json +19 -7
- package/resources/views/error.edge +199 -0
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import { createJiti } from "jiti";
|
|
2
|
+
import { existsSync, readdirSync } from "fs";
|
|
3
|
+
import { Arkstack } from "@arkstack/contract";
|
|
4
|
+
import { Arr, Obj, undot } from "@h3ravel/support";
|
|
5
|
+
import { readdirSync as readdirSync$1 } from "node:fs";
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
import path, { resolve } from "node:path";
|
|
8
|
+
import { config } from "dotenv";
|
|
9
|
+
import { pathToFileURL } from "node:url";
|
|
10
|
+
import { rm } from "node:fs/promises";
|
|
11
|
+
import { spawn } from "node:child_process";
|
|
12
|
+
//#region src/ConfigLoader.ts
|
|
13
|
+
const CONFIG_KEY = Symbol("globalConfig");
|
|
14
|
+
globalThis[CONFIG_KEY] = {};
|
|
15
|
+
/**
|
|
16
|
+
* Loads and resolves application configuration from the config directory.
|
|
17
|
+
*
|
|
18
|
+
* Config modules are read once (lazily) from the output directory and cached on
|
|
19
|
+
* a global symbol, then queried by dot-path. A partial config object can also be
|
|
20
|
+
* merged in at runtime.
|
|
21
|
+
*/
|
|
22
|
+
var ConfigLoader = class {
|
|
23
|
+
/**
|
|
24
|
+
* The cached config store, shared across instances via a global symbol.
|
|
25
|
+
*/
|
|
26
|
+
get store() {
|
|
27
|
+
return globalThis[CONFIG_KEY];
|
|
28
|
+
}
|
|
29
|
+
set store(value) {
|
|
30
|
+
globalThis[CONFIG_KEY] = value;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Read and cache config modules from the config directory on first use.
|
|
34
|
+
*/
|
|
35
|
+
load() {
|
|
36
|
+
if (Object.entries(this.store).length >= 1) return;
|
|
37
|
+
let files;
|
|
38
|
+
const require = createRequire(import.meta.url);
|
|
39
|
+
const configDir = this.resolveConfigDir();
|
|
40
|
+
try {
|
|
41
|
+
files = readdirSync$1(configDir, { withFileTypes: true }).filter((file) => {
|
|
42
|
+
if (file.name.includes("middleware") && globalThis.arkctx?.runtime === "CLI") return false;
|
|
43
|
+
return file.isFile() && (file.name.endsWith(".js") || file.name.endsWith(".ts"));
|
|
44
|
+
});
|
|
45
|
+
} catch {
|
|
46
|
+
files = [];
|
|
47
|
+
}
|
|
48
|
+
Object.assign(this.store, files.reduce((configs, file) => {
|
|
49
|
+
const configName = path.basename(file.name, path.extname(file.name));
|
|
50
|
+
try {
|
|
51
|
+
configs[configName] = require(path.join(file.parentPath, file.name)).default(typeof globalThis.app === "function" ? globalThis.app() : {});
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.warn(`[arkstack] Skipped config "${configName}": ${error.message}`);
|
|
54
|
+
}
|
|
55
|
+
return configs;
|
|
56
|
+
}, {}));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Resolve the directory to load config modules from.
|
|
60
|
+
*
|
|
61
|
+
* Prefers an explicit `CONFIG_PATH`, then the environment-selected output
|
|
62
|
+
* directory. Falls back to the other build output (`dist` ⇄
|
|
63
|
+
* `.arkstack/build`) so config still loads if the selected directory is
|
|
64
|
+
* missing or transiently emptied — e.g. a concurrent rebuild (`clean: true`)
|
|
65
|
+
* during a test run.
|
|
66
|
+
*/
|
|
67
|
+
resolveConfigDir() {
|
|
68
|
+
const root = Arkstack.rootDir();
|
|
69
|
+
const explicit = env("CONFIG_PATH");
|
|
70
|
+
if (explicit) return explicit;
|
|
71
|
+
const candidates = [
|
|
72
|
+
path.join(outputDir(), "config"),
|
|
73
|
+
path.join(root, env("OUTPUT_DIR", "dist"), "config"),
|
|
74
|
+
path.join(root, env("OUTPUT_DIR_DEV", ".arkstack/build"), "config")
|
|
75
|
+
];
|
|
76
|
+
return candidates.find((dir) => {
|
|
77
|
+
try {
|
|
78
|
+
return readdirSync$1(dir).some((file) => file.endsWith(".js") || file.endsWith(".ts"));
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}) ?? candidates[0];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Resolve configuration: read a dot-path value, merge a partial config
|
|
86
|
+
* object, or return the whole config.
|
|
87
|
+
*
|
|
88
|
+
* @param key Dot-path to read, or an object to merge in.
|
|
89
|
+
* @param defaultValue Returned when a string key is not found.
|
|
90
|
+
*/
|
|
91
|
+
resolve(key, defaultValue) {
|
|
92
|
+
if (typeof globalThis.env === "undefined") globalThis.env = (k, def) => k ? process.env[k] ?? def : process.env;
|
|
93
|
+
this.load();
|
|
94
|
+
if (typeof key === "object" && key !== null) this.store = undot(Object.assign({}, Arr.dot(this.store), Arr.dot(key)));
|
|
95
|
+
else if (typeof key === "string") return Obj.get(this.store, key, defaultValue);
|
|
96
|
+
return this.store;
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Shared config loader backing {@link config}.
|
|
101
|
+
*/
|
|
102
|
+
const configLoader = new ConfigLoader();
|
|
103
|
+
//#endregion
|
|
104
|
+
//#region src/EnvLoader.ts
|
|
105
|
+
/**
|
|
106
|
+
* Loads environment variables, reading the `.env` file on first access.
|
|
107
|
+
*
|
|
108
|
+
* The `.env` file is loaded lazily the first time a variable is read, so
|
|
109
|
+
* environment access never depends on a side-effect `import 'dotenv/config'`
|
|
110
|
+
* running first. Import ordering — which linters and bundlers may rewrite —
|
|
111
|
+
* could otherwise place an env-reading module before dotenv has populated
|
|
112
|
+
* `process.env`, leaving that module with default/stale values.
|
|
113
|
+
* `dotenv.config()` never overrides variables already set, so the lazy load is
|
|
114
|
+
* safe alongside other loaders.
|
|
115
|
+
*/
|
|
116
|
+
var EnvLoader = class {
|
|
117
|
+
loaded = false;
|
|
118
|
+
/**
|
|
119
|
+
* Load the `.env` file once.
|
|
120
|
+
*
|
|
121
|
+
* @returns
|
|
122
|
+
*/
|
|
123
|
+
ensureLoaded() {
|
|
124
|
+
if (this.loaded) return;
|
|
125
|
+
this.loaded = true;
|
|
126
|
+
try {
|
|
127
|
+
config({ quiet: true });
|
|
128
|
+
} catch {}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Read an environment variable, coercing booleans, numbers and `null`, and
|
|
132
|
+
* falling back to `defaultValue` when it is unset.
|
|
133
|
+
*
|
|
134
|
+
* @param name The variable name.
|
|
135
|
+
* @param defaultValue Returned when the variable is unset.
|
|
136
|
+
*/
|
|
137
|
+
get(name, defaultValue) {
|
|
138
|
+
this.ensureLoaded();
|
|
139
|
+
let val = process.env[name] ?? "";
|
|
140
|
+
if ([
|
|
141
|
+
true,
|
|
142
|
+
"true",
|
|
143
|
+
"on",
|
|
144
|
+
false,
|
|
145
|
+
"false",
|
|
146
|
+
"off"
|
|
147
|
+
].includes(val)) val = [
|
|
148
|
+
true,
|
|
149
|
+
"true",
|
|
150
|
+
"on"
|
|
151
|
+
].includes(val);
|
|
152
|
+
if (!isNaN(Number(val)) && typeof val !== "boolean" && typeof val !== "undefined" && val !== "") val = Number(val);
|
|
153
|
+
if (val === "") val = void 0;
|
|
154
|
+
if (val === "null") val = null;
|
|
155
|
+
val ??= defaultValue;
|
|
156
|
+
return val;
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
/**
|
|
160
|
+
* Shared environment loader backing {@link env}.
|
|
161
|
+
*/
|
|
162
|
+
const envLoader = new EnvLoader();
|
|
163
|
+
//#endregion
|
|
164
|
+
//#region src/system.ts
|
|
165
|
+
/**
|
|
166
|
+
* Read the .env file
|
|
167
|
+
*
|
|
168
|
+
* @param env
|
|
169
|
+
* @param def
|
|
170
|
+
* @returns
|
|
171
|
+
*/
|
|
172
|
+
const env$1 = (env, defaultValue) => envLoader.get(env, defaultValue);
|
|
173
|
+
/**
|
|
174
|
+
* Build the app url
|
|
175
|
+
*
|
|
176
|
+
* @param link
|
|
177
|
+
* @returns
|
|
178
|
+
*/
|
|
179
|
+
const appUrl = (link) => {
|
|
180
|
+
const port = env$1("PORT", env$1("APP_PORT", "3000"));
|
|
181
|
+
const defaultUrl = `http://localhost:${port}`;
|
|
182
|
+
const appUrl = env$1("APP_URL", `http://localhost:${port}`);
|
|
183
|
+
try {
|
|
184
|
+
const url = new URL(appUrl);
|
|
185
|
+
if (url.port || url.hostname === "localhost") url.port = String(port);
|
|
186
|
+
const baseUrl = url.toString().replace(/\/$/, "");
|
|
187
|
+
if (link) return `${baseUrl}${`/${link.replace(/^\/+/, "")}`}`;
|
|
188
|
+
return baseUrl;
|
|
189
|
+
} catch {
|
|
190
|
+
return link ? `${defaultUrl}/${link.replace(/^\/+/, "")}` : defaultUrl;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
/**
|
|
194
|
+
* Gets the application configuration.
|
|
195
|
+
*
|
|
196
|
+
* @param key The configuration key to retrieve.
|
|
197
|
+
* @param defaultValue The default value to return if the key is not found.
|
|
198
|
+
* @returns The configuration value.
|
|
199
|
+
*/
|
|
200
|
+
const config$1 = (key, defaultValue) => configLoader.resolve(key, defaultValue);
|
|
201
|
+
/**
|
|
202
|
+
* Resolve the unified application key.
|
|
203
|
+
*
|
|
204
|
+
* `APP_KEY` (exposed as `config('app.key')`) is the single secret used for
|
|
205
|
+
* signing and encryption across the framework. Resolution order:
|
|
206
|
+
*
|
|
207
|
+
* 1. An explicit `APP_KEY` environment variable.
|
|
208
|
+
* 2. Any legacy environment variable(s) passed in, for backward compatibility
|
|
209
|
+
* with apps that predate `APP_KEY` (e.g. `JWT_SECRET`,
|
|
210
|
+
* `TWO_FACTOR_ENCRYPTION_KEY`).
|
|
211
|
+
* 3. `config('app.key')` — the value from `src/config/app.ts`, which may itself
|
|
212
|
+
* be a placeholder default when no config is loaded.
|
|
213
|
+
*
|
|
214
|
+
* @param legacy Legacy env var name(s) to fall back to.
|
|
215
|
+
* @returns The resolved key, or `undefined` when none is configured.
|
|
216
|
+
*/
|
|
217
|
+
const appKey = (legacy = []) => {
|
|
218
|
+
const explicit = env$1("APP_KEY");
|
|
219
|
+
if (explicit) return explicit;
|
|
220
|
+
for (const name of Array.isArray(legacy) ? legacy : [legacy]) {
|
|
221
|
+
const value = env$1(name);
|
|
222
|
+
if (value) return value;
|
|
223
|
+
}
|
|
224
|
+
return config$1("app.key") || void 0;
|
|
225
|
+
};
|
|
226
|
+
/**
|
|
227
|
+
* Gets the current Node environment (development or production).
|
|
228
|
+
*
|
|
229
|
+
* @returns
|
|
230
|
+
*/
|
|
231
|
+
const nodeEnv = () => {
|
|
232
|
+
let envValue = env$1("NODE_ENV", "development");
|
|
233
|
+
if (envValue !== "development" && envValue !== "production") envValue = "development";
|
|
234
|
+
return envValue === "production" ? "prod" : "dev";
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Gets the output directory for the application based on the current environment.
|
|
238
|
+
*
|
|
239
|
+
* @param cwd The current working directory (optional, defaults to Arkstack.rootDir()).
|
|
240
|
+
* @returns
|
|
241
|
+
*/
|
|
242
|
+
const outputDir = (cwd) => {
|
|
243
|
+
cwd ??= Arkstack.rootDir();
|
|
244
|
+
const NODE_ENV = nodeEnv();
|
|
245
|
+
const output = {
|
|
246
|
+
dev: env$1("OUTPUT_DIR_DEV", ".arkstack/build"),
|
|
247
|
+
prod: env$1("OUTPUT_DIR", "dist")
|
|
248
|
+
};
|
|
249
|
+
return path.isAbsolute(output[NODE_ENV] ?? output.dev) ? output[NODE_ENV] ?? output.dev : path.join(cwd, output[NODE_ENV] ?? output.dev);
|
|
250
|
+
};
|
|
251
|
+
const SOURCE_DIR = "src";
|
|
252
|
+
const SOURCE_EXTENSIONS = [
|
|
253
|
+
".ts",
|
|
254
|
+
".tsx",
|
|
255
|
+
".mts",
|
|
256
|
+
".cts",
|
|
257
|
+
".js",
|
|
258
|
+
".mjs",
|
|
259
|
+
".cjs"
|
|
260
|
+
];
|
|
261
|
+
const OUTPUT_EXTENSIONS = [
|
|
262
|
+
".js",
|
|
263
|
+
".mjs",
|
|
264
|
+
".cjs",
|
|
265
|
+
".ts"
|
|
266
|
+
];
|
|
267
|
+
/**
|
|
268
|
+
* Strip a trailing known source/compiled extension from a path.
|
|
269
|
+
*
|
|
270
|
+
* @param value
|
|
271
|
+
* @returns
|
|
272
|
+
*/
|
|
273
|
+
const stripKnownExtension = (value) => value.replace(/\.(ts|tsx|mts|cts|js|mjs|cjs)$/i, "");
|
|
274
|
+
/**
|
|
275
|
+
* Build the list of concrete file candidates for a base path. Any existing
|
|
276
|
+
* source/compiled extension is dropped first so the correct runtime extension
|
|
277
|
+
* can be tried (e.g. a `.ts` source maps to a `.js` candidate under `dist`).
|
|
278
|
+
*
|
|
279
|
+
* @param base
|
|
280
|
+
* @param extensions
|
|
281
|
+
* @returns
|
|
282
|
+
*/
|
|
283
|
+
const moduleCandidates = (base, extensions) => {
|
|
284
|
+
const bare = stripKnownExtension(base);
|
|
285
|
+
return extensions.map((ext) => bare + ext);
|
|
286
|
+
};
|
|
287
|
+
/**
|
|
288
|
+
* Map an application source path to its build-output counterpart.
|
|
289
|
+
*
|
|
290
|
+
* Application code is authored under `src/` and compiled into {@link outputDir},
|
|
291
|
+
* which strips the leading `src/` segment and emits JavaScript (e.g.
|
|
292
|
+
* `src/app/models/User.ts` -> `dist/app/models/User.js`). A TypeScript source
|
|
293
|
+
* extension is rewritten to `.js`; paths without one (directories) keep their
|
|
294
|
+
* shape. Absolute or root-relative paths outside the app root are returned
|
|
295
|
+
* unchanged.
|
|
296
|
+
*
|
|
297
|
+
* This is a pure path transform — it does not touch the filesystem. Use
|
|
298
|
+
* {@link resolveRuntimeModule} / {@link resolveRuntimeDir} when you need an
|
|
299
|
+
* existing file/dir for the current environment.
|
|
300
|
+
*
|
|
301
|
+
* @param sourcePath Absolute or root-relative source path.
|
|
302
|
+
*/
|
|
303
|
+
const toOutputPath = (sourcePath) => {
|
|
304
|
+
const root = Arkstack.rootDir();
|
|
305
|
+
const abs = path.isAbsolute(sourcePath) ? sourcePath : path.join(root, sourcePath);
|
|
306
|
+
const rel = path.relative(root, abs);
|
|
307
|
+
if (!rel || rel.startsWith("..")) return abs;
|
|
308
|
+
return path.join(outputDir(), rel.replace(new RegExp(`^${SOURCE_DIR}[\\\\/]`), "")).replace(/\.(ts|tsx|mts|cts)$/i, ".js");
|
|
309
|
+
};
|
|
310
|
+
/**
|
|
311
|
+
* Resolve an application module's source path to a file that can be imported at
|
|
312
|
+
* runtime.
|
|
313
|
+
*
|
|
314
|
+
* In development the TypeScript source is loaded directly (jiti compiles on the
|
|
315
|
+
* fly); in production only the build output ships, so the path is remapped into
|
|
316
|
+
* {@link outputDir} with a compiled extension. The first existing candidate
|
|
317
|
+
* wins — production prefers the build output, development prefers source — so a
|
|
318
|
+
* deploy that ships only `dist` never reaches for `src`.
|
|
319
|
+
*
|
|
320
|
+
* @param sourcePath Absolute or root-relative source path, with or without extension.
|
|
321
|
+
* @returns An existing importable path, or `sourcePath` unchanged when none exists.
|
|
322
|
+
*/
|
|
323
|
+
const resolveRuntimeModule = (sourcePath) => {
|
|
324
|
+
const root = Arkstack.rootDir();
|
|
325
|
+
const abs = path.isAbsolute(sourcePath) ? sourcePath : path.join(root, sourcePath);
|
|
326
|
+
const sourceCandidates = moduleCandidates(abs, SOURCE_EXTENSIONS);
|
|
327
|
+
const outputCandidates = moduleCandidates(toOutputPath(abs), OUTPUT_EXTENSIONS);
|
|
328
|
+
return (nodeEnv() === "prod" ? [...outputCandidates, ...sourceCandidates] : [...sourceCandidates, ...outputCandidates]).find((candidate) => existsSync(candidate)) ?? abs;
|
|
329
|
+
};
|
|
330
|
+
/**
|
|
331
|
+
* Resolve an application source directory to the directory that exists at
|
|
332
|
+
* runtime.
|
|
333
|
+
*
|
|
334
|
+
* The directory counterpart of {@link resolveRuntimeModule}: it maps the source
|
|
335
|
+
* directory into {@link outputDir} (stripping the leading `src/` segment) but
|
|
336
|
+
* appends no file extension. Production prefers the build output, development
|
|
337
|
+
* prefers source, and the absolute source path is returned when neither exists.
|
|
338
|
+
*
|
|
339
|
+
* @param sourcePath Absolute or root-relative source directory.
|
|
340
|
+
* @returns An existing directory path, or the absolute source path when none exists.
|
|
341
|
+
*/
|
|
342
|
+
const resolveRuntimeDir = (sourcePath) => {
|
|
343
|
+
const root = Arkstack.rootDir();
|
|
344
|
+
const abs = path.isAbsolute(sourcePath) ? sourcePath : path.join(root, sourcePath);
|
|
345
|
+
const mapped = toOutputPath(abs);
|
|
346
|
+
return (nodeEnv() === "prod" ? [mapped, abs] : [abs, mapped]).find((candidate) => existsSync(candidate)) ?? abs;
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* Rebuild the application output (tsdown) into {@link outputDir}, wiping it first
|
|
350
|
+
* so no stale emitted modules survive a source change. Standalone — it does NOT
|
|
351
|
+
* boot the app — so the console kernel can call it to self-heal a stale or
|
|
352
|
+
* incomplete build artifact that would otherwise wedge startup. Build-only: it
|
|
353
|
+
* sets `CLI_BUILD` so tsdown emits without starting a watcher/dev server, and
|
|
354
|
+
* inherits the current `NODE_ENV` so it targets the same dir the kernel reads.
|
|
355
|
+
*/
|
|
356
|
+
const rebuildOutput = async () => {
|
|
357
|
+
await rm(outputDir(), {
|
|
358
|
+
recursive: true,
|
|
359
|
+
force: true
|
|
360
|
+
});
|
|
361
|
+
await new Promise((resolveBuild, reject) => {
|
|
362
|
+
const child = spawn(process.platform === "win32" ? "pnpm.cmd" : "pnpm", [
|
|
363
|
+
"exec",
|
|
364
|
+
"tsdown",
|
|
365
|
+
"--log-level",
|
|
366
|
+
"silent"
|
|
367
|
+
], {
|
|
368
|
+
cwd: Arkstack.rootDir(),
|
|
369
|
+
stdio: "inherit",
|
|
370
|
+
env: Object.assign({}, process.env, { CLI_BUILD: "true" })
|
|
371
|
+
});
|
|
372
|
+
child.on("error", reject);
|
|
373
|
+
child.on("exit", (code) => {
|
|
374
|
+
if (code === 0 || code === null) {
|
|
375
|
+
resolveBuild();
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
reject(/* @__PURE__ */ new Error(`tsdown exited with code ${code}`));
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
};
|
|
382
|
+
/**
|
|
383
|
+
*
|
|
384
|
+
* Dynamically imports a file at the given path with full TypeScript support,
|
|
385
|
+
* including `tsconfig.json` path aliases.
|
|
386
|
+
*
|
|
387
|
+
* @param filePath - The path to the file to import.
|
|
388
|
+
* @returns The imported module typed as `T`.
|
|
389
|
+
*
|
|
390
|
+
* @example
|
|
391
|
+
* const config = await importFile<AppConfig>('./config/app.ts')
|
|
392
|
+
*/
|
|
393
|
+
const importFile = async (filePath, userOptions, resolveOptions) => {
|
|
394
|
+
const resolvedPath = resolve(filePath);
|
|
395
|
+
return await createJiti(pathToFileURL(resolvedPath).href, {
|
|
396
|
+
...userOptions,
|
|
397
|
+
interopDefault: false,
|
|
398
|
+
tsconfigPaths: true
|
|
399
|
+
}).import(resolvedPath, resolveOptions);
|
|
400
|
+
};
|
|
401
|
+
/**
|
|
402
|
+
* Picks the command class out of an imported module. Prefers the export named
|
|
403
|
+
* after the file (musket's discovery convention), then a default export, then
|
|
404
|
+
* the first exported constructor it finds.
|
|
405
|
+
*
|
|
406
|
+
* @param mod The imported module namespace.
|
|
407
|
+
* @param basename The file name without extension.
|
|
408
|
+
* @returns The resolved command class, or undefined when none is found.
|
|
409
|
+
*/
|
|
410
|
+
const resolveCommandExport = (mod, basename) => {
|
|
411
|
+
const named = mod[basename];
|
|
412
|
+
if (typeof named === "function") return named;
|
|
413
|
+
if (typeof mod.default === "function") return mod.default;
|
|
414
|
+
return Object.values(mod).find((value) => typeof value === "function");
|
|
415
|
+
};
|
|
416
|
+
/**
|
|
417
|
+
* Discover console command classes from the application's command directory.
|
|
418
|
+
*
|
|
419
|
+
* Commands are loaded straight from TypeScript source through {@link importFile}
|
|
420
|
+
* (jiti), so they are picked up without a build and reflect edits on every run.
|
|
421
|
+
* The built output is only used as a fallback when the source directory is
|
|
422
|
+
* absent — e.g. a production deploy that ships `dist` without `src`.
|
|
423
|
+
*
|
|
424
|
+
* This exists because musket's own glob discovery imports paths with native
|
|
425
|
+
* `import()`, which silently skips `.ts` files — the reason commands previously
|
|
426
|
+
* only appeared after a `build --dev` and never reflected later edits.
|
|
427
|
+
*
|
|
428
|
+
* @param subPath Command directory relative to the app root (src/dist aware).
|
|
429
|
+
* @returns The discovered command classes.
|
|
430
|
+
*/
|
|
431
|
+
const discoverCommands = async (subPath = path.join("app", "console", "commands")) => {
|
|
432
|
+
const root = Arkstack.rootDir();
|
|
433
|
+
const sourceDir = path.join(root, SOURCE_DIR, subPath);
|
|
434
|
+
const outputCommandDir = path.join(outputDir(), subPath);
|
|
435
|
+
const candidateDirs = nodeEnv() === "prod" ? [outputCommandDir, sourceDir] : [sourceDir, outputCommandDir];
|
|
436
|
+
let commandsDir;
|
|
437
|
+
let files = [];
|
|
438
|
+
for (const dir of candidateDirs) try {
|
|
439
|
+
const entries = readdirSync(dir, { withFileTypes: true }).filter((file) => file.isFile() && [
|
|
440
|
+
".ts",
|
|
441
|
+
".js",
|
|
442
|
+
".mjs"
|
|
443
|
+
].includes(path.extname(file.name)));
|
|
444
|
+
if (entries.length > 0) {
|
|
445
|
+
commandsDir = dir;
|
|
446
|
+
files = entries;
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
} catch {}
|
|
450
|
+
if (!commandsDir) return [];
|
|
451
|
+
const commands = [];
|
|
452
|
+
for (const file of files) {
|
|
453
|
+
const basename = path.basename(file.name, path.extname(file.name));
|
|
454
|
+
try {
|
|
455
|
+
const command = resolveCommandExport(await importFile(path.join(commandsDir, file.name)), basename);
|
|
456
|
+
if (command) commands.push(command);
|
|
457
|
+
} catch (error) {
|
|
458
|
+
console.error(`[arkstack] Failed to load command "${file.name}":`, error);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return commands;
|
|
462
|
+
};
|
|
463
|
+
/**
|
|
464
|
+
* Resolves the default export from a module, handling both CJS and ESM interop.
|
|
465
|
+
* In CJS modules, the default export is often the module itself (a function or object),
|
|
466
|
+
* while in ESM the default is nested under the `default` property.
|
|
467
|
+
*
|
|
468
|
+
* @param imp - The imported module
|
|
469
|
+
* @returns The resolved default export
|
|
470
|
+
*/
|
|
471
|
+
const interopDefault = (imp) => {
|
|
472
|
+
return typeof imp === "function" ? imp : imp.default;
|
|
473
|
+
};
|
|
474
|
+
//#endregion
|
|
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 };
|
|
@@ -0,0 +1,188 @@
|
|
|
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
|
+
import { Model } from "arkormx";
|
|
3
|
+
|
|
4
|
+
//#region src/utils/traits.d.ts
|
|
5
|
+
declare const crc32: (str: string) => number;
|
|
6
|
+
type ResolveTraitLike<T extends Trait | TypeFactory<Trait>> = T extends TypeFactory<Trait> ? ExtractFactory<ReturnType<T>> : T extends Trait ? ExtractFactory<T> : unknown;
|
|
7
|
+
type Combine<T extends any[]> = T extends [infer Head, ...infer Tail] ? Head & Combine<Tail> : object;
|
|
8
|
+
type MapClassesToPrototypes<T extends Array<(new () => any) & {
|
|
9
|
+
prototype: any;
|
|
10
|
+
}>> = { [K in keyof T]: T[K]['prototype'] };
|
|
11
|
+
type MapClassesToInstances<T extends Array<(new () => any) & {
|
|
12
|
+
prototype: any;
|
|
13
|
+
}>> = { [K in keyof T]: InstanceType<T[K]> };
|
|
14
|
+
type CombineClasses<T extends Array<(new () => any) & {
|
|
15
|
+
prototype: any;
|
|
16
|
+
}>> = (new () => Combine<MapClassesToInstances<T>>) & {
|
|
17
|
+
prototype: Combine<MapClassesToPrototypes<T>>;
|
|
18
|
+
};
|
|
19
|
+
type ResolveTraitLikeArray<T extends Array<Trait | TypeFactory<Trait>>> = CombineClasses<{ [K in keyof T]: ResolveTraitLike<T[K]> }>;
|
|
20
|
+
/**
|
|
21
|
+
* utility type and function: constructor (function)
|
|
22
|
+
*/
|
|
23
|
+
type Cons<T = any> = new (...args: any[]) => T;
|
|
24
|
+
type ArkormModelCons<T extends Model = Model> = abstract new (...args: any[]) => T;
|
|
25
|
+
type DirectCons<T = any> = Cons<T> | ArkormModelCons<Model>;
|
|
26
|
+
type DirectBase = DirectCons | Model;
|
|
27
|
+
/**
|
|
28
|
+
* utility type and function: constructor factory (function)
|
|
29
|
+
*/
|
|
30
|
+
type ConsFactory<T extends Cons = Cons, B = any> = (base: B) => T;
|
|
31
|
+
/**
|
|
32
|
+
* utility type and function: type factory (function)
|
|
33
|
+
*/
|
|
34
|
+
type TypeFactory<T = any> = () => T;
|
|
35
|
+
/**
|
|
36
|
+
* utility type: map an object type into a bare properties type
|
|
37
|
+
*/
|
|
38
|
+
type Explode<T = any> = { [P in keyof T]: T[P] };
|
|
39
|
+
/**
|
|
40
|
+
* utility type: convert two arrays of types into an array of union types
|
|
41
|
+
*/
|
|
42
|
+
type MixParams<T1 extends any[], T2 extends any[]> = T1 extends [] ? (T2 extends [] ? [] : T2) : (T2 extends [] ? T1 : (T1 extends [infer H1, ...infer R1] ? (T2 extends [infer H2, ...infer R2] ? [H1 & H2, ...MixParams<R1, R2>] : []) : []));
|
|
43
|
+
/**
|
|
44
|
+
* API: trait type
|
|
45
|
+
*/
|
|
46
|
+
type TraitDefTypeT = ConsFactory<Cons>;
|
|
47
|
+
type TraitDefTypeST = (Trait | TypeFactory<Trait>)[] | undefined;
|
|
48
|
+
type Trait<T extends TraitDefTypeT = TraitDefTypeT, ST extends TraitDefTypeST = TraitDefTypeST> = {
|
|
49
|
+
id: number;
|
|
50
|
+
symbol: symbol;
|
|
51
|
+
factory: T;
|
|
52
|
+
superTraits: ST;
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* API: generate trait (regular variant)
|
|
56
|
+
*
|
|
57
|
+
* @param factory
|
|
58
|
+
*/
|
|
59
|
+
declare function trait<T extends ConsFactory<Cons>>(factory: T): Trait<T, undefined>;
|
|
60
|
+
/**
|
|
61
|
+
* API: generate trait (super-trait variant)
|
|
62
|
+
*
|
|
63
|
+
* @param superTraits
|
|
64
|
+
* @param factory
|
|
65
|
+
*/
|
|
66
|
+
declare function trait<const ST extends (Trait | TypeFactory<Trait>)[], T extends ConsFactory<Cons, ResolveTraitLikeArray<ST>>>(superTraits: ST, factory: T): Trait<T, ST>;
|
|
67
|
+
/**
|
|
68
|
+
* utility types: extract factory from a trait
|
|
69
|
+
*/
|
|
70
|
+
type ExtractFactory<T extends Trait> = T extends Trait<ConsFactory<infer C>, TraitDefTypeST> ? C : never;
|
|
71
|
+
/**
|
|
72
|
+
* utility types: extract supertraits from a trait
|
|
73
|
+
*/
|
|
74
|
+
type ExtractSuperTrait<T extends Trait> = T extends Trait<TraitDefTypeT, infer ST extends TraitDefTypeST> ? ST : never;
|
|
75
|
+
/**
|
|
76
|
+
* utility type: derive type constructor: merge two constructors
|
|
77
|
+
*/
|
|
78
|
+
type DeriveTraitsConsConsMerge<A extends Cons, B extends Cons> = A extends (new (...args: infer ArgsA) => infer RetA) ? (B extends (new (...args: infer ArgsB) => infer RetB) ? (new (...args: MixParams<ArgsA, ArgsB>) => RetA & RetB) : never) : never;
|
|
79
|
+
/**
|
|
80
|
+
* utility type: derive type constructor: extract plain constructor
|
|
81
|
+
*/
|
|
82
|
+
type DeriveTraitsConsCons<T extends DirectCons> = new (...args: ConstructorParameters<T>) => InstanceType<T>;
|
|
83
|
+
type DeriveTraitsConsDirectBase<T extends DirectBase> = T extends Model ? new (...args: any[]) => T : T extends DirectCons ? DeriveTraitsConsCons<T> : never;
|
|
84
|
+
/**
|
|
85
|
+
* utility type: derive type constructor: from trait parts
|
|
86
|
+
*/
|
|
87
|
+
type DeriveTraitsConsTraitParts<C extends Cons, ST extends ((Trait | TypeFactory<Trait>)[] | undefined)> = ST extends undefined ? DeriveTraitsConsCons<C> : ST extends [] ? DeriveTraitsConsCons<C> : DeriveTraitsConsConsMerge<DeriveTraitsConsCons<C>, DeriveTraitsConsAll<ST>>;
|
|
88
|
+
/**
|
|
89
|
+
* utility type: derive type constructor: from single trait
|
|
90
|
+
*/
|
|
91
|
+
type DeriveTraitsConsTrait<T extends Trait> = DeriveTraitsConsTraitParts<ExtractFactory<T>, ExtractSuperTrait<T>>;
|
|
92
|
+
/**
|
|
93
|
+
* utility type: derive type constructor: from single trait or trait factory
|
|
94
|
+
*/
|
|
95
|
+
type DeriveTraitsConsOne<T extends (Trait | TypeFactory<Trait>)> = T extends Trait ? DeriveTraitsConsTrait<T> : T extends TypeFactory<Trait> ? DeriveTraitsConsTrait<ReturnType<T>> : never;
|
|
96
|
+
/**
|
|
97
|
+
* utility type: derive type constructor: from one or more traits or trait factories
|
|
98
|
+
*/
|
|
99
|
+
type DeriveTraitsConsAll<T extends (((Trait | TypeFactory<Trait>)[] | [...(Trait | TypeFactory<Trait>)[], DirectBase]) | undefined)> = T extends [infer Only extends DirectBase] ? DeriveTraitsConsDirectBase<Only> : T extends [...infer Others extends (Trait | TypeFactory<Trait>)[], infer Last extends DirectBase] ? (Others extends [] ? DeriveTraitsConsDirectBase<Last> : DeriveTraitsConsConsMerge<DeriveTraitsConsAll<Others>, /* RECURSION */DeriveTraitsConsDirectBase<Last>>) : T extends (Trait | TypeFactory<Trait>)[] ? (T extends [infer First extends (Trait | TypeFactory<Trait>)] ? (DeriveTraitsConsOne<First>) : (T extends [infer First extends (Trait | TypeFactory<Trait>), ...infer Rest extends (Trait | TypeFactory<Trait>)[]] ? (DeriveTraitsConsConsMerge<DeriveTraitsConsOne<First>, DeriveTraitsConsAll<Rest>>) : never)) : never;
|
|
100
|
+
/**
|
|
101
|
+
* utility type: derive type constructor
|
|
102
|
+
*/
|
|
103
|
+
type DeriveTraitsCons<T extends ((Trait | TypeFactory<Trait>)[] | [...(Trait | TypeFactory<Trait>)[], DirectBase])> = DeriveTraitsConsAll<T>;
|
|
104
|
+
/**
|
|
105
|
+
* utility type: derive type statics: merge two objects with statics
|
|
106
|
+
*/
|
|
107
|
+
type DeriveTraitsStatsConsMerge<T1 extends object, T2 extends object> = T1 & T2;
|
|
108
|
+
/**
|
|
109
|
+
* utility type: derive type statics: extract plain statics
|
|
110
|
+
*/
|
|
111
|
+
type DeriveTraitsStatsCons<T extends DirectCons> = Explode<T>;
|
|
112
|
+
type DeriveTraitsStatsDirectBase<T extends DirectBase> = T extends Model ? object : T extends DirectCons ? DeriveTraitsStatsCons<T> : never;
|
|
113
|
+
/**
|
|
114
|
+
* utility type: derive type statics: from trait parts
|
|
115
|
+
*/
|
|
116
|
+
type DeriveTraitsStatsTraitParts<C extends Cons, ST extends ((Trait | TypeFactory<Trait>)[] | undefined)> = ST extends undefined ? DeriveTraitsStatsCons<C> : ST extends [] ? DeriveTraitsStatsCons<C> : DeriveTraitsStatsConsMerge<DeriveTraitsStatsCons<C>, DeriveTraitsStatsAll<ST>>;
|
|
117
|
+
/**
|
|
118
|
+
* utility type: derive type statics: from single trait
|
|
119
|
+
*/
|
|
120
|
+
type DeriveTraitsStatsTrait<T extends Trait> = DeriveTraitsStatsTraitParts<ExtractFactory<T>, ExtractSuperTrait<T>>;
|
|
121
|
+
/**
|
|
122
|
+
* utility type: derive type statics: from single trait or trait factory
|
|
123
|
+
*/
|
|
124
|
+
type DeriveTraitsStatsOne<T extends (Trait | TypeFactory<Trait>)> = T extends Trait ? DeriveTraitsStatsTrait<T> : T extends TypeFactory<Trait> ? DeriveTraitsStatsTrait<ReturnType<T>> : never;
|
|
125
|
+
/**
|
|
126
|
+
* utility type: derive type statics: from one or more traits or trait factories
|
|
127
|
+
*/
|
|
128
|
+
type DeriveTraitsStatsAll<T extends (((Trait | TypeFactory<Trait>)[] | [...(Trait | TypeFactory<Trait>)[], DirectBase]) | undefined)> = T extends [infer Only extends DirectBase] ? DeriveTraitsStatsDirectBase<Only> : T extends [...infer Others extends (Trait | TypeFactory<Trait>)[], infer Last extends DirectBase] ? (Others extends [] ? DeriveTraitsStatsDirectBase<Last> : DeriveTraitsStatsConsMerge<DeriveTraitsStatsAll<Others>, /* RECURSION */DeriveTraitsStatsDirectBase<Last>>) : T extends (Trait | TypeFactory<Trait>)[] ? (T extends [infer First extends (Trait | TypeFactory<Trait>)] ? (DeriveTraitsStatsOne<First>) : (T extends [infer First extends (Trait | TypeFactory<Trait>), ...infer Rest extends (Trait | TypeFactory<Trait>)[]] ? (DeriveTraitsStatsConsMerge<DeriveTraitsStatsOne<First>, DeriveTraitsStatsAll<Rest>>) : never)) : never;
|
|
129
|
+
/**
|
|
130
|
+
* utility type: derive type statics
|
|
131
|
+
*/
|
|
132
|
+
type DeriveTraitsStats<T extends ((Trait | TypeFactory<Trait>)[] | [...(Trait | TypeFactory<Trait>)[], DirectBase])> = DeriveTraitsStatsAll<T>;
|
|
133
|
+
/**
|
|
134
|
+
* utility type: derive type from one or more traits or trait type factories
|
|
135
|
+
*/
|
|
136
|
+
type DeriveTraits<T extends ((Trait | TypeFactory<Trait>)[] | [...(Trait | TypeFactory<Trait>)[], DirectBase])> = DeriveTraitsCons<T> & DeriveTraitsStats<T>;
|
|
137
|
+
type TraitMethodHelpers = {
|
|
138
|
+
getTraitMethods: <T extends TraitMethod = TraitMethod>(name: PropertyKey) => T[];
|
|
139
|
+
callTraitMethods: <T = any>(name: PropertyKey, ...args: any[]) => T[];
|
|
140
|
+
};
|
|
141
|
+
type DeriveTraitsWithMethodHelpers<T extends ((Trait | TypeFactory<Trait>)[] | [...(Trait | TypeFactory<Trait>)[], DirectBase])> = DeriveTraits<T> extends (new (...args: infer Args) => infer Instance) ? (new (...args: Args) => Instance & TraitMethodHelpers) & Omit<DeriveTraits<T>, 'prototype'> & TraitMethodHelpers : never;
|
|
142
|
+
type TraitMethod = (...args: any[]) => any;
|
|
143
|
+
/**
|
|
144
|
+
* Return every trait implementation for a method, bound to the supplied
|
|
145
|
+
* instance or class. Methods are ordered from the base implementation to the
|
|
146
|
+
* currently active trait implementation.
|
|
147
|
+
*
|
|
148
|
+
* @param target
|
|
149
|
+
* @param name
|
|
150
|
+
* @returns
|
|
151
|
+
*/
|
|
152
|
+
declare const getTraitMethods: <T extends TraitMethod = TraitMethod>(target: object | Cons, name: PropertyKey) => T[];
|
|
153
|
+
/**
|
|
154
|
+
* Invoke every trait implementation for a method in registration order.
|
|
155
|
+
*
|
|
156
|
+
* @param target
|
|
157
|
+
* @param name
|
|
158
|
+
* @param args
|
|
159
|
+
* @returns
|
|
160
|
+
*/
|
|
161
|
+
declare const callTraitMethods: <T = any>(target: object | Cons, name: PropertyKey, ...args: any[]) => T[];
|
|
162
|
+
type UsableTraits = ([Trait | TypeFactory<Trait>, ...(Trait | TypeFactory<Trait>)[]] | [...(Trait | TypeFactory<Trait>)[], DirectBase]);
|
|
163
|
+
/**
|
|
164
|
+
* API: derive a class from one or more traits or trait type factories
|
|
165
|
+
*
|
|
166
|
+
* @param traits
|
|
167
|
+
* @returns
|
|
168
|
+
*/
|
|
169
|
+
declare function use<T extends UsableTraits>(withMethodHelpers: true, ...traits: T): DeriveTraitsWithMethodHelpers<T>;
|
|
170
|
+
declare function use<T extends UsableTraits>(...traits: T): DeriveTraits<T>;
|
|
171
|
+
/**
|
|
172
|
+
* internal type: implements trait type
|
|
173
|
+
*/
|
|
174
|
+
type DerivedType<T extends Trait> = InstanceType<ExtractFactory<T>>;
|
|
175
|
+
/**
|
|
176
|
+
* internal type: implements trait type or trait type factory
|
|
177
|
+
*/
|
|
178
|
+
type Derived<T extends (Trait | TypeFactory<Trait> | Cons)> = T extends TypeFactory<Trait> ? DerivedType<ReturnType<T>> : T extends Trait ? DerivedType<T> : T extends Cons ? T : never;
|
|
179
|
+
/**
|
|
180
|
+
* API: type guard for checking whether class instance is derived from a trait
|
|
181
|
+
*
|
|
182
|
+
* @param instance
|
|
183
|
+
* @param trait
|
|
184
|
+
* @returns
|
|
185
|
+
*/
|
|
186
|
+
declare function uses<T extends (Trait | TypeFactory<Trait> | Cons)>(instance: unknown, trait: T): instance is Derived<T>;
|
|
187
|
+
//#endregion
|
|
188
|
+
export { AbstractModelConstructor, Derived, Encryption, Hash, ModelConstructor, ModelRegistry, Trait, abort, abortIf, assertFound, callTraitMethods, crc32, getModel, getTraitMethods, initializeGlobalContext, isClass, perPage, trait, use, uses };
|
|
@@ -0,0 +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-DLifcZWV.js";
|
|
2
|
+
export { Encryption, Hash, abort, abortIf, assertFound, callTraitMethods, crc32, getModel, getTraitMethods, initializeGlobalContext, isClass, perPage, trait, use, uses };
|