@asaidimu/hestia 1.0.0 → 1.0.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.
@@ -9,6 +9,7 @@ import type {
9
9
  PaginationInfo,
10
10
  StoreEvent,
11
11
  } from "./types";
12
+ import type { DocumentStore } from "./types";
12
13
 
13
14
  interface ServerEnvelope<T extends Record<string, any>> {
14
15
  data: Document<T>[];
@@ -19,19 +20,20 @@ interface SingleEnvelope<T extends Record<string, any>> {
19
20
  data: Document<T>;
20
21
  }
21
22
 
22
- export class HestiaCollection<T extends Record<string, any>> {
23
+ export class HestiaCollection<T extends Record<string, any>> implements DocumentStore<T, Record<string, unknown>, string, Record<string, unknown>, Record<string, unknown>, string, string, Record<string, unknown>> {
23
24
  private pagerOptions: PageOptions<T> = {};
24
25
  private pager: PagedData<T>;
25
26
 
26
27
  constructor(
27
28
  private client: HestiaNetworkClient,
28
29
  private collectionName: string,
30
+ private defaultLimit: number = 50,
29
31
  ) {
30
32
  this.pager = createPagedController<T>(
31
33
  collectionName,
32
34
  new ReactiveDataStore<any>({}),
33
35
  this.pagerOptions,
34
- (query) => this.find(query),
36
+ (query) => this.find(query as any),
35
37
  );
36
38
  }
37
39
 
@@ -51,7 +53,7 @@ export class HestiaCollection<T extends Record<string, any>> {
51
53
  return `${this.documentsPath}/${encodeURIComponent(id)}`;
52
54
  }
53
55
 
54
- async find(query?: QueryDSL<T>): Promise<Page<T>> {
56
+ async find(query?: Record<string, unknown>): Promise<Page<T>> {
55
57
  const res = await this.client.post<ServerEnvelope<T>>(
56
58
  this.queryPath,
57
59
  query ?? {},
@@ -82,18 +84,19 @@ export class HestiaCollection<T extends Record<string, any>> {
82
84
  }
83
85
  }
84
86
 
85
- async create(data: Partial<T>): Promise<Document<T>> {
87
+ async create(props: { data: Partial<T> }): Promise<Document<T> | undefined> {
86
88
  const res = await this.client.post<SingleEnvelope<T>>(
87
89
  this.documentsPath,
88
- data,
90
+ props.data,
89
91
  );
90
92
  return res.data!.data;
91
93
  }
92
94
 
93
- async update(id: string, data: Partial<T>): Promise<Document<T>> {
95
+ async update(props: { data: Partial<T>; options?: string }): Promise<Document<T> | undefined> {
96
+ const id = props.options!;
94
97
  const res = await this.client.patch<SingleEnvelope<T>>(
95
98
  this.documentPath(id),
96
- data,
99
+ props.data,
97
100
  );
98
101
  return res.data!.data;
99
102
  }
@@ -102,6 +105,16 @@ export class HestiaCollection<T extends Record<string, any>> {
102
105
  await this.client.delete(this.documentPath(id));
103
106
  }
104
107
 
108
+ async list(options?: Record<string, unknown>): Promise<Page<T>> {
109
+ return this.find(
110
+ options ?? { pagination: { type: "offset", offset: 0, limit: this.defaultLimit } },
111
+ );
112
+ }
113
+
114
+ async upload(_props: { file: File }): Promise<Document<T> | undefined> {
115
+ throw new Error("Upload not supported for collections");
116
+ }
117
+
105
118
  async subscribe(
106
119
  _scope: string,
107
120
  _callback: (event: StoreEvent) => void,
@@ -113,9 +126,20 @@ export class HestiaCollection<T extends Record<string, any>> {
113
126
  throw new Error("Notify not implemented for dynamic collections");
114
127
  }
115
128
 
116
- page(): PagedData<T> {
129
+ stream(
130
+ _options: Record<string, unknown>,
131
+ _onStreamChange: () => void,
132
+ ): {
133
+ stream: () => AsyncIterable<Document<T>>;
134
+ cancel: () => void;
135
+ status: () => "active" | "cancelled" | "completed";
136
+ } {
137
+ throw new Error("Stream not supported for collections");
138
+ }
139
+
140
+ page(_options?: Record<string, unknown>): PagedData<T> {
117
141
  return this.pager;
118
142
  }
119
143
  }
120
144
 
121
- export type { ServerEnvelope, SingleEnvelope };
145
+ export type { ServerEnvelope, SingleEnvelope };
package/core/errors.ts CHANGED
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  ApiResponse,
3
3
  } from "@asaidimu/network-client"
4
4
  import { SystemError, Errors, type Issue } from "@asaidimu/utils-error"
package/core/pager.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  import type { QueryDSL, QueryFilter, SortConfiguration } from "@asaidimu/query";
2
2
  import { DELETE_SYMBOL, type DataStore } from "@asaidimu/utils-store";
3
3
  import type { Page, PagedData, PagerRefreshOptions } from "./types";
4
+ export type { PagerRefreshOptions }
4
5
  import { Debouncer } from "@asaidimu/utils-sync";
5
6
 
7
+ declare function requestIdleCallback(cb: (deadline: { didTimeout: boolean }) => void): void;
8
+
6
9
  export interface PageOptions<T extends Record<string, any>> {
7
10
  page?: number;
8
11
  size?: number;
package/core/types.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { QueryFilter, SortConfiguration } from "@asaidimu/query";
1
+ import type { QueryFilter, SortConfiguration } from "@asaidimu/query";
2
2
 
3
3
  // Internal metadata managed by Anansi
4
4
  interface DocumentMetadata {
@@ -275,5 +275,5 @@ export interface DocumentStore<
275
275
  * // Navigate
276
276
  * await todosPaged.navigate(2);
277
277
  */
278
- page(options: TPageOptions): Promise<PagedData<T>>;
278
+ page(options: TPageOptions): PagedData<T>;
279
279
  }
package/index.ts CHANGED
@@ -1,60 +1,37 @@
1
1
  // Core
2
- export { HestiaNetworkClient, HestiaResponse } from "./core/client"
3
- export type { IdentityProvider } from "./core/client"
2
+ export * from "./core/client"
4
3
  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"
4
+ export * from "./core/errors"
5
+ export * from "./core/types"
6
+ export { createPagedController } from "./core/pager"
11
7
 
12
8
  // Auth
13
- export { HestiaAuth } from "./auth/store"
14
- export type { TokenPair, LoginResult, ServerHealth, LoginRequest, RegisterRequest } from "./auth/types"
9
+ export * from "./auth/store"
10
+ export * from "./auth/types"
15
11
 
16
12
  // 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"
13
+ export * from "./system/identity/store"
14
+ export * from "./system/identity/types"
24
15
 
25
16
  // System: api-keys
26
- export { HestiaKeyStore } from "./system/api-keys/store"
27
- export type { APIKey, APIKeyWithSecret, CreateKeyRequest, UpdateKeyRequest } from "./system/api-keys/types"
17
+ export * from "./system/api-keys/store"
18
+ export * from "./system/api-keys/types"
28
19
 
29
20
  // 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"
21
+ export * from "./system/policies/store"
22
+ export * from "./system/policies/types"
40
23
 
41
24
  // System: logs
42
- export { HestiaLogs } from "./system/logs/store"
43
- export type { AuditEntry, LogFilter } from "./system/logs/types"
25
+ export * from "./system/logs/store"
26
+ export * from "./system/logs/types"
44
27
 
45
28
  // 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"
29
+ export * from "./blobs/store"
30
+ export * from "./blobs/types"
54
31
 
55
32
  // Collections
56
- export { HestiaCollections } from "./collections/store"
57
- export type { CollectionMeta } from "./collections/types"
33
+ export * from "./collections/store"
34
+ export * from "./collections/types"
58
35
 
59
36
  // Container
60
37
  export { HestiaClient } from "./container"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asaidimu/hestia",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "TypeScript client SDK for the Hestia platform — auth, collections, API keys, policies, audit logs, blobs, and capabilities",
5
5
  "type": "module",
6
6
  "module": "index.ts",
@@ -21,12 +21,13 @@
21
21
  "type": "git",
22
22
  "url": "git+https://github.com/asaidimu/hestia.git"
23
23
  },
24
+ "author": "Saidimu <47994458+asaidimu@users.noreply.github.com>",
24
25
  "license": "MIT",
25
- "author": "Asaidimu <dev@asaidimu.com>",
26
26
  "publishConfig": {
27
27
  "access": "public"
28
28
  },
29
29
  "devDependencies": {
30
+ "@faker-js/faker": "^10.5.0",
30
31
  "@types/bun": "latest",
31
32
  "vitest": "^4.1.10"
32
33
  },
@@ -35,9 +36,12 @@
35
36
  },
36
37
  "dependencies": {
37
38
  "@asaidimu/network-client": "^2.0.0",
39
+ "@asaidimu/query": "^1.0.0",
38
40
  "@asaidimu/utils-artifacts": "^8.2.25",
39
41
  "@asaidimu/utils-error": "^1.1.10",
42
+ "@asaidimu/utils-schema": "^1.0.10",
40
43
  "@asaidimu/utils-store": "^10.2.18",
44
+ "@asaidimu/utils-sync": "^2.3.0",
41
45
  "uuid": "^14.0.1"
42
46
  }
43
47
  }
@@ -1,10 +1,22 @@
1
1
  import type { QueryDSL } from "@asaidimu/query"
2
2
  import { HestiaNetworkClient } from "../../core/client"
3
- import type { Document, Page, PagedData } from "../../core/types"
3
+ import { ReactiveDataStore } from "@asaidimu/utils-store"
4
+ import { createPagedController } from "../../core/pager"
5
+ import type { Document, Page, PagedData, StoreEvent } from "../../core/types"
6
+ import type { DocumentStore } from "../../core/types"
4
7
  import type { APIKey, APIKeyWithSecret, CreateKeyRequest, UpdateKeyRequest } from "./types"
5
8
 
6
- export class HestiaKeyStore {
7
- constructor(private client: HestiaNetworkClient) {}
9
+ export class HestiaKeyStore implements DocumentStore<APIKey, QueryDSL<APIKey>, string, QueryDSL<APIKey>, Record<string, unknown>, string, string, Record<string, unknown>> {
10
+ private pager: PagedData<APIKey>
11
+
12
+ constructor(private client: HestiaNetworkClient) {
13
+ this.pager = createPagedController<APIKey>(
14
+ "_api_key_",
15
+ new ReactiveDataStore<any>({}),
16
+ {},
17
+ (query) => this.find(query),
18
+ )
19
+ }
8
20
 
9
21
  private basePath = "/system/apikeys/key"
10
22
 
@@ -24,8 +36,8 @@ export class HestiaKeyStore {
24
36
  return { data: items, loading: false, page: pageMeta }
25
37
  }
26
38
 
27
- async list(): Promise<Page<APIKey>> {
28
- return this.find()
39
+ async list(options?: QueryDSL<APIKey>): Promise<Page<APIKey>> {
40
+ return options ? this.find(options) : this.find()
29
41
  }
30
42
 
31
43
  async read(id: string): Promise<Document<APIKey> | undefined> {
@@ -41,18 +53,19 @@ export class HestiaKeyStore {
41
53
  }
42
54
  }
43
55
 
44
- async create(data: CreateKeyRequest): Promise<Document<APIKeyWithSecret>> {
56
+ async create(props: { data: Partial<APIKey> }): Promise<Document<APIKey> | undefined> {
45
57
  const res = await this.client.post<{ data: Document<APIKeyWithSecret> }>(
46
58
  this.basePath,
47
- data,
59
+ props.data as CreateKeyRequest,
48
60
  )
49
61
  return res.data!.data
50
62
  }
51
63
 
52
- async update(id: string, data: UpdateKeyRequest): Promise<Document<APIKey>> {
64
+ async update(props: { data: Partial<APIKey>; options?: string }): Promise<Document<APIKey> | undefined> {
65
+ const id = props.options!
53
66
  const res = await this.client.patch<{ data: Document<APIKey> }>(
54
67
  `${this.basePath}/${encodeURIComponent(id)}`,
55
- data as any,
68
+ props.data as UpdateKeyRequest,
56
69
  )
57
70
  return res.data!.data
58
71
  }
@@ -61,10 +74,34 @@ export class HestiaKeyStore {
61
74
  await this.client.delete(`${this.basePath}/${encodeURIComponent(id)}`)
62
75
  }
63
76
 
77
+ async upload(_props: { file: File }): Promise<Document<APIKey> | undefined> {
78
+ throw new Error("Upload not supported for API keys")
79
+ }
80
+
81
+ async subscribe(_scope: string, _callback: (event: StoreEvent) => void): Promise<() => void> {
82
+ throw new Error("Subscription not supported for API keys")
83
+ }
84
+
85
+ async notify(_event: StoreEvent): Promise<void> {
86
+ throw new Error("Notify not supported for API keys")
87
+ }
88
+
89
+ stream(_options: Record<string, unknown>, _onStreamChange: () => void): {
90
+ stream: () => AsyncIterable<Document<APIKey>>;
91
+ cancel: () => void;
92
+ status: () => "active" | "cancelled" | "completed";
93
+ } {
94
+ throw new Error("Stream not supported for API keys")
95
+ }
96
+
97
+ page(_options?: Record<string, unknown>): PagedData<APIKey> {
98
+ return this.pager
99
+ }
100
+
64
101
  async rotate(id: string): Promise<Document<APIKeyWithSecret>> {
65
102
  const res = await this.client.post<{ data: Document<APIKeyWithSecret> }>(
66
103
  `${this.basePath}/${encodeURIComponent(id)}`,
67
104
  )
68
105
  return res.data!.data
69
106
  }
70
- }
107
+ }
@@ -2,10 +2,12 @@ export interface APIKey {
2
2
  _id_: string
3
3
  name: string
4
4
  prefix: string
5
- scopes: string[]
5
+ operations: string[]
6
6
  status: string
7
7
  expiry?: string | null
8
8
  environment?: string
9
+ usage?: number
10
+ last_used?: string | null
9
11
  _metadata_: Record<string, unknown>
10
12
  }
11
13
 
@@ -16,14 +18,14 @@ export interface APIKeyWithSecret extends APIKey {
16
18
  export interface CreateKeyRequest {
17
19
  name: string
18
20
  environment?: string
19
- scopes?: string[]
21
+ operations?: string[]
20
22
  expiry?: string
21
23
  }
22
24
 
23
25
  export interface UpdateKeyRequest {
24
26
  name?: string
25
27
  environment?: string
26
- scopes?: string[]
28
+ operations?: string[]
27
29
  status?: "active" | "revoked"
28
30
  expiry?: string
29
31
  }
@@ -1,23 +1,67 @@
1
1
  import { HestiaNetworkClient } from "../../core/client";
2
- import type { Document } from "../../core/types";
2
+ import type { Document, Page, PagedData, StoreEvent } from "../../core/types";
3
+ import type { DocumentStore } from "../../core/types";
3
4
 
4
- export class HestiaCapabilities {
5
+ export class HestiaCapabilities implements DocumentStore<any, Record<string, unknown>, string, Record<string, unknown>, Record<string, unknown>, string, string, Record<string, unknown>> {
5
6
  constructor(
6
7
  private client: HestiaNetworkClient,
7
8
  ) {
8
9
 
9
10
  }
10
11
 
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
- }
12
+ async find(_query?: Record<string, unknown>): Promise<Page<any>> {
13
+ const res = await this.client.get<{ data: Array<Document<any>> }>(
14
+ `/system/core/docs`,
15
+ );
16
+ const items = res.data?.data ?? [];
17
+ return {
18
+ data: items,
19
+ loading: false,
20
+ page: { number: 1, size: items.length, count: items.length, total: items.length, pages: 1 },
21
+ };
21
22
  }
22
23
 
23
- }
24
+ async read(_id: string): Promise<Document<any> | undefined> {
25
+ throw new Error("Read by ID not supported for capabilities; use find()")
26
+ }
27
+
28
+ async create(_props: { data: Partial<any> }): Promise<Document<any> | undefined> {
29
+ throw new Error("Capabilities are read-only")
30
+ }
31
+
32
+ async update(_props: { data: Partial<any>; options?: string }): Promise<Document<any> | undefined> {
33
+ throw new Error("Capabilities are read-only")
34
+ }
35
+
36
+ async delete(_id: string): Promise<void> {
37
+ throw new Error("Capabilities are read-only")
38
+ }
39
+
40
+ async list(_options?: Record<string, unknown>): Promise<Page<any>> {
41
+ return this.find()
42
+ }
43
+
44
+ async upload(_props: { file: File }): Promise<Document<any> | undefined> {
45
+ throw new Error("Upload not supported for capabilities")
46
+ }
47
+
48
+ async subscribe(_scope: string, _callback: (event: StoreEvent) => void): Promise<() => void> {
49
+ throw new Error("Subscription not supported for capabilities")
50
+ }
51
+
52
+ async notify(_event: StoreEvent): Promise<void> {
53
+ throw new Error("Notify not supported for capabilities")
54
+ }
55
+
56
+ stream(_options: Record<string, unknown>, _onStreamChange: () => void): {
57
+ stream: () => AsyncIterable<Document<any>>;
58
+ cancel: () => void;
59
+ status: () => "active" | "cancelled" | "completed";
60
+ } {
61
+ throw new Error("Stream not supported for capabilities")
62
+ }
63
+
64
+ page(_options?: Record<string, unknown>): PagedData<any> {
65
+ throw new Error("Pagination not supported for capabilities")
66
+ }
67
+ }
@@ -2,10 +2,11 @@ import type { QueryDSL } from "@asaidimu/query";
2
2
  import { ReactiveDataStore } from "@asaidimu/utils-store";
3
3
  import { HestiaNetworkClient } from "../../core/client";
4
4
  import { createPagedController } from "../../core/pager";
5
- import type { Document, Page, PagedData } from "../../core/types";
5
+ import type { Document, Page, PagedData, StoreEvent } from "../../core/types";
6
+ import type { DocumentStore } from "../../core/types";
6
7
  import type { UpdateUserRequest, UserData } from "./types";
7
8
 
8
- export class HestiaUsers {
9
+ export class HestiaUsers implements DocumentStore<UserData, QueryDSL<UserData>, string, QueryDSL<UserData>, Record<string, unknown>, string, string, Record<string, unknown>> {
9
10
  private pagerOptions: any = {};
10
11
  private pager: PagedData<UserData>;
11
12
 
@@ -58,13 +59,11 @@ export class HestiaUsers {
58
59
  }
59
60
  }
60
61
 
61
- async update(
62
- id: string,
63
- data: UpdateUserRequest,
64
- ): Promise<Document<UserData>> {
62
+ async update(props: { data: Partial<UserData>; options?: string }): Promise<Document<UserData> | undefined> {
63
+ const id = props.options!;
65
64
  const res = await this.client.patch<{ data: Document<UserData> }>(
66
65
  `/system/users/user/${encodeURIComponent(id)}`,
67
- data as any,
66
+ props.data as any,
68
67
  );
69
68
  return res.data!.data;
70
69
  }
@@ -73,6 +72,34 @@ export class HestiaUsers {
73
72
  await this.client.delete(`/system/users/user/${encodeURIComponent(id)}`);
74
73
  }
75
74
 
75
+ async create(props: { data: Partial<UserData> }): Promise<Document<UserData> | undefined> {
76
+ throw new Error("User creation requires email/password/name, use register endpoint");
77
+ }
78
+
79
+ async upload(_props: { file: File }): Promise<Document<UserData> | undefined> {
80
+ throw new Error("Upload not supported for users");
81
+ }
82
+
83
+ async subscribe(_scope: string, _callback: (event: StoreEvent) => void): Promise<() => void> {
84
+ throw new Error("Subscription not supported for users");
85
+ }
86
+
87
+ async notify(_event: StoreEvent): Promise<void> {
88
+ throw new Error("Notify not supported for users");
89
+ }
90
+
91
+ stream(_options: Record<string, unknown>, _onStreamChange: () => void): {
92
+ stream: () => AsyncIterable<Document<UserData>>;
93
+ cancel: () => void;
94
+ status: () => "active" | "cancelled" | "completed";
95
+ } {
96
+ throw new Error("Stream not supported for users");
97
+ }
98
+
99
+ page(_options?: Record<string, unknown>): PagedData<UserData> {
100
+ return this.pager;
101
+ }
102
+
76
103
  async changePassword(
77
104
  userId: string,
78
105
  current: string,
@@ -83,8 +110,4 @@ export class HestiaUsers {
83
110
  new: newPassword,
84
111
  });
85
112
  }
86
-
87
- page(): PagedData<UserData> {
88
- return this.pager;
89
- }
90
- }
113
+ }
@@ -1,11 +1,11 @@
1
- import { Document } from "../../core/types"
1
+ import type { Document } from "../../core/types"
2
2
 
3
3
  export interface UserData {
4
4
  email: string
5
5
  name: string
6
6
  verified: boolean
7
- scopes: string[]
8
- deleted_at?: string | null
7
+ permissions: string[]
8
+ deleted?: string | null
9
9
  }
10
10
 
11
11
  export type UserIdentity = Document<UserData>
@@ -14,14 +14,14 @@ export interface CreateUserRequest {
14
14
  email: string
15
15
  password: string
16
16
  name: string
17
- scopes?: string[]
17
+ permissions?: string[]
18
18
  verified?: boolean
19
19
  }
20
20
 
21
21
  export interface UpdateUserRequest {
22
22
  name?: string
23
23
  email?: string
24
- scopes?: string[]
24
+ permissions?: string[]
25
25
  verified?: boolean
26
26
  }
27
27