routesync 1.0.15 → 1.0.16

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 (3) hide show
  1. package/README.md +14 -0
  2. package/dist/cli.js +36 -6
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -249,6 +249,20 @@ const api = defineApi({
249
249
  }, config)
250
250
  ```
251
251
 
252
+ > **Note on Laravel Auto-generation:** When using `routesync sync` or `routesync scan` with the `--zod` flag, RouteSync uses PHP Reflection to automatically generate Zod schemas based on your backend validation rules.
253
+ >
254
+ > **Important:** To ensure your schemas are detected automatically, you **must use Laravel `FormRequest` classes**. Inline `$request->validate([...])` calls inside Controller methods cannot be reliably extracted.
255
+ >
256
+ > ```php
257
+ > // ✅ DO THIS: RouteSync will generate Zod schemas automatically
258
+ > public function store(StoreProductRequest $request)
259
+ >
260
+ > // ❌ AVOID THIS: Validation rules will be ignored
261
+ > public function store(Request $request) {
262
+ > $request->validate([...]);
263
+ > }
264
+ > ```
265
+
252
266
  ---
253
267
 
254
268
  ### Auto-generate TanStack hooks from defineApi
package/dist/cli.js CHANGED
@@ -8406,6 +8406,31 @@ foreach ($routes as $route) {
8406
8406
  }
8407
8407
  }
8408
8408
  }
8409
+
8410
+ // Fallback: Try to parse $request->validate([...]) from source code
8411
+ if (empty($schema)) {
8412
+ $fileName = $reflector->getFileName();
8413
+ $startLine = $reflector->getStartLine();
8414
+ $endLine = $reflector->getEndLine();
8415
+
8416
+ if ($fileName && $startLine !== false && $endLine !== false) {
8417
+ $lines = file($fileName);
8418
+ // startLine is 1-indexed
8419
+ $methodSource = implode("", array_slice($lines, $startLine - 1, $endLine - $startLine + 1));
8420
+
8421
+ // Look for $request->validate([ ... ])
8422
+ if (preg_match('/\\\\$request->validate\\\\s*\\\\(\\\\s*\\\\[(.*?)\\\\]\\\\s*\\\\)/s', $methodSource, $matches)) {
8423
+ $rulesString = $matches[1];
8424
+ // Match 'field' => 'rules'
8425
+ preg_match_all('~[\\'"]([a-zA-Z0-9_.*]+)[\\'"]\\\\s*=>\\\\s*[\\'"](.*?)[\\'"]~', $rulesString, $ruleMatches);
8426
+ if (!empty($ruleMatches[1])) {
8427
+ foreach ($ruleMatches[1] as $index => $field) {
8428
+ $schema[$field] = $ruleMatches[2][$index];
8429
+ }
8430
+ }
8431
+ }
8432
+ }
8433
+ }
8409
8434
  } catch (\\Exception $e) {}
8410
8435
  }
8411
8436
  }
@@ -8751,6 +8776,11 @@ var TypeGenerator = class {
8751
8776
  const isOptional = col.nullable ? "?" : "";
8752
8777
  lines.push(` ${col.name}${isOptional}: ${tsType}`);
8753
8778
  }
8779
+ if (model.appends && model.appends.length > 0) {
8780
+ for (const append of model.appends) {
8781
+ lines.push(` ${append}?: unknown`);
8782
+ }
8783
+ }
8754
8784
  lines.push(`}`);
8755
8785
  lines.push(``);
8756
8786
  }
@@ -8771,16 +8801,16 @@ var TypeGenerator = class {
8771
8801
  }
8772
8802
  if (hasSchemas) {
8773
8803
  for (const route of manifest.routes) {
8774
- if (route.schema && Object.keys(route.schema).length > 0) {
8804
+ if (route.schema && route.schema.rules && Object.keys(route.schema.rules).length > 0) {
8775
8805
  const actionName = toMethodName(route);
8776
8806
  const schemaName = actionName + "Schema";
8777
8807
  lines.push(`export const ${schemaName} = z.object({`);
8778
- for (const [field, rules] of Object.entries(route.schema)) {
8808
+ for (const [field, rules] of Object.entries(route.schema.rules)) {
8779
8809
  const ruleStr = Array.isArray(rules) ? rules.join("|") : String(rules);
8780
8810
  let zodRule = "z.string()";
8781
- if (ruleStr.includes("numeric") || ruleStr.includes("integer")) {
8811
+ if (ruleStr.includes("numeric") || ruleStr.includes("integer") || ruleStr.includes("int")) {
8782
8812
  zodRule = "z.number()";
8783
- } else if (ruleStr.includes("boolean")) {
8813
+ } else if (ruleStr.includes("boolean") || ruleStr.includes("bool")) {
8784
8814
  zodRule = "z.boolean()";
8785
8815
  } else if (ruleStr.includes("array")) {
8786
8816
  zodRule = "z.array(z.unknown())";
@@ -9100,7 +9130,7 @@ var ModelGenerator = class {
9100
9130
  };
9101
9131
 
9102
9132
  // packages/cli/src/commands/generate.ts
9103
- var generateCommand = new Command("generate").description("Generate typed SDK, types, and hooks from route manifest").option("-m, --manifest <path>", "Path to route manifest", "routesync.manifest.json").option("-o, --output <path>", "Output directory", "src/api").option("--no-hooks", "Skip generating React hooks").option("--next-actions", "Generate Next.js Server Actions").option("--msw", "Generate MSW Mock Handlers").option("--echo", "Generate Laravel Echo Hooks").action(async (options) => {
9133
+ var generateCommand = new Command("generate").description("Generate typed SDK, types, and hooks from route manifest").option("-m, --manifest <path>", "Path to route manifest", "routesync.manifest.json").option("-o, --output <path>", "Output directory", "src/api").option("--no-hooks", "Skip generating React hooks").option("--next-actions", "Generate Next.js Server Actions").option("--msw", "Generate MSW Mock Handlers").option("--echo", "Generate Laravel Echo Hooks").option("--zod", "Generate Zod schemas for validation").action(async (options) => {
9104
9134
  const spinner = ora("Generating SDK...").start();
9105
9135
  try {
9106
9136
  if (!import_fs_extra11.default.existsSync(options.manifest)) {
@@ -9113,7 +9143,7 @@ var generateCommand = new Command("generate").description("Generate typed SDK, t
9113
9143
  spinner.text = "Generating types...";
9114
9144
  await TypeGenerator.generate(manifest, options.output);
9115
9145
  spinner.text = "Generating SDK...";
9116
- await SDKGenerator.generate(manifest, options.output);
9146
+ await SDKGenerator.generate(manifest, options.output, options);
9117
9147
  if (options.hooks !== false) {
9118
9148
  spinner.text = "Generating hooks...";
9119
9149
  await HookGenerator.generate(manifest, options.output);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "routesync",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "Laravel routes to typed frontend SDKs.",
5
5
  "main": "./dist/sdk.js",
6
6
  "module": "./dist/sdk.mjs",