@ic-reactor/cli 0.4.1 → 0.5.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.
@@ -1,144 +0,0 @@
1
- /**
2
- * List command
3
- *
4
- * List available methods from a canister's DID file.
5
- */
6
-
7
- import * as p from "@clack/prompts"
8
- import path from "node:path"
9
- import pc from "picocolors"
10
- import { loadConfig, getProjectRoot, findConfigFile } from "../utils/config.js"
11
- import { parseDIDFile } from "@ic-reactor/codegen"
12
-
13
- interface ListOptions {
14
- canister?: string
15
- }
16
-
17
- export async function listCommand(options: ListOptions) {
18
- console.log()
19
- p.intro(pc.cyan("📋 List Canister Methods"))
20
-
21
- // Load config
22
- const configPath = findConfigFile()
23
- if (!configPath) {
24
- p.log.error(
25
- `No ${pc.yellow("reactor.config.json")} found. Run ${pc.cyan("npx @ic-reactor/cli init")} first.`
26
- )
27
- process.exit(1)
28
- }
29
-
30
- const config = loadConfig(configPath)
31
- if (!config) {
32
- p.log.error(`Failed to load config from ${pc.yellow(configPath)}`)
33
- process.exit(1)
34
- }
35
-
36
- const projectRoot = getProjectRoot()
37
- const canisterNames = Object.keys(config.canisters)
38
-
39
- if (canisterNames.length === 0) {
40
- p.log.error(
41
- `No canisters configured. Add a canister to ${pc.yellow("reactor.config.json")} first.`
42
- )
43
- process.exit(1)
44
- }
45
-
46
- // Select canister
47
- let selectedCanister = options.canister
48
-
49
- if (!selectedCanister) {
50
- if (canisterNames.length === 1) {
51
- selectedCanister = canisterNames[0]
52
- } else {
53
- const result = await p.select({
54
- message: "Select a canister",
55
- options: canisterNames.map((name) => ({
56
- value: name,
57
- label: name,
58
- })),
59
- })
60
-
61
- if (p.isCancel(result)) {
62
- p.cancel("Cancelled.")
63
- process.exit(0)
64
- }
65
-
66
- selectedCanister = result as string
67
- }
68
- }
69
-
70
- const canisterConfig = config.canisters[selectedCanister]
71
- if (!canisterConfig) {
72
- p.log.error(`Canister ${pc.yellow(selectedCanister)} not found in config.`)
73
- process.exit(1)
74
- }
75
-
76
- // Parse DID file
77
- const didFilePath = path.resolve(projectRoot, canisterConfig.didFile)
78
-
79
- try {
80
- const methods = parseDIDFile(didFilePath)
81
-
82
- if (methods.length === 0) {
83
- p.log.warn(`No methods found in ${pc.yellow(didFilePath)}`)
84
- process.exit(0)
85
- }
86
-
87
- const queries = methods.filter((m) => m.type === "query")
88
- const mutations = methods.filter((m) => m.type === "mutation")
89
- const generatedMethods = config.generatedHooks[selectedCanister] ?? []
90
-
91
- // Display queries
92
- if (queries.length > 0) {
93
- console.log()
94
- console.log(pc.bold(pc.cyan(" Queries:")))
95
- for (const method of queries) {
96
- const isGenerated = generatedMethods.includes(method.name)
97
- const status = isGenerated ? pc.green("✓") : pc.dim("○")
98
- const argsHint = method.hasArgs ? pc.dim("(args)") : pc.dim("()")
99
- console.log(` ${status} ${method.name} ${argsHint}`)
100
- }
101
- }
102
-
103
- // Display mutations
104
- if (mutations.length > 0) {
105
- console.log()
106
- console.log(pc.bold(pc.yellow(" Mutations (Updates):")))
107
- for (const method of mutations) {
108
- const isGenerated = generatedMethods.includes(method.name)
109
- const status = isGenerated ? pc.green("✓") : pc.dim("○")
110
- const argsHint = method.hasArgs ? pc.dim("(args)") : pc.dim("()")
111
- console.log(` ${status} ${method.name} ${argsHint}`)
112
- }
113
- }
114
-
115
- // Summary
116
- console.log()
117
- const generatedCount = generatedMethods.length
118
- const totalCount = methods.length
119
-
120
- p.note(
121
- `Total: ${pc.bold(totalCount.toString())} methods\n` +
122
- `Generated: ${pc.green(generatedCount.toString())} / ${totalCount}\n\n` +
123
- `${pc.green("✓")} = hook generated\n` +
124
- `${pc.dim("○")} = not yet generated`,
125
- selectedCanister
126
- )
127
-
128
- if (generatedCount < totalCount) {
129
- console.log()
130
- console.log(
131
- pc.dim(
132
- ` Run ${pc.cyan(`npx @ic-reactor/cli add -c ${selectedCanister}`)} to add hooks`
133
- )
134
- )
135
- }
136
- } catch (error) {
137
- p.log.error(
138
- `Failed to parse DID file: ${pc.yellow(didFilePath)}\n${(error as Error).message}`
139
- )
140
- process.exit(1)
141
- }
142
-
143
- console.log()
144
- }
@@ -1,122 +0,0 @@
1
- /**
2
- * Sync command
3
- *
4
- * Regenerate hooks when DID files change.
5
- */
6
-
7
- import * as p from "@clack/prompts"
8
- import fs from "node:fs"
9
- import path from "node:path"
10
- import pc from "picocolors"
11
- import { loadConfig, getProjectRoot, findConfigFile } from "../utils/config.js"
12
- import { generateReactorFile } from "../generators/index.js"
13
- import { generateDeclarations } from "@ic-reactor/codegen"
14
-
15
- interface SyncOptions {
16
- canister?: string
17
- }
18
-
19
- export async function syncCommand(options: SyncOptions) {
20
- console.log()
21
- p.intro(pc.cyan("🔄 Sync Canister Hooks"))
22
-
23
- // Load config
24
- const configPath = findConfigFile()
25
- if (!configPath) {
26
- p.log.error(
27
- `No ${pc.yellow("reactor.config.json")} found. Run ${pc.cyan("npx @ic-reactor/cli init")} first.`
28
- )
29
- process.exit(1)
30
- }
31
-
32
- const config = loadConfig(configPath)
33
- if (!config) {
34
- p.log.error(`Failed to load config from ${pc.yellow(configPath)}`)
35
- process.exit(1)
36
- }
37
-
38
- const projectRoot = getProjectRoot()
39
- const canisterNames = Object.keys(config.canisters)
40
-
41
- if (canisterNames.length === 0) {
42
- p.log.error("No canisters configured.")
43
- process.exit(1)
44
- }
45
-
46
- // Select canisters to sync
47
- let canistersToSync: string[]
48
-
49
- if (options.canister) {
50
- if (!config.canisters[options.canister]) {
51
- p.log.error(
52
- `Canister ${pc.yellow(options.canister)} not found in config.`
53
- )
54
- process.exit(1)
55
- }
56
- canistersToSync = [options.canister]
57
- } else {
58
- canistersToSync = canisterNames
59
- }
60
-
61
- const spinner = p.spinner()
62
- spinner.start("Syncing hooks...")
63
-
64
- let totalUpdated = 0
65
- const errors: string[] = []
66
-
67
- for (const canisterName of canistersToSync) {
68
- const canisterConfig = config.canisters[canisterName]
69
-
70
- // Regenerate declarations if missing
71
- const canisterOutDir = path.join(projectRoot, config.outDir, canisterName)
72
- const didFilePath = path.resolve(projectRoot, canisterConfig.didFile)
73
-
74
- spinner.message(`Regenerating declarations for ${canisterName}...`)
75
-
76
- try {
77
- const bindgenResult = await generateDeclarations({
78
- didFile: didFilePath,
79
- outDir: canisterOutDir,
80
- canisterName,
81
- })
82
-
83
- if (!bindgenResult.success) {
84
- errors.push(`${canisterName}: ${bindgenResult.error}`)
85
- p.log.warn(
86
- `Could not regenerate declarations for ${canisterName}: ${bindgenResult.error}`
87
- )
88
- continue
89
- }
90
-
91
- // Regenerate index.ts
92
- const reactorPath = path.join(canisterOutDir, "index.ts")
93
-
94
- const reactorContent = generateReactorFile({
95
- canisterName,
96
- canisterConfig,
97
- config,
98
- })
99
- fs.writeFileSync(reactorPath, reactorContent)
100
- totalUpdated++
101
- } catch (error) {
102
- errors.push(`${canisterName}: ${(error as Error).message}`)
103
- p.log.error(`Failed to sync ${canisterName}: ${(error as Error).message}`)
104
- }
105
- }
106
-
107
- spinner.stop("Sync complete!")
108
-
109
- // Display results
110
- if (errors.length > 0) {
111
- console.log()
112
- p.log.error("Errors encountered:")
113
- for (const error of errors) {
114
- console.log(` ${pc.red("•")} ${error}`)
115
- }
116
- }
117
-
118
- console.log()
119
- p.note(`Updated: ${pc.green(totalUpdated.toString())} canisters`, "Summary")
120
-
121
- p.outro(pc.green("✓ Sync complete!"))
122
- }
@@ -1 +0,0 @@
1
- export { generateReactorFile } from "./reactor.js"
@@ -1,30 +0,0 @@
1
- /**
2
- * Reactor file generator
3
- *
4
- * Generates the shared reactor instance file for a canister.
5
- */
6
-
7
- import {
8
- type CanisterConfig,
9
- generateReactorFile as generateReactorFileFromCodegen,
10
- } from "@ic-reactor/codegen"
11
- import type { ReactorConfig } from "../types.js"
12
-
13
- export interface ReactorGeneratorOptions {
14
- canisterName: string
15
- canisterConfig: CanisterConfig
16
- config: ReactorConfig
17
- }
18
-
19
- /**
20
- * Generate the reactor.ts file content
21
- */
22
- export function generateReactorFile(options: ReactorGeneratorOptions): string {
23
- const { canisterName, canisterConfig, config } = options
24
-
25
- return generateReactorFileFromCodegen({
26
- canisterName,
27
- canisterConfig,
28
- globalClientManagerPath: config.clientManagerPath,
29
- })
30
- }