@ic-reactor/cli 0.0.0-dev2 → 0.1.3

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/dist/index.js CHANGED
@@ -227,539 +227,74 @@ export const clientManager = new ClientManager({
227
227
 
228
228
  // src/commands/add.ts
229
229
  import * as p2 from "@clack/prompts";
230
- import fs4 from "fs";
230
+ import fs3 from "fs";
231
231
  import path3 from "path";
232
232
  import pc2 from "picocolors";
233
-
234
- // src/parsers/did.ts
235
- import fs3 from "fs";
236
- function parseDIDFile(didFilePath) {
237
- if (!fs3.existsSync(didFilePath)) {
238
- throw new Error(`DID file not found: ${didFilePath}`);
239
- }
240
- const content = fs3.readFileSync(didFilePath, "utf-8");
241
- return extractMethods(content);
242
- }
243
- function extractMethods(didContent) {
244
- const methods = [];
245
- const cleanContent = didContent.replace(/\/\/.*$/gm, "").replace(/\/\*[\s\S]*?\*\//g, "");
246
- const methodRegex = /([a-zA-Z_][a-zA-Z0-9_]*)\s*:\s*(?:func\s*)?\(([^)]*)\)\s*->\s*\(([^)]*)\)\s*(query|composite_query)?/g;
247
- let match;
248
- while ((match = methodRegex.exec(cleanContent)) !== null) {
249
- const name = match[1];
250
- const args = match[2].trim();
251
- const returnType = match[3].trim();
252
- const queryAnnotation = match[4];
253
- const isQuery = queryAnnotation === "query" || queryAnnotation === "composite_query";
254
- methods.push({
255
- name,
256
- type: isQuery ? "query" : "mutation",
257
- hasArgs: args.length > 0 && args !== "",
258
- argsDescription: args || void 0,
259
- returnDescription: returnType || void 0
260
- });
261
- }
262
- return methods;
263
- }
264
- function formatMethodForDisplay(method) {
265
- const typeLabel = method.type === "query" ? "query" : "update";
266
- const argsLabel = method.hasArgs ? "with args" : "no args";
267
- return `${method.name} (${typeLabel}, ${argsLabel})`;
268
- }
269
-
270
- // src/utils/naming.ts
271
- function toPascalCase(str) {
272
- return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
273
- }
274
- function toCamelCase(str) {
275
- const pascal = toPascalCase(str);
276
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
277
- }
278
- function getHookFileName(methodName, hookType) {
279
- const camelMethod = toCamelCase(methodName);
280
- const pascalType = toPascalCase(hookType);
281
- return `${camelMethod}${pascalType}.ts`;
282
- }
283
- function getHookExportName(methodName, hookType) {
284
- const camelMethod = toCamelCase(methodName);
285
- const pascalType = toPascalCase(hookType);
286
- return `${camelMethod}${pascalType}`;
287
- }
288
- function getReactHookName(methodName, hookType) {
289
- const pascalMethod = toPascalCase(methodName);
290
- const pascalType = toPascalCase(hookType);
291
- return `use${pascalMethod}${pascalType}`;
292
- }
293
- function getReactorName(canisterName) {
294
- return `${toCamelCase(canisterName)}Reactor`;
295
- }
296
- function getServiceTypeName(canisterName) {
297
- return `${toPascalCase(canisterName)}Service`;
298
- }
233
+ import { parseDIDFile, formatMethodForDisplay } from "@ic-reactor/codegen";
299
234
 
300
235
  // src/generators/reactor.ts
236
+ import {
237
+ generateReactorFile as generateReactorFileFromCodegen
238
+ } from "@ic-reactor/codegen";
301
239
  function generateReactorFile(options) {
302
- const { canisterName, canisterConfig } = options;
303
- const pascalName = toPascalCase(canisterName);
304
- const reactorName = getReactorName(canisterName);
305
- const serviceName = getServiceTypeName(canisterName);
306
- const reactorType = canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor";
307
- const clientManagerPath = canisterConfig.clientManagerPath ?? "../../lib/client";
308
- const declarationsPath = `./declarations/${canisterName}.did`;
309
- return `/**
310
- * ${pascalName} Reactor
311
- *
312
- * Auto-generated by @ic-reactor/cli
313
- * This file provides the shared reactor instance for the ${canisterName} canister.
314
- *
315
- * You can customize this file to add global configuration.
316
- */
317
-
318
- import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
319
- import { clientManager } from "${clientManagerPath}"
320
- import { idlFactory, type _SERVICE } from "${declarationsPath}"
321
-
322
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
323
- // SERVICE TYPE
324
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
325
-
326
- export type ${serviceName} = _SERVICE
327
-
328
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
329
- // REACTOR INSTANCE
330
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
331
-
332
- /**
333
- * ${pascalName} Reactor with ${canisterConfig.useDisplayReactor !== false ? "Display" : "Candid"} type transformations.
334
- * ${canisterConfig.useDisplayReactor !== false ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
335
- */
336
- export const ${reactorName} = new ${reactorType}<${serviceName}>({
337
- clientManager,
338
- idlFactory,
339
- name: "${canisterName}",
340
- })
341
-
342
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
343
- // BASE HOOKS
344
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
345
-
346
- /**
347
- * Base actor hooks - use these directly or import method-specific hooks.
348
- */
349
- export const {
350
- useActorQuery,
351
- useActorMutation,
352
- useActorSuspenseQuery,
353
- useActorInfiniteQuery,
354
- useActorSuspenseInfiniteQuery,
355
- useActorMethod,
356
- } = createActorHooks(${reactorName})
357
-
358
- /**
359
- * Auth hooks for the client manager.
360
- */
361
- export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
362
-
363
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
364
- // RE-EXPORTS
365
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
366
-
367
- export { idlFactory }
368
- `;
240
+ const {
241
+ canisterName,
242
+ canisterConfig,
243
+ config,
244
+ hasDeclarations = true
245
+ } = options;
246
+ return generateReactorFileFromCodegen({
247
+ canisterName,
248
+ canisterConfig,
249
+ hasDeclarations,
250
+ globalClientManagerPath: config.clientManagerPath,
251
+ // CLI doesn't currently expose advanced mode per-canister, but we default to false (simple mode)
252
+ // If we want to support it, we'd add 'advanced' to CanisterConfig or ReactorGeneratorOptions
253
+ advanced: false
254
+ });
369
255
  }
370
256
 
371
257
  // src/generators/query.ts
258
+ import {
259
+ generateQueryHook as generateQueryHookFromCodegen
260
+ } from "@ic-reactor/codegen";
372
261
  function generateQueryHook(options) {
373
- const { canisterName, method } = options;
374
- const pascalMethod = toPascalCase(method.name);
375
- const reactorName = getReactorName(canisterName);
376
- const serviceName = getServiceTypeName(canisterName);
377
- const hookExportName = getHookExportName(method.name, "query");
378
- const reactHookName = getReactHookName(method.name, "query");
379
- if (method.hasArgs) {
380
- return `/**
381
- * Query Hook: ${method.name}
382
- *
383
- * Auto-generated by @ic-reactor/cli
384
- * This hook wraps the ${method.name} query method.
385
- *
386
- * @example
387
- * // With the factory (for dynamic args)
388
- * const query = ${hookExportName}([arg1, arg2])
389
- * const { data } = query.useQuery()
390
- *
391
- * // Or use the hook directly
392
- * const { data } = ${reactHookName}([arg1, arg2])
393
- */
394
-
395
- import { createQueryFactory } from "@ic-reactor/react"
396
- import { ${reactorName}, type ${serviceName} } from "../reactor"
397
-
398
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
399
- // QUERY FACTORY
400
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
401
-
402
- /**
403
- * Query factory for ${method.name}
404
- *
405
- * Creates a query instance with the provided arguments.
406
- * Each unique set of args gets its own cached query.
407
- */
408
- export const ${hookExportName} = createQueryFactory(${reactorName}, {
409
- functionName: "${method.name}",
410
- // Customize your query options:
411
- // staleTime: 5 * 60 * 1000,
412
- // select: (data) => data,
413
- })
414
-
415
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
416
- // CONVENIENCE HOOK
417
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
418
-
419
- /**
420
- * React hook for ${method.name}
421
- *
422
- * @param args - Arguments to pass to the canister method
423
- * @param options - Additional React Query options
424
- */
425
- export function ${reactHookName}(
426
- args: Parameters<${serviceName}["${method.name}"]>,
427
- options?: Parameters<ReturnType<typeof ${hookExportName}>["useQuery"]>[0]
428
- ) {
429
- return ${hookExportName}(args).useQuery(options)
430
- }
431
-
432
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
433
- // UTILITY FUNCTIONS
434
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
435
-
436
- /**
437
- * Fetch ${method.name} directly (for loaders, server components, etc.)
438
- */
439
- export function fetch${pascalMethod}(args: Parameters<${serviceName}["${method.name}"]>) {
440
- return ${hookExportName}(args).fetch()
441
- }
442
-
443
- /**
444
- * Invalidate ${method.name} query cache
445
- */
446
- export function invalidate${pascalMethod}(args: Parameters<${serviceName}["${method.name}"]>) {
447
- return ${hookExportName}(args).invalidate()
448
- }
449
-
450
- /**
451
- * Get ${method.name} query key (for cache manipulation)
452
- */
453
- export function get${pascalMethod}QueryKey(args: Parameters<${serviceName}["${method.name}"]>) {
454
- return ${hookExportName}(args).getQueryKey()
455
- }
456
- `;
457
- } else {
458
- return `/**
459
- * Query Hook: ${method.name}
460
- *
461
- * Auto-generated by @ic-reactor/cli
462
- * This hook wraps the ${method.name} query method.
463
- *
464
- * @example
465
- * // Use the hook
466
- * const { data, isLoading } = ${reactHookName}()
467
- *
468
- * // Or access the query object directly
469
- * const data = await ${hookExportName}.fetch()
470
- * ${hookExportName}.invalidate()
471
- */
472
-
473
- import { createQuery } from "@ic-reactor/react"
474
- import { ${reactorName} } from "../reactor"
475
-
476
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
477
- // QUERY INSTANCE
478
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
479
-
480
- /**
481
- * Query for ${method.name}
482
- *
483
- * Provides:
484
- * - .useQuery() - React hook
485
- * - .fetch() - Direct fetch (for loaders)
486
- * - .invalidate() - Invalidate cache
487
- * - .getQueryKey() - Get query key
488
- * - .getCacheData() - Read from cache
489
- */
490
- export const ${hookExportName} = createQuery(${reactorName}, {
491
- functionName: "${method.name}",
492
- // Customize your query options:
493
- // staleTime: 5 * 60 * 1000,
494
- // select: (data) => data,
495
- })
496
-
497
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
498
- // CONVENIENCE EXPORTS
499
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
500
-
501
- /**
502
- * React hook for ${method.name}
503
- */
504
- export const ${reactHookName} = ${hookExportName}.useQuery
505
-
506
- /**
507
- * Fetch ${method.name} directly (for loaders, server components, etc.)
508
- */
509
- export const fetch${pascalMethod} = ${hookExportName}.fetch
510
-
511
- /**
512
- * Invalidate ${method.name} query cache
513
- */
514
- export const invalidate${pascalMethod} = ${hookExportName}.invalidate
515
-
516
- /**
517
- * Get ${method.name} query key (for cache manipulation)
518
- */
519
- export const get${pascalMethod}QueryKey = ${hookExportName}.getQueryKey
520
-
521
- /**
522
- * Get cached data for ${method.name}
523
- */
524
- export const get${pascalMethod}CacheData = ${hookExportName}.getCacheData
525
- `;
526
- }
262
+ const { canisterName, method, type } = options;
263
+ return generateQueryHookFromCodegen({
264
+ canisterName,
265
+ method,
266
+ type
267
+ });
527
268
  }
528
269
 
529
270
  // src/generators/mutation.ts
271
+ import {
272
+ generateMutationHook as generateMutationHookFromCodegen
273
+ } from "@ic-reactor/codegen";
530
274
  function generateMutationHook(options) {
531
275
  const { canisterName, method } = options;
532
- const pascalMethod = toPascalCase(method.name);
533
- const reactorName = getReactorName(canisterName);
534
- const serviceName = getServiceTypeName(canisterName);
535
- const hookExportName = getHookExportName(method.name, "mutation");
536
- const reactHookName = getReactHookName(method.name, "mutation");
537
- return `/**
538
- * Mutation Hook: ${method.name}
539
- *
540
- * Auto-generated by @ic-reactor/cli
541
- * This hook wraps the ${method.name} update method.
542
- *
543
- * @example
544
- * // Use the hook in a component
545
- * const { mutate, isPending } = ${reactHookName}()
546
- *
547
- * // Call the mutation
548
- * mutate(${method.hasArgs ? "[arg1, arg2]" : "[]"})
549
- *
550
- * // Or execute directly
551
- * const result = await execute${pascalMethod}(${method.hasArgs ? "[arg1, arg2]" : "[]"})
552
- */
553
-
554
- import { createMutation } from "@ic-reactor/react"
555
- import { ${reactorName}, type ${serviceName} } from "../reactor"
556
-
557
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
558
- // MUTATION INSTANCE
559
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
560
-
561
- /**
562
- * Mutation for ${method.name}
563
- *
564
- * Provides:
565
- * - .useMutation() - React hook
566
- * - .execute() - Direct execution
567
- */
568
- export const ${hookExportName} = createMutation(${reactorName}, {
569
- functionName: "${method.name}",
570
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
571
- // INVALIDATION
572
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
573
- // Uncomment and import query keys to auto-invalidate on success:
574
- // invalidateQueries: [
575
- // someQuery.getQueryKey(),
576
- // ],
577
-
578
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
579
- // SUCCESS HANDLER
580
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
581
- // onSuccess: (data, variables, context) => {
582
- // console.log("${method.name} succeeded:", data)
583
- // },
584
-
585
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
586
- // ERROR HANDLERS
587
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
588
- // Handle canister-level errors (from Result { Err })
589
- // onCanisterError: (error, variables) => {
590
- // console.error("Canister error:", error)
591
- // },
592
-
593
- // Handle all errors (network, canister, etc.)
594
- // onError: (error, variables, context) => {
595
- // console.error("Error:", error)
596
- // },
597
- })
598
-
599
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
600
- // CONVENIENCE EXPORTS
601
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
602
-
603
- /**
604
- * React hook for ${method.name}
605
- *
606
- * @param options - Mutation options (onSuccess, onError, etc.)
607
- */
608
- export const ${reactHookName} = ${hookExportName}.useMutation
609
-
610
- /**
611
- * Execute ${method.name} directly (outside of React)
612
- *
613
- * @param args - Arguments to pass to the canister method
614
- */
615
- export const execute${pascalMethod} = ${hookExportName}.execute
616
- `;
276
+ return generateMutationHookFromCodegen({
277
+ canisterName,
278
+ method
279
+ });
617
280
  }
618
281
 
619
282
  // src/generators/infiniteQuery.ts
283
+ import {
284
+ generateInfiniteQueryHook as generateInfiniteQueryHookFromCodegen
285
+ } from "@ic-reactor/codegen";
620
286
  function generateInfiniteQueryHook(options) {
621
- const { canisterName, method } = options;
622
- const pascalMethod = toPascalCase(method.name);
623
- const camelMethod = toCamelCase(method.name);
624
- const reactorName = getReactorName(canisterName);
625
- const serviceName = getServiceTypeName(canisterName);
626
- return `/**
627
- * Infinite Query Hook: ${method.name}
628
- *
629
- * Auto-generated by @ic-reactor/cli
630
- * This hook wraps the ${method.name} method for infinite/paginated queries.
631
- *
632
- * \u26A0\uFE0F CUSTOMIZATION REQUIRED:
633
- * You need to configure getArgs and getNextPageParam based on your API.
634
- *
635
- * @example
636
- * const {
637
- * data,
638
- * fetchNextPage,
639
- * hasNextPage,
640
- * isFetching,
641
- * } = ${camelMethod}InfiniteQuery.useInfiniteQuery()
642
- *
643
- * // Flatten all pages
644
- * const allItems = data?.pages.flatMap(page => page.items) ?? []
645
- */
646
-
647
- import { createInfiniteQuery } from "@ic-reactor/react"
648
- import { ${reactorName}, type ${serviceName} } from "../reactor"
649
-
650
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
651
- // CURSOR TYPE
652
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
653
-
654
- /**
655
- * Define your pagination cursor type here.
656
- * Common patterns:
657
- * - number (offset-based)
658
- * - string (cursor-based)
659
- * - { offset: number; limit: number }
660
- */
661
- type PageCursor = number
662
-
663
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
664
- // INFINITE QUERY INSTANCE
665
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
666
-
667
- /**
668
- * Infinite query for ${method.name}
669
- *
670
- * Provides:
671
- * - .useInfiniteQuery() - React hook with pagination
672
- * - .fetch() - Fetch first page
673
- * - .invalidate() - Invalidate all pages
674
- * - .getQueryKey() - Get query key
675
- * - .getCacheData() - Read from cache
676
- */
677
- export const ${camelMethod}InfiniteQuery = createInfiniteQuery(${reactorName}, {
678
- functionName: "${method.name}",
679
-
680
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
681
- // PAGINATION CONFIG (CUSTOMIZE THESE)
682
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
683
-
684
- /**
685
- * Initial page parameter (e.g., 0 for offset, null for cursor)
686
- */
687
- initialPageParam: 0 as PageCursor,
688
-
689
- /**
690
- * Convert page param to method arguments.
691
- * Customize this based on your canister API.
692
- */
693
- getArgs: (pageParam: PageCursor) => {
694
- // Example: offset-based pagination
695
- return [{ offset: pageParam, limit: 10 }] as Parameters<${serviceName}["${method.name}"]>
696
-
697
- // Example: cursor-based pagination
698
- // return [{ cursor: pageParam, limit: 10 }] as Parameters<${serviceName}["${method.name}"]>
699
- },
700
-
701
- /**
702
- * Extract next page param from the last page.
703
- * Return undefined/null when there are no more pages.
704
- */
705
- getNextPageParam: (lastPage, allPages, lastPageParam) => {
706
- // Example: offset-based - return next offset or undefined if no more
707
- // const items = lastPage.items ?? []
708
- // if (items.length < 10) return undefined
709
- // return lastPageParam + 10
710
-
711
- // Example: cursor-based
712
- // return lastPage.nextCursor ?? undefined
713
-
714
- // Placeholder - customize for your API
715
- return undefined
716
- },
717
-
718
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
719
- // OPTIONAL CONFIG
720
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
721
-
722
- // Bi-directional scrolling (optional)
723
- // getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
724
- // return firstPageParam > 0 ? firstPageParam - 10 : undefined
725
- // },
726
-
727
- // Max pages to keep in cache (for memory management)
728
- // maxPages: 10,
729
-
730
- // How long data stays fresh
731
- // staleTime: 5 * 60 * 1000,
732
-
733
- // Transform the data
734
- // select: (data) => ({
735
- // pages: data.pages,
736
- // pageParams: data.pageParams,
737
- // items: data.pages.flatMap(page => page.items),
738
- // }),
739
- })
740
-
741
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
742
- // CONVENIENCE EXPORTS
743
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
744
-
745
- /**
746
- * React hook for paginated ${method.name}
747
- */
748
- export const use${pascalMethod}InfiniteQuery = ${camelMethod}InfiniteQuery.useInfiniteQuery
749
-
750
- /**
751
- * Fetch first page of ${method.name}
752
- */
753
- export const fetch${pascalMethod}FirstPage = ${camelMethod}InfiniteQuery.fetch
754
-
755
- /**
756
- * Invalidate all cached pages
757
- */
758
- export const invalidate${pascalMethod}Pages = ${camelMethod}InfiniteQuery.invalidate
759
- `;
287
+ const { canisterName, method, type } = options;
288
+ return generateInfiniteQueryHookFromCodegen({
289
+ canisterName,
290
+ method,
291
+ type
292
+ });
760
293
  }
761
294
 
762
295
  // src/commands/add.ts
296
+ import { getHookFileName } from "@ic-reactor/codegen";
297
+ import { generateDeclarations } from "@ic-reactor/codegen";
763
298
  async function addCommand(options) {
764
299
  console.log();
765
300
  p2.intro(pc2.cyan("\u{1F527} Add Canister Hooks"));
@@ -954,13 +489,29 @@ ${error.message}`
954
489
  const generatedFiles = [];
955
490
  const reactorPath = path3.join(canisterOutDir, "reactor.ts");
956
491
  if (!fileExists(reactorPath)) {
492
+ spinner4.message("Generating TypeScript declarations...");
493
+ const bindgenResult = await generateDeclarations({
494
+ didFile: didFilePath,
495
+ outDir: canisterOutDir,
496
+ canisterName: selectedCanister
497
+ });
498
+ if (bindgenResult.success) {
499
+ generatedFiles.push("declarations/");
500
+ } else {
501
+ p2.log.warn(`Could not generate declarations: ${bindgenResult.error}`);
502
+ p2.log.info(
503
+ `You can manually run: npx @icp-sdk/bindgen --input ${didFilePath} --output ${canisterOutDir}/declarations`
504
+ );
505
+ }
506
+ spinner4.message("Generating reactor...");
957
507
  const reactorContent = generateReactorFile({
958
508
  canisterName: selectedCanister,
959
509
  canisterConfig,
960
510
  config,
961
- outDir: canisterOutDir
511
+ outDir: canisterOutDir,
512
+ hasDeclarations: bindgenResult.success
962
513
  });
963
- fs4.writeFileSync(reactorPath, reactorContent);
514
+ fs3.writeFileSync(reactorPath, reactorContent);
964
515
  generatedFiles.push("reactor.ts");
965
516
  }
966
517
  for (const { method, hookType } of methodsWithHookTypes) {
@@ -992,18 +543,42 @@ ${error.message}`
992
543
  });
993
544
  break;
994
545
  }
995
- fs4.writeFileSync(filePath, content);
546
+ fs3.writeFileSync(filePath, content);
996
547
  generatedFiles.push(path3.join("hooks", fileName));
997
548
  }
998
549
  const indexPath = path3.join(hooksOutDir, "index.ts");
999
- const indexContent = generateIndexFile(methodsWithHookTypes);
1000
- fs4.writeFileSync(indexPath, indexContent);
550
+ let existingExports = [];
551
+ if (fs3.existsSync(indexPath)) {
552
+ const content = fs3.readFileSync(indexPath, "utf-8");
553
+ existingExports = content.split("\n").filter((line) => line.trim().startsWith("export * from")).map((line) => line.trim());
554
+ }
555
+ const newExports = methodsWithHookTypes.map(({ method, hookType }) => {
556
+ const fileName = getHookFileName(method.name, hookType).replace(".ts", "");
557
+ return `export * from "./${fileName}"`;
558
+ });
559
+ const allExports = [.../* @__PURE__ */ new Set([...existingExports, ...newExports])];
560
+ const indexContent = `/**
561
+ * Hook barrel exports
562
+ *
563
+ * Auto-generated by @ic-reactor/cli
564
+ */
565
+
566
+ ${allExports.join("\n")}
567
+ `;
568
+ fs3.writeFileSync(indexPath, indexContent);
1001
569
  generatedFiles.push("hooks/index.ts");
570
+ const existingHooks = config.generatedHooks[selectedCanister] ?? [];
571
+ const newHookConfigs = methodsWithHookTypes.map(({ method, hookType }) => ({
572
+ name: method.name,
573
+ type: hookType
574
+ }));
575
+ const filteredExisting = existingHooks.filter((h) => {
576
+ const name = typeof h === "string" ? h : h.name;
577
+ return !newHookConfigs.some((n) => n.name === name);
578
+ });
1002
579
  config.generatedHooks[selectedCanister] = [
1003
- .../* @__PURE__ */ new Set([
1004
- ...config.generatedHooks[selectedCanister] ?? [],
1005
- ...selectedMethods.map((m) => m.name)
1006
- ])
580
+ ...filteredExisting,
581
+ ...newHookConfigs
1007
582
  ];
1008
583
  saveConfig(config, configPath);
1009
584
  spinner4.stop("Hooks generated!");
@@ -1030,7 +605,7 @@ async function promptForNewCanister(projectRoot) {
1030
605
  validate: (value) => {
1031
606
  if (!value) return "DID file path is required";
1032
607
  const fullPath = path3.resolve(projectRoot, value);
1033
- if (!fs4.existsSync(fullPath)) {
608
+ if (!fs3.existsSync(fullPath)) {
1034
609
  return `File not found: ${value}`;
1035
610
  }
1036
611
  return void 0;
@@ -1041,31 +616,19 @@ async function promptForNewCanister(projectRoot) {
1041
616
  name,
1042
617
  config: {
1043
618
  didFile,
1044
- clientManagerPath: "../../lib/client",
1045
619
  useDisplayReactor: true
1046
620
  }
1047
621
  };
1048
622
  }
1049
- function generateIndexFile(methods) {
1050
- const exports = methods.map(({ method, hookType }) => {
1051
- const fileName = getHookFileName(method.name, hookType).replace(".ts", "");
1052
- return `export * from "./${fileName}"`;
1053
- });
1054
- return `/**
1055
- * Hook barrel exports
1056
- *
1057
- * Auto-generated by @ic-reactor/cli
1058
- */
1059
-
1060
- ${exports.join("\n")}
1061
- `;
1062
- }
1063
623
 
1064
624
  // src/commands/sync.ts
1065
625
  import * as p3 from "@clack/prompts";
1066
- import fs5 from "fs";
626
+ import fs4 from "fs";
1067
627
  import path4 from "path";
1068
628
  import pc3 from "picocolors";
629
+ import { parseDIDFile as parseDIDFile2 } from "@ic-reactor/codegen";
630
+ import { getHookFileName as getHookFileName2 } from "@ic-reactor/codegen";
631
+ import { generateDeclarations as generateDeclarations2 } from "@ic-reactor/codegen";
1069
632
  async function syncCommand(options) {
1070
633
  console.log();
1071
634
  p3.intro(pc3.cyan("\u{1F504} Sync Canister Hooks"));
@@ -1119,29 +682,48 @@ async function syncCommand(options) {
1119
682
  const didFilePath = path4.resolve(projectRoot, canisterConfig.didFile);
1120
683
  let methods;
1121
684
  try {
1122
- methods = parseDIDFile(didFilePath);
685
+ methods = parseDIDFile2(didFilePath);
1123
686
  } catch (error) {
1124
687
  errors.push(
1125
688
  `${canisterName}: Failed to parse DID file - ${error.message}`
1126
689
  );
1127
690
  continue;
1128
691
  }
1129
- const currentMethodNames = methods.map((m) => m.name);
1130
- const removedMethods = generatedMethods.filter(
1131
- (name) => !currentMethodNames.includes(name)
692
+ const hooks = generatedMethods.map(
693
+ (h) => typeof h === "string" ? { name: h } : h
1132
694
  );
695
+ const currentMethodNames = methods.map((m) => m.name);
696
+ const removedMethods = hooks.filter((h) => !currentMethodNames.includes(h.name)).map((h) => h.name);
1133
697
  if (removedMethods.length > 0) {
1134
698
  p3.log.warn(
1135
699
  `${canisterName}: Methods removed from DID: ${pc3.yellow(removedMethods.join(", "))}`
1136
700
  );
1137
701
  }
1138
- const newMethods = methods.filter((m) => !generatedMethods.includes(m.name));
702
+ const generatedNames = hooks.map((h) => h.name);
703
+ const newMethods = methods.filter((m) => !generatedNames.includes(m.name));
1139
704
  if (newMethods.length > 0) {
1140
705
  p3.log.info(
1141
706
  `${canisterName}: New methods available: ${pc3.cyan(newMethods.map((m) => m.name).join(", "))}`
1142
707
  );
1143
708
  }
1144
709
  const canisterOutDir = path4.join(projectRoot, config.outDir, canisterName);
710
+ const declarationsDir = path4.join(canisterOutDir, "declarations");
711
+ const declarationsExist = fs4.existsSync(declarationsDir) && fs4.readdirSync(declarationsDir).some((f) => f.endsWith(".ts") || f.endsWith(".js"));
712
+ if (!declarationsExist) {
713
+ spinner4.message(`Regenerating declarations for ${canisterName}...`);
714
+ const bindgenResult = await generateDeclarations2({
715
+ didFile: didFilePath,
716
+ outDir: canisterOutDir,
717
+ canisterName
718
+ });
719
+ if (bindgenResult.success) {
720
+ totalUpdated++;
721
+ } else {
722
+ p3.log.warn(
723
+ `Could not regenerate declarations for ${canisterName}: ${bindgenResult.error}`
724
+ );
725
+ }
726
+ }
1145
727
  const reactorPath = path4.join(canisterOutDir, "reactor.ts");
1146
728
  const reactorContent = generateReactorFile({
1147
729
  canisterName,
@@ -1149,41 +731,52 @@ async function syncCommand(options) {
1149
731
  config,
1150
732
  outDir: canisterOutDir
1151
733
  });
1152
- fs5.writeFileSync(reactorPath, reactorContent);
734
+ fs4.writeFileSync(reactorPath, reactorContent);
1153
735
  totalUpdated++;
1154
736
  const hooksOutDir = path4.join(canisterOutDir, "hooks");
1155
737
  ensureDir(hooksOutDir);
1156
- for (const methodName of generatedMethods) {
738
+ for (const hookConfig of hooks) {
739
+ const methodName = hookConfig.name;
1157
740
  const method = methods.find((m) => m.name === methodName);
1158
741
  if (!method) {
1159
742
  totalSkipped++;
1160
743
  continue;
1161
744
  }
1162
- const queryFileName = getHookFileName(methodName, "query");
1163
- const mutationFileName = getHookFileName(methodName, "mutation");
1164
- const infiniteQueryFileName = getHookFileName(methodName, "infiniteQuery");
745
+ let hookType = hookConfig.type || method.type;
746
+ if (!hookConfig.type) {
747
+ const infiniteQueryFileName = getHookFileName2(
748
+ methodName,
749
+ "infiniteQuery"
750
+ );
751
+ if (fs4.existsSync(path4.join(hooksOutDir, infiniteQueryFileName))) {
752
+ hookType = "infiniteQuery";
753
+ }
754
+ }
755
+ const fileName = getHookFileName2(methodName, hookType);
1165
756
  let content;
1166
- let fileName;
1167
- if (fs5.existsSync(path4.join(hooksOutDir, infiniteQueryFileName))) {
1168
- fileName = infiniteQueryFileName;
1169
- totalSkipped++;
1170
- continue;
1171
- } else if (method.type === "query") {
1172
- fileName = queryFileName;
757
+ if (hookType.includes("Query")) {
1173
758
  content = generateQueryHook({
1174
759
  canisterName,
1175
760
  method,
1176
- config
761
+ config,
762
+ type: hookType
1177
763
  });
1178
764
  } else {
1179
- fileName = mutationFileName;
1180
765
  content = generateMutationHook({
1181
766
  canisterName,
1182
767
  method,
1183
768
  config
1184
769
  });
1185
770
  }
1186
- fs5.writeFileSync(path4.join(hooksOutDir, fileName), content);
771
+ const filePath = path4.join(hooksOutDir, fileName);
772
+ if (fs4.existsSync(filePath)) {
773
+ const existingContent = fs4.readFileSync(filePath, "utf-8");
774
+ if (existingContent !== content) {
775
+ totalSkipped++;
776
+ continue;
777
+ }
778
+ }
779
+ fs4.writeFileSync(filePath, content);
1187
780
  totalUpdated++;
1188
781
  }
1189
782
  }
@@ -1208,6 +801,7 @@ Skipped: ${pc3.dim(totalSkipped.toString())} files (preserved customizations)`,
1208
801
  import * as p4 from "@clack/prompts";
1209
802
  import path5 from "path";
1210
803
  import pc4 from "picocolors";
804
+ import { parseDIDFile as parseDIDFile3 } from "@ic-reactor/codegen";
1211
805
  async function listCommand(options) {
1212
806
  console.log();
1213
807
  p4.intro(pc4.cyan("\u{1F4CB} List Canister Methods"));
@@ -1257,7 +851,7 @@ async function listCommand(options) {
1257
851
  }
1258
852
  const didFilePath = path5.resolve(projectRoot, canisterConfig.didFile);
1259
853
  try {
1260
- const methods = parseDIDFile(didFilePath);
854
+ const methods = parseDIDFile3(didFilePath);
1261
855
  if (methods.length === 0) {
1262
856
  p4.log.warn(`No methods found in ${pc4.yellow(didFilePath)}`);
1263
857
  process.exit(0);
@@ -1316,14 +910,17 @@ ${error.message}`
1316
910
 
1317
911
  // src/commands/fetch.ts
1318
912
  import * as p5 from "@clack/prompts";
1319
- import fs6 from "fs";
913
+ import fs5 from "fs";
1320
914
  import path6 from "path";
1321
915
  import pc5 from "picocolors";
916
+ import { formatMethodForDisplay as formatMethodForDisplay2 } from "@ic-reactor/codegen";
917
+ import { getHookFileName as getHookFileName3, toCamelCase } from "@ic-reactor/codegen";
1322
918
 
1323
919
  // src/utils/network.ts
1324
920
  import { HttpAgent, Actor } from "@icp-sdk/core/agent";
1325
921
  import { Principal } from "@icp-sdk/core/principal";
1326
922
  import { IDL } from "@icp-sdk/core/candid";
923
+ import { extractMethods } from "@ic-reactor/codegen";
1327
924
  var IC_HOST = "https://icp-api.io";
1328
925
  var LOCAL_HOST = "http://127.0.0.1:4943";
1329
926
  async function fetchCandidFromCanister(options) {
@@ -1395,6 +992,7 @@ function shortenCanisterId(canisterId) {
1395
992
  }
1396
993
 
1397
994
  // src/commands/fetch.ts
995
+ import { generateDeclarations as generateDeclarations3 } from "@ic-reactor/codegen";
1398
996
  async function fetchCommand(options) {
1399
997
  console.log();
1400
998
  p5.intro(pc5.cyan("\u{1F310} Fetch from Live Canister"));
@@ -1495,7 +1093,7 @@ async function fetchCommand(options) {
1495
1093
  message: "Select methods to generate hooks for",
1496
1094
  options: methods.map((method) => ({
1497
1095
  value: method.name,
1498
- label: formatMethodForDisplay(method)
1096
+ label: formatMethodForDisplay2(method)
1499
1097
  })),
1500
1098
  required: true
1501
1099
  });
@@ -1591,7 +1189,6 @@ async function fetchCommand(options) {
1591
1189
  }
1592
1190
  const canisterConfig = {
1593
1191
  didFile: `./candid/${canisterName}.did`,
1594
- clientManagerPath: "../../lib/client",
1595
1192
  useDisplayReactor: true,
1596
1193
  canisterId
1597
1194
  };
@@ -1605,18 +1202,35 @@ async function fetchCommand(options) {
1605
1202
  genSpinner.start("Generating hooks...");
1606
1203
  const generatedFiles = [];
1607
1204
  const candidPath = path6.join(candidDir, `${canisterName}.did`);
1608
- fs6.writeFileSync(candidPath, candidSource);
1205
+ fs5.writeFileSync(candidPath, candidSource);
1609
1206
  generatedFiles.push(`candid/${canisterName}.did`);
1207
+ genSpinner.message("Generating TypeScript declarations...");
1208
+ const bindgenResult = await generateDeclarations3({
1209
+ didFile: candidPath,
1210
+ outDir: canisterOutDir,
1211
+ canisterName
1212
+ });
1213
+ if (bindgenResult.success) {
1214
+ generatedFiles.push("declarations/");
1215
+ } else {
1216
+ p5.log.warn(`Could not generate declarations: ${bindgenResult.error}`);
1217
+ p5.log.info(
1218
+ `You can manually run: npx @icp-sdk/bindgen --input ${candidPath} --output ${canisterOutDir}/declarations`
1219
+ );
1220
+ }
1221
+ genSpinner.message("Generating reactor...");
1610
1222
  const reactorPath = path6.join(canisterOutDir, "reactor.ts");
1611
1223
  const reactorContent = generateReactorFileForFetch({
1612
1224
  canisterName,
1613
1225
  canisterConfig,
1614
- canisterId
1226
+ config,
1227
+ canisterId,
1228
+ hasDeclarations: bindgenResult.success
1615
1229
  });
1616
- fs6.writeFileSync(reactorPath, reactorContent);
1230
+ fs5.writeFileSync(reactorPath, reactorContent);
1617
1231
  generatedFiles.push("reactor.ts");
1618
1232
  for (const { method, hookType } of methodsWithHookTypes) {
1619
- const fileName = getHookFileName(method.name, hookType);
1233
+ const fileName = getHookFileName3(method.name, hookType);
1620
1234
  const filePath = path6.join(hooksOutDir, fileName);
1621
1235
  let content;
1622
1236
  switch (hookType) {
@@ -1644,12 +1258,12 @@ async function fetchCommand(options) {
1644
1258
  });
1645
1259
  break;
1646
1260
  }
1647
- fs6.writeFileSync(filePath, content);
1261
+ fs5.writeFileSync(filePath, content);
1648
1262
  generatedFiles.push(path6.join("hooks", fileName));
1649
1263
  }
1650
1264
  const indexPath = path6.join(hooksOutDir, "index.ts");
1651
- const indexContent = generateIndexFile2(methodsWithHookTypes);
1652
- fs6.writeFileSync(indexPath, indexContent);
1265
+ const indexContent = generateIndexFile(methodsWithHookTypes);
1266
+ fs5.writeFileSync(indexPath, indexContent);
1653
1267
  generatedFiles.push("hooks/index.ts");
1654
1268
  config.generatedHooks[canisterName] = [
1655
1269
  .../* @__PURE__ */ new Set([
@@ -1675,13 +1289,15 @@ Methods: ${pc5.dim(selectedMethods.map((m) => m.name).join(", "))}`,
1675
1289
  p5.outro(pc5.green("\u2713 Done!"));
1676
1290
  }
1677
1291
  function generateReactorFileForFetch(options) {
1678
- const { canisterName, canisterConfig, canisterId } = options;
1292
+ const { canisterName, canisterConfig, config, canisterId, hasDeclarations } = options;
1679
1293
  const pascalName = canisterName.charAt(0).toUpperCase() + canisterName.slice(1);
1680
1294
  const reactorName = `${canisterName}Reactor`;
1681
1295
  const serviceName = `${pascalName}Service`;
1682
1296
  const reactorType = canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor";
1683
- const clientManagerPath = canisterConfig.clientManagerPath ?? "../../lib/client";
1684
- return `/**
1297
+ const clientManagerPath = canisterConfig.clientManagerPath ?? config.clientManagerPath ?? "../../lib/client";
1298
+ const didFileName = path6.basename(canisterConfig.didFile);
1299
+ if (hasDeclarations) {
1300
+ return `/**
1685
1301
  * ${pascalName} Reactor
1686
1302
  *
1687
1303
  * Auto-generated by @ic-reactor/cli
@@ -1690,22 +1306,81 @@ function generateReactorFileForFetch(options) {
1690
1306
  * You can customize this file to add global configuration.
1691
1307
  */
1692
1308
 
1693
- import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
1309
+ import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
1694
1310
  import { clientManager } from "${clientManagerPath}"
1695
1311
 
1696
1312
  // Import generated declarations
1697
- // Note: You may need to run 'npx @icp-sdk/bindgen' to generate TypeScript types
1698
- // For now, we use a generic service type
1699
- import { idlFactory } from "./declarations/${canisterName}.did"
1313
+ import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${didFileName}"
1314
+
1315
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1316
+ // REACTOR INSTANCE
1317
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1318
+
1319
+ /**
1320
+ * ${pascalName} Reactor
1321
+ *
1322
+ * Canister ID: ${canisterId}
1323
+ */
1324
+ export const ${reactorName} = new ${reactorType}<${serviceName}>({
1325
+ clientManager,
1326
+ idlFactory,
1327
+ canisterId: "${canisterId}",
1328
+ name: "${canisterName}",
1329
+ })
1330
+
1331
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1332
+ // ACTOR HOOKS
1333
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1334
+
1335
+ /**
1336
+ * Actor hooks for ${canisterName} - use these directly or import method-specific hooks.
1337
+ */
1338
+ export const {
1339
+ useActorQuery,
1340
+ useActorMutation,
1341
+ useActorSuspenseQuery,
1342
+ useActorInfiniteQuery,
1343
+ useActorSuspenseInfiniteQuery,
1344
+ useActorMethod,
1345
+ } = createActorHooks(${reactorName})
1346
+
1347
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1348
+ // RE-EXPORTS
1349
+ // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1350
+
1351
+ export { idlFactory }
1352
+ export type { ${serviceName} }
1353
+ `;
1354
+ }
1355
+ return `/**
1356
+ * ${pascalName} Reactor
1357
+ *
1358
+ * Auto-generated by @ic-reactor/cli
1359
+ * Fetched from canister: ${canisterId}
1360
+ *
1361
+ * You can customize this file to add global configuration.
1362
+ */
1363
+
1364
+ import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
1365
+ import { clientManager } from "${clientManagerPath}"
1700
1366
 
1701
1367
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1702
- // SERVICE TYPE
1368
+ // DECLARATIONS
1703
1369
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1704
1370
 
1705
- // TODO: Generate proper types using @icp-sdk/bindgen
1706
- // npx @icp-sdk/bindgen --input ./candid/${canisterName}.did --output ./src/canisters/${canisterName}/declarations
1371
+ // TODO: Generate proper types by running:
1372
+ // npx @icp-sdk/bindgen --input ./candid/${canisterName}.did --output ./${canisterName}/declarations
1373
+
1374
+ // For now, import just the IDL factory (you may need to create this manually)
1375
+ // import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${didFileName}"
1376
+
1377
+ // Fallback generic type - replace with generated types
1707
1378
  type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
1708
1379
 
1380
+ // You'll need to define idlFactory here or import from declarations
1381
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
1382
+ const idlFactory = ({ IDL }: { IDL: any }) => IDL.Service({})
1383
+
1709
1384
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1710
1385
  // REACTOR INSTANCE
1711
1386
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
@@ -1723,11 +1398,11 @@ export const ${reactorName} = new ${reactorType}<${serviceName}>({
1723
1398
  })
1724
1399
 
1725
1400
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1726
- // BASE HOOKS
1401
+ // ACTOR HOOKS
1727
1402
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1728
1403
 
1729
1404
  /**
1730
- * Base actor hooks - use these directly or import method-specific hooks.
1405
+ * Actor hooks for ${canisterName} - use these directly or import method-specific hooks.
1731
1406
  */
1732
1407
  export const {
1733
1408
  useActorQuery,
@@ -1738,11 +1413,6 @@ export const {
1738
1413
  useActorMethod,
1739
1414
  } = createActorHooks(${reactorName})
1740
1415
 
1741
- /**
1742
- * Auth hooks for the client manager.
1743
- */
1744
- export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
1745
-
1746
1416
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
1747
1417
  // RE-EXPORTS
1748
1418
  // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
@@ -1751,9 +1421,9 @@ export { idlFactory }
1751
1421
  export type { ${serviceName} }
1752
1422
  `;
1753
1423
  }
1754
- function generateIndexFile2(methods) {
1424
+ function generateIndexFile(methods) {
1755
1425
  const exports = methods.map(({ method, hookType }) => {
1756
- const fileName = getHookFileName(method.name, hookType).replace(".ts", "");
1426
+ const fileName = getHookFileName3(method.name, hookType).replace(".ts", "");
1757
1427
  return `export * from "./${fileName}"`;
1758
1428
  });
1759
1429
  return `/**