@lukoweb/apitogo 0.1.15 → 0.1.17

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/cli/cli.js CHANGED
@@ -131,7 +131,7 @@ import { stat } from "node:fs/promises";
131
131
  var fileExists;
132
132
  var init_file_exists = __esm({
133
133
  "src/config/file-exists.ts"() {
134
- fileExists = (path32) => stat(path32).then(() => true).catch(() => false);
134
+ fileExists = (path33) => stat(path33).then(() => true).catch(() => false);
135
135
  }
136
136
  });
137
137
 
@@ -3822,7 +3822,7 @@ import {
3822
3822
  // package.json
3823
3823
  var package_default = {
3824
3824
  name: "@lukoweb/apitogo",
3825
- version: "0.1.15",
3825
+ version: "0.1.17",
3826
3826
  type: "module",
3827
3827
  sideEffects: [
3828
3828
  "**/*.css",
@@ -4533,14 +4533,14 @@ var flattenAllOf = (schema2) => {
4533
4533
  };
4534
4534
 
4535
4535
  // src/lib/util/traverse.ts
4536
- var traverse = (specification, transform, path32 = []) => {
4537
- const transformed = transform(specification, path32);
4536
+ var traverse = (specification, transform, path33 = []) => {
4537
+ const transformed = transform(specification, path33);
4538
4538
  if (typeof transformed !== "object" || transformed === null) {
4539
4539
  return transformed;
4540
4540
  }
4541
4541
  const result = Array.isArray(transformed) ? [] : {};
4542
4542
  for (const [key, value] of Object.entries(transformed)) {
4543
- const currentPath = [...path32, key];
4543
+ const currentPath = [...path33, key];
4544
4544
  if (Array.isArray(value)) {
4545
4545
  result[key] = value.map(
4546
4546
  (item, index) => typeof item === "object" && item != null ? traverse(item, transform, [...currentPath, index.toString()]) : item
@@ -4568,9 +4568,9 @@ var resolveLocalRef = (schema2, ref) => {
4568
4568
  if (schemaCache?.has(ref)) {
4569
4569
  return schemaCache.get(ref);
4570
4570
  }
4571
- const path32 = ref.split("/").slice(1);
4571
+ const path33 = ref.split("/").slice(1);
4572
4572
  let current = schema2;
4573
- for (const segment of path32) {
4573
+ for (const segment of path33) {
4574
4574
  if (!current || typeof current !== "object") {
4575
4575
  current = null;
4576
4576
  }
@@ -4589,7 +4589,7 @@ var dereference = async (schema2, resolvers = []) => {
4589
4589
  }
4590
4590
  const cloned = structuredClone(schema2);
4591
4591
  const visited = /* @__PURE__ */ new Set();
4592
- const resolve = async (current, path32) => {
4592
+ const resolve = async (current, path33) => {
4593
4593
  if (isIndexableObject(current)) {
4594
4594
  if (visited.has(current)) {
4595
4595
  return CIRCULAR_REF;
@@ -4597,7 +4597,7 @@ var dereference = async (schema2, resolvers = []) => {
4597
4597
  visited.add(current);
4598
4598
  if (Array.isArray(current)) {
4599
4599
  for (let index = 0; index < current.length; index++) {
4600
- current[index] = await resolve(current[index], `${path32}/${index}`);
4600
+ current[index] = await resolve(current[index], `${path33}/${index}`);
4601
4601
  }
4602
4602
  } else {
4603
4603
  if ("$ref" in current && typeof current.$ref === "string") {
@@ -4608,13 +4608,13 @@ var dereference = async (schema2, resolvers = []) => {
4608
4608
  for (const resolver of resolvers) {
4609
4609
  const resolved = await resolver($ref);
4610
4610
  if (resolved) {
4611
- result2 = await resolve(resolved, path32);
4611
+ result2 = await resolve(resolved, path33);
4612
4612
  break;
4613
4613
  }
4614
4614
  }
4615
4615
  if (result2 === void 0) {
4616
4616
  const resolved = await resolveLocalRef(cloned, $ref);
4617
- result2 = await resolve(resolved, path32);
4617
+ result2 = await resolve(resolved, path33);
4618
4618
  }
4619
4619
  if (hasSiblings) {
4620
4620
  if (result2 === CIRCULAR_REF) {
@@ -4627,7 +4627,7 @@ var dereference = async (schema2, resolvers = []) => {
4627
4627
  return result2;
4628
4628
  }
4629
4629
  for (const key in current) {
4630
- current[key] = await resolve(current[key], `${path32}/${key}`);
4630
+ current[key] = await resolve(current[key], `${path33}/${key}`);
4631
4631
  }
4632
4632
  }
4633
4633
  visited.delete(current);
@@ -4666,9 +4666,9 @@ var upgradeSchema = (schema2) => {
4666
4666
  }
4667
4667
  return sub;
4668
4668
  });
4669
- schema2 = traverse(schema2, (sub, path32) => {
4669
+ schema2 = traverse(schema2, (sub, path33) => {
4670
4670
  if (sub.example !== void 0) {
4671
- if (isSchemaPath(path32 ?? [])) {
4671
+ if (isSchemaPath(path33 ?? [])) {
4672
4672
  sub.examples = [sub.example];
4673
4673
  } else {
4674
4674
  sub.examples = {
@@ -4681,11 +4681,11 @@ var upgradeSchema = (schema2) => {
4681
4681
  }
4682
4682
  return sub;
4683
4683
  });
4684
- schema2 = traverse(schema2, (schema3, path32) => {
4684
+ schema2 = traverse(schema2, (schema3, path33) => {
4685
4685
  if (schema3.type === "object" && schema3.properties !== void 0) {
4686
- const parentPath = path32?.slice(0, -1);
4686
+ const parentPath = path33?.slice(0, -1);
4687
4687
  const isMultipart = parentPath?.some((segment, index) => {
4688
- return segment === "content" && path32?.[index + 1] === "multipart/form-data";
4688
+ return segment === "content" && path33?.[index + 1] === "multipart/form-data";
4689
4689
  });
4690
4690
  if (isMultipart) {
4691
4691
  const entries = Object.entries(schema3.properties);
@@ -4699,8 +4699,8 @@ var upgradeSchema = (schema2) => {
4699
4699
  }
4700
4700
  return schema3;
4701
4701
  });
4702
- schema2 = traverse(schema2, (schema3, path32) => {
4703
- if (path32?.includes("content") && path32.includes("application/octet-stream")) {
4702
+ schema2 = traverse(schema2, (schema3, path33) => {
4703
+ if (path33?.includes("content") && path33.includes("application/octet-stream")) {
4704
4704
  return {};
4705
4705
  }
4706
4706
  if (schema3.type === "string" && schema3.format === "binary") {
@@ -4726,11 +4726,11 @@ var upgradeSchema = (schema2) => {
4726
4726
  }
4727
4727
  return sub;
4728
4728
  });
4729
- schema2 = traverse(schema2, (schema3, path32) => {
4729
+ schema2 = traverse(schema2, (schema3, path33) => {
4730
4730
  if (schema3.type === "string" && schema3.format === "byte") {
4731
- const parentPath = path32?.slice(0, -1);
4731
+ const parentPath = path33?.slice(0, -1);
4732
4732
  const contentMediaType = parentPath?.find(
4733
- (_, index) => path32?.[index - 1] === "content"
4733
+ (_, index) => path33?.[index - 1] === "content"
4734
4734
  );
4735
4735
  return {
4736
4736
  type: "string",
@@ -4742,7 +4742,7 @@ var upgradeSchema = (schema2) => {
4742
4742
  });
4743
4743
  return schema2;
4744
4744
  };
4745
- function isSchemaPath(path32) {
4745
+ function isSchemaPath(path33) {
4746
4746
  const schemaLocations = [
4747
4747
  ["components", "schemas"],
4748
4748
  "properties",
@@ -4755,10 +4755,10 @@ function isSchemaPath(path32) {
4755
4755
  ];
4756
4756
  return schemaLocations.some((location) => {
4757
4757
  if (Array.isArray(location)) {
4758
- return location.every((segment, index) => path32[index] === segment);
4758
+ return location.every((segment, index) => path33[index] === segment);
4759
4759
  }
4760
- return path32.includes(location);
4761
- }) || path32.includes("schema") || path32.some((segment) => segment.endsWith("Schema"));
4760
+ return path33.includes(location);
4761
+ }) || path33.includes("schema") || path33.some((segment) => segment.endsWith("Schema"));
4762
4762
  }
4763
4763
 
4764
4764
  // src/lib/oas/parser/index.ts
@@ -4840,13 +4840,13 @@ var OPENAPI_PROPS = /* @__PURE__ */ new Set([
4840
4840
  "anyOf",
4841
4841
  "oneOf"
4842
4842
  ]);
4843
- var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path32 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
4843
+ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs = /* @__PURE__ */ new WeakMap(), path33 = [], currentRefPaths = /* @__PURE__ */ new Set()) => {
4844
4844
  if (obj === null || typeof obj !== "object") return obj;
4845
4845
  const refPath = obj.__$ref;
4846
4846
  const isCircular = currentPath.has(obj) || typeof refPath === "string" && currentRefPaths.has(refPath);
4847
4847
  if (isCircular) {
4848
4848
  if (typeof refPath === "string") return SCHEMA_REF_PREFIX + refPath;
4849
- const circularProp = path32.find((p) => !OPENAPI_PROPS.has(p)) || path32[0];
4849
+ const circularProp = path33.find((p) => !OPENAPI_PROPS.has(p)) || path33[0];
4850
4850
  return [CIRCULAR_REF, circularProp].filter(Boolean).join(":");
4851
4851
  }
4852
4852
  if (refs.has(obj)) return refs.get(obj);
@@ -4856,7 +4856,7 @@ var handleCircularRefs = (obj, currentPath = /* @__PURE__ */ new WeakSet(), refs
4856
4856
  value,
4857
4857
  currentPath,
4858
4858
  refs,
4859
- [...path32, key],
4859
+ [...path33, key],
4860
4860
  currentRefPaths
4861
4861
  );
4862
4862
  const result = Array.isArray(obj) ? obj.map((item, i) => recurse(item, i.toString())) : Object.fromEntries(
@@ -4891,7 +4891,7 @@ var resolveExtensions = (obj) => Object.fromEntries(
4891
4891
  var getAllTags = (schema2) => {
4892
4892
  const rootTags = schema2.tags ?? [];
4893
4893
  const operations = Object.values(schema2.paths ?? {}).flatMap(
4894
- (path32) => HttpMethods.map((k) => path32?.[k]).filter((op) => op != null)
4894
+ (path33) => HttpMethods.map((k) => path33?.[k]).filter((op) => op != null)
4895
4895
  );
4896
4896
  const operationTags = new Set(operations.flatMap((op) => op.tags ?? []));
4897
4897
  const hasUntaggedOperations = operations.some(
@@ -4946,7 +4946,7 @@ var getAllSlugs = (ops, schemaTags = []) => {
4946
4946
  var getOperationSlugKey = (op) => [op.path, op.method, op.operationId, op.summary].filter(Boolean).join("-");
4947
4947
  var getAllOperations = (paths) => {
4948
4948
  const operations = Object.entries(paths ?? {}).flatMap(
4949
- ([path32, value]) => HttpMethods.flatMap((method) => {
4949
+ ([path33, value]) => HttpMethods.flatMap((method) => {
4950
4950
  if (!value?.[method]) return [];
4951
4951
  const operation = value[method];
4952
4952
  const pathParameters = value.parameters ?? [];
@@ -4966,7 +4966,7 @@ var getAllOperations = (paths) => {
4966
4966
  return {
4967
4967
  ...operation,
4968
4968
  method,
4969
- path: path32,
4969
+ path: path33,
4970
4970
  parameters,
4971
4971
  servers,
4972
4972
  tags: operation.tags ?? []
@@ -5324,8 +5324,8 @@ var Schema = builder.objectRef("Schema").implement({
5324
5324
  }),
5325
5325
  paths: t.field({
5326
5326
  type: [PathItem],
5327
- resolve: (root) => Object.entries(root.paths ?? {}).map(([path32, value]) => ({
5328
- path: path32,
5327
+ resolve: (root) => Object.entries(root.paths ?? {}).map(([path33, value]) => ({
5328
+ path: path33,
5329
5329
  // biome-ignore lint/style/noNonNullAssertion: value is guaranteed to be defined
5330
5330
  methods: Object.keys(value)
5331
5331
  }))
@@ -5472,7 +5472,7 @@ init_joinUrl();
5472
5472
 
5473
5473
  // src/vite/api/schema-codegen.ts
5474
5474
  var unescapeJsonPointer = (uri) => decodeURIComponent(uri.replace(/~1/g, "/").replace(/~0/g, "~"));
5475
- var getSegmentsFromPath = (path32) => path32.split("/").slice(1).map(unescapeJsonPointer);
5475
+ var getSegmentsFromPath = (path33) => path33.split("/").slice(1).map(unescapeJsonPointer);
5476
5476
  var createLocalRefMap = (obj) => {
5477
5477
  const refMap = /* @__PURE__ */ new Map();
5478
5478
  const siblingsMap = /* @__PURE__ */ new Map();
@@ -5503,16 +5503,16 @@ var replaceMarkers = (code, mergedRefs) => code.replace(/"__refMap:(.*?)"/g, '__
5503
5503
  /"__refMap\+Siblings:(.*?)"/g,
5504
5504
  (_, key) => mergedRefs.get(key) ?? `__refMap["${key}"]`
5505
5505
  );
5506
- var lookup = (schema2, path32, filePath) => {
5507
- const parts = getSegmentsFromPath(path32);
5506
+ var lookup = (schema2, path33, filePath) => {
5507
+ const parts = getSegmentsFromPath(path33);
5508
5508
  let val = schema2;
5509
5509
  for (const part of parts) {
5510
5510
  while (val.$ref?.startsWith("#/")) {
5511
- val = val.$ref === path32 ? val : lookup(schema2, val.$ref, filePath);
5511
+ val = val.$ref === path33 ? val : lookup(schema2, val.$ref, filePath);
5512
5512
  }
5513
5513
  if (val[part] === void 0) {
5514
5514
  throw new Error(
5515
- `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path32}`
5515
+ `Error in ${filePath ?? "code generation"}: Could not find path segment ${part} in path: ${path33}`
5516
5516
  );
5517
5517
  }
5518
5518
  val = val[part];
@@ -5757,8 +5757,8 @@ var SchemaManager = class {
5757
5757
  }
5758
5758
  }
5759
5759
  };
5760
- getLatestSchema = (path32) => this.processedSchemas[path32]?.at(0);
5761
- getSchemasForPath = (path32) => this.processedSchemas[path32];
5760
+ getLatestSchema = (path33) => this.processedSchemas[path33]?.at(0);
5761
+ getSchemasForPath = (path33) => this.processedSchemas[path33];
5762
5762
  getSchemaImports = () => Object.values(this.processedSchemas).flat().filter((s) => s.importKey);
5763
5763
  getUrlToFilePathMap = () => {
5764
5764
  const map = /* @__PURE__ */ new Map();
@@ -7847,14 +7847,14 @@ async function generateSitemap({
7847
7847
 
7848
7848
  // src/vite/prerender/utils.ts
7849
7849
  init_joinUrl();
7850
- var resolveRoutePath = (path32) => {
7851
- const segments = path32.split("/");
7850
+ var resolveRoutePath = (path33) => {
7851
+ const segments = path33.split("/");
7852
7852
  if (segments.some((s) => s.startsWith(":") && !s.endsWith("?"))) {
7853
7853
  return void 0;
7854
7854
  }
7855
7855
  return segments.filter((s) => !s.startsWith(":")).join("/") || void 0;
7856
7856
  };
7857
- var isSkipped = (path32) => path32.includes("*") || /^\d+$/.test(path32);
7857
+ var isSkipped = (path33) => path33.includes("*") || /^\d+$/.test(path33);
7858
7858
  var resolveRoutes = (routes, parentPath = "") => routes.flatMap((route) => {
7859
7859
  if (route.path && isSkipped(route.path)) return [];
7860
7860
  const routePath = route.path ? resolveRoutePath(route.path) : void 0;
@@ -8006,7 +8006,9 @@ var prerender = async ({
8006
8006
  }
8007
8007
  if (pagefindWriteResult?.outputPath) {
8008
8008
  logger.info(
8009
- colors7.blue(`\u2713 pagefind index built: ${pagefindWriteResult.outputPath}`)
8009
+ colors7.blue(
8010
+ `\u2713 pagefind index built: ${pagefindWriteResult.outputPath}`
8011
+ )
8010
8012
  );
8011
8013
  }
8012
8014
  }
@@ -8455,8 +8457,8 @@ var build_default = {
8455
8457
  };
8456
8458
 
8457
8459
  // src/cli/configure-github-workflow/handler.ts
8458
- import { access, mkdir as mkdir6, writeFile as writeFile6 } from "node:fs/promises";
8459
8460
  import { constants } from "node:fs";
8461
+ import { access, mkdir as mkdir6, writeFile as writeFile6 } from "node:fs/promises";
8460
8462
  import path25 from "node:path";
8461
8463
  var APITOGO_DEPLOY_WORKFLOW_YAML = `name: Build and Deploy
8462
8464
 
@@ -8602,9 +8604,7 @@ function isDeployJsonOnlyArgv() {
8602
8604
  if (deployIdx === -1) {
8603
8605
  return false;
8604
8606
  }
8605
- return args.slice(deployIdx).some(
8606
- (a) => a === "--json-only" || a.startsWith("--json-only=")
8607
- );
8607
+ return args.slice(deployIdx).some((a) => a === "--json-only" || a.startsWith("--json-only="));
8608
8608
  }
8609
8609
  function isJsonOnlyDeployEnv() {
8610
8610
  return process.env.APITOGO_JSON_ONLY === "1";
@@ -9144,32 +9144,231 @@ var dev_default = {
9144
9144
  }
9145
9145
  };
9146
9146
 
9147
- // src/cli/cmds/preview.ts
9148
- var previewCommand = {
9149
- desc: "Preview production build",
9150
- command: "preview",
9151
- builder: (yargs2) => yargs2.option("dir", {
9152
- type: "string",
9153
- describe: "The directory containing your project",
9154
- default: ".",
9155
- normalize: true,
9156
- hidden: true
9157
- }).option("port", {
9158
- type: "number",
9159
- describe: "The port to run the local server on"
9160
- }),
9161
- handler: async (argv) => {
9162
- process.env.NODE_ENV = "production";
9163
- await captureEvent({ argv, event: "apitogo preview" });
9164
- await preview(argv);
9165
- }
9166
- };
9167
- var preview_default = previewCommand;
9147
+ // src/cli/make-config/handler.ts
9148
+ import { readFile as readFile6, writeFile as writeFile8 } from "node:fs/promises";
9149
+ import path31 from "node:path";
9150
+ import { glob as glob5 } from "glob";
9168
9151
 
9169
- // src/cli/sync-config/handler.ts
9170
- import { readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
9152
+ // src/cli/make-config/scaffold-project.ts
9153
+ init_package_json();
9154
+ import { access as access3, readFile as readFile5, writeFile as writeFile7 } from "node:fs/promises";
9155
+ import { constants as constants2 } from "node:fs";
9171
9156
  import path30 from "node:path";
9172
9157
  import { glob as glob4 } from "glob";
9158
+ var OPENAPI_GLOBS = [
9159
+ "apis/openapi.json",
9160
+ "apis/openapi.yaml",
9161
+ "apis/openapi.yml",
9162
+ "openapi.json",
9163
+ "openapi.yaml",
9164
+ "openapi.yml"
9165
+ ];
9166
+ var findMatchingCurly = (source, startIndex) => {
9167
+ let depth = 0;
9168
+ let inSingleQuote = false;
9169
+ let inDoubleQuote = false;
9170
+ let inTemplate = false;
9171
+ let inLineComment = false;
9172
+ let inBlockComment = false;
9173
+ for (let index = startIndex; index < source.length; index++) {
9174
+ const char = source[index];
9175
+ const next = source[index + 1];
9176
+ const prev = source[index - 1];
9177
+ if (inLineComment) {
9178
+ if (char === "\n") inLineComment = false;
9179
+ continue;
9180
+ }
9181
+ if (inBlockComment) {
9182
+ if (prev === "*" && char === "/") inBlockComment = false;
9183
+ continue;
9184
+ }
9185
+ if (!inSingleQuote && !inDoubleQuote && !inTemplate) {
9186
+ if (char === "/" && next === "/") {
9187
+ inLineComment = true;
9188
+ index++;
9189
+ continue;
9190
+ }
9191
+ if (char === "/" && next === "*") {
9192
+ inBlockComment = true;
9193
+ index++;
9194
+ continue;
9195
+ }
9196
+ }
9197
+ if (!inDoubleQuote && !inTemplate && char === "'" && prev !== "\\") {
9198
+ inSingleQuote = !inSingleQuote;
9199
+ continue;
9200
+ }
9201
+ if (!inSingleQuote && !inTemplate && char === '"' && prev !== "\\") {
9202
+ inDoubleQuote = !inDoubleQuote;
9203
+ continue;
9204
+ }
9205
+ if (!inSingleQuote && !inDoubleQuote && char === "`" && prev !== "\\") {
9206
+ inTemplate = !inTemplate;
9207
+ continue;
9208
+ }
9209
+ if (inSingleQuote || inDoubleQuote || inTemplate) continue;
9210
+ if (char === "{") depth++;
9211
+ if (char === "}") depth--;
9212
+ if (depth === 0) return index;
9213
+ }
9214
+ throw new Error("Could not find matching closing brace for object.");
9215
+ };
9216
+ var toPosix = (p) => p.split(path30.sep).join("/");
9217
+ var sanitizePackageName = (dir) => {
9218
+ const base = path30.basename(path30.resolve(dir));
9219
+ const s = base.toLowerCase().replace(/[^a-z0-9-_.]/g, "-").replace(/^-+|-+$/g, "");
9220
+ return s || "apitogo-site";
9221
+ };
9222
+ var apitogoVersionRange = () => {
9223
+ const v = getZudokuPackageJson()?.version;
9224
+ return v ? `^${v}` : "latest";
9225
+ };
9226
+ async function findOpenApiSpecPath(dir) {
9227
+ for (const rel of OPENAPI_GLOBS) {
9228
+ const full = path30.join(dir, rel);
9229
+ try {
9230
+ await access3(full, constants2.R_OK);
9231
+ return toPosix(rel);
9232
+ } catch {
9233
+ }
9234
+ }
9235
+ const extra = await glob4("apis/**/*.{json,yaml,yml}", {
9236
+ cwd: dir,
9237
+ posix: true,
9238
+ ignore: ["**/node_modules/**"]
9239
+ });
9240
+ return extra[0] ? toPosix(extra[0]) : null;
9241
+ }
9242
+ async function ensurePackageJson(dir) {
9243
+ const pkgPath = path30.join(dir, "package.json");
9244
+ const apitogoVer = apitogoVersionRange();
9245
+ const baseDeps = {
9246
+ react: ">=19.0.0",
9247
+ "react-dom": ">=19.0.0",
9248
+ "@lukoweb/apitogo": apitogoVer
9249
+ };
9250
+ const baseScripts = {
9251
+ dev: "apitogo dev",
9252
+ build: "apitogo build",
9253
+ preview: "apitogo preview"
9254
+ };
9255
+ try {
9256
+ await access3(pkgPath, constants2.R_OK);
9257
+ } catch (err) {
9258
+ const code = err.code;
9259
+ if (code !== "ENOENT") throw err;
9260
+ const pkg2 = {
9261
+ name: sanitizePackageName(dir),
9262
+ version: "0.1.0",
9263
+ private: true,
9264
+ type: "module",
9265
+ scripts: baseScripts,
9266
+ dependencies: baseDeps,
9267
+ devDependencies: {
9268
+ "@types/node": "^22",
9269
+ "@types/react": "^19",
9270
+ "@types/react-dom": "^19",
9271
+ typescript: "^5"
9272
+ }
9273
+ };
9274
+ await writeFile7(pkgPath, `${JSON.stringify(pkg2, null, 2)}
9275
+ `, "utf8");
9276
+ return true;
9277
+ }
9278
+ const raw = await readFile5(pkgPath, "utf8");
9279
+ let pkg;
9280
+ try {
9281
+ pkg = JSON.parse(raw);
9282
+ } catch {
9283
+ throw new Error("package.json exists but is not valid JSON; fix or remove it and run again.");
9284
+ }
9285
+ pkg.scripts = {
9286
+ ...typeof pkg.scripts === "object" && pkg.scripts !== null ? pkg.scripts : {}
9287
+ };
9288
+ const scripts = pkg.scripts;
9289
+ for (const [k, v] of Object.entries(baseScripts)) {
9290
+ if (!scripts[k]) scripts[k] = v;
9291
+ }
9292
+ pkg.dependencies = {
9293
+ ...typeof pkg.dependencies === "object" && pkg.dependencies !== null ? pkg.dependencies : {}
9294
+ };
9295
+ const deps = pkg.dependencies;
9296
+ for (const [k, v] of Object.entries(baseDeps)) {
9297
+ if (!deps[k]) deps[k] = v;
9298
+ }
9299
+ await writeFile7(pkgPath, `${JSON.stringify(pkg, null, 2)}
9300
+ `, "utf8");
9301
+ return false;
9302
+ }
9303
+ async function ensureTsconfigJson(dir) {
9304
+ const tsPath = path30.join(dir, "tsconfig.json");
9305
+ try {
9306
+ await access3(tsPath, constants2.R_OK);
9307
+ return false;
9308
+ } catch {
9309
+ const tsconfig = {
9310
+ compilerOptions: {
9311
+ target: "ES2022",
9312
+ lib: ["ESNext", "DOM", "DOM.Iterable", "WebWorker"],
9313
+ module: "ESNext",
9314
+ moduleResolution: "Bundler",
9315
+ useDefineForClassFields: true,
9316
+ skipLibCheck: true,
9317
+ skipDefaultLibCheck: true,
9318
+ resolveJsonModule: true,
9319
+ isolatedModules: true,
9320
+ useUnknownInCatchVariables: false,
9321
+ jsx: "react-jsx"
9322
+ },
9323
+ include: [
9324
+ "*.config.ts",
9325
+ "*.config.tsx",
9326
+ "*.config.jsx",
9327
+ "*.config.js",
9328
+ "*.config.mjs",
9329
+ "pages"
9330
+ ]
9331
+ };
9332
+ await writeFile7(tsPath, `${JSON.stringify(tsconfig, null, 2)}
9333
+ `, "utf8");
9334
+ return true;
9335
+ }
9336
+ }
9337
+ function insertApisIfMissing(source, openapiInput) {
9338
+ if (/\bapis\s*:/.test(source)) return source;
9339
+ const apisBlock = [
9340
+ " apis: {",
9341
+ ' type: "file",',
9342
+ ` input: "${openapiInput.replace(/"/g, '\\"')}",`,
9343
+ ' path: "api",',
9344
+ " },"
9345
+ ].join("\n");
9346
+ const docsMatch = source.match(/\bdocs\s*:\s*\{/);
9347
+ if (!docsMatch || docsMatch.index === void 0) {
9348
+ const m = source.match(/(const\s+config\s*:\s*ApitogoConfig\s*=\s*\{)/);
9349
+ if (!m || m.index === void 0) {
9350
+ return source;
9351
+ }
9352
+ const open = m.index + m[0].length;
9353
+ return `${source.slice(0, open)}
9354
+ ${apisBlock}
9355
+ ${source.slice(open)}`;
9356
+ }
9357
+ const openBrace = source.indexOf("{", docsMatch.index);
9358
+ const closeBrace = findMatchingCurly(source, openBrace);
9359
+ let p = closeBrace + 1;
9360
+ while (p < source.length && /\s/.test(source[p])) p++;
9361
+ if (source[p] === ",") p++;
9362
+ while (p < source.length && /\s/.test(source[p])) p++;
9363
+ return `${source.slice(0, p)}${apisBlock}
9364
+ ${source.slice(p)}`;
9365
+ }
9366
+ function openapiInputForConfig(specRelativePosix) {
9367
+ if (specRelativePosix.startsWith("./")) return specRelativePosix;
9368
+ return `./${specRelativePosix}`;
9369
+ }
9370
+
9371
+ // src/cli/make-config/handler.ts
9173
9372
  var CONFIG_FILENAMES = [
9174
9373
  "apitogo.config.tsx",
9175
9374
  "apitogo.config.ts",
@@ -9182,8 +9381,9 @@ var CONFIG_FILENAMES = [
9182
9381
  "zudoku.config.js",
9183
9382
  "zudoku.config.mjs"
9184
9383
  ];
9384
+ var DEFAULT_NEW_CONFIG_FILENAME = "apitogo.config.tsx";
9185
9385
  var createTreeNode = () => ({ docs: [], children: /* @__PURE__ */ new Map() });
9186
- var toPosixPath2 = (value) => value.split(path30.sep).join(path30.posix.sep);
9386
+ var toPosixPath2 = (value) => value.split(path31.sep).join(path31.posix.sep);
9187
9387
  var toRoutePath = (relativeFile, pagesDir) => {
9188
9388
  const relativeNoExt = relativeFile.replace(/\.mdx?$/, "");
9189
9389
  const withoutPagesPrefix = relativeNoExt.replace(
@@ -9349,37 +9549,122 @@ var findMatchingBracket = (source, startIndex) => {
9349
9549
  }
9350
9550
  throw new Error("Could not find matching closing bracket.");
9351
9551
  };
9352
- var replaceArrayProperty = (source, propertyName, replacement) => {
9353
- const propertyIndex = source.indexOf(`${propertyName}:`);
9354
- if (propertyIndex === -1) {
9552
+ var findPropertyArrayBounds = (source, propertyName) => {
9553
+ const re = new RegExp(`\\b${propertyName}\\s*:`);
9554
+ const match = source.match(re);
9555
+ if (!match || match.index === void 0) {
9355
9556
  throw new Error(`Could not find '${propertyName}' in config file.`);
9356
9557
  }
9357
- const arrayStart = source.indexOf("[", propertyIndex);
9558
+ const arrayStart = source.indexOf("[", match.index);
9358
9559
  if (arrayStart === -1) {
9359
- throw new Error(`Could not find array start for '${propertyName}'.`);
9560
+ throw new Error(
9561
+ `Could not find array value for '${propertyName}' (expected [...]).`
9562
+ );
9360
9563
  }
9361
9564
  const arrayEnd = findMatchingBracket(source, arrayStart);
9565
+ return { arrayStart, arrayEnd };
9566
+ };
9567
+ var replaceArrayProperty = (source, propertyName, replacement) => {
9568
+ const { arrayStart, arrayEnd } = findPropertyArrayBounds(
9569
+ source,
9570
+ propertyName
9571
+ );
9362
9572
  return `${source.slice(0, arrayStart)}${replacement}${source.slice(arrayEnd + 1)}`;
9363
9573
  };
9364
- var findConfigPath = async (dir) => {
9574
+ var insertNavigationAndRedirectsKeys = (source) => {
9575
+ const m = source.match(/(const\s+config\s*:\s*ApitogoConfig\s*=\s*\{)/);
9576
+ if (!m || m.index === void 0) {
9577
+ throw new Error(
9578
+ "Could not find 'const config: ApitogoConfig = {' \u2014 add navigation and redirects arrays manually."
9579
+ );
9580
+ }
9581
+ const insertAt = m.index + m[0].length;
9582
+ return `${source.slice(0, insertAt)}
9583
+ navigation: [],
9584
+ redirects: [],${source.slice(insertAt)}`;
9585
+ };
9586
+ var insertRedirectsAfterNavigation = (source) => {
9587
+ if (/\bredirects\s*:/.test(source)) return source;
9588
+ const { arrayEnd } = findPropertyArrayBounds(source, "navigation");
9589
+ let i = arrayEnd + 1;
9590
+ while (i < source.length && /\s/.test(source[i])) i++;
9591
+ if (source[i] === ",") {
9592
+ return `${source.slice(0, i + 1)}
9593
+ redirects: [],${source.slice(i + 1)}`;
9594
+ }
9595
+ return `${source.slice(0, arrayEnd + 1)},
9596
+ redirects: [],${source.slice(arrayEnd + 1)}`;
9597
+ };
9598
+ var ensureNavigationAndRedirectsForUpdate = (source) => {
9599
+ const hasNav = /\bnavigation\s*:/.test(source);
9600
+ const hasRedirects = /\bredirects\s*:/.test(source);
9601
+ if (hasNav && hasRedirects) return source;
9602
+ if (!hasNav && !hasRedirects) return insertNavigationAndRedirectsKeys(source);
9603
+ if (hasNav && !hasRedirects) return insertRedirectsAfterNavigation(source);
9604
+ throw new Error(
9605
+ "Found 'redirects' but not 'navigation'. Add a navigation: [...] array manually."
9606
+ );
9607
+ };
9608
+ var buildNewConfigContents = (pagesDir, openApiSpecRelative) => {
9609
+ const filesGlob = `/${pagesDir}/**/*.{md,mdx}`;
9610
+ const apisSection = openApiSpecRelative == null ? "" : `
9611
+ apis: {
9612
+ type: "file",
9613
+ input: "${openapiInputForConfig(openApiSpecRelative)}",
9614
+ path: "api",
9615
+ },`;
9616
+ return `import type { ApitogoConfig } from "@lukoweb/apitogo";
9617
+
9618
+ const config: ApitogoConfig = {
9619
+ docs: {
9620
+ files: "${filesGlob}",
9621
+ },${apisSection}
9622
+ navigation: [],
9623
+ redirects: [],
9624
+ };
9625
+
9626
+ export default config;
9627
+ `;
9628
+ };
9629
+ var findExistingConfigPath = async (dir) => {
9365
9630
  for (const filename of CONFIG_FILENAMES) {
9366
- const fullPath = path30.join(dir, filename);
9631
+ const fullPath = path31.join(dir, filename);
9367
9632
  try {
9368
- await readFile5(fullPath, "utf8");
9633
+ await readFile6(fullPath, "utf8");
9369
9634
  return fullPath;
9370
9635
  } catch {
9371
9636
  }
9372
9637
  }
9373
- throw new Error("No APIToGo config file found in the project root.");
9638
+ return null;
9374
9639
  };
9375
- async function syncConfig(argv) {
9376
- const dir = path30.resolve(process.cwd(), argv.dir);
9640
+ async function makeConfig(argv) {
9641
+ const dir = path31.resolve(process.cwd(), argv.dir);
9377
9642
  const pagesDir = toPosixPath2(
9378
9643
  argv.pagesDir.replace(/^[./]+/, "").replace(/\/+$/, "")
9379
9644
  );
9380
9645
  try {
9381
- const configPath = await findConfigPath(dir);
9382
- const pageFiles = await glob4(`${pagesDir}/**/*.{md,mdx}`, {
9646
+ const openApiSpecPath = await findOpenApiSpecPath(dir);
9647
+ const createdPackageJson = await ensurePackageJson(dir);
9648
+ const createdTsconfig = await ensureTsconfigJson(dir);
9649
+ if (createdPackageJson) {
9650
+ printDiagnosticsToConsole("Created package.json with dev, build, and preview scripts.");
9651
+ }
9652
+ if (createdTsconfig) {
9653
+ printDiagnosticsToConsole("Created tsconfig.json.");
9654
+ }
9655
+ let configPath = await findExistingConfigPath(dir);
9656
+ let createdNew = false;
9657
+ if (!configPath) {
9658
+ configPath = path31.join(dir, DEFAULT_NEW_CONFIG_FILENAME);
9659
+ await writeFile8(
9660
+ configPath,
9661
+ buildNewConfigContents(pagesDir, openApiSpecPath),
9662
+ "utf8"
9663
+ );
9664
+ createdNew = true;
9665
+ printDiagnosticsToConsole(`Created ${DEFAULT_NEW_CONFIG_FILENAME}`);
9666
+ }
9667
+ const pageFiles = await glob5(`${pagesDir}/**/*.{md,mdx}`, {
9383
9668
  cwd: dir,
9384
9669
  ignore: ["**/node_modules/**", "**/.git/**", "**/dist/**"],
9385
9670
  posix: true
@@ -9390,23 +9675,34 @@ async function syncConfig(argv) {
9390
9675
  if (routePaths.length === 0) {
9391
9676
  throw new Error(`No .md or .mdx files found under '${pagesDir}'.`);
9392
9677
  }
9393
- const tree = buildTree(routePaths);
9394
- const navigationItems = treeToNavigationItems(tree);
9395
- const originalConfig = await readFile5(configPath, "utf8");
9678
+ let originalConfig = await readFile6(configPath, "utf8");
9679
+ if (!createdNew && openApiSpecPath) {
9680
+ originalConfig = insertApisIfMissing(
9681
+ originalConfig,
9682
+ openapiInputForConfig(openApiSpecPath)
9683
+ );
9684
+ }
9685
+ originalConfig = ensureNavigationAndRedirectsForUpdate(originalConfig);
9396
9686
  const withNavigation = replaceArrayProperty(
9397
9687
  originalConfig,
9398
9688
  "navigation",
9399
- buildNavigationCode(navigationItems)
9689
+ buildNavigationCode(treeToNavigationItems(buildTree(routePaths)))
9400
9690
  );
9401
9691
  const withRedirects = replaceArrayProperty(
9402
9692
  withNavigation,
9403
9693
  "redirects",
9404
9694
  buildRedirectsCode(routePaths[0])
9405
9695
  );
9406
- await writeFile7(configPath, withRedirects, "utf8");
9696
+ await writeFile8(configPath, withRedirects, "utf8");
9697
+ const action = createdNew ? "Initialized" : "Updated";
9407
9698
  printResultToConsole(
9408
- `Updated ${path30.basename(configPath)} from ${pagesDir}/`
9699
+ `${action} ${path31.basename(configPath)} from ${pagesDir}/ (navigation and redirects; other top-level settings preserved unless newly scaffolded).`
9409
9700
  );
9701
+ if (createdPackageJson) {
9702
+ printDiagnosticsToConsole(
9703
+ "Next: run npm install (or pnpm install / yarn) in this directory, then npm run dev."
9704
+ );
9705
+ }
9410
9706
  } catch (error) {
9411
9707
  await printCriticalFailureToConsoleAndExit(
9412
9708
  error instanceof Error ? error.message : String(error)
@@ -9414,10 +9710,10 @@ async function syncConfig(argv) {
9414
9710
  }
9415
9711
  }
9416
9712
 
9417
- // src/cli/cmds/sync-config.ts
9418
- var sync_config_default = {
9419
- desc: "Regenerate navigation from the pages folder",
9420
- command: "sync-config",
9713
+ // src/cli/cmds/make-config.ts
9714
+ var make_config_default = {
9715
+ desc: "Create or update config navigation from the pages folder (other settings preserved)",
9716
+ command: "make-config",
9421
9717
  builder: (yargs2) => yargs2.option("dir", {
9422
9718
  type: "string",
9423
9719
  describe: "The directory containing your project",
@@ -9430,14 +9726,36 @@ var sync_config_default = {
9430
9726
  default: "pages"
9431
9727
  }),
9432
9728
  handler: async (argv) => {
9433
- await captureEvent({ argv, event: "apitogo sync-config" });
9434
- await syncConfig(argv);
9729
+ await captureEvent({ argv, event: "apitogo make-config" });
9730
+ await makeConfig(argv);
9731
+ }
9732
+ };
9733
+
9734
+ // src/cli/cmds/preview.ts
9735
+ var previewCommand = {
9736
+ desc: "Preview production build",
9737
+ command: "preview",
9738
+ builder: (yargs2) => yargs2.option("dir", {
9739
+ type: "string",
9740
+ describe: "The directory containing your project",
9741
+ default: ".",
9742
+ normalize: true,
9743
+ hidden: true
9744
+ }).option("port", {
9745
+ type: "number",
9746
+ describe: "The port to run the local server on"
9747
+ }),
9748
+ handler: async (argv) => {
9749
+ process.env.NODE_ENV = "production";
9750
+ await captureEvent({ argv, event: "apitogo preview" });
9751
+ await preview(argv);
9435
9752
  }
9436
9753
  };
9754
+ var preview_default = previewCommand;
9437
9755
 
9438
9756
  // src/cli/common/outdated.ts
9439
9757
  import { existsSync as existsSync2, mkdirSync } from "node:fs";
9440
- import { readFile as readFile6, writeFile as writeFile8 } from "node:fs/promises";
9758
+ import { readFile as readFile7, writeFile as writeFile9 } from "node:fs/promises";
9441
9759
  import { join } from "node:path";
9442
9760
  import colors10 from "picocolors";
9443
9761
  import { gt } from "semver";
@@ -9487,12 +9805,12 @@ function box(message, {
9487
9805
 
9488
9806
  // src/cli/common/xdg/lib.ts
9489
9807
  import { homedir } from "node:os";
9490
- import path31 from "node:path";
9808
+ import path32 from "node:path";
9491
9809
  function defineDirectoryWithFallback(xdgName, fallback) {
9492
9810
  if (process.env[xdgName]) {
9493
9811
  return process.env[xdgName];
9494
9812
  } else {
9495
- return path31.join(homedir(), fallback);
9813
+ return path32.join(homedir(), fallback);
9496
9814
  }
9497
9815
  }
9498
9816
  var XDG_CONFIG_HOME = defineDirectoryWithFallback(
@@ -9507,15 +9825,15 @@ var XDG_STATE_HOME = defineDirectoryWithFallback(
9507
9825
  "XDG_DATA_HOME",
9508
9826
  ".local/state"
9509
9827
  );
9510
- var ZUDOKU_XDG_CONFIG_HOME = path31.join(
9828
+ var ZUDOKU_XDG_CONFIG_HOME = path32.join(
9511
9829
  XDG_CONFIG_HOME,
9512
9830
  CLI_XDG_FOLDER_NAME
9513
9831
  );
9514
- var ZUDOKU_XDG_DATA_HOME = path31.join(
9832
+ var ZUDOKU_XDG_DATA_HOME = path32.join(
9515
9833
  XDG_DATA_HOME,
9516
9834
  CLI_XDG_FOLDER_NAME
9517
9835
  );
9518
- var ZUDOKU_XDG_STATE_HOME = path31.join(
9836
+ var ZUDOKU_XDG_STATE_HOME = path32.join(
9519
9837
  XDG_STATE_HOME,
9520
9838
  CLI_XDG_FOLDER_NAME
9521
9839
  );
@@ -9567,7 +9885,7 @@ async function getVersionCheckInfo() {
9567
9885
  let versionCheckInfo;
9568
9886
  if (existsSync2(versionCheckPath)) {
9569
9887
  try {
9570
- versionCheckInfo = await readFile6(versionCheckPath, "utf-8").then(
9888
+ versionCheckInfo = await readFile7(versionCheckPath, "utf-8").then(
9571
9889
  JSON.parse
9572
9890
  );
9573
9891
  } catch {
@@ -9592,7 +9910,7 @@ async function getVersionCheckInfo() {
9592
9910
  lastCheck: Date.now(),
9593
9911
  latestVersion
9594
9912
  };
9595
- await writeFile8(
9913
+ await writeFile9(
9596
9914
  versionCheckPath,
9597
9915
  JSON.stringify(versionCheckInfo),
9598
9916
  "utf-8"
@@ -9679,7 +9997,7 @@ var cli = yargs(hideBin(process.argv)).option("zuplo", {
9679
9997
  process.env.ZUPLO = "1";
9680
9998
  printDiagnosticsToConsole("Running in Zuplo mode");
9681
9999
  }
9682
- }).middleware(warnPackageVersionMismatch).command(build_default).command(configure_github_workflow_default).command(dev_default).command(deploy_default).command(preview_default).command(sync_config_default).demandCommand().strictCommands().version(packageJson?.version).fail(false).help();
10000
+ }).middleware(warnPackageVersionMismatch).command(build_default).command(configure_github_workflow_default).command(dev_default).command(deploy_default).command(preview_default).command(make_config_default).demandCommand().strictCommands().version(packageJson?.version).fail(false).help();
9683
10001
  try {
9684
10002
  void warnIfOutdatedVersion(packageJson?.version);
9685
10003
  await cli.argv;