@ic-reactor/cli 0.4.0 → 0.5.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.
- package/README.md +42 -39
- package/dist/index.js +96 -281
- package/package.json +2 -2
- package/schema.json +21 -46
- package/src/commands/generate.ts +114 -0
- package/src/commands/init.ts +57 -124
- package/src/index.ts +12 -19
- package/src/types.ts +18 -17
- package/src/utils/config.ts +6 -29
- package/src/commands/list.ts +0 -144
- package/src/commands/sync.ts +0 -122
- package/src/generators/index.ts +0 -1
- package/src/generators/reactor.ts +0 -30
package/src/commands/list.ts
DELETED
|
@@ -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
|
-
}
|
package/src/commands/sync.ts
DELETED
|
@@ -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
|
-
}
|
package/src/generators/index.ts
DELETED
|
@@ -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
|
-
}
|