@jsm-mit/chat-motoko-package 0.1.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.
Files changed (37) hide show
  1. package/README.md +50 -0
  2. package/declarations/chat-motoko-backend/chat-motoko-backend.did +275 -0
  3. package/declarations/chat-motoko-backend/chat-motoko-backend.did.d.ts +197 -0
  4. package/declarations/chat-motoko-backend/chat-motoko-backend.did.js +198 -0
  5. package/declarations/chat-motoko-backend/index.d.ts +50 -0
  6. package/declarations/chat-motoko-backend/index.js +42 -0
  7. package/dist/actor-base.d.ts +47 -0
  8. package/dist/actor-base.d.ts.map +1 -0
  9. package/dist/actor-base.js +130 -0
  10. package/dist/actors/admin-actor.d.ts +61 -0
  11. package/dist/actors/admin-actor.d.ts.map +1 -0
  12. package/dist/actors/admin-actor.js +82 -0
  13. package/dist/actors/chats-actor.d.ts +77 -0
  14. package/dist/actors/chats-actor.d.ts.map +1 -0
  15. package/dist/actors/chats-actor.js +120 -0
  16. package/dist/actors/messages-actor.d.ts +43 -0
  17. package/dist/actors/messages-actor.d.ts.map +1 -0
  18. package/dist/actors/messages-actor.js +80 -0
  19. package/dist/actors/projects-actor.d.ts +38 -0
  20. package/dist/actors/projects-actor.d.ts.map +1 -0
  21. package/dist/actors/projects-actor.js +64 -0
  22. package/dist/chat-poller.d.ts +36 -0
  23. package/dist/chat-poller.d.ts.map +1 -0
  24. package/dist/chat-poller.js +82 -0
  25. package/dist/globals.d.ts +2 -0
  26. package/dist/globals.d.ts.map +1 -0
  27. package/dist/globals.js +1 -0
  28. package/dist/index.d.ts +10 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +7 -0
  31. package/dist/interfaces.d.ts +149 -0
  32. package/dist/interfaces.d.ts.map +1 -0
  33. package/dist/interfaces.js +6 -0
  34. package/dist/mappers.d.ts +17 -0
  35. package/dist/mappers.d.ts.map +1 -0
  36. package/dist/mappers.js +109 -0
  37. package/package.json +61 -0
@@ -0,0 +1,198 @@
1
+ export const idlFactory = ({ IDL }) => {
2
+ const AddAdminArgs = IDL.Record({ 'principal' : IDL.Principal });
3
+ const ErrorDetails = IDL.Variant({
4
+ 'InterCanisterConnectionError' : IDL.Null,
5
+ 'RemoteCanisterError' : IDL.Text,
6
+ 'NotFound' : IDL.Null,
7
+ 'NotAuthorized' : IDL.Text,
8
+ 'InvalidData' : IDL.Text,
9
+ 'AlreadyExists' : IDL.Null,
10
+ 'ReturnsNull' : IDL.Null,
11
+ });
12
+ const Error = IDL.Record({ 'details' : ErrorDetails, 'logsJson' : IDL.Text });
13
+ const Result_2 = IDL.Variant({ 'ok' : IDL.Bool, 'err' : Error });
14
+ const ChatId = IDL.Text;
15
+ const CloseChatArgs = IDL.Record({ 'chatId' : ChatId });
16
+ const ChatStatus = IDL.Variant({ 'closed' : IDL.Null, 'open' : IDL.Null });
17
+ const Role = IDL.Variant({
18
+ 'member' : IDL.Null,
19
+ 'admin' : IDL.Null,
20
+ 'agent' : IDL.Null,
21
+ 'owner' : IDL.Null,
22
+ });
23
+ const Member = IDL.Record({
24
+ 'principal' : IDL.Principal,
25
+ 'joinedAt' : IDL.Int,
26
+ 'role' : Role,
27
+ 'lastReadSeq' : IDL.Nat,
28
+ });
29
+ const ProjectId = IDL.Text;
30
+ const Policy = IDL.Record({ 'agentsReply' : IDL.Bool });
31
+ const Chat = IDL.Record({
32
+ 'id' : ChatId,
33
+ 'status' : ChatStatus,
34
+ 'title' : IDL.Opt(IDL.Text),
35
+ 'members' : IDL.Vec(Member),
36
+ 'lastMessageAt' : IDL.Int,
37
+ 'subject' : IDL.Opt(IDL.Text),
38
+ 'createdAt' : IDL.Int,
39
+ 'createdBy' : IDL.Principal,
40
+ 'projectId' : ProjectId,
41
+ 'lastSeq' : IDL.Nat,
42
+ 'policy' : Policy,
43
+ });
44
+ const Result_1 = IDL.Variant({ 'ok' : Chat, 'err' : Error });
45
+ const MemberInput = IDL.Record({
46
+ 'principal' : IDL.Principal,
47
+ 'role' : Role,
48
+ });
49
+ const CreateChatArgs = IDL.Record({
50
+ 'title' : IDL.Opt(IDL.Text),
51
+ 'members' : IDL.Vec(MemberInput),
52
+ 'subject' : IDL.Opt(IDL.Text),
53
+ 'projectId' : ProjectId,
54
+ 'firstMessage' : IDL.Opt(IDL.Text),
55
+ 'policy' : IDL.Opt(Policy),
56
+ });
57
+ const CreateProjectArgs = IDL.Record({
58
+ 'id' : ProjectId,
59
+ 'agents' : IDL.Vec(IDL.Principal),
60
+ 'admins' : IDL.Vec(IDL.Principal),
61
+ 'agentsMayInvite' : IDL.Bool,
62
+ 'defaultPolicy' : Policy,
63
+ });
64
+ const Project = IDL.Record({
65
+ 'id' : ProjectId,
66
+ 'createdAt' : IDL.Int,
67
+ 'createdBy' : IDL.Principal,
68
+ 'agents' : IDL.Vec(IDL.Principal),
69
+ 'admins' : IDL.Vec(IDL.Principal),
70
+ 'agentsMayInvite' : IDL.Bool,
71
+ 'defaultPolicy' : Policy,
72
+ });
73
+ const Result = IDL.Variant({ 'ok' : Project, 'err' : Error });
74
+ const Result_9 = IDL.Variant({
75
+ 'ok' : IDL.Vec(IDL.Principal),
76
+ 'err' : Error,
77
+ });
78
+ const GetChatArgs = IDL.Record({ 'chatId' : ChatId });
79
+ const GetMessagesArgs = IDL.Record({
80
+ 'limit' : IDL.Opt(IDL.Nat),
81
+ 'chatId' : ChatId,
82
+ 'sinceSeq' : IDL.Nat,
83
+ });
84
+ const SystemEvent = IDL.Variant({
85
+ 'memberJoined' : IDL.Record({ 'principal' : IDL.Principal, 'role' : Role }),
86
+ 'policyChanged' : Policy,
87
+ 'memberRemoved' : IDL.Record({
88
+ 'by' : IDL.Principal,
89
+ 'principal' : IDL.Principal,
90
+ }),
91
+ 'memberLeft' : IDL.Record({ 'principal' : IDL.Principal }),
92
+ 'chatCreated' : IDL.Null,
93
+ 'chatClosed' : IDL.Null,
94
+ 'memberInvited' : IDL.Record({
95
+ 'by' : IDL.Principal,
96
+ 'principal' : IDL.Principal,
97
+ 'role' : Role,
98
+ }),
99
+ });
100
+ const MessageKind = IDL.Variant({
101
+ 'toolTrace' : IDL.Null,
102
+ 'text' : IDL.Null,
103
+ 'event' : SystemEvent,
104
+ });
105
+ const Message = IDL.Record({
106
+ 'seq' : IDL.Nat,
107
+ 'kind' : MessageKind,
108
+ 'createdAt' : IDL.Int,
109
+ 'text' : IDL.Text,
110
+ 'globalSeq' : IDL.Nat,
111
+ 'author' : IDL.Principal,
112
+ 'projectId' : ProjectId,
113
+ 'chatId' : ChatId,
114
+ });
115
+ const MessagesPage = IDL.Record({
116
+ 'messages' : IDL.Vec(Message),
117
+ 'lastSeq' : IDL.Nat,
118
+ });
119
+ const Result_8 = IDL.Variant({ 'ok' : MessagesPage, 'err' : Error });
120
+ const GetMyChatsArgs = IDL.Record({
121
+ 'subject' : IDL.Opt(IDL.Text),
122
+ 'includeClosed' : IDL.Bool,
123
+ 'limit' : IDL.Opt(IDL.Nat),
124
+ 'projectId' : ProjectId,
125
+ });
126
+ const Result_7 = IDL.Variant({ 'ok' : IDL.Vec(Chat), 'err' : Error });
127
+ const GetProjectArgs = IDL.Record({ 'projectId' : ProjectId });
128
+ const InviteMemberArgs = IDL.Record({
129
+ 'principal' : IDL.Principal,
130
+ 'role' : Role,
131
+ 'chatId' : ChatId,
132
+ });
133
+ const LeaveChatArgs = IDL.Record({ 'chatId' : ChatId });
134
+ const ListProjectsArgs = IDL.Record({ 'limit' : IDL.Opt(IDL.Nat) });
135
+ const Result_6 = IDL.Variant({ 'ok' : IDL.Vec(Project), 'err' : Error });
136
+ const MarkReadArgs = IDL.Record({ 'seq' : IDL.Nat, 'chatId' : ChatId });
137
+ const PollNewMessagesArgs = IDL.Record({
138
+ 'limit' : IDL.Opt(IDL.Nat),
139
+ 'sinceGlobalSeq' : IDL.Nat,
140
+ });
141
+ const PollResult = IDL.Record({
142
+ 'nextSinceGlobalSeq' : IDL.Nat,
143
+ 'headGlobalSeq' : IDL.Nat,
144
+ 'messages' : IDL.Vec(Message),
145
+ });
146
+ const Result_5 = IDL.Variant({ 'ok' : PollResult, 'err' : Error });
147
+ const PostKind = IDL.Variant({ 'toolTrace' : IDL.Null, 'text' : IDL.Null });
148
+ const PostArgs = IDL.Record({
149
+ 'kind' : PostKind,
150
+ 'text' : IDL.Text,
151
+ 'chatId' : ChatId,
152
+ });
153
+ const Result_4 = IDL.Variant({ 'ok' : Message, 'err' : Error });
154
+ const PurgeChatArgs = IDL.Record({ 'chatId' : ChatId });
155
+ const Result_3 = IDL.Variant({ 'ok' : IDL.Principal, 'err' : Error });
156
+ const RemoveAdminArgs = IDL.Record({ 'principal' : IDL.Principal });
157
+ const RemoveMemberArgs = IDL.Record({
158
+ 'principal' : IDL.Principal,
159
+ 'chatId' : ChatId,
160
+ });
161
+ const SetLoggingEnabledArgs = IDL.Record({ 'enabled' : IDL.Bool });
162
+ const SetPolicyArgs = IDL.Record({ 'chatId' : ChatId, 'policy' : Policy });
163
+ const UpdateProjectArgs = IDL.Record({
164
+ 'agents' : IDL.Opt(IDL.Vec(IDL.Principal)),
165
+ 'projectId' : ProjectId,
166
+ 'admins' : IDL.Opt(IDL.Vec(IDL.Principal)),
167
+ 'agentsMayInvite' : IDL.Opt(IDL.Bool),
168
+ 'defaultPolicy' : IDL.Opt(Policy),
169
+ });
170
+ return IDL.Service({
171
+ 'addAdmin' : IDL.Func([AddAdminArgs], [Result_2], []),
172
+ 'clearLogs' : IDL.Func([], [], []),
173
+ 'closeChat' : IDL.Func([CloseChatArgs], [Result_1], []),
174
+ 'createChat' : IDL.Func([CreateChatArgs], [Result_1], []),
175
+ 'createProject' : IDL.Func([CreateProjectArgs], [Result], []),
176
+ 'getAdmins' : IDL.Func([], [Result_9], ['query']),
177
+ 'getChat' : IDL.Func([GetChatArgs], [Result_1], ['query']),
178
+ 'getLogs' : IDL.Func([], [IDL.Text], ['query']),
179
+ 'getMessages' : IDL.Func([GetMessagesArgs], [Result_8], ['query']),
180
+ 'getMyChats' : IDL.Func([GetMyChatsArgs], [Result_7], ['query']),
181
+ 'getProject' : IDL.Func([GetProjectArgs], [Result], ['query']),
182
+ 'inviteMember' : IDL.Func([InviteMemberArgs], [Result_1], []),
183
+ 'leaveChat' : IDL.Func([LeaveChatArgs], [Result_2], []),
184
+ 'listProjects' : IDL.Func([ListProjectsArgs], [Result_6], ['query']),
185
+ 'markRead' : IDL.Func([MarkReadArgs], [Result_2], []),
186
+ 'pollNewMessages' : IDL.Func([PollNewMessagesArgs], [Result_5], ['query']),
187
+ 'post' : IDL.Func([PostArgs], [Result_4], []),
188
+ 'purgeChat' : IDL.Func([PurgeChatArgs], [Result_2], []),
189
+ 'registerAsAdmin' : IDL.Func([], [Result_3], []),
190
+ 'removeAdmin' : IDL.Func([RemoveAdminArgs], [Result_2], []),
191
+ 'removeMember' : IDL.Func([RemoveMemberArgs], [Result_1], []),
192
+ 'setLoggingEnabled' : IDL.Func([SetLoggingEnabledArgs], [Result_2], []),
193
+ 'setPolicy' : IDL.Func([SetPolicyArgs], [Result_1], []),
194
+ 'updateProject' : IDL.Func([UpdateProjectArgs], [Result], []),
195
+ 'whoAmI' : IDL.Func([], [IDL.Principal], ['query']),
196
+ });
197
+ };
198
+ export const init = ({ IDL }) => { return []; };
@@ -0,0 +1,50 @@
1
+ import type {
2
+ ActorSubclass,
3
+ HttpAgentOptions,
4
+ ActorConfig,
5
+ Agent,
6
+ } from "@icp-sdk/core/agent";
7
+ import type { Principal } from "@icp-sdk/core/principal";
8
+ import type { IDL } from "@icp-sdk/core/candid";
9
+
10
+ import { _SERVICE } from './chat-motoko-backend.did';
11
+
12
+ export declare const idlFactory: IDL.InterfaceFactory;
13
+ export declare const canisterId: string;
14
+
15
+ export declare interface CreateActorOptions {
16
+ /**
17
+ * @see {@link Agent}
18
+ */
19
+ agent?: Agent;
20
+ /**
21
+ * @see {@link HttpAgentOptions}
22
+ */
23
+ agentOptions?: HttpAgentOptions;
24
+ /**
25
+ * @see {@link ActorConfig}
26
+ */
27
+ actorOptions?: ActorConfig;
28
+ }
29
+
30
+ /**
31
+ * Intializes an {@link ActorSubclass}, configured with the provided SERVICE interface of a canister.
32
+ * @constructs {@link ActorSubClass}
33
+ * @param {string | Principal} canisterId - ID of the canister the {@link Actor} will talk to
34
+ * @param {CreateActorOptions} options - see {@link CreateActorOptions}
35
+ * @param {CreateActorOptions["agent"]} options.agent - a pre-configured agent you'd like to use. Supercedes agentOptions
36
+ * @param {CreateActorOptions["agentOptions"]} options.agentOptions - options to set up a new agent
37
+ * @see {@link HttpAgentOptions}
38
+ * @param {CreateActorOptions["actorOptions"]} options.actorOptions - options for the Actor
39
+ * @see {@link ActorConfig}
40
+ */
41
+ export declare const createActor: (
42
+ canisterId: string | Principal,
43
+ options?: CreateActorOptions
44
+ ) => ActorSubclass<_SERVICE>;
45
+
46
+ /**
47
+ * Intialized Actor using default settings, ready to talk to a canister using its candid interface
48
+ * @constructs {@link ActorSubClass}
49
+ */
50
+ export declare const chat_motoko_backend: ActorSubclass<_SERVICE>;
@@ -0,0 +1,42 @@
1
+ import { Actor, HttpAgent } from "@icp-sdk/core/agent";
2
+
3
+ // Imports and re-exports candid interface
4
+ import { idlFactory } from "./chat-motoko-backend.did.js";
5
+ export { idlFactory } from "./chat-motoko-backend.did.js";
6
+
7
+ /* CANISTER_ID is replaced by webpack based on node environment
8
+ * Note: canister environment variable will be standardized as
9
+ * process.env.CANISTER_ID_<CANISTER_NAME_UPPERCASE>
10
+ * beginning in dfx 0.15.0
11
+ */
12
+ export const canisterId =
13
+ process.env.CANISTER_ID_CHAT_MOTOKO_BACKEND;
14
+
15
+ export const createActor = (canisterId, options = {}) => {
16
+ const agent = options.agent || new HttpAgent({ ...options.agentOptions });
17
+
18
+ if (options.agent && options.agentOptions) {
19
+ console.warn(
20
+ "Detected both agent and agentOptions passed to createActor. Ignoring agentOptions and proceeding with the provided agent."
21
+ );
22
+ }
23
+
24
+ // Fetch root key for certificate validation during development
25
+ if (process.env.DFX_NETWORK !== "ic") {
26
+ agent.fetchRootKey().catch((err) => {
27
+ console.warn(
28
+ "Unable to fetch root key. Check to ensure that your local replica is running"
29
+ );
30
+ console.error(err);
31
+ });
32
+ }
33
+
34
+ // Creates an actor with using the candid interface and the HttpAgent
35
+ return Actor.createActor(idlFactory, {
36
+ agent,
37
+ canisterId,
38
+ ...options.actorOptions,
39
+ });
40
+ };
41
+
42
+ export const chat_motoko_backend = canisterId ? createActor(canisterId) : undefined;
@@ -0,0 +1,47 @@
1
+ import { type ActorSubclass, HttpAgent, type Identity } from "@icp-sdk/core/agent";
2
+ import type { _SERVICE, Error as ErrorFromCanister } from "../declarations/chat-motoko-backend/chat-motoko-backend.did.js";
3
+ export interface LogEntry {
4
+ id: number;
5
+ message: string;
6
+ }
7
+ /**
8
+ * Base class for every per-domain actor wrapper (ChatsActor, MessagesActor, …). Builds the
9
+ * HttpAgent + raw candid actor and owns the single choke point that turns the canister's
10
+ * Framework.Result<T> into idiomatic TS:
11
+ * - `{ok: T}` resolves with `T` directly.
12
+ * - `{err: ...}` is thrown as `Error("CanisterError", { cause: {errorKey, errorMessage, logs} })`.
13
+ * - An `inspect()`-stage rejection is thrown as `Error("CallRefusedAtInspectionStage")`.
14
+ * - Any other transport failure is thrown as `Error("CriticalCanisterError")`.
15
+ */
16
+ export declare class ActorBase {
17
+ protected canisterId: string;
18
+ protected identity?: Identity | undefined;
19
+ protected actor: ActorSubclass<_SERVICE>;
20
+ protected agent: HttpAgent;
21
+ constructor(canisterId: string, identity?: Identity | undefined);
22
+ private initAgentAndActor;
23
+ /**
24
+ * Rebuilds the HttpAgent and the actor from scratch. Long-lived consumers (e.g.
25
+ * ChatPoller) call this after a prolonged streak of failed calls, where a stale
26
+ * agent/connection is the usual suspect. Safe to call at any time.
27
+ */
28
+ reinitializeAgent(): void;
29
+ protected executeFunctionAsyncUnsafe<T>(fnAsync: () => Promise<{
30
+ ok: T;
31
+ } | {
32
+ err: ErrorFromCanister;
33
+ }>): Promise<T>;
34
+ /**
35
+ * For the few diagnostic methods that do NOT return a Framework.Result (whoAmI,
36
+ * getLogs, clearLogs) — only the transport-level error translation applies.
37
+ */
38
+ protected executeRawAsyncUnsafe<T>(fnAsync: () => Promise<T>): Promise<T>;
39
+ protected handleResultErrors(errorFromCanister: ErrorFromCanister): {
40
+ errorKey: string;
41
+ errorMessage: any;
42
+ logs: LogEntry[];
43
+ };
44
+ protected isInspectionRejection(error: any): boolean;
45
+ protected extractLogsFromError(error: any): LogEntry[] | null;
46
+ }
47
+ //# sourceMappingURL=actor-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actor-base.d.ts","sourceRoot":"","sources":["../src/actor-base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAS,MAAM,qBAAqB,CAAC;AAE1F,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,IAAI,iBAAiB,EAAE,MAAM,gEAAgE,CAAC;AAG3H,MAAM,WAAW,QAAQ;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;GAQG;AACH,qBAAa,SAAS;IAIN,SAAS,CAAC,UAAU,EAAE,MAAM;IAAE,SAAS,CAAC,QAAQ,CAAC,EAAE,QAAQ;IAHvE,SAAS,CAAC,KAAK,EAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAC1C,SAAS,CAAC,KAAK,EAAG,SAAS,CAAC;gBAEN,UAAU,EAAE,MAAM,EAAY,QAAQ,CAAC,EAAE,QAAQ,YAAA;IAIvE,OAAO,CAAC,iBAAiB;IAWzB;;;;OAIG;IACI,iBAAiB,IAAI,IAAI;cAIhB,0BAA0B,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;QAAE,EAAE,EAAE,CAAC,CAAA;KAAE,GAAG;QAAE,GAAG,EAAE,iBAAiB,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAmCzH;;;OAGG;cACa,qBAAqB,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAW/E,SAAS,CAAC,kBAAkB,CAAC,iBAAiB,EAAE,iBAAiB;;;;;IAkBjE,SAAS,CAAC,qBAAqB,CAAC,KAAK,EAAE,GAAG,GAAG,OAAO;IAWpD,SAAS,CAAC,oBAAoB,CAAC,KAAK,EAAE,GAAG,GAAG,QAAQ,EAAE,GAAG,IAAI;CAwBhE"}
@@ -0,0 +1,130 @@
1
+ import { HttpAgent, Actor } from "@icp-sdk/core/agent";
2
+ import { idlFactory } from "../declarations/chat-motoko-backend/index.js";
3
+ import { BetterJSON } from "@jsm-mit/utils-package";
4
+ /**
5
+ * Base class for every per-domain actor wrapper (ChatsActor, MessagesActor, …). Builds the
6
+ * HttpAgent + raw candid actor and owns the single choke point that turns the canister's
7
+ * Framework.Result<T> into idiomatic TS:
8
+ * - `{ok: T}` resolves with `T` directly.
9
+ * - `{err: ...}` is thrown as `Error("CanisterError", { cause: {errorKey, errorMessage, logs} })`.
10
+ * - An `inspect()`-stage rejection is thrown as `Error("CallRefusedAtInspectionStage")`.
11
+ * - Any other transport failure is thrown as `Error("CriticalCanisterError")`.
12
+ */
13
+ export class ActorBase {
14
+ canisterId;
15
+ identity;
16
+ actor;
17
+ agent;
18
+ constructor(canisterId, identity) {
19
+ this.canisterId = canisterId;
20
+ this.identity = identity;
21
+ this.initAgentAndActor();
22
+ }
23
+ initAgentAndActor() {
24
+ this.agent = HttpAgent.createSync({
25
+ host: "https://icp0.io",
26
+ identity: this.identity,
27
+ });
28
+ this.actor = Actor.createActor(idlFactory, {
29
+ agent: this.agent,
30
+ canisterId: this.canisterId,
31
+ });
32
+ }
33
+ /**
34
+ * Rebuilds the HttpAgent and the actor from scratch. Long-lived consumers (e.g.
35
+ * ChatPoller) call this after a prolonged streak of failed calls, where a stale
36
+ * agent/connection is the usual suspect. Safe to call at any time.
37
+ */
38
+ reinitializeAgent() {
39
+ this.initAgentAndActor();
40
+ }
41
+ async executeFunctionAsyncUnsafe(fnAsync) {
42
+ let errorObj = {};
43
+ try {
44
+ const result = await fnAsync();
45
+ if ("ok" in result) {
46
+ return result.ok;
47
+ }
48
+ errorObj = this.handleResultErrors(result.err);
49
+ }
50
+ catch (err) {
51
+ // The canister's `system func inspect` rejected the call before it was ever
52
+ // decoded or executed (oversized payload or anonymous caller). This is a
53
+ // transport-level IC reject, not a decoded Framework.Result — there's never any
54
+ // Framework log to extract, and it's not a real "canister unreachable/crashed"
55
+ // scenario either, so it gets its own distinct error.
56
+ if (this.isInspectionRejection(err)) {
57
+ throw new Error("CallRefusedAtInspectionStage", { cause: err });
58
+ }
59
+ // No console output here — whatever was extractable is passed forward on `cause`
60
+ // instead, so callers can inspect it themselves rather than it only ever being
61
+ // printed and lost.
62
+ const logs = this.extractLogsFromError(err);
63
+ throw new Error("CriticalCanisterError", { cause: { logs, rawError: err } });
64
+ }
65
+ if (errorObj) {
66
+ throw new Error("CanisterError", { cause: errorObj });
67
+ }
68
+ else {
69
+ throw new Error("UnreachableCodeError", { cause: "Reached a code path that should be unreachable after error handling. Location Id: CHOPA" });
70
+ }
71
+ }
72
+ /**
73
+ * For the few diagnostic methods that do NOT return a Framework.Result (whoAmI,
74
+ * getLogs, clearLogs) — only the transport-level error translation applies.
75
+ */
76
+ async executeRawAsyncUnsafe(fnAsync) {
77
+ try {
78
+ return await fnAsync();
79
+ }
80
+ catch (err) {
81
+ if (this.isInspectionRejection(err)) {
82
+ throw new Error("CallRefusedAtInspectionStage", { cause: err });
83
+ }
84
+ throw new Error("CriticalCanisterError", { cause: { logs: null, rawError: err } });
85
+ }
86
+ }
87
+ handleResultErrors(errorFromCanister) {
88
+ const logs = BetterJSON.parse(errorFromCanister.logsJson, false);
89
+ const error = errorFromCanister.details;
90
+ const errorKey = Object.keys(error)[0];
91
+ const errorMessage = error[errorKey];
92
+ return {
93
+ errorKey: errorKey.toString(),
94
+ errorMessage,
95
+ logs,
96
+ };
97
+ }
98
+ // True if `error` is an IC transport-level reject caused by `system func inspect`
99
+ // returning false (`canister_inspect_message explicitly refused message`, IC0503).
100
+ // This text is a fixed IC/Motoko runtime message — it does not vary by which inspect
101
+ // check failed.
102
+ isInspectionRejection(error) {
103
+ const rejectText = error?.cause?.code?.rejectMessage ||
104
+ error?.message ||
105
+ "";
106
+ return rejectText.includes("canister_inspect_message explicitly refused message");
107
+ }
108
+ // On a trap, Logs.interrupt appends the request's log trail as a JSON array to the
109
+ // reject message — best-effort recovery of that array from the raw reject text.
110
+ extractLogsFromError(error) {
111
+ try {
112
+ const rejectMessage = error?.cause?.code?.rejectMessage ||
113
+ error?.message ||
114
+ "";
115
+ if (!rejectMessage)
116
+ return null;
117
+ const jsonStartIdx = rejectMessage.indexOf("[{");
118
+ const jsonEndIdx = rejectMessage.lastIndexOf("}]");
119
+ if (jsonStartIdx === -1 || jsonEndIdx === -1) {
120
+ return null;
121
+ }
122
+ const rawJson = rejectMessage.substring(jsonStartIdx, jsonEndIdx + 2);
123
+ const logs = JSON.parse(rawJson);
124
+ return logs;
125
+ }
126
+ catch (e) {
127
+ return null;
128
+ }
129
+ }
130
+ }
@@ -0,0 +1,61 @@
1
+ import { type Identity } from "@icp-sdk/core/agent";
2
+ import { ActorBase } from "../actor-base.js";
3
+ /** 1:1 wrapper for the canister's AdminController plus the three diagnostics. */
4
+ export declare class AdminActor extends ActorBase {
5
+ constructor(canisterId: string, identity?: Identity);
6
+ /**
7
+ * Bootstrap: succeeds only while the canister has NO admin at all — the first caller
8
+ * claims the admin role. Once any admin exists this always refuses.
9
+ * @returns The principal (text) that became admin, i.e. the caller.
10
+ * @throws Error with message `CanisterError` when the canister returns a business error (errorKey `NotAuthorized` when an admin already exists). Examine "cause" for {errorKey, errorMessage, logs}.
11
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
12
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
13
+ */
14
+ registerAsAdminAsyncUnsafe(): Promise<string>;
15
+ /**
16
+ * Adds a principal to the admin allowlist. Admin only.
17
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized, `AlreadyExists`, anonymous principal). Examine "cause" for {errorKey, errorMessage, logs}.
18
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
19
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
20
+ */
21
+ addAdminAsyncUnsafe(principal: string): Promise<boolean>;
22
+ /**
23
+ * Removes a principal from the admin allowlist; the last admin cannot be removed. Admin only.
24
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized, `NotFound`, last admin). Examine "cause" for {errorKey, errorMessage, logs}.
25
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
26
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
27
+ */
28
+ removeAdminAsyncUnsafe(principal: string): Promise<boolean>;
29
+ /**
30
+ * The admin allowlist. Admin only.
31
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized). Examine "cause" for {errorKey, errorMessage, logs}.
32
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
33
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
34
+ */
35
+ getAdminsAsyncUnsafe(): Promise<string[]>;
36
+ /**
37
+ * The logging kill switch. Admin only.
38
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized). Examine "cause" for {errorKey, errorMessage, logs}.
39
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
40
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
41
+ */
42
+ setLoggingEnabledAsyncUnsafe(enabled: boolean): Promise<boolean>;
43
+ /**
44
+ * The caller's principal as the canister sees it (text).
45
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister.
46
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
47
+ */
48
+ whoAmIAsyncUnsafe(): Promise<string>;
49
+ /**
50
+ * Leftover request logs as JSON text (normally empty — logs are cleared per request).
51
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister.
52
+ */
53
+ getLogsAsyncUnsafe(): Promise<string>;
54
+ /**
55
+ * Clears leftover request logs.
56
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister.
57
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
58
+ */
59
+ clearLogsAsyncUnsafe(): Promise<void>;
60
+ }
61
+ //# sourceMappingURL=admin-actor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-actor.d.ts","sourceRoot":"","sources":["../../src/actors/admin-actor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAE7C,iFAAiF;AACjF,qBAAa,UAAW,SAAQ,SAAS;gBAEzB,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ;IAInD;;;;;;;OAOG;IACU,0BAA0B,IAAI,OAAO,CAAC,MAAM,CAAC;IAK1D;;;;;OAKG;IACU,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrE;;;;;OAKG;IACU,sBAAsB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIxE;;;;;OAKG;IACU,oBAAoB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAKtD;;;;;OAKG;IACU,4BAA4B,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAI7E;;;;OAIG;IACU,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC;IAKjD;;;OAGG;IACU,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC;IAIlD;;;;OAIG;IACU,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;CAGrD"}
@@ -0,0 +1,82 @@
1
+ import {} from "@icp-sdk/core/agent";
2
+ import { Principal } from "@icp-sdk/core/principal";
3
+ import { ActorBase } from "../actor-base.js";
4
+ /** 1:1 wrapper for the canister's AdminController plus the three diagnostics. */
5
+ export class AdminActor extends ActorBase {
6
+ constructor(canisterId, identity) {
7
+ super(canisterId, identity);
8
+ }
9
+ /**
10
+ * Bootstrap: succeeds only while the canister has NO admin at all — the first caller
11
+ * claims the admin role. Once any admin exists this always refuses.
12
+ * @returns The principal (text) that became admin, i.e. the caller.
13
+ * @throws Error with message `CanisterError` when the canister returns a business error (errorKey `NotAuthorized` when an admin already exists). Examine "cause" for {errorKey, errorMessage, logs}.
14
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
15
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
16
+ */
17
+ async registerAsAdminAsyncUnsafe() {
18
+ const principal = await this.executeFunctionAsyncUnsafe(() => this.actor.registerAsAdmin());
19
+ return principal.toText();
20
+ }
21
+ /**
22
+ * Adds a principal to the admin allowlist. Admin only.
23
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized, `AlreadyExists`, anonymous principal). Examine "cause" for {errorKey, errorMessage, logs}.
24
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
25
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
26
+ */
27
+ async addAdminAsyncUnsafe(principal) {
28
+ return this.executeFunctionAsyncUnsafe(() => this.actor.addAdmin({ principal: Principal.fromText(principal) }));
29
+ }
30
+ /**
31
+ * Removes a principal from the admin allowlist; the last admin cannot be removed. Admin only.
32
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized, `NotFound`, last admin). Examine "cause" for {errorKey, errorMessage, logs}.
33
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
34
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
35
+ */
36
+ async removeAdminAsyncUnsafe(principal) {
37
+ return this.executeFunctionAsyncUnsafe(() => this.actor.removeAdmin({ principal: Principal.fromText(principal) }));
38
+ }
39
+ /**
40
+ * The admin allowlist. Admin only.
41
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized). Examine "cause" for {errorKey, errorMessage, logs}.
42
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
43
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
44
+ */
45
+ async getAdminsAsyncUnsafe() {
46
+ const admins = await this.executeFunctionAsyncUnsafe(() => this.actor.getAdmins());
47
+ return admins.map((p) => p.toText());
48
+ }
49
+ /**
50
+ * The logging kill switch. Admin only.
51
+ * @throws Error with message `CanisterError` when the canister returns a business error (not authorized). Examine "cause" for {errorKey, errorMessage, logs}.
52
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister or the canister code traps during execution.
53
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
54
+ */
55
+ async setLoggingEnabledAsyncUnsafe(enabled) {
56
+ return this.executeFunctionAsyncUnsafe(() => this.actor.setLoggingEnabled({ enabled }));
57
+ }
58
+ /**
59
+ * The caller's principal as the canister sees it (text).
60
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister.
61
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
62
+ */
63
+ async whoAmIAsyncUnsafe() {
64
+ const principal = await this.executeRawAsyncUnsafe(() => this.actor.whoAmI());
65
+ return principal.toText();
66
+ }
67
+ /**
68
+ * Leftover request logs as JSON text (normally empty — logs are cleared per request).
69
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister.
70
+ */
71
+ async getLogsAsyncUnsafe() {
72
+ return this.executeRawAsyncUnsafe(() => this.actor.getLogs());
73
+ }
74
+ /**
75
+ * Clears leftover request logs.
76
+ * @throws Error with message `CriticalCanisterError` when there is no communication with the canister.
77
+ * @throws Error with message `CallRefusedAtInspectionStage` when the canister's `inspect` rejects the call before execution.
78
+ */
79
+ async clearLogsAsyncUnsafe() {
80
+ await this.executeRawAsyncUnsafe(() => this.actor.clearLogs());
81
+ }
82
+ }