@bepalo/spine 1.2.11 → 1.4.13

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.
@@ -28,7 +28,7 @@ var __asyncValues = (this && this.__asyncValues) || function (o) {
28
28
  };
29
29
  var _Router_instances, _Router_config, _Router_routes, _Router_processPath, _Router_getRouteEntries, _Router_InitEntries, _Router_initRoutes;
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
- exports.Router = exports.HANDLER_TYPES = exports.HTTP_METHODS_UPPER = exports.CRUD_METHODS = exports.HTTP_METHODS = exports.REGISTER_PATH_REGEX = exports.PATH_PART_REGEX = void 0;
31
+ exports.translateRouteFilePath = exports.Router = exports.HANDLER_TYPES = exports.HTTP_METHODS_UPPER = exports.CRUD_METHODS = exports.HTTP_METHODS = exports.REGISTER_PATH_REGEX = exports.PATH_PART_REGEX = void 0;
32
32
  const status_ts_1 = require("./status.js");
33
33
  const types_ts_1 = require("./types.js");
34
34
  const utils_node_ts_1 = require("./utils.node.js");
@@ -101,7 +101,7 @@ class Router {
101
101
  respond(request, ctxInit) {
102
102
  return __awaiter(this, void 0, void 0, function* () {
103
103
  var _a, _b, _c, _d, _e, _f;
104
- const requestTimestamp = performance.now();
104
+ const requestTimestamp = Date.now();
105
105
  const method = request.method;
106
106
  const url = new URL(request.url);
107
107
  let pathname;
@@ -331,7 +331,7 @@ class Router {
331
331
  response = new Response(response.body, Object.assign(Object.assign({}, response), { status: response.status, statusText: (0, status_ts_1.getHttpStatusText)(response.status), headers: ctx.headers }));
332
332
  }
333
333
  ctx.response = response;
334
- ctx.timestamps.response = performance.now();
334
+ ctx.timestamps.response = Date.now();
335
335
  // afters
336
336
  if ((_f = __classPrivateFieldGet(this, _Router_config, "f").enable) === null || _f === void 0 ? void 0 : _f.after) {
337
337
  const afterRoutes = __classPrivateFieldGet(this, _Router_instances, "m", _Router_getRouteEntries).call(this, pathname, parts, __classPrivateFieldGet(this, _Router_routes, "f").after[method], false);
@@ -394,7 +394,7 @@ class Router {
394
394
  const pathname = !node.parent
395
395
  ? `/${processedName}`
396
396
  : `/${node.parent}/${processedName}`;
397
- const path = this.translateRouteFilePath(pathname);
397
+ const path = (0, exports.translateRouteFilePath)(pathname, __classPrivateFieldGet(this, _Router_config, "f").maxPath);
398
398
  for (const _method of Object.keys(handlersImp)) {
399
399
  const _definition = handlersImp[_method];
400
400
  let method;
@@ -466,62 +466,172 @@ class Router {
466
466
  });
467
467
  }
468
468
  generateOpenAPI(info) {
469
+ const { title = "API", version = "1.0.0", description, servers = [{ url: "/", description: "Current server" }], security, components: globalComponents, } = info !== null && info !== void 0 ? info : {};
469
470
  return new Promise((resolve) => {
470
- var _a, _b, _c, _d;
471
- const { title = "API", version = "1.0.0" } = info !== null && info !== void 0 ? info : {};
472
- const paths = {};
471
+ var _a, _b, _c;
473
472
  const handlers = __classPrivateFieldGet(this, _Router_routes, "f").handler;
473
+ const paths = {};
474
+ const schemas = {};
475
+ const securitySchemes = {};
476
+ // Group routes by path
477
+ const routeGroups = new Map();
478
+ // Collect all routes
474
479
  for (const method of Object.keys(handlers)) {
475
480
  const methodHandlers = handlers[method];
476
481
  for (const entries of [
477
482
  methodHandlers.entries,
478
483
  methodHandlers.globs,
479
484
  methodHandlers.superGlobs,
480
- // methodHandlers.superGlobsWithGlobs,
481
- ])
485
+ ]) {
482
486
  for (const bucket of entries) {
483
- if (bucket == null) {
487
+ if (bucket == null)
484
488
  continue;
485
- }
486
- for (const key of bucket.keys()) {
487
- const entry = bucket.get(key);
488
- if (entry == null) {
489
+ for (const [, entry] of bucket) {
490
+ if (entry == null)
491
+ continue;
492
+ // Skip super glob routes for OpenAPI (they're catch-alls)
493
+ if (entry.path.endsWith("/**")) {
489
494
  continue;
490
495
  }
491
- const pathname = entry.openApiPath;
492
- const openApi = entry.openApi;
493
- if (openApi != null) {
494
- if (paths[pathname] == null) {
495
- paths[pathname] = {};
496
- }
497
- const methodLower = method.toLowerCase();
498
- const parameters = (_c = (_a = openApi.parameters) !== null && _a !== void 0 ? _a : (_b = entry.params) === null || _b === void 0 ? void 0 : _b.map(([, paramId]) => ({
499
- name: paramId,
500
- in: "path",
501
- required: true,
502
- schema: { type: "string" },
503
- }))) !== null && _c !== void 0 ? _c : [];
504
- const responses = (_d = openApi.responses) !== null && _d !== void 0 ? _d : {
505
- "200": {
506
- description: "OK",
507
- content: {
508
- "application/json": {
509
- schema: { type: "object" },
496
+ let pathMethods = routeGroups.get(entry.openApiPath);
497
+ if (!pathMethods) {
498
+ pathMethods = new Map();
499
+ routeGroups.set(entry.openApiPath, pathMethods);
500
+ }
501
+ pathMethods.set(method, entry);
502
+ }
503
+ }
504
+ }
505
+ }
506
+ // Build paths
507
+ for (const [pathname, methods] of routeGroups) {
508
+ const pathItem = {};
509
+ paths[pathname] = pathItem;
510
+ for (const [method, entry] of methods) {
511
+ const openApi = (_a = entry.openApi) !== null && _a !== void 0 ? _a : {};
512
+ const methodLower = method.toLowerCase();
513
+ // Build parameters - only include path params that are actually in the path
514
+ // console.log(entry);
515
+ const pathParams = [];
516
+ if (entry.params != null) {
517
+ for (const [idx, paramId] of entry.params) {
518
+ // if(entry.pathParts[idx])
519
+ console.log([idx, paramId, entry.pathParts[idx]]);
520
+ pathParams.push({
521
+ name: paramId,
522
+ in: "path",
523
+ required: true,
524
+ schema: { type: "string" },
525
+ });
526
+ }
527
+ }
528
+ // Combine with user-defined parameters
529
+ const userParams = (_b = openApi.parameters) !== null && _b !== void 0 ? _b : [];
530
+ const allParams = [...userParams, ...pathParams];
531
+ // Remove duplicates (by name + in combination)
532
+ const paramSet = new Set();
533
+ const parameters = [];
534
+ for (const param of allParams) {
535
+ const key = `${param.name}:${param.in}`;
536
+ if (!paramSet.has(key)) {
537
+ paramSet.add(key);
538
+ parameters.push(param);
539
+ }
540
+ }
541
+ // Build request body
542
+ const requestBody = openApi.requestBody;
543
+ // Build responses
544
+ const responses = {};
545
+ if (openApi.responses) {
546
+ Object.assign(responses, openApi.responses);
547
+ }
548
+ else {
549
+ // Infer responses from HTTP method
550
+ if (method !== "DELETE" && method !== "HEAD") {
551
+ responses["200"] = {
552
+ description: "Successful response",
553
+ content: {
554
+ "application/json": {
555
+ schema: {
556
+ type: "object",
557
+ properties: {
558
+ data: { type: "object" },
559
+ message: { type: "string" },
510
560
  },
511
561
  },
512
562
  },
513
- };
514
- paths[pathname][methodLower] = Object.assign(Object.assign({}, openApi), { parameters,
515
- responses });
516
- }
563
+ },
564
+ };
565
+ }
566
+ else if (method === "DELETE") {
567
+ responses["204"] = {
568
+ description: "Resource deleted successfully",
569
+ };
570
+ }
571
+ // Add common error responses
572
+ if ((_c = entry.params) === null || _c === void 0 ? void 0 : _c.length) {
573
+ responses["404"] = {
574
+ description: "Resource not found",
575
+ };
517
576
  }
577
+ responses["400"] = {
578
+ description: "Bad request",
579
+ };
580
+ responses["500"] = {
581
+ description: "Internal server error",
582
+ };
518
583
  }
584
+ // Build security
585
+ let operationSecurity = openApi.security;
586
+ if (!operationSecurity && security) {
587
+ operationSecurity = security;
588
+ }
589
+ // Generate clean operation ID
590
+ const operationId = openApi.operationId || generateOperationId(method, pathname);
591
+ // Build operation object
592
+ const operation = {
593
+ summary: openApi.summary,
594
+ description: openApi.description,
595
+ tags: openApi.tags,
596
+ parameters: parameters.length > 0 ? parameters : undefined,
597
+ requestBody,
598
+ responses,
599
+ security: operationSecurity,
600
+ operationId,
601
+ };
602
+ // Remove undefined properties
603
+ const cleanedOperation = Object.fromEntries(Object.entries(operation).filter(([_, value]) => value !== undefined));
604
+ pathItem[methodLower] = cleanedOperation;
605
+ }
519
606
  }
520
- resolve({
607
+ // Merge global components with any collected schemas
608
+ const components = {};
609
+ if (globalComponents === null || globalComponents === void 0 ? void 0 : globalComponents.schemas) {
610
+ Object.assign(schemas, globalComponents.schemas);
611
+ }
612
+ if (Object.keys(schemas).length > 0) {
613
+ components.schemas = schemas;
614
+ }
615
+ if (globalComponents === null || globalComponents === void 0 ? void 0 : globalComponents.securitySchemes) {
616
+ Object.assign(securitySchemes, globalComponents.securitySchemes);
617
+ }
618
+ if (Object.keys(securitySchemes).length > 0) {
619
+ components.securitySchemes = securitySchemes;
620
+ }
621
+ const result = {
521
622
  openapi: "3.0.0",
522
- info: { title, version },
623
+ info: Object.assign({ title,
624
+ version }, (description && { description })),
625
+ servers,
523
626
  paths,
524
- });
627
+ };
628
+ if (Object.keys(components).length > 0) {
629
+ result.components = components;
630
+ }
631
+ if (security && security.length > 0) {
632
+ result.security = security;
633
+ }
634
+ resolve(result);
525
635
  });
526
636
  }
527
637
  all(paths, pipe, options) {
@@ -1069,106 +1179,6 @@ class Router {
1069
1179
  }
1070
1180
  return count;
1071
1181
  }
1072
- translateRouteFilePath(pathname) {
1073
- const parts = pathname.split("/", __classPrivateFieldGet(this, _Router_config, "f").maxPath + 1);
1074
- const parts_len_1 = parts.length - 1;
1075
- let lastPartIsEscaped = false;
1076
- for (let i = 1; i < parts.length; i++) {
1077
- const part = parts[i];
1078
- if (part == null) {
1079
- parts[i] = "";
1080
- continue;
1081
- }
1082
- // validate part
1083
- if (!exports.PATH_PART_REGEX.test(part)) {
1084
- throw new types_ts_1.RouterError(`Invalid path ${pathname} -> ${part}`);
1085
- }
1086
- // parse part
1087
- switch (part) {
1088
- case "":
1089
- break;
1090
- case "[#]":
1091
- parts[i] = "*";
1092
- break;
1093
- case "[##]":
1094
- parts[i] = "**";
1095
- break;
1096
- case "[[#]]":
1097
- parts[i] = "*!";
1098
- break;
1099
- case "[[##]]":
1100
- parts[i] = "**!";
1101
- break;
1102
- default:
1103
- if (part.startsWith("#")) {
1104
- if (i === parts_len_1) {
1105
- lastPartIsEscaped = true;
1106
- }
1107
- parts[i] = part.substring(1);
1108
- }
1109
- else if (part.startsWith("[") && part.endsWith("]")) {
1110
- const nextBracketIdx = part.indexOf("[", 1);
1111
- if (nextBracketIdx < 0) {
1112
- const extractedPart = part.substring(1, part.length - 1);
1113
- // check for ## name
1114
- if (extractedPart.startsWith("##")) {
1115
- parts[i] = "::" + extractedPart.substring(2).trim();
1116
- }
1117
- else {
1118
- // [name]
1119
- parts[i] = ":" + extractedPart;
1120
- }
1121
- }
1122
- else {
1123
- // [[##name]] | [[## name! ]] | [[a,b]name!] | [[a,b] name ] | [[a,b] [name] ]
1124
- const lastBracketIdx = part.indexOf("]", 1);
1125
- const separatorIdx = lastBracketIdx >= 0 ? lastBracketIdx : part.length;
1126
- const paths = part.substring(nextBracketIdx + 1, separatorIdx);
1127
- // [[##name]] | [[## name]]
1128
- if (paths.startsWith("##")) {
1129
- const paramId = paths.substring(2).trim();
1130
- parts[i] = `::${paramId}!`;
1131
- }
1132
- else {
1133
- const paramId = part
1134
- .substring(separatorIdx + 1, part.length - 1)
1135
- .trim();
1136
- // split values a,b,c and replace , with |
1137
- let newPaths = "";
1138
- let lastI = 0;
1139
- for (let i = 0; i < paths.length;) {
1140
- const cc = paths.charCodeAt(i);
1141
- if (cc === 44) {
1142
- newPaths += paths.substring(lastI, i) + "|";
1143
- lastI = ++i;
1144
- continue;
1145
- }
1146
- i++;
1147
- }
1148
- if (lastI < paths.length) {
1149
- newPaths += paths.substring(lastI);
1150
- }
1151
- if (paramId.startsWith("[") && paramId.endsWith("]")) {
1152
- const extrParamId = paramId
1153
- .substring(1, paramId.length - 1)
1154
- .trim();
1155
- parts[i] = `${newPaths}:${extrParamId}!`;
1156
- }
1157
- else {
1158
- parts[i] = newPaths + ":" + paramId;
1159
- }
1160
- }
1161
- }
1162
- }
1163
- }
1164
- }
1165
- if (!lastPartIsEscaped &&
1166
- parts.length > 1 &&
1167
- parts[parts_len_1] === "index") {
1168
- parts[parts_len_1] = "";
1169
- }
1170
- return parts.join("/");
1171
- }
1172
1182
  }
1173
1183
  exports.Router = Router;
1174
1184
  _Router_config = new WeakMap(), _Router_routes = new WeakMap(), _Router_instances = new WeakSet(), _Router_processPath = function _Router_processPath(path) {
@@ -1391,5 +1401,118 @@ const parseParams = (superGlobIndex, params, pathname, parts) => {
1391
1401
  }
1392
1402
  return paramsRec;
1393
1403
  };
1404
+ const translateRouteFilePath = (pathname, maxPath = 64) => {
1405
+ const parts = pathname.split("/", maxPath + 1);
1406
+ const parts_len_1 = parts.length - 1;
1407
+ let lastPartIsEscaped = false;
1408
+ for (let i = 1; i < parts.length; i++) {
1409
+ const part = parts[i];
1410
+ if (part == null) {
1411
+ parts[i] = "";
1412
+ continue;
1413
+ }
1414
+ // validate part
1415
+ if (!exports.PATH_PART_REGEX.test(part)) {
1416
+ throw new types_ts_1.RouterError(`Invalid path ${pathname} -> ${part}`);
1417
+ }
1418
+ // parse part
1419
+ switch (part) {
1420
+ case "":
1421
+ break;
1422
+ case "[#]":
1423
+ parts[i] = "*";
1424
+ break;
1425
+ case "[##]":
1426
+ parts[i] = "**";
1427
+ break;
1428
+ case "[[#]]":
1429
+ parts[i] = "*!";
1430
+ break;
1431
+ case "[[##]]":
1432
+ parts[i] = "**!";
1433
+ break;
1434
+ default:
1435
+ if (part.startsWith("#")) {
1436
+ if (i === parts_len_1) {
1437
+ lastPartIsEscaped = true;
1438
+ }
1439
+ parts[i] = part.substring(1);
1440
+ }
1441
+ else if (part.startsWith("[") && part.endsWith("]")) {
1442
+ const nextBracketIdx = part.indexOf("[", 1);
1443
+ if (nextBracketIdx < 0) {
1444
+ const extractedPart = part.substring(1, part.length - 1);
1445
+ // check for ## name
1446
+ if (extractedPart.startsWith("##")) {
1447
+ parts[i] = "::" + extractedPart.substring(2).trim();
1448
+ }
1449
+ else {
1450
+ // [name]
1451
+ parts[i] = ":" + extractedPart;
1452
+ }
1453
+ }
1454
+ else {
1455
+ // [[##name]] | [[## name! ]] | [[a,b]name!] | [[a,b] name ] | [[a,b] [name] ]
1456
+ const lastBracketIdx = part.indexOf("]", 1);
1457
+ const separatorIdx = lastBracketIdx >= 0 ? lastBracketIdx : part.length;
1458
+ const paths = part.substring(nextBracketIdx + 1, separatorIdx);
1459
+ // [[##name]] | [[## name]]
1460
+ if (paths.startsWith("##")) {
1461
+ const paramId = paths.substring(2).trim();
1462
+ parts[i] = `::${paramId}!`;
1463
+ }
1464
+ else {
1465
+ const paramId = part
1466
+ .substring(separatorIdx + 1, part.length - 1)
1467
+ .trim();
1468
+ // split values a,b,c and replace , with |
1469
+ let newPaths = "";
1470
+ let lastI = 0;
1471
+ for (let i = 0; i < paths.length;) {
1472
+ const cc = paths.charCodeAt(i);
1473
+ if (cc === 44) {
1474
+ newPaths += paths.substring(lastI, i) + "|";
1475
+ lastI = ++i;
1476
+ continue;
1477
+ }
1478
+ i++;
1479
+ }
1480
+ if (lastI < paths.length) {
1481
+ newPaths += paths.substring(lastI);
1482
+ }
1483
+ if (paramId.startsWith("[") && paramId.endsWith("]")) {
1484
+ const extrParamId = paramId
1485
+ .substring(1, paramId.length - 1)
1486
+ .trim();
1487
+ parts[i] = `${newPaths}:${extrParamId}!`;
1488
+ }
1489
+ else {
1490
+ parts[i] = newPaths + ":" + paramId;
1491
+ }
1492
+ }
1493
+ }
1494
+ }
1495
+ }
1496
+ }
1497
+ if (!lastPartIsEscaped &&
1498
+ parts.length > 1 &&
1499
+ parts[parts_len_1] === "index") {
1500
+ parts[parts_len_1] = "";
1501
+ }
1502
+ return parts.join("/");
1503
+ };
1504
+ exports.translateRouteFilePath = translateRouteFilePath;
1505
+ const generateOperationId = (method, path) => {
1506
+ // Remove all special characters and format properly
1507
+ const cleanPath = path
1508
+ .replace(/[{}]/g, "")
1509
+ .replace(/\*/g, "")
1510
+ .replace(/[^a-zA-Z0-9\/]/g, "")
1511
+ .split("/")
1512
+ .filter(Boolean)
1513
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
1514
+ .join("");
1515
+ return `${method.toLowerCase()}${cleanPath}`;
1516
+ };
1394
1517
  exports.default = Router;
1395
1518
  //# sourceMappingURL=router.js.map