@ti-engine/web-framework 1.19.1 → 1.20.1

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.
@@ -0,0 +1,125 @@
1
+ export = AuthManager;
2
+ import User = require("#user");
3
+ import type { SettingsAuth } from "#web-server";
4
+ export type TiAuthMethod = string;
5
+ /** @import { SettingsAuth } from "#web-server" */
6
+ /**
7
+ * Enum for specifying the authentication method.
8
+ *
9
+ * @readonly
10
+ * @enum {string}
11
+ * @typedef {string} TiAuthMethod
12
+ */
13
+ declare const authMethodEnum: import("@ti-engine/core/definitions").TiEnumOf<{
14
+ LOCAL: string[];
15
+ OPENID_AZURE: string[];
16
+ OPENID_GOOGLE: string[];
17
+ }>;
18
+ export type TiTokenEndpointAuthMethod = string;
19
+ /**
20
+ * The AuthManager class is used to manage authentication and authorization.
21
+ *
22
+ * @class AuthManager
23
+ * @public
24
+ */
25
+ declare class AuthManager {
26
+ #private;
27
+ /**
28
+ * @constructor
29
+ * @param {SettingsAuth} settings
30
+ */
31
+ constructor(settings: SettingsAuth);
32
+ /**
33
+ * Used to initialize the authentication manager.
34
+ *
35
+ * @method
36
+ * @returns {Promise}
37
+ * @public
38
+ */
39
+ initialize(): Promise<any>;
40
+ /**
41
+ * Used to check whether the specified authentication method is enabled.
42
+ *
43
+ * @method
44
+ * @param {TiAuthMethod} authMethod
45
+ * @returns {boolean}
46
+ * @public
47
+ */
48
+ isAuthEnabled(authMethod: TiAuthMethod): boolean;
49
+ /**
50
+ * Returns the list of currently enabled authentication methods, reflecting any OpenID providers dropped by
51
+ * {@link AuthManager#initialize} for being enabled but unconfigured. Callers (e.g. the login-page renderer)
52
+ * use this to present only the methods a user can actually complete.
53
+ *
54
+ * @method
55
+ * @returns {TiAuthMethod[]}
56
+ * @public
57
+ */
58
+ getEnabledMethods(): TiAuthMethod[];
59
+ /**
60
+ * Used to authenticate a user via the specified authentication method.
61
+ *
62
+ * @method
63
+ * @param {TiAuthMethod} authMethod
64
+ * @param {Object} authDetails
65
+ * @returns {Promise<Object>}
66
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the authentication method is not recognized or enabled.
67
+ * @throws {TiException.E_GEN_NOT_INITIALIZED} If the auth manager was not properly initialized.
68
+ * @public
69
+ */
70
+ authenticate(authMethod: TiAuthMethod, authDetails: Object): Promise<Object>;
71
+ /**
72
+ * Used to set up user authorization according to the specified authentication method.
73
+ *
74
+ * @method
75
+ * @param {TiAuthMethod} authMethod
76
+ * @param {URL} currentUrl
77
+ * @param {Object} oidc
78
+ * @returns {Promise<User>}
79
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the authentication method is not recognized.
80
+ * @public
81
+ */
82
+ authorize(authMethod: TiAuthMethod, currentUrl: URL, oidc: Object): Promise<User>;
83
+ /**
84
+ * Used to get the callback URL for the specified OAuth2 authentication method.
85
+ *
86
+ * @method
87
+ * @param {TiAuthMethod} authMethod
88
+ * @returns {string}
89
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the requested OAuth2 method is not recognized or enabled.
90
+ * @public
91
+ */
92
+ getOAuth2CallbackUrl(authMethod: TiAuthMethod): string;
93
+ /**
94
+ * Used to get the local route path of the callback for the specified OAuth2 authentication method.
95
+ * <br/>
96
+ * A callback can legitimately be configured either as a path or as the full absolute URL registered with the
97
+ * identity provider. The absolute form is what the provider expects as the redirect URI, but it is not a usable
98
+ * Express route pattern, so this reduces whatever is configured to the path the server must actually listen on.
99
+ *
100
+ * @method
101
+ * @param {TiAuthMethod} authMethod
102
+ * @returns {string|null} The route path, or null if the configured callback yields no usable path.
103
+ * @throws {TiException.E_SEC_UNRECOGNIZED_AUTH_METHOD} If the requested OAuth2 method is not recognized or enabled.
104
+ * @public
105
+ */
106
+ getOAuth2CallbackPath(authMethod: TiAuthMethod): string | null;
107
+ /**
108
+ * Reduces a configured OAuth2 callback value to the local route path it corresponds to. Accepts an absolute URL
109
+ * ('https://host/login/azure-callback'), a protocol-relative URL, or a path with or without its leading slash,
110
+ * and strips any query string or fragment. Pure and static; exposed for unit testing.
111
+ * <br/>
112
+ * NOTE: This exists because Express 5 parses a route pattern with path-to-regexp v8, where ':' opens a parameter
113
+ * name — so an absolute URL used verbatim as a route path throws 'Missing parameter name' at startup.
114
+ *
115
+ * @method
116
+ * @static
117
+ * @param {string} callbackUrl
118
+ * @returns {string|null} The route path, or null if no usable path can be derived.
119
+ * @public
120
+ */
121
+ static toCallbackPath(callbackUrl: string): string | null;
122
+ }
123
+ declare namespace AuthManager {
124
+ export { authMethodEnum as authMethod };
125
+ }
@@ -0,0 +1,54 @@
1
+ declare const _exports: {
2
+ ADMIN_ROLE: string;
3
+ isAdminIdentity: typeof isAdminIdentity;
4
+ applyAdminRole: typeof applyAdminRole;
5
+ hasAnyRole: typeof hasAnyRole;
6
+ isAccessAllowed: typeof isAccessAllowed;
7
+ requireRole: typeof requireRole;
8
+ requireAdmin: (request: Object, response: Object, next: Function) => void;
9
+ };
10
+ export = _exports;
11
+ /**
12
+ * Returns `true` if the user matches any entry in the admin allowlist. An entry may match the user's `userID`,
13
+ * `username`, or `email` (case-insensitive).
14
+ *
15
+ * @param {Object} user A session user (`{ userID, username, email, roles, ... }`).
16
+ * @param {string[]} admins The configured allowlist of admin identifiers.
17
+ * @returns {boolean}
18
+ */
19
+ declare function isAdminIdentity(user: Object, admins: string[]): boolean;
20
+ /**
21
+ * Adds the `admin` role to the session user (additively, no duplicates) when the user is in the allowlist.
22
+ * Safe to call with an empty/missing allowlist or session — it is then a no-op. Returns the session for chaining.
23
+ *
24
+ * @param {Object} session
25
+ * @param {string[]} [admins]
26
+ * @returns {Object} The (possibly modified) session.
27
+ */
28
+ declare function applyAdminRole(session: Object, admins?: string[]): Object;
29
+ /**
30
+ * @param {Object} session
31
+ * @param {Array<string|number>} roles
32
+ * @returns {boolean} `true` if the session user holds any of the given roles.
33
+ */
34
+ declare function hasAnyRole(session: Object, roles: Array<string | number>): boolean;
35
+ /**
36
+ * Pure access decision for a resource (e.g. an HTML fragment) that declares a set of required roles. A resource with
37
+ * no required roles (`null` / `undefined` / empty) is public — any (authenticated) user may access it; otherwise the
38
+ * user must hold at least one of the required roles. Roles are treated opaquely, so this works equally for numeric
39
+ * application role codes and the string `admin` role — there is no implicit hierarchy (an `admin`-gated resource is
40
+ * reachable only by holders of the `admin` role, never by a high numeric role). Backs {@link TiWebAppManager#verifyAccess}.
41
+ *
42
+ * @param {Array<string|number>} [requiredRoles] The roles permitted to access the resource; empty/absent = public.
43
+ * @param {Array<string|number>} [userRoles] The roles held by the current session user.
44
+ * @returns {boolean}
45
+ */
46
+ declare function isAccessAllowed(requiredRoles?: Array<string | number>, userRoles?: Array<string | number>): boolean;
47
+ /**
48
+ * Express middleware factory that admits a request only if its session user holds at least one of the given roles.
49
+ * Responds `401` when unauthenticated (no session user) and `403` when authenticated but lacking the role.
50
+ *
51
+ * @param {...(string|number)} roles
52
+ * @returns {(request: Object, response: Object, next: Function) => void}
53
+ */
54
+ declare function requireRole(...roles: (string | number)[]): (request: Object, response: Object, next: Function) => void;
@@ -0,0 +1,73 @@
1
+ export = ConfigChangeNotifier;
2
+ export type ConfigChangeEvent = {
3
+ changeSetID: string;
4
+ /**
5
+ * The configuration documents affected by the change.
6
+ */
7
+ configKeys: string[];
8
+ /**
9
+ * Who committed the change.
10
+ */
11
+ adminID: string;
12
+ /**
13
+ * ISO timestamp.
14
+ */
15
+ timestamp: string;
16
+ };
17
+ /**
18
+ * @typedef {Object} ConfigChangeEvent
19
+ * @property {string} changeSetID
20
+ * @property {string[]} configKeys The configuration documents affected by the change.
21
+ * @property {string} adminID Who committed the change.
22
+ * @property {string} timestamp ISO timestamp.
23
+ */
24
+ declare const CONFIG_CHANGED = "config:changed";
25
+ /**
26
+ * Notifies subscribers that configuration changed, so they can react (e.g. invalidate an in-memory cache, or push a
27
+ * live update to an admin UI). This is the **in-process** implementation of a deliberately transport-agnostic
28
+ * contract — `publish(event)` (fire-and-forget) and `subscribe(listener) → unsubscribe`.
29
+ *
30
+ * **Designed for an eventual switch to a reusable core pub/sub.** Cross-instance propagation is out of scope for v1
31
+ * (the store-backed model already makes a committed change visible to every instance via the shared Redis cache;
32
+ * this emitter exists to invalidate optional *in-memory* caches and drive live UI within a process). When a Redis
33
+ * (or other) pub/sub primitive lands in `@ti-engine/core`, a drop-in implementation of this same contract can be
34
+ * provided and injected into {@link ConfigService} — no change to publishers or subscribers. To keep that swap
35
+ * behavior-safe, **delivery here is already asynchronous** (matching cross-instance transports); subscribers must
36
+ * not assume synchronous delivery, and the event payload is plain serializable JSON so it survives a wire transport.
37
+ *
38
+ * @class ConfigChangeNotifier
39
+ * @public
40
+ */
41
+ declare class ConfigChangeNotifier {
42
+ #private;
43
+ constructor();
44
+ /**
45
+ * Publishes a configuration-change event to all subscribers. Fire-and-forget; delivery is asynchronous.
46
+ *
47
+ * @method
48
+ * @param {ConfigChangeEvent} event
49
+ * @returns {ConfigChangeEvent} The (frozen) event that will be delivered.
50
+ * @public
51
+ */
52
+ publish(event: ConfigChangeEvent): ConfigChangeEvent;
53
+ /**
54
+ * Subscribes a listener to configuration-change events.
55
+ *
56
+ * @method
57
+ * @param {(event: ConfigChangeEvent) => void} listener
58
+ * @returns {() => void} An unsubscribe function.
59
+ * @public
60
+ */
61
+ subscribe(listener: (event: ConfigChangeEvent) => void): () => void;
62
+ /**
63
+ * @method
64
+ * @returns {number} The current number of subscribers.
65
+ * @public
66
+ */
67
+ subscriberCount(): number;
68
+ }
69
+ declare namespace ConfigChangeNotifier {
70
+ export { instance };
71
+ export { CONFIG_CHANGED };
72
+ }
73
+ declare const instance: ConfigChangeNotifier;
@@ -0,0 +1,149 @@
1
+ export = ConfigRegistry;
2
+ export type ConfigValidationIssue = {
3
+ /**
4
+ * A JSON pointer / data path to the offending value (e.g. ".competencies.E1-1.name"), or "".
5
+ */
6
+ path: string;
7
+ /**
8
+ * Human-readable problem description.
9
+ */
10
+ message: string;
11
+ /**
12
+ * "schema" for JSON-Schema failures, or a validator-supplied code (default "semantic").
13
+ */
14
+ code: string;
15
+ /**
16
+ * Optional structured details (e.g. ajv params).
17
+ */
18
+ params?: Object;
19
+ };
20
+ export type ValidatorContext = {
21
+ /**
22
+ * Resolves the *pending* value of `key` when it is part of the
23
+ * current edit batch, otherwise its current committed value. Lets a validator check a sibling document's
24
+ * post-edit state — but calling this for the document being validated itself just returns the same incoming
25
+ * value already passed as the validator's first argument, not its prior state.
26
+ */
27
+ getConfig: (key: string) => Promise<any>;
28
+ /**
29
+ * Always resolves the current committed value of `key`,
30
+ * even when `key` is the document currently under validation. Use this to compare a document against its own
31
+ * previous state (e.g. detecting an edit that should have bumped a version marker).
32
+ */
33
+ getStoredConfig: (key: string) => Promise<any>;
34
+ };
35
+ export type SemanticValidator = (value: Object, context: ValidatorContext) => ConfigValidationIssue[] | Promise<ConfigValidationIssue[]>;
36
+ /**
37
+ * @typedef {Object} ConfigValidationIssue
38
+ * @property {string} path A JSON pointer / data path to the offending value (e.g. ".competencies.E1-1.name"), or "".
39
+ * @property {string} message Human-readable problem description.
40
+ * @property {string} code "schema" for JSON-Schema failures, or a validator-supplied code (default "semantic").
41
+ * @property {Object} [params] Optional structured details (e.g. ajv params).
42
+ */
43
+ /**
44
+ * The cross-document read context passed to every {@link SemanticValidator}, built fresh for each
45
+ * {@link ConfigService#applyEdits} call.
46
+ *
47
+ * @typedef {Object} ValidatorContext
48
+ * @property {(key: string) => Promise<*>} getConfig Resolves the *pending* value of `key` when it is part of the
49
+ * current edit batch, otherwise its current committed value. Lets a validator check a sibling document's
50
+ * post-edit state — but calling this for the document being validated itself just returns the same incoming
51
+ * value already passed as the validator's first argument, not its prior state.
52
+ * @property {(key: string) => Promise<*>} getStoredConfig Always resolves the current committed value of `key`,
53
+ * even when `key` is the document currently under validation. Use this to compare a document against its own
54
+ * previous state (e.g. detecting an edit that should have bumped a version marker).
55
+ */
56
+ /**
57
+ * @typedef {(value: Object, context: ValidatorContext) => ConfigValidationIssue[]|Promise<ConfigValidationIssue[]>} SemanticValidator
58
+ * A semantic validator receives the candidate value and a {@link ValidatorContext} and returns the issues it found
59
+ * (empty array = OK). May be async.
60
+ */
61
+ /**
62
+ * Registry of editable configuration *documents* and the gate that validates a candidate value against a
63
+ * document's JSON Schema (ajv) plus its semantic validators. The framework stays domain-agnostic: an application
64
+ * registers its config documents (schemas, validators, defaults, editor metadata) at startup; this component knows
65
+ * only "validated, versioned JSON documents". Validation must pass *before* {@link ConfigStore#saveChangeSet}.
66
+ *
67
+ * @class ConfigRegistry
68
+ * @public
69
+ */
70
+ declare class ConfigRegistry {
71
+ #private;
72
+ constructor();
73
+ /**
74
+ * Registers an editable configuration document.
75
+ *
76
+ * @method
77
+ * @param {string} configKey Stable identifier for the document (also the ConfigStore key).
78
+ * @param {Object} definition
79
+ * @param {Object} definition.schema A JSON Schema for the document.
80
+ * @param {SemanticValidator[]} [definition.validators] Cross-cutting/semantic checks beyond the schema.
81
+ * @param {Object} [definition.defaultValue] The bootstrap default (seeds an empty store).
82
+ * @param {Object} [definition.metadata] Editor metadata (label, editor type, group, risk class, …).
83
+ * @returns {ConfigRegistry} this (chainable)
84
+ * @throws {TiException.E_WEB_INVALID_REQUEST_PARAMETERS} On missing key/schema.
85
+ * @public
86
+ */
87
+ register(configKey: string, definition: {
88
+ schema: Object;
89
+ validators?: SemanticValidator[];
90
+ defaultValue?: Object;
91
+ metadata?: Object;
92
+ }): ConfigRegistry;
93
+ /**
94
+ * Adds a schema that is referenced (via `$ref`/`$id`) by document schemas but is not itself a document.
95
+ *
96
+ * @method
97
+ * @param {Object} schema
98
+ * @returns {ConfigRegistry} this (chainable)
99
+ * @public
100
+ */
101
+ addSchema(schema: Object): ConfigRegistry;
102
+ /**
103
+ * @method
104
+ * @param {string} configKey
105
+ * @returns {boolean}
106
+ * @public
107
+ */
108
+ has(configKey: string): boolean;
109
+ /**
110
+ * @method
111
+ * @returns {string[]} All registered configuration keys.
112
+ * @public
113
+ */
114
+ list(): string[];
115
+ /**
116
+ * @method
117
+ * @param {string} configKey
118
+ * @returns {Object|undefined} The editor metadata registered for the document.
119
+ * @public
120
+ */
121
+ metadataFor(configKey: string): Object | undefined;
122
+ /**
123
+ * @method
124
+ * @param {string} configKey
125
+ * @returns {Object|undefined} The bootstrap default value registered for the document.
126
+ * @public
127
+ */
128
+ getDefault(configKey: string): Object | undefined;
129
+ /**
130
+ * Validates a candidate value for a registered document: JSON Schema first, then the semantic validators.
131
+ * Resolves with `{ valid, errors }` (errors is an array of {@link ConfigValidationIssue}).
132
+ *
133
+ * @method
134
+ * @param {string} configKey
135
+ * @param {Object} value
136
+ * @param {ValidatorContext} [context] Passed to each semantic validator.
137
+ * @returns {Promise<{valid: boolean, errors: ConfigValidationIssue[]}>}
138
+ * @throws {TiException.E_WEB_INVALID_REQUEST_PARAMETERS} If the document is not registered.
139
+ * @public
140
+ */
141
+ validate(configKey: string, value: Object, context?: ValidatorContext): Promise<{
142
+ valid: boolean;
143
+ errors: ConfigValidationIssue[];
144
+ }>;
145
+ }
146
+ declare namespace ConfigRegistry {
147
+ export { instance };
148
+ }
149
+ declare const instance: ConfigRegistry;
@@ -0,0 +1,218 @@
1
+ export = ConfigService;
2
+ import type ConfigChangeNotifier from "#config-change-notifier";
3
+ import type ConfigRegistry from "#config-registry";
4
+ import type ConfigStore from "#config-store";
5
+ /** @import ConfigChangeNotifier from "#config-change-notifier" */
6
+ /** @import ConfigRegistry from "#config-registry" */
7
+ /** @import ConfigStore from "#config-store" */
8
+ /**
9
+ * Orchestrates validated, versioned configuration edits on top of {@link ConfigStore} and {@link ConfigRegistry}.
10
+ *
11
+ * Two layers:
12
+ * - **Document level** — {@link ConfigService#applyEdits}: validate every affected document (schema + semantic,
13
+ * with a cross-document {@link ValidatorContext} whose `getConfig` sees the *pending* values of the same edit —
14
+ * letting a validator check a sibling document's post-edit state — while `getStoredConfig` always returns the
15
+ * committed value, even for the document currently under validation) and, only if all pass, commit them as one
16
+ * change-set. Validation failures return `{ ok:false, errors }` and write nothing; a version conflict from the
17
+ * store surfaces as a rejection.
18
+ * - **Entity level** — composite editors registered with `compose(docs)→view` / `decompose(edited, docs)→{key:value}`,
19
+ * so the UI edits a domain entity (e.g. a "competency") that is projected from, and scattered back into, several
20
+ * documents. {@link ConfigService#saveEditorEdit} decomposes the edit and routes it through `applyEdits`.
21
+ *
22
+ * @class ConfigService
23
+ * @public
24
+ */
25
+ declare class ConfigService {
26
+ #private;
27
+ /**
28
+ * @constructor
29
+ * @param {Object} [options]
30
+ * @param {ConfigStore} [options.store] Defaults to the ConfigStore singleton.
31
+ * @param {ConfigRegistry} [options.registry] Defaults to the ConfigRegistry singleton.
32
+ * @param {ConfigChangeNotifier} [options.notifier] Defaults to the ConfigChangeNotifier singleton.
33
+ */
34
+ constructor(options?: {
35
+ store?: ConfigStore;
36
+ registry?: ConfigRegistry;
37
+ notifier?: ConfigChangeNotifier;
38
+ });
39
+ /**
40
+ * Validates and commits a set of document edits atomically. Each edit: `{ configKey, value, expectedVersion }`.
41
+ *
42
+ * @method
43
+ * @param {Array<{configKey: string, value: Object, expectedVersion: number}>} edits
44
+ * @param {Object} meta
45
+ * @param {string} meta.adminID
46
+ * @param {string} [meta.note]
47
+ * @returns {Promise<{ok: true, changeSetID: string, versions: Object<string, number>} | {ok: false, errors: Object<string, Array>}>}
48
+ * @public
49
+ */
50
+ applyEdits(edits: Array<{
51
+ configKey: string;
52
+ value: Object;
53
+ expectedVersion: number;
54
+ }>, meta: {
55
+ adminID: string;
56
+ note?: string;
57
+ }): Promise<{
58
+ ok: true;
59
+ changeSetID: string;
60
+ versions: Record<string, number>;
61
+ } | {
62
+ ok: false;
63
+ errors: Record<string, any[]>;
64
+ }>;
65
+ /**
66
+ * Registers a composite editor over one or more documents.
67
+ *
68
+ * @method
69
+ * @param {string} editorKey
70
+ * @param {Object} definition
71
+ * @param {string[]} definition.documents The configKeys this editor spans.
72
+ * @param {(configs: Object) => *} definition.compose Maps `{ [configKey]: value }` → a view for the UI.
73
+ * @param {(editedView: *, currentDocs: Object) => Object.<string, Object>} definition.decompose Maps `(editedView, currentDocs)` → the
74
+ * full new values for the documents that changed (`{ [configKey]: newValue }`).
75
+ * @param {Object} [definition.metadata]
76
+ * @returns {ConfigService} this (chainable)
77
+ * @throws {TiException.E_WEB_INVALID_REQUEST_PARAMETERS}
78
+ * @public
79
+ */
80
+ registerEditor(editorKey: string, definition: {
81
+ documents: string[];
82
+ compose: (configs: Object) => any;
83
+ decompose: (editedView: any, currentDocs: Object) => Record<string, Object>;
84
+ metadata?: Object;
85
+ }): ConfigService;
86
+ /**
87
+ * @method
88
+ * @param {string} editorKey
89
+ * @returns {boolean}
90
+ * @public
91
+ */
92
+ hasEditor(editorKey: string): boolean;
93
+ /**
94
+ * @method
95
+ * @returns {string[]}
96
+ * @public
97
+ */
98
+ listEditors(): string[];
99
+ /**
100
+ * Loads the editor's documents and composes them into a view. Returns the view plus the current per-document
101
+ * versions, which the client must echo back on save for optimistic locking.
102
+ *
103
+ * @method
104
+ * @param {string} editorKey
105
+ * @returns {Promise<{rows: *, versions: Object<string, number>}>}
106
+ * @public
107
+ */
108
+ composeView(editorKey: string): Promise<{
109
+ rows: any;
110
+ versions: Record<string, number>;
111
+ }>;
112
+ /**
113
+ * Applies an edit made against a composite editor: decompose the edited view into per-document new values, then
114
+ * route through {@link ConfigService#applyEdits} (validate-all → atomic change-set). `expectedVersions` should be
115
+ * the versions returned by {@link ConfigService#composeView} when the edit started.
116
+ *
117
+ * @method
118
+ * @param {string} editorKey
119
+ * @param {*} editedView
120
+ * @param {Object} meta
121
+ * @param {Object<string, number>} [expectedVersions]
122
+ * @returns {Promise<Object>} The {@link ConfigService#applyEdits} result (or `{ ok:true, changeSetID:null }` if nothing changed).
123
+ * @public
124
+ */
125
+ saveEditorEdit(editorKey: string, editedView: any, meta: Object, expectedVersions?: Record<string, number>): Promise<Object>;
126
+ /**
127
+ * Restores a prior change-set through the validated path: rebuild edits from the change-set's historic snapshots
128
+ * and route them through {@link ConfigService#applyEdits} — so the restore is **re-validated against the current
129
+ * schemas/validators** (a snapshot valid when written may be invalid now) and emits `config:changed`. Returns the
130
+ * `applyEdits` result (`{ ok:false, errors }` if a snapshot no longer validates; nothing is written then).
131
+ *
132
+ * @method
133
+ * @param {string} changeSetID
134
+ * @param {Object} meta
135
+ * @param {string} meta.adminID
136
+ * @param {string} [meta.note]
137
+ * @returns {Promise<Object>}
138
+ * @public
139
+ */
140
+ restoreChangeSet(changeSetID: string, meta: {
141
+ adminID: string;
142
+ note?: string;
143
+ }): Promise<Object>;
144
+ /**
145
+ * @method
146
+ * @param {string} configKey
147
+ * @returns {Promise<Object|null>} The current envelope for a configuration document.
148
+ * @public
149
+ */
150
+ getCurrent(configKey: string): Promise<Object | null>;
151
+ /**
152
+ * @method
153
+ * @param {string} configKey
154
+ * @returns {Promise<Array<Object>>} The document's version history (ascending), each a full snapshot entry.
155
+ * @public
156
+ */
157
+ getHistory(configKey: string): Promise<Array<Object>>;
158
+ /**
159
+ * @method
160
+ * @param {string} changeSetID
161
+ * @returns {Promise<Object|null>} A single change-set record.
162
+ * @public
163
+ */
164
+ getChange(changeSetID: string): Promise<Object | null>;
165
+ /**
166
+ * @method
167
+ * @returns {Promise<Array<Object>>} The cross-document audit feed (change-sets, most-recent first).
168
+ * @public
169
+ */
170
+ listChanges(): Promise<Array<Object>>;
171
+ /**
172
+ * Builds a downloadable snapshot of the current live configuration — for every registered document, its repo file
173
+ * `path` (from registration metadata, if provided), current `version`, and `value`. This is the one-way export
174
+ * *out* of the store; an admin downloads it and commits the files to git. The store remains the live truth.
175
+ *
176
+ * @method
177
+ * @param {Object} [meta]
178
+ * @param {string} [meta.adminID]
179
+ * @returns {Promise<{exportedAt: string, exportedBy: (string|null), documents: Array<{configKey: string, path: (string|null), version: number, value: Object}>}>}
180
+ * @public
181
+ */
182
+ exportBundle(meta?: {
183
+ adminID?: string;
184
+ }): Promise<{
185
+ exportedAt: string;
186
+ exportedBy: (string | null);
187
+ documents: Array<{
188
+ configKey: string;
189
+ path: (string | null);
190
+ version: number;
191
+ value: Object;
192
+ }>;
193
+ }>;
194
+ /**
195
+ * Seeds a document's default value into the store only if it has never been written (idempotent bootstrap).
196
+ * Used by an application to bring its file defaults into the store at startup before serving live config.
197
+ *
198
+ * @method
199
+ * @param {string} configKey
200
+ * @param {Object} defaultValue
201
+ * @returns {Promise<Object>} The current envelope.
202
+ * @public
203
+ */
204
+ seedDefault(configKey: string, defaultValue: Object): Promise<Object>;
205
+ /**
206
+ * Subscribes a listener to `config:changed` events (delegates to the change notifier). Returns an unsubscribe fn.
207
+ *
208
+ * @method
209
+ * @param {(configs: Object) => void} listener
210
+ * @returns {() => void}
211
+ * @public
212
+ */
213
+ onConfigChanged(listener: (configs: Object) => void): () => void;
214
+ }
215
+ declare namespace ConfigService {
216
+ export { instance };
217
+ }
218
+ declare const instance: ConfigService;