@flink-app/flink 0.12.1-alpha.4 → 0.12.1-alpha.44

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 (31) hide show
  1. package/dist/src/FlinkApp.d.ts +2 -1
  2. package/dist/src/FlinkApp.js +21 -11
  3. package/dist/src/FlinkHttpHandler.d.ts +14 -1
  4. package/dist/src/FlinkRepo.d.ts +23 -7
  5. package/dist/src/FlinkRepo.js +32 -10
  6. package/dist/src/TypeScriptCompiler.d.ts +10 -0
  7. package/dist/src/TypeScriptCompiler.js +91 -18
  8. package/package.json +7 -4
  9. package/spec/FlinkRepo.spec.ts +11 -0
  10. package/spec/TypeScriptCompiler.spec.ts +8 -0
  11. package/spec/mock-project/dist/src/handlers/GetCar.js +2 -0
  12. package/spec/mock-project/dist/src/handlers/GetCar2.js +2 -0
  13. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema.js +2 -0
  14. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema2.js +2 -0
  15. package/spec/mock-project/dist/src/handlers/GetCarWithArraySchema3.js +2 -0
  16. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema.js +2 -0
  17. package/spec/mock-project/dist/src/handlers/GetCarWithLiteralSchema2.js +2 -0
  18. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile.js +2 -0
  19. package/spec/mock-project/dist/src/handlers/GetCarWithSchemaInFile2.js +2 -0
  20. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler.js +2 -0
  21. package/spec/mock-project/dist/src/handlers/ManuallyAddedHandler2.js +2 -0
  22. package/spec/mock-project/dist/src/handlers/PostCar.js +2 -0
  23. package/spec/mock-project/dist/src/handlers/PostLogin.js +2 -0
  24. package/spec/mock-project/dist/src/handlers/{GetCarWithOmitSchema.js → PostLogout.js} +16 -18
  25. package/spec/mock-project/dist/src/handlers/PutCar.js +2 -0
  26. package/spec/mock-project/src/handlers/PostLogout.ts +19 -0
  27. package/src/FlinkApp.ts +20 -5
  28. package/src/FlinkHttpHandler.ts +95 -96
  29. package/src/FlinkRepo.ts +24 -7
  30. package/src/TypeScriptCompiler.ts +81 -23
  31. package/spec/mock-project/dist/src/handlers/GetCarWithTypeSchema.js +0 -60
@@ -1,7 +1,7 @@
1
1
  import { OptionsJson } from "body-parser";
2
2
  import { Express } from "express";
3
3
  import { JSONSchema7 } from "json-schema";
4
- import { Db } from "mongodb";
4
+ import { Db, MongoClient } from "mongodb";
5
5
  import { ToadScheduler } from "toad-scheduler";
6
6
  import { FlinkAuthPlugin } from "./auth/FlinkAuthPlugin";
7
7
  import { FlinkContext } from "./FlinkContext";
@@ -156,6 +156,7 @@ export declare class FlinkApp<C extends FlinkContext> {
156
156
  name: string;
157
157
  expressApp?: Express;
158
158
  db?: Db;
159
+ dbClient?: MongoClient;
159
160
  handlers: HandlerConfig[];
160
161
  port?: number;
161
162
  started: boolean;
@@ -442,11 +442,11 @@ var FlinkApp = /** @class */ (function () {
442
442
  */
443
443
  FlinkApp.prototype.registerAutoRegisterableHandlers = function () {
444
444
  return __awaiter(this, void 0, void 0, function () {
445
- var _i, autoRegisteredHandlers_1, _a, handler, assumedHttpMethod, pathParams, _b, _c, param;
446
- var _d, _e, _f;
447
- return __generator(this, function (_g) {
448
- for (_i = 0, autoRegisteredHandlers_1 = exports.autoRegisteredHandlers; _i < autoRegisteredHandlers_1.length; _i++) {
449
- _a = autoRegisteredHandlers_1[_i], handler = _a.handler, assumedHttpMethod = _a.assumedHttpMethod;
445
+ var _i, _a, _b, handler, assumedHttpMethod, pathParams, _c, _d, param;
446
+ var _e, _f, _g;
447
+ return __generator(this, function (_h) {
448
+ for (_i = 0, _a = exports.autoRegisteredHandlers.sort(function (a, b) { var _a, _b; return (((_a = a.handler.Route) === null || _a === void 0 ? void 0 : _a.order) || 0) - (((_b = b.handler.Route) === null || _b === void 0 ? void 0 : _b.order) || 0); }); _i < _a.length; _i++) {
449
+ _b = _a[_i], handler = _b.handler, assumedHttpMethod = _b.assumedHttpMethod;
450
450
  if (!handler.Route) {
451
451
  FlinkLog_1.log.error("Missing Props in handler ".concat(handler.__file));
452
452
  continue;
@@ -455,10 +455,10 @@ var FlinkApp = /** @class */ (function () {
455
455
  FlinkLog_1.log.error("Missing exported handler function in handler ".concat(handler.__file));
456
456
  continue;
457
457
  }
458
- if (!!((_d = handler.__params) === null || _d === void 0 ? void 0 : _d.length)) {
458
+ if (!!((_e = handler.__params) === null || _e === void 0 ? void 0 : _e.length)) {
459
459
  pathParams = (0, utils_1.getPathParams)(handler.Route.path);
460
- for (_b = 0, _c = handler.__params; _b < _c.length; _b++) {
461
- param = _c[_b];
460
+ for (_c = 0, _d = handler.__params; _c < _d.length; _c++) {
461
+ param = _d[_c];
462
462
  if (!pathParams.includes(param.name)) {
463
463
  FlinkLog_1.log.error("Handler ".concat(handler.__file, " has param ").concat(param.name, " but it is not present in the path '").concat(handler.Route.path, "'"));
464
464
  throw new Error("Invalid/missing handler path param");
@@ -471,8 +471,8 @@ var FlinkApp = /** @class */ (function () {
471
471
  this.registerHandler({
472
472
  routeProps: __assign(__assign({}, handler.Route), { method: handler.Route.method || assumedHttpMethod, origin: this.name }),
473
473
  schema: {
474
- reqSchema: (_e = handler.__schemas) === null || _e === void 0 ? void 0 : _e.reqSchema,
475
- resSchema: (_f = handler.__schemas) === null || _f === void 0 ? void 0 : _f.resSchema,
474
+ reqSchema: (_f = handler.__schemas) === null || _f === void 0 ? void 0 : _f.reqSchema,
475
+ resSchema: (_g = handler.__schemas) === null || _g === void 0 ? void 0 : _g.resSchema,
476
476
  },
477
477
  queryMetadata: handler.__query || [],
478
478
  paramsMetadata: handler.__params || [],
@@ -583,7 +583,7 @@ var FlinkApp = /** @class */ (function () {
583
583
  if (this.dbOpts) {
584
584
  for (_i = 0, autoRegisteredRepos_1 = exports.autoRegisteredRepos; _i < autoRegisteredRepos_1.length; _i++) {
585
585
  _a = autoRegisteredRepos_1[_i], collectionName = _a.collectionName, repoInstanceName = _a.repoInstanceName, Repo = _a.Repo;
586
- repoInstance = new Repo(collectionName, this.db);
586
+ repoInstance = new Repo(collectionName, this.db, this.dbClient);
587
587
  this.repos[repoInstanceName] = repoInstance;
588
588
  FlinkLog_1.log.info("Registered repo ".concat(repoInstanceName));
589
589
  }
@@ -629,6 +629,7 @@ var FlinkApp = /** @class */ (function () {
629
629
  case 2:
630
630
  client = _a.sent();
631
631
  this.db = client.db();
632
+ this.dbClient = client;
632
633
  return [3 /*break*/, 4];
633
634
  case 3:
634
635
  err_2 = _a.sent();
@@ -715,6 +716,15 @@ var FlinkApp = /** @class */ (function () {
715
716
  if (!this.dbOpts) {
716
717
  throw new Error("No db configured");
717
718
  }
719
+ var driverVersion = require("mongodb/package.json").version;
720
+ if (driverVersion.startsWith("3")) {
721
+ FlinkLog_1.log.debug("Using legacy mongodb connection options as mongo client is version ".concat(driverVersion));
722
+ return {
723
+ useNewUrlParser: true,
724
+ useUnifiedTopology: true,
725
+ };
726
+ }
727
+ FlinkLog_1.log.debug("Using modern MongoDB client options (driver version ".concat(driverVersion, ")"));
718
728
  return {
719
729
  serverApi: {
720
730
  version: mongodb_1.ServerApiVersion.v1,
@@ -10,7 +10,12 @@ export declare enum HttpMethod {
10
10
  delete = "delete"
11
11
  }
12
12
  type Params = Request["params"];
13
- type Query = Request["query"];
13
+ /**
14
+ * Query type for request query parameters.
15
+ * Does currently not allow nested objects, although
16
+ * underlying express Request does allow it.
17
+ */
18
+ type Query = Record<string, string | string[] | undefined>;
14
19
  /**
15
20
  * Flink request extends express Request but adds reqId and user object.
16
21
  */
@@ -63,6 +68,14 @@ export interface RouteProps {
63
68
  * I.e. filename or plugin name that describes where handler origins from
64
69
  */
65
70
  origin?: string;
71
+ /**
72
+ * Order handler should be registered in.
73
+ *
74
+ * By default all handlers has order 0 and in most cases this is fine,
75
+ * but if for example you want to register a handler before all others
76
+ * to avoid conflicts you can set a negative order.
77
+ */
78
+ order?: number;
66
79
  }
67
80
  /**
68
81
  * Http handler function that handlers implements in order to
@@ -1,22 +1,38 @@
1
- import { Collection, Db, Document, ObjectId } from "mongodb";
1
+ import { Collection, Db, Document, MongoClient, ObjectId } from "mongodb";
2
2
  import { FlinkContext } from "./FlinkContext";
3
+ /**
4
+ * Partial model to have intellisense for partial updates but
5
+ * also allow any other properties to be set such as nested objects etc.
6
+ */
7
+ type PartialModel<Model> = Partial<Model> & {
8
+ [x: string]: any;
9
+ };
3
10
  export declare abstract class FlinkRepo<C extends FlinkContext, Model extends Document> {
4
- private collectionName;
5
- private db;
11
+ collectionName: string;
12
+ db: Db;
13
+ client?: MongoClient | undefined;
6
14
  collection: Collection;
7
15
  private _ctx?;
8
16
  set ctx(ctx: FlinkContext);
9
17
  get ctx(): FlinkContext;
10
- constructor(collectionName: string, db: Db);
18
+ constructor(collectionName: string, db: Db, client?: MongoClient | undefined);
11
19
  findAll(query?: {}): Promise<Model[]>;
12
20
  getById(id: string | ObjectId): Promise<Model | null>;
13
21
  getOne(query?: {}): Promise<Model | null>;
14
22
  create<C = Omit<Model, "_id">>(model: C): Promise<C & {
15
23
  _id: string;
16
24
  }>;
17
- updateOne(id: string | ObjectId, model: Partial<Model>): Promise<Model | null>;
18
- updateMany<U = Partial<Model>>(query: any, model: U): Promise<number>;
25
+ updateOne(id: string | ObjectId, model: PartialModel<Model>): Promise<Model | null>;
26
+ updateMany<U = PartialModel<Model>>(query: any, model: U): Promise<number>;
19
27
  deleteById(id: string | ObjectId): Promise<number>;
20
- private buildId;
28
+ /**
29
+ * Helper to ensure the id is always an ObjectId.
30
+ * If a string is passed, it will be converted to an ObjectId.
31
+ * If an ObjectId is passed, it will be returned as is.
32
+ * @param id
33
+ * @returns
34
+ */
35
+ buildId(id: string | ObjectId): ObjectId;
21
36
  private objectIdToString;
22
37
  }
38
+ export {};
@@ -46,13 +46,25 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
46
46
  if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
47
47
  }
48
48
  };
49
+ var __rest = (this && this.__rest) || function (s, e) {
50
+ var t = {};
51
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
52
+ t[p] = s[p];
53
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
54
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
55
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
56
+ t[p[i]] = s[p[i]];
57
+ }
58
+ return t;
59
+ };
49
60
  Object.defineProperty(exports, "__esModule", { value: true });
50
61
  exports.FlinkRepo = void 0;
51
62
  var mongodb_1 = require("mongodb");
52
63
  var FlinkRepo = /** @class */ (function () {
53
- function FlinkRepo(collectionName, db) {
64
+ function FlinkRepo(collectionName, db, client) {
54
65
  this.collectionName = collectionName;
55
66
  this.db = db;
67
+ this.client = client;
56
68
  this.collection = db.collection(this.collectionName);
57
69
  }
58
70
  Object.defineProperty(FlinkRepo.prototype, "ctx", {
@@ -129,12 +141,13 @@ var FlinkRepo = /** @class */ (function () {
129
141
  };
130
142
  FlinkRepo.prototype.updateOne = function (id, model) {
131
143
  return __awaiter(this, void 0, void 0, function () {
132
- var oid, res;
144
+ var oid, _id, modelWithoutId, res;
133
145
  return __generator(this, function (_a) {
134
146
  switch (_a.label) {
135
147
  case 0:
136
148
  oid = this.buildId(id);
137
- return [4 /*yield*/, this.collection.updateOne({ _id: oid }, { $set: model })];
149
+ _id = model._id, modelWithoutId = __rest(model, ["_id"]);
150
+ return [4 /*yield*/, this.collection.updateOne({ _id: oid }, { $set: modelWithoutId })];
138
151
  case 1:
139
152
  _a.sent();
140
153
  return [4 /*yield*/, this.collection.findOne({ _id: oid })];
@@ -150,14 +163,16 @@ var FlinkRepo = /** @class */ (function () {
150
163
  };
151
164
  FlinkRepo.prototype.updateMany = function (query, model) {
152
165
  return __awaiter(this, void 0, void 0, function () {
153
- var modifiedCount;
154
- return __generator(this, function (_a) {
155
- switch (_a.label) {
156
- case 0: return [4 /*yield*/, this.collection.updateMany(query, {
157
- $set: model,
158
- })];
166
+ var _a, _id, modelWithoutId, modifiedCount;
167
+ return __generator(this, function (_b) {
168
+ switch (_b.label) {
169
+ case 0:
170
+ _a = model, _id = _a._id, modelWithoutId = __rest(_a, ["_id"]);
171
+ return [4 /*yield*/, this.collection.updateMany(query, {
172
+ $set: modelWithoutId,
173
+ })];
159
174
  case 1:
160
- modifiedCount = (_a.sent()).modifiedCount;
175
+ modifiedCount = (_b.sent()).modifiedCount;
161
176
  return [2 /*return*/, modifiedCount];
162
177
  }
163
178
  });
@@ -178,6 +193,13 @@ var FlinkRepo = /** @class */ (function () {
178
193
  });
179
194
  });
180
195
  };
196
+ /**
197
+ * Helper to ensure the id is always an ObjectId.
198
+ * If a string is passed, it will be converted to an ObjectId.
199
+ * If an ObjectId is passed, it will be returned as is.
200
+ * @param id
201
+ * @returns
202
+ */
181
203
  FlinkRepo.prototype.buildId = function (id) {
182
204
  var oid;
183
205
  if (typeof id === "string") {
@@ -3,6 +3,7 @@ declare class TypeScriptCompiler {
3
3
  private cwd;
4
4
  private project;
5
5
  private schemaGenerator?;
6
+ private isEsm;
6
7
  /**
7
8
  * Parsed typescript schemas that will be added to intermediate
8
9
  * schemas.ts file.
@@ -17,6 +18,15 @@ declare class TypeScriptCompiler {
17
18
  */
18
19
  private tsSchemasSymbolsToImports;
19
20
  constructor(cwd: string);
21
+ /**
22
+ * Detects if the project is using ESM (ECMAScript Modules)
23
+ * by checking type in package.json.
24
+ */
25
+ private isEsmProject;
26
+ /**
27
+ * Gets the module specifier for imports, adding .js extension for ESM
28
+ */
29
+ private getModuleSpecifier;
20
30
  /**
21
31
  * Deletes all generated files.
22
32
  * @param cwd
@@ -10,6 +10,29 @@ var __assign = (this && this.__assign) || function () {
10
10
  };
11
11
  return __assign.apply(this, arguments);
12
12
  };
13
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ var desc = Object.getOwnPropertyDescriptor(m, k);
16
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
17
+ desc = { enumerable: true, get: function() { return m[k]; } };
18
+ }
19
+ Object.defineProperty(o, k2, desc);
20
+ }) : (function(o, m, k, k2) {
21
+ if (k2 === undefined) k2 = k;
22
+ o[k2] = m[k];
23
+ }));
24
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
25
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
26
+ }) : function(o, v) {
27
+ o["default"] = v;
28
+ });
29
+ var __importStar = (this && this.__importStar) || function (mod) {
30
+ if (mod && mod.__esModule) return mod;
31
+ var result = {};
32
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
33
+ __setModuleDefault(result, mod);
34
+ return result;
35
+ };
13
36
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
14
37
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
15
38
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -59,7 +82,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
59
82
  return (mod && mod.__esModule) ? mod : { "default": mod };
60
83
  };
61
84
  Object.defineProperty(exports, "__esModule", { value: true });
62
- var fs_1 = require("fs");
85
+ var fs_1 = __importStar(require("fs"));
63
86
  var path_1 = require("path");
64
87
  var tiny_glob_1 = __importDefault(require("tiny-glob"));
65
88
  var ts_json_schema_generator_1 = require("ts-json-schema-generator");
@@ -83,16 +106,62 @@ var TypeScriptCompiler = /** @class */ (function () {
83
106
  * This will be added to file in a batch for performance reasons.
84
107
  */
85
108
  this.tsSchemasSymbolsToImports = [];
109
+ // Detect if project is using ESM based solely on package.json "type": "module"
110
+ this.isEsm = this.isEsmProject(cwd);
111
+ var compilerOptions = {
112
+ noEmit: false, // We need to emit files
113
+ outDir: (0, path_1.join)(cwd, "dist"),
114
+ };
115
+ // Set appropriate module settings based on detected module system
116
+ if (this.isEsm) {
117
+ // For ESM projects, use ESNext module with Node resolution
118
+ compilerOptions.module = ts_morph_1.ts.ModuleKind.ESNext;
119
+ compilerOptions.moduleResolution = ts_morph_1.ts.ModuleResolutionKind.NodeJs;
120
+ compilerOptions.esModuleInterop = true;
121
+ }
122
+ else {
123
+ // For CommonJS projects, use CommonJS module with Node resolution
124
+ compilerOptions.module = ts_morph_1.ts.ModuleKind.CommonJS;
125
+ compilerOptions.moduleResolution = ts_morph_1.ts.ModuleResolutionKind.NodeJs;
126
+ }
86
127
  this.project = new ts_morph_1.Project({
87
128
  tsConfigFilePath: (0, path_1.join)(cwd, "tsconfig.json"),
88
- compilerOptions: {
89
- noEmit: false,
90
- outDir: (0, path_1.join)(cwd, "dist"),
91
- // incremental: true,
92
- },
129
+ compilerOptions: compilerOptions,
93
130
  });
94
131
  console.log("Loaded", this.project.getSourceFiles().length, "source file(s) from", cwd);
132
+ console.log("Module system:", this.isEsm ? "ESM" : "CommonJS");
133
+ console.log("Using module:", compilerOptions.module === ts_morph_1.ts.ModuleKind.ESNext ? "ESNext" : "CommonJS");
95
134
  }
135
+ /**
136
+ * Detects if the project is using ESM (ECMAScript Modules)
137
+ * by checking type in package.json.
138
+ */
139
+ TypeScriptCompiler.prototype.isEsmProject = function (cwd) {
140
+ try {
141
+ // Check package.json for "type": "module"
142
+ var packageJsonPath = (0, path_1.join)(cwd, "package.json");
143
+ if (fs_1.default.existsSync(packageJsonPath)) {
144
+ var packageJson = JSON.parse(fs_1.default.readFileSync(packageJsonPath, "utf8"));
145
+ return packageJson.type === "module";
146
+ }
147
+ }
148
+ catch (error) {
149
+ // If we can't determine, default to CommonJS
150
+ console.warn("Error detecting module system, defaulting to CommonJS:", error);
151
+ }
152
+ return false;
153
+ };
154
+ /**
155
+ * Gets the module specifier for imports, adding .js extension for ESM
156
+ */
157
+ TypeScriptCompiler.prototype.getModuleSpecifier = function (fromFile, toFile) {
158
+ var moduleSpecifier = fromFile.getRelativePathAsModuleSpecifierTo(toFile);
159
+ // Add .js extension for ESM imports (only for relative paths)
160
+ if (this.isEsm && !moduleSpecifier.startsWith("@") && !moduleSpecifier.endsWith(".js")) {
161
+ moduleSpecifier += ".js";
162
+ }
163
+ return moduleSpecifier;
164
+ };
96
165
  /**
97
166
  * Deletes all generated files.
98
167
  * @param cwd
@@ -233,7 +302,7 @@ var TypeScriptCompiler = /** @class */ (function () {
233
302
  namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + i;
234
303
  imports.push({
235
304
  defaultImport: "* as " + namespaceImport,
236
- moduleSpecifier: generatedFile.getRelativePathAsModuleSpecifierTo(sf),
305
+ moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
237
306
  });
238
307
  assumedHttpMethod = (0, utils_1.getHttpMethodFromHandlerName)(sf.getBaseName());
239
308
  return [4 /*yield*/, this.extractSchemasFromHandlerSourceFile(sf)];
@@ -309,7 +378,7 @@ var TypeScriptCompiler = /** @class */ (function () {
309
378
  console.log("Detected repo ".concat(sf.getBaseName()));
310
379
  imports.push({
311
380
  defaultImport: sf.getBaseNameWithoutExtension(),
312
- moduleSpecifier: generatedFile.getRelativePathAsModuleSpecifierTo(sf),
381
+ moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
313
382
  });
314
383
  reposArr.insertElement(i, "{collectionName: \"".concat((0, utils_1.getCollectionNameForRepo)(sf.getBaseName()), "\", repoInstanceName: \"").concat((0, utils_1.getRepoInstanceName)(sf.getBaseName()), "\", Repo: ").concat(sf.getBaseNameWithoutExtension(), "}"));
315
384
  i++;
@@ -341,7 +410,7 @@ var TypeScriptCompiler = /** @class */ (function () {
341
410
  console.error("Cannot find entry script '".concat(appEntryScript, "'"));
342
411
  return [2 /*return*/, process.exit(1)];
343
412
  }
344
- sf = this.createSourceFile(["start.ts"], "// Generated ".concat(new Date(), "\nimport \"./generatedHandlers\";\nimport \"./generatedRepos\";\nimport \"./generatedJobs\";\nimport \"..").concat(appEntryScript.replace(/\.ts/g, ""), "\";\n"));
413
+ sf = this.createSourceFile(["start.ts"], "// Generated ".concat(new Date(), "\nimport \"./generatedHandlers").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedRepos").concat(this.isEsm ? ".js" : "", "\";\nimport \"./generatedJobs").concat(this.isEsm ? ".js" : "", "\";\nimport \"..").concat(appEntryScript.replace(/\.ts/g, "")).concat(this.isEsm ? ".js" : "", "\";\nexport default {}; // Export an empty object to make it a module\n"));
345
414
  return [4 /*yield*/, sf.save()];
346
415
  case 1:
347
416
  _a.sent();
@@ -397,7 +466,7 @@ var TypeScriptCompiler = /** @class */ (function () {
397
466
  };
398
467
  TypeScriptCompiler.prototype.saveIntermediateTsSchema = function (schema, handlerFile, suffix) {
399
468
  return __awaiter(this, void 0, void 0, function () {
400
- var handlerFileName, generatedSchemaInterfaceStr, schemaInterfaceName, schemaSymbol, interfaceName, declaration, _i, _a, typeToImport, arrayTypeArg, schemaSymbol, interfaceName, declaration, props, _b, _c, typeToImport, declaration, typeRefIdentifiers;
469
+ var handlerFileName, generatedSchemaInterfaceStr, schemaInterfaceName, schemaSymbol, interfaceName, declaration, _i, _a, typeToImport, arrayTypeArg, schemaSymbol, interfaceName, declaration, props, _b, _c, typeToImport, declarations, declaration, typeRefIdentifiers;
401
470
  var _this = this;
402
471
  return __generator(this, function (_d) {
403
472
  if (schema.isAny()) {
@@ -454,13 +523,17 @@ var TypeScriptCompiler = /** @class */ (function () {
454
523
  }
455
524
  }
456
525
  else if (schema.isObject()) {
457
- declaration = schema.getSymbolOrThrow().getDeclarations()[0];
458
- typeRefIdentifiers = declaration
459
- .getDescendantsOfKind(ts_morph_1.SyntaxKind.TypeReference)
460
- .map(function (typeRef) { return typeRef.getFirstChildByKindOrThrow(ts_morph_1.SyntaxKind.Identifier); });
461
- typeRefIdentifiers.forEach(function (tr) {
462
- _this.tsSchemasSymbolsToImports.push(tr.getSymbolOrThrow().getDeclaredType().getSymbolOrThrow());
463
- });
526
+ declarations = schema.getSymbolOrThrow().getDeclarations();
527
+ declaration = declarations[0];
528
+ // Only extract type references if declaration exists (won't exist for empty object literals like {})
529
+ if (declaration) {
530
+ typeRefIdentifiers = declaration
531
+ .getDescendantsOfKind(ts_morph_1.SyntaxKind.TypeReference)
532
+ .map(function (typeRef) { return typeRef.getFirstChildByKindOrThrow(ts_morph_1.SyntaxKind.Identifier); });
533
+ typeRefIdentifiers.forEach(function (tr) {
534
+ _this.tsSchemasSymbolsToImports.push(tr.getSymbolOrThrow().getDeclaredType().getSymbolOrThrow());
535
+ });
536
+ }
464
537
  generatedSchemaInterfaceStr = "export interface ".concat(schemaInterfaceName, " { ").concat(schema
465
538
  .getProperties()
466
539
  .map(function (p) { return p.getValueDeclarationOrThrow().getText(); })
@@ -672,7 +745,7 @@ var TypeScriptCompiler = /** @class */ (function () {
672
745
  namespaceImport = sf.getBaseNameWithoutExtension().replace(/\./g, "_") + "_" + i;
673
746
  imports.push({
674
747
  defaultImport: "* as " + namespaceImport,
675
- moduleSpecifier: generatedFile.getRelativePathAsModuleSpecifierTo(sf),
748
+ moduleSpecifier: this.getModuleSpecifier(generatedFile, sf),
676
749
  });
677
750
  // Append metadata to source file that will be part of emitted dist bundle (javascript)
678
751
  sf.addVariableStatement({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flink-app/flink",
3
- "version": "0.12.1-alpha.4",
3
+ "version": "0.12.1-alpha.44",
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",
@@ -9,7 +9,7 @@
9
9
  "test": "jasmine-ts --preserve-symlinks --config=./spec/support/jasmine.json",
10
10
  "test:watch": "nodemon --ext ts --exec 'jasmine-ts --config=./spec/support/jasmine.json'",
11
11
  "start": "ts-node src/index.ts",
12
- "prepublish": "npm run build",
12
+ "prepare": "npm run build",
13
13
  "build": "tsc --project tsconfig.dist.json",
14
14
  "watch": "nodemon --exec 'tsc --project tsconfig.dist.json'",
15
15
  "clean": "rimraf dist"
@@ -38,7 +38,6 @@
38
38
  "fs-extra": "^10.0.0",
39
39
  "mkdirp": "^1.0.4",
40
40
  "mock-json-schema": "^1.0.8",
41
- "mongodb": "^6.15.0",
42
41
  "morgan": "^1.10.0",
43
42
  "ms": "^2.0.0",
44
43
  "node-color-log": "^10.0.2",
@@ -62,9 +61,13 @@
62
61
  "jasmine": "^3.7.0",
63
62
  "jasmine-spec-reporter": "^7.0.0",
64
63
  "jasmine-ts": "^0.3.3",
64
+ "mongodb": "^6.15.0",
65
65
  "nodemon": "^2.0.7",
66
66
  "rimraf": "^3.0.2",
67
67
  "ts-node": "^9.1.1"
68
68
  },
69
- "gitHead": "8f06a4ab98e7179322d36546756d4305fd196f5a"
69
+ "peerDependencies": {
70
+ "mongodb": ">=3.7.0 <7.0.0"
71
+ },
72
+ "gitHead": "4243e3b3cd6d4e1ca001a61baa8436bf2bbe4113"
70
73
  }
@@ -4,6 +4,7 @@ import { FlinkRepo } from "../src/FlinkRepo";
4
4
  interface Model {
5
5
  _id: string;
6
6
  name: string;
7
+ nested?: { field: number };
7
8
  }
8
9
 
9
10
  class Repo extends FlinkRepo<any, Model> {}
@@ -61,9 +62,19 @@ describe("FlinkRepo", () => {
61
62
 
62
63
  const updatedDoc = await repo.updateOne(createdDoc._id + "", {
63
64
  name: "foo",
65
+ "nested.field": 1,
64
66
  });
65
67
 
66
68
  expect(updatedDoc).toBeDefined();
67
69
  expect(updatedDoc?.name).toBe("foo");
70
+ expect(updatedDoc?.nested?.field).toBe(1);
71
+ });
72
+
73
+ it("should update many documents", async () => {
74
+ await collection.insertMany([{ name: "foo" }, { name: "foo" }, { name: "foo" }]);
75
+
76
+ const updatedCount = await repo.updateMany({ name: "foo" }, { name: "bar", "nested.field": 1 });
77
+
78
+ expect(updatedCount).toBe(3);
68
79
  });
69
80
  });
@@ -30,6 +30,14 @@ describe("TypeScriptCompiler", () => {
30
30
  // );
31
31
  });
32
32
 
33
+ it("should handle empty object literal {} as request schema", async () => {
34
+ const generatedFile = await compiler.parseHandlers();
35
+ compiler.emit();
36
+
37
+ // Should successfully parse PostLogout handler with Handler<Ctx, {}, Response> syntax
38
+ expect(generatedFile.getText()).toContain(`PostLogout`);
39
+ });
40
+
33
41
  it("should generate start script", async () => {
34
42
  const startScript = await compiler.generateStartScript();
35
43
 
@@ -55,3 +55,5 @@ var GetCar = function (_a) { return __awaiter(void 0, [_a], void 0, function (_b
55
55
  exports.default = GetCar;
56
56
  exports.__assumedHttpMethod = "get", exports.__file = "GetCar.ts", exports.__query = [{ description: "For pagination", name: "page" }], exports.__params = [{ description: "", name: "id" }];
57
57
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} } };
58
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCar.ts", exports.__query = [{ description: "For pagination", name: "page" }], exports.__params = [{ description: "", name: "id" }];
59
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} } };
@@ -57,3 +57,5 @@ var GetCar2 = function (_a) { return __awaiter(void 0, [_a], void 0, function (_
57
57
  exports.default = GetCar2;
58
58
  exports.__assumedHttpMethod = "get", exports.__file = "GetCar2.ts", exports.__query = [], exports.__params = [];
59
59
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "object", "properties": { "name": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["name"], "additionalProperties": false }, "engine": { "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"], "additionalProperties": false } }, "required": ["model", "engine"], "additionalProperties": false, "definitions": {} } };
60
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCar2.ts", exports.__query = [], exports.__params = [];
61
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "object", "properties": { "name": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["name"], "additionalProperties": false }, "engine": { "type": "object", "properties": { "name": { "type": "string" } }, "required": ["name"], "additionalProperties": false } }, "required": ["model", "engine"], "additionalProperties": false, "definitions": {} } };
@@ -51,3 +51,5 @@ var GetCarWithArraySchema = function (_a) { return __awaiter(void 0, [_a], void
51
51
  exports.default = GetCarWithArraySchema;
52
52
  exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithArraySchema.ts", exports.__query = [], exports.__params = [];
53
53
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "array", "items": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false }, "definitions": {} } };
54
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithArraySchema.ts", exports.__query = [], exports.__params = [];
55
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "array", "items": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false }, "definitions": {} } };
@@ -51,3 +51,5 @@ var GetCarWithArraySchema2 = function (_a) { return __awaiter(void 0, [_a], void
51
51
  exports.default = GetCarWithArraySchema2;
52
52
  exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithArraySchema2.ts", exports.__query = [], exports.__params = [];
53
53
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "array", "items": { "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false }, "definitions": {} } };
54
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithArraySchema2.ts", exports.__query = [], exports.__params = [];
55
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "array", "items": { "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false }, "definitions": {} } };
@@ -51,3 +51,5 @@ var GetCarWithArraySchema3 = function (_a) { return __awaiter(void 0, [_a], void
51
51
  exports.default = GetCarWithArraySchema3;
52
52
  exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithArraySchema3.ts", exports.__query = [], exports.__params = [];
53
53
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "array", "items": { "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false }, "year": { "type": "number" } }, "required": ["car", "year"], "additionalProperties": false }, "definitions": {} } };
54
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithArraySchema3.ts", exports.__query = [], exports.__params = [];
55
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "array", "items": { "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false }, "year": { "type": "number" } }, "required": ["car", "year"], "additionalProperties": false }, "definitions": {} } };
@@ -53,3 +53,5 @@ var GetCarWithLiteralSchema = function (_a) { return __awaiter(void 0, [_a], voi
53
53
  exports.default = GetCarWithLiteralSchema;
54
54
  exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema.ts", exports.__query = [], exports.__params = [];
55
55
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
56
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema.ts", exports.__query = [], exports.__params = [];
57
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
@@ -53,3 +53,5 @@ var GetCarWithLiteralSchema2 = function (_a) { return __awaiter(void 0, [_a], vo
53
53
  exports.default = GetCarWithLiteralSchema2;
54
54
  exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema2.ts", exports.__query = [], exports.__params = [];
55
55
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "nestedCar": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["nestedCar"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
56
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithLiteralSchema2.ts", exports.__query = [], exports.__params = [];
57
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "nestedCar": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["nestedCar"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
@@ -56,3 +56,5 @@ var GetCarWithSchemaInFile = function (_a) { return __awaiter(void 0, [_a], void
56
56
  exports.default = GetCarWithSchemaInFile;
57
57
  exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile.ts", exports.__query = [], exports.__params = [];
58
58
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "string" } }, "required": ["model"], "additionalProperties": false, "definitions": {} } };
59
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile.ts", exports.__query = [], exports.__params = [];
60
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "model": { "type": "string" } }, "required": ["model"], "additionalProperties": false, "definitions": {} } };
@@ -56,3 +56,5 @@ var GetCarWithSchemaInFile2 = function (_a) { return __awaiter(void 0, [_a], voi
56
56
  exports.default = GetCarWithSchemaInFile2;
57
57
  exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile2.ts", exports.__query = [], exports.__params = [];
58
58
  exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
59
+ exports.__assumedHttpMethod = "get", exports.__file = "GetCarWithSchemaInFile2.ts", exports.__query = [], exports.__params = [];
60
+ exports.__schemas = { reqSchema: undefined, resSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "properties": { "car": { "type": "object", "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "additionalProperties": false } }, "required": ["car"], "additionalProperties": false, "definitions": {} } };
@@ -52,3 +52,5 @@ var manuallyAddedHandler = function (_a) { return __awaiter(void 0, [_a], void 0
52
52
  exports.default = manuallyAddedHandler;
53
53
  exports.__assumedHttpMethod = "", exports.__file = "ManuallyAddedHandler.ts", exports.__query = [], exports.__params = [];
54
54
  exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };
55
+ exports.__assumedHttpMethod = "", exports.__file = "ManuallyAddedHandler.ts", exports.__query = [], exports.__params = [];
56
+ exports.__schemas = { reqSchema: { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "properties": { "model": { "type": "string" }, "metadata": { "type": "object", "properties": { "created": { "type": "string", "format": "date-time" } }, "additionalProperties": false } }, "required": ["model"], "definitions": {} }, resSchema: undefined };