@axium/core 0.8.0 → 0.9.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
@@ -8,6 +8,15 @@ import type { Passkey, PasskeyAuthenticationResponse, PasskeyChangeable, Passkey
8
8
  import type { RequestMethod } from './requests.js';
9
9
  import type { LogoutSessions, User, UserAuthOptions, UserChangeable, UserInternal, UserPublic, UserRegistration } from './user.js';
10
10
  import type { PluginInternal } from './plugins.js';
11
+ export interface AdminSummary {
12
+ users: number;
13
+ passkeys: number;
14
+ sessions: number;
15
+ auditEvents: Record<keyof typeof Severity, number>;
16
+ configFiles: number;
17
+ plugins: number;
18
+ version: string;
19
+ }
11
20
  /**
12
21
  * Types for all API endpoints
13
22
  * @internal
@@ -91,15 +100,7 @@ export interface $API {
91
100
  }, AccessControl];
92
101
  };
93
102
  'admin/summary': {
94
- GET: {
95
- users: number;
96
- passkeys: number;
97
- sessions: number;
98
- auditEvents: Record<keyof typeof Severity, number>;
99
- configFiles: number;
100
- plugins: number;
101
- version: string;
102
- };
103
+ GET: AdminSummary;
103
104
  };
104
105
  'admin/users/all': {
105
106
  GET: UserInternal[];
@@ -119,6 +120,11 @@ export interface $API {
119
120
  GET: PluginInternal[];
120
121
  };
121
122
  'admin/audit/events': {
123
+ OPTIONS: {
124
+ name: string[];
125
+ source: string[];
126
+ tags: string[];
127
+ } | false;
122
128
  GET: [filter: z.input<typeof AuditFilter>, result: AuditEvent[]];
123
129
  };
124
130
  'admin/audit/:eventId': {
@@ -128,5 +134,6 @@ export interface $API {
128
134
  export type Endpoint = keyof $API;
129
135
  export type APIFunction<Method extends RequestMethod, E extends Endpoint> = Method extends keyof $API[E] ? $API[E][Method] extends [infer Body, infer Result] ? (body: Body) => Promise<Result> : () => $API[E][Method] : unknown;
130
136
  export type RequestBody<Method extends RequestMethod, E extends Endpoint> = Method extends keyof $API[E] ? $API[E][Method] extends [infer Body, unknown] ? Body : any : unknown;
131
- export type Result<Method extends RequestMethod, E extends Endpoint> = Promise<Method extends keyof $API[E] ? ($API[E][Method] extends [unknown, infer R] ? R : $API[E][Method]) : unknown>;
137
+ export type Result<Method extends RequestMethod & keyof $API[E], E extends Endpoint> = $API[E][Method] extends [unknown, infer R] ? R : $API[E][Method];
138
+ export type AsyncResult<Method extends RequestMethod & keyof $API[E], E extends Endpoint> = Promise<Result<Method, E>>;
132
139
  export type APIParameters<S extends string> = S extends `${string}/:${infer Right}` ? [string, ...APIParameters<Right>] : [];
package/dist/apps.d.ts CHANGED
@@ -7,3 +7,4 @@ export declare const App: z.ZodObject<{
7
7
  }, z.core.$strip>;
8
8
  export interface App extends z.infer<typeof App> {
9
9
  }
10
+ export declare const apps: Map<string, App>;
package/dist/apps.js CHANGED
@@ -5,3 +5,4 @@ export const App = z.object({
5
5
  image: z.string().optional(),
6
6
  icon: z.string().optional(),
7
7
  });
8
+ export const apps = new Map();
package/dist/audit.d.ts CHANGED
@@ -34,7 +34,7 @@ export declare const AuditFilter: z.ZodObject<{
34
34
  since: z.ZodOptional<z.ZodCoercedDate<unknown>>;
35
35
  until: z.ZodOptional<z.ZodCoercedDate<unknown>>;
36
36
  user: z.ZodOptional<z.ZodNullable<z.ZodUnion<readonly [z.ZodUUID, z.ZodPipe<z.ZodLiteral<"null">, z.ZodTransform<null, "null">>]>>>;
37
- severity: z.ZodOptional<z.ZodPipe<z.ZodLiteral<"error" | "debug" | "emergency" | "alert" | "critical" | "warning" | "notice" | "info">, z.ZodTransform<Severity, "error" | "debug" | "emergency" | "alert" | "critical" | "warning" | "notice" | "info">>>;
37
+ severity: z.ZodOptional<z.ZodPipe<z.ZodLiteral<"debug" | "error" | "emergency" | "alert" | "critical" | "warning" | "notice" | "info">, z.ZodTransform<Severity, "debug" | "error" | "emergency" | "alert" | "critical" | "warning" | "notice" | "info">>>;
38
38
  source: z.ZodOptional<z.ZodString>;
39
39
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
40
40
  event: z.ZodOptional<z.ZodString>;
package/dist/auth.d.ts CHANGED
@@ -1,9 +1,12 @@
1
- export interface Session {
2
- id: string;
3
- userId: string;
4
- expires: Date;
5
- created: Date;
6
- elevated: boolean;
1
+ import * as z from 'zod';
2
+ export declare const Session: z.ZodObject<{
3
+ id: z.ZodUUID;
4
+ userId: z.ZodUUID;
5
+ expires: z.ZodCoercedDate<unknown>;
6
+ created: z.ZodCoercedDate<unknown>;
7
+ elevated: z.ZodBoolean;
8
+ }, z.core.$strip>;
9
+ export interface Session extends z.infer<typeof Session> {
7
10
  }
8
11
  export interface Verification {
9
12
  userId: string;
package/dist/auth.js CHANGED
@@ -1 +1,8 @@
1
- export {};
1
+ import * as z from 'zod';
2
+ export const Session = z.object({
3
+ id: z.uuid(),
4
+ userId: z.uuid(),
5
+ expires: z.coerce.date(),
6
+ created: z.coerce.date(),
7
+ elevated: z.boolean(),
8
+ });
package/dist/io.d.ts ADDED
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Get a human-readable string for a date that also fits into CLIs well (fixed-width)
3
+ */
4
+ export declare function prettyDate(date: Date): string;
5
+ export declare let _debugOutput: boolean;
6
+ /**
7
+ * Enable or disable debug output.
8
+ */
9
+ export declare function _setDebugOutput(enabled: boolean): void;
10
+ export declare let start: (message: string) => void;
11
+ export declare let progress: (value: number, max: number, message?: any) => void;
12
+ export declare let done: () => void;
13
+ export interface ProgressIO {
14
+ start(message: string): void;
15
+ progress(value: number, max: number, message?: any): void;
16
+ done(): void;
17
+ }
18
+ export declare function useProgressIO(io: ProgressIO): void;
19
+ export declare let debug: (...args: any[]) => void;
20
+ export declare let log: (...args: any[]) => void;
21
+ export declare let info: (...args: any[]) => void;
22
+ export declare let warn: (...args: any[]) => void;
23
+ export declare let error: (...args: any[]) => void;
24
+ export interface ConsoleLike {
25
+ debug(...args: any[]): void;
26
+ log(...args: any[]): void;
27
+ info(...args: any[]): void;
28
+ warn(...args: any[]): void;
29
+ error(...args: any[]): void;
30
+ }
31
+ export declare function useOutput(output: ConsoleLike): void;
32
+ /**
33
+ * This is a factory for handling errors when performing operations.
34
+ * The handler will allow the parent scope to continue if certain errors occur,
35
+ * rather than fatally exiting.
36
+ */
37
+ export declare function someWarnings(...allowList: [RegExp, string?][]): (error: string | Error) => void;
38
+ /** @hidden @internal for Logzen */
39
+ export declare const constructor: {
40
+ name: string;
41
+ };
package/dist/io.js ADDED
@@ -0,0 +1,57 @@
1
+ const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
2
+ // Shortcut to convert to 2-digit. Mostly used to make the line shorter.
3
+ const _2 = (v) => v.toString().padStart(2, '0');
4
+ /**
5
+ * Get a human-readable string for a date that also fits into CLIs well (fixed-width)
6
+ */
7
+ export function prettyDate(date) {
8
+ return `${date.getFullYear()} ${months[date.getMonth()]} ${_2(date.getDate())} ${_2(date.getHours())}:${_2(date.getMinutes())}:${_2(date.getSeconds())}.${date.getMilliseconds().toString().padStart(3, '0')}`;
9
+ }
10
+ export let _debugOutput = false;
11
+ /**
12
+ * Enable or disable debug output.
13
+ */
14
+ export function _setDebugOutput(enabled) {
15
+ _debugOutput = enabled;
16
+ }
17
+ // I/O for "progressive" actions
18
+ export let start;
19
+ export let progress;
20
+ export let done;
21
+ export function useProgressIO(io) {
22
+ start = io.start.bind(io);
23
+ progress = io.progress.bind(io);
24
+ done = io.done.bind(io);
25
+ }
26
+ // User-facing messaging
27
+ export let debug;
28
+ export let log;
29
+ export let info;
30
+ export let warn;
31
+ export let error;
32
+ export function useOutput(output) {
33
+ debug = output.debug.bind(output);
34
+ log = output.log.bind(output);
35
+ info = output.info.bind(output);
36
+ warn = output.warn.bind(output);
37
+ error = output.error.bind(output);
38
+ }
39
+ /**
40
+ * This is a factory for handling errors when performing operations.
41
+ * The handler will allow the parent scope to continue if certain errors occur,
42
+ * rather than fatally exiting.
43
+ */
44
+ export function someWarnings(...allowList) {
45
+ return (error) => {
46
+ error = typeof error == 'object' && 'message' in error ? error.message : error;
47
+ for (const [pattern, message = error] of allowList) {
48
+ if (!pattern.test(error))
49
+ continue;
50
+ warn(message);
51
+ return;
52
+ }
53
+ throw error;
54
+ };
55
+ }
56
+ /** @hidden @internal for Logzen */
57
+ export const constructor = { name: 'Console' };
@@ -0,0 +1 @@
1
+ export declare function outputDaemonStatus(name: string): void;
@@ -0,0 +1,21 @@
1
+ import { styleText } from 'node:util';
2
+ import { spawnSync } from 'node:child_process';
3
+ export function outputDaemonStatus(name) {
4
+ process.stdout.write(styleText('whiteBright', 'Daemon: '));
5
+ const daemonIs = (sub) => spawnSync('systemctl', ['is-' + sub, name], { stdio: 'pipe', encoding: 'utf8' });
6
+ const { status: dNotActive, stdout: dStatus } = daemonIs('active');
7
+ const { status: dNotFailed } = daemonIs('failed');
8
+ const { stdout: dEnabled } = daemonIs('enabled');
9
+ if (dEnabled.trim() == 'not-found')
10
+ console.log(styleText('dim', 'not found'));
11
+ else {
12
+ process.stdout.write(dEnabled.trim() + ', ');
13
+ const status = dStatus.trim();
14
+ if (!dNotFailed)
15
+ console.log(styleText('red', status));
16
+ else if (!dNotActive)
17
+ console.log(styleText('green', status));
18
+ else
19
+ console.log(styleText('yellow', status));
20
+ }
21
+ }
@@ -0,0 +1,3 @@
1
+ export * from './daemon.js';
2
+ export * as io from './io.js';
3
+ export * from './plugins.js';
@@ -0,0 +1,3 @@
1
+ export * from './daemon.js';
2
+ export * as io from './io.js';
3
+ export * from './plugins.js';
@@ -0,0 +1,11 @@
1
+ export * from '../io.js';
2
+ export declare function setCommandTimeout(value: number): void;
3
+ /**
4
+ * Run a system command with the fancy "Example... done."
5
+ * @internal
6
+ */
7
+ export declare function run(message: string, command: string): Promise<string>;
8
+ /** Yet another convenience function */
9
+ export declare function exit(message: string | Error, code?: number): never;
10
+ export declare function handleError(e: number | string | Error): void;
11
+ export declare function writeJSON(path: string, data: any): void;
@@ -0,0 +1,84 @@
1
+ import { exec } from 'node:child_process';
2
+ import { writeFileSync } from 'node:fs';
3
+ import { styleText } from 'node:util';
4
+ import { _debugOutput, done, error, start, useOutput, useProgressIO } from '../io.js';
5
+ export * from '../io.js';
6
+ useProgressIO({
7
+ start(message) {
8
+ process.stdout.write(message + '... \x1b[s');
9
+ },
10
+ /** @todo implement additional messaging */
11
+ progress(value, max, message) {
12
+ process.stdout.write(`\x1b[u\x1b[K${value.toString().padStart(max.toString().length)}/${max}`);
13
+ if (value >= max)
14
+ console.log();
15
+ },
16
+ done() {
17
+ console.log('done.');
18
+ },
19
+ });
20
+ useOutput({
21
+ error(message) {
22
+ console.error(message.startsWith('\x1b') ? message : styleText('red', message));
23
+ },
24
+ warn(message) {
25
+ console.warn(message.startsWith('\x1b') ? message : styleText('yellow', message));
26
+ },
27
+ info(message) {
28
+ console.info(message.startsWith('\x1b') ? message : styleText('blue', message));
29
+ },
30
+ log(message) {
31
+ console.log(message);
32
+ },
33
+ debug(message) {
34
+ _debugOutput && console.debug(message.startsWith('\x1b') ? message : styleText('gray', message));
35
+ },
36
+ });
37
+ let timeout = 1000;
38
+ export function setCommandTimeout(value) {
39
+ timeout = value;
40
+ }
41
+ /**
42
+ * Run a system command with the fancy "Example... done."
43
+ * @internal
44
+ */
45
+ export async function run(message, command) {
46
+ let stderr;
47
+ try {
48
+ start(message);
49
+ const { promise, resolve, reject } = Promise.withResolvers();
50
+ exec(command, { timeout }, (err, stdout, _stderr) => {
51
+ stderr = _stderr.startsWith('ERROR:') ? _stderr.slice(6).trim() : _stderr;
52
+ if (err)
53
+ reject('[command]');
54
+ else
55
+ resolve(stdout);
56
+ });
57
+ const value = await promise;
58
+ done();
59
+ return value;
60
+ }
61
+ catch (error) {
62
+ throw error == '[command]'
63
+ ? stderr?.slice(0, 100) || 'failed.'
64
+ : typeof error == 'object' && 'message' in error
65
+ ? error.message
66
+ : error;
67
+ }
68
+ }
69
+ /** Yet another convenience function */
70
+ export function exit(message, code = 1) {
71
+ if (message instanceof Error)
72
+ message = message.message;
73
+ error(message);
74
+ process.exit(code);
75
+ }
76
+ export function handleError(e) {
77
+ if (typeof e == 'number')
78
+ process.exit(e);
79
+ else
80
+ exit(e);
81
+ }
82
+ export function writeJSON(path, data) {
83
+ writeFileSync(path, JSON.stringify(data, null, 4).replaceAll(/^( {4})+/g, match => '\t'.repeat(match.length / 4)), 'utf-8');
84
+ }
@@ -0,0 +1,3 @@
1
+ import { type PluginInternal } from '@axium/core/plugins';
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<void>;
@@ -0,0 +1,77 @@
1
+ import * as io from '@axium/core/node/io';
2
+ import { Plugin, plugins } from '@axium/core/plugins';
3
+ import * as fs from 'node:fs';
4
+ import { dirname, join, resolve } from 'node:path/posix';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { styleText } from 'node:util';
7
+ import * as z from 'zod';
8
+ import { apps } from '../apps.js';
9
+ export function* pluginText(plugin) {
10
+ yield styleText('whiteBright', plugin.name);
11
+ yield `Version: ${plugin.version}`;
12
+ yield `Description: ${plugin.description ?? styleText('dim', '(none)')}`;
13
+ yield `CLI Integration: ${plugin.cli ? 'Yes' : 'No'}`;
14
+ if (plugin.isServer) {
15
+ yield `Hooks: ${plugin._hooks ? styleText(['dim', 'bold'], `(${Object.keys(plugin._hooks).length}) `) + Object.keys(plugin._hooks).join(', ') : plugin.server.hooks || styleText('dim', '(none)')}`;
16
+ yield `HTTP Handler: ${plugin.server.http_handler ?? styleText('dim', '(none)')}`;
17
+ }
18
+ else {
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
+ }
21
+ }
22
+ function _locatePlugin(specifier, _loadedBy) {
23
+ if (specifier[0] == '/' || specifier.startsWith('./') || specifier.startsWith('../')) {
24
+ return resolve(dirname(_loadedBy), specifier);
25
+ }
26
+ let packageDir = dirname(fileURLToPath(import.meta.resolve(specifier)));
27
+ for (; !fs.existsSync(join(packageDir, 'package.json')); packageDir = dirname(packageDir))
28
+ ;
29
+ return join(packageDir, 'package.json');
30
+ }
31
+ export async function loadPlugin(mode, specifier, _loadedBy, safeMode = false) {
32
+ try {
33
+ const path = _locatePlugin(specifier, _loadedBy);
34
+ let imported;
35
+ try {
36
+ imported = JSON.parse(fs.readFileSync(path, 'utf8'));
37
+ }
38
+ catch {
39
+ throw new Error('Invalid or missing metadata for ' + specifier);
40
+ }
41
+ if ('axium' in imported)
42
+ Object.assign(imported, imported.axium); // support axium field in package.json
43
+ const plugin = Object.assign(await Plugin.parseAsync(imported).catch(e => {
44
+ throw e instanceof z.core.$ZodError ? z.prettifyError(e) : e;
45
+ }), { path, specifier, _loadedBy, dirname: dirname(path), cli: imported[mode]?.cli, isServer: mode === 'server' });
46
+ if (!plugin[mode])
47
+ throw `Plugin does not support running ${mode}-side`;
48
+ if (!safeMode) {
49
+ if (plugin.cli)
50
+ await import(resolve(plugin.dirname, plugin.cli));
51
+ if (mode == 'client') {
52
+ if (plugin.client.hooks)
53
+ Object.assign(plugin, { _client: await import(resolve(plugin.dirname, plugin.client.hooks)) });
54
+ }
55
+ if (mode == 'server') {
56
+ if (plugin.server.hooks)
57
+ Object.assign(plugin, { _hooks: await import(resolve(plugin.dirname, plugin.server.hooks)) });
58
+ }
59
+ }
60
+ Object.freeze(plugin);
61
+ if (plugins.has(plugin.name))
62
+ throw 'Plugin already loaded';
63
+ if (plugin.name.startsWith('#') || plugin.name.includes(' ')) {
64
+ throw 'Invalid plugin name. Plugin names can not start with a hash or contain spaces.';
65
+ }
66
+ for (const app of plugin.apps ?? []) {
67
+ if (apps.has(app.id))
68
+ throw new ReferenceError(`App with ID "${app.id}" already exists.`);
69
+ apps.set(app.id, app);
70
+ }
71
+ plugins.set(plugin.name, plugin);
72
+ io.debug(`Loaded plugin: ${plugin.name} ${plugin.version}`);
73
+ }
74
+ catch (e) {
75
+ io.warn(`Failed to load plugin from ${specifier}: ${e ? (e instanceof Error ? e.message : e.toString()) : e}`);
76
+ }
77
+ }
@@ -11,12 +11,12 @@ export declare const PasskeyRegistration: z.ZodObject<{
11
11
  authenticatorData: z.ZodOptional<z.ZodString>;
12
12
  transports: z.ZodOptional<z.ZodArray<z.ZodEnum<{
13
13
  ble: "ble";
14
+ cable: "cable";
14
15
  hybrid: "hybrid";
15
16
  internal: "internal";
16
17
  nfc: "nfc";
17
- usb: "usb";
18
- cable: "cable";
19
18
  "smart-card": "smart-card";
19
+ usb: "usb";
20
20
  }>>>;
21
21
  publicKeyAlgorithm: z.ZodOptional<z.ZodNumber>;
22
22
  publicKey: z.ZodOptional<z.ZodString>;
package/dist/plugins.d.ts CHANGED
@@ -3,17 +3,22 @@ export declare const Plugin: z.ZodObject<{
3
3
  name: z.ZodString;
4
4
  version: z.ZodString;
5
5
  description: z.ZodOptional<z.ZodString>;
6
- /** The path to the hooks script */
7
- hooks: z.ZodOptional<z.ZodString>;
8
- /** The path to the HTTP handler */
9
- http_handler: z.ZodOptional<z.ZodString>;
10
6
  apps: z.ZodOptional<z.ZodArray<z.ZodObject<{
11
7
  id: z.ZodString;
12
8
  name: z.ZodOptional<z.ZodString>;
13
9
  image: z.ZodOptional<z.ZodString>;
14
10
  icon: z.ZodOptional<z.ZodString>;
15
11
  }, z.core.$strip>>>;
16
- routes: z.ZodOptional<z.ZodString>;
12
+ client: z.ZodOptional<z.ZodObject<{
13
+ cli: z.ZodOptional<z.ZodString>;
14
+ hooks: z.ZodOptional<z.ZodString>;
15
+ }, z.core.$strip>>;
16
+ server: z.ZodOptional<z.ZodObject<{
17
+ hooks: z.ZodOptional<z.ZodString>;
18
+ http_handler: z.ZodOptional<z.ZodString>;
19
+ routes: z.ZodOptional<z.ZodString>;
20
+ cli: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strip>>;
17
22
  }, z.core.$loose>;
18
23
  export type Plugin = z.infer<typeof Plugin>;
19
24
  export interface PluginInternal extends Plugin {
@@ -21,9 +26,19 @@ export interface PluginInternal extends Plugin {
21
26
  readonly dirname: string;
22
27
  readonly specifier: string;
23
28
  readonly _loadedBy: string;
24
- readonly _hooks?: Hooks;
29
+ readonly cli?: string;
30
+ /** @internal */
31
+ readonly _hooks?: ServerHooks;
32
+ /** @internal */
33
+ readonly _client?: ClientHooks;
34
+ readonly isServer: boolean;
25
35
  }
26
- export declare const PluginHooks: z.ZodObject<{
36
+ export declare const plugins: Map<string, PluginInternal>;
37
+ /**
38
+ * @internal
39
+ */
40
+ export declare function _findPlugin(search: string): PluginInternal;
41
+ export declare const PluginServerHooks: z.ZodObject<{
27
42
  statusText: z.ZodOptional<z.ZodCustom<z.core.$InferInnerFunctionTypeAsync<z.ZodTuple<readonly [], null>, z.ZodString>, z.core.$InferInnerFunctionTypeAsync<z.ZodTuple<readonly [], null>, z.ZodString>>>;
28
43
  db_init: z.ZodOptional<z.ZodCustom<(...args: any[]) => any, (...args: any[]) => any>>;
29
44
  remove: z.ZodOptional<z.ZodCustom<(...args: any[]) => any, (...args: any[]) => any>>;
@@ -35,7 +50,7 @@ interface _InitOptions {
35
50
  skip: boolean;
36
51
  check: boolean;
37
52
  }
38
- export interface Hooks {
53
+ export interface ServerHooks {
39
54
  statusText?(): string | Promise<string>;
40
55
  db_init?: (opt: _InitOptions) => void | Promise<void>;
41
56
  remove?: (opt: {
@@ -46,4 +61,7 @@ export interface Hooks {
46
61
  }) => void | Promise<void>;
47
62
  clean?: (opt: Partial<_InitOptions>) => void | Promise<void>;
48
63
  }
64
+ export interface ClientHooks {
65
+ run(): void | Promise<void>;
66
+ }
49
67
  export {};
package/dist/plugins.js CHANGED
@@ -5,15 +5,39 @@ export const Plugin = z.looseObject({
5
5
  name: z.string(),
6
6
  version: z.string(),
7
7
  description: z.string().optional(),
8
- /** The path to the hooks script */
9
- hooks: z.string().optional(),
10
- /** The path to the HTTP handler */
11
- http_handler: z.string().optional(),
12
8
  apps: z.array(App).optional(),
13
- routes: z.string().optional(),
9
+ client: z
10
+ .object({
11
+ /** CLI mixin path */
12
+ cli: z.string().optional(),
13
+ /** The path to the hooks script */
14
+ hooks: z.string().optional(),
15
+ })
16
+ .optional(),
17
+ server: z
18
+ .object({
19
+ /** The path to the hooks script */
20
+ hooks: z.string().optional(),
21
+ http_handler: z.string().optional(),
22
+ /** The path to the HTTP handler used by the server */
23
+ routes: z.string().optional(),
24
+ /** CLI mixin path */
25
+ cli: z.string().optional(),
26
+ })
27
+ .optional(),
14
28
  });
29
+ export const plugins = new Map();
30
+ /**
31
+ * @internal
32
+ */
33
+ export function _findPlugin(search) {
34
+ const plugin = plugins.get(search) ?? plugins.values().find(p => p.specifier.toLowerCase() == search.toLowerCase());
35
+ if (!plugin)
36
+ throw `Can't find a plugin matching "${search}"`;
37
+ return plugin;
38
+ }
15
39
  const fn = z.custom(data => typeof data === 'function');
16
- export const PluginHooks = z.object({
40
+ export const PluginServerHooks = z.object({
17
41
  statusText: zAsyncFunction(z.function({ input: [], output: z.string() })).optional(),
18
42
  db_init: fn.optional(),
19
43
  remove: fn.optional(),
package/dist/user.d.ts CHANGED
@@ -8,7 +8,7 @@ export declare const User: z.ZodObject<{
8
8
  image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
9
9
  preferences: z.ZodRecord<z.ZodString, z.ZodAny>;
10
10
  roles: z.ZodArray<z.ZodString>;
11
- registeredAt: z.ZodDate;
11
+ registeredAt: z.ZodCoercedDate<unknown>;
12
12
  isAdmin: z.ZodBoolean;
13
13
  }, z.core.$strip>;
14
14
  export interface User extends z.infer<typeof User> {
@@ -47,12 +47,12 @@ export declare const UserRegistration: z.ZodObject<{
47
47
  authenticatorData: z.ZodOptional<z.ZodString>;
48
48
  transports: z.ZodOptional<z.ZodArray<z.ZodEnum<{
49
49
  ble: "ble";
50
+ cable: "cable";
50
51
  hybrid: "hybrid";
51
52
  internal: "internal";
52
53
  nfc: "nfc";
53
- usb: "usb";
54
- cable: "cable";
55
54
  "smart-card": "smart-card";
55
+ usb: "usb";
56
56
  }>>>;
57
57
  publicKeyAlgorithm: z.ZodOptional<z.ZodNumber>;
58
58
  publicKey: z.ZodOptional<z.ZodString>;
@@ -63,7 +63,7 @@ export declare const UserRegistration: z.ZodObject<{
63
63
  }, z.core.$strip>;
64
64
  }, z.core.$strip>;
65
65
  export declare const UserAuthOptions: z.ZodObject<{
66
- type: z.ZodLiteral<"login" | "action">;
66
+ type: z.ZodLiteral<"login" | "action" | "client_login">;
67
67
  }, z.core.$strip>;
68
68
  export type UserAuthOptions = z.infer<typeof UserAuthOptions>;
69
69
  export declare const LogoutSessions: z.ZodObject<{
package/dist/user.js CHANGED
@@ -9,7 +9,7 @@ export const User = z.object({
9
9
  image: z.url().nullish(),
10
10
  preferences: z.record(z.string(), z.any()),
11
11
  roles: z.array(z.string()),
12
- registeredAt: z.date(),
12
+ registeredAt: z.coerce.date(),
13
13
  isAdmin: z.boolean(),
14
14
  });
15
15
  export const userPublicFields = ['id', 'image', 'name', 'registeredAt', 'roles'];
@@ -34,7 +34,7 @@ export const UserRegistration = z.object({
34
34
  userId: z.uuid(),
35
35
  response: PasskeyRegistration,
36
36
  });
37
- export const UserAuthOptions = z.object({ type: z.literal(['login', 'action']) });
37
+ export const UserAuthOptions = z.object({ type: z.literal(['login', 'action', 'client_login']) });
38
38
  export const LogoutSessions = z.object({
39
39
  id: z.array(z.uuid()).optional(),
40
40
  confirm_all: z.boolean().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/core",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "author": "James Prevett <axium@jamespre.dev>",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -20,7 +20,8 @@
20
20
  "types": "dist/index.d.ts",
21
21
  "exports": {
22
22
  ".": "./dist/index.js",
23
- "./*": "./dist/*.js"
23
+ "./*": "./dist/*.js",
24
+ "./node": "./dist/node/index.js"
24
25
  },
25
26
  "files": [
26
27
  "dist",