@algolia/wizard 0.19.0 → 0.20.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.
Files changed (2) hide show
  1. package/dist/main.js +106 -80
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -2246,7 +2246,7 @@ async function ensureApplication() {
2246
2246
  }
2247
2247
 
2248
2248
  // src/workflows/default.ts
2249
- import { z as z29 } from "zod";
2249
+ import { z as z30 } from "zod";
2250
2250
 
2251
2251
  // src/actions/listIndices.ts
2252
2252
  import { z as z5 } from "zod";
@@ -2767,6 +2767,28 @@ async function ensureGitIgnored(root, target) {
2767
2767
  var APP_ID_VAR = "ALGOLIA_APPLICATION_ID";
2768
2768
  var API_KEY_VAR = "ALGOLIA_WRITE_API_KEY";
2769
2769
  var INDEX_NAME_VAR = "ALGOLIA_INDEX_NAME";
2770
+ var PUBLIC_APP_ID_SUFFIX = "ALGOLIA_APP_ID";
2771
+ var PUBLIC_SEARCH_KEY_SUFFIX = "ALGOLIA_SEARCH_KEY";
2772
+ var PUBLIC_INDEX_NAME_SUFFIX = "ALGOLIA_INDEX_NAME";
2773
+ function publicAppIdVar(prefix) {
2774
+ return `${prefix}${PUBLIC_APP_ID_SUFFIX}`;
2775
+ }
2776
+ function publicSearchKeyVar(prefix) {
2777
+ return `${prefix}${PUBLIC_SEARCH_KEY_SUFFIX}`;
2778
+ }
2779
+ function publicIndexNameVar(prefix) {
2780
+ return `${prefix}${PUBLIC_INDEX_NAME_SUFFIX}`;
2781
+ }
2782
+ function publicSearchEnvVars(prefix, index, appId, searchKey) {
2783
+ return [
2784
+ { name: publicAppIdVar(prefix), value: appId ?? "<your-algolia-app-id>" },
2785
+ {
2786
+ name: publicSearchKeyVar(prefix),
2787
+ value: searchKey ?? "<your-algolia-search-only-api-key>"
2788
+ },
2789
+ { name: publicIndexNameVar(prefix), value: index }
2790
+ ];
2791
+ }
2770
2792
  function appendEnv(content, entries) {
2771
2793
  const prefix = content.length > 0 && !content.endsWith("\n") ? "\n" : "";
2772
2794
  const lines = entries.map(([name, value]) => `${name}=${value}
@@ -3500,7 +3522,10 @@ async function runAgent(req) {
3500
3522
  import z21 from "zod";
3501
3523
  var detectLanguageSchema = z21.object({
3502
3524
  languages: z21.array(z21.object({ name: z21.string(), version: z21.string() })),
3503
- frameworks: z21.array(z21.object({ name: z21.string(), version: z21.string() }))
3525
+ frameworks: z21.array(z21.object({ name: z21.string(), version: z21.string() })),
3526
+ publicEnvVarPrefix: z21.string().regex(/^([A-Z0-9]+_)*$/).describe(
3527
+ `The prefix a bundler/framework requires for an env var to reach client-side code (e.g. "NEXT_PUBLIC_", "VITE_", "NUXT_PUBLIC_"), uppercase with a trailing underscore when non-empty. Prefer the project's own existing convention over a framework default. Empty string when the project has no client-side env var exposure (e.g. a backend-only project).`
3528
+ )
3504
3529
  });
3505
3530
  var detectLanguage = () => runAgent({
3506
3531
  instructions: [
@@ -3509,6 +3534,7 @@ var detectLanguage = () => runAgent({
3509
3534
  "If a meta-framework is used, exclude the framework it builds on (e.g. Next.js over React, Rails over Rack).",
3510
3535
  "Return the exact version",
3511
3536
  "Exclude things like CSS frameworks, build tools, or testing frameworks",
3537
+ `Determine publicEnvVarPrefix: check the project's own env var usage first (e.g. names already referenced in code, .env/.env.example); if none exists, fall back to the detected framework's known convention for exposing env vars to client-side code; use "" when the project has no such convention (e.g. a backend-only project).`,
3512
3538
  'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
3513
3539
  "When done, call reportStatus"
3514
3540
  ],
@@ -3604,7 +3630,7 @@ async function runAnalysis(mode, extraInstructions = []) {
3604
3630
  // package.json
3605
3631
  var package_default = {
3606
3632
  name: "@algolia/wizard",
3607
- version: "0.19.0",
3633
+ version: "0.20.0",
3608
3634
  description: "Magically implement Algolia functionality in your codebase",
3609
3635
  type: "module",
3610
3636
  engines: {
@@ -4009,7 +4035,7 @@ ${formatCompletedSteps(ctx.completedSteps)}`,
4009
4035
  };
4010
4036
 
4011
4037
  // src/actions/implement.ts
4012
- import z28 from "zod";
4038
+ import z29 from "zod";
4013
4039
  import { join as join12 } from "node:path";
4014
4040
 
4015
4041
  // src/lib/worktree.ts
@@ -4259,31 +4285,48 @@ function shellQuote(value) {
4259
4285
  return "'" + value.replace(/'/g, "'\\''") + "'";
4260
4286
  }
4261
4287
 
4288
+ // src/actions/resolveEnvVarPrefix.ts
4289
+ import z28 from "zod";
4290
+ var resolveEnvVarPrefixSchema = z28.object({
4291
+ publicEnvVarPrefix: detectLanguageSchema.shape.publicEnvVarPrefix
4292
+ });
4293
+ var resolveEnvVarPrefix = (frameworkName) => runAgent({
4294
+ instructions: [
4295
+ `The developer corrected the project's framework to "${frameworkName}".`,
4296
+ `Determine publicEnvVarPrefix for this framework: check the project's own env var usage first (e.g. names already referenced in code, .env/.env.example); if none exists, fall back to this framework's known convention for exposing env vars to client-side code; use "" when the framework has no such convention (e.g. a backend-only framework).`,
4297
+ 'Use as few tools as possible, but do not guess. If you cant find the answer, say "unknown"',
4298
+ "When done, call reportStatus"
4299
+ ],
4300
+ tools: ["listFiles", "changeDirectory", "readFile", "searchFiles"],
4301
+ outputSchema: resolveEnvVarPrefixSchema,
4302
+ modelSize: "small"
4303
+ });
4304
+
4262
4305
  // src/actions/implement.ts
4263
- var implementSchema = z28.object({
4264
- filesChanged: z28.array(z28.string()),
4265
- summary: z28.string(),
4266
- worktreePath: z28.string().optional(),
4267
- ingestCommand: z28.string().optional(),
4268
- ingestScriptRan: z28.boolean().optional(),
4269
- ingestRecordCount: z28.number().optional(),
4270
- ingestDurationMs: z28.number().optional(),
4271
- ingestionSource: z28.enum(["local", "fileUpload", "generated"]),
4272
- searchEnvVars: z28.array(
4273
- z28.object({
4274
- name: z28.string(),
4275
- value: z28.string()
4306
+ var implementSchema = z29.object({
4307
+ filesChanged: z29.array(z29.string()),
4308
+ summary: z29.string(),
4309
+ worktreePath: z29.string().optional(),
4310
+ ingestCommand: z29.string().optional(),
4311
+ ingestScriptRan: z29.boolean().optional(),
4312
+ ingestRecordCount: z29.number().optional(),
4313
+ ingestDurationMs: z29.number().optional(),
4314
+ ingestionSource: z29.enum(["local", "fileUpload", "generated"]),
4315
+ searchEnvVars: z29.array(
4316
+ z29.object({
4317
+ name: z29.string(),
4318
+ value: z29.string()
4276
4319
  })
4277
4320
  ).optional()
4278
4321
  });
4279
- var implementationOutputSchema = z28.object({
4280
- summary: z28.string(),
4281
- ingestCommand: z28.string().optional()
4322
+ var implementationOutputSchema = z29.object({
4323
+ summary: z29.string(),
4324
+ ingestCommand: z29.string().optional()
4282
4325
  });
4283
- var verificationOutputSchema = z28.object({
4284
- summary: z28.string(),
4285
- sufficient: z28.boolean(),
4286
- additionalInstructions: z28.string().optional()
4326
+ var verificationOutputSchema = z29.object({
4327
+ summary: z29.string(),
4328
+ sufficient: z29.boolean(),
4329
+ additionalInstructions: z29.string().optional()
4287
4330
  });
4288
4331
  var MAX_IMPLEMENT_VERIFICATION_ATTEMPTS = 3;
4289
4332
  var DEFAULT_IMPLEMENT_USE_CASES = ["ingestion", "search"];
@@ -4314,53 +4357,6 @@ function searchUiTarget(language) {
4314
4357
  function frameworksForDoc(language) {
4315
4358
  return [matchUiFramework(language)?.doc ?? "js"];
4316
4359
  }
4317
- function publicEnvPrefix(language) {
4318
- const frameworkNames = lower(language.frameworks);
4319
- if (frameworkNames.some((name) => name.includes("next"))) {
4320
- return "NEXT_PUBLIC_";
4321
- }
4322
- if (frameworkNames.some((name) => name.includes("nuxt"))) {
4323
- return "NUXT_PUBLIC_";
4324
- }
4325
- if (frameworkNames.some((name) => name.includes("astro"))) {
4326
- return "PUBLIC_";
4327
- }
4328
- if (frameworkNames.some((name) => name.includes("vite"))) {
4329
- return "VITE_";
4330
- }
4331
- return isJsProject(language) ? "PUBLIC_" : "";
4332
- }
4333
- var APP_ID_VAR_SUFFIX = "ALGOLIA_APP_ID";
4334
- var SEARCH_KEY_VAR_SUFFIX = "ALGOLIA_SEARCH_API_KEY";
4335
- var INDEX_VAR_SUFFIX = "ALGOLIA_INDEX_NAME";
4336
- function appIdVar(language) {
4337
- return `${publicEnvPrefix(language)}${APP_ID_VAR_SUFFIX}`;
4338
- }
4339
- function searchKeyVar(language) {
4340
- return `${publicEnvPrefix(language)}${SEARCH_KEY_VAR_SUFFIX}`;
4341
- }
4342
- function searchIndexVar(language) {
4343
- return `${publicEnvPrefix(language)}${INDEX_VAR_SUFFIX}`;
4344
- }
4345
- function searchEnvVars(language, index, appId, searchKey) {
4346
- return [
4347
- {
4348
- name: appIdVar(language),
4349
- value: appId ?? "<your-algolia-app-id>"
4350
- },
4351
- {
4352
- name: searchKeyVar(language),
4353
- value: searchKey ?? "<your-algolia-search-only-api-key>"
4354
- },
4355
- // Wizard-supplied rather than written into the generated code, because an
4356
- // agent that retypes the name (appending the project name, re-casing it)
4357
- // leaves the UI querying an index that does not exist.
4358
- {
4359
- name: searchIndexVar(language),
4360
- value: index
4361
- }
4362
- ];
4363
- }
4364
4360
  function baseInstructions(input) {
4365
4361
  return [
4366
4362
  // Agents have renamed this (e.g. appending the project name), which the
@@ -4436,7 +4432,7 @@ function searchInstructions(input) {
4436
4432
  ],
4437
4433
  `Add the search UI at ${input.searchLocation ? `"${input.searchLocation}"` : "the best shared, always-rendered layout location (e.g. a header/nav component)"} so it is reachable across the app \u2014 at least a working search input and results list against the target index.`,
4438
4434
  "If a search box already exists, replace it with yours.",
4439
- `Read the index name from the ${searchIndexVar(input.language)} env var, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or component name.`,
4435
+ `Read the index name from the ${publicIndexNameVar(input.publicEnvVarPrefix)} env var, which the wizard sets to "${input.targetIndex}". Never hardcode an index name or derive one from the project, file, or component name.`,
4440
4436
  "Read the App ID, the search-only API key, and the index name from env vars; never hardcode them. A search-only key is safe to expose client-side.",
4441
4437
  // The key is provisioned only after verification passes, so the agent never
4442
4438
  // sees one. It must also leave .env alone: the wizard reads that file to
@@ -4580,6 +4576,21 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4580
4576
  languages: ctx.getStepOutput("confirm-language")?.languages ?? scan.languages,
4581
4577
  frameworks: ctx.getStepOutput("confirm-framework")?.frameworks ?? scan.frameworks
4582
4578
  };
4579
+ const normalizeFrameworkName = (name) => name.toLowerCase().replace(/[^a-z0-9]/g, "");
4580
+ const confirmedPrimaryFramework = language.frameworks[0]?.name;
4581
+ const frameworkWasCorrected = confirmedPrimaryFramework !== void 0 && !scan.frameworks.some(
4582
+ (fw) => normalizeFrameworkName(fw.name) === normalizeFrameworkName(confirmedPrimaryFramework)
4583
+ );
4584
+ const publicEnvVarPrefixPromise = frameworkWasCorrected ? resolveEnvVarPrefix(confirmedPrimaryFramework).then(
4585
+ (r) => r.publicEnvVarPrefix,
4586
+ (err) => {
4587
+ logger.warn(
4588
+ { err, framework: confirmedPrimaryFramework },
4589
+ "implement: could not re-resolve publicEnvVarPrefix after a framework correction; using the stale scan value"
4590
+ );
4591
+ return scan.publicEnvVarPrefix;
4592
+ }
4593
+ ) : Promise.resolve(scan.publicEnvVarPrefix);
4583
4594
  const selected = ctx.getStepOutput(
4584
4595
  "select-index"
4585
4596
  );
@@ -4667,14 +4678,20 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4667
4678
  );
4668
4679
  }
4669
4680
  }
4681
+ const publicEnvVarPrefix = await publicEnvVarPrefixPromise;
4670
4682
  const input = {
4671
4683
  findings: normalized,
4672
4684
  confirmed: confirmed2,
4673
4685
  searchLocation,
4674
4686
  targetIndex,
4675
4687
  language,
4688
+ publicEnvVarPrefix,
4676
4689
  appId,
4677
- searchEnvVars: searchEnvVars(language, targetIndex, appId),
4690
+ searchEnvVars: publicSearchEnvVars(
4691
+ publicEnvVarPrefix,
4692
+ targetIndex,
4693
+ appId
4694
+ ),
4678
4695
  ingestDir: INGEST_DIR,
4679
4696
  ingestionSource,
4680
4697
  uploadFilePath,
@@ -4685,13 +4702,21 @@ async function implement(ctx, useCases = DEFAULT_IMPLEMENT_USE_CASES, existingWo
4685
4702
  let envSearchKey;
4686
4703
  let envAppIdMismatch = false;
4687
4704
  if (useCases.includes("search") && appId) {
4688
- const envAppId = await readEnvVar(worktree, appIdVar(language));
4705
+ const envAppId = await readEnvVar(
4706
+ worktree,
4707
+ publicAppIdVar(publicEnvVarPrefix)
4708
+ );
4689
4709
  if (envAppId === appId) {
4690
- envSearchKey = await readEnvVar(worktree, searchKeyVar(language));
4710
+ envSearchKey = await readEnvVar(
4711
+ worktree,
4712
+ publicSearchKeyVar(publicEnvVarPrefix)
4713
+ );
4691
4714
  } else if (envAppId) {
4692
4715
  envAppIdMismatch = true;
4716
+ const appIdVarName = publicAppIdVar(publicEnvVarPrefix);
4717
+ const searchKeyVarName = publicSearchKeyVar(publicEnvVarPrefix);
4693
4718
  summaries.push(
4694
- `\u26A0\uFE0F .env already sets ${appIdVar(language)}=${envAppId}, but the active Algolia application is ${appId}. The wizard left those values alone \u2014 update ${appIdVar(language)} and ${searchKeyVar(language)} by hand, or searches will fail.`
4719
+ `\u26A0\uFE0F .env already sets ${appIdVarName}=${envAppId}, but the active Algolia application is ${appId}. The wizard left those values alone \u2014 update ${appIdVarName} and ${searchKeyVarName} by hand, or searches will fail.`
4695
4720
  );
4696
4721
  logger.warn(
4697
4722
  { envAppId, appId },
@@ -4877,8 +4902,8 @@ ${detail}` : ""}`
4877
4902
  );
4878
4903
  }
4879
4904
  }
4880
- finalSearchEnvVars = searchEnvVars(
4881
- language,
4905
+ finalSearchEnvVars = publicSearchEnvVars(
4906
+ publicEnvVarPrefix,
4882
4907
  targetIndex,
4883
4908
  appId,
4884
4909
  searchKey
@@ -4990,8 +5015,8 @@ var defaultWorkflow = {
4990
5015
  defineStep({
4991
5016
  id: "select-index",
4992
5017
  title: "Set up index",
4993
- outputSchema: z29.object({
4994
- selection: z29.string()
5018
+ outputSchema: z30.object({
5019
+ selection: z30.string()
4995
5020
  }),
4996
5021
  run: (ctx) => selectIndexStep(ctx)
4997
5022
  }),
@@ -5063,6 +5088,7 @@ function getWorkflow(id) {
5063
5088
  var projectScan2 = {
5064
5089
  languages: [{ name: "TypeScript", version: "5.7.2" }],
5065
5090
  frameworks: [{ name: "Next.js", version: "15.1.0" }],
5091
+ publicEnvVarPrefix: "NEXT_PUBLIC_",
5066
5092
  ingestionAnalysis: [
5067
5093
  {
5068
5094
  name: "Product",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@algolia/wizard",
3
- "version": "0.19.0",
3
+ "version": "0.20.0",
4
4
  "description": "Magically implement Algolia functionality in your codebase",
5
5
  "type": "module",
6
6
  "engines": {