movehat 0.1.3 โ†’ 0.1.4

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 (58) hide show
  1. package/README.md +159 -80
  2. package/dist/commands/run.d.ts.map +1 -1
  3. package/dist/commands/run.js +40 -3
  4. package/dist/commands/run.js.map +1 -1
  5. package/dist/core/AccountManager.d.ts +186 -0
  6. package/dist/core/AccountManager.d.ts.map +1 -0
  7. package/dist/core/AccountManager.js +363 -0
  8. package/dist/core/AccountManager.js.map +1 -0
  9. package/dist/fork/manager.d.ts +33 -0
  10. package/dist/fork/manager.d.ts.map +1 -1
  11. package/dist/fork/manager.js +61 -0
  12. package/dist/fork/manager.js.map +1 -1
  13. package/dist/fork/storage.d.ts +10 -0
  14. package/dist/fork/storage.d.ts.map +1 -1
  15. package/dist/fork/storage.js +28 -1
  16. package/dist/fork/storage.js.map +1 -1
  17. package/dist/helpers/index.d.ts +8 -1
  18. package/dist/helpers/index.d.ts.map +1 -1
  19. package/dist/helpers/index.js +4 -0
  20. package/dist/helpers/index.js.map +1 -1
  21. package/dist/helpers/setup.d.ts.map +1 -1
  22. package/dist/helpers/setup.js +5 -4
  23. package/dist/helpers/setup.js.map +1 -1
  24. package/dist/helpers/setupLocalTesting.d.ts +60 -0
  25. package/dist/helpers/setupLocalTesting.d.ts.map +1 -0
  26. package/dist/helpers/setupLocalTesting.js +277 -0
  27. package/dist/helpers/setupLocalTesting.js.map +1 -0
  28. package/dist/helpers/testFixtures.d.ts +115 -0
  29. package/dist/helpers/testFixtures.d.ts.map +1 -0
  30. package/dist/helpers/testFixtures.js +163 -0
  31. package/dist/helpers/testFixtures.js.map +1 -0
  32. package/dist/node/LocalNodeManager.d.ts +68 -0
  33. package/dist/node/LocalNodeManager.d.ts.map +1 -0
  34. package/dist/node/LocalNodeManager.js +237 -0
  35. package/dist/node/LocalNodeManager.js.map +1 -0
  36. package/dist/runtime.d.ts.map +1 -1
  37. package/dist/runtime.js +91 -46
  38. package/dist/runtime.js.map +1 -1
  39. package/dist/templates/README.md +17 -4
  40. package/dist/templates/move/sources/Counter.move +12 -4
  41. package/dist/templates/tests/Counter.test.ts +98 -57
  42. package/dist/types/config.d.ts +24 -0
  43. package/dist/types/config.d.ts.map +1 -1
  44. package/package.json +1 -1
  45. package/src/commands/run.ts +43 -3
  46. package/src/core/AccountManager.ts +439 -0
  47. package/src/fork/manager.ts +74 -0
  48. package/src/fork/storage.ts +33 -1
  49. package/src/helpers/index.ts +18 -1
  50. package/src/helpers/setup.ts +4 -3
  51. package/src/helpers/setupLocalTesting.ts +335 -0
  52. package/src/helpers/testFixtures.ts +222 -0
  53. package/src/node/LocalNodeManager.ts +297 -0
  54. package/src/runtime.ts +108 -47
  55. package/src/templates/README.md +17 -4
  56. package/src/templates/move/sources/Counter.move +12 -4
  57. package/src/templates/tests/Counter.test.ts +98 -57
  58. package/src/types/config.ts +32 -0
@@ -0,0 +1,163 @@
1
+ import { AccountManager } from "../core/AccountManager.js";
2
+ import { setupLocalTesting, stopLocalTesting } from "./setupLocalTesting.js";
3
+ /**
4
+ * Setup a complete test fixture with local fork, accounts, and deployed contracts
5
+ *
6
+ * This is the recommended way to setup tests in movehat. It provides:
7
+ * - Local fork server (no testnet required)
8
+ * - Pre-funded labeled accounts
9
+ * - Auto-deployment of specified modules
10
+ * - Contract instances ready to use
11
+ *
12
+ * @param modules Array of module names to auto-deploy
13
+ * @param accountLabels Optional array of account labels (defaults to ['alice', 'bob'])
14
+ * @param options Optional LocalTestOptions for advanced configuration
15
+ * @returns TestFixture with runtime, accounts, and contracts
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * import { setupTestFixture } from "movehat/helpers";
20
+ *
21
+ * describe("Counter Contract", () => {
22
+ * let fixture: TestFixture;
23
+ *
24
+ * before(async function () {
25
+ * this.timeout(60000); // Allow time for fork + deployment
26
+ *
27
+ * fixture = await setupTestFixture(['counter'], ['alice', 'bob']);
28
+ * });
29
+ *
30
+ * it("should initialize with value 0", async () => {
31
+ * const counter = fixture.contracts.counter;
32
+ * const value = await counter.view<number>("get", [
33
+ * fixture.accounts.deployer.accountAddress.toString()
34
+ * ]);
35
+ *
36
+ * expect(value).to.equal(0);
37
+ * });
38
+ *
39
+ * it("alice can increment counter", async () => {
40
+ * const tx = await fixture.contracts.counter.call(
41
+ * fixture.accounts.alice,
42
+ * "increment",
43
+ * []
44
+ * );
45
+ *
46
+ * expect(tx.success).to.be.true;
47
+ * });
48
+ *
49
+ * after(async () => {
50
+ * await teardownTestFixture();
51
+ * });
52
+ * });
53
+ * ```
54
+ */
55
+ export async function setupTestFixture(modules, accountLabels = ["alice", "bob"], options = {}) {
56
+ console.log(`\n๐Ÿงช Setting up test fixture...`);
57
+ console.log(` Modules to deploy: ${modules.join(", ")}`);
58
+ console.log(` Account labels: deployer, ${accountLabels.join(", ")}\n`);
59
+ // Ensure 'deployer' is always in the account labels
60
+ const allLabels = ["deployer", ...accountLabels.filter((l) => l !== "deployer")];
61
+ // Setup local testing environment with auto-deploy
62
+ const setupOptions = {
63
+ ...options,
64
+ accountLabels: allLabels,
65
+ autoDeploy: modules, // Auto-deploy specified modules
66
+ };
67
+ const mh = await setupLocalTesting(setupOptions);
68
+ // Get all labeled accounts
69
+ const labeledAccounts = AccountManager.getLabeledAccounts();
70
+ // Build accounts object with required accounts
71
+ const accounts = {
72
+ deployer: labeledAccounts.deployer,
73
+ };
74
+ // Add other labeled accounts
75
+ for (const label of accountLabels) {
76
+ accounts[label] = labeledAccounts[label] || AccountManager.getOrCreateLabeled(label);
77
+ }
78
+ // Build contracts object - TypeScript will infer the correct type
79
+ const contracts = {};
80
+ for (const moduleName of modules) {
81
+ // Get deployed contract instance
82
+ const deploymentAddress = mh.getDeploymentAddress(moduleName);
83
+ if (!deploymentAddress) {
84
+ throw new Error(`Module "${moduleName}" was not deployed. Check auto-deploy logs.`);
85
+ }
86
+ contracts[moduleName] = mh.getContract(deploymentAddress, moduleName);
87
+ console.log(` โœ“ Contract "${moduleName}" ready at ${deploymentAddress}`);
88
+ }
89
+ console.log(`\nโœ… Test fixture ready!\n`);
90
+ return {
91
+ mh,
92
+ accounts,
93
+ contracts,
94
+ };
95
+ }
96
+ /**
97
+ * Teardown test fixture and cleanup resources
98
+ *
99
+ * Call this in your test suite's `after` hook to properly cleanup:
100
+ * - Stops fork server
101
+ * - Clears account pool
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * after(async () => {
106
+ * await teardownTestFixture();
107
+ * });
108
+ * ```
109
+ */
110
+ export async function teardownTestFixture() {
111
+ console.log(`\n๐Ÿงน Tearing down test fixture...`);
112
+ // Stop fork server
113
+ await stopLocalTesting();
114
+ // Clear account pool for test isolation
115
+ AccountManager.clearPool();
116
+ console.log(`โœ“ Teardown complete\n`);
117
+ }
118
+ /**
119
+ * Create a minimal test fixture without auto-deployment
120
+ * Useful when you want to deploy contracts manually in tests
121
+ *
122
+ * @param accountLabels Account labels to create (defaults to ['alice', 'bob'])
123
+ * @param options Optional LocalTestOptions
124
+ * @returns Partial TestFixture (without contracts)
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * let fixture: Partial<TestFixture>;
129
+ *
130
+ * before(async function () {
131
+ * this.timeout(30000);
132
+ * fixture = await setupMinimalFixture(['alice', 'bob', 'charlie']);
133
+ * });
134
+ *
135
+ * it("should deploy contract manually", async () => {
136
+ * await fixture.mh!.deployContract("counter");
137
+ * // ...
138
+ * });
139
+ * ```
140
+ */
141
+ export async function setupMinimalFixture(accountLabels = ["alice", "bob"], options = {}) {
142
+ console.log(`\n๐Ÿงช Setting up minimal test fixture (no auto-deploy)...`);
143
+ const allLabels = ["deployer", ...accountLabels.filter((l) => l !== "deployer")];
144
+ const setupOptions = {
145
+ ...options,
146
+ accountLabels: allLabels,
147
+ autoDeploy: [], // No auto-deploy
148
+ };
149
+ const mh = await setupLocalTesting(setupOptions);
150
+ const labeledAccounts = AccountManager.getLabeledAccounts();
151
+ const accounts = {
152
+ deployer: labeledAccounts.deployer,
153
+ };
154
+ for (const label of accountLabels) {
155
+ accounts[label] = labeledAccounts[label] || AccountManager.getOrCreateLabeled(label);
156
+ }
157
+ console.log(`\nโœ… Minimal fixture ready!\n`);
158
+ return {
159
+ mh,
160
+ accounts,
161
+ };
162
+ }
163
+ //# sourceMappingURL=testFixtures.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testFixtures.js","sourceRoot":"","sources":["../../src/helpers/testFixtures.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
1
+ import { Account } from "@aptos-labs/ts-sdk";
2
+ export interface LocalNodeOptions {
3
+ testDir?: string;
4
+ forceRestart?: boolean;
5
+ faucetPort?: number;
6
+ apiPort?: number;
7
+ readyPort?: number;
8
+ silent?: boolean;
9
+ }
10
+ export interface LocalNodeInfo {
11
+ rpcUrl: string;
12
+ faucetUrl: string;
13
+ readyUrl: string;
14
+ testDir: string;
15
+ }
16
+ /**
17
+ * Manages the lifecycle of a local Movement node
18
+ *
19
+ * This class handles:
20
+ * - Starting/stopping a local Movement node
21
+ * - Health checks and ready detection
22
+ * - Auto-funding from the built-in faucet
23
+ * - Cleanup on shutdown
24
+ */
25
+ export declare class LocalNodeManager {
26
+ private process;
27
+ private options;
28
+ private starting;
29
+ constructor(options?: LocalNodeOptions);
30
+ /**
31
+ * Start the local Movement node
32
+ *
33
+ * @returns LocalNodeInfo with connection details
34
+ */
35
+ start(): Promise<LocalNodeInfo>;
36
+ /**
37
+ * Stop the local node
38
+ */
39
+ stop(): Promise<void>;
40
+ /**
41
+ * Check if the node is running
42
+ */
43
+ isRunning(): boolean;
44
+ /**
45
+ * Get node connection info
46
+ */
47
+ getNodeInfo(): LocalNodeInfo;
48
+ /**
49
+ * Wait for the node to be ready by checking the ready endpoint
50
+ */
51
+ private waitForReady;
52
+ /**
53
+ * Fund an account using the local faucet
54
+ *
55
+ * @param account Account instance or address to fund
56
+ * @param amount Amount in octas (default: 100_000_000 = 100 APT)
57
+ */
58
+ fundAccount(account: Account | string, amount?: number): Promise<void>;
59
+ /**
60
+ * Fund multiple accounts in batch
61
+ */
62
+ fundAccounts(accounts: (Account | string)[], amount?: number): Promise<void>;
63
+ /**
64
+ * Clean up node data directory
65
+ */
66
+ clean(): Promise<void>;
67
+ }
68
+ //# sourceMappingURL=LocalNodeManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalNodeManager.d.ts","sourceRoot":"","sources":["../../src/node/LocalNodeManager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAkB;gBAEtB,OAAO,GAAE,gBAAqB;IAW1C;;;;OAIG;IACG,KAAK,IAAI,OAAO,CAAC,aAAa,CAAC;IAiGrC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAgC3B;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,WAAW,IAAI,aAAa;IAS5B;;OAEG;YACW,YAAY;IA0B1B;;;;;OAKG;IACG,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,EAAE,MAAM,GAAE,MAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCzF;;OAEG;IACG,YAAY,CAAC,QAAQ,EAAE,CAAC,OAAO,GAAG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAE,MAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAU/F;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAW7B"}
@@ -0,0 +1,237 @@
1
+ import { spawn } from "child_process";
2
+ import { existsSync, rmSync } from "fs";
3
+ import { join } from "path";
4
+ /**
5
+ * Manages the lifecycle of a local Movement node
6
+ *
7
+ * This class handles:
8
+ * - Starting/stopping a local Movement node
9
+ * - Health checks and ready detection
10
+ * - Auto-funding from the built-in faucet
11
+ * - Cleanup on shutdown
12
+ */
13
+ export class LocalNodeManager {
14
+ process = null;
15
+ options;
16
+ starting = false;
17
+ constructor(options = {}) {
18
+ this.options = {
19
+ testDir: options.testDir || join(process.cwd(), ".movehat", "local-node"),
20
+ forceRestart: options.forceRestart ?? false,
21
+ faucetPort: options.faucetPort || 8081,
22
+ apiPort: options.apiPort || 8080,
23
+ readyPort: options.readyPort || 8070,
24
+ silent: options.silent ?? false,
25
+ };
26
+ }
27
+ /**
28
+ * Start the local Movement node
29
+ *
30
+ * @returns LocalNodeInfo with connection details
31
+ */
32
+ async start() {
33
+ if (this.process) {
34
+ console.log("Local node already running");
35
+ return this.getNodeInfo();
36
+ }
37
+ if (this.starting) {
38
+ throw new Error("Node is already starting");
39
+ }
40
+ this.starting = true;
41
+ try {
42
+ console.log("\n๐Ÿš€ Starting local Movement node...");
43
+ console.log(` Test directory: ${this.options.testDir}`);
44
+ console.log(` RPC port: ${this.options.apiPort}`);
45
+ console.log(` Faucet port: ${this.options.faucetPort}`);
46
+ console.log(` Ready port: ${this.options.readyPort}\n`);
47
+ // Clean state if force restart
48
+ if (this.options.forceRestart && existsSync(this.options.testDir)) {
49
+ console.log("๐Ÿงน Cleaning previous node state...");
50
+ rmSync(this.options.testDir, { recursive: true, force: true });
51
+ }
52
+ // Build command arguments
53
+ const args = [
54
+ "node",
55
+ "run-localnet",
56
+ "--test-dir", this.options.testDir,
57
+ "--faucet-port", this.options.faucetPort.toString(),
58
+ "--ready-server-listen-port", this.options.readyPort.toString(),
59
+ "--assume-yes", // Auto-accept prompts
60
+ ];
61
+ if (this.options.forceRestart) {
62
+ args.push("--force-restart");
63
+ }
64
+ // Start the node process
65
+ this.process = spawn("movement", args, {
66
+ stdio: this.options.silent ? "ignore" : "pipe",
67
+ detached: false,
68
+ });
69
+ // Handle process output
70
+ if (!this.options.silent && this.process.stdout && this.process.stderr) {
71
+ this.process.stdout.on("data", (data) => {
72
+ const output = data.toString().trim();
73
+ if (output) {
74
+ console.log(`[Node] ${output}`);
75
+ }
76
+ });
77
+ this.process.stderr.on("data", (data) => {
78
+ const output = data.toString().trim();
79
+ if (output && !output.includes("WARN")) {
80
+ console.error(`[Node Error] ${output}`);
81
+ }
82
+ });
83
+ }
84
+ // Handle process exit
85
+ this.process.on("exit", (code) => {
86
+ if (code !== 0 && code !== null) {
87
+ console.error(`\nโŒ Local node exited with code ${code}`);
88
+ }
89
+ this.process = null;
90
+ });
91
+ this.process.on("error", (error) => {
92
+ console.error(`\nโŒ Failed to start local node: ${error.message}`);
93
+ this.process = null;
94
+ });
95
+ // Wait for node to be ready
96
+ console.log("โณ Waiting for node to be ready...");
97
+ await this.waitForReady(60000); // 60 second timeout
98
+ console.log("โœ… Local Movement node is ready!\n");
99
+ this.starting = false;
100
+ return this.getNodeInfo();
101
+ }
102
+ catch (error) {
103
+ this.starting = false;
104
+ // Cleanup on failure
105
+ if (this.process) {
106
+ this.process.kill();
107
+ this.process = null;
108
+ }
109
+ throw error;
110
+ }
111
+ }
112
+ /**
113
+ * Stop the local node
114
+ */
115
+ async stop() {
116
+ if (!this.process) {
117
+ return;
118
+ }
119
+ console.log("\n๐Ÿ›‘ Stopping local Movement node...");
120
+ return new Promise((resolve) => {
121
+ if (!this.process) {
122
+ resolve();
123
+ return;
124
+ }
125
+ this.process.once("exit", () => {
126
+ console.log("โœ… Local node stopped\n");
127
+ this.process = null;
128
+ resolve();
129
+ });
130
+ // Send SIGTERM for graceful shutdown
131
+ this.process.kill("SIGTERM");
132
+ // Force kill after 5 seconds if still running
133
+ setTimeout(() => {
134
+ if (this.process) {
135
+ console.log("โš ๏ธ Force killing node...");
136
+ this.process.kill("SIGKILL");
137
+ }
138
+ }, 5000);
139
+ });
140
+ }
141
+ /**
142
+ * Check if the node is running
143
+ */
144
+ isRunning() {
145
+ return this.process !== null && !this.process.killed;
146
+ }
147
+ /**
148
+ * Get node connection info
149
+ */
150
+ getNodeInfo() {
151
+ return {
152
+ rpcUrl: `http://127.0.0.1:${this.options.apiPort}`,
153
+ faucetUrl: `http://127.0.0.1:${this.options.faucetPort}`,
154
+ readyUrl: `http://127.0.0.1:${this.options.readyPort}`,
155
+ testDir: this.options.testDir,
156
+ };
157
+ }
158
+ /**
159
+ * Wait for the node to be ready by checking the ready endpoint
160
+ */
161
+ async waitForReady(timeoutMs = 60000) {
162
+ const startTime = Date.now();
163
+ const readyUrl = `http://127.0.0.1:${this.options.readyPort}`;
164
+ while (Date.now() - startTime < timeoutMs) {
165
+ try {
166
+ const response = await fetch(readyUrl);
167
+ if (response.ok) {
168
+ return; // Node is ready!
169
+ }
170
+ }
171
+ catch (error) {
172
+ // Node not ready yet, continue waiting
173
+ }
174
+ // Wait 1 second before next check
175
+ await new Promise((resolve) => setTimeout(resolve, 1000));
176
+ }
177
+ throw new Error(`Local node did not become ready within ${timeoutMs / 1000} seconds. ` +
178
+ `Check that 'movement' CLI is installed and ports ${this.options.apiPort}, ` +
179
+ `${this.options.faucetPort}, and ${this.options.readyPort} are available.`);
180
+ }
181
+ /**
182
+ * Fund an account using the local faucet
183
+ *
184
+ * @param account Account instance or address to fund
185
+ * @param amount Amount in octas (default: 100_000_000 = 100 APT)
186
+ */
187
+ async fundAccount(account, amount = 100_000_000) {
188
+ if (!this.isRunning()) {
189
+ throw new Error("Local node is not running. Call start() first.");
190
+ }
191
+ // Extract address if Account is provided
192
+ const address = typeof account === 'string'
193
+ ? account
194
+ : account.accountAddress.toString();
195
+ // Use query parameters for Aptos/Movement faucet
196
+ const faucetUrl = `http://127.0.0.1:${this.options.faucetPort}/mint?amount=${amount}&address=${address}`;
197
+ try {
198
+ const response = await fetch(faucetUrl, {
199
+ method: "POST",
200
+ });
201
+ if (!response.ok) {
202
+ const errorText = await response.text();
203
+ throw new Error(`Faucet request failed: ${errorText}`);
204
+ }
205
+ const result = await response.json();
206
+ console.log(` โœ“ Funded ${address} with ${amount} octas`);
207
+ return result;
208
+ }
209
+ catch (error) {
210
+ throw new Error(`Failed to fund account: ${error.message}`);
211
+ }
212
+ }
213
+ /**
214
+ * Fund multiple accounts in batch
215
+ */
216
+ async fundAccounts(accounts, amount = 100_000_000) {
217
+ console.log(`\n๐Ÿ’ฐ Funding ${accounts.length} accounts from local faucet...`);
218
+ for (const account of accounts) {
219
+ await this.fundAccount(account, amount);
220
+ }
221
+ console.log(`โœ“ All accounts funded successfully\n`);
222
+ }
223
+ /**
224
+ * Clean up node data directory
225
+ */
226
+ async clean() {
227
+ if (this.isRunning()) {
228
+ throw new Error("Cannot clean while node is running. Stop the node first.");
229
+ }
230
+ if (existsSync(this.options.testDir)) {
231
+ console.log(`๐Ÿงน Cleaning node data at ${this.options.testDir}...`);
232
+ rmSync(this.options.testDir, { recursive: true, force: true });
233
+ console.log("โœ“ Node data cleaned");
234
+ }
235
+ }
236
+ }
237
+ //# sourceMappingURL=LocalNodeManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LocalNodeManager.js","sourceRoot":"","sources":["../../src/node/LocalNodeManager.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAe,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAetD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC,CA+PzB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAO3C;AAED;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,cAAc,CAAC,CAK1D;AAGD,eAAO,MAAM,EAAE;;CAId,CAAC"}
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,cAAc,EAAe,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAgBtD,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;CAC7C;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,cAAc,CAAC,CA2TzB;AAED;;;GAGG;AACH,wBAAgB,UAAU,IAAI,cAAc,CAO3C;AAED;;;GAGG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,cAAc,CAAC,CAK1D;AAGD,eAAO,MAAM,EAAE;;CAId,CAAC"}