@ic-reactor/codegen 0.6.0 → 0.7.1

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
@@ -23,75 +23,16 @@ await runCanisterPipeline({
23
23
  globalConfig: {
24
24
  outDir: "src/declarations",
25
25
  clientManagerPath: "../../clients",
26
- reactor: {
27
- defaultMode: "display",
28
- canisters: {
29
- workflow_engine: "raw",
30
- },
31
- },
32
26
  },
33
27
  })
34
28
  ```
35
29
 
36
- ## Reactor Mode Configuration
37
-
38
- `reactor.defaultMode` controls whether generated default hook exports use:
39
-
40
- - `display` → `DisplayReactor` (current/default behavior)
41
- - `raw` → `Reactor` (raw Candid shapes)
42
-
43
- `reactor.canisters` lets you override specific canisters without editing generated files.
44
-
45
- ## Generated File Layout (Stable Wrapper)
46
-
47
- Each canister now generates:
48
-
49
- - `index.generated.ts` (regenerated on every run)
50
- - `index.ts` (created only if missing; not overwritten)
51
-
52
- The stable wrapper defaults to:
53
-
54
- ```ts
55
- export * from "./index.generated"
56
- ```
57
-
58
- This lets applications customize exports once and keep them across regenerations.
59
-
60
- ## Generated Output Examples
61
-
62
- Display default:
63
-
64
- ```ts
65
- export function createBackendRawReactor() {
66
- /* ... */
67
- }
68
- export function createBackendDisplayReactor() {
69
- /* ... */
70
- }
71
- export const backendReactor = createBackendDisplayReactor()
72
- export const BackendReactorMode = "display" as const
73
- ```
74
-
75
- Raw default:
76
-
77
- ```ts
78
- export function createWorkflowEngineRawReactor() {
79
- /* ... */
80
- }
81
- export function createWorkflowEngineDisplayReactor() {
82
- /* ... */
83
- }
84
- export const workflowEngineReactor = createWorkflowEngineRawReactor()
85
- export const WorkflowEngineReactorMode = "raw" as const
86
- ```
87
-
88
30
  ## Generators
89
31
 
90
32
  You can also use individual generators if you need more granular control:
91
33
 
92
34
  - **`generateDeclarations`**: Generates `.js` (factory), `.d.ts` (types), and `.did` copy.
93
- - **`generateReactorFile`**: Generates the `index.generated.ts` implementation with raw/display factories and typed hooks.
94
- - **`generateReactorWrapperFile`**: Generates the stable `index.ts` wrapper (create-once, preserve-on-regenerate).
35
+ - **`generateReactorFile`**: Generates the `index.ts` file with `DisplayReactor` and hooks.
95
36
  - **`generateClientFile`**: Generates a `ClientManager` boilerplate file.
96
37
 
97
38
  ## Utilities
package/dist/index.cjs CHANGED
@@ -35,7 +35,6 @@ __export(index_exports, {
35
35
  generateClientFile: () => generateClientFile,
36
36
  generateDeclarations: () => generateDeclarations,
37
37
  generateReactorFile: () => generateReactorFile,
38
- generateReactorWrapperFile: () => generateReactorWrapperFile,
39
38
  getReactorName: () => getReactorName,
40
39
  getServiceTypeName: () => getServiceTypeName,
41
40
  parseDIDFile: () => parseDIDFile,
@@ -125,21 +124,13 @@ function getServiceTypeName(canisterName) {
125
124
 
126
125
  // src/generators/reactor.ts
127
126
  function generateReactorFile(options) {
128
- const {
129
- canisterName,
130
- didFile,
131
- clientManagerPath = "../../clients",
132
- reactorMode = "display"
133
- } = options;
127
+ const { canisterName, didFile, clientManagerPath = "../../clients" } = options;
134
128
  const pascalName = toPascalCase(canisterName);
135
129
  const reactorName = getReactorName(canisterName);
136
130
  const serviceName = getServiceTypeName(canisterName);
137
- const rawFactoryName = `create${pascalName}RawReactor`;
138
- const displayFactoryName = `create${pascalName}DisplayReactor`;
139
- const defaultFactoryName = reactorMode === "raw" ? rawFactoryName : displayFactoryName;
140
131
  const baseName = import_node_path2.default.basename(didFile, ".did");
141
132
  const declarationsPath = `./declarations/${baseName}`;
142
- return `import { DisplayReactor, Reactor, createActorHooks } from "@ic-reactor/react"
133
+ return `import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
143
134
  import { clientManager } from "${clientManagerPath}"
144
135
  import { idlFactory, type _SERVICE } from "${declarationsPath}"
145
136
 
@@ -151,25 +142,11 @@ export type ${serviceName} = _SERVICE
151
142
  * Auto-generated by @ic-reactor/codegen \u2014 do not edit.
152
143
  * Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
153
144
  */
154
- export function ${rawFactoryName}() {
155
- return new Reactor<${serviceName}>({
156
- clientManager,
157
- idlFactory,
158
- name: "${canisterName}",
159
- })
160
- }
161
-
162
- export function ${displayFactoryName}() {
163
- return new DisplayReactor<${serviceName}>({
164
- clientManager,
165
- idlFactory,
166
- name: "${canisterName}",
167
- })
168
- }
169
-
170
- export const ${reactorName} = ${defaultFactoryName}()
171
-
172
- export const ${pascalName}ReactorMode = "${reactorMode}" as const
145
+ export const ${reactorName} = new DisplayReactor<${serviceName}>({
146
+ clientManager,
147
+ idlFactory,
148
+ name: "${canisterName}",
149
+ })
173
150
 
174
151
  export const {
175
152
  useActorQuery: use${pascalName}Query,
@@ -181,29 +158,8 @@ export const {
181
158
  } = createActorHooks(${reactorName})
182
159
  `;
183
160
  }
184
- function generateReactorWrapperFile(options) {
185
- const { canisterName } = options;
186
- const pascalName = toPascalCase(canisterName);
187
- return `/**
188
- * ${pascalName} canister exports
189
- *
190
- * This wrapper is created once by @ic-reactor/codegen and is not overwritten.
191
- * Customize it if you need to swap reactor implementations or compose custom exports.
192
- */
193
- export * from "./index.generated"
194
- `;
195
- }
196
161
 
197
162
  // src/pipeline.ts
198
- function resolveReactorMode(canisterName, globalConfig) {
199
- return globalConfig.reactor?.canisters?.[canisterName] ?? globalConfig.reactor?.defaultMode ?? "display";
200
- }
201
- function isStableWrapperFile(content) {
202
- return content.includes('export * from "./index.generated"');
203
- }
204
- function isLegacyGeneratedIndexFile(content) {
205
- return content.includes("Auto-generated by @ic-reactor/codegen") && content.includes("createActorHooks(") && !isStableWrapperFile(content);
206
- }
207
163
  async function runCanisterPipeline(options) {
208
164
  const { canisterConfig, projectRoot, globalConfig } = options;
209
165
  const { name, didFile, clientManagerPath } = canisterConfig;
@@ -242,41 +198,20 @@ async function runCanisterPipeline(options) {
242
198
  error: `Declarations step failed: ${err instanceof Error ? err.message : String(err)}`
243
199
  };
244
200
  }
245
- const generatedReactorPath = import_node_path3.default.join(canisterOutDir, "index.generated.ts");
246
- const wrapperPath = import_node_path3.default.join(canisterOutDir, "index.ts");
247
- const reactorMode = resolveReactorMode(name, globalConfig);
201
+ const reactorPath = import_node_path3.default.join(canisterOutDir, "index.ts");
248
202
  try {
249
203
  const reactorContent = generateReactorFile({
250
204
  canisterName: name,
251
205
  didFile: resolvedDidFile,
252
- clientManagerPath: resolvedClientManagerPath,
253
- reactorMode
254
- });
255
- const wrapperContent = generateReactorWrapperFile({
256
- canisterName: name,
257
- didFile: resolvedDidFile,
258
- clientManagerPath: resolvedClientManagerPath,
259
- reactorMode
206
+ clientManagerPath: resolvedClientManagerPath
260
207
  });
261
208
  import_node_fs2.default.mkdirSync(canisterOutDir, { recursive: true });
262
- import_node_fs2.default.writeFileSync(generatedReactorPath, reactorContent);
263
- files.push({ success: true, filePath: generatedReactorPath });
264
- if (!import_node_fs2.default.existsSync(wrapperPath)) {
265
- import_node_fs2.default.writeFileSync(wrapperPath, wrapperContent);
266
- files.push({ success: true, filePath: wrapperPath });
267
- } else {
268
- const existingWrapperContent = import_node_fs2.default.readFileSync(wrapperPath, "utf-8");
269
- if (isLegacyGeneratedIndexFile(existingWrapperContent)) {
270
- import_node_fs2.default.writeFileSync(wrapperPath, wrapperContent);
271
- files.push({ success: true, filePath: wrapperPath });
272
- } else {
273
- files.push({ success: true, filePath: wrapperPath, skipped: true });
274
- }
275
- }
209
+ import_node_fs2.default.writeFileSync(reactorPath, reactorContent);
210
+ files.push({ success: true, filePath: reactorPath });
276
211
  } catch (err) {
277
212
  files.push({
278
213
  success: false,
279
- filePath: generatedReactorPath,
214
+ filePath: reactorPath,
280
215
  error: `Reactor generation failed: ${err instanceof Error ? err.message : String(err)}`
281
216
  });
282
217
  return {
@@ -355,7 +290,6 @@ export const clientManager = new ClientManager({
355
290
  generateClientFile,
356
291
  generateDeclarations,
357
292
  generateReactorFile,
358
- generateReactorWrapperFile,
359
293
  getReactorName,
360
294
  getServiceTypeName,
361
295
  parseDIDFile,
package/dist/index.d.cts CHANGED
@@ -22,19 +22,6 @@ interface CanisterConfig {
22
22
  /** Optional fixed canister ID */
23
23
  canisterId?: string;
24
24
  }
25
- type ReactorMode = "raw" | "display";
26
- interface ReactorGenerationConfig {
27
- /**
28
- * Default reactor mode used for generated hook exports.
29
- * `display` preserves current behavior.
30
- */
31
- defaultMode?: ReactorMode;
32
- /**
33
- * Optional per-canister overrides keyed by canister name.
34
- * Example: { workflow_engine: "raw" }
35
- */
36
- canisters?: Record<string, ReactorMode>;
37
- }
38
25
  /**
39
26
  * Top-level codegen / CLI configuration (stored in `ic-reactor.json`).
40
27
  */
@@ -51,8 +38,6 @@ interface CodegenConfig {
51
38
  * Individual canisters can override via `CanisterConfig.clientManagerPath`.
52
39
  */
53
40
  clientManagerPath?: string;
54
- /** Optional reactor mode generation settings */
55
- reactor?: ReactorGenerationConfig;
56
41
  /** Canister configurations, keyed by canister name */
57
42
  canisters: Record<string, CanisterConfig>;
58
43
  }
@@ -77,7 +62,7 @@ interface GeneratorResult {
77
62
  * Pipeline steps (in order):
78
63
  * 1. Resolve paths (didFile, outDir)
79
64
  * 2. Generate declarations (JS + .d.ts + .did copy)
80
- * 3. Generate reactor implementation + stable wrapper (`index.generated.ts` + `index.ts`)
65
+ * 3. Generate reactor file (index.ts)
81
66
  */
82
67
 
83
68
  interface PipelineOptions {
@@ -91,7 +76,7 @@ interface PipelineOptions {
91
76
  /**
92
77
  * Global codegen config (for fallback outDir and clientManagerPath).
93
78
  */
94
- globalConfig: Pick<CodegenConfig, "outDir" | "clientManagerPath" | "reactor">;
79
+ globalConfig: Pick<CodegenConfig, "outDir" | "clientManagerPath">;
95
80
  }
96
81
  interface PipelineResult {
97
82
  canisterName: string;
@@ -219,7 +204,6 @@ declare function declarationsExist(outDir: string, canisterName: string): boolea
219
204
  * ...
220
205
  * } = createActorHooks(backendReactor)
221
206
  */
222
-
223
207
  interface ReactorGeneratorOptions {
224
208
  /** Canister name (e.g. "backend") */
225
209
  canisterName: string;
@@ -233,21 +217,11 @@ interface ReactorGeneratorOptions {
233
217
  * Default: "../../clients"
234
218
  */
235
219
  clientManagerPath?: string;
236
- /**
237
- * Which reactor implementation should back the default exported hooks.
238
- * Default: "display" (backward compatible)
239
- */
240
- reactorMode?: ReactorMode;
241
220
  }
242
221
  /**
243
- * Generate the content of a canister's `index.generated.ts` implementation file.
222
+ * Generate the content of a canister's `index.ts` reactor file.
244
223
  */
245
224
  declare function generateReactorFile(options: ReactorGeneratorOptions): string;
246
- /**
247
- * Generate the stable user wrapper `index.ts` for a canister.
248
- * This file is intended to be created once and preserved across regenerations.
249
- */
250
- declare function generateReactorWrapperFile(options: ReactorGeneratorOptions): string;
251
225
 
252
226
  /**
253
227
  * Client Manager Generator
@@ -272,4 +246,4 @@ interface ClientGeneratorOptions {
272
246
  */
273
247
  declare function generateClientFile(options?: ClientGeneratorOptions): string;
274
248
 
275
- export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type ReactorGenerationConfig, type ReactorGeneratorOptions, type ReactorMode, declarationsExist, extractMethods, generateClientFile, generateDeclarations, generateReactorFile, generateReactorWrapperFile, getReactorName, getServiceTypeName, parseDIDFile, runCanisterPipeline, toPascalCase };
249
+ export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type ReactorGeneratorOptions, declarationsExist, extractMethods, generateClientFile, generateDeclarations, generateReactorFile, getReactorName, getServiceTypeName, parseDIDFile, runCanisterPipeline, toPascalCase };
package/dist/index.d.ts CHANGED
@@ -22,19 +22,6 @@ interface CanisterConfig {
22
22
  /** Optional fixed canister ID */
23
23
  canisterId?: string;
24
24
  }
25
- type ReactorMode = "raw" | "display";
26
- interface ReactorGenerationConfig {
27
- /**
28
- * Default reactor mode used for generated hook exports.
29
- * `display` preserves current behavior.
30
- */
31
- defaultMode?: ReactorMode;
32
- /**
33
- * Optional per-canister overrides keyed by canister name.
34
- * Example: { workflow_engine: "raw" }
35
- */
36
- canisters?: Record<string, ReactorMode>;
37
- }
38
25
  /**
39
26
  * Top-level codegen / CLI configuration (stored in `ic-reactor.json`).
40
27
  */
@@ -51,8 +38,6 @@ interface CodegenConfig {
51
38
  * Individual canisters can override via `CanisterConfig.clientManagerPath`.
52
39
  */
53
40
  clientManagerPath?: string;
54
- /** Optional reactor mode generation settings */
55
- reactor?: ReactorGenerationConfig;
56
41
  /** Canister configurations, keyed by canister name */
57
42
  canisters: Record<string, CanisterConfig>;
58
43
  }
@@ -77,7 +62,7 @@ interface GeneratorResult {
77
62
  * Pipeline steps (in order):
78
63
  * 1. Resolve paths (didFile, outDir)
79
64
  * 2. Generate declarations (JS + .d.ts + .did copy)
80
- * 3. Generate reactor implementation + stable wrapper (`index.generated.ts` + `index.ts`)
65
+ * 3. Generate reactor file (index.ts)
81
66
  */
82
67
 
83
68
  interface PipelineOptions {
@@ -91,7 +76,7 @@ interface PipelineOptions {
91
76
  /**
92
77
  * Global codegen config (for fallback outDir and clientManagerPath).
93
78
  */
94
- globalConfig: Pick<CodegenConfig, "outDir" | "clientManagerPath" | "reactor">;
79
+ globalConfig: Pick<CodegenConfig, "outDir" | "clientManagerPath">;
95
80
  }
96
81
  interface PipelineResult {
97
82
  canisterName: string;
@@ -219,7 +204,6 @@ declare function declarationsExist(outDir: string, canisterName: string): boolea
219
204
  * ...
220
205
  * } = createActorHooks(backendReactor)
221
206
  */
222
-
223
207
  interface ReactorGeneratorOptions {
224
208
  /** Canister name (e.g. "backend") */
225
209
  canisterName: string;
@@ -233,21 +217,11 @@ interface ReactorGeneratorOptions {
233
217
  * Default: "../../clients"
234
218
  */
235
219
  clientManagerPath?: string;
236
- /**
237
- * Which reactor implementation should back the default exported hooks.
238
- * Default: "display" (backward compatible)
239
- */
240
- reactorMode?: ReactorMode;
241
220
  }
242
221
  /**
243
- * Generate the content of a canister's `index.generated.ts` implementation file.
222
+ * Generate the content of a canister's `index.ts` reactor file.
244
223
  */
245
224
  declare function generateReactorFile(options: ReactorGeneratorOptions): string;
246
- /**
247
- * Generate the stable user wrapper `index.ts` for a canister.
248
- * This file is intended to be created once and preserved across regenerations.
249
- */
250
- declare function generateReactorWrapperFile(options: ReactorGeneratorOptions): string;
251
225
 
252
226
  /**
253
227
  * Client Manager Generator
@@ -272,4 +246,4 @@ interface ClientGeneratorOptions {
272
246
  */
273
247
  declare function generateClientFile(options?: ClientGeneratorOptions): string;
274
248
 
275
- export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type ReactorGenerationConfig, type ReactorGeneratorOptions, type ReactorMode, declarationsExist, extractMethods, generateClientFile, generateDeclarations, generateReactorFile, generateReactorWrapperFile, getReactorName, getServiceTypeName, parseDIDFile, runCanisterPipeline, toPascalCase };
249
+ export { type CanisterConfig, type ClientGeneratorOptions, type CodegenConfig, type DeclarationsGeneratorOptions, type DeclarationsGeneratorResult, type GeneratorResult, type MethodInfo, type MethodType, type PipelineOptions, type PipelineResult, type ReactorGeneratorOptions, declarationsExist, extractMethods, generateClientFile, generateDeclarations, generateReactorFile, getReactorName, getServiceTypeName, parseDIDFile, runCanisterPipeline, toPascalCase };
package/dist/index.js CHANGED
@@ -79,21 +79,13 @@ function getServiceTypeName(canisterName) {
79
79
 
80
80
  // src/generators/reactor.ts
81
81
  function generateReactorFile(options) {
82
- const {
83
- canisterName,
84
- didFile,
85
- clientManagerPath = "../../clients",
86
- reactorMode = "display"
87
- } = options;
82
+ const { canisterName, didFile, clientManagerPath = "../../clients" } = options;
88
83
  const pascalName = toPascalCase(canisterName);
89
84
  const reactorName = getReactorName(canisterName);
90
85
  const serviceName = getServiceTypeName(canisterName);
91
- const rawFactoryName = `create${pascalName}RawReactor`;
92
- const displayFactoryName = `create${pascalName}DisplayReactor`;
93
- const defaultFactoryName = reactorMode === "raw" ? rawFactoryName : displayFactoryName;
94
86
  const baseName = path2.basename(didFile, ".did");
95
87
  const declarationsPath = `./declarations/${baseName}`;
96
- return `import { DisplayReactor, Reactor, createActorHooks } from "@ic-reactor/react"
88
+ return `import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
97
89
  import { clientManager } from "${clientManagerPath}"
98
90
  import { idlFactory, type _SERVICE } from "${declarationsPath}"
99
91
 
@@ -105,25 +97,11 @@ export type ${serviceName} = _SERVICE
105
97
  * Auto-generated by @ic-reactor/codegen \u2014 do not edit.
106
98
  * Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
107
99
  */
108
- export function ${rawFactoryName}() {
109
- return new Reactor<${serviceName}>({
110
- clientManager,
111
- idlFactory,
112
- name: "${canisterName}",
113
- })
114
- }
115
-
116
- export function ${displayFactoryName}() {
117
- return new DisplayReactor<${serviceName}>({
118
- clientManager,
119
- idlFactory,
120
- name: "${canisterName}",
121
- })
122
- }
123
-
124
- export const ${reactorName} = ${defaultFactoryName}()
125
-
126
- export const ${pascalName}ReactorMode = "${reactorMode}" as const
100
+ export const ${reactorName} = new DisplayReactor<${serviceName}>({
101
+ clientManager,
102
+ idlFactory,
103
+ name: "${canisterName}",
104
+ })
127
105
 
128
106
  export const {
129
107
  useActorQuery: use${pascalName}Query,
@@ -135,29 +113,8 @@ export const {
135
113
  } = createActorHooks(${reactorName})
136
114
  `;
137
115
  }
138
- function generateReactorWrapperFile(options) {
139
- const { canisterName } = options;
140
- const pascalName = toPascalCase(canisterName);
141
- return `/**
142
- * ${pascalName} canister exports
143
- *
144
- * This wrapper is created once by @ic-reactor/codegen and is not overwritten.
145
- * Customize it if you need to swap reactor implementations or compose custom exports.
146
- */
147
- export * from "./index.generated"
148
- `;
149
- }
150
116
 
151
117
  // src/pipeline.ts
152
- function resolveReactorMode(canisterName, globalConfig) {
153
- return globalConfig.reactor?.canisters?.[canisterName] ?? globalConfig.reactor?.defaultMode ?? "display";
154
- }
155
- function isStableWrapperFile(content) {
156
- return content.includes('export * from "./index.generated"');
157
- }
158
- function isLegacyGeneratedIndexFile(content) {
159
- return content.includes("Auto-generated by @ic-reactor/codegen") && content.includes("createActorHooks(") && !isStableWrapperFile(content);
160
- }
161
118
  async function runCanisterPipeline(options) {
162
119
  const { canisterConfig, projectRoot, globalConfig } = options;
163
120
  const { name, didFile, clientManagerPath } = canisterConfig;
@@ -196,41 +153,20 @@ async function runCanisterPipeline(options) {
196
153
  error: `Declarations step failed: ${err instanceof Error ? err.message : String(err)}`
197
154
  };
198
155
  }
199
- const generatedReactorPath = path3.join(canisterOutDir, "index.generated.ts");
200
- const wrapperPath = path3.join(canisterOutDir, "index.ts");
201
- const reactorMode = resolveReactorMode(name, globalConfig);
156
+ const reactorPath = path3.join(canisterOutDir, "index.ts");
202
157
  try {
203
158
  const reactorContent = generateReactorFile({
204
159
  canisterName: name,
205
160
  didFile: resolvedDidFile,
206
- clientManagerPath: resolvedClientManagerPath,
207
- reactorMode
208
- });
209
- const wrapperContent = generateReactorWrapperFile({
210
- canisterName: name,
211
- didFile: resolvedDidFile,
212
- clientManagerPath: resolvedClientManagerPath,
213
- reactorMode
161
+ clientManagerPath: resolvedClientManagerPath
214
162
  });
215
163
  fs2.mkdirSync(canisterOutDir, { recursive: true });
216
- fs2.writeFileSync(generatedReactorPath, reactorContent);
217
- files.push({ success: true, filePath: generatedReactorPath });
218
- if (!fs2.existsSync(wrapperPath)) {
219
- fs2.writeFileSync(wrapperPath, wrapperContent);
220
- files.push({ success: true, filePath: wrapperPath });
221
- } else {
222
- const existingWrapperContent = fs2.readFileSync(wrapperPath, "utf-8");
223
- if (isLegacyGeneratedIndexFile(existingWrapperContent)) {
224
- fs2.writeFileSync(wrapperPath, wrapperContent);
225
- files.push({ success: true, filePath: wrapperPath });
226
- } else {
227
- files.push({ success: true, filePath: wrapperPath, skipped: true });
228
- }
229
- }
164
+ fs2.writeFileSync(reactorPath, reactorContent);
165
+ files.push({ success: true, filePath: reactorPath });
230
166
  } catch (err) {
231
167
  files.push({
232
168
  success: false,
233
- filePath: generatedReactorPath,
169
+ filePath: reactorPath,
234
170
  error: `Reactor generation failed: ${err instanceof Error ? err.message : String(err)}`
235
171
  });
236
172
  return {
@@ -308,7 +244,6 @@ export {
308
244
  generateClientFile,
309
245
  generateDeclarations,
310
246
  generateReactorFile,
311
- generateReactorWrapperFile,
312
247
  getReactorName,
313
248
  getServiceTypeName,
314
249
  parseDIDFile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ic-reactor/codegen",
3
- "version": "0.6.0",
3
+ "version": "0.7.1",
4
4
  "description": "Shared code generation utilities for IC Reactor",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -11,7 +11,7 @@ export type {
11
11
  DeclarationsGeneratorResult,
12
12
  } from "./declarations.js"
13
13
 
14
- export { generateReactorFile, generateReactorWrapperFile } from "./reactor.js"
14
+ export { generateReactorFile } from "./reactor.js"
15
15
  export type { ReactorGeneratorOptions } from "./reactor.js"
16
16
 
17
17
  export { generateClientFile } from "./client.js"
@@ -22,7 +22,6 @@
22
22
 
23
23
  import path from "node:path"
24
24
  import { toPascalCase, getReactorName, getServiceTypeName } from "../naming.js"
25
- import type { ReactorMode } from "../types.js"
26
25
 
27
26
  export interface ReactorGeneratorOptions {
28
27
  /** Canister name (e.g. "backend") */
@@ -37,37 +36,23 @@ export interface ReactorGeneratorOptions {
37
36
  * Default: "../../clients"
38
37
  */
39
38
  clientManagerPath?: string
40
- /**
41
- * Which reactor implementation should back the default exported hooks.
42
- * Default: "display" (backward compatible)
43
- */
44
- reactorMode?: ReactorMode
45
39
  }
46
40
 
47
41
  /**
48
- * Generate the content of a canister's `index.generated.ts` implementation file.
42
+ * Generate the content of a canister's `index.ts` reactor file.
49
43
  */
50
44
  export function generateReactorFile(options: ReactorGeneratorOptions): string {
51
- const {
52
- canisterName,
53
- didFile,
54
- clientManagerPath = "../../clients",
55
- reactorMode = "display",
56
- } = options
45
+ const { canisterName, didFile, clientManagerPath = "../../clients" } = options
57
46
 
58
47
  const pascalName = toPascalCase(canisterName)
59
48
  const reactorName = getReactorName(canisterName)
60
49
  const serviceName = getServiceTypeName(canisterName)
61
- const rawFactoryName = `create${pascalName}RawReactor`
62
- const displayFactoryName = `create${pascalName}DisplayReactor`
63
- const defaultFactoryName =
64
- reactorMode === "raw" ? rawFactoryName : displayFactoryName
65
50
 
66
51
  // Derive the declarations import path from the .did filename
67
52
  const baseName = path.basename(didFile, ".did")
68
53
  const declarationsPath = `./declarations/${baseName}`
69
54
 
70
- return `import { DisplayReactor, Reactor, createActorHooks } from "@ic-reactor/react"
55
+ return `import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
71
56
  import { clientManager } from "${clientManagerPath}"
72
57
  import { idlFactory, type _SERVICE } from "${declarationsPath}"
73
58
 
@@ -79,25 +64,11 @@ export type ${serviceName} = _SERVICE
79
64
  * Auto-generated by @ic-reactor/codegen — do not edit.
80
65
  * Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
81
66
  */
82
- export function ${rawFactoryName}() {
83
- return new Reactor<${serviceName}>({
84
- clientManager,
85
- idlFactory,
86
- name: "${canisterName}",
87
- })
88
- }
89
-
90
- export function ${displayFactoryName}() {
91
- return new DisplayReactor<${serviceName}>({
92
- clientManager,
93
- idlFactory,
94
- name: "${canisterName}",
95
- })
96
- }
97
-
98
- export const ${reactorName} = ${defaultFactoryName}()
99
-
100
- export const ${pascalName}ReactorMode = "${reactorMode}" as const
67
+ export const ${reactorName} = new DisplayReactor<${serviceName}>({
68
+ clientManager,
69
+ idlFactory,
70
+ name: "${canisterName}",
71
+ })
101
72
 
102
73
  export const {
103
74
  useActorQuery: use${pascalName}Query,
@@ -109,23 +80,3 @@ export const {
109
80
  } = createActorHooks(${reactorName})
110
81
  `
111
82
  }
112
-
113
- /**
114
- * Generate the stable user wrapper `index.ts` for a canister.
115
- * This file is intended to be created once and preserved across regenerations.
116
- */
117
- export function generateReactorWrapperFile(
118
- options: ReactorGeneratorOptions
119
- ): string {
120
- const { canisterName } = options
121
- const pascalName = toPascalCase(canisterName)
122
-
123
- return `/**
124
- * ${pascalName} canister exports
125
- *
126
- * This wrapper is created once by @ic-reactor/codegen and is not overwritten.
127
- * Customize it if you need to swap reactor implementations or compose custom exports.
128
- */
129
- export * from "./index.generated"
130
- `
131
- }
package/src/index.ts CHANGED
@@ -6,13 +6,7 @@
6
6
  */
7
7
 
8
8
  // Core Types
9
- export type {
10
- CanisterConfig,
11
- CodegenConfig,
12
- GeneratorResult,
13
- ReactorMode,
14
- ReactorGenerationConfig,
15
- } from "./types.js"
9
+ export type { CanisterConfig, CodegenConfig, GeneratorResult } from "./types.js"
16
10
 
17
11
  // Pipeline (Primary Entry Point)
18
12
  export { runCanisterPipeline } from "./pipeline.js"
package/src/pipeline.ts CHANGED
@@ -7,22 +7,14 @@
7
7
  * Pipeline steps (in order):
8
8
  * 1. Resolve paths (didFile, outDir)
9
9
  * 2. Generate declarations (JS + .d.ts + .did copy)
10
- * 3. Generate reactor implementation + stable wrapper (`index.generated.ts` + `index.ts`)
10
+ * 3. Generate reactor file (index.ts)
11
11
  */
12
12
 
13
13
  import fs from "node:fs"
14
14
  import path from "node:path"
15
- import type {
16
- CanisterConfig,
17
- CodegenConfig,
18
- GeneratorResult,
19
- ReactorMode,
20
- } from "./types.js"
15
+ import type { CanisterConfig, CodegenConfig, GeneratorResult } from "./types.js"
21
16
  import { generateDeclarations } from "./generators/declarations.js"
22
- import {
23
- generateReactorFile,
24
- generateReactorWrapperFile,
25
- } from "./generators/reactor.js"
17
+ import { generateReactorFile } from "./generators/reactor.js"
26
18
 
27
19
  export interface PipelineOptions {
28
20
  /** Canister name and config */
@@ -35,30 +27,7 @@ export interface PipelineOptions {
35
27
  /**
36
28
  * Global codegen config (for fallback outDir and clientManagerPath).
37
29
  */
38
- globalConfig: Pick<CodegenConfig, "outDir" | "clientManagerPath" | "reactor">
39
- }
40
-
41
- function resolveReactorMode(
42
- canisterName: string,
43
- globalConfig: Pick<CodegenConfig, "reactor">
44
- ): ReactorMode {
45
- return (
46
- globalConfig.reactor?.canisters?.[canisterName] ??
47
- globalConfig.reactor?.defaultMode ??
48
- "display"
49
- )
50
- }
51
-
52
- function isStableWrapperFile(content: string): boolean {
53
- return content.includes('export * from "./index.generated"')
54
- }
55
-
56
- function isLegacyGeneratedIndexFile(content: string): boolean {
57
- return (
58
- content.includes("Auto-generated by @ic-reactor/codegen") &&
59
- content.includes("createActorHooks(") &&
60
- !isStableWrapperFile(content)
61
- )
30
+ globalConfig: Pick<CodegenConfig, "outDir" | "clientManagerPath">
62
31
  }
63
32
 
64
33
  export interface PipelineResult {
@@ -137,48 +106,25 @@ export async function runCanisterPipeline(
137
106
  }
138
107
  }
139
108
 
140
- // ── Step 2: Reactor implementation + stable wrapper ───────────────────────
109
+ // ── Step 2: Reactor file ───────────────────────────────────────────────────
141
110
 
142
- const generatedReactorPath = path.join(canisterOutDir, "index.generated.ts")
143
- const wrapperPath = path.join(canisterOutDir, "index.ts")
144
- const reactorMode = resolveReactorMode(name, globalConfig)
111
+ const reactorPath = path.join(canisterOutDir, "index.ts")
145
112
 
146
113
  try {
147
114
  const reactorContent = generateReactorFile({
148
115
  canisterName: name,
149
116
  didFile: resolvedDidFile,
150
117
  clientManagerPath: resolvedClientManagerPath,
151
- reactorMode,
152
- })
153
- const wrapperContent = generateReactorWrapperFile({
154
- canisterName: name,
155
- didFile: resolvedDidFile,
156
- clientManagerPath: resolvedClientManagerPath,
157
- reactorMode,
158
118
  })
159
119
 
160
120
  fs.mkdirSync(canisterOutDir, { recursive: true })
161
- fs.writeFileSync(generatedReactorPath, reactorContent)
162
-
163
- files.push({ success: true, filePath: generatedReactorPath })
121
+ fs.writeFileSync(reactorPath, reactorContent)
164
122
 
165
- if (!fs.existsSync(wrapperPath)) {
166
- fs.writeFileSync(wrapperPath, wrapperContent)
167
- files.push({ success: true, filePath: wrapperPath })
168
- } else {
169
- const existingWrapperContent = fs.readFileSync(wrapperPath, "utf-8")
170
-
171
- if (isLegacyGeneratedIndexFile(existingWrapperContent)) {
172
- fs.writeFileSync(wrapperPath, wrapperContent)
173
- files.push({ success: true, filePath: wrapperPath })
174
- } else {
175
- files.push({ success: true, filePath: wrapperPath, skipped: true })
176
- }
177
- }
123
+ files.push({ success: true, filePath: reactorPath })
178
124
  } catch (err) {
179
125
  files.push({
180
126
  success: false,
181
- filePath: generatedReactorPath,
127
+ filePath: reactorPath,
182
128
  error: `Reactor generation failed: ${err instanceof Error ? err.message : String(err)}`,
183
129
  })
184
130
 
package/src/types.ts CHANGED
@@ -28,21 +28,6 @@ export interface CanisterConfig {
28
28
  canisterId?: string
29
29
  }
30
30
 
31
- export type ReactorMode = "raw" | "display"
32
-
33
- export interface ReactorGenerationConfig {
34
- /**
35
- * Default reactor mode used for generated hook exports.
36
- * `display` preserves current behavior.
37
- */
38
- defaultMode?: ReactorMode
39
- /**
40
- * Optional per-canister overrides keyed by canister name.
41
- * Example: { workflow_engine: "raw" }
42
- */
43
- canisters?: Record<string, ReactorMode>
44
- }
45
-
46
31
  /**
47
32
  * Top-level codegen / CLI configuration (stored in `ic-reactor.json`).
48
33
  */
@@ -59,8 +44,6 @@ export interface CodegenConfig {
59
44
  * Individual canisters can override via `CanisterConfig.clientManagerPath`.
60
45
  */
61
46
  clientManagerPath?: string
62
- /** Optional reactor mode generation settings */
63
- reactor?: ReactorGenerationConfig
64
47
  /** Canister configurations, keyed by canister name */
65
48
  canisters: Record<string, CanisterConfig>
66
49
  }
@@ -1,100 +0,0 @@
1
- // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
-
3
- exports[`Reactor generator > generates a stable wrapper file > stable-wrapper-index 1`] = `
4
- "/**
5
- * Backend canister exports
6
- *
7
- * This wrapper is created once by @ic-reactor/codegen and is not overwritten.
8
- * Customize it if you need to swap reactor implementations or compose custom exports.
9
- */
10
- export * from "./index.generated"
11
- "
12
- `;
13
-
14
- exports[`Reactor generator > keeps default behavior as display mode > display-mode-index-generated 1`] = `
15
- "import { DisplayReactor, Reactor, createActorHooks } from "@ic-reactor/react"
16
- import { clientManager } from "../../clients"
17
- import { idlFactory, type _SERVICE } from "./declarations/backend"
18
-
19
- export type BackendService = _SERVICE
20
-
21
- /**
22
- * Backend Reactor
23
- *
24
- * Auto-generated by @ic-reactor/codegen — do not edit.
25
- * Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
26
- */
27
- export function createBackendRawReactor() {
28
- return new Reactor<BackendService>({
29
- clientManager,
30
- idlFactory,
31
- name: "backend",
32
- })
33
- }
34
-
35
- export function createBackendDisplayReactor() {
36
- return new DisplayReactor<BackendService>({
37
- clientManager,
38
- idlFactory,
39
- name: "backend",
40
- })
41
- }
42
-
43
- export const backendReactor = createBackendDisplayReactor()
44
-
45
- export const BackendReactorMode = "display" as const
46
-
47
- export const {
48
- useActorQuery: useBackendQuery,
49
- useActorSuspenseQuery: useBackendSuspenseQuery,
50
- useActorInfiniteQuery: useBackendInfiniteQuery,
51
- useActorSuspenseInfiniteQuery: useBackendSuspenseInfiniteQuery,
52
- useActorMutation: useBackendMutation,
53
- useActorMethod: useBackendMethod,
54
- } = createActorHooks(backendReactor)
55
- "
56
- `;
57
-
58
- exports[`Reactor generator > supports raw mode generation > raw-mode-index-generated 1`] = `
59
- "import { DisplayReactor, Reactor, createActorHooks } from "@ic-reactor/react"
60
- import { clientManager } from "../../clients"
61
- import { idlFactory, type _SERVICE } from "./declarations/workflow_engine"
62
-
63
- export type WorkflowEngineService = _SERVICE
64
-
65
- /**
66
- * WorkflowEngine Reactor
67
- *
68
- * Auto-generated by @ic-reactor/codegen — do not edit.
69
- * Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
70
- */
71
- export function createWorkflowEngineRawReactor() {
72
- return new Reactor<WorkflowEngineService>({
73
- clientManager,
74
- idlFactory,
75
- name: "workflow_engine",
76
- })
77
- }
78
-
79
- export function createWorkflowEngineDisplayReactor() {
80
- return new DisplayReactor<WorkflowEngineService>({
81
- clientManager,
82
- idlFactory,
83
- name: "workflow_engine",
84
- })
85
- }
86
-
87
- export const workflowEngineReactor = createWorkflowEngineRawReactor()
88
-
89
- export const WorkflowEngineReactorMode = "raw" as const
90
-
91
- export const {
92
- useActorQuery: useWorkflowEngineQuery,
93
- useActorSuspenseQuery: useWorkflowEngineSuspenseQuery,
94
- useActorInfiniteQuery: useWorkflowEngineInfiniteQuery,
95
- useActorSuspenseInfiniteQuery: useWorkflowEngineSuspenseInfiniteQuery,
96
- useActorMutation: useWorkflowEngineMutation,
97
- useActorMethod: useWorkflowEngineMethod,
98
- } = createActorHooks(workflowEngineReactor)
99
- "
100
- `;
@@ -1,178 +0,0 @@
1
- import fs from "node:fs"
2
- import os from "node:os"
3
- import path from "node:path"
4
- import { afterEach, describe, expect, it } from "vitest"
5
- import { runCanisterPipeline } from "./pipeline"
6
-
7
- describe("Codegen pipeline", () => {
8
- const tempDirs: string[] = []
9
-
10
- afterEach(() => {
11
- for (const dir of tempDirs) {
12
- fs.rmSync(dir, { recursive: true, force: true })
13
- }
14
- tempDirs.length = 0
15
- })
16
-
17
- function createTempProject() {
18
- const projectRoot = fs.mkdtempSync(
19
- path.join(os.tmpdir(), "ic-reactor-codegen-pipeline-")
20
- )
21
- tempDirs.push(projectRoot)
22
- return projectRoot
23
- }
24
-
25
- function writeDid(projectRoot: string, fileName: string) {
26
- fs.writeFileSync(
27
- path.join(projectRoot, fileName),
28
- `service : {
29
- greet: (text) -> (text) query;
30
- }`
31
- )
32
- }
33
-
34
- it("applies per-canister reactor mode overrides over the global default", async () => {
35
- const projectRoot = createTempProject()
36
- writeDid(projectRoot, "workflow_engine.did")
37
-
38
- const result = await runCanisterPipeline({
39
- canisterConfig: {
40
- name: "workflow_engine",
41
- didFile: "workflow_engine.did",
42
- },
43
- projectRoot,
44
- globalConfig: {
45
- outDir: "src/declarations",
46
- clientManagerPath: "../../clients",
47
- reactor: {
48
- defaultMode: "display",
49
- canisters: {
50
- workflow_engine: "raw",
51
- },
52
- },
53
- },
54
- })
55
-
56
- expect(result.success).toBe(true)
57
-
58
- const generatedPath = path.join(
59
- projectRoot,
60
- "src/declarations/workflow_engine/index.generated.ts"
61
- )
62
- const generated = fs.readFileSync(generatedPath, "utf-8")
63
-
64
- expect(generated).toContain("new Reactor<WorkflowEngineService>")
65
- expect(generated).toContain(
66
- 'export const WorkflowEngineReactorMode = "raw" as const'
67
- )
68
- })
69
-
70
- it("does not overwrite the wrapper file on regenerate", async () => {
71
- const projectRoot = createTempProject()
72
- writeDid(projectRoot, "backend.did")
73
-
74
- const options = {
75
- canisterConfig: {
76
- name: "backend",
77
- didFile: "backend.did",
78
- },
79
- projectRoot,
80
- globalConfig: {
81
- outDir: "src/declarations",
82
- clientManagerPath: "../../clients",
83
- },
84
- } as const
85
-
86
- const first = await runCanisterPipeline(options)
87
- expect(first.success).toBe(true)
88
-
89
- const wrapperPath = path.join(
90
- projectRoot,
91
- "src/declarations/backend/index.ts"
92
- )
93
- fs.writeFileSync(
94
- wrapperPath,
95
- `// user wrapper
96
- export const customBackendWrapper = true
97
- `
98
- )
99
-
100
- const second = await runCanisterPipeline(options)
101
- expect(second.success).toBe(true)
102
-
103
- const wrapper = fs.readFileSync(wrapperPath, "utf-8")
104
- expect(wrapper).toContain("customBackendWrapper = true")
105
- expect(second.files).toEqual(
106
- expect.arrayContaining([
107
- expect.objectContaining({
108
- filePath: wrapperPath,
109
- skipped: true,
110
- success: true,
111
- }),
112
- ])
113
- )
114
- })
115
-
116
- it("migrates a legacy generated index.ts into a stable wrapper", async () => {
117
- const projectRoot = createTempProject()
118
- writeDid(projectRoot, "backend.did")
119
-
120
- const canisterOutDir = path.join(projectRoot, "src/declarations/backend")
121
- fs.mkdirSync(canisterOutDir, { recursive: true })
122
-
123
- // Simulate the pre-wrapper generated index.ts content from older versions.
124
- fs.writeFileSync(
125
- path.join(canisterOutDir, "index.ts"),
126
- `import { DisplayReactor, createActorHooks } from "@ic-reactor/react"
127
- import { clientManager } from "../../clients"
128
- import { idlFactory, type _SERVICE } from "./declarations/backend"
129
-
130
- export type BackendService = _SERVICE
131
-
132
- /**
133
- * Backend Reactor
134
- *
135
- * Auto-generated by @ic-reactor/codegen — do not edit.
136
- * Re-run \`ic-reactor generate\` (or the Vite plugin) to regenerate.
137
- */
138
- export const backendReactor = new DisplayReactor<BackendService>({
139
- clientManager,
140
- idlFactory,
141
- name: "backend",
142
- })
143
-
144
- export const {
145
- useActorQuery: useBackendQuery,
146
- } = createActorHooks(backendReactor)
147
- `
148
- )
149
-
150
- const result = await runCanisterPipeline({
151
- canisterConfig: {
152
- name: "backend",
153
- didFile: "backend.did",
154
- },
155
- projectRoot,
156
- globalConfig: {
157
- outDir: "src/declarations",
158
- clientManagerPath: "../../clients",
159
- },
160
- })
161
-
162
- expect(result.success).toBe(true)
163
-
164
- const wrapperPath = path.join(canisterOutDir, "index.ts")
165
- const wrapper = fs.readFileSync(wrapperPath, "utf-8")
166
- expect(wrapper).toContain('export * from "./index.generated"')
167
- expect(wrapper).not.toContain("createActorHooks(")
168
-
169
- const generated = fs.readFileSync(
170
- path.join(canisterOutDir, "index.generated.ts"),
171
- "utf-8"
172
- )
173
- expect(generated).toContain("createBackendDisplayReactor")
174
- expect(generated).toContain(
175
- 'export const BackendReactorMode = "display" as const'
176
- )
177
- })
178
- })
@@ -1,42 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import { generateReactorFile, generateReactorWrapperFile } from "./generators"
3
-
4
- describe("Reactor generator", () => {
5
- it("keeps default behavior as display mode", () => {
6
- const content = generateReactorFile({
7
- canisterName: "backend",
8
- didFile: "mock/backend.did",
9
- clientManagerPath: "../../clients",
10
- })
11
-
12
- expect(content).toMatchSnapshot("display-mode-index-generated")
13
- expect(content).toContain("new DisplayReactor<BackendService>")
14
- expect(content).toContain(
15
- 'export const BackendReactorMode = "display" as const'
16
- )
17
- })
18
-
19
- it("supports raw mode generation", () => {
20
- const content = generateReactorFile({
21
- canisterName: "workflow_engine",
22
- didFile: "mock/workflow_engine.did",
23
- reactorMode: "raw",
24
- })
25
-
26
- expect(content).toMatchSnapshot("raw-mode-index-generated")
27
- expect(content).toContain("new Reactor<WorkflowEngineService>")
28
- expect(content).toContain(
29
- 'export const WorkflowEngineReactorMode = "raw" as const'
30
- )
31
- })
32
-
33
- it("generates a stable wrapper file", () => {
34
- const content = generateReactorWrapperFile({
35
- canisterName: "backend",
36
- didFile: "mock/backend.did",
37
- })
38
-
39
- expect(content).toMatchSnapshot("stable-wrapper-index")
40
- expect(content).toContain('export * from "./index.generated"')
41
- })
42
- })