@asaidimu/hestia 1.0.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/README.md +139 -0
- package/auth/store.ts +75 -0
- package/auth/types.ts +38 -0
- package/blobs/store.ts +208 -0
- package/blobs/types.ts +28 -0
- package/collections/store.ts +59 -0
- package/collections/types.ts +11 -0
- package/container.ts +95 -0
- package/core/client.ts +247 -0
- package/core/collection.ts +121 -0
- package/core/errors.ts +55 -0
- package/core/pager.ts +159 -0
- package/core/types.ts +279 -0
- package/index.ts +62 -0
- package/package.json +43 -0
- package/system/api-keys/store.ts +70 -0
- package/system/api-keys/types.ts +29 -0
- package/system/capabilities/store.ts +23 -0
- package/system/capabilities/types.ts +1 -0
- package/system/identity/store.ts +90 -0
- package/system/identity/types.ts +31 -0
- package/system/logs/store.ts +147 -0
- package/system/logs/types.ts +38 -0
- package/system/policies/store.ts +74 -0
- package/system/policies/types.ts +43 -0
- package/test-setup.ts +53 -0
- package/utils/index.ts +1 -0
- package/utils/pager.ts +226 -0
- package/vitest.config.ts +8 -0
package/core/types.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { QueryFilter, SortConfiguration } from "@asaidimu/query";
|
|
2
|
+
|
|
3
|
+
// Internal metadata managed by Anansi
|
|
4
|
+
interface DocumentMetadata {
|
|
5
|
+
checksum: string; // Integrity hash of the document
|
|
6
|
+
created: string; // Timestamp (often as a numeric string / nanoseconds)
|
|
7
|
+
updated: string; // Timestamp (often as a numeric string / nanoseconds)
|
|
8
|
+
version: number; // Optimistic locking version (increments on each write)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// The base envelope applied to every single document in the system
|
|
12
|
+
interface BaseDocument {
|
|
13
|
+
_id_: string;
|
|
14
|
+
_metadata_: DocumentMetadata;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Generic Document type: base envelope + your custom properties T
|
|
18
|
+
export type Document<T extends Record<string, any>> = BaseDocument & T;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Provides comprehensive pagination information for a collection of records.
|
|
22
|
+
*/
|
|
23
|
+
export type PaginationInfo = {
|
|
24
|
+
/** The current page number (1-based). */
|
|
25
|
+
number: number;
|
|
26
|
+
|
|
27
|
+
/** The maximum number of items requested per page. */
|
|
28
|
+
size: number;
|
|
29
|
+
|
|
30
|
+
/** The number of items in the current page */
|
|
31
|
+
count: number;
|
|
32
|
+
|
|
33
|
+
/** The total count of all items across all pages. */
|
|
34
|
+
total: number;
|
|
35
|
+
|
|
36
|
+
/** The total number of available pages. */
|
|
37
|
+
pages: number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Represents a single paginated response containing a list of records and pagination details.
|
|
42
|
+
* @template T The type of the records in the page.
|
|
43
|
+
*/
|
|
44
|
+
export interface Page<T extends Record<string, any>> {
|
|
45
|
+
/** The array of records for the current page. */
|
|
46
|
+
data: Document<T>[];
|
|
47
|
+
|
|
48
|
+
/** Indicates if the data query is currently loading. */
|
|
49
|
+
loading: boolean;
|
|
50
|
+
|
|
51
|
+
/** An error object if the query failed, otherwise `undefined`. */
|
|
52
|
+
error?: any | undefined;
|
|
53
|
+
|
|
54
|
+
/** Pagination metadata providing details about the current page and total collection. */
|
|
55
|
+
page: PaginationInfo;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface PagerRefreshOptions {
|
|
59
|
+
sort?: SortConfiguration | SortConfiguration[];
|
|
60
|
+
filter?: QueryFilter;
|
|
61
|
+
delay?: number;
|
|
62
|
+
size?: number;
|
|
63
|
+
page?: number;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Represents the complete state and control mechanisms for consuming paginated data.
|
|
67
|
+
* @template T The type of the records in the page (`TableRowData` or an extension).
|
|
68
|
+
*/
|
|
69
|
+
export interface PagedData<T extends Record<string, any>> {
|
|
70
|
+
id: () => string
|
|
71
|
+
|
|
72
|
+
/** The current paginated data, or `undefined` if not found or still loading. */
|
|
73
|
+
page: () => Page<T>;
|
|
74
|
+
|
|
75
|
+
/** Function to fetch a specific page of data.
|
|
76
|
+
* @param page The 1-based page number to fetch.
|
|
77
|
+
*/
|
|
78
|
+
navigate: (page: number) => Promise<void>;
|
|
79
|
+
|
|
80
|
+
sort: (sort: SortConfiguration<T> | SortConfiguration<T>[]) => Promise<void>;
|
|
81
|
+
filter: (filter?: QueryFilter<T>) => Promise<void>;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Function to change the page size of the data
|
|
85
|
+
*/
|
|
86
|
+
resize: (size: number, page: number) => Promise<void>;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Function to force a refresh of the current page data, optionally with a delay.
|
|
90
|
+
* @param delay Optional delay in milliseconds before the refresh operation starts.
|
|
91
|
+
*/
|
|
92
|
+
refresh: (opts?: PagerRefreshOptions) => Promise<void>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Subscribes to page changes.
|
|
96
|
+
* The callback is invoked immediately with the current page, and then on every
|
|
97
|
+
* subsequent change (navigation, resize, SSE patch, refresh).
|
|
98
|
+
*
|
|
99
|
+
* @param listener A function that receives the current Page<T>.
|
|
100
|
+
* @returns An unsubscribe function.
|
|
101
|
+
*/
|
|
102
|
+
subscribe: (listener: (page: Page<T>) => void) => () => void;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Invalidates the pagination controller, cleaning up any cached resources
|
|
106
|
+
* (e.g., store subscriptions, in‑flight requests) and removing internal state.
|
|
107
|
+
* After calling `invalidate()`, the controller should no longer be used.
|
|
108
|
+
*
|
|
109
|
+
* This is useful when the component using the pager is unmounted or when
|
|
110
|
+
* you need to reset the pagination state completely.
|
|
111
|
+
*/
|
|
112
|
+
invalidate: () => void;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Represents an event emitted by the store, typically for notifications or state changes.
|
|
117
|
+
*/
|
|
118
|
+
export interface StoreEvent {
|
|
119
|
+
/** The scope of the event, indicating its type and context. Custom scopes are allowed. */
|
|
120
|
+
scope: string;
|
|
121
|
+
/** Optional payload carrying data related to the event. */
|
|
122
|
+
payload?: any;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Defines the core interface for interacting with a remote data store.
|
|
127
|
+
* @template T The type of the records managed by the store, extending Record.
|
|
128
|
+
* @template TFindOptions Options for the find operation.
|
|
129
|
+
* @template TReadOptions Options for the read operation.
|
|
130
|
+
* @template TListOptions Options for the list operation.
|
|
131
|
+
* @template TPageOptions Options for the paging operation.
|
|
132
|
+
* @template TDeleteOptions Options for the delete operation.
|
|
133
|
+
* @template TUpdateOptions Options for the update operation.
|
|
134
|
+
* @template TCreateOptions Options for the create operation.
|
|
135
|
+
* @template TUploadOptions Options for the upload operation.
|
|
136
|
+
* @template TStreamOptions Options for the stream operation.
|
|
137
|
+
*/
|
|
138
|
+
export interface DocumentStore<
|
|
139
|
+
T extends Record<string, any>,
|
|
140
|
+
TFindOptions = Record<string, unknown>,
|
|
141
|
+
TReadOptions = Record<string, unknown>,
|
|
142
|
+
TListOptions = Record<string, unknown>,
|
|
143
|
+
TPageOptions = Record<string, unknown>,
|
|
144
|
+
TDeleteOptions = Record<string, unknown>,
|
|
145
|
+
TUpdateOptions = Record<string, unknown>,
|
|
146
|
+
TCreateOptions = Record<string, unknown>,
|
|
147
|
+
TUploadOptions = Record<string, unknown>,
|
|
148
|
+
TStreamOptions = Record<string, unknown>,
|
|
149
|
+
> {
|
|
150
|
+
/**
|
|
151
|
+
* Finds records based on provided options, returning a paginated result.
|
|
152
|
+
* @param options The options for the find operation.
|
|
153
|
+
* @returns A promise that resolves to a Page of records.
|
|
154
|
+
*/
|
|
155
|
+
find: (options: TFindOptions) => Promise<Page<T>>;
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Reads a single record by its identifier or other read options.
|
|
159
|
+
* @param options The options for the read operation, typically including an ID.
|
|
160
|
+
* @returns A promise that resolves to the record or undefined if not found.
|
|
161
|
+
*/
|
|
162
|
+
read: (options: TReadOptions) => Promise<Document<T> | undefined>;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Lists records based on provided options, returning a paginated result.
|
|
166
|
+
* @param options The options for the list operation.
|
|
167
|
+
* @returns A promise that resolves to a Page of records.
|
|
168
|
+
*/
|
|
169
|
+
list: (options: TListOptions) => Promise<Page<T>>;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Deletes a record based on provided options, typically including an ID.
|
|
173
|
+
* @param options The options for the delete operation.
|
|
174
|
+
* @returns A promise that resolves when the deletion is complete.
|
|
175
|
+
*/
|
|
176
|
+
delete: (options: TDeleteOptions) => Promise<void>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Updates an existing record.
|
|
180
|
+
* @param props An object containing the ID of the record to update, the partial data, and optional update options.
|
|
181
|
+
* @returns A promise that resolves to the updated record or undefined if not found.
|
|
182
|
+
*/
|
|
183
|
+
update: (props: {
|
|
184
|
+
data: Partial<T>;
|
|
185
|
+
options?: TUpdateOptions;
|
|
186
|
+
}) => Promise<Document<T> | undefined>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Creates a new record.
|
|
190
|
+
* @param props An object containing the data for the new record and optional create options.
|
|
191
|
+
* @returns A promise that resolves to the newly created record or undefined if creation failed.
|
|
192
|
+
*/
|
|
193
|
+
create: (props: {
|
|
194
|
+
data: Partial<T>;
|
|
195
|
+
options?: TCreateOptions;
|
|
196
|
+
}) => Promise<Document<T> | undefined>;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Uploads a file associated with a record. Optionally updates the record with upload details.
|
|
200
|
+
* @param props An object containing the file to upload and optional upload options.
|
|
201
|
+
* @returns A promise that resolves to the updated record after upload or undefined if upload failed.
|
|
202
|
+
*/
|
|
203
|
+
upload: (props: {
|
|
204
|
+
file: File;
|
|
205
|
+
options?: TUploadOptions;
|
|
206
|
+
}) => Promise<Document<T> | undefined>;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Subscribes to store events for a given scope.
|
|
210
|
+
* @param scope The event scope to subscribe to (e.g., 'todos:created:success', '*').
|
|
211
|
+
* @param callback The function to call when an event matching the scope is received.
|
|
212
|
+
* @returns A promise that resolves to an unsubscribe function. Call this function to stop receiving events.
|
|
213
|
+
*/
|
|
214
|
+
subscribe(
|
|
215
|
+
scope: string,
|
|
216
|
+
callback: (event: StoreEvent) => void,
|
|
217
|
+
): Promise<() => void>;
|
|
218
|
+
/**
|
|
219
|
+
* Notifies the store of an event, which can then be broadcast to subscribers.
|
|
220
|
+
* @param event The StoreEvent to notify.
|
|
221
|
+
* @returns A promise that resolves when the notification has been processed.
|
|
222
|
+
*/
|
|
223
|
+
notify: (event: StoreEvent) => Promise<void>;
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Establishes a stream of records based on provided options.
|
|
227
|
+
* @param options Options for configuring the stream (e.g., batch size, delay, filters).
|
|
228
|
+
* @param onStreamChange A callback function that is called when the stream's status changes.
|
|
229
|
+
* @returns An object containing the async iterable stream, a cancel function, and a status getter.
|
|
230
|
+
*/
|
|
231
|
+
stream: (
|
|
232
|
+
options: TStreamOptions,
|
|
233
|
+
onStreamChange: () => void,
|
|
234
|
+
) => {
|
|
235
|
+
/** An async iterable that yields records as they become available in the stream. */
|
|
236
|
+
stream: () => AsyncIterable<Document<T>>;
|
|
237
|
+
/** A function to call to cancel the ongoing stream. */
|
|
238
|
+
cancel: () => void;
|
|
239
|
+
/** A getter function that returns the current status of the stream ('active', 'cancelled', or 'completed'). */
|
|
240
|
+
status: () => "active" | "cancelled" | "completed";
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Creates a paginated data controller for this store.
|
|
245
|
+
*
|
|
246
|
+
* @param options - The pagination options, whose type is defined by the
|
|
247
|
+
* store's `TPageOptions` generic parameter. This lets you
|
|
248
|
+
* pass any configuration (e.g., filters, sorting, custom
|
|
249
|
+
* pagination metadata) that your store implementation needs.
|
|
250
|
+
* @returns A `PagedData<T>` controller that manages loading, navigation,
|
|
251
|
+
* resizing, and refresh.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* // Define your store with a custom TPageOptions type
|
|
255
|
+
* interface MyTodoStore extends DocumentStore<
|
|
256
|
+
* Todo,
|
|
257
|
+
* FindOptions,
|
|
258
|
+
* ReadOptions,
|
|
259
|
+
* ListOptions,
|
|
260
|
+
* { page: number; size: number; sort?: string; filter?: string }
|
|
261
|
+
* > {}
|
|
262
|
+
*
|
|
263
|
+
* // Use it
|
|
264
|
+
* const todosPaged = myTodoStore.page({
|
|
265
|
+
* page: 1,
|
|
266
|
+
* size: 10,
|
|
267
|
+
* sort: 'createdAt',
|
|
268
|
+
* filter: 'done:false'
|
|
269
|
+
* });
|
|
270
|
+
*
|
|
271
|
+
* // Get current page state
|
|
272
|
+
* const { data, loading, page } = todosPaged.page();
|
|
273
|
+
* console.log(`Page ${page.number} of ${page.pages}`);
|
|
274
|
+
*
|
|
275
|
+
* // Navigate
|
|
276
|
+
* await todosPaged.navigate(2);
|
|
277
|
+
*/
|
|
278
|
+
page(options: TPageOptions): Promise<PagedData<T>>;
|
|
279
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Core
|
|
2
|
+
export { HestiaNetworkClient, HestiaResponse } from "./core/client"
|
|
3
|
+
export type { IdentityProvider } from "./core/client"
|
|
4
|
+
export { HestiaCollection } from "./core/collection"
|
|
5
|
+
export { toSystemError } from "./core/errors"
|
|
6
|
+
|
|
7
|
+
// Generic types
|
|
8
|
+
export type { Document, Page, PagedData, PaginationInfo, StoreEvent } from "./core/types"
|
|
9
|
+
|
|
10
|
+
export type { PageOptions } from "./core/pager"
|
|
11
|
+
|
|
12
|
+
// Auth
|
|
13
|
+
export { HestiaAuth } from "./auth/store"
|
|
14
|
+
export type { TokenPair, LoginResult, ServerHealth, LoginRequest, RegisterRequest } from "./auth/types"
|
|
15
|
+
|
|
16
|
+
// System: identity
|
|
17
|
+
export { HestiaUsers } from "./system/identity/store"
|
|
18
|
+
export type {
|
|
19
|
+
UserData,
|
|
20
|
+
UserIdentity,
|
|
21
|
+
CreateUserRequest,
|
|
22
|
+
UpdateUserRequest,
|
|
23
|
+
} from "./system/identity/types"
|
|
24
|
+
|
|
25
|
+
// System: api-keys
|
|
26
|
+
export { HestiaKeyStore } from "./system/api-keys/store"
|
|
27
|
+
export type { APIKey, APIKeyWithSecret, CreateKeyRequest, UpdateKeyRequest } from "./system/api-keys/types"
|
|
28
|
+
|
|
29
|
+
// System: policies
|
|
30
|
+
export { HestiaPolicies } from "./system/policies/store"
|
|
31
|
+
export type {
|
|
32
|
+
PolicyOperation,
|
|
33
|
+
PolicyRule,
|
|
34
|
+
UpsertOperationRequest,
|
|
35
|
+
UpsertRuleRequest,
|
|
36
|
+
ValidateRuleRequest,
|
|
37
|
+
ValidateRuleResult,
|
|
38
|
+
ReloadResult,
|
|
39
|
+
} from "./system/policies/types"
|
|
40
|
+
|
|
41
|
+
// System: logs
|
|
42
|
+
export { HestiaLogs } from "./system/logs/store"
|
|
43
|
+
export type { AuditEntry, LogFilter } from "./system/logs/types"
|
|
44
|
+
|
|
45
|
+
// Blobs
|
|
46
|
+
export { HestiaBlobClient, BlobNamespace } from "./blobs/store"
|
|
47
|
+
export type {
|
|
48
|
+
NamespaceInfo,
|
|
49
|
+
BlobMeta,
|
|
50
|
+
BlobDocument as BlobDoc,
|
|
51
|
+
ListBlobsRequest,
|
|
52
|
+
CreateNamespaceRequest,
|
|
53
|
+
} from "./blobs/types"
|
|
54
|
+
|
|
55
|
+
// Collections
|
|
56
|
+
export { HestiaCollections } from "./collections/store"
|
|
57
|
+
export type { CollectionMeta } from "./collections/types"
|
|
58
|
+
|
|
59
|
+
// Container
|
|
60
|
+
export { HestiaClient } from "./container"
|
|
61
|
+
|
|
62
|
+
export * from "./utils"
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@asaidimu/hestia",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "TypeScript client SDK for the Hestia platform — auth, collections, API keys, policies, audit logs, blobs, and capabilities",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"module": "index.ts",
|
|
7
|
+
"main": "index.ts",
|
|
8
|
+
"types": "index.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "vitest run",
|
|
11
|
+
"test:watch": "vitest",
|
|
12
|
+
"test:browser": "vitest --browser --run"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"*.ts",
|
|
16
|
+
"*/**/*.ts",
|
|
17
|
+
"!*.test.ts",
|
|
18
|
+
"!*/**/*.test.ts"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/asaidimu/hestia.git"
|
|
23
|
+
},
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"author": "Asaidimu <dev@asaidimu.com>",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/bun": "latest",
|
|
31
|
+
"vitest": "^4.1.10"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"typescript": "^5"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"@asaidimu/network-client": "^2.0.0",
|
|
38
|
+
"@asaidimu/utils-artifacts": "^8.2.25",
|
|
39
|
+
"@asaidimu/utils-error": "^1.1.10",
|
|
40
|
+
"@asaidimu/utils-store": "^10.2.18",
|
|
41
|
+
"uuid": "^14.0.1"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import type { QueryDSL } from "@asaidimu/query"
|
|
2
|
+
import { HestiaNetworkClient } from "../../core/client"
|
|
3
|
+
import type { Document, Page, PagedData } from "../../core/types"
|
|
4
|
+
import type { APIKey, APIKeyWithSecret, CreateKeyRequest, UpdateKeyRequest } from "./types"
|
|
5
|
+
|
|
6
|
+
export class HestiaKeyStore {
|
|
7
|
+
constructor(private client: HestiaNetworkClient) {}
|
|
8
|
+
|
|
9
|
+
private basePath = "/system/apikeys/key"
|
|
10
|
+
|
|
11
|
+
async find(_query?: QueryDSL<APIKey>): Promise<Page<APIKey>> {
|
|
12
|
+
const res = await this.client.get<{
|
|
13
|
+
data: Document<APIKey>[];
|
|
14
|
+
metadata?: { page?: any };
|
|
15
|
+
}>(this.basePath)
|
|
16
|
+
const items = res.data?.data ?? []
|
|
17
|
+
const pageMeta = res.data?.metadata?.page ?? {
|
|
18
|
+
number: 1,
|
|
19
|
+
size: items.length,
|
|
20
|
+
count: items.length,
|
|
21
|
+
total: items.length,
|
|
22
|
+
pages: 1,
|
|
23
|
+
}
|
|
24
|
+
return { data: items, loading: false, page: pageMeta }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async list(): Promise<Page<APIKey>> {
|
|
28
|
+
return this.find()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async read(id: string): Promise<Document<APIKey> | undefined> {
|
|
32
|
+
try {
|
|
33
|
+
const res = await this.client.get<{ data: Document<APIKey> }>(
|
|
34
|
+
`${this.basePath}/${encodeURIComponent(id)}`,
|
|
35
|
+
)
|
|
36
|
+
return res.data?.data
|
|
37
|
+
} catch (err: any) {
|
|
38
|
+
if (err?.code === "SYNC-001-NF") return undefined
|
|
39
|
+
if (err?.code === "INTERNAL_ERROR" && typeof err?.message === "string" && err.message.includes("not found")) return undefined
|
|
40
|
+
throw err
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async create(data: CreateKeyRequest): Promise<Document<APIKeyWithSecret>> {
|
|
45
|
+
const res = await this.client.post<{ data: Document<APIKeyWithSecret> }>(
|
|
46
|
+
this.basePath,
|
|
47
|
+
data,
|
|
48
|
+
)
|
|
49
|
+
return res.data!.data
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async update(id: string, data: UpdateKeyRequest): Promise<Document<APIKey>> {
|
|
53
|
+
const res = await this.client.patch<{ data: Document<APIKey> }>(
|
|
54
|
+
`${this.basePath}/${encodeURIComponent(id)}`,
|
|
55
|
+
data as any,
|
|
56
|
+
)
|
|
57
|
+
return res.data!.data
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async delete(id: string): Promise<void> {
|
|
61
|
+
await this.client.delete(`${this.basePath}/${encodeURIComponent(id)}`)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async rotate(id: string): Promise<Document<APIKeyWithSecret>> {
|
|
65
|
+
const res = await this.client.post<{ data: Document<APIKeyWithSecret> }>(
|
|
66
|
+
`${this.basePath}/${encodeURIComponent(id)}`,
|
|
67
|
+
)
|
|
68
|
+
return res.data!.data
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface APIKey {
|
|
2
|
+
_id_: string
|
|
3
|
+
name: string
|
|
4
|
+
prefix: string
|
|
5
|
+
scopes: string[]
|
|
6
|
+
status: string
|
|
7
|
+
expiry?: string | null
|
|
8
|
+
environment?: string
|
|
9
|
+
_metadata_: Record<string, unknown>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface APIKeyWithSecret extends APIKey {
|
|
13
|
+
key: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface CreateKeyRequest {
|
|
17
|
+
name: string
|
|
18
|
+
environment?: string
|
|
19
|
+
scopes?: string[]
|
|
20
|
+
expiry?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface UpdateKeyRequest {
|
|
24
|
+
name?: string
|
|
25
|
+
environment?: string
|
|
26
|
+
scopes?: string[]
|
|
27
|
+
status?: "active" | "revoked"
|
|
28
|
+
expiry?: string
|
|
29
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { HestiaNetworkClient } from "../../core/client";
|
|
2
|
+
import type { Document } from "../../core/types";
|
|
3
|
+
|
|
4
|
+
export class HestiaCapabilities {
|
|
5
|
+
constructor(
|
|
6
|
+
private client: HestiaNetworkClient,
|
|
7
|
+
) {
|
|
8
|
+
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async fetch(): Promise<Array<Document<any>> | undefined> {
|
|
12
|
+
try {
|
|
13
|
+
const res = await this.client.get<{ data: Array<Document<any>> }>(
|
|
14
|
+
`/system/core/docs`,
|
|
15
|
+
);
|
|
16
|
+
return res.data?.data;
|
|
17
|
+
} catch (err: any) {
|
|
18
|
+
if (err?.code === "SYNC-001-NF") return undefined;
|
|
19
|
+
throw err;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// Capabilities types — currently empty
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type { QueryDSL } from "@asaidimu/query";
|
|
2
|
+
import { ReactiveDataStore } from "@asaidimu/utils-store";
|
|
3
|
+
import { HestiaNetworkClient } from "../../core/client";
|
|
4
|
+
import { createPagedController } from "../../core/pager";
|
|
5
|
+
import type { Document, Page, PagedData } from "../../core/types";
|
|
6
|
+
import type { UpdateUserRequest, UserData } from "./types";
|
|
7
|
+
|
|
8
|
+
export class HestiaUsers {
|
|
9
|
+
private pagerOptions: any = {};
|
|
10
|
+
private pager: PagedData<UserData>;
|
|
11
|
+
|
|
12
|
+
constructor(
|
|
13
|
+
private client: HestiaNetworkClient,
|
|
14
|
+
) {
|
|
15
|
+
this.pager = createPagedController<UserData>(
|
|
16
|
+
"users",
|
|
17
|
+
new ReactiveDataStore<any>({}),
|
|
18
|
+
this.pagerOptions,
|
|
19
|
+
(query) => this.find(query),
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
name() {
|
|
24
|
+
return "users";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async find(query?: QueryDSL<UserData>): Promise<Page<UserData>> {
|
|
28
|
+
const res = await this.client.post<{
|
|
29
|
+
data: Document<UserData>[];
|
|
30
|
+
metadata?: { page?: any };
|
|
31
|
+
}>("/system/users/user/query", query ?? {});
|
|
32
|
+
const data = res.data?.data ?? [];
|
|
33
|
+
const pageMeta = res.data?.metadata?.page ?? {
|
|
34
|
+
number: 1,
|
|
35
|
+
size: data.length,
|
|
36
|
+
count: data.length,
|
|
37
|
+
total: data.length,
|
|
38
|
+
pages: 1,
|
|
39
|
+
};
|
|
40
|
+
return { data, loading: false, page: pageMeta };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async list(options?: QueryDSL<UserData>): Promise<Page<UserData>> {
|
|
44
|
+
return this.find(
|
|
45
|
+
options ?? { pagination: { type: "offset", offset: 0, limit: 50 } },
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async read(id: string): Promise<Document<UserData> | undefined> {
|
|
50
|
+
try {
|
|
51
|
+
const res = await this.client.get<{ data: Document<UserData> }>(
|
|
52
|
+
`/system/users/user/${encodeURIComponent(id)}`,
|
|
53
|
+
);
|
|
54
|
+
return res.data?.data;
|
|
55
|
+
} catch (err: any) {
|
|
56
|
+
if (err?.code === "SYNC-001-NF") return undefined;
|
|
57
|
+
throw err;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async update(
|
|
62
|
+
id: string,
|
|
63
|
+
data: UpdateUserRequest,
|
|
64
|
+
): Promise<Document<UserData>> {
|
|
65
|
+
const res = await this.client.patch<{ data: Document<UserData> }>(
|
|
66
|
+
`/system/users/user/${encodeURIComponent(id)}`,
|
|
67
|
+
data as any,
|
|
68
|
+
);
|
|
69
|
+
return res.data!.data;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async delete(id: string): Promise<void> {
|
|
73
|
+
await this.client.delete(`/system/users/user/${encodeURIComponent(id)}`);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async changePassword(
|
|
77
|
+
userId: string,
|
|
78
|
+
current: string,
|
|
79
|
+
newPassword: string,
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
await this.client.patch(`/system/users/password/${encodeURIComponent(userId)}`, {
|
|
82
|
+
current,
|
|
83
|
+
new: newPassword,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
page(): PagedData<UserData> {
|
|
88
|
+
return this.pager;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Document } from "../../core/types"
|
|
2
|
+
|
|
3
|
+
export interface UserData {
|
|
4
|
+
email: string
|
|
5
|
+
name: string
|
|
6
|
+
verified: boolean
|
|
7
|
+
scopes: string[]
|
|
8
|
+
deleted_at?: string | null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type UserIdentity = Document<UserData>
|
|
12
|
+
|
|
13
|
+
export interface CreateUserRequest {
|
|
14
|
+
email: string
|
|
15
|
+
password: string
|
|
16
|
+
name: string
|
|
17
|
+
scopes?: string[]
|
|
18
|
+
verified?: boolean
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface UpdateUserRequest {
|
|
22
|
+
name?: string
|
|
23
|
+
email?: string
|
|
24
|
+
scopes?: string[]
|
|
25
|
+
verified?: boolean
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ChangePasswordRequest {
|
|
29
|
+
current: string
|
|
30
|
+
new: string
|
|
31
|
+
}
|