@ic-reactor/vite-plugin 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -260,27 +260,28 @@ icReactorPlugin({
260
260
 
261
261
  ## Integration with ICP CLI
262
262
 
263
- If you're using [icp-cli](https://github.com/AstroxNetwork/icp-cli), configure your `vite.config.ts` to pass canister IDs via the `ic_env` cookie:
263
+ `@ic-reactor/vite-plugin` now supports **zero-config local `icp-cli` canister env injection** during `vite dev`.
264
+
265
+ When dev server starts, the plugin automatically tries to read:
266
+
267
+ - `.icp/cache/mappings/local.ids.json`
268
+
269
+ If present, it sets an `ic_env` cookie with:
270
+
271
+ - `ic_root_key=<local-root-key>`
272
+ - `PUBLIC_CANISTER_ID:<name>=<canister-id>`
273
+
274
+ This means `withCanisterEnv: true` works out of the box after `icp deploy`, without custom cookie code in `vite.config.ts`.
264
275
 
265
276
  ```typescript
266
277
  // vite.config.ts
278
+ import { defineConfig } from "vite"
279
+ import react from "@vitejs/plugin-react"
267
280
  import { icReactorPlugin } from "@ic-reactor/vite-plugin"
268
- import fs from "fs"
269
- import path from "path"
270
-
271
- function loadCanisterIds(): Record<string, string> {
272
- const idsPath = path.resolve(__dirname, ".icp/cache/mappings/local.ids.json")
273
- try {
274
- return JSON.parse(fs.readFileSync(idsPath, "utf-8"))
275
- } catch {
276
- return {}
277
- }
278
- }
279
-
280
- const canisterIds = loadCanisterIds()
281
281
 
282
282
  export default defineConfig({
283
283
  plugins: [
284
+ react(),
284
285
  icReactorPlugin({
285
286
  canisters: [
286
287
  {
@@ -290,15 +291,15 @@ export default defineConfig({
290
291
  ],
291
292
  }),
292
293
  ],
293
- server: {
294
- headers: {
295
- "Set-Cookie": `ic_env=${encodeURIComponent(
296
- Object.entries(canisterIds)
297
- .map(([name, id]) => `PUBLIC_CANISTER_ID:${name}=${id}`)
298
- .join("&")
299
- )}; SameSite=Lax;`,
300
- },
301
- },
294
+ })
295
+ ```
296
+
297
+ If you need to disable this behavior:
298
+
299
+ ```typescript
300
+ icReactorPlugin({
301
+ canisters: [...],
302
+ autoInjectIcEnv: false,
302
303
  })
303
304
  ```
304
305
 
package/dist/index.cjs CHANGED
@@ -30,380 +30,117 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- icReactorAdvancedPlugin: () => icReactorAdvancedPlugin,
33
+ default: () => index_default,
34
34
  icReactorPlugin: () => icReactorPlugin
35
35
  });
36
36
  module.exports = __toCommonJS(index_exports);
37
-
38
- // src/simple.ts
39
- var import_core2 = require("@icp-sdk/bindgen/core");
40
- var import_fs2 = __toESM(require("fs"), 1);
41
- var import_path2 = __toESM(require("path"), 1);
42
-
43
- // src/advanced.ts
44
37
  var import_fs = __toESM(require("fs"), 1);
45
38
  var import_path = __toESM(require("path"), 1);
46
- var import_core = require("@icp-sdk/bindgen/core");
47
- function toPascalCase(str) {
48
- return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
49
- }
50
- function toCamelCase(str) {
51
- const pascal = toPascalCase(str);
52
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
39
+ var import_codegen = require("@ic-reactor/codegen");
40
+ var ICP_LOCAL_IDS_PATH = ".icp/cache/mappings/local.ids.json";
41
+ var IC_ROOT_KEY_HEX = "308182301d060d2b0601040182dc7c0503010201060c2b0601040182dc7c050302010361008b52b4994f94c7ce4be1c1542d7c81dc79fea17d49efe8fa42e8566373581d4b969c4a59e96a0ef51b711fe5027ec01601182519d0a788f4bfe388e593b97cd1d7e44904de79422430bca686ac8c21305b3397b5ba4d7037d17877312fb7ee34";
42
+ function loadLocalCanisterIds(rootDir) {
43
+ const idsPath = import_path.default.resolve(rootDir, ICP_LOCAL_IDS_PATH);
44
+ try {
45
+ return JSON.parse(import_fs.default.readFileSync(idsPath, "utf-8"));
46
+ } catch {
47
+ return null;
48
+ }
53
49
  }
54
- function extractMethods(didContent) {
55
- const methodRegex = /([a-zA-Z0-9_]+)\s*:\s*(?:func\s*)?\(([\s\S]*?)\)\s*->\s*\(([\s\S]*?)\)\s*(query|composite_query)?/g;
56
- const methods = [];
57
- let match;
58
- while ((match = methodRegex.exec(didContent)) !== null) {
59
- const name = match[1];
60
- const args = match[2].trim();
61
- const isQuery = !!match[4];
62
- methods.push({
63
- name,
64
- type: isQuery ? "query" : "mutation",
65
- hasArgs: args.length > 0
66
- });
50
+ function buildIcEnvCookie(canisterIds) {
51
+ const envParts = [`ic_root_key=${IC_ROOT_KEY_HEX}`];
52
+ for (const [name, id] of Object.entries(canisterIds)) {
53
+ envParts.push(`PUBLIC_CANISTER_ID:${name}=${id}`);
67
54
  }
68
- return methods;
55
+ return encodeURIComponent(envParts.join("&"));
69
56
  }
70
- function generateAdvancedReactorFile(canisterName, useDisplayReactor, clientManagerPath, didContent) {
71
- const pascalName = toPascalCase(canisterName);
72
- const camelName = toCamelCase(canisterName);
73
- const reactorType = useDisplayReactor ? "DisplayReactor" : "Reactor";
74
- const methods = extractMethods(didContent);
75
- const hooks = methods.map(({ name, type, hasArgs }) => {
76
- const pascalMethod = toPascalCase(name);
77
- const camelMethod = toCamelCase(name);
78
- if (type === "query") {
79
- const hook = `
80
- export const use${pascalMethod}Query = (
81
- args: Parameters<${pascalName}Service["${name}"]>,
82
- options?: any
83
- ) =>
84
- useActorQuery({
85
- functionName: "${name}",
86
- args,
87
- ...options,
88
- })
89
- `;
90
- const staticQuery = !hasArgs ? `
91
- export const ${camelMethod}Query = createQuery(${camelName}Reactor, {
92
- functionName: "${name}",
93
- })
94
- ` : "";
95
- return hook + staticQuery;
96
- } else {
97
- const hook = `
98
- export const use${pascalMethod}Mutation = (
99
- options?: any
100
- ) =>
101
- useActorMutation({
102
- functionName: "${name}",
103
- ...options,
104
- })
105
- `;
106
- const staticMutation = !hasArgs ? `
107
- export const ${camelMethod}Mutation = createMutation(${camelName}Reactor, {
108
- functionName: "${name}",
109
- })
110
- ` : "";
111
- return hook + staticMutation;
112
- }
113
- });
114
- return `/**
115
- * AUTO-GENERATED BY @ic-reactor/vite-plugin
116
- * DO NOT EDIT MANUALLY
117
- *
118
- * Canister: ${canisterName}
119
- * Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
120
- *
121
- * This file provides type-safe React hooks for interacting with the
122
- * ${canisterName} canister using ic-reactor.
123
- */
124
-
125
- import {
126
- ${reactorType},
127
- createActorHooks,
128
- createAuthHooks,
129
- createQuery,
130
- createMutation,
131
- } from "@ic-reactor/react"
132
-
133
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
134
- // USER-PROVIDED CLIENT MANAGER
135
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
136
- import { clientManager } from "${clientManagerPath}"
137
-
138
- // Import generated declarations from @icp-sdk/bindgen
139
- import {
140
- idlFactory,
141
- type _SERVICE,
142
- } from "./declarations/${canisterName}.did"
143
-
144
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
145
- // REACTOR INSTANCE
146
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
147
- type ${pascalName}Service = _SERVICE
148
-
149
- /**
150
- * ${pascalName} Reactor with ${useDisplayReactor ? "Display" : "Candid"} type transformations.
151
- * ${useDisplayReactor ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
152
- */
153
- export const ${camelName}Reactor = new ${reactorType}<${pascalName}Service>({
154
- clientManager,
155
- idlFactory,
156
- name: "${canisterName}",
157
- })
158
-
159
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
160
- // ACTOR & AUTH HOOKS
161
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
162
- export const {
163
- useActorQuery,
164
- useActorMutation,
165
- useActorSuspenseQuery,
166
- useActorInfiniteQuery,
167
- useActorSuspenseInfiniteQuery,
168
- useActorMethod,
169
- } = createActorHooks(${camelName}Reactor)
170
-
171
- export const use${pascalName}Query = useActorQuery
172
- export const use${pascalName}Mutation = useActorMutation
173
- export const use${pascalName}SuspenseQuery = useActorSuspenseQuery
174
- export const use${pascalName}InfiniteQuery = useActorInfiniteQuery
175
- export const use${pascalName}SuspenseInfiniteQuery = useActorSuspenseInfiniteQuery
176
- export const use${pascalName}Method = useActorMethod
177
-
178
- export const { useAuth, useAgentState, useUserPrincipal } = createAuthHooks(
179
- clientManager
180
- )
181
-
182
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
183
- // METHOD HOOKS
184
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
185
- ${hooks.join("")}
186
-
187
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
188
- // RE-EXPORTS
189
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
190
- export { idlFactory }
191
- export type { ${pascalName}Service }
192
- `;
57
+ function addOrReplaceSetCookie(existing, cookie) {
58
+ const cookieEntries = typeof existing === "string" ? [existing] : Array.isArray(existing) ? existing.filter((value) => typeof value === "string") : [];
59
+ const nonIcEnvCookies = cookieEntries.filter(
60
+ (entry) => !entry.trim().startsWith("ic_env=")
61
+ );
62
+ return [...nonIcEnvCookies, cookie];
193
63
  }
194
- function icReactorAdvancedPlugin(options) {
195
- const baseOutDir = options.outDir ?? "./src/canisters";
196
- return {
197
- name: "ic-reactor-advanced-plugin",
198
- async buildStart() {
199
- for (const canister of options.canisters) {
200
- const outDir = canister.outDir ?? import_path.default.join(baseOutDir, canister.name);
201
- if (!import_fs.default.existsSync(outDir)) {
202
- import_fs.default.mkdirSync(outDir, { recursive: true });
203
- }
204
- console.log(
205
- `[ic-reactor] Generating advanced hooks for ${canister.name} from ${canister.didFile}`
206
- );
207
- try {
208
- await (0, import_core.generate)({
209
- didFile: canister.didFile,
210
- outDir,
211
- output: {
212
- actor: {
213
- disabled: true
214
- },
215
- force: true
216
- }
217
- });
218
- console.log(
219
- `[ic-reactor] Declarations generated at ${import_path.default.join(
220
- outDir,
221
- "declarations"
222
- )}`
223
- );
224
- } catch (error) {
225
- console.error(`[ic-reactor] Failed to generate declarations:`, error);
226
- continue;
227
- }
228
- const clientManagerPath = canister.clientManagerPath ?? options.clientManagerPath ?? "../../lib/client";
229
- let didContent = "";
230
- try {
231
- didContent = import_fs.default.readFileSync(canister.didFile, "utf-8");
232
- } catch (e) {
233
- console.warn(
234
- `[ic-reactor] Could not read DID file at ${canister.didFile}, skipping hook generation.`
235
- );
236
- continue;
237
- }
238
- const reactorContent = generateAdvancedReactorFile(
239
- canister.name,
240
- canister.useDisplayReactor ?? true,
241
- clientManagerPath,
242
- didContent
243
- );
244
- const reactorPath = import_path.default.join(outDir, "index.ts");
245
- import_fs.default.writeFileSync(reactorPath, reactorContent);
246
- console.log(
247
- `[ic-reactor] Advanced reactor hooks generated at ${reactorPath}`
64
+ function setupIcEnvMiddleware(server) {
65
+ const rootDir = server.config.root || process.cwd();
66
+ const idsPath = import_path.default.resolve(rootDir, ICP_LOCAL_IDS_PATH);
67
+ let hasLoggedHint = false;
68
+ server.middlewares.use((req, res, next) => {
69
+ const canisterIds = loadLocalCanisterIds(rootDir);
70
+ if (!canisterIds) {
71
+ if (!hasLoggedHint) {
72
+ server.config.logger.info(
73
+ `[ic-reactor] icp-cli local IDs not found at ${idsPath}. Run \`icp deploy\` to enable automatic ic_env cookie injection.`
248
74
  );
75
+ hasLoggedHint = true;
249
76
  }
250
- },
251
- handleHotUpdate({ file, server }) {
252
- if (file.endsWith(".did")) {
253
- const canister = options.canisters.find(
254
- (c) => import_path.default.resolve(c.didFile) === file
255
- );
256
- if (canister) {
257
- console.log(
258
- `[ic-reactor] Detected change in ${file}, regenerating...`
259
- );
260
- server.restart();
261
- }
262
- }
77
+ return next();
263
78
  }
264
- };
265
- }
266
-
267
- // src/simple.ts
268
- function toPascalCase2(str) {
269
- return str.split(/[-_]/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
270
- }
271
- function toCamelCase2(str) {
272
- const pascal = toPascalCase2(str);
273
- return pascal.charAt(0).toLowerCase() + pascal.slice(1);
274
- }
275
- function generateReactorFile(canisterName, useDisplayReactor, clientManagerPath) {
276
- const pascalName = toPascalCase2(canisterName);
277
- const camelName = toCamelCase2(canisterName);
278
- const reactorType = useDisplayReactor ? "DisplayReactor" : "Reactor";
279
- return `/**
280
- * AUTO-GENERATED BY @ic-reactor/vite-plugin
281
- *
282
- * Canister: ${canisterName}
283
- * Generated: ${(/* @__PURE__ */ new Date()).toISOString()}
284
- *
285
- * This file provides type-safe React hooks for interacting with the
286
- * ${canisterName} canister using ic-reactor.
287
- */
288
-
289
- import {
290
- ${reactorType},
291
- createActorHooks,
292
- } from "@ic-reactor/react"
293
-
294
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
295
- // USER-PROVIDED CLIENT MANAGER
296
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
297
- // The clientManager is imported from the user's own configuration file.
298
- // This allows full customization of agent options, network settings, etc.
299
- import { clientManager } from "${clientManagerPath}"
300
-
301
- // Import generated declarations from @icp-sdk/bindgen
302
- import {
303
- idlFactory,
304
- type _SERVICE,
305
- } from "./declarations/${canisterName}.did"
306
-
307
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
308
- // REACTOR INSTANCE
309
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
310
-
311
- /**
312
- * ${pascalName} Reactor with ${useDisplayReactor ? "Display" : "Candid"} type transformations.
313
- * ${useDisplayReactor ? "Automatically converts bigint \u2192 string, Principal \u2192 string, etc." : "Uses raw Candid types."}
314
- */
315
- export const ${camelName}Reactor = new ${reactorType}<_SERVICE>({
316
- clientManager,
317
- idlFactory,
318
- name: "${canisterName}",
319
- })
320
-
321
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
322
- // HOOKS
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
-
325
- /**
326
- * React hooks for the ${canisterName} canister.
327
- */
328
- const {
329
- useActorQuery: use${pascalName}Query,
330
- useActorSuspenseQuery: use${pascalName}SuspenseQuery,
331
- useActorInfiniteQuery: use${pascalName}InfiniteQuery,
332
- useActorSuspenseInfiniteQuery: use${pascalName}SuspenseInfiniteQuery,
333
- useActorMutation: use${pascalName}Mutation,
334
- useActorMethod: use${pascalName}Method,
335
- } = createActorHooks(${camelName}Reactor)
336
-
337
- export {
338
- use${pascalName}Query,
339
- use${pascalName}SuspenseQuery,
340
- use${pascalName}InfiniteQuery,
341
- use${pascalName}SuspenseInfiniteQuery,
342
- use${pascalName}Mutation,
343
- use${pascalName}Method,
344
- }
345
-
346
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
347
- // RE-EXPORTS
348
- // \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550
349
-
350
- export { idlFactory }
351
- export type { _SERVICE as ${pascalName}Service }
352
- `;
79
+ const cookie = `ic_env=${buildIcEnvCookie(canisterIds)}; Path=/; SameSite=Lax;`;
80
+ const current = res.getHeader("Set-Cookie");
81
+ res.setHeader("Set-Cookie", addOrReplaceSetCookie(current, cookie));
82
+ next();
83
+ });
353
84
  }
354
85
  function icReactorPlugin(options) {
355
86
  const baseOutDir = options.outDir ?? "./src/canisters";
356
87
  return {
357
88
  name: "ic-reactor-plugin",
89
+ configureServer(server) {
90
+ if (options.autoInjectIcEnv ?? true) {
91
+ setupIcEnvMiddleware(server);
92
+ }
93
+ },
358
94
  async buildStart() {
359
95
  for (const canister of options.canisters) {
360
- const outDir = canister.outDir ?? import_path2.default.join(baseOutDir, canister.name);
361
- const declarationsDir = import_path2.default.join(outDir, "declarations");
96
+ const outDir = canister.outDir ?? import_path.default.join(baseOutDir, canister.name);
362
97
  console.log(
363
98
  `[ic-reactor] Generating hooks for ${canister.name} from ${canister.didFile}`
364
99
  );
365
- if (import_fs2.default.existsSync(declarationsDir)) {
366
- import_fs2.default.rmSync(declarationsDir, { recursive: true, force: true });
367
- }
368
- import_fs2.default.mkdirSync(declarationsDir, { recursive: true });
369
- try {
370
- if (!import_fs2.default.existsSync(outDir)) {
371
- import_fs2.default.mkdirSync(outDir, { recursive: true });
372
- }
373
- await (0, import_core2.generate)({
374
- didFile: canister.didFile,
375
- outDir,
376
- // Pass the parent directory; bindgen appends "declarations"
377
- output: {
378
- actor: {
379
- disabled: true
380
- },
381
- force: true
382
- }
383
- });
384
- console.log(
385
- `[ic-reactor] Declarations generated at ${declarationsDir}`
100
+ const result = await (0, import_codegen.generateDeclarations)({
101
+ didFile: canister.didFile,
102
+ outDir,
103
+ canisterName: canister.name
104
+ });
105
+ if (!result.success) {
106
+ console.error(
107
+ `[ic-reactor] Failed to generate declarations: ${result.error}`
386
108
  );
387
- } catch (error) {
388
- console.error(`[ic-reactor] Failed to generate declarations:`, error);
389
109
  continue;
390
110
  }
391
- const clientManagerPath = canister.clientManagerPath ?? options.clientManagerPath ?? "../../lib/client";
392
- const reactorContent = generateReactorFile(
393
- canister.name,
394
- canister.useDisplayReactor ?? true,
395
- clientManagerPath
111
+ console.log(
112
+ `[ic-reactor] Declarations generated at ${result.declarationsDir}`
396
113
  );
397
- const reactorPath = import_path2.default.join(outDir, "index.ts");
398
- import_fs2.default.mkdirSync(outDir, { recursive: true });
399
- import_fs2.default.writeFileSync(reactorPath, reactorContent);
114
+ const useAdvanced = canister.advanced ?? options.advanced ?? false;
115
+ let didContent;
116
+ if (useAdvanced) {
117
+ try {
118
+ didContent = import_fs.default.readFileSync(canister.didFile, "utf-8");
119
+ } catch (e) {
120
+ console.warn(
121
+ `[ic-reactor] Could not read DID file at ${canister.didFile}, skipping advanced hook generation for ${canister.name}.`
122
+ );
123
+ continue;
124
+ }
125
+ }
126
+ const reactorContent = (0, import_codegen.generateReactorFile)({
127
+ canisterName: canister.name,
128
+ canisterConfig: canister,
129
+ globalClientManagerPath: options.clientManagerPath,
130
+ hasDeclarations: true,
131
+ advanced: useAdvanced,
132
+ didContent
133
+ });
134
+ const reactorPath = import_path.default.join(outDir, "index.ts");
135
+ import_fs.default.mkdirSync(outDir, { recursive: true });
136
+ import_fs.default.writeFileSync(reactorPath, reactorContent);
400
137
  console.log(`[ic-reactor] Reactor hooks generated at ${reactorPath}`);
401
138
  }
402
139
  },
403
140
  handleHotUpdate({ file, server }) {
404
141
  if (file.endsWith(".did")) {
405
142
  const canister = options.canisters.find(
406
- (c) => import_path2.default.resolve(c.didFile) === file
143
+ (c) => import_path.default.resolve(c.didFile) === file
407
144
  );
408
145
  if (canister) {
409
146
  console.log(
@@ -415,8 +152,8 @@ function icReactorPlugin(options) {
415
152
  }
416
153
  };
417
154
  }
155
+ var index_default = icReactorPlugin;
418
156
  // Annotate the CommonJS export names for ESM import in node:
419
157
  0 && (module.exports = {
420
- icReactorAdvancedPlugin,
421
158
  icReactorPlugin
422
159
  });
package/dist/index.d.cts CHANGED
@@ -1,18 +1,12 @@
1
1
  import { Plugin } from 'vite';
2
-
3
- declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugin;
2
+ import { CanisterConfig } from '@ic-reactor/codegen';
3
+ export { CanisterConfig } from '@ic-reactor/codegen';
4
4
 
5
5
  /**
6
6
  * IC-Reactor Vite Plugin
7
7
  *
8
8
  * A Vite plugin that generates ic-reactor hooks from Candid .did files.
9
- *
10
- * ⚠️ IMPORTANT: This plugin ONLY generates the reactor and hooks.
11
- * The user is responsible for creating and configuring:
12
- * - ClientManager
13
- * - QueryClient
14
- *
15
- * The generated file will import the clientManager from a user-specified path.
9
+ * Uses @ic-reactor/codegen for all code generation logic.
16
10
  *
17
11
  * Usage:
18
12
  * ```ts
@@ -25,7 +19,7 @@ declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugi
25
19
  * {
26
20
  * name: "backend",
27
21
  * didFile: "../backend/backend.did",
28
- * clientManagerPath: "../lib/client" // User provides their own ClientManager
22
+ * clientManagerPath: "../lib/client"
29
23
  * }
30
24
  * ]
31
25
  * })
@@ -34,34 +28,32 @@ declare function icReactorAdvancedPlugin(options: IcReactorPluginOptions): Plugi
34
28
  * ```
35
29
  */
36
30
 
37
- interface CanisterConfig {
38
- /** Name of the canister (used for variable naming) */
39
- name: string;
40
- /** Path to the .did file */
41
- didFile: string;
42
- /** Output directory (default: ./src/canisters/<name>) */
43
- outDir?: string;
44
- /** Use DisplayReactor for React-friendly types (default: true) */
45
- useDisplayReactor?: boolean;
46
- /**
47
- * Path to import ClientManager from (relative to generated file).
48
- * The file at this path should export: { clientManager: ClientManager }
49
- * Default: "../../lib/client"
50
- */
51
- clientManagerPath?: string;
52
- }
53
31
  interface IcReactorPluginOptions {
54
32
  /** List of canisters to generate hooks for */
55
- canisters: CanisterConfig[];
33
+ canisters: (CanisterConfig & {
34
+ name: string;
35
+ advanced?: boolean;
36
+ })[];
56
37
  /** Base output directory (default: ./src/canisters) */
57
38
  outDir?: string;
58
39
  /**
59
40
  * Path to import ClientManager from (relative to generated file).
60
- * The file at this path should export: { clientManager: ClientManager }
61
41
  * Default: "../../lib/client"
62
42
  */
63
43
  clientManagerPath?: string;
44
+ /**
45
+ * Generate advanced per-method hooks with createQuery/createMutation
46
+ * instead of generic actor hooks (default: false).
47
+ * Can be overridden per-canister by setting `advanced` on the individual canister entry.
48
+ */
49
+ advanced?: boolean;
50
+ /**
51
+ * Automatically set the `ic_env` cookie in Vite dev server from
52
+ * `.icp/cache/mappings/local.ids.json` (default: true).
53
+ */
54
+ autoInjectIcEnv?: boolean;
64
55
  }
56
+
65
57
  declare function icReactorPlugin(options: IcReactorPluginOptions): Plugin;
66
58
 
67
- export { type CanisterConfig, type IcReactorPluginOptions, icReactorAdvancedPlugin, icReactorPlugin };
59
+ export { type IcReactorPluginOptions, icReactorPlugin as default, icReactorPlugin };