zkcloudworker 0.1.18 → 0.1.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. package/LICENSE +201 -0
  2. package/dist/README.md +79 -0
  3. package/dist/package.json +49 -0
  4. package/dist/yarn.lock +94 -0
  5. package/jest-config.ts +32 -0
  6. package/package.json +50 -40
  7. package/src/api/api.ts +250 -0
  8. package/src/api/client-api.ts +186 -0
  9. package/src/cloud/cloud.ts +22 -0
  10. package/src/cloud/local.ts +34 -0
  11. package/src/config.ts +9 -0
  12. package/src/custom/backend.ts +30 -0
  13. package/src/fee.ts +7 -0
  14. package/src/index.ts +38 -0
  15. package/src/mina.ts +230 -0
  16. package/src/networks.ts +88 -0
  17. package/tsconfig.json +41 -0
  18. package/tsconfig.web.json +29 -0
  19. /package/{lib → dist/lib}/ts/src/api/api.d.ts +0 -0
  20. /package/{lib → dist/lib}/ts/src/api/api.js +0 -0
  21. /package/{lib → dist/lib}/ts/src/api/client-api.d.ts +0 -0
  22. /package/{lib → dist/lib}/ts/src/api/client-api.js +0 -0
  23. /package/{lib → dist/lib}/ts/src/cloud/cloud.d.ts +0 -0
  24. /package/{lib → dist/lib}/ts/src/cloud/cloud.js +0 -0
  25. /package/{lib → dist/lib}/ts/src/cloud/local.d.ts +0 -0
  26. /package/{lib → dist/lib}/ts/src/cloud/local.js +0 -0
  27. /package/{lib → dist/lib}/ts/src/config.d.ts +0 -0
  28. /package/{lib → dist/lib}/ts/src/config.js +0 -0
  29. /package/{lib → dist/lib}/ts/src/custom/backend.d.ts +0 -0
  30. /package/{lib → dist/lib}/ts/src/custom/backend.js +0 -0
  31. /package/{lib → dist/lib}/ts/src/fee.d.ts +0 -0
  32. /package/{lib → dist/lib}/ts/src/fee.js +0 -0
  33. /package/{lib → dist/lib}/ts/src/index.d.ts +0 -0
  34. /package/{lib → dist/lib}/ts/src/index.js +0 -0
  35. /package/{lib → dist/lib}/ts/src/mina.d.ts +0 -0
  36. /package/{lib → dist/lib}/ts/src/mina.js +0 -0
  37. /package/{lib → dist/lib}/ts/src/networks.d.ts +0 -0
  38. /package/{lib → dist/lib}/ts/src/networks.js +0 -0
  39. /package/{lib → dist/lib}/ts/tsconfig.tsbuildinfo +0 -0
  40. /package/{lib → dist/lib}/web/src/api/api.d.ts +0 -0
  41. /package/{lib → dist/lib}/web/src/api/api.js +0 -0
  42. /package/{lib → dist/lib}/web/src/api/api.js.map +0 -0
  43. /package/{lib → dist/lib}/web/src/api/client-api.d.ts +0 -0
  44. /package/{lib → dist/lib}/web/src/api/client-api.js +0 -0
  45. /package/{lib → dist/lib}/web/src/api/client-api.js.map +0 -0
  46. /package/{lib → dist/lib}/web/src/cloud/cloud.d.ts +0 -0
  47. /package/{lib → dist/lib}/web/src/cloud/cloud.js +0 -0
  48. /package/{lib → dist/lib}/web/src/cloud/cloud.js.map +0 -0
  49. /package/{lib → dist/lib}/web/src/cloud/local.d.ts +0 -0
  50. /package/{lib → dist/lib}/web/src/cloud/local.js +0 -0
  51. /package/{lib → dist/lib}/web/src/cloud/local.js.map +0 -0
  52. /package/{lib → dist/lib}/web/src/config.d.ts +0 -0
  53. /package/{lib → dist/lib}/web/src/config.js +0 -0
  54. /package/{lib → dist/lib}/web/src/config.js.map +0 -0
  55. /package/{lib → dist/lib}/web/src/custom/backend.d.ts +0 -0
  56. /package/{lib → dist/lib}/web/src/custom/backend.js +0 -0
  57. /package/{lib → dist/lib}/web/src/custom/backend.js.map +0 -0
  58. /package/{lib → dist/lib}/web/src/fee.d.ts +0 -0
  59. /package/{lib → dist/lib}/web/src/fee.js +0 -0
  60. /package/{lib → dist/lib}/web/src/fee.js.map +0 -0
  61. /package/{lib → dist/lib}/web/src/index.d.ts +0 -0
  62. /package/{lib → dist/lib}/web/src/index.js +0 -0
  63. /package/{lib → dist/lib}/web/src/index.js.map +0 -0
  64. /package/{lib → dist/lib}/web/src/mina.d.ts +0 -0
  65. /package/{lib → dist/lib}/web/src/mina.js +0 -0
  66. /package/{lib → dist/lib}/web/src/mina.js.map +0 -0
  67. /package/{lib → dist/lib}/web/src/networks.d.ts +0 -0
  68. /package/{lib → dist/lib}/web/src/networks.js +0 -0
  69. /package/{lib → dist/lib}/web/src/networks.js.map +0 -0
  70. /package/{lib → dist/lib}/web/tsconfig.web.tsbuildinfo +0 -0
package/src/api/api.ts ADDED
@@ -0,0 +1,250 @@
1
+ import axios from "axios";
2
+ import { sleep } from "../mina";
3
+ import config from "../config";
4
+ const { ZKCLOUDWORKER_AUTH, ZKCLOUDWORKER_API } = config;
5
+
6
+ /**
7
+ * API class for interacting with the zkCloudWorker
8
+ * @property jwt The jwt token for authentication, get it at https://t.me/minanft_bot?start=auth
9
+ * @property endpoint The endpoint of the serverless api
10
+ */
11
+ export class zkCloudWorker {
12
+ jwt: string;
13
+ endpoint: string;
14
+
15
+ /**
16
+ * Constructor for the API class
17
+ * @param jwt The jwt token for authentication, get it at https://t.me/minanft_bot?start=auth
18
+ */
19
+ constructor(jwt: string) {
20
+ this.jwt = jwt;
21
+ this.endpoint = ZKCLOUDWORKER_API;
22
+ }
23
+
24
+ /**
25
+ * Starts a new job for the proof calculation using serverless api call
26
+ * The developer and name should correspond to the BackupPlugin of the API
27
+ * All other parameters should correspond to the parameters of the BackupPlugin
28
+ * @param data the data for the proof call
29
+ * @param data.transactions the transactions
30
+ * @param data.developer the developer
31
+ * @param data.name the name of the job
32
+ * @param data.task the task of the job
33
+ * @param data.args the arguments of the job
34
+ * @returns { success: boolean, error?: string, jobId?: string }
35
+ * where jonId is the jobId of the job
36
+ */
37
+ public async createJob(data: {
38
+ transactions: string[];
39
+ developer: string;
40
+ name: string;
41
+ task: string;
42
+ args: string[];
43
+ metadata?: string;
44
+ }): Promise<{
45
+ success: boolean;
46
+ error?: string;
47
+ jobId?: string;
48
+ }> {
49
+ const result = await this.apiHub("createJob", data);
50
+ if (result.data === "error")
51
+ return {
52
+ success: false,
53
+ error: result.error,
54
+ };
55
+ else
56
+ return {
57
+ success: result.success,
58
+ jobId: result.data,
59
+ error: result.error,
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Gets the result of the job using serverless api call
65
+ * @param data the data for the jobResult call
66
+ * @param data.jobId the jobId of the job
67
+ * @returns { success: boolean, error?: string, result?: any }
68
+ * where result is the result of the job
69
+ * if the job is not finished yet, the result will be undefined
70
+ * if the job failed, the result will be undefined and error will be set
71
+ * if the job is finished, the result will be set and error will be undefined
72
+ * if the job is not found, the result will be undefined and error will be set
73
+ */
74
+ public async jobResult(data: { jobId: string }): Promise<{
75
+ success: boolean;
76
+ error?: string;
77
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
78
+ result?: any;
79
+ }> {
80
+ const result = await this.apiHub("jobResult", data);
81
+ if (this.isError(result.data))
82
+ return {
83
+ success: false,
84
+ error: result.error,
85
+ result: result.data,
86
+ };
87
+ else
88
+ return {
89
+ success: result.success,
90
+ error: result.error,
91
+ result: result.data,
92
+ };
93
+ }
94
+
95
+ /**
96
+ * Gets the result of the job using serverless api call
97
+ * @param data the data for the deploy call
98
+ * @param data.packageName the name of the zip file with the code to be deployed
99
+ * @returns { success: boolean, error?: string, result?: any }
100
+ * where result is the result of the job
101
+ * if the job is not finished yet, the result will be undefined
102
+ * if the job failed, the result will be undefined and error will be set
103
+ * if the job is finished, the result will be set and error will be undefined
104
+ * if the job is not found, the result will be undefined and error will be set
105
+ */
106
+ public async deploy(data: { packageName: string }): Promise<{
107
+ success: boolean;
108
+ error?: string;
109
+ jobId?: string;
110
+ }> {
111
+ const result = await this.apiHub("deploy", data);
112
+ if (result.data === "error")
113
+ return {
114
+ success: false,
115
+ error: result.error,
116
+ };
117
+ else
118
+ return {
119
+ success: result.success,
120
+ jobId: result.data,
121
+ error: result.error,
122
+ };
123
+ }
124
+
125
+ /**
126
+ * Gets the billing report for the jobs sent using JWT
127
+ * @returns { success: boolean, error?: string, result?: any }
128
+ * where result is the billing report
129
+ */
130
+ public async queryBilling(): Promise<{
131
+ success: boolean;
132
+ error?: string;
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ result?: any;
135
+ }> {
136
+ const result = await this.apiHub("queryBilling", {});
137
+ if (this.isError(result.data))
138
+ return {
139
+ success: false,
140
+ error: result.error,
141
+ result: result.data,
142
+ };
143
+ else
144
+ return {
145
+ success: result.success,
146
+ error: result.error,
147
+ result: result.data,
148
+ };
149
+ }
150
+
151
+ /**
152
+ * Waits for the job to finish
153
+ * @param data the data for the waitForJobResult call
154
+ * @param data.jobId the jobId of the job
155
+ * @param data.maxAttempts the maximum number of attempts, default is 360 (2 hours)
156
+ * @param data.interval the interval between attempts, default is 20000 (20 seconds)
157
+ * @param data.maxErrors the maximum number of network errors, default is 10
158
+ * @returns { success: boolean, error?: string, result?: any }
159
+ * where result is the result of the job
160
+ */
161
+ public async waitForJobResult(data: {
162
+ jobId: string;
163
+ maxAttempts?: number;
164
+ interval?: number;
165
+ maxErrors?: number;
166
+ }): Promise<{
167
+ success: boolean;
168
+ error?: string;
169
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
+ result?: any;
171
+ }> {
172
+ const maxAttempts = data?.maxAttempts ?? 360; // 2 hours
173
+ const interval = data?.interval ?? 20000;
174
+ const maxErrors = data?.maxErrors ?? 10;
175
+ const errorDelay = 30000; // 30 seconds
176
+ let attempts = 0;
177
+ let errors = 0;
178
+ while (attempts < maxAttempts) {
179
+ const result = await this.apiHub("jobResult", data);
180
+ if (result.success === false) {
181
+ errors++;
182
+ if (errors > maxErrors) {
183
+ return {
184
+ success: false,
185
+ error: "Too many network errors",
186
+ result: undefined,
187
+ };
188
+ }
189
+ await sleep(errorDelay * errors);
190
+ } else {
191
+ if (this.isError(result.data))
192
+ return {
193
+ success: false,
194
+ error: result.error,
195
+ result: result.data,
196
+ };
197
+ else if (result.data?.result !== undefined) {
198
+ return {
199
+ success: result.success,
200
+ error: result.error,
201
+ result: result.data,
202
+ };
203
+ }
204
+ await sleep(interval);
205
+ }
206
+ attempts++;
207
+ }
208
+ return {
209
+ success: false,
210
+ error: "Timeout",
211
+ result: undefined,
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Calls the serverless API
217
+ * @param command the command of the API
218
+ * @param data the data of the API
219
+ * */
220
+ private async apiHub(
221
+ command: string,
222
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
223
+ data: any
224
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
225
+ ): Promise<{ success: boolean; data?: any; error?: any }> {
226
+ const apiData = {
227
+ auth: ZKCLOUDWORKER_AUTH,
228
+ command: command,
229
+ jwtToken: this.jwt,
230
+ data: data,
231
+ };
232
+
233
+ try {
234
+ const response = await axios.post(this.endpoint, apiData);
235
+ return { success: true, data: response.data };
236
+ } catch (error: any) {
237
+ console.error("apiHub error:", error.message ?? error);
238
+ return { success: false, error: error };
239
+ }
240
+ }
241
+
242
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
243
+ private isError(data: any): boolean {
244
+ if (data === "error") return true;
245
+ if (data?.jobStatus === "failed") return true;
246
+ if (typeof data === "string" && data.toLowerCase().startsWith("error"))
247
+ return true;
248
+ return false;
249
+ }
250
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Client API for calling the zkCloudWorker
3
+ */
4
+ export {
5
+ TxnPayload,
6
+ IsError,
7
+ SerializedTxn,
8
+ SignedSerializedTxn,
9
+ TxnResult,
10
+ JobPayload,
11
+ JobResult,
12
+ zkCloudWorkerAPI,
13
+ };
14
+
15
+ interface TxnPayload {
16
+ data: object;
17
+ options?: object;
18
+ }
19
+
20
+ interface IsError {
21
+ code: number;
22
+ message: string;
23
+ exception: any;
24
+ }
25
+
26
+ interface SerializedTxn {
27
+ hash: string | null;
28
+ transaction: any | null;
29
+ error: IsError | null;
30
+ }
31
+
32
+ interface SignedSerializedTxn {
33
+ hash: string | null;
34
+ transaction: any | null;
35
+ error: IsError | null;
36
+ }
37
+
38
+ interface TxnResult {
39
+ hash: string | null;
40
+ data: any | null;
41
+ error: IsError | null;
42
+ }
43
+
44
+ interface JobPayload {
45
+ data: object;
46
+ options?: object;
47
+ }
48
+
49
+ interface JobResult {
50
+ data: any | null;
51
+ error: IsError | null;
52
+ }
53
+
54
+ class zkCloudWorkerAPI {
55
+ private API_KEY: string = "";
56
+
57
+ constructor(apiKey: string) {
58
+ this.API_KEY = apiKey;
59
+ }
60
+
61
+ /**
62
+ * prove() sign() and send()
63
+ *
64
+ * The called cloud worker is expected to compile the needed Contract,
65
+ * create the transaction, prove it and send it back serialized.
66
+ *
67
+ * Then the serialized transaction can be signed locally using AuroWallet
68
+ * and finally send it to MINA using the cloud worker.
69
+ *
70
+ * IMPORTANT: the transaction fee will be paid by the local sender, using
71
+ * the Auro Wallet at the moment of signing the serialized transaction.
72
+ *
73
+ * We only need the sender public key to create and prove the transaction.
74
+ * The sender private key NEVER leaves the local wallet.
75
+ *
76
+ * Example:
77
+ * ~~~
78
+ * let zkWorker = new ZKRunner(API_KEY);
79
+ *
80
+ * let serializedTxn = await zkWorker.prove('batch-voting-...', {
81
+ * data: {
82
+ * claimUid: '012345...789',
83
+ * // ...
84
+ * },
85
+ * options: {
86
+ * senderAddress: 'B62...',
87
+ * fee: MIN_FEE // MAX_FEE | AUTO_FEE | number
88
+ * }
89
+ * });
90
+ * if (serializedTxn.error)
91
+ * // treat error here
92
+ *
93
+ * let signedTxn = await zkWorker.sign(signerAddress, serializedTxn);
94
+ * if (signedTxn.error)
95
+ * // treat error here
96
+ *
97
+ * let txnResult = await zkWorker.send(signedTxn) ;
98
+ * if (txnResult.error)
99
+ * // treat error here
100
+ * ~~~
101
+ */
102
+ async prove(
103
+ jobName: string,
104
+ payload: TxnPayload //: Promise < SerializedTxn > {
105
+ ) {}
106
+
107
+ async sign(
108
+ signerAddress: string,
109
+ serializedTxn: SerializedTxn //: Promise<SignedSerializedTxn> {
110
+ ) {}
111
+
112
+ async send(
113
+ txn: SignedSerializedTxn //: Promise<TxnResult> {
114
+ ) {}
115
+
116
+ /**
117
+ * proveAndSend()
118
+ *
119
+ * The called cloud worker is expected to do all: compile the needed Contract,
120
+ * create the transaction, prove it, sign it using one of the available
121
+ * fee payers, and finally send it to MINA.
122
+ *
123
+ * IMPORTANT: the transaction fee will be paid by the first fee payer
124
+ * available from the list of fee payers provided by the ZKRunner service.
125
+ * Also the fee will be set by the cloud worker using some optimal algorithm
126
+ * that minimizes fees.
127
+ *
128
+ * In this case the sender public key to create and prove the transaction
129
+ * will be the selected ZKRunner fee payer previously mentioned.
130
+ *
131
+ * Example:
132
+ * ~~~
133
+ * let zkWorker = new ZKRunner(API_KEY);
134
+ *
135
+ * let txnResult = await zkWorker.proveAndSend('batch-voting-...', {
136
+ * data: {
137
+ * claimUid: '012345...789',
138
+ * // ...
139
+ * }
140
+ * });
141
+ *
142
+ * if (txnResult.error)
143
+ * // treat error here
144
+ * ~~~
145
+ */
146
+ async proveAndSend(
147
+ jobName: string,
148
+ payload: TxnPayload //: Promise<TxnResult> {
149
+ ) {}
150
+
151
+ /**
152
+ * runJob()
153
+ *
154
+ * The called cloud worker can also be used to easily run jobs not related to
155
+ * a MINA transaction, and will act just like any serverless function.
156
+ *
157
+ * This "generic" job can benefit from the easy to use deploy and call service
158
+ * already implemented for cloud proving without no extra costs.
159
+ *
160
+ * IMPORTANT: there will be a small fee that needs to be paid for service usage,
161
+ * but no MINA fees need to be paid.
162
+ *
163
+ * Example:
164
+ * ~~~
165
+ * let zkWorker = new ZKRunner(API_KEY);
166
+ *
167
+ * let jobResult = await zkWorker.runJob('send-email-to-judges', {
168
+ * data: {
169
+ * judges: [
170
+ * // ...
171
+ * ],
172
+ * },
173
+ * options: {
174
+ * // ...
175
+ * }
176
+ * });
177
+ *
178
+ * if (txnResult.error)
179
+ * // treat error here
180
+ * ~~~
181
+ */
182
+ async runJob(
183
+ jobName: string,
184
+ payload: JobPayload //: Promise<JobResult> {
185
+ ) {}
186
+ }
@@ -0,0 +1,22 @@
1
+ import { Cache, PrivateKey } from "o1js";
2
+
3
+ export abstract class Cloud {
4
+ cache: Cache;
5
+ constructor(cache: Cache) {
6
+ this.cache = cache;
7
+ }
8
+ // TODO: change it to the sign method to protect the private key
9
+ abstract getDeployer(): Promise<PrivateKey>;
10
+ abstract log(msg: string): void;
11
+
12
+ /* TODO: add more methods
13
+ -getDataByKey
14
+ -saveDataByKey
15
+ -saveFile
16
+ -loadFile
17
+ */
18
+ abstract getDataByKey(key: string): Promise<string | undefined>;
19
+ abstract saveDataByKey(key: string, value: string): Promise<void>;
20
+ abstract saveFile(filename: string, value: Buffer): Promise<void>;
21
+ abstract loadFile(filename: string): Promise<Buffer | undefined>;
22
+ }
@@ -0,0 +1,34 @@
1
+ import { Cache, PrivateKey } from "o1js";
2
+ import { Cloud } from "./cloud";
3
+
4
+ export class LocalCloud extends Cloud {
5
+ cache: Cache;
6
+ data: Map<string, string> = new Map<string, string>();
7
+
8
+ constructor() {
9
+ const cache = Cache.FileSystem("./cache");
10
+ super(cache);
11
+ }
12
+ public async getDeployer(): Promise<PrivateKey> {
13
+ throw new Error("Method not implemented.");
14
+ }
15
+ public async log(msg: string): Promise<void> {
16
+ console.log("LocalCloud:", msg);
17
+ }
18
+
19
+ public async getDataByKey(key: string): Promise<string | undefined> {
20
+ const value = this.data.get(key);
21
+ return value;
22
+ }
23
+
24
+ public async saveDataByKey(key: string, value: string): Promise<void> {
25
+ this.data.set(key, value);
26
+ }
27
+
28
+ public async saveFile(filename: string, value: Buffer): Promise<void> {
29
+ throw new Error("Method not implemented.");
30
+ }
31
+ public async loadFile(filename: string): Promise<Buffer | undefined> {
32
+ throw new Error("Method not implemented.");
33
+ }
34
+ }
package/src/config.ts ADDED
@@ -0,0 +1,9 @@
1
+ const config = {
2
+ MINAFEE: "200000000",
3
+ ZKCLOUDWORKER_AUTH:
4
+ "M6t4jtbBAFFXhLERHQWyEB9JA9xi4cWqmYduaCXtbrFjb7yaY7TyaXDunKDJNiUTBEcyUomNXJgC",
5
+ ZKCLOUDWORKER_API:
6
+ "https://cuq99yahhi.execute-api.eu-west-1.amazonaws.com/dev/zkcloudworker",
7
+ };
8
+
9
+ export default config;
@@ -0,0 +1,30 @@
1
+ export { BackendPlugin };
2
+ import type { Cache } from "o1js";
3
+
4
+ abstract class BackendPlugin {
5
+ name: string;
6
+ task: string;
7
+ args: string[];
8
+ jobId?: string;
9
+
10
+ constructor(params: {
11
+ name: string;
12
+ task: string;
13
+ args: string[];
14
+ jobId?: string;
15
+ }) {
16
+ const { name, task, args, jobId } = params;
17
+ this.name = name;
18
+ this.task = task;
19
+ this.args = args;
20
+ this.jobId = jobId;
21
+ }
22
+
23
+ abstract compile(cache: Cache): Promise<void>;
24
+ abstract create(transaction: string): Promise<string | undefined>;
25
+ abstract merge(proof1: string, proof2: string): Promise<string | undefined>;
26
+
27
+ abstract send(transaction: string): Promise<string | undefined>;
28
+ abstract mint(transaction: string): Promise<string | undefined>;
29
+ abstract verify(proof: string): Promise<string | undefined>;
30
+ }
package/src/fee.ts ADDED
@@ -0,0 +1,7 @@
1
+ import { UInt64 } from "o1js";
2
+ import config from "./config";
3
+
4
+ export async function fee(): Promise<UInt64> {
5
+ //TODO: update after mainnet launch
6
+ return UInt64.fromJSON(config.MINAFEE);
7
+ }
package/src/index.ts ADDED
@@ -0,0 +1,38 @@
1
+ export { zkCloudWorker } from "./api/api";
2
+ export { Cloud } from "./cloud/cloud";
3
+ export { LocalCloud } from "./cloud/local";
4
+ export {
5
+ initBlockchain,
6
+ Memory,
7
+ makeString,
8
+ sleep,
9
+ accountBalance,
10
+ accountBalanceMina,
11
+ formatTime,
12
+ MinaNetworkInstance,
13
+ currentNetwork,
14
+ getNetworkIdHash,
15
+ } from "./mina";
16
+ export { fee } from "./fee";
17
+ export {
18
+ blockchain,
19
+ MinaNetwork,
20
+ networks,
21
+ Mainnet,
22
+ Berkeley,
23
+ Zeko,
24
+ TestWorld2,
25
+ Lightnet,
26
+ Local,
27
+ } from "./networks";
28
+ export {
29
+ TxnPayload,
30
+ IsError,
31
+ SerializedTxn,
32
+ SignedSerializedTxn,
33
+ TxnResult,
34
+ JobPayload,
35
+ JobResult,
36
+ zkCloudWorkerAPI,
37
+ } from "./api/client-api";
38
+ export { BackendPlugin } from "./custom/backend";