@flink-app/flink 0.13.2 → 0.13.4

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.
@@ -0,0 +1,8 @@
1
+ # Changesets
2
+
3
+ Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
4
+ with multi-package repos, or single-package repos to help you version and publish your code. You can
5
+ find the full documentation for it [in our repository](https://github.com/changesets/changesets)
6
+
7
+ We have a quick list of common questions to get you started engaging with this project in
8
+ [our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
@@ -0,0 +1,11 @@
1
+ {
2
+ "$schema": "https://unpkg.com/@changesets/config@3.1.2/schema.json",
3
+ "changelog": "@changesets/cli/changelog",
4
+ "commit": false,
5
+ "fixed": [],
6
+ "linked": [],
7
+ "access": "restricted",
8
+ "baseBranch": "main",
9
+ "updateInternalDependencies": "patch",
10
+ "ignore": []
11
+ }
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @flink-app/flink
2
2
 
3
+ ## 0.13.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Add PATCH HTTP method support and fix schema generation for utility types
8
+
9
+ - Add PATCH HTTP method to HttpMethod enum and auto-detection from handler filenames
10
+ - Fix schema generation to support nested Partial types with indexed access (e.g., `Partial<Interface["property"]>`)
11
+ - Add recursive dependency resolution for interface copying in TypeScript compiler
12
+ - Add support for union and intersection types with utility type patterns
13
+ - Add test coverage for complex type scenarios (union, intersection, nested utility types)
14
+
15
+ ## 0.13.3
16
+
17
+ ### Patch Changes
18
+
19
+ - Use projects tsconfig.json for schema generation
20
+
3
21
  ## 0.13.2
4
22
 
5
23
  ### Patch Changes
@@ -7,7 +7,8 @@ export declare enum HttpMethod {
7
7
  get = "get",
8
8
  post = "post",
9
9
  put = "put",
10
- delete = "delete"
10
+ delete = "delete",
11
+ patch = "patch"
11
12
  }
12
13
  type Params = Request["params"];
13
14
  /**
@@ -7,4 +7,5 @@ var HttpMethod;
7
7
  HttpMethod["post"] = "post";
8
8
  HttpMethod["put"] = "put";
9
9
  HttpMethod["delete"] = "delete";
10
+ HttpMethod["patch"] = "patch";
10
11
  })(HttpMethod || (exports.HttpMethod = HttpMethod = {}));
@@ -89,6 +89,10 @@ declare class TypeScriptCompiler {
89
89
  * Return names of req and/or res schema types.
90
90
  */
91
91
  private extractSchemasFromHandlerSourceFile;
92
+ /**
93
+ * Recursively copies an interface and all its dependencies from the same file
94
+ */
95
+ private copyInterfaceWithDependencies;
92
96
  private saveIntermediateTsSchema;
93
97
  private initJsonSchemaGenerator;
94
98
  private generateAndSaveJsonSchemas;
@@ -480,10 +480,50 @@ var TypeScriptCompiler = /** @class */ (function () {
480
480
  });
481
481
  });
482
482
  };
483
+ /**
484
+ * Recursively copies an interface and all its dependencies from the same file
485
+ */
486
+ TypeScriptCompiler.prototype.copyInterfaceWithDependencies = function (interfaceDecl, handlerFile) {
487
+ var _a, _b, _c;
488
+ var interfaceName = ((_a = interfaceDecl.getName) === null || _a === void 0 ? void 0 : _a.call(interfaceDecl)) || ((_b = interfaceDecl.getFirstChildByKind(ts_morph_1.SyntaxKind.Identifier)) === null || _b === void 0 ? void 0 : _b.getText());
489
+ if (!interfaceName)
490
+ return;
491
+ // Check if already copied
492
+ var existingInterface = this.parsedTsSchemas.find(function (s) { return s.includes("interface ".concat(interfaceName, " ")) || s.includes("type ".concat(interfaceName, " =")); });
493
+ if (existingInterface)
494
+ return;
495
+ // Copy the interface
496
+ this.parsedTsSchemas.push(interfaceDecl.getText());
497
+ // Find and recursively copy dependencies from the same file
498
+ // First, find direct type references in this interface
499
+ var typeRefIdentifiers = interfaceDecl
500
+ .getDescendantsOfKind(ts_morph_1.SyntaxKind.TypeReference)
501
+ .filter(function (typeRefNode) { return !!typeRefNode.getFirstChildIfKind(ts_morph_1.SyntaxKind.Identifier); })
502
+ .map(function (typeRefNode) { return typeRefNode.getFirstChildIfKindOrThrow(ts_morph_1.SyntaxKind.Identifier); });
503
+ for (var _i = 0, typeRefIdentifiers_1 = typeRefIdentifiers; _i < typeRefIdentifiers_1.length; _i++) {
504
+ var typeRefIdentifier = typeRefIdentifiers_1[_i];
505
+ var typeSymbol = typeRefIdentifier.getSymbol();
506
+ if (typeSymbol) {
507
+ var declaredType = typeSymbol.getDeclaredType();
508
+ var declaration = (_c = declaredType.getSymbol()) === null || _c === void 0 ? void 0 : _c.getDeclarations()[0];
509
+ if (declaration && declaration.getSourceFile() === handlerFile) {
510
+ // Same file - recursively copy this dependency
511
+ this.copyInterfaceWithDependencies(declaration, handlerFile);
512
+ }
513
+ else if (declaration && declaration.getSourceFile() !== handlerFile) {
514
+ // Different file - add to imports
515
+ var declaredTypeSymbol = declaredType.getSymbol();
516
+ if (declaredTypeSymbol) {
517
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
518
+ }
519
+ }
520
+ }
521
+ }
522
+ };
483
523
  TypeScriptCompiler.prototype.saveIntermediateTsSchema = function (schema, handlerFile, suffix) {
484
524
  return __awaiter(this, void 0, void 0, function () {
485
- var handlerFileName, generatedSchemaInterfaceStr, schemaInterfaceName, schemaSymbol, interfaceName, declaration, _i, _a, typeToImport, arrayTypeArg, schemaSymbol, interfaceName, declaration, props, _b, _c, typeToImport, declarations, declaration, propertySignatures, _d, _e, prop, propType, typeSymbol, typeDeclaration, elementType, elementSymbol, elementDeclaration;
486
- return __generator(this, function (_f) {
525
+ var handlerFileName, generatedSchemaInterfaceStr, schemaInterfaceName, schemaSymbol, interfaceName, declaration, _i, _a, typeToImport, typeSymbol, declaredTypeSymbol, _b, _c, prop, propDecl, propText, interfaceNameMatches, _d, interfaceNameMatches_1, match, referencedInterfaceName, referencedInterfaceDecl, arrayTypeArg, schemaSymbol, interfaceName, declaration, props, _e, _f, typeToImport, typeSymbol, declaredTypeSymbol, schemaSymbol, declarations, declaration, propertySignatures, _g, _h, prop, propType, typeSymbol, typeDeclaration, elementType, elementSymbol, elementDeclaration, currentPropTypeText, interfaceNameMatches, _j, interfaceNameMatches_2, match, interfaceName, interfaceDecl, typeArgs, _k, typeArgs_1, typeArg, argSymbol, argDeclaration, _l, _m, typeToImport, typeSymbol, declaredTypeSymbol;
526
+ return __generator(this, function (_o) {
487
527
  if (schema.isAny()) {
488
528
  return [2 /*return*/]; // 'any' indicates that no schema is used
489
529
  }
@@ -496,13 +536,39 @@ var TypeScriptCompiler = /** @class */ (function () {
496
536
  declaration = schemaSymbol.getDeclarations()[0];
497
537
  if (declaration.getSourceFile() === handlerFile) {
498
538
  // Interface is declared within handler file
499
- generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " { \n ").concat(schema
539
+ generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " {\n ").concat(schema
500
540
  .getProperties()
501
541
  .map(function (p) { return p.getValueDeclarationOrThrow().getText(); })
502
542
  .join("\n"), "\n }");
503
543
  for (_i = 0, _a = (0, TypeScriptUtils_1.getTypesToImport)(declaration); _i < _a.length; _i++) {
504
544
  typeToImport = _a[_i];
505
- this.tsSchemasSymbolsToImports.push(typeToImport.getSymbolOrThrow().getDeclaredType().getSymbolOrThrow());
545
+ typeSymbol = typeToImport.getSymbol();
546
+ if (typeSymbol) {
547
+ declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
548
+ if (declaredTypeSymbol) {
549
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
550
+ }
551
+ }
552
+ }
553
+ // Also check for utility types with indexed access patterns like Partial<Foo["bar"]>
554
+ for (_b = 0, _c = schema.getProperties(); _b < _c.length; _b++) {
555
+ prop = _c[_b];
556
+ propDecl = prop.getValueDeclaration();
557
+ if (propDecl) {
558
+ propText = propDecl.getText();
559
+ interfaceNameMatches = propText.match(/\b([A-Z][a-zA-Z0-9]*)\s*\[/g);
560
+ if (interfaceNameMatches) {
561
+ for (_d = 0, interfaceNameMatches_1 = interfaceNameMatches; _d < interfaceNameMatches_1.length; _d++) {
562
+ match = interfaceNameMatches_1[_d];
563
+ referencedInterfaceName = match.replace(/\s*\[$/, '').trim();
564
+ referencedInterfaceDecl = handlerFile.getInterface(referencedInterfaceName) || handlerFile.getTypeAlias(referencedInterfaceName);
565
+ if (referencedInterfaceDecl) {
566
+ // Interface is in same file - copy it and all its dependencies recursively
567
+ this.copyInterfaceWithDependencies(referencedInterfaceDecl, handlerFile);
568
+ }
569
+ }
570
+ }
571
+ }
506
572
  }
507
573
  }
508
574
  else {
@@ -531,15 +597,22 @@ var TypeScriptCompiler = /** @class */ (function () {
531
597
  else {
532
598
  generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " extends Array<").concat(declaration.getText(), "> {}");
533
599
  }
534
- for (_b = 0, _c = (0, TypeScriptUtils_1.getTypesToImport)(declaration); _b < _c.length; _b++) {
535
- typeToImport = _c[_b];
536
- this.tsSchemasSymbolsToImports.push(typeToImport.getSymbolOrThrow().getDeclaredType().getSymbolOrThrow());
600
+ for (_e = 0, _f = (0, TypeScriptUtils_1.getTypesToImport)(declaration); _e < _f.length; _e++) {
601
+ typeToImport = _f[_e];
602
+ typeSymbol = typeToImport.getSymbol();
603
+ if (typeSymbol) {
604
+ declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
605
+ if (declaredTypeSymbol) {
606
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
607
+ }
608
+ }
537
609
  }
538
610
  }
539
611
  }
540
612
  else if (schema.isObject()) {
541
- declarations = schema.getSymbolOrThrow().getDeclarations();
542
- declaration = declarations[0];
613
+ schemaSymbol = schema.getSymbol();
614
+ declarations = schemaSymbol === null || schemaSymbol === void 0 ? void 0 : schemaSymbol.getDeclarations();
615
+ declaration = declarations === null || declarations === void 0 ? void 0 : declarations[0];
543
616
  propertySignatures = schema.getProperties().map(function (prop) {
544
617
  var propName = prop.getName();
545
618
  var propType = prop.getTypeAtLocation(handlerFile);
@@ -550,19 +623,18 @@ var TypeScriptCompiler = /** @class */ (function () {
550
623
  var isOptional = false;
551
624
  if (valueDeclaration) {
552
625
  // Property has a source declaration (normal case)
553
- isOptional = valueDeclaration.getType().isNullable() ||
554
- valueDeclaration.compilerNode.questionToken !== undefined;
626
+ isOptional = valueDeclaration.getType().isNullable() || valueDeclaration.compilerNode.questionToken !== undefined;
555
627
  }
556
628
  else {
557
629
  // Property is synthetic (from utility types like Omit, Pick, etc.)
558
630
  // Check if the property itself is optional by examining the symbol flags
559
631
  isOptional = !!(prop.getFlags() & ts_morph_1.ts.SymbolFlags.Optional);
560
632
  }
561
- return "".concat(propName).concat(isOptional ? '?' : '', ": ").concat(propTypeText);
633
+ return "".concat(propName).concat(isOptional ? "?" : "", ": ").concat(propTypeText);
562
634
  });
563
635
  // Extract type references for imports from resolved types
564
- for (_d = 0, _e = schema.getProperties(); _d < _e.length; _d++) {
565
- prop = _e[_d];
636
+ for (_g = 0, _h = schema.getProperties(); _g < _h.length; _g++) {
637
+ prop = _h[_g];
566
638
  propType = prop.getTypeAtLocation(handlerFile);
567
639
  typeSymbol = propType.getSymbol();
568
640
  if (typeSymbol) {
@@ -582,6 +654,45 @@ var TypeScriptCompiler = /** @class */ (function () {
582
654
  }
583
655
  }
584
656
  }
657
+ currentPropTypeText = propType.getText(undefined, ts_morph_1.ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
658
+ interfaceNameMatches = currentPropTypeText.match(/\b([A-Z][a-zA-Z0-9]*)\s*\[/g);
659
+ if (interfaceNameMatches) {
660
+ for (_j = 0, interfaceNameMatches_2 = interfaceNameMatches; _j < interfaceNameMatches_2.length; _j++) {
661
+ match = interfaceNameMatches_2[_j];
662
+ interfaceName = match.replace(/\s*\[$/, '').trim();
663
+ interfaceDecl = handlerFile.getInterface(interfaceName) || handlerFile.getTypeAlias(interfaceName);
664
+ if (interfaceDecl) {
665
+ // Interface is in same file - copy it and all its dependencies recursively
666
+ this.copyInterfaceWithDependencies(interfaceDecl, handlerFile);
667
+ }
668
+ }
669
+ }
670
+ typeArgs = propType.getTypeArguments();
671
+ if (typeArgs && typeArgs.length > 0) {
672
+ for (_k = 0, typeArgs_1 = typeArgs; _k < typeArgs_1.length; _k++) {
673
+ typeArg = typeArgs_1[_k];
674
+ argSymbol = typeArg.getSymbol();
675
+ if (argSymbol) {
676
+ argDeclaration = argSymbol.getDeclarations()[0];
677
+ if (argDeclaration && argDeclaration.getSourceFile() !== handlerFile) {
678
+ this.tsSchemasSymbolsToImports.push(argSymbol);
679
+ }
680
+ }
681
+ }
682
+ }
683
+ }
684
+ // If we have a declaration, check if we need to import any types
685
+ if (declaration) {
686
+ for (_l = 0, _m = (0, TypeScriptUtils_1.getTypesToImport)(declaration); _l < _m.length; _l++) {
687
+ typeToImport = _m[_l];
688
+ typeSymbol = typeToImport.getSymbol();
689
+ if (typeSymbol) {
690
+ declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
691
+ if (declaredTypeSymbol) {
692
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
693
+ }
694
+ }
695
+ }
585
696
  }
586
697
  generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " { ").concat(propertySignatures.join(";\n"), " }");
587
698
  }
@@ -613,7 +724,7 @@ var TypeScriptCompiler = /** @class */ (function () {
613
724
  extraTags: [],
614
725
  functions: "fail",
615
726
  discriminatorType: "json-schema",
616
- // tsconfig: tsconfigPath,
727
+ tsconfig: tsconfigPath,
617
728
  };
618
729
  console.log("Creating TypeScript program for schema generation:");
619
730
  console.log(" Schema file:", schemaFilePath);
@@ -634,9 +745,7 @@ var TypeScriptCompiler = /** @class */ (function () {
634
745
  if (info.file) {
635
746
  var _b = info.file.getLineAndCharacterOfPosition(info.start), line = _b.line, character = _b.character;
636
747
  var fileName = info.file.fileName.replace(this.cwd, ".");
637
- var message = typeof info.messageText === "string"
638
- ? info.messageText
639
- : info.messageText.messageText;
748
+ var message = typeof info.messageText === "string" ? info.messageText : info.messageText.messageText;
640
749
  console.error(" ".concat(fileName, ":").concat(line + 1, ":").concat(character + 1));
641
750
  console.error(" ".concat(message, "\n"));
642
751
  }
package/dist/src/utils.js CHANGED
@@ -138,6 +138,8 @@ function getHttpMethodFromHandlerName(handlerFilename) {
138
138
  return FlinkHttpHandler_1.HttpMethod.put;
139
139
  if (handlerFilename.startsWith(FlinkHttpHandler_1.HttpMethod.delete))
140
140
  return FlinkHttpHandler_1.HttpMethod.delete;
141
+ if (handlerFilename.startsWith(FlinkHttpHandler_1.HttpMethod.patch))
142
+ return FlinkHttpHandler_1.HttpMethod.patch;
141
143
  }
142
144
  function getJsDocComment(comment) {
143
145
  var rows = comment.split("\n").map(function (line) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/flink",
3
- "version": "0.13.2",
3
+ "version": "0.13.4",
4
4
  "description": "Typescript only framework for creating REST-like APIs on top of Express and mongodb",
5
5
  "types": "dist/src/index.d.ts",
6
6
  "main": "dist/src/index.js",
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.Route = void 0;
40
+ var flink_1 = require("@flink-app/flink");
41
+ exports.Route = {
42
+ path: "/car/:id",
43
+ method: flink_1.HttpMethod.patch,
44
+ };
45
+ var PatchCar = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
46
+ var req = _b.req;
47
+ return __generator(this, function (_c) {
48
+ return [2 /*return*/, {
49
+ data: {
50
+ model: req.body.model || "Updated Model",
51
+ year: req.body.year || 2024,
52
+ },
53
+ }];
54
+ });
55
+ }); };
56
+ exports.default = PatchCar;
57
+ exports.__assumedHttpMethod = "patch", exports.__file = "PatchCar.ts", exports.__query = [], exports.__params = [{ description: "", name: "id" }];
58
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "string" }, "year": { "type": "number" } }, "additionalProperties": false, "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "string" }, "year": { "type": "number" } }, "required": ["model", "year"], "additionalProperties": false, "definitions": {} } };
@@ -0,0 +1,76 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.Route = void 0;
40
+ var flink_1 = require("@flink-app/flink");
41
+ exports.Route = {
42
+ path: "/onboarding/:sessionId",
43
+ method: flink_1.HttpMethod.patch,
44
+ };
45
+ var PatchOnboardingSession = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
46
+ var _c, _d;
47
+ var req = _b.req;
48
+ return __generator(this, function (_e) {
49
+ return [2 /*return*/, {
50
+ data: {
51
+ sessionId: req.params.sessionId,
52
+ status: req.body.status || "pending",
53
+ extractedData: {
54
+ companyName: ((_c = req.body.extractedData) === null || _c === void 0 ? void 0 : _c.companyName) || "Test Company",
55
+ orgNumber: ((_d = req.body.extractedData) === null || _d === void 0 ? void 0 : _d.orgNumber) || "123456",
56
+ address: {
57
+ street: "Main St",
58
+ city: "Stockholm",
59
+ postalCode: "12345",
60
+ },
61
+ contactInfo: {
62
+ email: "test@example.com",
63
+ phone: "+46701234567",
64
+ },
65
+ },
66
+ metadata: {
67
+ createdAt: new Date(),
68
+ updatedAt: new Date(),
69
+ },
70
+ },
71
+ }];
72
+ });
73
+ }); };
74
+ exports.default = PatchOnboardingSession;
75
+ exports.__assumedHttpMethod = "patch", exports.__file = "PatchOnboardingSession.ts", exports.__query = [], exports.__params = [{ description: "", name: "sessionId" }];
76
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "status": { "type": "string" }, "extractedData": { "type": "object", "properties": { "companyName": { "type": "string" }, "orgNumber": { "type": "string" }, "address": { "type": "object", "properties": { "street": { "type": "string" }, "city": { "type": "string" }, "postalCode": { "type": "string" } }, "required": ["street", "city", "postalCode"], "additionalProperties": false }, "contactInfo": { "type": "object", "properties": { "email": { "type": "string" }, "phone": { "type": "string" } }, "required": ["email", "phone"], "additionalProperties": false } }, "additionalProperties": false }, "metadata": { "type": "object", "properties": { "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "additionalProperties": false, "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "sessionId": { "type": "string" }, "status": { "type": "string" }, "extractedData": { "type": "object", "properties": { "companyName": { "type": "string" }, "orgNumber": { "type": "string" }, "address": { "type": "object", "properties": { "street": { "type": "string" }, "city": { "type": "string" }, "postalCode": { "type": "string" } }, "required": ["street", "city", "postalCode"], "additionalProperties": false }, "contactInfo": { "type": "object", "properties": { "email": { "type": "string" }, "phone": { "type": "string" } }, "required": ["email", "phone"], "additionalProperties": false } }, "required": ["companyName", "orgNumber", "address", "contactInfo"], "additionalProperties": false }, "metadata": { "type": "object", "properties": { "createdAt": { "type": "string", "format": "date-time" }, "updatedAt": { "type": "string", "format": "date-time" } }, "required": ["createdAt", "updatedAt"], "additionalProperties": false } }, "required": ["sessionId", "status", "extractedData", "metadata"], "additionalProperties": false, "definitions": {} } };
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.Route = void 0;
40
+ var flink_1 = require("@flink-app/flink");
41
+ exports.Route = {
42
+ path: "/order/:orderId",
43
+ method: flink_1.HttpMethod.patch,
44
+ };
45
+ var PatchOrderWithComplexTypes = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
46
+ var req = _b.req;
47
+ return __generator(this, function (_c) {
48
+ return [2 /*return*/, {
49
+ data: {
50
+ orderId: req.params.orderId,
51
+ status: "updated",
52
+ },
53
+ }];
54
+ });
55
+ }); };
56
+ exports.default = PatchOrderWithComplexTypes;
57
+ exports.__assumedHttpMethod = "patch", exports.__file = "PatchOrderWithComplexTypes.ts", exports.__query = [], exports.__params = [{ description: "", name: "orderId" }];
58
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "customerCity": { "type": "object", "properties": { "street": { "type": "string" }, "city": { "type": "string" }, "country": { "type": "string" } }, "additionalProperties": false }, "items": { "type": "object", "additionalProperties": false, "properties": { "items": { "type": "array", "items": { "type": "object", "properties": { "productId": { "type": "string" }, "quantity": { "type": "number" }, "price": { "type": "number" } }, "required": ["productId", "quantity", "price"], "additionalProperties": false } }, "subtotal": { "type": "number" } }, "required": ["items", "subtotal"] }, "shipping": { "type": "object", "additionalProperties": false, "properties": { "method": { "type": "string" } }, "required": ["method"] }, "paymentOrShipping": { "type": "object", "additionalProperties": false }, "itemUpdates": { "type": "array", "items": { "type": "object", "properties": { "productId": { "type": "string" }, "quantity": { "type": "number" }, "price": { "type": "number" } }, "additionalProperties": false } } }, "additionalProperties": false, "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "orderId": { "type": "string" }, "status": { "type": "string" } }, "required": ["orderId", "status"], "additionalProperties": false, "definitions": {} } };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.Route = void 0;
40
+ var flink_1 = require("@flink-app/flink");
41
+ exports.Route = {
42
+ path: "/product/:productId",
43
+ method: flink_1.HttpMethod.patch,
44
+ };
45
+ var PatchProductWithIntersection = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
46
+ var req = _b.req;
47
+ return __generator(this, function (_c) {
48
+ return [2 /*return*/, {
49
+ data: {
50
+ name: "Test Product",
51
+ description: "Test Description",
52
+ price: req.body.update || 99.99,
53
+ },
54
+ }];
55
+ });
56
+ }); };
57
+ exports.default = PatchProductWithIntersection;
58
+ exports.__assumedHttpMethod = "patch", exports.__file = "PatchProductWithIntersection.ts", exports.__query = [], exports.__params = [{ description: "", name: "productId" }];
59
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "update": { "type": "object", "additionalProperties": false }, "fullUpdate": { "type": "object", "additionalProperties": false, "properties": { "notes": { "type": "string" }, "stock": { "type": "number" }, "warehouse": { "type": "string" }, "sku": { "type": "string" }, "name": { "type": "string" }, "description": { "type": "string" }, "price": { "type": "number" } }, "required": ["notes"] }, "metadataUpdate": { "type": "object", "additionalProperties": false, "properties": { "updatedBy": { "type": "string" } }, "required": ["updatedBy"] } }, "additionalProperties": false, "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "price": { "type": "number" } }, "required": ["name", "description", "price"], "additionalProperties": false, "definitions": {} } };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
13
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.__schemas = exports.__params = exports.__query = exports.__file = exports.__assumedHttpMethod = exports.Route = void 0;
40
+ var flink_1 = require("@flink-app/flink");
41
+ exports.Route = {
42
+ path: "/user/:userId",
43
+ method: flink_1.HttpMethod.patch,
44
+ };
45
+ var PatchUserWithUnion = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b) {
46
+ var req = _b.req;
47
+ return __generator(this, function (_c) {
48
+ return [2 /*return*/, {
49
+ data: {
50
+ firstName: "Test",
51
+ lastName: "User",
52
+ bio: req.body.data || "Default bio",
53
+ },
54
+ }];
55
+ });
56
+ }); };
57
+ exports.default = PatchUserWithUnion;
58
+ exports.__assumedHttpMethod = "patch", exports.__file = "PatchUserWithUnion.ts", exports.__query = [], exports.__params = [{ description: "", name: "userId" }];
59
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "data": { "type": "object", "additionalProperties": false }, "profileUpdate": { "anyOf": [{ "type": "object", "properties": { "firstName": { "type": "string" }, "lastName": { "type": "string" }, "bio": { "type": "string" } }, "additionalProperties": false }, { "type": "object", "properties": { "theme": { "type": "string" }, "notifications": { "type": "boolean" }, "language": { "type": "string" } }, "additionalProperties": false }] } }, "additionalProperties": false, "definitions": {} }, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "firstName": { "type": "string" }, "lastName": { "type": "string" }, "bio": { "type": "string" } }, "required": ["firstName", "lastName", "bio"], "additionalProperties": false, "definitions": {} } };
@@ -0,0 +1,25 @@
1
+ import { Handler, HttpMethod, RouteProps } from "@flink-app/flink";
2
+
3
+ export const Route: RouteProps = {
4
+ path: "/car/:id",
5
+ method: HttpMethod.patch,
6
+ };
7
+
8
+ interface Car {
9
+ model: string;
10
+ year: number;
11
+ }
12
+
13
+ type Params = { id: string };
14
+ type PatchCarReq = Partial<Car>;
15
+
16
+ const PatchCar: Handler<any, PatchCarReq, Car, Params> = async ({ req }) => {
17
+ return {
18
+ data: {
19
+ model: req.body.model || "Updated Model",
20
+ year: req.body.year || 2024,
21
+ },
22
+ };
23
+ };
24
+
25
+ export default PatchCar;
@@ -0,0 +1,66 @@
1
+ import { Handler, HttpMethod, RouteProps } from "@flink-app/flink";
2
+
3
+ export const Route: RouteProps = {
4
+ path: "/onboarding/:sessionId",
5
+ method: HttpMethod.patch,
6
+ };
7
+
8
+ // Simulating a complex nested structure like in your example
9
+ interface OnboardingSession {
10
+ sessionId: string;
11
+ status: string;
12
+ extractedData: {
13
+ companyName: string;
14
+ orgNumber: string;
15
+ address: {
16
+ street: string;
17
+ city: string;
18
+ postalCode: string;
19
+ };
20
+ contactInfo: {
21
+ email: string;
22
+ phone: string;
23
+ };
24
+ };
25
+ metadata: {
26
+ createdAt: Date;
27
+ updatedAt: Date;
28
+ };
29
+ }
30
+
31
+ // Test case: Partial of a nested property using bracket notation
32
+ interface PatchOnboardingSessionReq {
33
+ status?: string;
34
+ extractedData?: Partial<OnboardingSession["extractedData"]>;
35
+ metadata?: Partial<OnboardingSession["metadata"]>;
36
+ }
37
+
38
+ type Params = { sessionId: string };
39
+
40
+ const PatchOnboardingSession: Handler<any, PatchOnboardingSessionReq, OnboardingSession, Params> = async ({ req }) => {
41
+ return {
42
+ data: {
43
+ sessionId: req.params.sessionId,
44
+ status: req.body.status || "pending",
45
+ extractedData: {
46
+ companyName: req.body.extractedData?.companyName || "Test Company",
47
+ orgNumber: req.body.extractedData?.orgNumber || "123456",
48
+ address: {
49
+ street: "Main St",
50
+ city: "Stockholm",
51
+ postalCode: "12345",
52
+ },
53
+ contactInfo: {
54
+ email: "test@example.com",
55
+ phone: "+46701234567",
56
+ },
57
+ },
58
+ metadata: {
59
+ createdAt: new Date(),
60
+ updatedAt: new Date(),
61
+ },
62
+ },
63
+ };
64
+ };
65
+
66
+ export default PatchOnboardingSession;
@@ -0,0 +1,79 @@
1
+ import { Handler, HttpMethod, RouteProps } from "@flink-app/flink";
2
+
3
+ export const Route: RouteProps = {
4
+ path: "/order/:orderId",
5
+ method: HttpMethod.patch,
6
+ };
7
+
8
+ // Complex nested structures for testing edge cases
9
+ interface OrderCustomer {
10
+ customerId: string;
11
+ name: string;
12
+ email: string;
13
+ address: {
14
+ street: string;
15
+ city: string;
16
+ country: string;
17
+ };
18
+ }
19
+
20
+ interface OrderItem {
21
+ productId: string;
22
+ quantity: number;
23
+ price: number;
24
+ }
25
+
26
+ interface OrderItems {
27
+ items: Array<OrderItem>;
28
+ subtotal: number;
29
+ tax: number;
30
+ }
31
+
32
+ interface OrderShipping {
33
+ method: string;
34
+ trackingNumber: string;
35
+ estimatedDelivery: Date;
36
+ }
37
+
38
+ interface OrderPayment {
39
+ method: string;
40
+ status: string;
41
+ transactionId: string;
42
+ }
43
+
44
+ // Test case: Complex combinations of utility types
45
+ interface PatchOrderWithComplexTypesReq {
46
+ // Nested indexed access - should extract OrderCustomer
47
+ customerCity?: Partial<OrderCustomer["address"]>;
48
+
49
+ // Omit with indexed access - should extract OrderItems
50
+ items?: Omit<OrderItems, "tax"> & Partial<OrderItems["subtotal"]>;
51
+
52
+ // Pick with indexed access - should extract OrderShipping
53
+ shipping?: Pick<OrderShipping, "method"> & Partial<OrderShipping["trackingNumber"]>;
54
+
55
+ // Union of Partial with indexed access
56
+ paymentOrShipping?:
57
+ Partial<OrderPayment["status"]> | Partial<OrderShipping["method"]>;
58
+
59
+ // Array of Partial types
60
+ itemUpdates?: Array<Partial<OrderItem>>;
61
+ }
62
+
63
+ type Params = { orderId: string };
64
+
65
+ interface OrderResponse {
66
+ orderId: string;
67
+ status: string;
68
+ }
69
+
70
+ const PatchOrderWithComplexTypes: Handler<any, PatchOrderWithComplexTypesReq, OrderResponse, Params> = async ({ req }) => {
71
+ return {
72
+ data: {
73
+ orderId: req.params.orderId,
74
+ status: "updated",
75
+ },
76
+ };
77
+ };
78
+
79
+ export default PatchOrderWithComplexTypes;
@@ -0,0 +1,49 @@
1
+ import { Handler, HttpMethod, RouteProps } from "@flink-app/flink";
2
+
3
+ export const Route: RouteProps = {
4
+ path: "/product/:productId",
5
+ method: HttpMethod.patch,
6
+ };
7
+
8
+ // Base interfaces for testing intersection types
9
+ interface ProductDetails {
10
+ name: string;
11
+ description: string;
12
+ price: number;
13
+ }
14
+
15
+ interface ProductInventory {
16
+ stock: number;
17
+ warehouse: string;
18
+ sku: string;
19
+ }
20
+
21
+ interface ProductMetadata {
22
+ createdAt: Date;
23
+ updatedAt: Date;
24
+ createdBy: string;
25
+ }
26
+
27
+ // Test case: Intersection type with Partial indexed access patterns
28
+ interface PatchProductWithIntersectionReq {
29
+ // Intersection of Partial types - should extract ProductDetails and ProductInventory
30
+ update?: Partial<ProductDetails["price"]> & Partial<ProductInventory["stock"]>;
31
+ // More realistic intersection pattern
32
+ fullUpdate?: Partial<ProductDetails> & Partial<ProductInventory> & { notes: string };
33
+ // Intersection with indexed access
34
+ metadataUpdate?: Partial<ProductMetadata["updatedAt"]> & { updatedBy: string };
35
+ }
36
+
37
+ type Params = { productId: string };
38
+
39
+ const PatchProductWithIntersection: Handler<any, PatchProductWithIntersectionReq, ProductDetails, Params> = async ({ req }) => {
40
+ return {
41
+ data: {
42
+ name: "Test Product",
43
+ description: "Test Description",
44
+ price: (req.body.update as any) || 99.99,
45
+ },
46
+ };
47
+ };
48
+
49
+ export default PatchProductWithIntersection;
@@ -0,0 +1,46 @@
1
+ import { Handler, HttpMethod, RouteProps } from "@flink-app/flink";
2
+
3
+ export const Route: RouteProps = {
4
+ path: "/user/:userId",
5
+ method: HttpMethod.patch,
6
+ };
7
+
8
+ // Base interfaces for testing union types
9
+ interface UserProfile {
10
+ firstName: string;
11
+ lastName: string;
12
+ bio: string;
13
+ }
14
+
15
+ interface UserSettings {
16
+ theme: string;
17
+ notifications: boolean;
18
+ language: string;
19
+ }
20
+
21
+ interface UserPreferences {
22
+ emailFrequency: string;
23
+ newsletter: boolean;
24
+ }
25
+
26
+ // Test case: Union type with multiple Partial indexed access patterns
27
+ interface PatchUserWithUnionReq {
28
+ // Union of Partial types - should extract UserProfile, UserSettings, UserPreferences
29
+ data?: Partial<UserProfile["firstName"]> | Partial<UserSettings["theme"]> | Partial<UserPreferences["emailFrequency"]>;
30
+ // More realistic union pattern
31
+ profileUpdate?: Partial<UserProfile> | Partial<UserSettings>;
32
+ }
33
+
34
+ type Params = { userId: string };
35
+
36
+ const PatchUserWithUnion: Handler<any, PatchUserWithUnionReq, UserProfile, Params> = async ({ req }) => {
37
+ return {
38
+ data: {
39
+ firstName: "Test",
40
+ lastName: "User",
41
+ bio: req.body.data as string || "Default bio",
42
+ },
43
+ };
44
+ };
45
+
46
+ export default PatchUserWithUnion;
@@ -9,6 +9,7 @@ export enum HttpMethod {
9
9
  post = "post",
10
10
  put = "put",
11
11
  delete = "delete",
12
+ patch = "patch",
12
13
  }
13
14
 
14
15
  type Params = Request["params"];
@@ -409,6 +409,48 @@ export default {}; // Export an empty object to make it a module
409
409
  }
410
410
  }
411
411
 
412
+ /**
413
+ * Recursively copies an interface and all its dependencies from the same file
414
+ */
415
+ private copyInterfaceWithDependencies(interfaceDecl: any, handlerFile: SourceFile): void {
416
+ const interfaceName = interfaceDecl.getName?.() || interfaceDecl.getFirstChildByKind(SyntaxKind.Identifier)?.getText();
417
+ if (!interfaceName) return;
418
+
419
+ // Check if already copied
420
+ const existingInterface = this.parsedTsSchemas.find(
421
+ s => s.includes(`interface ${interfaceName} `) || s.includes(`type ${interfaceName} =`)
422
+ );
423
+ if (existingInterface) return;
424
+
425
+ // Copy the interface
426
+ this.parsedTsSchemas.push(interfaceDecl.getText());
427
+
428
+ // Find and recursively copy dependencies from the same file
429
+ // First, find direct type references in this interface
430
+ const typeRefIdentifiers = interfaceDecl
431
+ .getDescendantsOfKind(SyntaxKind.TypeReference)
432
+ .filter((typeRefNode: any) => !!typeRefNode.getFirstChildIfKind(SyntaxKind.Identifier))
433
+ .map((typeRefNode: any) => typeRefNode.getFirstChildIfKindOrThrow(SyntaxKind.Identifier));
434
+
435
+ for (const typeRefIdentifier of typeRefIdentifiers) {
436
+ const typeSymbol = typeRefIdentifier.getSymbol();
437
+ if (typeSymbol) {
438
+ const declaredType = typeSymbol.getDeclaredType();
439
+ const declaration = declaredType.getSymbol()?.getDeclarations()[0];
440
+ if (declaration && declaration.getSourceFile() === handlerFile) {
441
+ // Same file - recursively copy this dependency
442
+ this.copyInterfaceWithDependencies(declaration, handlerFile);
443
+ } else if (declaration && declaration.getSourceFile() !== handlerFile) {
444
+ // Different file - add to imports
445
+ const declaredTypeSymbol = declaredType.getSymbol();
446
+ if (declaredTypeSymbol) {
447
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
448
+ }
449
+ }
450
+ }
451
+ }
452
+ }
453
+
412
454
  private async saveIntermediateTsSchema(schema: Type<ts.Type>, handlerFile: SourceFile, suffix: string) {
413
455
  if (schema.isAny()) {
414
456
  return; // 'any' indicates that no schema is used
@@ -431,7 +473,7 @@ export default {}; // Export an empty object to make it a module
431
473
 
432
474
  if (declaration.getSourceFile() === handlerFile) {
433
475
  // Interface is declared within handler file
434
- generatedSchemaInterfaceStr = `export interface ${schemaInterfaceName} {
476
+ generatedSchemaInterfaceStr = `export interface ${schemaInterfaceName} {
435
477
  ${schema
436
478
  .getProperties()
437
479
  .map((p) => p.getValueDeclarationOrThrow().getText())
@@ -439,7 +481,34 @@ export default {}; // Export an empty object to make it a module
439
481
  }`;
440
482
 
441
483
  for (const typeToImport of getTypesToImport(declaration)) {
442
- this.tsSchemasSymbolsToImports.push(typeToImport.getSymbolOrThrow().getDeclaredType().getSymbolOrThrow());
484
+ const typeSymbol = typeToImport.getSymbol();
485
+ if (typeSymbol) {
486
+ const declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
487
+ if (declaredTypeSymbol) {
488
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
489
+ }
490
+ }
491
+ }
492
+
493
+ // Also check for utility types with indexed access patterns like Partial<Foo["bar"]>
494
+ for (const prop of schema.getProperties()) {
495
+ const propDecl = prop.getValueDeclaration();
496
+ if (propDecl) {
497
+ const propText = propDecl.getText();
498
+ // Match interface names in patterns like: Partial<InterfaceName["prop"]>
499
+ const interfaceNameMatches = propText.match(/\b([A-Z][a-zA-Z0-9]*)\s*\[/g);
500
+ if (interfaceNameMatches) {
501
+ for (const match of interfaceNameMatches) {
502
+ const referencedInterfaceName = match.replace(/\s*\[$/, '').trim();
503
+ // Try to find this interface in the handler file
504
+ const referencedInterfaceDecl = handlerFile.getInterface(referencedInterfaceName) || handlerFile.getTypeAlias(referencedInterfaceName);
505
+ if (referencedInterfaceDecl) {
506
+ // Interface is in same file - copy it and all its dependencies recursively
507
+ this.copyInterfaceWithDependencies(referencedInterfaceDecl, handlerFile);
508
+ }
509
+ }
510
+ }
511
+ }
443
512
  }
444
513
  } else {
445
514
  // Interface is imported from other file
@@ -468,7 +537,13 @@ export default {}; // Export an empty object to make it a module
468
537
  }
469
538
 
470
539
  for (const typeToImport of getTypesToImport(declaration)) {
471
- this.tsSchemasSymbolsToImports.push(typeToImport.getSymbolOrThrow().getDeclaredType().getSymbolOrThrow());
540
+ const typeSymbol = typeToImport.getSymbol();
541
+ if (typeSymbol) {
542
+ const declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
543
+ if (declaredTypeSymbol) {
544
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
545
+ }
546
+ }
472
547
  }
473
548
  }
474
549
  } else if (schema.isObject()) {
@@ -477,8 +552,11 @@ export default {}; // Export an empty object to make it a module
477
552
  * We need extract `{car: Car}` into its own interface and make sure
478
553
  * to import types if needed to
479
554
  */
480
- const declarations = schema.getSymbolOrThrow().getDeclarations();
481
- const declaration = declarations[0];
555
+
556
+ // Try to get symbol - it may not exist for utility types (Partial, Omit, Pick, etc.)
557
+ const schemaSymbol = schema.getSymbol();
558
+ const declarations = schemaSymbol?.getDeclarations();
559
+ const declaration = declarations?.[0];
482
560
 
483
561
  // Build property signatures using resolved types instead of source text
484
562
  // This ensures generic type parameters are properly expanded
@@ -494,15 +572,14 @@ export default {}; // Export an empty object to make it a module
494
572
 
495
573
  if (valueDeclaration) {
496
574
  // Property has a source declaration (normal case)
497
- isOptional = valueDeclaration.getType().isNullable() ||
498
- (valueDeclaration.compilerNode as any).questionToken !== undefined;
575
+ isOptional = valueDeclaration.getType().isNullable() || (valueDeclaration.compilerNode as any).questionToken !== undefined;
499
576
  } else {
500
577
  // Property is synthetic (from utility types like Omit, Pick, etc.)
501
578
  // Check if the property itself is optional by examining the symbol flags
502
579
  isOptional = !!(prop.getFlags() & ts.SymbolFlags.Optional);
503
580
  }
504
581
 
505
- return `${propName}${isOptional ? '?' : ''}: ${propTypeText}`;
582
+ return `${propName}${isOptional ? "?" : ""}: ${propTypeText}`;
506
583
  });
507
584
 
508
585
  // Extract type references for imports from resolved types
@@ -529,6 +606,51 @@ export default {}; // Export an empty object to make it a module
529
606
  }
530
607
  }
531
608
  }
609
+
610
+ // Check for utility types (Partial, Omit, Pick, etc.) and extract their type arguments
611
+ // For example: Partial<Foo["bar"]> should extract Foo
612
+ // Use a pragmatic text-based approach to find interface references in type expressions
613
+ // Match interface names in patterns like: Partial<InterfaceName["prop"]>, Omit<InterfaceName, "key">, etc.
614
+ const currentPropTypeText = propType.getText(undefined, ts.TypeFormatFlags.UseAliasDefinedOutsideCurrentScope);
615
+ const interfaceNameMatches = currentPropTypeText.match(/\b([A-Z][a-zA-Z0-9]*)\s*\[/g);
616
+ if (interfaceNameMatches) {
617
+ for (const match of interfaceNameMatches) {
618
+ const interfaceName = match.replace(/\s*\[$/, '').trim();
619
+ // Try to find this interface in the handler file
620
+ const interfaceDecl = handlerFile.getInterface(interfaceName) || handlerFile.getTypeAlias(interfaceName);
621
+ if (interfaceDecl) {
622
+ // Interface is in same file - copy it and all its dependencies recursively
623
+ this.copyInterfaceWithDependencies(interfaceDecl, handlerFile);
624
+ }
625
+ }
626
+ }
627
+
628
+ // Also check regular type arguments (for types like Array<Foo>, Promise<Bar>)
629
+ const typeArgs = propType.getTypeArguments();
630
+ if (typeArgs && typeArgs.length > 0) {
631
+ for (const typeArg of typeArgs) {
632
+ const argSymbol = typeArg.getSymbol();
633
+ if (argSymbol) {
634
+ const argDeclaration = argSymbol.getDeclarations()[0];
635
+ if (argDeclaration && argDeclaration.getSourceFile() !== handlerFile) {
636
+ this.tsSchemasSymbolsToImports.push(argSymbol);
637
+ }
638
+ }
639
+ }
640
+ }
641
+ }
642
+
643
+ // If we have a declaration, check if we need to import any types
644
+ if (declaration) {
645
+ for (const typeToImport of getTypesToImport(declaration)) {
646
+ const typeSymbol = typeToImport.getSymbol();
647
+ if (typeSymbol) {
648
+ const declaredTypeSymbol = typeSymbol.getDeclaredType().getSymbol();
649
+ if (declaredTypeSymbol) {
650
+ this.tsSchemasSymbolsToImports.push(declaredTypeSymbol);
651
+ }
652
+ }
653
+ }
532
654
  }
533
655
 
534
656
  generatedSchemaInterfaceStr = `export interface ${schemaInterfaceName} { ${propertySignatures.join(";\n")} }`;
@@ -561,7 +683,7 @@ export default {}; // Export an empty object to make it a module
561
683
  extraTags: [],
562
684
  functions: "fail",
563
685
  discriminatorType: "json-schema",
564
- // tsconfig: tsconfigPath,
686
+ tsconfig: tsconfigPath,
565
687
  };
566
688
 
567
689
  console.log("Creating TypeScript program for schema generation:");
@@ -583,9 +705,7 @@ export default {}; // Export an empty object to make it a module
583
705
  if (info.file) {
584
706
  const { line, character } = info.file.getLineAndCharacterOfPosition(info.start);
585
707
  const fileName = info.file.fileName.replace(this.cwd, ".");
586
- const message = typeof info.messageText === "string"
587
- ? info.messageText
588
- : info.messageText.messageText;
708
+ const message = typeof info.messageText === "string" ? info.messageText : info.messageText.messageText;
589
709
 
590
710
  console.error(` ${fileName}:${line + 1}:${character + 1}`);
591
711
  console.error(` ${message}\n`);
package/src/utils.ts CHANGED
@@ -75,6 +75,7 @@ export function getHttpMethodFromHandlerName(handlerFilename: string) {
75
75
  if (handlerFilename.startsWith(HttpMethod.post)) return HttpMethod.post;
76
76
  if (handlerFilename.startsWith(HttpMethod.put)) return HttpMethod.put;
77
77
  if (handlerFilename.startsWith(HttpMethod.delete)) return HttpMethod.delete;
78
+ if (handlerFilename.startsWith(HttpMethod.patch)) return HttpMethod.patch;
78
79
  }
79
80
 
80
81
  export function getJsDocComment(comment: string) {