@axium/core 0.12.1 → 0.14.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/access.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import * as z from 'zod';
2
2
  import type { User } from './user.js';
3
+ import { type Omit } from 'utilium';
3
4
  export interface AccessControl {
4
5
  itemId: string;
5
6
  userId?: string | null;
package/dist/api.d.ts CHANGED
@@ -1,13 +1,15 @@
1
1
  import type { PublicKeyCredentialCreationOptionsJSON, PublicKeyCredentialRequestOptionsJSON } from '@simplewebauthn/types';
2
+ import type { Omit } from 'utilium';
2
3
  import type z from 'zod';
3
4
  import type { AccessControl, AccessMap } from './access.js';
4
5
  import type { App } from './apps.js';
5
6
  import type { AuditEvent, AuditFilter, Severity } from './audit.js';
6
7
  import type { NewSessionResponse, Session, Verification } from './auth.js';
8
+ import type { PackageVersionInfo } from './packages.js';
7
9
  import type { Passkey, PasskeyAuthenticationResponse, PasskeyChangeable, PasskeyRegistration } from './passkeys.js';
10
+ import type { PluginInternal } from './plugins.js';
8
11
  import type { RequestMethod } from './requests.js';
9
12
  import type { LogoutSessions, User, UserAuthOptions, UserChangeable, UserInternal, UserPublic, UserRegistration } from './user.js';
10
- import type { PluginInternal } from './plugins.js';
11
13
  export interface AdminSummary {
12
14
  users: number;
13
15
  passkeys: number;
@@ -15,7 +17,7 @@ export interface AdminSummary {
15
17
  auditEvents: Record<keyof typeof Severity, number>;
16
18
  configFiles: number;
17
19
  plugins: number;
18
- version: string;
20
+ versions: Record<'core' | 'server' | 'client', PackageVersionInfo>;
19
21
  }
20
22
  /**
21
23
  * Types for all API endpoints
@@ -24,12 +26,11 @@ export interface AdminSummary {
24
26
  export interface $API {
25
27
  metadata: {
26
28
  GET: {
27
- version: string;
29
+ versions: PackageVersionInfo[];
28
30
  routes: Record<string, {
29
31
  params: Record<string, string | null>;
30
32
  methods: string[];
31
33
  }>;
32
- plugins: Record<string, string>;
33
34
  };
34
35
  };
35
36
  apps: {
@@ -115,7 +116,7 @@ export interface $API {
115
116
  };
116
117
  };
117
118
  'admin/plugins': {
118
- GET: PluginInternal[];
119
+ GET: (Omit<PluginInternal, '_hooks' | '_client'> & PackageVersionInfo)[];
119
120
  };
120
121
  'admin/audit/events': {
121
122
  OPTIONS: {
@@ -1,3 +1,3 @@
1
1
  import { type PluginInternal } from '@axium/core/plugins';
2
2
  export declare function pluginText(plugin: PluginInternal): Generator<string>;
3
- export declare function loadPlugin<const T extends 'client' | 'server'>(mode: T, specifier: string, _loadedBy: string, safeMode?: boolean): Promise<PluginInternal | void>;
3
+ export declare function loadPlugin<const T extends 'client' | 'server'>(mode: T, specifier: string, loadedBy: string, safeMode?: boolean): Promise<PluginInternal | void>;
@@ -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
6
  import * as z from 'zod';
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,25 +19,10 @@ 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
- export async function loadPlugin(mode, specifier, _loadedBy, safeMode = false) {
22
+ export async function loadPlugin(mode, specifier, loadedBy, safeMode = false) {
38
23
  try {
39
- const path = _locatePlugin(specifier, _loadedBy);
40
- io.debug(`Loading plugin at ${path} (from ${_loadedBy})`);
24
+ const path = locatePackage(specifier, loadedBy);
25
+ io.debug(`Loading plugin at ${path} (from ${loadedBy})`);
41
26
  let imported;
42
27
  try {
43
28
  imported = JSON.parse(fs.readFileSync(path, 'utf8'));
@@ -49,12 +34,12 @@ export async function loadPlugin(mode, specifier, _loadedBy, safeMode = false) {
49
34
  Object.assign(imported, imported.axium); // support axium field in package.json
50
35
  const plugin = Object.assign(await Plugin.parseAsync(imported).catch(e => {
51
36
  throw e instanceof z.core.$ZodError ? z.prettifyError(e) : e;
52
- }), { path, specifier, _loadedBy, dirname: dirname(path), cli: imported[mode]?.cli, isServer: mode === 'server' });
37
+ }), { path, specifier, loadedBy, dirname: dirname(path), cli: imported[mode]?.cli, isServer: mode === 'server' });
53
38
  if (!plugin[mode])
54
39
  throw `Plugin does not support running ${mode}-side`;
55
40
  if (!safeMode) {
56
- if (plugin[mode].cli)
57
- await import(resolve(plugin.dirname, plugin[mode].cli));
41
+ if (plugin.cli)
42
+ await import(resolve(plugin.dirname, plugin.cli));
58
43
  if (mode == 'client') {
59
44
  if (plugin.client.hooks)
60
45
  Object.assign(plugin, { _client: await import(resolve(plugin.dirname, plugin.client.hooks)) });
@@ -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
@@ -22,13 +22,15 @@ export declare const Plugin: z.ZodObject<{
22
22
  routes: z.ZodOptional<z.ZodString>;
23
23
  db: z.ZodOptional<z.ZodString>;
24
24
  }, z.core.$strip>>;
25
+ /** If set Axium can check the npm registry for updates */
26
+ update_checks: z.ZodOptional<z.ZodNullable<z.ZodBoolean>>;
25
27
  }, z.core.$loose>;
26
28
  export type Plugin = z.infer<typeof Plugin>;
27
29
  export interface PluginInfo {
28
30
  path: string;
29
31
  dirname: string;
30
32
  specifier: string;
31
- _loadedBy: string;
33
+ loadedBy: string;
32
34
  cli?: string;
33
35
  /** @internal */
34
36
  _hooks?: ServerHooks;
package/dist/plugins.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as z from 'zod';
2
- import { zAsyncFunction } from './schemas.js';
3
2
  import { App } from './apps.js';
4
3
  import { debug, warn } from './io.js';
4
+ import { zAsyncFunction } from './schemas.js';
5
5
  const PluginCommon = z.object({
6
6
  /** CLI mixin path */
7
7
  cli: z.string().optional(),
@@ -22,6 +22,8 @@ export const Plugin = z.looseObject({
22
22
  routes: z.string().optional(),
23
23
  db: z.string().optional(),
24
24
  }).optional(),
25
+ /** If set Axium can check the npm registry for updates */
26
+ update_checks: z.boolean().nullish(),
25
27
  });
26
28
  export const plugins = new Map();
27
29
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/core",
3
- "version": "0.12.1",
3
+ "version": "0.14.0",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -35,6 +35,8 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@simplewebauthn/types": "^12.0.0",
38
- "mime": "^4.0.7"
38
+ "@types/semver": "^7.7.1",
39
+ "mime": "^4.0.7",
40
+ "semver": "^7.7.3"
39
41
  }
40
42
  }