@boon4681/giri 0.0.3-alpha-1 → 0.0.3-alpha-3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -48,10 +48,10 @@ var init_es5 = __esm({
48
48
  // src/generator/schema/program.ts
49
49
  function createSchemaProgram(paths, routeFiles) {
50
50
  let options = { ...DEFAULT_OPTIONS };
51
- const configPath = import_typescript2.default.findConfigFile(paths.cwd, import_typescript2.default.sys.fileExists, "tsconfig.json");
51
+ const configPath = import_typescript3.default.findConfigFile(paths.cwd, import_typescript3.default.sys.fileExists, "tsconfig.json");
52
52
  if (configPath) {
53
- const parsed = import_typescript2.default.getParsedCommandLineOfConfigFile(configPath, {}, {
54
- ...import_typescript2.default.sys,
53
+ const parsed = import_typescript3.default.getParsedCommandLineOfConfigFile(configPath, {}, {
54
+ ...import_typescript3.default.sys,
55
55
  onUnRecoverableConfigFileDiagnostic: () => {
56
56
  }
57
57
  });
@@ -59,17 +59,17 @@ function createSchemaProgram(paths, routeFiles) {
59
59
  options = { ...parsed.options, noEmit: true };
60
60
  }
61
61
  }
62
- return import_typescript2.default.createProgram(routeFiles, options);
62
+ return import_typescript3.default.createProgram(routeFiles, options);
63
63
  }
64
- var import_typescript2, DEFAULT_OPTIONS;
64
+ var import_typescript3, DEFAULT_OPTIONS;
65
65
  var init_program = __esm({
66
66
  "src/generator/schema/program.ts"() {
67
67
  "use strict";
68
- import_typescript2 = __toESM(require("typescript"));
68
+ import_typescript3 = __toESM(require("typescript"));
69
69
  DEFAULT_OPTIONS = {
70
- target: import_typescript2.default.ScriptTarget.ES2022,
71
- module: import_typescript2.default.ModuleKind.NodeNext,
72
- moduleResolution: import_typescript2.default.ModuleResolutionKind.NodeNext,
70
+ target: import_typescript3.default.ScriptTarget.ES2022,
71
+ module: import_typescript3.default.ModuleKind.NodeNext,
72
+ moduleResolution: import_typescript3.default.ModuleResolutionKind.NodeNext,
73
73
  strict: true,
74
74
  skipLibCheck: true,
75
75
  noEmit: true
@@ -103,7 +103,7 @@ function literalValuesOf(types) {
103
103
  for (const member of types) {
104
104
  if (member.isStringLiteral() || member.isNumberLiteral()) {
105
105
  values.push(member.value);
106
- } else if (member.flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
106
+ } else if (member.flags & import_typescript4.default.TypeFlags.BooleanLiteral) {
107
107
  values.push(intrinsicName(member) === "true");
108
108
  } else {
109
109
  return void 0;
@@ -112,7 +112,7 @@ function literalValuesOf(types) {
112
112
  return values;
113
113
  }
114
114
  function walkUnion(type, ctx) {
115
- const flag = import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void | import_typescript3.default.TypeFlags.Never;
115
+ const flag = import_typescript4.default.TypeFlags.Undefined | import_typescript4.default.TypeFlags.Void | import_typescript4.default.TypeFlags.Never;
116
116
  const members = type.types.filter((member) => !(member.flags & flag));
117
117
  if (members.length === 1) {
118
118
  return walkType(members[0], ctx);
@@ -125,13 +125,13 @@ function walkUnion(type, ctx) {
125
125
  }
126
126
  function buildObjectSchema(type, ctx) {
127
127
  const { checker } = ctx;
128
- const indexInfo = checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.String) ?? checker.getIndexInfoOfType(type, import_typescript3.default.IndexKind.Number);
128
+ const indexInfo = checker.getIndexInfoOfType(type, import_typescript4.default.IndexKind.String) ?? checker.getIndexInfoOfType(type, import_typescript4.default.IndexKind.Number);
129
129
  const properties = {};
130
130
  const required = [];
131
131
  for (const symbol of checker.getPropertiesOfType(type)) {
132
132
  const name = symbol.getName();
133
133
  const propType = checker.getTypeOfSymbolAtLocation(symbol, ctx.location);
134
- const optional = Boolean(symbol.getFlags() & import_typescript3.default.SymbolFlags.Optional) || Boolean(propType.flags & import_typescript3.default.TypeFlags.Union && propType.types.some((t) => t.flags & import_typescript3.default.TypeFlags.Undefined));
134
+ const optional = Boolean(symbol.getFlags() & import_typescript4.default.SymbolFlags.Optional) || Boolean(propType.flags & import_typescript4.default.TypeFlags.Union && propType.types.some((t) => t.flags & import_typescript4.default.TypeFlags.Undefined));
135
135
  properties[name] = walkType(propType, ctx);
136
136
  if (!optional) {
137
137
  required.push(name);
@@ -190,16 +190,16 @@ function walkObject(type, ctx) {
190
190
  }
191
191
  function walkType(type, ctx) {
192
192
  const flags = type.flags;
193
- if (flags & (import_typescript3.default.TypeFlags.Any | import_typescript3.default.TypeFlags.Unknown)) {
193
+ if (flags & (import_typescript4.default.TypeFlags.Any | import_typescript4.default.TypeFlags.Unknown)) {
194
194
  return {};
195
195
  }
196
- if (flags & import_typescript3.default.TypeFlags.Null) {
196
+ if (flags & import_typescript4.default.TypeFlags.Null) {
197
197
  return { type: "null" };
198
198
  }
199
- if (flags & (import_typescript3.default.TypeFlags.Undefined | import_typescript3.default.TypeFlags.Void)) {
199
+ if (flags & (import_typescript4.default.TypeFlags.Undefined | import_typescript4.default.TypeFlags.Void)) {
200
200
  return {};
201
201
  }
202
- if (flags & (import_typescript3.default.TypeFlags.BigInt | import_typescript3.default.TypeFlags.BigIntLiteral)) {
202
+ if (flags & (import_typescript4.default.TypeFlags.BigInt | import_typescript4.default.TypeFlags.BigIntLiteral)) {
203
203
  ctx.warnings.push("bigint is not JSON-serializable (JSON.stringify throws); documented as string.");
204
204
  return { type: "string" };
205
205
  }
@@ -209,45 +209,45 @@ function walkType(type, ctx) {
209
209
  if (type.isNumberLiteral()) {
210
210
  return { type: "number", const: type.value };
211
211
  }
212
- if (flags & import_typescript3.default.TypeFlags.BooleanLiteral) {
212
+ if (flags & import_typescript4.default.TypeFlags.BooleanLiteral) {
213
213
  return { type: "boolean", const: intrinsicName(type) === "true" };
214
214
  }
215
- if (flags & import_typescript3.default.TypeFlags.String) {
215
+ if (flags & import_typescript4.default.TypeFlags.String) {
216
216
  return { type: "string" };
217
217
  }
218
- if (flags & import_typescript3.default.TypeFlags.Number) {
218
+ if (flags & import_typescript4.default.TypeFlags.Number) {
219
219
  return { type: "number" };
220
220
  }
221
- if (flags & import_typescript3.default.TypeFlags.Boolean) {
221
+ if (flags & import_typescript4.default.TypeFlags.Boolean) {
222
222
  return { type: "boolean" };
223
223
  }
224
224
  if (type.isUnion()) {
225
225
  return walkUnion(type, ctx);
226
226
  }
227
- if (flags & import_typescript3.default.TypeFlags.Object || type.isIntersection()) {
227
+ if (flags & import_typescript4.default.TypeFlags.Object || type.isIntersection()) {
228
228
  return walkObject(type, ctx);
229
229
  }
230
230
  return {};
231
231
  }
232
- var import_typescript3;
232
+ var import_typescript4;
233
233
  var init_json_schema = __esm({
234
234
  "src/generator/schema/json-schema.ts"() {
235
235
  "use strict";
236
- import_typescript3 = __toESM(require("typescript"));
236
+ import_typescript4 = __toESM(require("typescript"));
237
237
  }
238
238
  });
239
239
 
240
240
  // src/generator/schema/responses.ts
241
241
  function findHandleFunction(source) {
242
242
  let found;
243
- const isExported = (node) => import_typescript4.default.canHaveModifiers(node) && (import_typescript4.default.getModifiers(node)?.some((m) => m.kind === import_typescript4.default.SyntaxKind.ExportKeyword) ?? false);
243
+ const isExported = (node) => import_typescript5.default.canHaveModifiers(node) && (import_typescript5.default.getModifiers(node)?.some((m) => m.kind === import_typescript5.default.SyntaxKind.ExportKeyword) ?? false);
244
244
  for (const statement of source.statements) {
245
- if (import_typescript4.default.isFunctionDeclaration(statement) && statement.name?.text === "handle" && isExported(statement)) {
245
+ if (import_typescript5.default.isFunctionDeclaration(statement) && statement.name?.text === "handle" && isExported(statement)) {
246
246
  found = statement;
247
247
  }
248
- if (import_typescript4.default.isVariableStatement(statement) && isExported(statement)) {
248
+ if (import_typescript5.default.isVariableStatement(statement) && isExported(statement)) {
249
249
  for (const declaration of statement.declarationList.declarations) {
250
- if (import_typescript4.default.isIdentifier(declaration.name) && declaration.name.text === "handle" && declaration.initializer && (import_typescript4.default.isArrowFunction(declaration.initializer) || import_typescript4.default.isFunctionExpression(declaration.initializer))) {
250
+ if (import_typescript5.default.isIdentifier(declaration.name) && declaration.name.text === "handle" && declaration.initializer && (import_typescript5.default.isArrowFunction(declaration.initializer) || import_typescript5.default.isFunctionExpression(declaration.initializer))) {
251
251
  found = declaration.initializer;
252
252
  }
253
253
  }
@@ -256,7 +256,7 @@ function findHandleFunction(source) {
256
256
  return found;
257
257
  }
258
258
  function collectReturnExpressions(fn) {
259
- if (import_typescript4.default.isArrowFunction(fn) && !import_typescript4.default.isBlock(fn.body)) {
259
+ if (import_typescript5.default.isArrowFunction(fn) && !import_typescript5.default.isBlock(fn.body)) {
260
260
  return [fn.body];
261
261
  }
262
262
  if (!fn.body) {
@@ -264,15 +264,15 @@ function collectReturnExpressions(fn) {
264
264
  }
265
265
  const expressions = [];
266
266
  const visit = (node) => {
267
- if (import_typescript4.default.isFunctionDeclaration(node) || import_typescript4.default.isFunctionExpression(node) || import_typescript4.default.isArrowFunction(node)) {
267
+ if (import_typescript5.default.isFunctionDeclaration(node) || import_typescript5.default.isFunctionExpression(node) || import_typescript5.default.isArrowFunction(node)) {
268
268
  return;
269
269
  }
270
- if (import_typescript4.default.isReturnStatement(node) && node.expression) {
270
+ if (import_typescript5.default.isReturnStatement(node) && node.expression) {
271
271
  expressions.push(node.expression);
272
272
  }
273
- import_typescript4.default.forEachChild(node, visit);
273
+ import_typescript5.default.forEachChild(node, visit);
274
274
  };
275
- import_typescript4.default.forEachChild(fn.body, visit);
275
+ import_typescript5.default.forEachChild(fn.body, visit);
276
276
  return expressions;
277
277
  }
278
278
  function propertyType(checker, type, name, location) {
@@ -286,10 +286,10 @@ function isTypedResponse(checker, type) {
286
286
  }
287
287
  function firstParameterName(fn) {
288
288
  const [first] = fn.parameters;
289
- return first && import_typescript4.default.isIdentifier(first.name) ? first.name.text : void 0;
289
+ return first && import_typescript5.default.isIdentifier(first.name) ? first.name.text : void 0;
290
290
  }
291
291
  function readFromCall(checker, expression, contextName) {
292
- if (!import_typescript4.default.isCallExpression(expression) || !import_typescript4.default.isPropertyAccessExpression(expression.expression)) {
292
+ if (!import_typescript5.default.isCallExpression(expression) || !import_typescript5.default.isPropertyAccessExpression(expression.expression)) {
293
293
  return void 0;
294
294
  }
295
295
  const method = expression.expression.name.text;
@@ -297,7 +297,7 @@ function readFromCall(checker, expression, contextName) {
297
297
  return void 0;
298
298
  }
299
299
  const target = expression.expression.expression;
300
- const directContextCall = contextName && import_typescript4.default.isIdentifier(target) && target.text === contextName;
300
+ const directContextCall = contextName && import_typescript5.default.isIdentifier(target) && target.text === contextName;
301
301
  if (!directContextCall && !isTypedResponse(checker, checker.getTypeAtLocation(expression))) {
302
302
  return void 0;
303
303
  }
@@ -373,11 +373,11 @@ function extractRouteResponses(program, file) {
373
373
  result.$defs = ctx.defs;
374
374
  return result;
375
375
  }
376
- var import_typescript4;
376
+ var import_typescript5;
377
377
  var init_responses = __esm({
378
378
  "src/generator/schema/responses.ts"() {
379
379
  "use strict";
380
- import_typescript4 = __toESM(require("typescript"));
380
+ import_typescript5 = __toESM(require("typescript"));
381
381
  init_json_schema();
382
382
  }
383
383
  });
@@ -516,6 +516,7 @@ var import_node_fs2 = require("fs");
516
516
  var import_promises = require("fs/promises");
517
517
  var import_node_path2 = require("path");
518
518
  var import_tinyglobby = require("tinyglobby");
519
+ var import_typescript = __toESM(require("typescript"));
519
520
  var METHOD_ORDER = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
520
521
  var METHOD_FROM_FILE = new Map(
521
522
  METHOD_ORDER.map((method) => [`+${method.toLowerCase()}`, method])
@@ -533,6 +534,117 @@ function methodFromFile(fileName) {
533
534
  const stem = fileName.replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, "").toLowerCase();
534
535
  return METHOD_FROM_FILE.get(stem);
535
536
  }
537
+ function hasExportModifier(node) {
538
+ return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.ExportKeyword) ?? false);
539
+ }
540
+ function hasDeclareModifier(node) {
541
+ return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.DeclareKeyword) ?? false);
542
+ }
543
+ function propertyName(node) {
544
+ if (import_typescript.default.isIdentifier(node) || import_typescript.default.isStringLiteralLike(node) || import_typescript.default.isNumericLiteral(node)) {
545
+ return node.text;
546
+ }
547
+ if (import_typescript.default.isPropertyAccessExpression(node)) {
548
+ return node.name.text;
549
+ }
550
+ if (import_typescript.default.isElementAccessExpression(node) && node.argumentExpression) {
551
+ const argument = node.argumentExpression;
552
+ if (import_typescript.default.isStringLiteralLike(argument)) {
553
+ return argument.text;
554
+ }
555
+ }
556
+ return void 0;
557
+ }
558
+ function isExportsObject(node) {
559
+ return import_typescript.default.isIdentifier(node) && node.text === "exports";
560
+ }
561
+ function isModuleExports(node) {
562
+ return import_typescript.default.isPropertyAccessExpression(node) && import_typescript.default.isIdentifier(node.expression) && node.expression.text === "module" && node.name.text === "exports";
563
+ }
564
+ function isCommonJsHandleTarget(node) {
565
+ if (!import_typescript.default.isPropertyAccessExpression(node) && !import_typescript.default.isElementAccessExpression(node)) {
566
+ return false;
567
+ }
568
+ return propertyName(node) === "handle" && (isExportsObject(node.expression) || isModuleExports(node.expression));
569
+ }
570
+ function objectExportsHandle(node) {
571
+ if (!import_typescript.default.isObjectLiteralExpression(node)) {
572
+ return false;
573
+ }
574
+ return node.properties.some((property) => {
575
+ if (import_typescript.default.isShorthandPropertyAssignment(property)) {
576
+ return property.name.text === "handle";
577
+ }
578
+ return (import_typescript.default.isPropertyAssignment(property) || import_typescript.default.isMethodDeclaration(property)) && propertyName(property.name) === "handle";
579
+ });
580
+ }
581
+ function hasNamedHandleExport(source) {
582
+ for (const statement of source.statements) {
583
+ if (hasExportModifier(statement) && !hasDeclareModifier(statement) && import_typescript.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
584
+ return true;
585
+ }
586
+ if (hasExportModifier(statement) && !hasDeclareModifier(statement) && import_typescript.default.isVariableStatement(statement)) {
587
+ if (statement.declarationList.declarations.some(
588
+ (declaration) => import_typescript.default.isIdentifier(declaration.name) && declaration.name.text === "handle"
589
+ )) {
590
+ return true;
591
+ }
592
+ }
593
+ if (import_typescript.default.isExportDeclaration(statement) && !statement.isTypeOnly && statement.exportClause && import_typescript.default.isNamedExports(statement.exportClause)) {
594
+ if (statement.exportClause.elements.some(
595
+ (element) => !element.isTypeOnly && element.name.text === "handle"
596
+ )) {
597
+ return true;
598
+ }
599
+ }
600
+ if (!import_typescript.default.isExpressionStatement(statement) || !import_typescript.default.isBinaryExpression(statement.expression)) {
601
+ continue;
602
+ }
603
+ const assignment = statement.expression;
604
+ if (assignment.operatorToken.kind !== import_typescript.default.SyntaxKind.EqualsToken) {
605
+ continue;
606
+ }
607
+ if (isCommonJsHandleTarget(assignment.left) || isModuleExports(assignment.left) && objectExportsHandle(assignment.right)) {
608
+ return true;
609
+ }
610
+ }
611
+ return false;
612
+ }
613
+ function parseSource(file) {
614
+ return import_typescript.default.createSourceFile(
615
+ file,
616
+ (0, import_node_fs2.readFileSync)(file, "utf8"),
617
+ import_typescript.default.ScriptTarget.Latest,
618
+ true
619
+ );
620
+ }
621
+ function parseDiagnostics(source) {
622
+ return source.parseDiagnostics ?? [];
623
+ }
624
+ function formatSyntaxDiagnostic(diagnostic) {
625
+ const position = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
626
+ const message = import_typescript.default.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
627
+ return `${diagnostic.file.fileName}:${position.line + 1}:${position.character + 1} - error TS${diagnostic.code}: ${message}`;
628
+ }
629
+ function assertSourceSyntax(file) {
630
+ if (!/\.(?:[cm]?[jt]s|[jt]sx)$/i.test(file)) {
631
+ return;
632
+ }
633
+ const diagnostics = parseDiagnostics(parseSource(file));
634
+ if (diagnostics.length > 0) {
635
+ throw new SyntaxError(diagnostics.map(formatSyntaxDiagnostic).join("\n"));
636
+ }
637
+ }
638
+ function assertRouteHandleExport(file) {
639
+ const source = parseSource(file);
640
+ const diagnostics = parseDiagnostics(source);
641
+ if (diagnostics.length > 0) {
642
+ throw new SyntaxError(diagnostics.map(formatSyntaxDiagnostic).join("\n"));
643
+ }
644
+ if (!hasNamedHandleExport(source)) {
645
+ throw new Error(`${file} must export a named handle function.`);
646
+ }
647
+ }
536
648
  function sharedFileIn(dir, cache) {
537
649
  if (cache?.has(dir)) {
538
650
  return cache.get(dir);
@@ -812,6 +924,11 @@ function resolveGiriPaths(config, cwd = process.cwd()) {
812
924
  async function buildGiriApp(config, options = {}) {
813
925
  const paths = resolveGiriPaths(config, options.cwd);
814
926
  const routes = await scanRoutes(paths.routesDir);
927
+ if (options.lazy) {
928
+ for (const route of routes) {
929
+ assertRouteHandleExport(route.file);
930
+ }
931
+ }
815
932
  const app = config.adapter.createApp();
816
933
  ensureGiriAliasResolver(paths.outDir);
817
934
  if (options.lazy && (!options.loaderRegistered || !options.aliasResolverRegistered)) {
@@ -1292,7 +1409,7 @@ async function writeParamTypes(paths, folders) {
1292
1409
 
1293
1410
  // src/generator/route-meta.ts
1294
1411
  var import_node_fs6 = require("fs");
1295
- var import_typescript = __toESM(require("typescript"));
1412
+ var import_typescript2 = __toESM(require("typescript"));
1296
1413
 
1297
1414
  // src/generator/inputs.ts
1298
1415
  function sanitize(schema) {
@@ -1304,7 +1421,12 @@ function inputToJsonSchema(schema) {
1304
1421
  if (!isGiriInputSchema(schema)) {
1305
1422
  return void 0;
1306
1423
  }
1307
- return sanitize(schema.toJsonSchema());
1424
+ try {
1425
+ return sanitize(schema.toJsonSchema());
1426
+ } catch (error) {
1427
+ console.warn(`giri: skipped a request schema that can't be represented as JSON Schema (${error.message}).`);
1428
+ return void 0;
1429
+ }
1308
1430
  }
1309
1431
  function bodyToJsonSchemas(value) {
1310
1432
  if (!isGiriBodySchema(value)) {
@@ -1320,6 +1442,46 @@ function bodyToJsonSchemas(value) {
1320
1442
  return Object.keys(out).length > 0 ? out : void 0;
1321
1443
  }
1322
1444
 
1445
+ // src/generator/schema/route-openapi.ts
1446
+ var import_typebox2 = require("@sinclair/typebox");
1447
+ var import_value2 = require("@sinclair/typebox/value");
1448
+ var StringSchema = import_typebox2.Type.String();
1449
+ var BooleanSchema = import_typebox2.Type.Boolean();
1450
+ var StringArraySchema = import_typebox2.Type.Array(StringSchema);
1451
+ var routeOpenApiSchema = import_typebox2.Type.Object(
1452
+ {
1453
+ hidden: import_typebox2.Type.Optional(BooleanSchema),
1454
+ tags: import_typebox2.Type.Optional(StringArraySchema),
1455
+ summary: import_typebox2.Type.Optional(StringSchema),
1456
+ description: import_typebox2.Type.Optional(StringSchema),
1457
+ deprecated: import_typebox2.Type.Optional(BooleanSchema),
1458
+ operationId: import_typebox2.Type.Optional(StringSchema)
1459
+ },
1460
+ { additionalProperties: true }
1461
+ );
1462
+ function pick(schema, value) {
1463
+ return import_value2.Value.Check(schema, value) ? value : void 0;
1464
+ }
1465
+ function parseRouteOpenApi(value) {
1466
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1467
+ return void 0;
1468
+ }
1469
+ const o = value;
1470
+ const parsed = {};
1471
+ if ("hidden" in o) {
1472
+ parsed.hidden = Boolean(o.hidden);
1473
+ }
1474
+ const tags = pick(StringArraySchema, o.tags);
1475
+ if (tags) {
1476
+ parsed.tags = tags;
1477
+ }
1478
+ parsed.summary = pick(StringSchema, o.summary);
1479
+ parsed.description = pick(StringSchema, o.description);
1480
+ parsed.operationId = pick(StringSchema, o.operationId);
1481
+ parsed.deprecated = pick(BooleanSchema, o.deprecated);
1482
+ return parsed;
1483
+ }
1484
+
1323
1485
  // src/generator/route-meta.ts
1324
1486
  function loadModule2(file) {
1325
1487
  const resolved = require.resolve(file);
@@ -1354,33 +1516,33 @@ function readInput(routeModule) {
1354
1516
  }
1355
1517
  return input.body || input.query ? input : void 0;
1356
1518
  }
1357
- function hasExportModifier(node) {
1358
- return import_typescript.default.canHaveModifiers(node) && (import_typescript.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.ExportKeyword) ?? false);
1519
+ function hasExportModifier2(node) {
1520
+ return import_typescript2.default.canHaveModifiers(node) && (import_typescript2.default.getModifiers(node)?.some((modifier) => modifier.kind === import_typescript2.default.SyntaxKind.ExportKeyword) ?? false);
1359
1521
  }
1360
1522
  function unwrapExpression(expression) {
1361
1523
  let current = expression;
1362
- while (import_typescript.default.isParenthesizedExpression(current) || import_typescript.default.isAsExpression(current) || import_typescript.default.isSatisfiesExpression(current)) {
1524
+ while (import_typescript2.default.isParenthesizedExpression(current) || import_typescript2.default.isAsExpression(current) || import_typescript2.default.isSatisfiesExpression(current)) {
1363
1525
  current = current.expression;
1364
1526
  }
1365
1527
  return current;
1366
1528
  }
1367
1529
  function staticBoolean(expression) {
1368
1530
  const value = unwrapExpression(expression);
1369
- if (value.kind === import_typescript.default.SyntaxKind.TrueKeyword) {
1531
+ if (value.kind === import_typescript2.default.SyntaxKind.TrueKeyword) {
1370
1532
  return true;
1371
1533
  }
1372
- if (value.kind === import_typescript.default.SyntaxKind.FalseKeyword) {
1534
+ if (value.kind === import_typescript2.default.SyntaxKind.FalseKeyword) {
1373
1535
  return false;
1374
1536
  }
1375
1537
  return void 0;
1376
1538
  }
1377
1539
  function staticString(expression) {
1378
1540
  const value = unwrapExpression(expression);
1379
- return import_typescript.default.isStringLiteralLike(value) ? value.text : void 0;
1541
+ return import_typescript2.default.isStringLiteralLike(value) ? value.text : void 0;
1380
1542
  }
1381
1543
  function staticStringArray(expression) {
1382
1544
  const value = unwrapExpression(expression);
1383
- if (!import_typescript.default.isArrayLiteralExpression(value)) {
1545
+ if (!import_typescript2.default.isArrayLiteralExpression(value)) {
1384
1546
  return void 0;
1385
1547
  }
1386
1548
  const strings = [];
@@ -1393,8 +1555,8 @@ function staticStringArray(expression) {
1393
1555
  }
1394
1556
  return strings;
1395
1557
  }
1396
- function propertyName(name) {
1397
- if (import_typescript.default.isIdentifier(name) || import_typescript.default.isStringLiteral(name) || import_typescript.default.isNumericLiteral(name)) {
1558
+ function propertyName2(name) {
1559
+ if (import_typescript2.default.isIdentifier(name) || import_typescript2.default.isStringLiteral(name) || import_typescript2.default.isNumericLiteral(name)) {
1398
1560
  return name.text;
1399
1561
  }
1400
1562
  return void 0;
@@ -1402,7 +1564,7 @@ function propertyName(name) {
1402
1564
  function collectImportedNames(source) {
1403
1565
  const names = /* @__PURE__ */ new Set();
1404
1566
  for (const statement of source.statements) {
1405
- if (!import_typescript.default.isImportDeclaration(statement)) {
1567
+ if (!import_typescript2.default.isImportDeclaration(statement)) {
1406
1568
  continue;
1407
1569
  }
1408
1570
  const clause = statement.importClause;
@@ -1413,10 +1575,10 @@ function collectImportedNames(source) {
1413
1575
  names.add(clause.name.text);
1414
1576
  }
1415
1577
  const bindings = clause.namedBindings;
1416
- if (bindings && import_typescript.default.isNamespaceImport(bindings)) {
1578
+ if (bindings && import_typescript2.default.isNamespaceImport(bindings)) {
1417
1579
  names.add(bindings.name.text);
1418
1580
  }
1419
- if (bindings && import_typescript.default.isNamedImports(bindings)) {
1581
+ if (bindings && import_typescript2.default.isNamedImports(bindings)) {
1420
1582
  for (const element of bindings.elements) {
1421
1583
  names.add(element.name.text);
1422
1584
  }
@@ -1431,11 +1593,11 @@ function expressionReferencesImportedMiddleware(expression, importedNames) {
1431
1593
  if (found) {
1432
1594
  return;
1433
1595
  }
1434
- if (import_typescript.default.isIdentifier(node) && importedNames.has(node.text) && !allowedImportedHelpers.has(node.text)) {
1596
+ if (import_typescript2.default.isIdentifier(node) && importedNames.has(node.text) && !allowedImportedHelpers.has(node.text)) {
1435
1597
  found = true;
1436
1598
  return;
1437
1599
  }
1438
- import_typescript.default.forEachChild(node, visit);
1600
+ import_typescript2.default.forEachChild(node, visit);
1439
1601
  };
1440
1602
  visit(expression);
1441
1603
  return found;
@@ -1446,15 +1608,15 @@ function parseStaticOpenApi(expression) {
1446
1608
  if (boolean !== void 0) {
1447
1609
  return boolean;
1448
1610
  }
1449
- if (!import_typescript.default.isObjectLiteralExpression(value)) {
1611
+ if (!import_typescript2.default.isObjectLiteralExpression(value)) {
1450
1612
  return void 0;
1451
1613
  }
1452
1614
  const openapi = {};
1453
1615
  for (const property of value.properties) {
1454
- if (!import_typescript.default.isPropertyAssignment(property)) {
1616
+ if (!import_typescript2.default.isPropertyAssignment(property)) {
1455
1617
  return void 0;
1456
1618
  }
1457
- const name = propertyName(property.name);
1619
+ const name = propertyName2(property.name);
1458
1620
  if (!name) {
1459
1621
  return void 0;
1460
1622
  }
@@ -1491,7 +1653,7 @@ function parseStaticOpenApi(expression) {
1491
1653
  function readStaticModuleMeta(file) {
1492
1654
  let source;
1493
1655
  try {
1494
- source = import_typescript.default.createSourceFile(file, (0, import_node_fs6.readFileSync)(file, "utf8"), import_typescript.default.ScriptTarget.Latest, true);
1656
+ source = import_typescript2.default.createSourceFile(file, (0, import_node_fs6.readFileSync)(file, "utf8"), import_typescript2.default.ScriptTarget.Latest, true);
1495
1657
  } catch {
1496
1658
  return void 0;
1497
1659
  }
@@ -1500,15 +1662,15 @@ function readStaticModuleMeta(file) {
1500
1662
  const canSkipMiddlewareRuntime = !sourceText.includes("defineMiddleware") && !sourceText.includes(".openapi");
1501
1663
  const meta = { middlewareSecurity: false };
1502
1664
  for (const statement of source.statements) {
1503
- if (import_typescript.default.isImportDeclaration(statement) || import_typescript.default.isInterfaceDeclaration(statement) || import_typescript.default.isTypeAliasDeclaration(statement) || import_typescript.default.isEmptyStatement(statement) || !hasExportModifier(statement)) {
1665
+ if (import_typescript2.default.isImportDeclaration(statement) || import_typescript2.default.isInterfaceDeclaration(statement) || import_typescript2.default.isTypeAliasDeclaration(statement) || import_typescript2.default.isEmptyStatement(statement) || !hasExportModifier2(statement)) {
1504
1666
  continue;
1505
1667
  }
1506
- if (import_typescript.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
1668
+ if (import_typescript2.default.isFunctionDeclaration(statement) && statement.name?.text === "handle") {
1507
1669
  continue;
1508
1670
  }
1509
- if (import_typescript.default.isVariableStatement(statement)) {
1671
+ if (import_typescript2.default.isVariableStatement(statement)) {
1510
1672
  for (const declaration of statement.declarationList.declarations) {
1511
- if (!import_typescript.default.isIdentifier(declaration.name)) {
1673
+ if (!import_typescript2.default.isIdentifier(declaration.name)) {
1512
1674
  return void 0;
1513
1675
  }
1514
1676
  const name = declaration.name.text;
@@ -1522,7 +1684,7 @@ function readStaticModuleMeta(file) {
1522
1684
  }
1523
1685
  meta.openapi = openapi;
1524
1686
  } else if (name === "handle") {
1525
- if (!declaration.initializer || !import_typescript.default.isArrowFunction(declaration.initializer) && !import_typescript.default.isFunctionExpression(declaration.initializer)) {
1687
+ if (!declaration.initializer || !import_typescript2.default.isArrowFunction(declaration.initializer) && !import_typescript2.default.isFunctionExpression(declaration.initializer)) {
1526
1688
  return void 0;
1527
1689
  }
1528
1690
  continue;
@@ -1632,27 +1794,27 @@ function resolveOpenApi(route, routeModule, loadShared) {
1632
1794
  hidden = false;
1633
1795
  return;
1634
1796
  }
1635
- if (!value || typeof value !== "object") {
1797
+ const parsed = parseRouteOpenApi(value);
1798
+ if (!parsed) {
1636
1799
  return;
1637
1800
  }
1638
- const o = value;
1639
- if ("hidden" in o) {
1640
- hidden = Boolean(o.hidden);
1801
+ if (parsed.hidden !== void 0) {
1802
+ hidden = parsed.hidden;
1641
1803
  }
1642
- if (Array.isArray(o.tags)) {
1643
- tags.push(...o.tags.filter((tag2) => typeof tag2 === "string"));
1804
+ if (parsed.tags) {
1805
+ tags.push(...parsed.tags);
1644
1806
  }
1645
- if (typeof o.summary === "string") {
1646
- meta.summary = o.summary;
1807
+ if (parsed.summary !== void 0) {
1808
+ meta.summary = parsed.summary;
1647
1809
  }
1648
- if (typeof o.description === "string") {
1649
- meta.description = o.description;
1810
+ if (parsed.description !== void 0) {
1811
+ meta.description = parsed.description;
1650
1812
  }
1651
- if (typeof o.deprecated === "boolean") {
1652
- meta.deprecated = o.deprecated;
1813
+ if (parsed.deprecated !== void 0) {
1814
+ meta.deprecated = parsed.deprecated;
1653
1815
  }
1654
- if (isVerb && typeof o.operationId === "string") {
1655
- meta.operationId = o.operationId;
1816
+ if (isVerb && parsed.operationId !== void 0) {
1817
+ meta.operationId = parsed.operationId;
1656
1818
  }
1657
1819
  };
1658
1820
  for (const file of route.sharedFiles) {
@@ -2050,7 +2212,7 @@ var import_node_path15 = require("path");
2050
2212
  // src/loader/import-graph.ts
2051
2213
  var import_node_fs9 = require("fs");
2052
2214
  var import_node_path13 = require("path");
2053
- var import_typescript5 = __toESM(require("typescript"));
2215
+ var import_typescript6 = __toESM(require("typescript"));
2054
2216
  var import_tinyglobby3 = require("tinyglobby");
2055
2217
  var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
2056
2218
  var JS_EXT = /\.(?:c|m)?jsx?$/;
@@ -2096,25 +2258,25 @@ function resolveSpecifier(specifier, fromFile, alias, cwd) {
2096
2258
  function importSpecifiers(file) {
2097
2259
  let source;
2098
2260
  try {
2099
- source = import_typescript5.default.createSourceFile(file, (0, import_node_fs9.readFileSync)(file, "utf8"), import_typescript5.default.ScriptTarget.Latest, false);
2261
+ source = import_typescript6.default.createSourceFile(file, (0, import_node_fs9.readFileSync)(file, "utf8"), import_typescript6.default.ScriptTarget.Latest, false);
2100
2262
  } catch {
2101
2263
  return [];
2102
2264
  }
2103
2265
  const specifiers = [];
2104
2266
  const visit = (node) => {
2105
- if ((import_typescript5.default.isImportDeclaration(node) || import_typescript5.default.isExportDeclaration(node)) && node.moduleSpecifier && import_typescript5.default.isStringLiteral(node.moduleSpecifier)) {
2267
+ if ((import_typescript6.default.isImportDeclaration(node) || import_typescript6.default.isExportDeclaration(node)) && node.moduleSpecifier && import_typescript6.default.isStringLiteral(node.moduleSpecifier)) {
2106
2268
  specifiers.push(node.moduleSpecifier.text);
2107
- } else if (import_typescript5.default.isImportEqualsDeclaration(node) && import_typescript5.default.isExternalModuleReference(node.moduleReference) && import_typescript5.default.isStringLiteralLike(node.moduleReference.expression)) {
2269
+ } else if (import_typescript6.default.isImportEqualsDeclaration(node) && import_typescript6.default.isExternalModuleReference(node.moduleReference) && import_typescript6.default.isStringLiteralLike(node.moduleReference.expression)) {
2108
2270
  specifiers.push(node.moduleReference.expression.text);
2109
- } else if (import_typescript5.default.isCallExpression(node)) {
2110
- const isRequire = import_typescript5.default.isIdentifier(node.expression) && node.expression.text === "require";
2111
- const isDynamicImport = node.expression.kind === import_typescript5.default.SyntaxKind.ImportKeyword;
2271
+ } else if (import_typescript6.default.isCallExpression(node)) {
2272
+ const isRequire = import_typescript6.default.isIdentifier(node.expression) && node.expression.text === "require";
2273
+ const isDynamicImport = node.expression.kind === import_typescript6.default.SyntaxKind.ImportKeyword;
2112
2274
  const [first] = node.arguments;
2113
- if ((isRequire || isDynamicImport) && first && import_typescript5.default.isStringLiteralLike(first)) {
2275
+ if ((isRequire || isDynamicImport) && first && import_typescript6.default.isStringLiteralLike(first)) {
2114
2276
  specifiers.push(first.text);
2115
2277
  }
2116
2278
  }
2117
- import_typescript5.default.forEachChild(node, visit);
2279
+ import_typescript6.default.forEachChild(node, visit);
2118
2280
  };
2119
2281
  visit(source);
2120
2282
  return specifiers;
@@ -2135,6 +2297,7 @@ async function buildImportGraph(config, cwd) {
2135
2297
  if (file.startsWith(outDir)) {
2136
2298
  continue;
2137
2299
  }
2300
+ nodes.add(toSlash(file));
2138
2301
  for (const specifier of importSpecifiers(file)) {
2139
2302
  const dep = resolveSpecifier(specifier, file, config.alias, root);
2140
2303
  if (!dep || dep.startsWith(outDir)) {
@@ -2142,7 +2305,6 @@ async function buildImportGraph(config, cwd) {
2142
2305
  }
2143
2306
  const from = toSlash(file);
2144
2307
  const to = toSlash(dep);
2145
- nodes.add(from);
2146
2308
  nodes.add(to);
2147
2309
  let set = importers.get(to);
2148
2310
  if (!set) {
@@ -2204,7 +2366,9 @@ function createWatchUpdater(config, initial) {
2204
2366
  const paths = initial.paths;
2205
2367
  let routes = initial.routes;
2206
2368
  const data = initial.data;
2369
+ let metadataQueue = Promise.resolve();
2207
2370
  const fullResync = async () => {
2371
+ await metadataQueue;
2208
2372
  purgeProjectModules(paths.cwd);
2209
2373
  const result = await syncProject(config, { cwd: paths.cwd });
2210
2374
  routes = result.routes;
@@ -2216,39 +2380,54 @@ function createWatchUpdater(config, initial) {
2216
2380
  purgeGeneratedModules(paths.outDir);
2217
2381
  return "full";
2218
2382
  };
2219
- const reextractRoute = async (route) => {
2220
- const key = route.file;
2383
+ const reextractRoutes = async (affected) => {
2384
+ if (affected.length === 0) {
2385
+ return;
2386
+ }
2221
2387
  try {
2222
2388
  const { createSchemaProgram: createSchemaProgram2, extractRouteResponses: extractRouteResponses2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
2223
2389
  const appTypes = (0, import_node_path15.join)(paths.outDir, "types", "app.d.ts");
2224
- const program = createSchemaProgram2(paths, (0, import_node_fs10.existsSync)(appTypes) ? [key, appTypes] : [key]);
2225
- data.responsesByFile.set(key, extractRouteResponses2(program, key));
2390
+ const files = affected.map((route) => route.file);
2391
+ const program = createSchemaProgram2(
2392
+ paths,
2393
+ (0, import_node_fs10.existsSync)(appTypes) ? [...files, appTypes] : files
2394
+ );
2395
+ for (const route of affected) {
2396
+ data.responsesByFile.set(
2397
+ route.file,
2398
+ extractRouteResponses2(program, route.file)
2399
+ );
2400
+ }
2226
2401
  } catch {
2227
2402
  }
2228
2403
  try {
2229
- const meta = await extractRouteMeta(config, paths, [route]);
2230
- const entry = meta.get(key);
2231
- data.inputsByFile.delete(key);
2232
- data.securityByFile.delete(key);
2233
- data.hiddenFiles.delete(key);
2234
- data.openapiByFile.delete(key);
2235
- if (entry?.input) {
2236
- data.inputsByFile.set(key, entry.input);
2237
- }
2238
- if (entry?.security) {
2239
- data.securityByFile.set(key, entry.security);
2240
- }
2241
- if (entry?.hidden) {
2242
- data.hiddenFiles.add(key);
2243
- }
2244
- if (entry?.openapi) {
2245
- data.openapiByFile.set(key, entry.openapi);
2404
+ const meta = await extractRouteMeta(config, paths, affected);
2405
+ for (const route of affected) {
2406
+ const key = route.file;
2407
+ const entry = meta.get(key);
2408
+ data.inputsByFile.delete(key);
2409
+ data.securityByFile.delete(key);
2410
+ data.hiddenFiles.delete(key);
2411
+ data.openapiByFile.delete(key);
2412
+ if (entry?.input) {
2413
+ data.inputsByFile.set(key, entry.input);
2414
+ }
2415
+ if (entry?.security) {
2416
+ data.securityByFile.set(key, entry.security);
2417
+ }
2418
+ if (entry?.hidden) {
2419
+ data.hiddenFiles.add(key);
2420
+ }
2421
+ if (entry?.openapi) {
2422
+ data.openapiByFile.set(key, entry.openapi);
2423
+ }
2246
2424
  }
2247
2425
  } catch {
2248
2426
  }
2249
2427
  };
2250
2428
  return {
2251
- async apply(filename) {
2429
+ settled: () => metadataQueue,
2430
+ async apply(filename, options = {}) {
2252
2431
  if (!filename) {
2253
2432
  return fullResync();
2254
2433
  }
@@ -2260,9 +2439,21 @@ function createWatchUpdater(config, initial) {
2260
2439
  if ((0, import_node_fs10.statSync)(abs).isDirectory()) {
2261
2440
  return "skip";
2262
2441
  }
2263
- const graph = await buildImportGraph(config, paths.cwd);
2442
+ assertSourceSyntax(abs);
2264
2443
  const isRoute = routes.some((candidate) => slash(candidate.file) === file);
2265
- if (!graph.nodes.has(file) && !isRoute) {
2444
+ const isShared = routes.some(
2445
+ (route) => route.sharedFiles.some((shared) => slash(shared) === file)
2446
+ );
2447
+ const isMethodFile = /^\+(?:get|post|put|patch|delete|options|head)\.(?:[cm]?[jt]s|[jt]sx)$/i.test((0, import_node_path15.basename)(file));
2448
+ const isNewRouteStructure = file.startsWith(`${slash(paths.routesDir)}/`) && /^\+(?:get|post|put|patch|delete|options|head|shared)\.(?:[cm]?[jt]s|[jt]sx)$/i.test((0, import_node_path15.basename)(file)) && !isRoute && !isShared;
2449
+ if (isRoute || isNewRouteStructure && isMethodFile) {
2450
+ assertRouteHandleExport(abs);
2451
+ }
2452
+ if (isNewRouteStructure) {
2453
+ return fullResync();
2454
+ }
2455
+ const graph = await buildImportGraph(config, paths.cwd);
2456
+ if (!graph.nodes.has(file) && !isRoute && !isShared) {
2266
2457
  return fullResync();
2267
2458
  }
2268
2459
  const dependents = collectDependents(graph, file);
@@ -2270,13 +2461,20 @@ function createWatchUpdater(config, initial) {
2270
2461
  (route) => dependents.has(slash(route.file)) || route.sharedFiles.some((shared) => dependents.has(slash(shared)))
2271
2462
  );
2272
2463
  purgeModules(dependents);
2273
- for (const route of affected) {
2274
- await reextractRoute(route);
2464
+ const refreshMetadata = async () => {
2465
+ await reextractRoutes(affected);
2466
+ await writeManifest(paths, routes, data);
2467
+ await writeOpenApi(paths, routes, data);
2468
+ await writeSyncCache(paths, await syncFingerprint(config, paths), data);
2469
+ purgeGeneratedModules(paths.outDir);
2470
+ };
2471
+ const task = metadataQueue.then(refreshMetadata);
2472
+ metadataQueue = task.catch((error) => {
2473
+ console.error("giri: deferred metadata update failed", error);
2474
+ });
2475
+ if (!options.deferMetadata) {
2476
+ await task;
2275
2477
  }
2276
- await writeManifest(paths, routes, data);
2277
- await writeOpenApi(paths, routes, data);
2278
- await writeSyncCache(paths, await syncFingerprint(config, paths), data);
2279
- purgeGeneratedModules(paths.outDir);
2280
2478
  return "incremental";
2281
2479
  }
2282
2480
  };
@@ -2368,6 +2566,12 @@ var tag = {
2368
2566
  warn: color.bold(color.yellow(`[${TAG}]`)),
2369
2567
  error: color.bold(color.red(`[${TAG}]`))
2370
2568
  };
2569
+ function formatError(error) {
2570
+ if (error instanceof Error) {
2571
+ return error.stack ?? `${error.name}: ${error.message}`;
2572
+ }
2573
+ return String(error);
2574
+ }
2371
2575
  var log2 = {
2372
2576
  info(message, scope) {
2373
2577
  console.log(line(tag.info, message, scope));
@@ -2717,7 +2921,9 @@ async function serveProject(config, flags) {
2717
2921
  let fullSync = false;
2718
2922
  const dirtySet = /* @__PURE__ */ new Set();
2719
2923
  for (const name of batch) {
2720
- const outcome = await updater.apply(name || null);
2924
+ const outcome = await updater.apply(name || null, {
2925
+ deferMetadata: true
2926
+ });
2721
2927
  if (outcome === "skip") {
2722
2928
  continue;
2723
2929
  }
@@ -2743,7 +2949,7 @@ async function serveProject(config, flags) {
2743
2949
  }
2744
2950
  }
2745
2951
  } catch (error) {
2746
- log2.error(error instanceof Error ? error.message : String(error), "watch");
2952
+ log2.error(formatError(error), "watch");
2747
2953
  } finally {
2748
2954
  syncing = false;
2749
2955
  }