@ic-reactor/cli 0.1.3 → 0.3.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 +51 -193
- package/dist/index.js +77 -1080
- package/package.json +3 -3
- package/src/commands/index.ts +0 -2
- package/src/commands/init.ts +7 -7
- package/src/commands/sync.ts +20 -157
- package/src/generators/index.ts +0 -7
- package/src/generators/reactor.ts +1 -12
- package/src/index.ts +0 -20
- package/src/types.ts +1 -1
- package/src/utils/config.ts +2 -2
- package/src/commands/add.ts +0 -447
- package/src/commands/fetch.ts +0 -600
- package/src/generators/infiniteQuery.ts +0 -34
- package/src/generators/mutation.ts +0 -29
- package/src/generators/query.ts +0 -32
package/src/commands/fetch.ts
DELETED
|
@@ -1,600 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fetch command
|
|
3
|
-
*
|
|
4
|
-
* Fetch Candid interface from a live canister and generate hooks.
|
|
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 {
|
|
12
|
-
loadConfig,
|
|
13
|
-
saveConfig,
|
|
14
|
-
getProjectRoot,
|
|
15
|
-
ensureDir,
|
|
16
|
-
findConfigFile,
|
|
17
|
-
CONFIG_FILE_NAME,
|
|
18
|
-
DEFAULT_CONFIG,
|
|
19
|
-
} from "../utils/config.js"
|
|
20
|
-
import { formatMethodForDisplay } from "@ic-reactor/codegen"
|
|
21
|
-
import {
|
|
22
|
-
generateQueryHook,
|
|
23
|
-
generateMutationHook,
|
|
24
|
-
generateInfiniteQueryHook,
|
|
25
|
-
} from "../generators/index.js"
|
|
26
|
-
import { getHookFileName, toCamelCase } from "@ic-reactor/codegen"
|
|
27
|
-
import {
|
|
28
|
-
fetchCandidFromCanister,
|
|
29
|
-
isValidCanisterId,
|
|
30
|
-
shortenCanisterId,
|
|
31
|
-
type NetworkType,
|
|
32
|
-
} from "../utils/network.js"
|
|
33
|
-
import { generateDeclarations } from "@ic-reactor/codegen"
|
|
34
|
-
import type { MethodInfo, HookType, CanisterConfig } from "@ic-reactor/codegen"
|
|
35
|
-
import type { ReactorConfig } from "../types.js"
|
|
36
|
-
|
|
37
|
-
interface FetchOptions {
|
|
38
|
-
canisterId?: string
|
|
39
|
-
network?: NetworkType
|
|
40
|
-
name?: string
|
|
41
|
-
methods?: string[]
|
|
42
|
-
all?: boolean
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function fetchCommand(options: FetchOptions) {
|
|
46
|
-
console.log()
|
|
47
|
-
p.intro(pc.cyan("🌐 Fetch from Live Canister"))
|
|
48
|
-
|
|
49
|
-
const projectRoot = getProjectRoot()
|
|
50
|
-
|
|
51
|
-
// Get canister ID
|
|
52
|
-
let canisterId = options.canisterId
|
|
53
|
-
|
|
54
|
-
if (!canisterId) {
|
|
55
|
-
const input = await p.text({
|
|
56
|
-
message: "Enter canister ID",
|
|
57
|
-
placeholder: "ryjl3-tyaaa-aaaaa-aaaba-cai",
|
|
58
|
-
validate: (value) => {
|
|
59
|
-
if (!value) return "Canister ID is required"
|
|
60
|
-
if (!isValidCanisterId(value)) {
|
|
61
|
-
return "Invalid canister ID format"
|
|
62
|
-
}
|
|
63
|
-
return undefined
|
|
64
|
-
},
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
if (p.isCancel(input)) {
|
|
68
|
-
p.cancel("Cancelled.")
|
|
69
|
-
process.exit(0)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
canisterId = input as string
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Validate canister ID
|
|
76
|
-
if (!isValidCanisterId(canisterId)) {
|
|
77
|
-
p.log.error(`Invalid canister ID: ${pc.yellow(canisterId)}`)
|
|
78
|
-
process.exit(1)
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// Get network
|
|
82
|
-
let network = options.network
|
|
83
|
-
|
|
84
|
-
if (!network) {
|
|
85
|
-
const result = await p.select({
|
|
86
|
-
message: "Select network",
|
|
87
|
-
options: [
|
|
88
|
-
{ value: "ic", label: "IC Mainnet", hint: "icp-api.io" },
|
|
89
|
-
{ value: "local", label: "Local Replica", hint: "localhost:4943" },
|
|
90
|
-
],
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
if (p.isCancel(result)) {
|
|
94
|
-
p.cancel("Cancelled.")
|
|
95
|
-
process.exit(0)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
network = result as NetworkType
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Fetch Candid from canister
|
|
102
|
-
const spinner = p.spinner()
|
|
103
|
-
spinner.start(`Fetching Candid from ${shortenCanisterId(canisterId)}...`)
|
|
104
|
-
|
|
105
|
-
let candidResult
|
|
106
|
-
try {
|
|
107
|
-
candidResult = await fetchCandidFromCanister({
|
|
108
|
-
canisterId,
|
|
109
|
-
network,
|
|
110
|
-
})
|
|
111
|
-
spinner.stop(
|
|
112
|
-
`Found ${pc.green(candidResult.methods.length.toString())} methods`
|
|
113
|
-
)
|
|
114
|
-
} catch (error) {
|
|
115
|
-
spinner.stop("Failed to fetch Candid")
|
|
116
|
-
p.log.error((error as Error).message)
|
|
117
|
-
process.exit(1)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
const { methods, candidSource } = candidResult
|
|
121
|
-
|
|
122
|
-
if (methods.length === 0) {
|
|
123
|
-
p.log.warn("No methods found in canister interface")
|
|
124
|
-
process.exit(0)
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Get canister name
|
|
128
|
-
let canisterName = options.name
|
|
129
|
-
|
|
130
|
-
if (!canisterName) {
|
|
131
|
-
const input = await p.text({
|
|
132
|
-
message: "Name for this canister (used in generated code)",
|
|
133
|
-
placeholder: toCamelCase(canisterId.split("-")[0]),
|
|
134
|
-
defaultValue: toCamelCase(canisterId.split("-")[0]),
|
|
135
|
-
validate: (value) => {
|
|
136
|
-
if (!value) return "Name is required"
|
|
137
|
-
if (!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(value)) {
|
|
138
|
-
return "Name must start with a letter and contain only letters, numbers, hyphens, and underscores"
|
|
139
|
-
}
|
|
140
|
-
return undefined
|
|
141
|
-
},
|
|
142
|
-
})
|
|
143
|
-
|
|
144
|
-
if (p.isCancel(input)) {
|
|
145
|
-
p.cancel("Cancelled.")
|
|
146
|
-
process.exit(0)
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
canisterName = input as string
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Select methods
|
|
153
|
-
let selectedMethods: MethodInfo[]
|
|
154
|
-
|
|
155
|
-
if (options.all) {
|
|
156
|
-
selectedMethods = methods
|
|
157
|
-
} else if (options.methods && options.methods.length > 0) {
|
|
158
|
-
// Parse comma-separated method names
|
|
159
|
-
const requestedMethods = options.methods
|
|
160
|
-
.flatMap((m) => m.split(","))
|
|
161
|
-
.map((m) => m.trim())
|
|
162
|
-
.filter((m) => m.length > 0)
|
|
163
|
-
|
|
164
|
-
selectedMethods = methods.filter((m) => requestedMethods.includes(m.name))
|
|
165
|
-
const notFound = requestedMethods.filter(
|
|
166
|
-
(name) => !methods.some((m) => m.name === name)
|
|
167
|
-
)
|
|
168
|
-
if (notFound.length > 0) {
|
|
169
|
-
p.log.warn(`Methods not found: ${pc.yellow(notFound.join(", "))}`)
|
|
170
|
-
}
|
|
171
|
-
} else {
|
|
172
|
-
// Interactive selection
|
|
173
|
-
const result = await p.multiselect({
|
|
174
|
-
message: "Select methods to generate hooks for",
|
|
175
|
-
options: methods.map((method) => ({
|
|
176
|
-
value: method.name,
|
|
177
|
-
label: formatMethodForDisplay(method),
|
|
178
|
-
})),
|
|
179
|
-
required: true,
|
|
180
|
-
})
|
|
181
|
-
|
|
182
|
-
if (p.isCancel(result)) {
|
|
183
|
-
p.cancel("Cancelled.")
|
|
184
|
-
process.exit(0)
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
selectedMethods = methods.filter((m) =>
|
|
188
|
-
(result as string[]).includes(m.name)
|
|
189
|
-
)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (selectedMethods.length === 0) {
|
|
193
|
-
p.log.warn("No methods selected.")
|
|
194
|
-
process.exit(0)
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Prompt for hook types for query methods
|
|
198
|
-
const methodsWithHookTypes: Array<{
|
|
199
|
-
method: MethodInfo
|
|
200
|
-
hookType: HookType
|
|
201
|
-
}> = []
|
|
202
|
-
|
|
203
|
-
for (const method of selectedMethods) {
|
|
204
|
-
if (method.type === "query") {
|
|
205
|
-
const hookType = await p.select({
|
|
206
|
-
message: `Hook type for ${pc.cyan(method.name)}`,
|
|
207
|
-
options: [
|
|
208
|
-
{
|
|
209
|
-
value: "skip",
|
|
210
|
-
label: "Skip",
|
|
211
|
-
hint: "Don't generate hook for this method",
|
|
212
|
-
},
|
|
213
|
-
{ value: "query", label: "Query", hint: "Standard query hook" },
|
|
214
|
-
{
|
|
215
|
-
value: "suspenseQuery",
|
|
216
|
-
label: "Suspense Query",
|
|
217
|
-
hint: "For React Suspense",
|
|
218
|
-
},
|
|
219
|
-
{
|
|
220
|
-
value: "infiniteQuery",
|
|
221
|
-
label: "Infinite Query",
|
|
222
|
-
hint: "Paginated/infinite scroll",
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
value: "suspenseInfiniteQuery",
|
|
226
|
-
label: "Suspense Infinite Query",
|
|
227
|
-
hint: "Paginated with Suspense",
|
|
228
|
-
},
|
|
229
|
-
],
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
if (p.isCancel(hookType)) {
|
|
233
|
-
p.cancel("Cancelled.")
|
|
234
|
-
process.exit(0)
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Skip this method if user chose "skip"
|
|
238
|
-
if (hookType === "skip") {
|
|
239
|
-
p.log.info(`Skipping ${pc.dim(method.name)}`)
|
|
240
|
-
continue
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
methodsWithHookTypes.push({ method, hookType: hookType as HookType })
|
|
244
|
-
} else {
|
|
245
|
-
// For mutations, also allow skipping
|
|
246
|
-
const hookType = await p.select({
|
|
247
|
-
message: `Hook type for ${pc.yellow(method.name)} (mutation)`,
|
|
248
|
-
options: [
|
|
249
|
-
{
|
|
250
|
-
value: "mutation",
|
|
251
|
-
label: "Mutation",
|
|
252
|
-
hint: "Standard mutation hook",
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
value: "skip",
|
|
256
|
-
label: "Skip",
|
|
257
|
-
hint: "Don't generate hook for this method",
|
|
258
|
-
},
|
|
259
|
-
],
|
|
260
|
-
})
|
|
261
|
-
|
|
262
|
-
if (p.isCancel(hookType)) {
|
|
263
|
-
p.cancel("Cancelled.")
|
|
264
|
-
process.exit(0)
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
if (hookType === "skip") {
|
|
268
|
-
p.log.info(`Skipping ${pc.dim(method.name)}`)
|
|
269
|
-
continue
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
methodsWithHookTypes.push({ method, hookType: "mutation" })
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// Check if any methods were selected after skipping
|
|
277
|
-
if (methodsWithHookTypes.length === 0) {
|
|
278
|
-
p.log.warn("All methods were skipped. Nothing to generate.")
|
|
279
|
-
process.exit(0)
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
// Load or create config
|
|
283
|
-
let configPath = findConfigFile()
|
|
284
|
-
let config: ReactorConfig
|
|
285
|
-
|
|
286
|
-
if (!configPath) {
|
|
287
|
-
// Create default config
|
|
288
|
-
configPath = path.join(projectRoot, CONFIG_FILE_NAME)
|
|
289
|
-
config = { ...DEFAULT_CONFIG }
|
|
290
|
-
p.log.info(`Creating ${pc.yellow(CONFIG_FILE_NAME)}`)
|
|
291
|
-
} else {
|
|
292
|
-
config = loadConfig(configPath) ?? { ...DEFAULT_CONFIG }
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// Add canister to config
|
|
296
|
-
const canisterConfig: CanisterConfig = {
|
|
297
|
-
didFile: `./candid/${canisterName}.did`,
|
|
298
|
-
useDisplayReactor: true,
|
|
299
|
-
canisterId: canisterId,
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
config.canisters[canisterName] = canisterConfig
|
|
303
|
-
|
|
304
|
-
// Generate files
|
|
305
|
-
const canisterOutDir = path.join(projectRoot, config.outDir, canisterName)
|
|
306
|
-
const hooksOutDir = path.join(canisterOutDir, "hooks")
|
|
307
|
-
const candidDir = path.join(projectRoot, "candid")
|
|
308
|
-
|
|
309
|
-
ensureDir(hooksOutDir)
|
|
310
|
-
ensureDir(candidDir)
|
|
311
|
-
|
|
312
|
-
const genSpinner = p.spinner()
|
|
313
|
-
genSpinner.start("Generating hooks...")
|
|
314
|
-
|
|
315
|
-
const generatedFiles: string[] = []
|
|
316
|
-
|
|
317
|
-
// Save the Candid source file
|
|
318
|
-
const candidPath = path.join(candidDir, `${canisterName}.did`)
|
|
319
|
-
fs.writeFileSync(candidPath, candidSource)
|
|
320
|
-
generatedFiles.push(`candid/${canisterName}.did`)
|
|
321
|
-
|
|
322
|
-
// Generate TypeScript declarations using bindgen
|
|
323
|
-
genSpinner.message("Generating TypeScript declarations...")
|
|
324
|
-
const bindgenResult = await generateDeclarations({
|
|
325
|
-
didFile: candidPath,
|
|
326
|
-
outDir: canisterOutDir,
|
|
327
|
-
canisterName,
|
|
328
|
-
})
|
|
329
|
-
|
|
330
|
-
if (bindgenResult.success) {
|
|
331
|
-
generatedFiles.push("declarations/")
|
|
332
|
-
} else {
|
|
333
|
-
p.log.warn(`Could not generate declarations: ${bindgenResult.error}`)
|
|
334
|
-
p.log.info(
|
|
335
|
-
`You can manually run: npx @icp-sdk/bindgen --input ${candidPath} --output ${canisterOutDir}/declarations`
|
|
336
|
-
)
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
// Generate reactor.ts
|
|
340
|
-
genSpinner.message("Generating reactor...")
|
|
341
|
-
const reactorPath = path.join(canisterOutDir, "reactor.ts")
|
|
342
|
-
const reactorContent = generateReactorFileForFetch({
|
|
343
|
-
canisterName,
|
|
344
|
-
canisterConfig,
|
|
345
|
-
config,
|
|
346
|
-
canisterId,
|
|
347
|
-
hasDeclarations: bindgenResult.success,
|
|
348
|
-
})
|
|
349
|
-
fs.writeFileSync(reactorPath, reactorContent)
|
|
350
|
-
generatedFiles.push("reactor.ts")
|
|
351
|
-
|
|
352
|
-
// Generate hooks for each method
|
|
353
|
-
for (const { method, hookType } of methodsWithHookTypes) {
|
|
354
|
-
const fileName = getHookFileName(method.name, hookType)
|
|
355
|
-
const filePath = path.join(hooksOutDir, fileName)
|
|
356
|
-
|
|
357
|
-
let content: string
|
|
358
|
-
|
|
359
|
-
switch (hookType) {
|
|
360
|
-
case "query":
|
|
361
|
-
case "suspenseQuery":
|
|
362
|
-
content = generateQueryHook({
|
|
363
|
-
canisterName,
|
|
364
|
-
method,
|
|
365
|
-
config,
|
|
366
|
-
})
|
|
367
|
-
break
|
|
368
|
-
case "infiniteQuery":
|
|
369
|
-
case "suspenseInfiniteQuery":
|
|
370
|
-
content = generateInfiniteQueryHook({
|
|
371
|
-
canisterName,
|
|
372
|
-
method,
|
|
373
|
-
config,
|
|
374
|
-
})
|
|
375
|
-
break
|
|
376
|
-
case "mutation":
|
|
377
|
-
content = generateMutationHook({
|
|
378
|
-
canisterName,
|
|
379
|
-
method,
|
|
380
|
-
config,
|
|
381
|
-
})
|
|
382
|
-
break
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
fs.writeFileSync(filePath, content)
|
|
386
|
-
generatedFiles.push(path.join("hooks", fileName))
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
// Generate index.ts barrel export
|
|
390
|
-
const indexPath = path.join(hooksOutDir, "index.ts")
|
|
391
|
-
const indexContent = generateIndexFile(methodsWithHookTypes)
|
|
392
|
-
fs.writeFileSync(indexPath, indexContent)
|
|
393
|
-
generatedFiles.push("hooks/index.ts")
|
|
394
|
-
|
|
395
|
-
// Update config with generated hooks
|
|
396
|
-
config.generatedHooks[canisterName] = [
|
|
397
|
-
...new Set([
|
|
398
|
-
...(config.generatedHooks[canisterName] ?? []),
|
|
399
|
-
...selectedMethods.map((m) => m.name),
|
|
400
|
-
]),
|
|
401
|
-
]
|
|
402
|
-
saveConfig(config, configPath)
|
|
403
|
-
|
|
404
|
-
genSpinner.stop("Hooks generated!")
|
|
405
|
-
|
|
406
|
-
// Display results
|
|
407
|
-
console.log()
|
|
408
|
-
p.note(
|
|
409
|
-
generatedFiles.map((f) => pc.green(`✓ ${f}`)).join("\n"),
|
|
410
|
-
`Generated in ${pc.dim(path.relative(projectRoot, canisterOutDir))}`
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
console.log()
|
|
414
|
-
p.note(
|
|
415
|
-
`Canister ID: ${pc.cyan(canisterId)}\n` +
|
|
416
|
-
`Network: ${pc.yellow(network)}\n` +
|
|
417
|
-
`Name: ${pc.green(canisterName)}\n` +
|
|
418
|
-
`Methods: ${pc.dim(selectedMethods.map((m) => m.name).join(", "))}`,
|
|
419
|
-
"Canister Info"
|
|
420
|
-
)
|
|
421
|
-
|
|
422
|
-
p.outro(pc.green("✓ Done!"))
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
/**
|
|
426
|
-
* Generate reactor file specifically for fetched canisters
|
|
427
|
-
* (with canister ID hardcoded or from environment)
|
|
428
|
-
*/
|
|
429
|
-
function generateReactorFileForFetch(options: {
|
|
430
|
-
canisterName: string
|
|
431
|
-
canisterConfig: CanisterConfig
|
|
432
|
-
config: ReactorConfig
|
|
433
|
-
canisterId: string
|
|
434
|
-
hasDeclarations: boolean
|
|
435
|
-
}): string {
|
|
436
|
-
const { canisterName, canisterConfig, config, canisterId, hasDeclarations } =
|
|
437
|
-
options
|
|
438
|
-
|
|
439
|
-
const pascalName =
|
|
440
|
-
canisterName.charAt(0).toUpperCase() + canisterName.slice(1)
|
|
441
|
-
const reactorName = `${canisterName}Reactor`
|
|
442
|
-
const serviceName = `${pascalName}Service`
|
|
443
|
-
const reactorType =
|
|
444
|
-
canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor"
|
|
445
|
-
|
|
446
|
-
// Calculate relative path to client manager (canister-specific → global → default)
|
|
447
|
-
const clientManagerPath =
|
|
448
|
-
canisterConfig.clientManagerPath ??
|
|
449
|
-
config.clientManagerPath ??
|
|
450
|
-
"../../lib/client"
|
|
451
|
-
|
|
452
|
-
// Calculate relative path to declarations (use DID file name)
|
|
453
|
-
const didFileName = path.basename(canisterConfig.didFile)
|
|
454
|
-
|
|
455
|
-
// Generate different imports based on whether declarations were generated
|
|
456
|
-
if (hasDeclarations) {
|
|
457
|
-
return `/**
|
|
458
|
-
* ${pascalName} Reactor
|
|
459
|
-
*
|
|
460
|
-
* Auto-generated by @ic-reactor/cli
|
|
461
|
-
* Fetched from canister: ${canisterId}
|
|
462
|
-
*
|
|
463
|
-
* You can customize this file to add global configuration.
|
|
464
|
-
*/
|
|
465
|
-
|
|
466
|
-
import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
467
|
-
import { clientManager } from "${clientManagerPath}"
|
|
468
|
-
|
|
469
|
-
// Import generated declarations
|
|
470
|
-
import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${didFileName}"
|
|
471
|
-
|
|
472
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
473
|
-
// REACTOR INSTANCE
|
|
474
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* ${pascalName} Reactor
|
|
478
|
-
*
|
|
479
|
-
* Canister ID: ${canisterId}
|
|
480
|
-
*/
|
|
481
|
-
export const ${reactorName} = new ${reactorType}<${serviceName}>({
|
|
482
|
-
clientManager,
|
|
483
|
-
idlFactory,
|
|
484
|
-
canisterId: "${canisterId}",
|
|
485
|
-
name: "${canisterName}",
|
|
486
|
-
})
|
|
487
|
-
|
|
488
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
489
|
-
// ACTOR HOOKS
|
|
490
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Actor hooks for ${canisterName} - use these directly or import method-specific hooks.
|
|
494
|
-
*/
|
|
495
|
-
export const {
|
|
496
|
-
useActorQuery,
|
|
497
|
-
useActorMutation,
|
|
498
|
-
useActorSuspenseQuery,
|
|
499
|
-
useActorInfiniteQuery,
|
|
500
|
-
useActorSuspenseInfiniteQuery,
|
|
501
|
-
useActorMethod,
|
|
502
|
-
} = createActorHooks(${reactorName})
|
|
503
|
-
|
|
504
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
505
|
-
// RE-EXPORTS
|
|
506
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
507
|
-
|
|
508
|
-
export { idlFactory }
|
|
509
|
-
export type { ${serviceName} }
|
|
510
|
-
`
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
// Fallback when declarations weren't generated
|
|
514
|
-
return `/**
|
|
515
|
-
* ${pascalName} Reactor
|
|
516
|
-
*
|
|
517
|
-
* Auto-generated by @ic-reactor/cli
|
|
518
|
-
* Fetched from canister: ${canisterId}
|
|
519
|
-
*
|
|
520
|
-
* You can customize this file to add global configuration.
|
|
521
|
-
*/
|
|
522
|
-
|
|
523
|
-
import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
524
|
-
import { clientManager } from "${clientManagerPath}"
|
|
525
|
-
|
|
526
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
527
|
-
// DECLARATIONS
|
|
528
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
529
|
-
|
|
530
|
-
// TODO: Generate proper types by running:
|
|
531
|
-
// npx @icp-sdk/bindgen --input ./candid/${canisterName}.did --output ./${canisterName}/declarations
|
|
532
|
-
|
|
533
|
-
// For now, import just the IDL factory (you may need to create this manually)
|
|
534
|
-
// import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${didFileName}"
|
|
535
|
-
|
|
536
|
-
// Fallback generic type - replace with generated types
|
|
537
|
-
type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
|
|
538
|
-
|
|
539
|
-
// You'll need to define idlFactory here or import from declarations
|
|
540
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
541
|
-
const idlFactory = ({ IDL }: { IDL: any }) => IDL.Service({})
|
|
542
|
-
|
|
543
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
544
|
-
// REACTOR INSTANCE
|
|
545
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* ${pascalName} Reactor
|
|
549
|
-
*
|
|
550
|
-
* Canister ID: ${canisterId}
|
|
551
|
-
*/
|
|
552
|
-
export const ${reactorName} = new ${reactorType}<${serviceName}>({
|
|
553
|
-
clientManager,
|
|
554
|
-
idlFactory,
|
|
555
|
-
canisterId: "${canisterId}",
|
|
556
|
-
name: "${canisterName}",
|
|
557
|
-
})
|
|
558
|
-
|
|
559
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
560
|
-
// ACTOR HOOKS
|
|
561
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
562
|
-
|
|
563
|
-
/**
|
|
564
|
-
* Actor hooks for ${canisterName} - use these directly or import method-specific hooks.
|
|
565
|
-
*/
|
|
566
|
-
export const {
|
|
567
|
-
useActorQuery,
|
|
568
|
-
useActorMutation,
|
|
569
|
-
useActorSuspenseQuery,
|
|
570
|
-
useActorInfiniteQuery,
|
|
571
|
-
useActorSuspenseInfiniteQuery,
|
|
572
|
-
useActorMethod,
|
|
573
|
-
} = createActorHooks(${reactorName})
|
|
574
|
-
|
|
575
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
576
|
-
// RE-EXPORTS
|
|
577
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
578
|
-
|
|
579
|
-
export { idlFactory }
|
|
580
|
-
export type { ${serviceName} }
|
|
581
|
-
`
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
function generateIndexFile(
|
|
585
|
-
methods: Array<{ method: MethodInfo; hookType: HookType }>
|
|
586
|
-
): string {
|
|
587
|
-
const exports = methods.map(({ method, hookType }) => {
|
|
588
|
-
const fileName = getHookFileName(method.name, hookType).replace(".ts", "")
|
|
589
|
-
return `export * from "./${fileName}"`
|
|
590
|
-
})
|
|
591
|
-
|
|
592
|
-
return `/**
|
|
593
|
-
* Hook barrel exports
|
|
594
|
-
*
|
|
595
|
-
* Auto-generated by @ic-reactor/cli
|
|
596
|
-
*/
|
|
597
|
-
|
|
598
|
-
${exports.join("\n")}
|
|
599
|
-
`
|
|
600
|
-
}
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Infinite Query hook generator
|
|
3
|
-
*
|
|
4
|
-
* Generates createInfiniteQuery-based hooks for paginated canister methods.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
type MethodInfo,
|
|
9
|
-
type HookType,
|
|
10
|
-
generateInfiniteQueryHook as generateInfiniteQueryHookFromCodegen,
|
|
11
|
-
} from "@ic-reactor/codegen"
|
|
12
|
-
import type { ReactorConfig } from "../types.js"
|
|
13
|
-
|
|
14
|
-
export interface InfiniteQueryHookOptions {
|
|
15
|
-
canisterName: string
|
|
16
|
-
method: MethodInfo
|
|
17
|
-
type?: HookType
|
|
18
|
-
config: ReactorConfig // Unused
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate an infinite query hook file content
|
|
23
|
-
*/
|
|
24
|
-
export function generateInfiniteQueryHook(
|
|
25
|
-
options: InfiniteQueryHookOptions
|
|
26
|
-
): string {
|
|
27
|
-
const { canisterName, method, type } = options
|
|
28
|
-
|
|
29
|
-
return generateInfiniteQueryHookFromCodegen({
|
|
30
|
-
canisterName,
|
|
31
|
-
method,
|
|
32
|
-
type,
|
|
33
|
-
})
|
|
34
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mutation hook generator
|
|
3
|
-
*
|
|
4
|
-
* Generates createMutation-based hooks for canister update methods.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
type MethodInfo,
|
|
9
|
-
generateMutationHook as generateMutationHookFromCodegen,
|
|
10
|
-
} from "@ic-reactor/codegen"
|
|
11
|
-
import type { ReactorConfig } from "../types.js"
|
|
12
|
-
|
|
13
|
-
export interface MutationHookOptions {
|
|
14
|
-
canisterName: string
|
|
15
|
-
method: MethodInfo
|
|
16
|
-
config: ReactorConfig // Unused
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Generate a mutation hook file content
|
|
21
|
-
*/
|
|
22
|
-
export function generateMutationHook(options: MutationHookOptions): string {
|
|
23
|
-
const { canisterName, method } = options
|
|
24
|
-
|
|
25
|
-
return generateMutationHookFromCodegen({
|
|
26
|
-
canisterName,
|
|
27
|
-
method,
|
|
28
|
-
})
|
|
29
|
-
}
|
package/src/generators/query.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Query hook generator
|
|
3
|
-
*
|
|
4
|
-
* Generates createQuery-based hooks for canister query methods.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import {
|
|
8
|
-
type MethodInfo,
|
|
9
|
-
type HookType,
|
|
10
|
-
generateQueryHook as generateQueryHookFromCodegen,
|
|
11
|
-
} from "@ic-reactor/codegen"
|
|
12
|
-
import type { ReactorConfig } from "../types.js"
|
|
13
|
-
|
|
14
|
-
export interface QueryHookOptions {
|
|
15
|
-
canisterName: string
|
|
16
|
-
method: MethodInfo
|
|
17
|
-
config: ReactorConfig // Not used by codegen but kept for CLI consistency if needed
|
|
18
|
-
type?: HookType
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate a query hook file content
|
|
23
|
-
*/
|
|
24
|
-
export function generateQueryHook(options: QueryHookOptions): string {
|
|
25
|
-
const { canisterName, method, type } = options
|
|
26
|
-
|
|
27
|
-
return generateQueryHookFromCodegen({
|
|
28
|
-
canisterName,
|
|
29
|
-
method,
|
|
30
|
-
type,
|
|
31
|
-
})
|
|
32
|
-
}
|