@mayhem93/nexxus-core-lib 0.0.1 → 0.0.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/dist-cjs/common/BuiltinSchemas.js +24 -5
- package/dist-cjs/common/BuiltinSchemas.js.map +1 -1
- package/dist-cjs/common/FilterQuery.js +5 -18
- package/dist-cjs/common/FilterQuery.js.map +1 -1
- package/dist-cjs/common/InferModel.js +3 -0
- package/dist-cjs/common/InferModel.js.map +1 -0
- package/dist-cjs/common/JsonPatch.js +42 -225
- package/dist-cjs/common/JsonPatch.js.map +1 -1
- package/dist-cjs/common/SchemaValidator.js +100 -29
- package/dist-cjs/common/SchemaValidator.js.map +1 -1
- package/dist-cjs/index.js +4 -0
- package/dist-cjs/index.js.map +1 -1
- package/dist-cjs/lib/BaseService.js +6 -6
- package/dist-cjs/lib/BaseService.js.map +1 -1
- package/dist-cjs/lib/ConfigManager.js +56 -25
- package/dist-cjs/lib/ConfigManager.js.map +1 -1
- package/dist-cjs/lib/ConfigProvider.js +14 -1
- package/dist-cjs/lib/ConfigProvider.js.map +1 -1
- package/dist-cjs/lib/Exceptions.js +22 -4
- package/dist-cjs/lib/Exceptions.js.map +1 -1
- package/dist-cjs/lib/Logger.js +138 -65
- package/dist-cjs/lib/Logger.js.map +1 -1
- package/dist-cjs/lib/ServiceResolver.js +57 -0
- package/dist-cjs/lib/ServiceResolver.js.map +1 -0
- package/dist-cjs/models/AppModel.js +17 -3
- package/dist-cjs/models/AppModel.js.map +1 -1
- package/dist-cjs/models/Application.js +51 -12
- package/dist-cjs/models/Application.js.map +1 -1
- package/dist-cjs/models/BaseModel.js.map +1 -1
- package/dist-cjs/models/BuiltinModel.js +8 -0
- package/dist-cjs/models/BuiltinModel.js.map +1 -0
- package/dist-cjs/models/User.js +12 -3
- package/dist-cjs/models/User.js.map +1 -1
- package/dist-esm/common/BuiltinSchemas.js +23 -4
- package/dist-esm/common/BuiltinSchemas.js.map +1 -1
- package/dist-esm/common/FilterQuery.js +6 -19
- package/dist-esm/common/FilterQuery.js.map +1 -1
- package/dist-esm/common/InferModel.js +2 -0
- package/dist-esm/common/InferModel.js.map +1 -0
- package/dist-esm/common/JsonPatch.js +44 -227
- package/dist-esm/common/JsonPatch.js.map +1 -1
- package/dist-esm/common/SchemaValidator.js +100 -29
- package/dist-esm/common/SchemaValidator.js.map +1 -1
- package/dist-esm/index.js +4 -0
- package/dist-esm/index.js.map +1 -1
- package/dist-esm/lib/BaseService.js +8 -8
- package/dist-esm/lib/BaseService.js.map +1 -1
- package/dist-esm/lib/ConfigManager.js +56 -25
- package/dist-esm/lib/ConfigManager.js.map +1 -1
- package/dist-esm/lib/ConfigProvider.js +14 -1
- package/dist-esm/lib/ConfigProvider.js.map +1 -1
- package/dist-esm/lib/Exceptions.js +20 -3
- package/dist-esm/lib/Exceptions.js.map +1 -1
- package/dist-esm/lib/Logger.js +139 -66
- package/dist-esm/lib/Logger.js.map +1 -1
- package/dist-esm/lib/ServiceResolver.js +52 -0
- package/dist-esm/lib/ServiceResolver.js.map +1 -0
- package/dist-esm/models/AppModel.js +17 -3
- package/dist-esm/models/AppModel.js.map +1 -1
- package/dist-esm/models/Application.js +52 -13
- package/dist-esm/models/Application.js.map +1 -1
- package/dist-esm/models/BaseModel.js.map +1 -1
- package/dist-esm/models/BuiltinModel.js +4 -0
- package/dist-esm/models/BuiltinModel.js.map +1 -0
- package/dist-esm/models/User.js +11 -2
- package/dist-esm/models/User.js.map +1 -1
- package/dist-types/common/BuiltinSchemas.d.ts +59 -5
- package/dist-types/common/FilterQuery.d.ts +3 -11
- package/dist-types/common/InferModel.d.ts +29 -0
- package/dist-types/common/JsonPatch.d.ts +4 -45
- package/dist-types/common/ModelTypes.d.ts +1 -0
- package/dist-types/common/QueuePayloads.d.ts +48 -26
- package/dist-types/common/SchemaValidator.d.ts +42 -2
- package/dist-types/index.d.ts +4 -0
- package/dist-types/lib/BaseService.d.ts +51 -1
- package/dist-types/lib/ConfigManager.d.ts +50 -13
- package/dist-types/lib/ConfigProvider.d.ts +6 -0
- package/dist-types/lib/Exceptions.d.ts +15 -2
- package/dist-types/lib/Logger.d.ts +84 -14
- package/dist-types/lib/ServiceResolver.d.ts +23 -0
- package/dist-types/models/AppModel.d.ts +31 -5
- package/dist-types/models/Application.d.ts +36 -8
- package/dist-types/models/BaseModel.d.ts +14 -12
- package/dist-types/models/BuiltinModel.d.ts +3 -0
- package/dist-types/models/User.d.ts +15 -13
- package/package.json +4 -4
- package/src/schemas/winston-logger.schema.json +51 -6
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { INexxusAppModel } from '../models/AppModel';
|
|
2
2
|
import { NexxusJsonPatchInternal, NexxusJsonPatchMetadata } from '../common/JsonPatch';
|
|
3
3
|
export interface NexxusBaseQueuePayload {
|
|
4
4
|
event: string;
|
|
@@ -6,65 +6,87 @@ export interface NexxusBaseQueuePayload {
|
|
|
6
6
|
}
|
|
7
7
|
export type NexxusModelCreatedPayload = {
|
|
8
8
|
event: 'model_created';
|
|
9
|
-
data:
|
|
9
|
+
data: INexxusAppModel;
|
|
10
10
|
};
|
|
11
11
|
export type NexxusModelUpdatedPayload = {
|
|
12
12
|
event: 'model_updated';
|
|
13
13
|
data: Array<NexxusJsonPatchInternal>;
|
|
14
14
|
};
|
|
15
|
-
export type NexxusModelDeletedData = Pick<
|
|
15
|
+
export type NexxusModelDeletedData = Pick<INexxusAppModel, 'id' | 'type' | 'appId' | 'userId'>;
|
|
16
16
|
export type NexxusModelDeletedPayload = {
|
|
17
17
|
event: 'model_deleted';
|
|
18
18
|
data: NexxusModelDeletedData;
|
|
19
19
|
};
|
|
20
20
|
export type NexxusWriterPayload = NexxusModelCreatedPayload | NexxusModelUpdatedPayload | NexxusModelDeletedPayload;
|
|
21
|
-
export type
|
|
21
|
+
export type NexxusTransportManagerJsonPatch = Omit<NexxusJsonPatchInternal, 'metadata'> & {
|
|
22
22
|
metadata: NexxusJsonPatchMetadata & {
|
|
23
|
-
partialModel: Partial<
|
|
23
|
+
partialModel: Partial<INexxusAppModel>;
|
|
24
24
|
};
|
|
25
25
|
};
|
|
26
26
|
export type NexxusTransportManagerModelUpdatedPayload = {
|
|
27
27
|
event: 'model_updated';
|
|
28
|
-
data: Array<
|
|
28
|
+
data: Array<NexxusTransportManagerJsonPatch>;
|
|
29
29
|
};
|
|
30
30
|
export type NexxusTransportManagerPayload = NexxusModelCreatedPayload | NexxusTransportManagerModelUpdatedPayload | NexxusModelDeletedPayload;
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
/**
|
|
32
|
+
* Metadata for transport-bound model events (create/update/delete alike).
|
|
33
|
+
* Carries the channels that matched the subscription router for this device,
|
|
34
|
+
* so the client can correlate the event with its local subscription containers.
|
|
35
|
+
*/
|
|
36
|
+
export interface NexxusTransportMetadata {
|
|
33
37
|
channels: Array<string>;
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
|
-
*
|
|
40
|
+
* Payload for any transport worker - model created
|
|
41
|
+
*/
|
|
42
|
+
export type NexxusTransportModelCreatedPayload = {
|
|
43
|
+
event: 'model_created';
|
|
44
|
+
model: INexxusAppModel;
|
|
45
|
+
metadata: NexxusTransportMetadata;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Payload for any transport worker - model deleted
|
|
37
49
|
*/
|
|
38
|
-
export type
|
|
39
|
-
|
|
50
|
+
export type NexxusTransportModelDeletedPayload = {
|
|
51
|
+
event: 'model_deleted';
|
|
52
|
+
model: NexxusModelDeletedData;
|
|
53
|
+
metadata: NexxusTransportMetadata;
|
|
40
54
|
};
|
|
41
55
|
/**
|
|
42
|
-
* Payload for
|
|
56
|
+
* Payload for any transport worker - model updated.
|
|
57
|
+
* All patches in a single event target the same model — its identity is hoisted
|
|
58
|
+
* into `model`, and the matched channels into top-level `metadata`. Per-patch
|
|
59
|
+
* metadata is intentionally absent (it was redundant across patches of the same
|
|
60
|
+
* update event under the previous shape).
|
|
43
61
|
*/
|
|
44
|
-
export type
|
|
62
|
+
export type NexxusTransportModelUpdatedPayload = {
|
|
45
63
|
event: 'model_updated';
|
|
46
|
-
|
|
64
|
+
/**
|
|
65
|
+
* Includes `version` — the post-update version stamp assigned by the adapter.
|
|
66
|
+
* Clients use it for gap detection: apply if `model.version === local.version + 1`,
|
|
67
|
+
* request a fresh GET if there's a gap, ignore if it's already been applied.
|
|
68
|
+
*/
|
|
69
|
+
model: Pick<INexxusAppModel, 'id' | 'type' | 'appId' | 'userId' | 'version'>;
|
|
70
|
+
patches: Array<Omit<NexxusJsonPatchInternal, 'metadata'>>;
|
|
71
|
+
metadata: NexxusTransportMetadata;
|
|
47
72
|
};
|
|
48
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Canonical payload consumed by ALL transport workers (volatile and persistent alike).
|
|
75
|
+
* The shape is identical across transports; each transport translates the inner `data`
|
|
76
|
+
* into its own wire format inside its `sendToDevice` implementation.
|
|
77
|
+
*/
|
|
78
|
+
export type NexxusTransportWorkerPayload = {
|
|
49
79
|
event: 'device_message';
|
|
50
80
|
deviceIds: Array<string>;
|
|
51
|
-
data:
|
|
52
|
-
};
|
|
53
|
-
export type NexxusMqttPayload = {
|
|
54
|
-
event: 'mqtt_publish';
|
|
55
|
-
topic: string;
|
|
56
|
-
payload: Buffer;
|
|
57
|
-
} | {
|
|
58
|
-
event: 'mqtt_subscribe';
|
|
59
|
-
topic: string;
|
|
81
|
+
data: NexxusTransportModelCreatedPayload | NexxusTransportModelUpdatedPayload | NexxusTransportModelDeletedPayload;
|
|
60
82
|
};
|
|
61
83
|
export interface NexxusBuiltInQueuePayloadMap {
|
|
62
84
|
'writer': NexxusWriterPayload;
|
|
63
85
|
'transport-manager': NexxusTransportManagerPayload;
|
|
64
86
|
}
|
|
65
87
|
export interface NexxusDynamicQueuePayloadMap {
|
|
66
|
-
'websockets-transport':
|
|
67
|
-
'mqtt-transport':
|
|
88
|
+
'websockets-transport': NexxusTransportWorkerPayload;
|
|
89
|
+
'mqtt-transport': NexxusTransportWorkerPayload;
|
|
68
90
|
}
|
|
69
91
|
export type NexxusBuiltInQueueName = keyof NexxusBuiltInQueuePayloadMap;
|
|
70
92
|
export type NexxusDynamicQueueType = keyof NexxusDynamicQueuePayloadMap;
|
|
@@ -1,4 +1,44 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { NexxusFieldDef, NexxusModelDef } from './ModelTypes';
|
|
2
|
+
/**
|
|
3
|
+
* Pure schema-aware validation + normalization for a single value against a
|
|
4
|
+
* field definition, or for whole-model data against a model definition.
|
|
5
|
+
*
|
|
6
|
+
* Returns a normalized copy of the input value (e.g. date strings become
|
|
7
|
+
* integer timestamps). Throws InvalidSchemaDataException on the first violation.
|
|
8
|
+
*
|
|
9
|
+
* Used by:
|
|
10
|
+
* - NexxusJsonPatch (per-path validation on patch operations)
|
|
11
|
+
* - NexxusAppModel (whole-model validation on construction)
|
|
12
|
+
*
|
|
13
|
+
* Required-field checking is intentionally NOT performed here yet; only fields
|
|
14
|
+
* present in the input are validated.
|
|
15
|
+
*/
|
|
2
16
|
export declare class NexxusSchemaValidator {
|
|
3
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Validate every present field of `data` against the model definition.
|
|
19
|
+
* Returns a shallow-merged copy of `data` with each value normalized.
|
|
20
|
+
* Unknown fields (present in data but not declared in modelDef) are passed
|
|
21
|
+
* through unchanged.
|
|
22
|
+
*/
|
|
23
|
+
static validateAgainstSchema(data: Record<string, unknown>, modelDef: NexxusModelDef): Record<string, unknown>;
|
|
24
|
+
/**
|
|
25
|
+
* Validate a single value against a field definition. Returns the normalized
|
|
26
|
+
* value. Throws InvalidSchemaDataException on failure.
|
|
27
|
+
*
|
|
28
|
+
* `path` is used only for error messages.
|
|
29
|
+
*/
|
|
30
|
+
static validateValue(value: unknown, fieldDef: NexxusFieldDef, path: string): unknown;
|
|
31
|
+
private static validateString;
|
|
32
|
+
private static validateNumber;
|
|
33
|
+
private static validateBoolean;
|
|
34
|
+
/**
|
|
35
|
+
* Accepts: number (ms timestamp), ISO date string, or numeric string.
|
|
36
|
+
* Returns: integer ms timestamp.
|
|
37
|
+
*
|
|
38
|
+
* This is the single canonical date parser for the system; the previous
|
|
39
|
+
* inconsistency between `Date.parse()` and `new Date(s).getTime()` is gone.
|
|
40
|
+
*/
|
|
41
|
+
private static validateDate;
|
|
42
|
+
private static validateObject;
|
|
43
|
+
private static validateArray;
|
|
4
44
|
}
|
package/dist-types/index.d.ts
CHANGED
|
@@ -3,10 +3,14 @@ export * from "./lib/ConfigProvider";
|
|
|
3
3
|
export * from "./lib/ConfigManager";
|
|
4
4
|
export * from "./lib/BaseService";
|
|
5
5
|
export * from "./lib/Logger";
|
|
6
|
+
export * from "./lib/ServiceResolver";
|
|
6
7
|
export * from "./common/QueuePayloads";
|
|
7
8
|
export * from "./common/JsonPatch";
|
|
8
9
|
export * from "./common/FilterQuery";
|
|
10
|
+
export * from "./common/InferModel";
|
|
11
|
+
export * from "./common/SchemaValidator";
|
|
9
12
|
export * from "./models/BaseModel";
|
|
13
|
+
export * from "./models/BuiltinModel";
|
|
10
14
|
export * from './common/BuiltinSchemas';
|
|
11
15
|
export * from "./models/AppModel";
|
|
12
16
|
export * from "./models/Application";
|
|
@@ -1,11 +1,54 @@
|
|
|
1
1
|
import type { ConfigEnvVars, ConfigCliArgs, AddJsonSchemaDefFuncArg, NexxusConfigManager } from './ConfigManager';
|
|
2
|
-
import type { NexxusBaseLogger } from
|
|
2
|
+
import type { NexxusBaseLogger } from './Logger';
|
|
3
3
|
import { NexxusConfig } from './ConfigProvider';
|
|
4
4
|
export type EventMap = Record<string | symbol, any[]>;
|
|
5
5
|
export interface INexxusBaseServices {
|
|
6
6
|
configManager: NexxusConfigManager;
|
|
7
7
|
logger: NexxusBaseLogger<NexxusConfig>;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* Static-only shape of a Nexxus service class. Anything ConfigManager needs to
|
|
11
|
+
* register a service's config lives here — `name` (for diagnostics), plus the
|
|
12
|
+
* three static-config accessors. Constructor visibility is intentionally
|
|
13
|
+
* absent, so classes that expose an async factory instead of a public
|
|
14
|
+
* constructor (e.g. `WinstonNexxusLogger.create(...)`) still satisfy the type.
|
|
15
|
+
*
|
|
16
|
+
* All `NexxusBaseService` subclasses satisfy this structurally. Downstream code
|
|
17
|
+
* writing custom services should type against this interface when passing
|
|
18
|
+
* classes to `ConfigManager.validateServices(...)`.
|
|
19
|
+
*/
|
|
20
|
+
export interface NexxusServiceClass {
|
|
21
|
+
readonly name: string;
|
|
22
|
+
schema(): AddJsonSchemaDefFuncArg;
|
|
23
|
+
envVarConfig(): ConfigEnvVars;
|
|
24
|
+
cliArgConfig(): ConfigCliArgs;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* `NexxusServiceClass` + a public constructor. This is the default shape for
|
|
28
|
+
* pluggable adapters — DB, MQ, most anything without async init. Consumers
|
|
29
|
+
* `new Cls(services)` directly at the bootstrap site.
|
|
30
|
+
*
|
|
31
|
+
* If your adapter needs to await something before it's ready (dynamic
|
|
32
|
+
* imports, remote fetches, warm-up connections), use
|
|
33
|
+
* `NexxusFactoryServiceClass` instead — that's what `WinstonNexxusLogger`
|
|
34
|
+
* does for dynamic transports.
|
|
35
|
+
*/
|
|
36
|
+
export interface NexxusConstructableServiceClass extends NexxusServiceClass {
|
|
37
|
+
new (services: INexxusBaseServices): NexxusBaseService<any, any>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* `NexxusServiceClass` + a static async factory. For adapters whose
|
|
41
|
+
* initialization has to await something — dynamic imports, remote handshakes,
|
|
42
|
+
* schema fetches. The class typically hides its constructor (private) so
|
|
43
|
+
* consumers must go through `Cls.create(services)`.
|
|
44
|
+
*
|
|
45
|
+
* Currently used only for `WinstonNexxusLogger`. Most adapters are fine as
|
|
46
|
+
* `NexxusConstructableServiceClass`; only reach for this when async init is
|
|
47
|
+
* actually needed.
|
|
48
|
+
*/
|
|
49
|
+
export interface NexxusFactoryServiceClass extends NexxusServiceClass {
|
|
50
|
+
create(services: INexxusBaseServices): Promise<NexxusBaseService<any, any>>;
|
|
51
|
+
}
|
|
9
52
|
declare class TypedEventEmitter<E> {
|
|
10
53
|
private emitter;
|
|
11
54
|
constructor();
|
|
@@ -18,6 +61,13 @@ export declare abstract class NexxusBaseService<T extends NexxusConfig, Ev exten
|
|
|
18
61
|
protected config: Readonly<T>;
|
|
19
62
|
protected static envVars: ConfigEnvVars;
|
|
20
63
|
protected static cliArgs: ConfigCliArgs;
|
|
64
|
+
/**
|
|
65
|
+
* The top-level key under which this service's configuration sits in the merged
|
|
66
|
+
* root config (e.g. "logger", "database", "redis", "app"). Required for every
|
|
67
|
+
* concrete service — ConfigManager uses it as the property name when grafting
|
|
68
|
+
* the service's schema definition into the root JSON Schema.
|
|
69
|
+
*/
|
|
70
|
+
protected static configRootKey: string;
|
|
21
71
|
protected static schemaPath: string;
|
|
22
72
|
private static schemaContents;
|
|
23
73
|
constructor(config: Readonly<T>);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NexxusServiceClass } from './BaseService';
|
|
2
2
|
import { CliArgType, NexxusConfig, INexxusConfigProvider } from "./ConfigProvider";
|
|
3
3
|
import type { JSONSchema7Definition } from "json-schema";
|
|
4
4
|
export type AddJsonSchemaDefFuncArg = {
|
|
@@ -9,35 +9,72 @@ export type AddJsonSchemaDefFuncArg = {
|
|
|
9
9
|
};
|
|
10
10
|
type EnvVarsSpec = {
|
|
11
11
|
name: string;
|
|
12
|
+
/** Dot-notation path relative to the service's configRootKey (e.g. "host", "auth.jwtSecret"). */
|
|
12
13
|
location: string;
|
|
13
14
|
};
|
|
14
|
-
export type ConfigEnvVars =
|
|
15
|
-
source: string;
|
|
16
|
-
specs: Array<EnvVarsSpec>;
|
|
17
|
-
};
|
|
15
|
+
export type ConfigEnvVars = Array<EnvVarsSpec>;
|
|
18
16
|
type CliArgsSpec = {
|
|
19
17
|
name: string;
|
|
18
|
+
/** Dot-notation path relative to the service's configRootKey. */
|
|
20
19
|
location: string;
|
|
21
20
|
type: CliArgType;
|
|
22
21
|
};
|
|
23
|
-
export type ConfigCliArgs =
|
|
24
|
-
source: string;
|
|
25
|
-
specs: Array<CliArgsSpec>;
|
|
26
|
-
};
|
|
22
|
+
export type ConfigCliArgs = Array<CliArgsSpec>;
|
|
27
23
|
export declare class NexxusConfigManager {
|
|
28
24
|
private static CONF_FILE_NAME;
|
|
25
|
+
private static DEFAULT_CONF_PATH;
|
|
29
26
|
private jsonSchema;
|
|
30
27
|
private envVarsSpecs;
|
|
31
28
|
private cliArgsSpecs;
|
|
32
29
|
private data;
|
|
33
30
|
private configProviders;
|
|
34
31
|
private customProviders;
|
|
35
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Names (class.name) of services already registered. Guards against
|
|
34
|
+
* double-registration when the same class is passed to `registerService`
|
|
35
|
+
* or `validateServices` more than once — schema, env, and CLI specs each
|
|
36
|
+
* carry a uniqueness invariant that would otherwise throw on a second pass.
|
|
37
|
+
*/
|
|
38
|
+
private registeredServices;
|
|
39
|
+
/**
|
|
40
|
+
* True until the file + custom providers have been loaded into `this.data`
|
|
41
|
+
* (i.e., first successful validate()). Subsequent validate() calls skip
|
|
42
|
+
* re-reading the config file — the file is treated as immutable for the
|
|
43
|
+
* lifetime of this ConfigManager.
|
|
44
|
+
*/
|
|
45
|
+
private dataInitialized;
|
|
46
|
+
/**
|
|
47
|
+
* True when at least one service has been registered since the last
|
|
48
|
+
* successful validate(). Lets `validateServices()` no-op cleanly when
|
|
49
|
+
* called after everything's already been validated.
|
|
50
|
+
*/
|
|
51
|
+
private hasNewRegistrations;
|
|
52
|
+
constructor(configFilePath?: string);
|
|
36
53
|
addCustomProvider(provider: INexxusConfigProvider): void;
|
|
37
54
|
private addJsonSchemaDef;
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Register a service's config schema, env-var spec, and CLI-arg spec so
|
|
57
|
+
* they're included in the next `validateServices()` pass. Idempotent per
|
|
58
|
+
* class name — repeat calls with the same class are silent no-ops.
|
|
59
|
+
*
|
|
60
|
+
* Intended usage: call once per service class ahead of time (in any order),
|
|
61
|
+
* then call `validateServices()` once to actually run validation. For the
|
|
62
|
+
* shorthand where you know all services up front, `validateServices([...])`
|
|
63
|
+
* still accepts an array and registers each entry before validating.
|
|
64
|
+
*/
|
|
65
|
+
registerService(svc: NexxusServiceClass): void;
|
|
66
|
+
/**
|
|
67
|
+
* Runs config validation against every registered service's schema. Safe
|
|
68
|
+
* to call multiple times — the config file is read only on the first call,
|
|
69
|
+
* and repeat calls with no new registrations short-circuit as a no-op.
|
|
70
|
+
*
|
|
71
|
+
* The optional `svcs` array is a shorthand: each entry is passed through
|
|
72
|
+
* `registerService()` before validation runs. Existing callers passing all
|
|
73
|
+
* services up front (`validateServices([A, B, C])`) continue to work.
|
|
74
|
+
* Later calls can register additional services (e.g. dynamically-resolved
|
|
75
|
+
* logger/db/mq implementations) and re-validate.
|
|
76
|
+
*/
|
|
77
|
+
validateServices(svcs?: Array<NexxusServiceClass>): Promise<void>;
|
|
41
78
|
private populateFromCliArgs;
|
|
42
79
|
private populateFromEnvVars;
|
|
43
80
|
private populateFromCustomProviders;
|
|
@@ -21,7 +21,13 @@ export declare class NexxusEnvVarsConfigProvider extends NexxusConfigProvider {
|
|
|
21
21
|
export declare class NexxusCliArgConfigProvider extends NexxusConfigProvider {
|
|
22
22
|
private argParser;
|
|
23
23
|
private originalExit;
|
|
24
|
+
private addedArgs;
|
|
24
25
|
constructor();
|
|
26
|
+
/**
|
|
27
|
+
* Idempotent per argument name — safe to call multiple times across repeat
|
|
28
|
+
* `ConfigManager.validateServices()` invocations. Argparse would otherwise
|
|
29
|
+
* throw on a second `add_argument` with the same name.
|
|
30
|
+
*/
|
|
25
31
|
addArgument(name: string, type: CliArgType): void;
|
|
26
32
|
getConfig(): NexxusConfig;
|
|
27
33
|
}
|
|
@@ -5,13 +5,22 @@ export declare enum NexxusExceptions {
|
|
|
5
5
|
INVALID_CONFIG = "InvalidConfigException",
|
|
6
6
|
INVALID_JSON_PATCH = "InvalidJsonPatchException",
|
|
7
7
|
INVALID_QUERY_FILTER = "InvalidQueryFilterException",
|
|
8
|
+
INVALID_SCHEMA_DATA = "InvalidSchemaDataException",
|
|
8
9
|
INVALID_USER_MODEL = "InvalidUserModelException"
|
|
9
10
|
}
|
|
10
11
|
export declare class NexxusException extends Error {
|
|
11
|
-
|
|
12
|
+
readonly subcode?: string;
|
|
13
|
+
constructor(type: string, message: string, subcode?: string);
|
|
14
|
+
}
|
|
15
|
+
declare enum FatalErrorSubcodes {
|
|
16
|
+
CONFIG_FILE_NOT_FOUND = "CONFIG_FILE_NOT_FOUND",
|
|
17
|
+
CONFIG_FILE_INVALID_JSON = "CONFIG_FILE_INVALID_JSON",
|
|
18
|
+
CONFIG_FILE_INVALID_SCHEMA = "CONFIG_FILE_INVALID_SCHEMA",
|
|
19
|
+
CONFIG_FILE_UNREADABLE = "CONFIG_FILE_UNREADABLE"
|
|
12
20
|
}
|
|
13
21
|
export declare class FatalErrorException extends NexxusException {
|
|
14
|
-
|
|
22
|
+
static SUBCODES: Readonly<typeof FatalErrorSubcodes>;
|
|
23
|
+
constructor(message: string, subcode?: FatalErrorSubcodes);
|
|
15
24
|
}
|
|
16
25
|
export declare class BadRequestException extends NexxusException {
|
|
17
26
|
constructor(message: string);
|
|
@@ -28,6 +37,10 @@ export declare class InvalidJsonPatchException extends NexxusException {
|
|
|
28
37
|
export declare class InvalidQueryFilterException extends NexxusException {
|
|
29
38
|
constructor(message: string);
|
|
30
39
|
}
|
|
40
|
+
export declare class InvalidSchemaDataException extends NexxusException {
|
|
41
|
+
constructor(message: string);
|
|
42
|
+
}
|
|
31
43
|
export declare class InvalidUserModelException extends NexxusException {
|
|
32
44
|
constructor(message: string);
|
|
33
45
|
}
|
|
46
|
+
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ConfigEnvVars, ConfigCliArgs } from './ConfigManager';
|
|
2
2
|
import { NexxusBaseService, INexxusBaseServices } from './BaseService';
|
|
3
3
|
import { NexxusConfig } from './ConfigProvider';
|
|
4
|
-
type
|
|
4
|
+
export type LogAttributes = Record<string, unknown>;
|
|
5
5
|
export declare const enum NexxusLoggerLevels {
|
|
6
6
|
EMERGENCY = "emerg",
|
|
7
7
|
ALERT = "alert",
|
|
@@ -12,37 +12,107 @@ export declare const enum NexxusLoggerLevels {
|
|
|
12
12
|
INFO = "info",
|
|
13
13
|
DEBUG = "debug"
|
|
14
14
|
}
|
|
15
|
+
export type StdoutTransportConfig = {
|
|
16
|
+
type: 'stdout';
|
|
17
|
+
};
|
|
18
|
+
export type FileTransportConfig = {
|
|
19
|
+
type: 'file';
|
|
20
|
+
filename: string;
|
|
21
|
+
maxSize?: number;
|
|
22
|
+
maxFiles?: number;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Catch-all for transports loaded dynamically from npm packages. The `type`
|
|
26
|
+
* field is the package name (e.g. "winston-papertrail"); `export` optionally
|
|
27
|
+
* names the class export within the package; `options` is the passthrough
|
|
28
|
+
* config object handed to the transport's constructor.
|
|
29
|
+
*
|
|
30
|
+
* The schema's `oneOf` discriminator excludes built-in type strings from this
|
|
31
|
+
* variant, so the TS-side `type: string` doesn't cause ambiguity at runtime.
|
|
32
|
+
*/
|
|
33
|
+
export type CustomTransportConfig = {
|
|
34
|
+
type: string;
|
|
35
|
+
export?: string;
|
|
36
|
+
options?: Record<string, unknown>;
|
|
37
|
+
};
|
|
38
|
+
export type WinstonNexxusTransportConfig = StdoutTransportConfig | FileTransportConfig | CustomTransportConfig;
|
|
15
39
|
type WinstonNexxusLoggerConfig = {
|
|
16
40
|
level: NexxusLoggerLevels;
|
|
17
41
|
logType: "json" | "text";
|
|
18
42
|
timestamps: boolean;
|
|
19
43
|
colors: boolean;
|
|
44
|
+
transports?: Array<WinstonNexxusTransportConfig>;
|
|
20
45
|
} & NexxusConfig;
|
|
21
46
|
export interface INexxusLogger {
|
|
22
|
-
log(level: NexxusLoggerLevels, message:
|
|
47
|
+
log(level: NexxusLoggerLevels, message: string, attributes?: LogAttributes, label?: string): void;
|
|
23
48
|
}
|
|
24
|
-
export interface INexxusAsyncLogger
|
|
25
|
-
log(level: NexxusLoggerLevels, message:
|
|
49
|
+
export interface INexxusAsyncLogger {
|
|
50
|
+
log(level: NexxusLoggerLevels, message: string, attributes?: LogAttributes, label?: string): Promise<void>;
|
|
26
51
|
}
|
|
27
52
|
interface NexxusLoggerServices extends Omit<INexxusBaseServices, 'logger'> {
|
|
28
53
|
}
|
|
29
54
|
export declare abstract class NexxusBaseLogger<T extends NexxusConfig> extends NexxusBaseService<T> implements INexxusLogger {
|
|
55
|
+
protected static configRootKey: string;
|
|
30
56
|
constructor(services: NexxusLoggerServices);
|
|
31
|
-
abstract log(level: NexxusLoggerLevels, message:
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
57
|
+
abstract log(level: NexxusLoggerLevels, message: string, attributes?: LogAttributes, label?: string): void;
|
|
58
|
+
protected static serializeError(err: Error): Record<string, unknown>;
|
|
59
|
+
protected static makeSafeReplacer(): (key: string, value: unknown) => unknown;
|
|
60
|
+
protected static safeStringify(value: unknown): string;
|
|
61
|
+
debug(message: string, label?: string): void;
|
|
62
|
+
debug(message: string, attributes: LogAttributes, label?: string): void;
|
|
63
|
+
info(message: string, label?: string): void;
|
|
64
|
+
info(message: string, attributes: LogAttributes, label?: string): void;
|
|
65
|
+
warn(message: string, label?: string): void;
|
|
66
|
+
warn(message: string, attributes: LogAttributes, label?: string): void;
|
|
67
|
+
error(message: string, label?: string): void;
|
|
68
|
+
error(message: string, attributes: LogAttributes, label?: string): void;
|
|
69
|
+
critical(message: string, label?: string): void;
|
|
70
|
+
critical(message: string, attributes: LogAttributes, label?: string): void;
|
|
71
|
+
alert(message: string, label?: string): void;
|
|
72
|
+
alert(message: string, attributes: LogAttributes, label?: string): void;
|
|
73
|
+
emerg(message: string, label?: string): void;
|
|
74
|
+
emerg(message: string, attributes: LogAttributes, label?: string): void;
|
|
75
|
+
private dispatch;
|
|
39
76
|
}
|
|
40
77
|
export declare class WinstonNexxusLogger extends NexxusBaseLogger<WinstonNexxusLoggerConfig> {
|
|
41
78
|
private winston;
|
|
42
79
|
protected static schemaPath: string;
|
|
43
80
|
protected static envVars: ConfigEnvVars;
|
|
44
81
|
protected static cliArgs: ConfigCliArgs;
|
|
45
|
-
|
|
46
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Private — use `WinstonNexxusLogger.create(...)` instead. Transport instances
|
|
84
|
+
* must be resolved (async, since custom ones are dynamically imported) before
|
|
85
|
+
* the Winston logger can be constructed; the factory owns that step.
|
|
86
|
+
*/
|
|
87
|
+
private constructor();
|
|
88
|
+
/**
|
|
89
|
+
* Async factory. Custom transports are loaded via dynamic `import()` so
|
|
90
|
+
* initialization is unavoidably async. API/Worker bootstrap code should
|
|
91
|
+
* `await WinstonNexxusLogger.create(...)` in place of `new WinstonNexxusLogger(...)`.
|
|
92
|
+
*/
|
|
93
|
+
static create(services: NexxusLoggerServices): Promise<WinstonNexxusLogger>;
|
|
94
|
+
/**
|
|
95
|
+
* Maps each transport config entry to a constructed Winston transport.
|
|
96
|
+
* Built-in types are instantiated synchronously; everything else falls
|
|
97
|
+
* through to `loadCustomTransport`, which dynamically imports the npm
|
|
98
|
+
* package named by `type`.
|
|
99
|
+
*
|
|
100
|
+
* If the config has no transports (undefined or empty array), defaults to
|
|
101
|
+
* a single Console transport so the logger is never silent.
|
|
102
|
+
*/
|
|
103
|
+
private static resolveTransports;
|
|
104
|
+
/**
|
|
105
|
+
* Dynamically imports the npm package named by `transport.type` and instantiates
|
|
106
|
+
* its transport class. The class is resolved in priority order:
|
|
107
|
+
* 1. `mod[transport.export]` if `transport.export` is set in config
|
|
108
|
+
* 2. `mod.default` if it's a function/class (ESM default export)
|
|
109
|
+
* 3. The module itself if it's a function/class (CJS `module.exports = Cls`)
|
|
110
|
+
*
|
|
111
|
+
* On any failure (package missing, no resolvable class, constructor throws)
|
|
112
|
+
* we re-wrap as `InvalidConfigException` so the operator sees an actionable
|
|
113
|
+
* message rather than a raw module-resolution stack trace.
|
|
114
|
+
*/
|
|
115
|
+
private static loadCustomTransport;
|
|
116
|
+
log(level: NexxusLoggerLevels, message: string, attributes?: LogAttributes, label?: string): void;
|
|
47
117
|
}
|
|
48
118
|
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { NexxusConstructableServiceClass, NexxusFactoryServiceClass } from './BaseService';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a config-declared service to a **constructable** class — one the
|
|
4
|
+
* bootstrap will instantiate via `new Cls(services)`.
|
|
5
|
+
*
|
|
6
|
+
* 1. If `configuredName` is in `builtins`, that class is returned directly
|
|
7
|
+
* (fast path — no I/O, no dynamic import). Callers pass their known
|
|
8
|
+
* static-imported builtins here.
|
|
9
|
+
* 2. Otherwise the name is treated as an npm package specifier and loaded
|
|
10
|
+
* via dynamic import against the app's install tree.
|
|
11
|
+
*
|
|
12
|
+
* Used by `NexxusApi.resolveConstructableService` and
|
|
13
|
+
* `NexxusBaseWorker.resolveConstructableService` — the host classes provide
|
|
14
|
+
* their own builtin maps.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveConstructableServiceClass(configuredName: string, builtins: Record<string, NexxusConstructableServiceClass>): Promise<NexxusConstructableServiceClass>;
|
|
17
|
+
/**
|
|
18
|
+
* Same as `resolveConstructableServiceClass` but for services that expose an
|
|
19
|
+
* async `static create(services)` factory. Runtime-checks the class actually
|
|
20
|
+
* has `create` before returning, so misconfigurations surface here instead
|
|
21
|
+
* of blowing up at bootstrap time.
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveFactoryServiceClass(configuredName: string, builtins: Record<string, NexxusFactoryServiceClass>): Promise<NexxusFactoryServiceClass>;
|
|
@@ -1,9 +1,35 @@
|
|
|
1
|
+
import { NexxusApplicationSchema } from "./Application";
|
|
1
2
|
import { NexxusBaseModel, INexxusBaseModel } from "./BaseModel";
|
|
2
|
-
export
|
|
3
|
+
export interface INexxusAppModel extends INexxusBaseModel {
|
|
3
4
|
appId: string;
|
|
4
5
|
userId?: string;
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Monotonically-increasing per-document version counter, assigned by the
|
|
8
|
+
* database adapter on every write. Used for client-side gap detection and
|
|
9
|
+
* dedup when applying real-time update events. App models flow through the
|
|
10
|
+
* async Writer → Transport Manager → client pipeline; built-in models
|
|
11
|
+
* (Application, User) do not and therefore do not carry a version.
|
|
12
|
+
* Never set by user input — the validator rejects user-supplied values.
|
|
13
|
+
*/
|
|
14
|
+
version?: number;
|
|
15
|
+
[key: string]: unknown;
|
|
16
|
+
}
|
|
17
|
+
export declare class NexxusAppModel extends NexxusBaseModel<INexxusAppModel> {
|
|
18
|
+
/**
|
|
19
|
+
* Construct an AppModel from user-supplied or pre-validated data.
|
|
20
|
+
*
|
|
21
|
+
* Pass the application's schema as the second argument to validate + normalize
|
|
22
|
+
* the input (e.g. date strings → integer timestamps). Pass `null` to skip
|
|
23
|
+
* validation — only do that when the data is already trusted (e.g. coming
|
|
24
|
+
* back from storage). The `fromStorage` static factory below is the preferred
|
|
25
|
+
* way to express that intent.
|
|
26
|
+
*/
|
|
27
|
+
constructor(props: INexxusAppModel, appSchema: NexxusApplicationSchema | null);
|
|
28
|
+
/**
|
|
29
|
+
* Hydrate an AppModel from already-validated storage. Use when reading back
|
|
30
|
+
* from the database — the data was validated when it was written, so we don't
|
|
31
|
+
* re-validate (saves work and avoids false-positives on legacy/edge-case docs
|
|
32
|
+
* that pre-date a later schema change).
|
|
33
|
+
*/
|
|
34
|
+
static fromStorage(props: INexxusAppModel): NexxusAppModel;
|
|
9
35
|
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { INexxusBaseModel } from "./BaseModel";
|
|
2
|
+
import { NexxusBuiltinModel } from "./BuiltinModel";
|
|
2
3
|
import { NexxusModelDef } from "../common/ModelTypes";
|
|
4
|
+
import { InferModel } from "../common/InferModel";
|
|
5
|
+
import { NEXXUS_BUILTIN_MODEL_SCHEMAS } from "../common/BuiltinSchemas";
|
|
3
6
|
import { NexxusUserDetailSchema } from "./User";
|
|
4
7
|
export interface NexxusApplicationSchema {
|
|
5
8
|
[modelName: string]: NexxusModelDef;
|
|
@@ -7,23 +10,48 @@ export interface NexxusApplicationSchema {
|
|
|
7
10
|
export interface NexxusUserTypeConfig {
|
|
8
11
|
private?: boolean;
|
|
9
12
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Per-application auth configuration. Lives on the Application document so
|
|
15
|
+
* that each tenant carries its own JWT secret and per-strategy settings.
|
|
16
|
+
*
|
|
17
|
+
* `strategies` is a map keyed by strategy name (must be a subset of the
|
|
18
|
+
* deployment's `api.auth.availableStrategies`). Each value's shape is
|
|
19
|
+
* whatever the strategy's own JSON Schema defines — validated by the
|
|
20
|
+
* corresponding `NexxusAuthStrategy` subclass at instantiation time.
|
|
21
|
+
*/
|
|
22
|
+
export interface NexxusApplicationAuthConfig {
|
|
23
|
+
jwtSecret: string;
|
|
24
|
+
jwtExpiresIn?: string;
|
|
25
|
+
strategies: Record<string, unknown>;
|
|
26
|
+
}
|
|
27
|
+
export type INexxusApplication = INexxusBaseModel<'application'> & InferModel<typeof NEXXUS_BUILTIN_MODEL_SCHEMAS.application> & {
|
|
13
28
|
schema: NexxusApplicationSchema;
|
|
14
|
-
authEnabled: boolean;
|
|
15
|
-
allowMultipleLogin: boolean | null;
|
|
16
29
|
userTypes?: {
|
|
17
30
|
[userType: string]: NexxusUserTypeConfig;
|
|
18
31
|
};
|
|
19
32
|
userDetailSchema?: {
|
|
20
33
|
[userType: string]: NexxusUserDetailSchema;
|
|
21
34
|
};
|
|
35
|
+
auth?: NexxusApplicationAuthConfig;
|
|
36
|
+
defaultLimit?: number;
|
|
37
|
+
maxLimit?: number;
|
|
22
38
|
};
|
|
23
|
-
export declare class NexxusApplication extends
|
|
24
|
-
constructor(data:
|
|
39
|
+
export declare class NexxusApplication extends NexxusBuiltinModel<INexxusApplication> {
|
|
40
|
+
constructor(data: INexxusApplication);
|
|
25
41
|
getSchema(): NexxusApplicationSchema;
|
|
26
42
|
getUserDetailSchema(userType?: string): NexxusUserDetailSchema | null;
|
|
43
|
+
hasAuthEnabled(): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Runtime field schema for one of the developer-declared models in this
|
|
46
|
+
* application's `schema` field. Built-in models (user, application) have
|
|
47
|
+
* their own static `getModelSchema` and are not resolved here.
|
|
48
|
+
*/
|
|
49
|
+
getAppModelSchema(modelType: string): NexxusModelDef;
|
|
50
|
+
/**
|
|
51
|
+
* Runtime field schema for Application records. Static because the schema
|
|
52
|
+
* is global — not per-app.
|
|
53
|
+
*/
|
|
54
|
+
static getModelSchema(): NexxusModelDef;
|
|
27
55
|
getAppModelFieldType(modelType: string, fieldPath: string): string | undefined;
|
|
28
56
|
getModelFilterableFields(modelType: string): Set<string>;
|
|
29
57
|
}
|