@axium/core 0.13.0 → 0.15.0

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/api.d.ts CHANGED
@@ -5,8 +5,9 @@ import type { AccessControl, AccessMap } from './access.js';
5
5
  import type { App } from './apps.js';
6
6
  import type { AuditEvent, AuditFilter, Severity } from './audit.js';
7
7
  import type { NewSessionResponse, Session, Verification } from './auth.js';
8
+ import type { PackageVersionInfo } from './packages.js';
8
9
  import type { Passkey, PasskeyAuthenticationResponse, PasskeyChangeable, PasskeyRegistration } from './passkeys.js';
9
- import type { PluginInternal, PluginVersionInfo } from './plugins.js';
10
+ import type { PluginInternal } from './plugins.js';
10
11
  import type { RequestMethod } from './requests.js';
11
12
  import type { LogoutSessions, User, UserAuthOptions, UserChangeable, UserInternal, UserPublic, UserRegistration } from './user.js';
12
13
  export interface AdminSummary {
@@ -16,7 +17,7 @@ export interface AdminSummary {
16
17
  auditEvents: Record<keyof typeof Severity, number>;
17
18
  configFiles: number;
18
19
  plugins: number;
19
- version: string;
20
+ versions: Record<'core' | 'server' | 'client', PackageVersionInfo>;
20
21
  }
21
22
  /**
22
23
  * Types for all API endpoints
@@ -25,12 +26,11 @@ export interface AdminSummary {
25
26
  export interface $API {
26
27
  metadata: {
27
28
  GET: {
28
- version: string;
29
+ versions: PackageVersionInfo[];
29
30
  routes: Record<string, {
30
31
  params: Record<string, string | null>;
31
32
  methods: string[];
32
33
  }>;
33
- plugins: Record<string, string>;
34
34
  };
35
35
  };
36
36
  apps: {
@@ -116,7 +116,7 @@ export interface $API {
116
116
  };
117
117
  };
118
118
  'admin/plugins': {
119
- GET: (Omit<PluginInternal, '_hooks' | '_client'> & PluginVersionInfo)[];
119
+ GET: (Omit<PluginInternal, '_hooks' | '_client'> & PackageVersionInfo)[];
120
120
  };
121
121
  'admin/audit/events': {
122
122
  OPTIONS: {
package/dist/io.d.ts CHANGED
@@ -35,6 +35,7 @@ export declare function useOutput(output: ConsoleLike): void;
35
35
  * rather than fatally exiting.
36
36
  */
37
37
  export declare function someWarnings(...allowList: [RegExp, string?][]): (error: string | Error) => void;
38
+ export declare function errorText(error: unknown): string;
38
39
  /** @hidden @internal for Logzen */
39
40
  export declare const constructor: {
40
41
  name: string;
package/dist/io.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { $ZodError, prettifyError } from 'zod/v4/core';
1
2
  const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
2
3
  // Shortcut to convert to 2-digit. Mostly used to make the line shorter.
3
4
  const _2 = (v) => v.toString().padStart(2, '0');
@@ -53,5 +54,12 @@ export function someWarnings(...allowList) {
53
54
  throw error;
54
55
  };
55
56
  }
57
+ export function errorText(error) {
58
+ if (error instanceof $ZodError)
59
+ return prettifyError(error);
60
+ if (error instanceof Error)
61
+ return error.message;
62
+ return String(error);
63
+ }
56
64
  /** @hidden @internal for Logzen */
57
65
  export const constructor = { name: 'Console' };
package/dist/node/io.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as z from 'zod';
1
+ import type * as z from 'zod';
2
2
  export * from '../io.js';
3
3
  export declare function setCommandTimeout(value: number): void;
4
4
  /**
@@ -7,8 +7,7 @@ export declare function setCommandTimeout(value: number): void;
7
7
  */
8
8
  export declare function run(message: string, command: string): Promise<string>;
9
9
  /** Yet another convenience function */
10
- export declare function exit(message: string | Error, code?: number): never;
11
- export declare function handleError(e: number | string | Error): never;
10
+ export declare function exit(message: unknown, code?: number): never;
12
11
  /**
13
12
  *
14
13
  * @param defaultValue Returned when the file can't be loaded. If omitted, loading errors will be thrown.
package/dist/node/io.js CHANGED
@@ -51,10 +51,9 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
53
  import { exec } from 'node:child_process';
54
- import { writeFileSync, readFileSync } from 'node:fs';
54
+ import { readFileSync, writeFileSync } from 'node:fs';
55
55
  import { styleText } from 'node:util';
56
56
  import * as io from '../io.js';
57
- import * as z from 'zod';
58
57
  export * from '../io.js';
59
58
  let _currentOperation = null, _progress = null;
60
59
  function handleProgress() {
@@ -217,17 +216,15 @@ export async function run(message, command) {
217
216
  }
218
217
  /** Yet another convenience function */
219
218
  export function exit(message, code = 1) {
219
+ if (typeof message == 'number') {
220
+ code = message;
221
+ message = 'Unknown error!';
222
+ }
220
223
  if (message instanceof Error)
221
224
  message = message.message;
222
225
  io.error(message);
223
226
  process.exit(code);
224
227
  }
225
- export function handleError(e) {
226
- if (typeof e == 'number')
227
- process.exit(e);
228
- else
229
- exit(e);
230
- }
231
228
  /**
232
229
  *
233
230
  * @param defaultValue Returned when the file can't be loaded. If omitted, loading errors will be thrown.
@@ -238,7 +235,7 @@ export function readJSON(path, schema) {
238
235
  return schema.parse(data);
239
236
  }
240
237
  catch (e) {
241
- throw e instanceof z.core.$ZodError ? z.prettifyError(e) : e instanceof Error ? e.message : e;
238
+ throw io.errorText(e);
242
239
  }
243
240
  }
244
241
  export function writeJSON(path, data) {
@@ -1,11 +1,11 @@
1
1
  import * as io from '@axium/core/node/io';
2
2
  import { Plugin, plugins } from '@axium/core/plugins';
3
3
  import * as fs from 'node:fs';
4
- import { dirname, join, resolve } from 'node:path/posix';
5
- import { fileURLToPath } from 'node:url';
4
+ import { dirname, resolve } from 'node:path/posix';
6
5
  import { styleText } from 'node:util';
7
- import * as z from 'zod';
6
+ import { _throw } from 'utilium';
8
7
  import { apps } from '../apps.js';
8
+ import { locatePackage } from '../packages.js';
9
9
  export function* pluginText(plugin) {
10
10
  yield styleText('whiteBright', plugin.name);
11
11
  yield `Version: ${plugin.version}`;
@@ -19,24 +19,9 @@ export function* pluginText(plugin) {
19
19
  yield `Hooks: ${plugin._client ? styleText(['dim', 'bold'], `(${Object.keys(plugin._client).length}) `) + Object.keys(plugin._client).join(', ') : plugin.client.hooks || styleText('dim', '(none)')}`;
20
20
  }
21
21
  }
22
- function _locatePlugin(specifier, loadedBy) {
23
- if (specifier[0] == '/' || ['.', '..'].includes(specifier.split('/')[0])) {
24
- const path = resolve(dirname(loadedBy), specifier);
25
- const stats = fs.statSync(path);
26
- if (stats.isFile())
27
- return path;
28
- if (!stats.isDirectory())
29
- throw new Error('Can not resolve plugin path: ' + path);
30
- return join(path, 'package.json');
31
- }
32
- let packageDir = dirname(fileURLToPath(import.meta.resolve(specifier)));
33
- for (; !fs.existsSync(join(packageDir, 'package.json')); packageDir = dirname(packageDir))
34
- ;
35
- return join(packageDir, 'package.json');
36
- }
37
22
  export async function loadPlugin(mode, specifier, loadedBy, safeMode = false) {
38
23
  try {
39
- const path = _locatePlugin(specifier, loadedBy);
24
+ const path = locatePackage(specifier, loadedBy);
40
25
  io.debug(`Loading plugin at ${path} (from ${loadedBy})`);
41
26
  let imported;
42
27
  try {
@@ -47,9 +32,14 @@ export async function loadPlugin(mode, specifier, loadedBy, safeMode = false) {
47
32
  }
48
33
  if ('axium' in imported)
49
34
  Object.assign(imported, imported.axium); // support axium field in package.json
50
- const plugin = Object.assign(await Plugin.parseAsync(imported).catch(e => {
51
- throw e instanceof z.core.$ZodError ? z.prettifyError(e) : e;
52
- }), { path, specifier, loadedBy, dirname: dirname(path), cli: imported[mode]?.cli, isServer: mode === 'server' });
35
+ const plugin = Object.assign(await Plugin.parseAsync(imported).catch(e => _throw(io.errorText(e))), {
36
+ path,
37
+ specifier,
38
+ loadedBy,
39
+ dirname: dirname(path),
40
+ cli: imported[mode]?.cli,
41
+ isServer: mode === 'server',
42
+ });
53
43
  if (!plugin[mode])
54
44
  throw `Plugin does not support running ${mode}-side`;
55
45
  if (!safeMode) {
@@ -0,0 +1,12 @@
1
+ export declare function locatePackage(specifier: string, loadedBy: string): string;
2
+ export interface PackageVersionInfo {
3
+ name: string;
4
+ version: string;
5
+ latest: string | null;
6
+ }
7
+ export declare function setPackageCacheTTL(seconds: number): void;
8
+ /**
9
+ * @param name Package name
10
+ * @param version The current/installed version
11
+ */
12
+ export declare function getVersionInfo(specifier: string, from?: string): Promise<PackageVersionInfo>;
@@ -0,0 +1,51 @@
1
+ import * as fs from 'node:fs';
2
+ import { dirname, join, resolve } from 'node:path/posix';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { lt as ltVersion } from 'semver';
5
+ import { warn } from './io.js';
6
+ function isRelative(specifier) {
7
+ return specifier[0] == '/' || ['.', '..'].includes(specifier.split('/')[0]);
8
+ }
9
+ export function locatePackage(specifier, loadedBy) {
10
+ if (isRelative(specifier)) {
11
+ const path = resolve(dirname(loadedBy), specifier);
12
+ const stats = fs.statSync(path);
13
+ if (stats.isFile())
14
+ return path;
15
+ if (!stats.isDirectory())
16
+ throw new Error('Can not resolve package path: ' + path);
17
+ return join(path, 'package.json');
18
+ }
19
+ let packageDir = dirname(fileURLToPath(import.meta.resolve(specifier)));
20
+ for (; !fs.existsSync(join(packageDir, 'package.json')); packageDir = dirname(packageDir))
21
+ ;
22
+ return join(packageDir, 'package.json');
23
+ }
24
+ let cacheTTL = 1000 * 60 * 60;
25
+ export function setPackageCacheTTL(seconds) {
26
+ cacheTTL = seconds * 1000;
27
+ }
28
+ const cache = new Map();
29
+ /**
30
+ * @param name Package name
31
+ * @param version The current/installed version
32
+ */
33
+ export async function getVersionInfo(specifier, from = import.meta.filename) {
34
+ const path = locatePackage(specifier, from);
35
+ const { version, name } = JSON.parse(fs.readFileSync(path, 'utf8'));
36
+ const info = { name, version, latest: null };
37
+ if (isRelative(specifier))
38
+ return info;
39
+ const cached = cache.get(specifier);
40
+ const useCache = cached && Date.now() - cached.timestamp < cacheTTL;
41
+ try {
42
+ const pkg = useCache ? cached.data : await fetch('https://registry.npmjs.org/' + specifier).then(res => res.json());
43
+ if (!useCache)
44
+ cache.set(specifier, { timestamp: Date.now(), data: pkg });
45
+ info.latest = pkg['dist-tags']?.latest || Object.keys(pkg.versions).sort((a, b) => (ltVersion(a, b) ? 1 : -1))[0];
46
+ }
47
+ catch (e) {
48
+ warn(`Failed to fetch version info for package ${name}: ${e instanceof Error ? e.message : String(e)}`);
49
+ }
50
+ return info;
51
+ }
package/dist/plugins.d.ts CHANGED
@@ -67,8 +67,4 @@ export interface ClientHooks {
67
67
  run(): void | Promise<void>;
68
68
  }
69
69
  export declare function runIntegrations(): Promise<void>;
70
- export interface PluginVersionInfo {
71
- latest: string | null;
72
- }
73
- export declare function getVersionInfo(plugin: PluginInternal): Promise<PluginVersionInfo>;
74
70
  export {};
package/dist/plugins.js CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as z from 'zod';
2
- import { zAsyncFunction } from './schemas.js';
3
2
  import { App } from './apps.js';
4
- import { debug, info, warn } from './io.js';
5
- import { lt as ltVersion } from 'semver';
3
+ import { debug, warn } from './io.js';
4
+ import { zAsyncFunction } from './schemas.js';
6
5
  const PluginCommon = z.object({
7
6
  /** CLI mixin path */
8
7
  cli: z.string().optional(),
@@ -58,17 +57,3 @@ export async function runIntegrations() {
58
57
  }
59
58
  }
60
59
  }
61
- export async function getVersionInfo(plugin) {
62
- if (!plugin.update_checks)
63
- return { latest: null };
64
- try {
65
- const res = await fetch('https://registry.npmjs.org/' + plugin.name);
66
- const pkg = await res.json();
67
- const latest = pkg['dist-tags']?.latest || Object.keys(pkg.versions).sort((a, b) => (ltVersion(a, b) ? 1 : -1))[0];
68
- return { latest };
69
- }
70
- catch (e) {
71
- warn(`Failed to fetch version info for plugin ${plugin.name}: ${e instanceof Error ? e.message : String(e)}`);
72
- return { latest: null };
73
- }
74
- }
@@ -6,18 +6,17 @@ type ZodPrefComposite = ZodPrefPrimitive | z.ZodNullable<ZodPrefPrimitive> | z.Z
6
6
  /** @internal Used so we can narrow using `type` and get access to type-specific properties (e.g. `ZodNumber.minValue`) */
7
7
  export type ZodPref = ZodPrefComposite | z.ZodObject<Readonly<Record<string, ZodPrefComposite>>>;
8
8
  /**
9
- * Interface for the user preferences schema shape.
10
- * Modify with `declare module ...`.
9
+ * @internal
11
10
  */
12
- export interface PreferenceSchemas {
11
+ export declare let Preferences: z.ZodObject<{
13
12
  debug: z.ZodBoolean;
14
- }
15
- export type PreferenceName = keyof PreferenceSchemas & string;
13
+ }, z.core.$strip>;
16
14
  /**
17
- * @internal
15
+ * Interface for the user preferences schema shape.
16
+ * Modify with `declare module ...`.
18
17
  */
19
- export declare const preferenceSchemas: PreferenceSchemas;
20
- export type Preferences = z.infer<z.ZodObject<Readonly<PreferenceSchemas>>>;
18
+ export interface Preferences extends z.infer<typeof Preferences> {
19
+ }
21
20
  /**
22
21
  * @internal
23
22
  */
@@ -26,14 +25,14 @@ export declare const preferenceDefaults: Preferences;
26
25
  * @internal
27
26
  * @todo Implement proper localization
28
27
  */
29
- export declare const preferenceLabels: Record<PreferenceName, string>;
30
- export declare const preferenceDescriptions: Partial<Record<PreferenceName, string>>;
31
- export interface PreferenceInit<T extends PreferenceName = PreferenceName> {
28
+ export declare const preferenceLabels: Record<keyof Preferences, string>;
29
+ export declare const preferenceDescriptions: Partial<Record<keyof Preferences, string>>;
30
+ export interface PreferenceInit<T extends keyof Preferences = keyof Preferences, S extends ZodPref = ZodPref> {
32
31
  name: T;
33
- schema: PreferenceSchemas[T] & ZodPref;
34
- initial: z.infer<PreferenceSchemas[T] & ZodPref>;
32
+ schema: S;
33
+ initial: z.infer<S> & Preferences[T];
35
34
  label: string;
36
35
  descriptions?: string;
37
36
  }
38
- export declare function addPreference<T extends PreferenceName = PreferenceName>(init: PreferenceInit<T>): void;
37
+ export declare function addPreference<T extends keyof Preferences = keyof Preferences>(init: PreferenceInit<T>): void;
39
38
  export {};
@@ -2,9 +2,9 @@ import * as z from 'zod';
2
2
  /**
3
3
  * @internal
4
4
  */
5
- export const preferenceSchemas = {
5
+ export let Preferences = z.object({
6
6
  debug: z.boolean(),
7
- };
7
+ });
8
8
  /**
9
9
  * @internal
10
10
  */
@@ -20,7 +20,7 @@ export const preferenceLabels = {
20
20
  };
21
21
  export const preferenceDescriptions = {};
22
22
  export function addPreference(init) {
23
- preferenceSchemas[init.name] = init.schema;
23
+ Preferences = z.object({ ...Preferences.shape, [init.name]: init.schema });
24
24
  preferenceDefaults[init.name] = init.initial;
25
25
  preferenceLabels[init.name] = init.label;
26
26
  preferenceDescriptions[init.name] = init.descriptions;
package/dist/user.d.ts CHANGED
@@ -1,18 +1,18 @@
1
1
  import * as z from 'zod';
2
- import type { Preferences } from './preferences.js';
3
2
  export declare const User: z.ZodObject<{
4
3
  id: z.ZodUUID;
5
4
  name: z.ZodString;
6
5
  email: z.ZodEmail;
7
6
  emailVerified: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
8
7
  image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
9
- preferences: z.ZodRecord<z.ZodString, z.ZodAny>;
8
+ preferences: z.ZodObject<{
9
+ debug: z.ZodBoolean;
10
+ }, z.core.$strip>;
10
11
  roles: z.ZodArray<z.ZodString>;
11
12
  registeredAt: z.ZodCoercedDate<unknown>;
12
13
  isAdmin: z.ZodBoolean;
13
14
  }, z.core.$strip>;
14
15
  export interface User extends z.infer<typeof User> {
15
- preferences: Preferences;
16
16
  }
17
17
  export interface UserInternal extends User {
18
18
  isSuspended: boolean;
@@ -26,7 +26,9 @@ export declare const UserPublic: z.ZodObject<{
26
26
  email: z.ZodOptional<z.ZodEmail>;
27
27
  emailVerified: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodDate>>>;
28
28
  image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
29
- preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
29
+ preferences: z.ZodOptional<z.ZodObject<{
30
+ debug: z.ZodBoolean;
31
+ }, z.core.$strip>>;
30
32
  roles: z.ZodArray<z.ZodString>;
31
33
  registeredAt: z.ZodCoercedDate<unknown>;
32
34
  isAdmin: z.ZodOptional<z.ZodBoolean>;
@@ -37,11 +39,12 @@ export declare const userProtectedFields: ["email", "emailVerified", "preference
37
39
  export declare const UserChangeable: z.ZodObject<{
38
40
  email: z.ZodOptional<z.ZodEmail>;
39
41
  name: z.ZodOptional<z.ZodString>;
42
+ preferences: z.ZodOptional<z.ZodObject<{
43
+ debug: z.ZodBoolean;
44
+ }, z.core.$strip>>;
40
45
  image: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodURL>>>;
41
- preferences: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
42
46
  }, z.core.$strip>;
43
47
  export interface UserChangeable extends z.infer<typeof UserChangeable> {
44
- preferences?: Preferences;
45
48
  }
46
49
  export declare function getUserImage(user: Partial<User>): string;
47
50
  export declare const UserRegistration: z.ZodObject<{
package/dist/user.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import * as z from 'zod';
2
+ import { Preferences } from './preferences.js';
2
3
  import { PasskeyRegistration } from './passkeys.js';
3
4
  import { colorHash } from './color.js';
4
5
  export const User = z.object({
@@ -7,7 +8,7 @@ export const User = z.object({
7
8
  email: z.email(),
8
9
  emailVerified: z.date().nullish(),
9
10
  image: z.url().nullish(),
10
- preferences: z.record(z.string(), z.any()),
11
+ preferences: Preferences,
11
12
  roles: z.array(z.string()),
12
13
  registeredAt: z.coerce.date(),
13
14
  isAdmin: z.boolean(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/core",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "funding": {
6
6
  "type": "individual",