@ic-reactor/cli 0.0.0-dev3 → 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,652 +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 fs5 from "fs";
231
- import path4 from "path";
232
- import pc2 from "picocolors";
233
-
234
- // src/parsers/did.ts
235
230
  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
- }
231
+ import path3 from "path";
232
+ import pc2 from "picocolors";
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, hasDeclarations = true } = 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
- if (hasDeclarations) {
310
- return `/**
311
- * ${pascalName} Reactor
312
- *
313
- * Auto-generated by @ic-reactor/cli
314
- * This file provides the shared reactor instance for the ${canisterName} canister.
315
- *
316
- * You can customize this file to add global configuration.
317
- */
318
-
319
- import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
320
- import { clientManager } from "${clientManagerPath}"
321
- import { idlFactory, type _SERVICE } from "${declarationsPath}"
322
-
323
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
324
- // SERVICE TYPE
325
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
326
-
327
- export type ${serviceName} = _SERVICE
328
-
329
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
330
- // REACTOR INSTANCE
331
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
332
-
333
- /**
334
- * ${pascalName} Reactor with ${canisterConfig.useDisplayReactor !== false ? "Display" : "Candid"} type transformations.
335
- * ${canisterConfig.useDisplayReactor !== false ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
336
- */
337
- export const ${reactorName} = new ${reactorType}<${serviceName}>({
338
- clientManager,
339
- idlFactory,
340
- name: "${canisterName}",
341
- })
342
-
343
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
344
- // BASE HOOKS
345
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
346
-
347
- /**
348
- * Base actor hooks - use these directly or import method-specific hooks.
349
- */
350
- export const {
351
- useActorQuery,
352
- useActorMutation,
353
- useActorSuspenseQuery,
354
- useActorInfiniteQuery,
355
- useActorSuspenseInfiniteQuery,
356
- useActorMethod,
357
- } = createActorHooks(${reactorName})
358
-
359
- /**
360
- * Auth hooks for the client manager.
361
- */
362
- export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
363
-
364
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
365
- // RE-EXPORTS
366
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
367
-
368
- export { idlFactory }
369
- `;
370
- }
371
- return `/**
372
- * ${pascalName} Reactor
373
- *
374
- * Auto-generated by @ic-reactor/cli
375
- * This file provides the shared reactor instance for the ${canisterName} canister.
376
- *
377
- * You can customize this file to add global configuration.
378
- */
379
-
380
- import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
381
- import { clientManager } from "${clientManagerPath}"
382
-
383
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
384
- // DECLARATIONS
385
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
386
-
387
- // TODO: Generate proper types by running:
388
- // npx @icp-sdk/bindgen --input <path-to-did> --output ./${canisterName}/declarations
389
-
390
- // Then uncomment:
391
- // import { idlFactory, type _SERVICE as ${serviceName} } from "${declarationsPath}"
392
-
393
- // Fallback generic type - replace with generated types
394
- type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
395
-
396
- // You'll need to define idlFactory here or import from declarations
397
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
398
- const idlFactory = ({ IDL }: { IDL: any }) => IDL.Service({})
399
-
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
- // REACTOR INSTANCE
402
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
403
-
404
- /**
405
- * ${pascalName} Reactor with ${canisterConfig.useDisplayReactor !== false ? "Display" : "Candid"} type transformations.
406
- * ${canisterConfig.useDisplayReactor !== false ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
407
- */
408
- export const ${reactorName} = new ${reactorType}<${serviceName}>({
409
- clientManager,
410
- idlFactory,
411
- name: "${canisterName}",
412
- })
413
-
414
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
415
- // BASE HOOKS
416
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
417
-
418
- /**
419
- * Base actor hooks - use these directly or import method-specific hooks.
420
- */
421
- export const {
422
- useActorQuery,
423
- useActorMutation,
424
- useActorSuspenseQuery,
425
- useActorInfiniteQuery,
426
- useActorSuspenseInfiniteQuery,
427
- useActorMethod,
428
- } = createActorHooks(${reactorName})
429
-
430
- /**
431
- * Auth hooks for the client manager.
432
- */
433
- export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
434
-
435
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
436
- // RE-EXPORTS
437
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
438
-
439
- export { idlFactory }
440
- export type { ${serviceName} }
441
- `;
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
+ });
442
255
  }
443
256
 
444
257
  // src/generators/query.ts
258
+ import {
259
+ generateQueryHook as generateQueryHookFromCodegen
260
+ } from "@ic-reactor/codegen";
445
261
  function generateQueryHook(options) {
446
- const { canisterName, method } = options;
447
- const pascalMethod = toPascalCase(method.name);
448
- const reactorName = getReactorName(canisterName);
449
- const serviceName = getServiceTypeName(canisterName);
450
- const hookExportName = getHookExportName(method.name, "query");
451
- const reactHookName = getReactHookName(method.name, "query");
452
- if (method.hasArgs) {
453
- return `/**
454
- * Query Hook: ${method.name}
455
- *
456
- * Auto-generated by @ic-reactor/cli
457
- * This hook wraps the ${method.name} query method.
458
- *
459
- * @example
460
- * // With the factory (for dynamic args)
461
- * const query = ${hookExportName}([arg1, arg2])
462
- * const { data } = query.useQuery()
463
- *
464
- * // Or use the hook directly
465
- * const { data } = ${reactHookName}([arg1, arg2])
466
- */
467
-
468
- import { createQueryFactory } from "@ic-reactor/react"
469
- import { ${reactorName}, type ${serviceName} } from "../reactor"
470
-
471
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
472
- // QUERY FACTORY
473
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
474
-
475
- /**
476
- * Query factory for ${method.name}
477
- *
478
- * Creates a query instance with the provided arguments.
479
- * Each unique set of args gets its own cached query.
480
- */
481
- export const ${hookExportName} = createQueryFactory(${reactorName}, {
482
- functionName: "${method.name}",
483
- // Customize your query options:
484
- // staleTime: 5 * 60 * 1000,
485
- // select: (data) => data,
486
- })
487
-
488
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
489
- // CONVENIENCE HOOK
490
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
491
-
492
- /**
493
- * React hook for ${method.name}
494
- *
495
- * @param args - Arguments to pass to the canister method
496
- * @param options - Additional React Query options
497
- */
498
- export function ${reactHookName}(
499
- args: Parameters<${serviceName}["${method.name}"]>,
500
- options?: Parameters<ReturnType<typeof ${hookExportName}>["useQuery"]>[0]
501
- ) {
502
- return ${hookExportName}(args).useQuery(options)
503
- }
504
-
505
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
506
- // UTILITY FUNCTIONS
507
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
508
-
509
- /**
510
- * Fetch ${method.name} directly (for loaders, server components, etc.)
511
- */
512
- export function fetch${pascalMethod}(args: Parameters<${serviceName}["${method.name}"]>) {
513
- return ${hookExportName}(args).fetch()
514
- }
515
-
516
- /**
517
- * Invalidate ${method.name} query cache
518
- */
519
- export function invalidate${pascalMethod}(args: Parameters<${serviceName}["${method.name}"]>) {
520
- return ${hookExportName}(args).invalidate()
521
- }
522
-
523
- /**
524
- * Get ${method.name} query key (for cache manipulation)
525
- */
526
- export function get${pascalMethod}QueryKey(args: Parameters<${serviceName}["${method.name}"]>) {
527
- return ${hookExportName}(args).getQueryKey()
528
- }
529
- `;
530
- } else {
531
- return `/**
532
- * Query Hook: ${method.name}
533
- *
534
- * Auto-generated by @ic-reactor/cli
535
- * This hook wraps the ${method.name} query method.
536
- *
537
- * @example
538
- * // Use the hook
539
- * const { data, isLoading } = ${reactHookName}()
540
- *
541
- * // Or access the query object directly
542
- * const data = await ${hookExportName}.fetch()
543
- * ${hookExportName}.invalidate()
544
- */
545
-
546
- import { createQuery } from "@ic-reactor/react"
547
- import { ${reactorName} } from "../reactor"
548
-
549
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
550
- // QUERY INSTANCE
551
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
552
-
553
- /**
554
- * Query for ${method.name}
555
- *
556
- * Provides:
557
- * - .useQuery() - React hook
558
- * - .fetch() - Direct fetch (for loaders)
559
- * - .invalidate() - Invalidate cache
560
- * - .getQueryKey() - Get query key
561
- * - .getCacheData() - Read from cache
562
- */
563
- export const ${hookExportName} = createQuery(${reactorName}, {
564
- functionName: "${method.name}",
565
- // Customize your query options:
566
- // staleTime: 5 * 60 * 1000,
567
- // select: (data) => data,
568
- })
569
-
570
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
571
- // CONVENIENCE EXPORTS
572
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
573
-
574
- /**
575
- * React hook for ${method.name}
576
- */
577
- export const ${reactHookName} = ${hookExportName}.useQuery
578
-
579
- /**
580
- * Fetch ${method.name} directly (for loaders, server components, etc.)
581
- */
582
- export const fetch${pascalMethod} = ${hookExportName}.fetch
583
-
584
- /**
585
- * Invalidate ${method.name} query cache
586
- */
587
- export const invalidate${pascalMethod} = ${hookExportName}.invalidate
588
-
589
- /**
590
- * Get ${method.name} query key (for cache manipulation)
591
- */
592
- export const get${pascalMethod}QueryKey = ${hookExportName}.getQueryKey
593
-
594
- /**
595
- * Get cached data for ${method.name}
596
- */
597
- export const get${pascalMethod}CacheData = ${hookExportName}.getCacheData
598
- `;
599
- }
262
+ const { canisterName, method, type } = options;
263
+ return generateQueryHookFromCodegen({
264
+ canisterName,
265
+ method,
266
+ type
267
+ });
600
268
  }
601
269
 
602
270
  // src/generators/mutation.ts
271
+ import {
272
+ generateMutationHook as generateMutationHookFromCodegen
273
+ } from "@ic-reactor/codegen";
603
274
  function generateMutationHook(options) {
604
275
  const { canisterName, method } = options;
605
- const pascalMethod = toPascalCase(method.name);
606
- const reactorName = getReactorName(canisterName);
607
- const serviceName = getServiceTypeName(canisterName);
608
- const hookExportName = getHookExportName(method.name, "mutation");
609
- const reactHookName = getReactHookName(method.name, "mutation");
610
- return `/**
611
- * Mutation Hook: ${method.name}
612
- *
613
- * Auto-generated by @ic-reactor/cli
614
- * This hook wraps the ${method.name} update method.
615
- *
616
- * @example
617
- * // Use the hook in a component
618
- * const { mutate, isPending } = ${reactHookName}()
619
- *
620
- * // Call the mutation
621
- * mutate(${method.hasArgs ? "[arg1, arg2]" : "[]"})
622
- *
623
- * // Or execute directly
624
- * const result = await execute${pascalMethod}(${method.hasArgs ? "[arg1, arg2]" : "[]"})
625
- */
626
-
627
- import { createMutation } from "@ic-reactor/react"
628
- import { ${reactorName}, type ${serviceName} } from "../reactor"
629
-
630
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
631
- // MUTATION INSTANCE
632
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
633
-
634
- /**
635
- * Mutation for ${method.name}
636
- *
637
- * Provides:
638
- * - .useMutation() - React hook
639
- * - .execute() - Direct execution
640
- */
641
- export const ${hookExportName} = createMutation(${reactorName}, {
642
- functionName: "${method.name}",
643
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
644
- // INVALIDATION
645
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
646
- // Uncomment and import query keys to auto-invalidate on success:
647
- // invalidateQueries: [
648
- // someQuery.getQueryKey(),
649
- // ],
650
-
651
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
652
- // SUCCESS HANDLER
653
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
654
- // onSuccess: (data, variables, context) => {
655
- // console.log("${method.name} succeeded:", data)
656
- // },
657
-
658
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
659
- // ERROR HANDLERS
660
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
661
- // Handle canister-level errors (from Result { Err })
662
- // onCanisterError: (error, variables) => {
663
- // console.error("Canister error:", error)
664
- // },
665
-
666
- // Handle all errors (network, canister, etc.)
667
- // onError: (error, variables, context) => {
668
- // console.error("Error:", error)
669
- // },
670
- })
671
-
672
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
673
- // CONVENIENCE EXPORTS
674
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
675
-
676
- /**
677
- * React hook for ${method.name}
678
- *
679
- * @param options - Mutation options (onSuccess, onError, etc.)
680
- */
681
- export const ${reactHookName} = ${hookExportName}.useMutation
682
-
683
- /**
684
- * Execute ${method.name} directly (outside of React)
685
- *
686
- * @param args - Arguments to pass to the canister method
687
- */
688
- export const execute${pascalMethod} = ${hookExportName}.execute
689
- `;
276
+ return generateMutationHookFromCodegen({
277
+ canisterName,
278
+ method
279
+ });
690
280
  }
691
281
 
692
282
  // src/generators/infiniteQuery.ts
283
+ import {
284
+ generateInfiniteQueryHook as generateInfiniteQueryHookFromCodegen
285
+ } from "@ic-reactor/codegen";
693
286
  function generateInfiniteQueryHook(options) {
694
- const { canisterName, method } = options;
695
- const pascalMethod = toPascalCase(method.name);
696
- const camelMethod = toCamelCase(method.name);
697
- const reactorName = getReactorName(canisterName);
698
- const serviceName = getServiceTypeName(canisterName);
699
- return `/**
700
- * Infinite Query Hook: ${method.name}
701
- *
702
- * Auto-generated by @ic-reactor/cli
703
- * This hook wraps the ${method.name} method for infinite/paginated queries.
704
- *
705
- * \u26A0\uFE0F CUSTOMIZATION REQUIRED:
706
- * You need to configure getArgs and getNextPageParam based on your API.
707
- *
708
- * @example
709
- * const {
710
- * data,
711
- * fetchNextPage,
712
- * hasNextPage,
713
- * isFetching,
714
- * } = ${camelMethod}InfiniteQuery.useInfiniteQuery()
715
- *
716
- * // Flatten all pages
717
- * const allItems = data?.pages.flatMap(page => page.items) ?? []
718
- */
719
-
720
- import { createInfiniteQuery } from "@ic-reactor/react"
721
- import { ${reactorName}, type ${serviceName} } from "../reactor"
722
-
723
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
724
- // CURSOR TYPE
725
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
726
-
727
- /**
728
- * Define your pagination cursor type here.
729
- * Common patterns:
730
- * - number (offset-based)
731
- * - string (cursor-based)
732
- * - { offset: number; limit: number }
733
- */
734
- type PageCursor = number
735
-
736
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
737
- // INFINITE QUERY INSTANCE
738
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
739
-
740
- /**
741
- * Infinite query for ${method.name}
742
- *
743
- * Provides:
744
- * - .useInfiniteQuery() - React hook with pagination
745
- * - .fetch() - Fetch first page
746
- * - .invalidate() - Invalidate all pages
747
- * - .getQueryKey() - Get query key
748
- * - .getCacheData() - Read from cache
749
- */
750
- export const ${camelMethod}InfiniteQuery = createInfiniteQuery(${reactorName}, {
751
- functionName: "${method.name}",
752
-
753
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
754
- // PAGINATION CONFIG (CUSTOMIZE THESE)
755
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
756
-
757
- /**
758
- * Initial page parameter (e.g., 0 for offset, null for cursor)
759
- */
760
- initialPageParam: 0 as PageCursor,
761
-
762
- /**
763
- * Convert page param to method arguments.
764
- * Customize this based on your canister API.
765
- */
766
- getArgs: (pageParam: PageCursor) => {
767
- // Example: offset-based pagination
768
- return [{ offset: pageParam, limit: 10 }] as Parameters<${serviceName}["${method.name}"]>
769
-
770
- // Example: cursor-based pagination
771
- // return [{ cursor: pageParam, limit: 10 }] as Parameters<${serviceName}["${method.name}"]>
772
- },
773
-
774
- /**
775
- * Extract next page param from the last page.
776
- * Return undefined/null when there are no more pages.
777
- */
778
- getNextPageParam: (lastPage, allPages, lastPageParam) => {
779
- // Example: offset-based - return next offset or undefined if no more
780
- // const items = lastPage.items ?? []
781
- // if (items.length < 10) return undefined
782
- // return lastPageParam + 10
783
-
784
- // Example: cursor-based
785
- // return lastPage.nextCursor ?? undefined
786
-
787
- // Placeholder - customize for your API
788
- return undefined
789
- },
790
-
791
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
792
- // OPTIONAL CONFIG
793
- // \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
794
-
795
- // Bi-directional scrolling (optional)
796
- // getPreviousPageParam: (firstPage, allPages, firstPageParam) => {
797
- // return firstPageParam > 0 ? firstPageParam - 10 : undefined
798
- // },
799
-
800
- // Max pages to keep in cache (for memory management)
801
- // maxPages: 10,
802
-
803
- // How long data stays fresh
804
- // staleTime: 5 * 60 * 1000,
805
-
806
- // Transform the data
807
- // select: (data) => ({
808
- // pages: data.pages,
809
- // pageParams: data.pageParams,
810
- // items: data.pages.flatMap(page => page.items),
811
- // }),
812
- })
813
-
814
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
815
- // CONVENIENCE EXPORTS
816
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
817
-
818
- /**
819
- * React hook for paginated ${method.name}
820
- */
821
- export const use${pascalMethod}InfiniteQuery = ${camelMethod}InfiniteQuery.useInfiniteQuery
822
-
823
- /**
824
- * Fetch first page of ${method.name}
825
- */
826
- export const fetch${pascalMethod}FirstPage = ${camelMethod}InfiniteQuery.fetch
827
-
828
- /**
829
- * Invalidate all cached pages
830
- */
831
- export const invalidate${pascalMethod}Pages = ${camelMethod}InfiniteQuery.invalidate
832
- `;
833
- }
834
-
835
- // src/utils/bindgen.ts
836
- import { generate } from "@icp-sdk/bindgen/core";
837
- import path3 from "path";
838
- import fs4 from "fs";
839
- async function generateDeclarations(options) {
840
- const { didFile, outDir } = options;
841
- if (!fs4.existsSync(didFile)) {
842
- return {
843
- success: false,
844
- declarationsDir: "",
845
- error: `DID file not found: ${didFile}`
846
- };
847
- }
848
- const declarationsDir = path3.join(outDir, "declarations");
849
- try {
850
- await generate({
851
- didFile,
852
- outDir: declarationsDir,
853
- output: {
854
- actor: {
855
- disabled: true
856
- // We don't need actor creation, we use Reactor
857
- },
858
- force: true
859
- // Overwrite existing files
860
- }
861
- });
862
- return {
863
- success: true,
864
- declarationsDir
865
- };
866
- } catch (error) {
867
- return {
868
- success: false,
869
- declarationsDir,
870
- error: error instanceof Error ? error.message : String(error)
871
- };
872
- }
287
+ const { canisterName, method, type } = options;
288
+ return generateInfiniteQueryHookFromCodegen({
289
+ canisterName,
290
+ method,
291
+ type
292
+ });
873
293
  }
874
294
 
875
295
  // src/commands/add.ts
296
+ import { getHookFileName } from "@ic-reactor/codegen";
297
+ import { generateDeclarations } from "@ic-reactor/codegen";
876
298
  async function addCommand(options) {
877
299
  console.log();
878
300
  p2.intro(pc2.cyan("\u{1F527} Add Canister Hooks"));
@@ -934,7 +356,7 @@ async function addCommand(options) {
934
356
  p2.log.error(`Canister ${pc2.yellow(selectedCanister)} not found in config.`);
935
357
  process.exit(1);
936
358
  }
937
- const didFilePath = path4.resolve(projectRoot, canisterConfig.didFile);
359
+ const didFilePath = path3.resolve(projectRoot, canisterConfig.didFile);
938
360
  let methods;
939
361
  try {
940
362
  methods = parseDIDFile(didFilePath);
@@ -1059,13 +481,13 @@ ${error.message}`
1059
481
  p2.log.warn("All methods were skipped. Nothing to generate.");
1060
482
  process.exit(0);
1061
483
  }
1062
- const canisterOutDir = path4.join(projectRoot, config.outDir, selectedCanister);
1063
- const hooksOutDir = path4.join(canisterOutDir, "hooks");
484
+ const canisterOutDir = path3.join(projectRoot, config.outDir, selectedCanister);
485
+ const hooksOutDir = path3.join(canisterOutDir, "hooks");
1064
486
  ensureDir(hooksOutDir);
1065
487
  const spinner4 = p2.spinner();
1066
488
  spinner4.start("Generating hooks...");
1067
489
  const generatedFiles = [];
1068
- const reactorPath = path4.join(canisterOutDir, "reactor.ts");
490
+ const reactorPath = path3.join(canisterOutDir, "reactor.ts");
1069
491
  if (!fileExists(reactorPath)) {
1070
492
  spinner4.message("Generating TypeScript declarations...");
1071
493
  const bindgenResult = await generateDeclarations({
@@ -1089,12 +511,12 @@ ${error.message}`
1089
511
  outDir: canisterOutDir,
1090
512
  hasDeclarations: bindgenResult.success
1091
513
  });
1092
- fs5.writeFileSync(reactorPath, reactorContent);
514
+ fs3.writeFileSync(reactorPath, reactorContent);
1093
515
  generatedFiles.push("reactor.ts");
1094
516
  }
1095
517
  for (const { method, hookType } of methodsWithHookTypes) {
1096
518
  const fileName = getHookFileName(method.name, hookType);
1097
- const filePath = path4.join(hooksOutDir, fileName);
519
+ const filePath = path3.join(hooksOutDir, fileName);
1098
520
  let content;
1099
521
  switch (hookType) {
1100
522
  case "query":
@@ -1121,25 +543,49 @@ ${error.message}`
1121
543
  });
1122
544
  break;
1123
545
  }
1124
- fs5.writeFileSync(filePath, content);
1125
- generatedFiles.push(path4.join("hooks", fileName));
546
+ fs3.writeFileSync(filePath, content);
547
+ generatedFiles.push(path3.join("hooks", fileName));
1126
548
  }
1127
- const indexPath = path4.join(hooksOutDir, "index.ts");
1128
- const indexContent = generateIndexFile(methodsWithHookTypes);
1129
- fs5.writeFileSync(indexPath, indexContent);
549
+ const indexPath = path3.join(hooksOutDir, "index.ts");
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);
1130
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
+ });
1131
579
  config.generatedHooks[selectedCanister] = [
1132
- .../* @__PURE__ */ new Set([
1133
- ...config.generatedHooks[selectedCanister] ?? [],
1134
- ...selectedMethods.map((m) => m.name)
1135
- ])
580
+ ...filteredExisting,
581
+ ...newHookConfigs
1136
582
  ];
1137
583
  saveConfig(config, configPath);
1138
584
  spinner4.stop("Hooks generated!");
1139
585
  console.log();
1140
586
  p2.note(
1141
587
  generatedFiles.map((f) => pc2.green(`\u2713 ${f}`)).join("\n"),
1142
- `Generated in ${pc2.dim(path4.relative(projectRoot, canisterOutDir))}`
588
+ `Generated in ${pc2.dim(path3.relative(projectRoot, canisterOutDir))}`
1143
589
  );
1144
590
  p2.outro(pc2.green("\u2713 Done!"));
1145
591
  }
@@ -1158,8 +604,8 @@ async function promptForNewCanister(projectRoot) {
1158
604
  placeholder: "./backend.did",
1159
605
  validate: (value) => {
1160
606
  if (!value) return "DID file path is required";
1161
- const fullPath = path4.resolve(projectRoot, value);
1162
- if (!fs5.existsSync(fullPath)) {
607
+ const fullPath = path3.resolve(projectRoot, value);
608
+ if (!fs3.existsSync(fullPath)) {
1163
609
  return `File not found: ${value}`;
1164
610
  }
1165
611
  return void 0;
@@ -1170,31 +616,19 @@ async function promptForNewCanister(projectRoot) {
1170
616
  name,
1171
617
  config: {
1172
618
  didFile,
1173
- clientManagerPath: "../../lib/client",
1174
619
  useDisplayReactor: true
1175
620
  }
1176
621
  };
1177
622
  }
1178
- function generateIndexFile(methods) {
1179
- const exports = methods.map(({ method, hookType }) => {
1180
- const fileName = getHookFileName(method.name, hookType).replace(".ts", "");
1181
- return `export * from "./${fileName}"`;
1182
- });
1183
- return `/**
1184
- * Hook barrel exports
1185
- *
1186
- * Auto-generated by @ic-reactor/cli
1187
- */
1188
-
1189
- ${exports.join("\n")}
1190
- `;
1191
- }
1192
623
 
1193
624
  // src/commands/sync.ts
1194
625
  import * as p3 from "@clack/prompts";
1195
- import fs6 from "fs";
1196
- import path5 from "path";
626
+ import fs4 from "fs";
627
+ import path4 from "path";
1197
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";
1198
632
  async function syncCommand(options) {
1199
633
  console.log();
1200
634
  p3.intro(pc3.cyan("\u{1F504} Sync Canister Hooks"));
@@ -1245,74 +679,104 @@ async function syncCommand(options) {
1245
679
  if (generatedMethods.length === 0) {
1246
680
  continue;
1247
681
  }
1248
- const didFilePath = path5.resolve(projectRoot, canisterConfig.didFile);
682
+ const didFilePath = path4.resolve(projectRoot, canisterConfig.didFile);
1249
683
  let methods;
1250
684
  try {
1251
- methods = parseDIDFile(didFilePath);
685
+ methods = parseDIDFile2(didFilePath);
1252
686
  } catch (error) {
1253
687
  errors.push(
1254
688
  `${canisterName}: Failed to parse DID file - ${error.message}`
1255
689
  );
1256
690
  continue;
1257
691
  }
1258
- const currentMethodNames = methods.map((m) => m.name);
1259
- const removedMethods = generatedMethods.filter(
1260
- (name) => !currentMethodNames.includes(name)
692
+ const hooks = generatedMethods.map(
693
+ (h) => typeof h === "string" ? { name: h } : h
1261
694
  );
695
+ const currentMethodNames = methods.map((m) => m.name);
696
+ const removedMethods = hooks.filter((h) => !currentMethodNames.includes(h.name)).map((h) => h.name);
1262
697
  if (removedMethods.length > 0) {
1263
698
  p3.log.warn(
1264
699
  `${canisterName}: Methods removed from DID: ${pc3.yellow(removedMethods.join(", "))}`
1265
700
  );
1266
701
  }
1267
- 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));
1268
704
  if (newMethods.length > 0) {
1269
705
  p3.log.info(
1270
706
  `${canisterName}: New methods available: ${pc3.cyan(newMethods.map((m) => m.name).join(", "))}`
1271
707
  );
1272
708
  }
1273
- const canisterOutDir = path5.join(projectRoot, config.outDir, canisterName);
1274
- const reactorPath = path5.join(canisterOutDir, "reactor.ts");
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
+ }
727
+ const reactorPath = path4.join(canisterOutDir, "reactor.ts");
1275
728
  const reactorContent = generateReactorFile({
1276
729
  canisterName,
1277
730
  canisterConfig,
1278
731
  config,
1279
732
  outDir: canisterOutDir
1280
733
  });
1281
- fs6.writeFileSync(reactorPath, reactorContent);
734
+ fs4.writeFileSync(reactorPath, reactorContent);
1282
735
  totalUpdated++;
1283
- const hooksOutDir = path5.join(canisterOutDir, "hooks");
736
+ const hooksOutDir = path4.join(canisterOutDir, "hooks");
1284
737
  ensureDir(hooksOutDir);
1285
- for (const methodName of generatedMethods) {
738
+ for (const hookConfig of hooks) {
739
+ const methodName = hookConfig.name;
1286
740
  const method = methods.find((m) => m.name === methodName);
1287
741
  if (!method) {
1288
742
  totalSkipped++;
1289
743
  continue;
1290
744
  }
1291
- const queryFileName = getHookFileName(methodName, "query");
1292
- const mutationFileName = getHookFileName(methodName, "mutation");
1293
- 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);
1294
756
  let content;
1295
- let fileName;
1296
- if (fs6.existsSync(path5.join(hooksOutDir, infiniteQueryFileName))) {
1297
- fileName = infiniteQueryFileName;
1298
- totalSkipped++;
1299
- continue;
1300
- } else if (method.type === "query") {
1301
- fileName = queryFileName;
757
+ if (hookType.includes("Query")) {
1302
758
  content = generateQueryHook({
1303
759
  canisterName,
1304
760
  method,
1305
- config
761
+ config,
762
+ type: hookType
1306
763
  });
1307
764
  } else {
1308
- fileName = mutationFileName;
1309
765
  content = generateMutationHook({
1310
766
  canisterName,
1311
767
  method,
1312
768
  config
1313
769
  });
1314
770
  }
1315
- fs6.writeFileSync(path5.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);
1316
780
  totalUpdated++;
1317
781
  }
1318
782
  }
@@ -1335,8 +799,9 @@ Skipped: ${pc3.dim(totalSkipped.toString())} files (preserved customizations)`,
1335
799
 
1336
800
  // src/commands/list.ts
1337
801
  import * as p4 from "@clack/prompts";
1338
- import path6 from "path";
802
+ import path5 from "path";
1339
803
  import pc4 from "picocolors";
804
+ import { parseDIDFile as parseDIDFile3 } from "@ic-reactor/codegen";
1340
805
  async function listCommand(options) {
1341
806
  console.log();
1342
807
  p4.intro(pc4.cyan("\u{1F4CB} List Canister Methods"));
@@ -1384,9 +849,9 @@ async function listCommand(options) {
1384
849
  p4.log.error(`Canister ${pc4.yellow(selectedCanister)} not found in config.`);
1385
850
  process.exit(1);
1386
851
  }
1387
- const didFilePath = path6.resolve(projectRoot, canisterConfig.didFile);
852
+ const didFilePath = path5.resolve(projectRoot, canisterConfig.didFile);
1388
853
  try {
1389
- const methods = parseDIDFile(didFilePath);
854
+ const methods = parseDIDFile3(didFilePath);
1390
855
  if (methods.length === 0) {
1391
856
  p4.log.warn(`No methods found in ${pc4.yellow(didFilePath)}`);
1392
857
  process.exit(0);
@@ -1445,14 +910,17 @@ ${error.message}`
1445
910
 
1446
911
  // src/commands/fetch.ts
1447
912
  import * as p5 from "@clack/prompts";
1448
- import fs7 from "fs";
1449
- import path7 from "path";
913
+ import fs5 from "fs";
914
+ import path6 from "path";
1450
915
  import pc5 from "picocolors";
916
+ import { formatMethodForDisplay as formatMethodForDisplay2 } from "@ic-reactor/codegen";
917
+ import { getHookFileName as getHookFileName3, toCamelCase } from "@ic-reactor/codegen";
1451
918
 
1452
919
  // src/utils/network.ts
1453
920
  import { HttpAgent, Actor } from "@icp-sdk/core/agent";
1454
921
  import { Principal } from "@icp-sdk/core/principal";
1455
922
  import { IDL } from "@icp-sdk/core/candid";
923
+ import { extractMethods } from "@ic-reactor/codegen";
1456
924
  var IC_HOST = "https://icp-api.io";
1457
925
  var LOCAL_HOST = "http://127.0.0.1:4943";
1458
926
  async function fetchCandidFromCanister(options) {
@@ -1524,6 +992,7 @@ function shortenCanisterId(canisterId) {
1524
992
  }
1525
993
 
1526
994
  // src/commands/fetch.ts
995
+ import { generateDeclarations as generateDeclarations3 } from "@ic-reactor/codegen";
1527
996
  async function fetchCommand(options) {
1528
997
  console.log();
1529
998
  p5.intro(pc5.cyan("\u{1F310} Fetch from Live Canister"));
@@ -1624,7 +1093,7 @@ async function fetchCommand(options) {
1624
1093
  message: "Select methods to generate hooks for",
1625
1094
  options: methods.map((method) => ({
1626
1095
  value: method.name,
1627
- label: formatMethodForDisplay(method)
1096
+ label: formatMethodForDisplay2(method)
1628
1097
  })),
1629
1098
  required: true
1630
1099
  });
@@ -1712,7 +1181,7 @@ async function fetchCommand(options) {
1712
1181
  let configPath = findConfigFile();
1713
1182
  let config;
1714
1183
  if (!configPath) {
1715
- configPath = path7.join(projectRoot, CONFIG_FILE_NAME);
1184
+ configPath = path6.join(projectRoot, CONFIG_FILE_NAME);
1716
1185
  config = { ...DEFAULT_CONFIG };
1717
1186
  p5.log.info(`Creating ${pc5.yellow(CONFIG_FILE_NAME)}`);
1718
1187
  } else {
@@ -1720,24 +1189,23 @@ async function fetchCommand(options) {
1720
1189
  }
1721
1190
  const canisterConfig = {
1722
1191
  didFile: `./candid/${canisterName}.did`,
1723
- clientManagerPath: "../../lib/client",
1724
1192
  useDisplayReactor: true,
1725
1193
  canisterId
1726
1194
  };
1727
1195
  config.canisters[canisterName] = canisterConfig;
1728
- const canisterOutDir = path7.join(projectRoot, config.outDir, canisterName);
1729
- const hooksOutDir = path7.join(canisterOutDir, "hooks");
1730
- const candidDir = path7.join(projectRoot, "candid");
1196
+ const canisterOutDir = path6.join(projectRoot, config.outDir, canisterName);
1197
+ const hooksOutDir = path6.join(canisterOutDir, "hooks");
1198
+ const candidDir = path6.join(projectRoot, "candid");
1731
1199
  ensureDir(hooksOutDir);
1732
1200
  ensureDir(candidDir);
1733
1201
  const genSpinner = p5.spinner();
1734
1202
  genSpinner.start("Generating hooks...");
1735
1203
  const generatedFiles = [];
1736
- const candidPath = path7.join(candidDir, `${canisterName}.did`);
1737
- fs7.writeFileSync(candidPath, candidSource);
1204
+ const candidPath = path6.join(candidDir, `${canisterName}.did`);
1205
+ fs5.writeFileSync(candidPath, candidSource);
1738
1206
  generatedFiles.push(`candid/${canisterName}.did`);
1739
1207
  genSpinner.message("Generating TypeScript declarations...");
1740
- const bindgenResult = await generateDeclarations({
1208
+ const bindgenResult = await generateDeclarations3({
1741
1209
  didFile: candidPath,
1742
1210
  outDir: canisterOutDir,
1743
1211
  canisterName
@@ -1751,18 +1219,19 @@ async function fetchCommand(options) {
1751
1219
  );
1752
1220
  }
1753
1221
  genSpinner.message("Generating reactor...");
1754
- const reactorPath = path7.join(canisterOutDir, "reactor.ts");
1222
+ const reactorPath = path6.join(canisterOutDir, "reactor.ts");
1755
1223
  const reactorContent = generateReactorFileForFetch({
1756
1224
  canisterName,
1757
1225
  canisterConfig,
1226
+ config,
1758
1227
  canisterId,
1759
1228
  hasDeclarations: bindgenResult.success
1760
1229
  });
1761
- fs7.writeFileSync(reactorPath, reactorContent);
1230
+ fs5.writeFileSync(reactorPath, reactorContent);
1762
1231
  generatedFiles.push("reactor.ts");
1763
1232
  for (const { method, hookType } of methodsWithHookTypes) {
1764
- const fileName = getHookFileName(method.name, hookType);
1765
- const filePath = path7.join(hooksOutDir, fileName);
1233
+ const fileName = getHookFileName3(method.name, hookType);
1234
+ const filePath = path6.join(hooksOutDir, fileName);
1766
1235
  let content;
1767
1236
  switch (hookType) {
1768
1237
  case "query":
@@ -1789,12 +1258,12 @@ async function fetchCommand(options) {
1789
1258
  });
1790
1259
  break;
1791
1260
  }
1792
- fs7.writeFileSync(filePath, content);
1793
- generatedFiles.push(path7.join("hooks", fileName));
1261
+ fs5.writeFileSync(filePath, content);
1262
+ generatedFiles.push(path6.join("hooks", fileName));
1794
1263
  }
1795
- const indexPath = path7.join(hooksOutDir, "index.ts");
1796
- const indexContent = generateIndexFile2(methodsWithHookTypes);
1797
- fs7.writeFileSync(indexPath, indexContent);
1264
+ const indexPath = path6.join(hooksOutDir, "index.ts");
1265
+ const indexContent = generateIndexFile(methodsWithHookTypes);
1266
+ fs5.writeFileSync(indexPath, indexContent);
1798
1267
  generatedFiles.push("hooks/index.ts");
1799
1268
  config.generatedHooks[canisterName] = [
1800
1269
  .../* @__PURE__ */ new Set([
@@ -1807,7 +1276,7 @@ async function fetchCommand(options) {
1807
1276
  console.log();
1808
1277
  p5.note(
1809
1278
  generatedFiles.map((f) => pc5.green(`\u2713 ${f}`)).join("\n"),
1810
- `Generated in ${pc5.dim(path7.relative(projectRoot, canisterOutDir))}`
1279
+ `Generated in ${pc5.dim(path6.relative(projectRoot, canisterOutDir))}`
1811
1280
  );
1812
1281
  console.log();
1813
1282
  p5.note(
@@ -1820,12 +1289,13 @@ Methods: ${pc5.dim(selectedMethods.map((m) => m.name).join(", "))}`,
1820
1289
  p5.outro(pc5.green("\u2713 Done!"));
1821
1290
  }
1822
1291
  function generateReactorFileForFetch(options) {
1823
- const { canisterName, canisterConfig, canisterId, hasDeclarations } = options;
1292
+ const { canisterName, canisterConfig, config, canisterId, hasDeclarations } = options;
1824
1293
  const pascalName = canisterName.charAt(0).toUpperCase() + canisterName.slice(1);
1825
1294
  const reactorName = `${canisterName}Reactor`;
1826
1295
  const serviceName = `${pascalName}Service`;
1827
1296
  const reactorType = canisterConfig.useDisplayReactor !== false ? "DisplayReactor" : "Reactor";
1828
- const clientManagerPath = canisterConfig.clientManagerPath ?? "../../lib/client";
1297
+ const clientManagerPath = canisterConfig.clientManagerPath ?? config.clientManagerPath ?? "../../lib/client";
1298
+ const didFileName = path6.basename(canisterConfig.didFile);
1829
1299
  if (hasDeclarations) {
1830
1300
  return `/**
1831
1301
  * ${pascalName} Reactor
@@ -1836,11 +1306,11 @@ function generateReactorFileForFetch(options) {
1836
1306
  * You can customize this file to add global configuration.
1837
1307
  */
1838
1308
 
1839
- import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
1309
+ import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
1840
1310
  import { clientManager } from "${clientManagerPath}"
1841
1311
 
1842
1312
  // Import generated declarations
1843
- import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${canisterName}.did"
1313
+ import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${didFileName}"
1844
1314
 
1845
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
1846
1316
  // REACTOR INSTANCE
@@ -1859,11 +1329,11 @@ export const ${reactorName} = new ${reactorType}<${serviceName}>({
1859
1329
  })
1860
1330
 
1861
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
1862
- // BASE HOOKS
1332
+ // ACTOR HOOKS
1863
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
1864
1334
 
1865
1335
  /**
1866
- * Base actor hooks - use these directly or import method-specific hooks.
1336
+ * Actor hooks for ${canisterName} - use these directly or import method-specific hooks.
1867
1337
  */
1868
1338
  export const {
1869
1339
  useActorQuery,
@@ -1874,11 +1344,6 @@ export const {
1874
1344
  useActorMethod,
1875
1345
  } = createActorHooks(${reactorName})
1876
1346
 
1877
- /**
1878
- * Auth hooks for the client manager.
1879
- */
1880
- export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
1881
-
1882
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
1883
1348
  // RE-EXPORTS
1884
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
@@ -1896,7 +1361,7 @@ export type { ${serviceName} }
1896
1361
  * You can customize this file to add global configuration.
1897
1362
  */
1898
1363
 
1899
- import { ${reactorType}, createActorHooks, createAuthHooks } from "@ic-reactor/react"
1364
+ import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
1900
1365
  import { clientManager } from "${clientManagerPath}"
1901
1366
 
1902
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
@@ -1907,7 +1372,7 @@ import { clientManager } from "${clientManagerPath}"
1907
1372
  // npx @icp-sdk/bindgen --input ./candid/${canisterName}.did --output ./${canisterName}/declarations
1908
1373
 
1909
1374
  // For now, import just the IDL factory (you may need to create this manually)
1910
- // import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${canisterName}.did"
1375
+ // import { idlFactory, type _SERVICE as ${serviceName} } from "./declarations/${didFileName}"
1911
1376
 
1912
1377
  // Fallback generic type - replace with generated types
1913
1378
  type ${serviceName} = Record<string, (...args: unknown[]) => Promise<unknown>>
@@ -1933,11 +1398,11 @@ export const ${reactorName} = new ${reactorType}<${serviceName}>({
1933
1398
  })
1934
1399
 
1935
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
1936
- // BASE HOOKS
1401
+ // ACTOR HOOKS
1937
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
1938
1403
 
1939
1404
  /**
1940
- * Base actor hooks - use these directly or import method-specific hooks.
1405
+ * Actor hooks for ${canisterName} - use these directly or import method-specific hooks.
1941
1406
  */
1942
1407
  export const {
1943
1408
  useActorQuery,
@@ -1948,11 +1413,6 @@ export const {
1948
1413
  useActorMethod,
1949
1414
  } = createActorHooks(${reactorName})
1950
1415
 
1951
- /**
1952
- * Auth hooks for the client manager.
1953
- */
1954
- export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(clientManager)
1955
-
1956
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
1957
1417
  // RE-EXPORTS
1958
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
@@ -1961,9 +1421,9 @@ export { idlFactory }
1961
1421
  export type { ${serviceName} }
1962
1422
  `;
1963
1423
  }
1964
- function generateIndexFile2(methods) {
1424
+ function generateIndexFile(methods) {
1965
1425
  const exports = methods.map(({ method, hookType }) => {
1966
- const fileName = getHookFileName(method.name, hookType).replace(".ts", "");
1426
+ const fileName = getHookFileName3(method.name, hookType).replace(".ts", "");
1967
1427
  return `export * from "./${fileName}"`;
1968
1428
  });
1969
1429
  return `/**