@pacaf/wizard-ux 3.4.1 → 3.4.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.html CHANGED
@@ -28,7 +28,7 @@
28
28
  }
29
29
  @keyframes spin { to { transform: rotate(360deg); } }
30
30
  </style>
31
- <script type="module" crossorigin src="/assets/index-CJnhmiwH.js"></script>
31
+ <script type="module" crossorigin src="/assets/index-DJRPvUY4.js"></script>
32
32
  </head>
33
33
  <body>
34
34
  <div id="root"><div id="boot"><div class="ring"></div></div></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pacaf/wizard-ux",
3
- "version": "3.4.1",
3
+ "version": "3.4.3",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Browser-based setup wizard for Power Apps Code Apps (parallel to @pacaf/wizard CLI).",
@@ -38,7 +38,7 @@
38
38
  "react-dom": "^19.0.0",
39
39
  "react-resizable-panels": "^2.1.7",
40
40
  "react-router-dom": "^7.1.0",
41
- "@pacaf/wizard": "3.4.1"
41
+ "@pacaf/wizard": "3.4.3"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/react": "^19.0.0",
@@ -11,7 +11,6 @@ const PACKAGE_DIR = resolve(__dirname, '..', '..', '..');
11
11
  const PROJECT_DIR = process.cwd();
12
12
  const SHELL = await import(pathToFileURL(resolve(PACKAGE_DIR, 'wizard', 'lib', 'shell.mjs')).href);
13
13
  const PAC_TARGET = await import(pathToFileURL(resolve(PACKAGE_DIR, 'wizard', 'lib', 'pac-target.mjs')).href);
14
- const SOLUTION_MEMBERSHIP = await import(pathToFileURL(resolve(PACKAGE_DIR, 'wizard', 'lib', 'solution-membership.mjs')).href);
15
14
 
16
15
  function hasCommand(name) {
17
16
  try {
@@ -107,40 +106,11 @@ function verifyUserProfile(pac, projectDir, state, credentialValues) {
107
106
 
108
107
  // Ensure the deployed Code App is a component of the selected solution.
109
108
  //
110
- // `pac code push -s <UNIQUE name>` associates the app on the FIRST push (the
111
- // CREATE). But several real-world paths leave it OUTSIDE the chosen solution:
112
- // a unique-name mismatch makes pac fall back to the env's preferred/default
113
- // solution, or the app was first pushed bare and a later -s re-push (an UPDATE)
114
- // is ignored. The earlier #81 design treated that as unrecoverable and told the
115
- // user to delete + recreate. That is wrong: appId === canvasappid, so the
116
- // app can be added to the solution after the fact.
117
- //
118
- // READ + REPAIR (auth-agnostic, via the shared lib):
119
- // • READ — `pac org fetch` on solutioncomponent (componenttype 300) under
120
- // the active profile (user OR SPN), so no client secret is needed.
121
- // • REPAIR — `pac solution add-solution-component --component <appId>
122
- // --componentType 300 --solutionUniqueName <name>` when absent.
123
- // The old `pac solution export --managed false` + type-300 count read was
124
- // malformed and produced false negatives; the old `pac solution list` check
125
- // was a false positive (it only proved the solution exists). Both are gone.
126
- async function ensureAppInSolution(log, pac, projectDir, appId, solutionUniqueName, { appDisplayName } = {}) {
127
- if (!solutionUniqueName) return { status: 'unknown' };
128
- if (!appId) {
129
- log.warn(`No appId found in power.config.json — cannot confirm membership in "${solutionUniqueName}". Verify in the Maker Portal.`);
130
- return { status: 'unknown' };
131
- }
132
- log.info(`Confirming the app is in solution "${solutionUniqueName}" (and adding it if missing)...`);
133
- const runCapture = (file, args, opts) => runFileCapture(log, file, args, opts);
134
- const result = await SOLUTION_MEMBERSHIP.ensureAppInSolution({
135
- pac, projectDir, appId, solutionUniqueName, runCapture, log,
136
- });
137
- if (result.status !== 'member' && result.status === 'absent') {
138
- for (const line of SOLUTION_MEMBERSHIP.manualSolutionAddSteps(solutionUniqueName, appDisplayName || 'this Code App', appId)) {
139
- log.warn(line);
140
- }
141
- }
142
- return result;
143
- }
109
+ // The first `pac code push -s <UNIQUE name>` (the CREATE) both creates the
110
+ // canvasapp record AND makes it a component of the chosen solution in one shot
111
+ // the documented golden path. We REFUSE a bare push below, so the app can
112
+ // never be created OUTSIDE its solution (a silent failure a later -s re-push,
113
+ // which is an ignored UPDATE, cannot fix). No post-push repair is needed (#81).
144
114
 
145
115
  export default {
146
116
  meta: {
@@ -207,7 +177,6 @@ export default {
207
177
  // the solution — association only happens on the first push WITH the unique
208
178
  // name, and a later -s re-push cannot fix it (issue #81).
209
179
  const solutionUniqueName = String(state.SOLUTION_UNIQUE_NAME || '').trim();
210
- const appDisplayName = String(state.APP_NAME || 'this Code App').trim();
211
180
  const pushArgs = ['code', 'push'];
212
181
  if (solutionUniqueName) {
213
182
  pushArgs.push('-s', solutionUniqueName);
@@ -215,14 +184,35 @@ export default {
215
184
  throw new Error('No solution unique name (SOLUTION_UNIQUE_NAME) is available. Refusing to run a bare `pac code push`, which would create the Code App outside any solution (a silent failure a later -s re-push cannot fix). Re-run the wizard solution step so the unique name is captured before deploying.');
216
185
  }
217
186
 
218
- // PRE-PUSH NOTE. On an UPDATE push (appId already present) `-s` is ignored,
219
- // but that is no longer a dead-end: after the push we read membership from
220
- // Dataverse and add the app to the solution if needed (appId === canvasappid),
221
- // which works regardless of whether this was a CREATE or an UPDATE.
187
+ // The first push with -s (the CREATE) both creates the canvasapp record and
188
+ // makes it a component of the chosen solution in one shot. An UPDATE push
189
+ // (appId already present) just republishes the existing app in place; -s is
190
+ // a no-op but the app stays in the solution it was created in (#81).
222
191
  const preInfo = PAC_TARGET.loadPowerConfigInfo(powerConfigPath);
223
192
  const isFirstPush = !preInfo.appId;
224
193
  if (!isFirstPush) {
225
- log.info(`Existing appId detected (${preInfo.appId}) — this push is an UPDATE. Solution membership will be confirmed (and repaired if needed) after the push.`);
194
+ log.info(`Existing appId detected (${preInfo.appId}) — this push is an UPDATE (republish in place).`);
195
+ }
196
+
197
+ // PRECONDITION (issue #81 follow-up): `pac code push -s` only associates the
198
+ // app with its solution if that UNIQUE name already EXISTS in the target
199
+ // environment. If it does not, pac SILENTLY publishes into the Default
200
+ // solution. On the FIRST push, verify the solution exists FIRST and STOP if
201
+ // it is absent — never let the CREATE land the app outside its solution.
202
+ if (isFirstPush) {
203
+ log.info(`Verifying solution "${solutionUniqueName}" exists in the target environment...`);
204
+ const solCheck = PAC_TARGET.solutionExistsInSelectedEnv({ pac, uniqueName: solutionUniqueName, cwd: projectDir });
205
+ if (solCheck.status === 'absent') {
206
+ throw new Error(`Solution "${solutionUniqueName}" does not exist in the target environment. `
207
+ + `Running "pac code push -s ${solutionUniqueName}" now would SILENTLY publish the app into the Default solution `
208
+ + `(the recurring "app not in my solution" failure). Create the solution in this environment (Maker Portal → Solutions → New solution, or reuse an existing one), `
209
+ + `then re-run this step. Tip: the -s value must be the solution UNIQUE name, not the display name.`);
210
+ }
211
+ if (solCheck.status === 'unknown') {
212
+ log.warn(`Could not confirm solution "${solutionUniqueName}" exists (${solCheck.reason}). Proceeding, but if the app lands in the Default solution, verify the unique name is correct.`);
213
+ } else {
214
+ log.ok(`Solution "${solutionUniqueName}" exists in the target environment — safe to push with -s`);
215
+ }
226
216
  }
227
217
 
228
218
  const pushResult = await runFileCapture(log, pac, pushArgs, { cwd: projectDir });
@@ -246,15 +236,6 @@ export default {
246
236
  log.warn('Could not detect deployed app URL in pac output. Open the app from Power Apps Maker Portal.');
247
237
  }
248
238
 
249
- // POST-PUSH MEMBERSHIP ENSURE. Read the appId the push wrote into
250
- // power.config.json, confirm the app is a component of the selected
251
- // solution via a Dataverse read, and add it automatically if it is not.
252
- // This covers BOTH a CREATE that silently fell back to the preferred/default
253
- // solution AND an UPDATE of an app that was first pushed bare (issue #81),
254
- // without ever deleting or recreating the app.
255
- const postInfo = PAC_TARGET.loadPowerConfigInfo(powerConfigPath);
256
- await ensureAppInSolution(log, pac, projectDir, postInfo.appId, solutionUniqueName, { appDisplayName });
257
-
258
239
  return { stateUpdate, completedStep: 9 };
259
240
  },
260
241
  };