@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 +258 -588
- package/package.json +15 -12
- package/schema.json +24 -2
- package/src/commands/add.ts +447 -0
- package/src/commands/fetch.ts +600 -0
- package/src/commands/index.ts +9 -0
- package/src/commands/init.ts +224 -0
- package/src/commands/list.ts +144 -0
- package/src/commands/sync.ts +259 -0
- package/src/generators/index.ts +8 -0
- package/src/generators/infiniteQuery.ts +34 -0
- package/src/generators/mutation.ts +29 -0
- package/src/generators/query.ts +32 -0
- package/src/generators/reactor.ts +41 -0
- package/src/index.ts +63 -0
- package/src/types.ts +26 -0
- package/src/utils/config.ts +114 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/network.ts +139 -0
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
|
|
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 {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
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
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1000
|
-
|
|
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
|
-
|
|
1004
|
-
|
|
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 (!
|
|
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
|
|
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 =
|
|
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
|
|
1130
|
-
|
|
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
|
|
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
|
-
|
|
734
|
+
fs4.writeFileSync(reactorPath, reactorContent);
|
|
1153
735
|
totalUpdated++;
|
|
1154
736
|
const hooksOutDir = path4.join(canisterOutDir, "hooks");
|
|
1155
737
|
ensureDir(hooksOutDir);
|
|
1156
|
-
for (const
|
|
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
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
1226
|
+
config,
|
|
1227
|
+
canisterId,
|
|
1228
|
+
hasDeclarations: bindgenResult.success
|
|
1615
1229
|
});
|
|
1616
|
-
|
|
1230
|
+
fs5.writeFileSync(reactorPath, reactorContent);
|
|
1617
1231
|
generatedFiles.push("reactor.ts");
|
|
1618
1232
|
for (const { method, hookType } of methodsWithHookTypes) {
|
|
1619
|
-
const fileName =
|
|
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
|
-
|
|
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 =
|
|
1652
|
-
|
|
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
|
-
|
|
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
|
|
1309
|
+
import { ${reactorType}, createActorHooks } from "@ic-reactor/react"
|
|
1694
1310
|
import { clientManager } from "${clientManagerPath}"
|
|
1695
1311
|
|
|
1696
1312
|
// Import generated declarations
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
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
|
-
//
|
|
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
|
|
1706
|
-
// npx @icp-sdk/bindgen --input ./candid/${canisterName}.did --output
|
|
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
|
-
//
|
|
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
|
-
*
|
|
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
|
|
1424
|
+
function generateIndexFile(methods) {
|
|
1755
1425
|
const exports = methods.map(({ method, hookType }) => {
|
|
1756
|
-
const fileName =
|
|
1426
|
+
const fileName = getHookFileName3(method.name, hookType).replace(".ts", "");
|
|
1757
1427
|
return `export * from "./${fileName}"`;
|
|
1758
1428
|
});
|
|
1759
1429
|
return `/**
|