@boon4681/giri 0.0.1
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/LICENSE +21 -0
- package/README.md +231 -0
- package/dist/adapters/hono.d.ts +7 -0
- package/dist/adapters/hono.js +301 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2070 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +164 -0
- package/dist/index.js +1762 -0
- package/dist/index.js.map +1 -0
- package/dist/types-BrUMxh5u.d.ts +233 -0
- package/dist/typescript-plugin.d.ts +7 -0
- package/dist/typescript-plugin.js +120 -0
- package/dist/typescript-plugin.js.map +1 -0
- package/dist/validators/valibot.d.ts +21 -0
- package/dist/validators/valibot.js +81 -0
- package/dist/validators/valibot.js.map +1 -0
- package/dist/validators/zod.d.ts +26 -0
- package/dist/validators/zod.js +70 -0
- package/dist/validators/zod.js.map +1 -0
- package/package.json +93 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1762 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __esm = (fn, res) => function __init() {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
};
|
|
11
|
+
var __export = (target, all) => {
|
|
12
|
+
for (var name in all)
|
|
13
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
14
|
+
};
|
|
15
|
+
var __copyProps = (to, from, except, desc) => {
|
|
16
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
17
|
+
for (let key of __getOwnPropNames(from))
|
|
18
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
19
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
20
|
+
}
|
|
21
|
+
return to;
|
|
22
|
+
};
|
|
23
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
24
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
25
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
26
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
27
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
28
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
29
|
+
mod
|
|
30
|
+
));
|
|
31
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
|
+
|
|
33
|
+
// src/loader/_es5.ts
|
|
34
|
+
var es5_exports = {};
|
|
35
|
+
__export(es5_exports, {
|
|
36
|
+
default: () => es5_default
|
|
37
|
+
});
|
|
38
|
+
var _, es5_default;
|
|
39
|
+
var init_es5 = __esm({
|
|
40
|
+
"src/loader/_es5.ts"() {
|
|
41
|
+
"use strict";
|
|
42
|
+
_ = "";
|
|
43
|
+
es5_default = _;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// src/index.ts
|
|
48
|
+
var index_exports = {};
|
|
49
|
+
__export(index_exports, {
|
|
50
|
+
buildGiriApp: () => buildGiriApp,
|
|
51
|
+
composeMiddleware: () => composeMiddleware,
|
|
52
|
+
createContext: () => createContext,
|
|
53
|
+
createTypedResponse: () => createTypedResponse,
|
|
54
|
+
defineBodySchema: () => defineBodySchema,
|
|
55
|
+
defineConfig: () => defineConfig,
|
|
56
|
+
defineInputSchema: () => defineInputSchema,
|
|
57
|
+
defineMiddleware: () => defineMiddleware,
|
|
58
|
+
isGiriBodySchema: () => isGiriBodySchema,
|
|
59
|
+
isGiriInputSchema: () => isGiriInputSchema,
|
|
60
|
+
isTypedResponse: () => isTypedResponse,
|
|
61
|
+
loadLifecycle: () => loadLifecycle,
|
|
62
|
+
prepareRequestInput: () => prepareRequestInput,
|
|
63
|
+
resolveGiriPaths: () => resolveGiriPaths,
|
|
64
|
+
runInit: () => runInit,
|
|
65
|
+
scanRoutes: () => scanRoutes,
|
|
66
|
+
stack: () => stack,
|
|
67
|
+
syncProject: () => syncProject,
|
|
68
|
+
toResponse: () => toResponse,
|
|
69
|
+
typedResponseToResponse: () => typedResponseToResponse
|
|
70
|
+
});
|
|
71
|
+
module.exports = __toCommonJS(index_exports);
|
|
72
|
+
|
|
73
|
+
// src/types.ts
|
|
74
|
+
var typedResponseBrand = /* @__PURE__ */ Symbol.for("giri.typed-response");
|
|
75
|
+
var inputSchemaBrand = /* @__PURE__ */ Symbol.for("giri.input-schema");
|
|
76
|
+
var bodySchemaBrand = /* @__PURE__ */ Symbol.for("giri.body-schema");
|
|
77
|
+
|
|
78
|
+
// src/context.ts
|
|
79
|
+
var BODYLESS_STATUS = /* @__PURE__ */ new Set([101, 103, 204, 205, 304]);
|
|
80
|
+
function createTypedResponse(data, status, format, headers) {
|
|
81
|
+
return {
|
|
82
|
+
[typedResponseBrand]: { data, status, format },
|
|
83
|
+
data,
|
|
84
|
+
status,
|
|
85
|
+
format,
|
|
86
|
+
headers
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function isTypedResponse(value) {
|
|
90
|
+
return Boolean(value && typeof value === "object" && typedResponseBrand in value);
|
|
91
|
+
}
|
|
92
|
+
function createContext(options) {
|
|
93
|
+
const url = new URL(options.request.url);
|
|
94
|
+
const store = /* @__PURE__ */ new Map();
|
|
95
|
+
const validated = options.validated ?? {};
|
|
96
|
+
return {
|
|
97
|
+
params: options.params ?? {},
|
|
98
|
+
app: options.app ?? {},
|
|
99
|
+
req: {
|
|
100
|
+
raw: options.request,
|
|
101
|
+
url,
|
|
102
|
+
method: options.request.method,
|
|
103
|
+
header: (name) => options.request.headers.get(name),
|
|
104
|
+
json: () => options.request.json(),
|
|
105
|
+
text: () => options.request.text(),
|
|
106
|
+
arrayBuffer: () => options.request.arrayBuffer(),
|
|
107
|
+
formData: () => options.request.formData(),
|
|
108
|
+
valid: (key) => {
|
|
109
|
+
if (!(key in validated)) {
|
|
110
|
+
throw new Error(`No validated ${String(key)} data is available for this route.`);
|
|
111
|
+
}
|
|
112
|
+
return validated[key];
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
set: (key, value) => {
|
|
116
|
+
store.set(key, value);
|
|
117
|
+
},
|
|
118
|
+
get: (key) => store.get(key),
|
|
119
|
+
json: (data, status = 200, headers) => createTypedResponse(data, status, "json", headers),
|
|
120
|
+
text: (text, status = 200, headers) => createTypedResponse(text, status, "text", headers)
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function typedResponseToResponse(response) {
|
|
124
|
+
const headers = new Headers(response.headers);
|
|
125
|
+
if (response.format === "json" && !headers.has("content-type")) {
|
|
126
|
+
headers.set("content-type", "application/json; charset=utf-8");
|
|
127
|
+
}
|
|
128
|
+
if (response.format === "text" && !headers.has("content-type")) {
|
|
129
|
+
headers.set("content-type", "text/plain; charset=utf-8");
|
|
130
|
+
}
|
|
131
|
+
const body = BODYLESS_STATUS.has(response.status) ? null : response.format === "json" ? JSON.stringify(response.data) : String(response.data);
|
|
132
|
+
return new Response(body, {
|
|
133
|
+
status: response.status,
|
|
134
|
+
headers
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function toResponse(response) {
|
|
138
|
+
return isTypedResponse(response) ? typedResponseToResponse(response) : response;
|
|
139
|
+
}
|
|
140
|
+
async function composeMiddleware(middleware, handle, context) {
|
|
141
|
+
let index = -1;
|
|
142
|
+
let result;
|
|
143
|
+
const dispatch = async (i) => {
|
|
144
|
+
if (i <= index) {
|
|
145
|
+
throw new Error("next() called multiple times in giri middleware.");
|
|
146
|
+
}
|
|
147
|
+
index = i;
|
|
148
|
+
if (i === middleware.length) {
|
|
149
|
+
result = await handle(context);
|
|
150
|
+
return result;
|
|
151
|
+
}
|
|
152
|
+
const returned = await middleware[i](context, () => dispatch(i + 1));
|
|
153
|
+
if (returned !== void 0) {
|
|
154
|
+
result = returned;
|
|
155
|
+
return returned;
|
|
156
|
+
}
|
|
157
|
+
return result;
|
|
158
|
+
};
|
|
159
|
+
await dispatch(0);
|
|
160
|
+
if (result === void 0) {
|
|
161
|
+
throw new Error("Route completed without returning a response.");
|
|
162
|
+
}
|
|
163
|
+
return result;
|
|
164
|
+
}
|
|
165
|
+
function defineMiddleware(optionsOrMiddleware, maybeMiddleware) {
|
|
166
|
+
if (typeof optionsOrMiddleware === "function") {
|
|
167
|
+
return optionsOrMiddleware;
|
|
168
|
+
}
|
|
169
|
+
if (!maybeMiddleware) {
|
|
170
|
+
throw new Error("defineMiddleware(options, middleware) requires a middleware function.");
|
|
171
|
+
}
|
|
172
|
+
maybeMiddleware.openapi = optionsOrMiddleware.openapi;
|
|
173
|
+
return maybeMiddleware;
|
|
174
|
+
}
|
|
175
|
+
function stack(...middleware) {
|
|
176
|
+
return middleware;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// src/validation.ts
|
|
180
|
+
function defineInputSchema(schema) {
|
|
181
|
+
return { [inputSchemaBrand]: true, ...schema };
|
|
182
|
+
}
|
|
183
|
+
function isGiriInputSchema(value) {
|
|
184
|
+
return Boolean(
|
|
185
|
+
value && typeof value === "object" && value[inputSchemaBrand] === true
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
function defineBodySchema(contents) {
|
|
189
|
+
return { [bodySchemaBrand]: true, contents };
|
|
190
|
+
}
|
|
191
|
+
function isGiriBodySchema(value) {
|
|
192
|
+
return Boolean(
|
|
193
|
+
value && typeof value === "object" && value[bodySchemaBrand] === true
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
var MIME_TO_CONTENT_TYPE = {
|
|
197
|
+
"application/json": "json",
|
|
198
|
+
"multipart/form-data": "form",
|
|
199
|
+
"application/x-www-form-urlencoded": "urlencoded",
|
|
200
|
+
"text/plain": "text"
|
|
201
|
+
};
|
|
202
|
+
function contentTypeFromHeader(header) {
|
|
203
|
+
if (!header) {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
const mime = header.split(";", 1)[0].trim().toLowerCase();
|
|
207
|
+
return MIME_TO_CONTENT_TYPE[mime];
|
|
208
|
+
}
|
|
209
|
+
function formDataObject(form) {
|
|
210
|
+
const result = {};
|
|
211
|
+
form.forEach((value, key) => {
|
|
212
|
+
const current = result[key];
|
|
213
|
+
if (current === void 0) {
|
|
214
|
+
result[key] = value;
|
|
215
|
+
} else if (Array.isArray(current)) {
|
|
216
|
+
current.push(value);
|
|
217
|
+
} else {
|
|
218
|
+
result[key] = [current, value];
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
return result;
|
|
222
|
+
}
|
|
223
|
+
async function readRawBody(request, contentType) {
|
|
224
|
+
const cloned = request.clone();
|
|
225
|
+
if (contentType === "json") {
|
|
226
|
+
return cloned.json();
|
|
227
|
+
}
|
|
228
|
+
if (contentType === "text") {
|
|
229
|
+
return cloned.text();
|
|
230
|
+
}
|
|
231
|
+
return formDataObject(await cloned.formData());
|
|
232
|
+
}
|
|
233
|
+
function queryObject(url) {
|
|
234
|
+
const result = {};
|
|
235
|
+
for (const [key, value] of url.searchParams) {
|
|
236
|
+
const current = result[key];
|
|
237
|
+
if (current === void 0) {
|
|
238
|
+
result[key] = value;
|
|
239
|
+
} else if (Array.isArray(current)) {
|
|
240
|
+
current.push(value);
|
|
241
|
+
} else {
|
|
242
|
+
result[key] = [current, value];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return result;
|
|
246
|
+
}
|
|
247
|
+
async function runValidation(schema, value, label) {
|
|
248
|
+
if (!isGiriInputSchema(schema)) {
|
|
249
|
+
throw new Error(
|
|
250
|
+
`giri: ${label} schema must be wrapped with a validator, e.g. \`export const ${label} = zod(...)\` from @boon4681/giri/validators/zod.`
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
return schema.validate(value);
|
|
254
|
+
}
|
|
255
|
+
async function prepareRequestInput(request, input) {
|
|
256
|
+
const validated = {};
|
|
257
|
+
if (input?.query) {
|
|
258
|
+
const query = queryObject(new URL(request.url));
|
|
259
|
+
const result = await runValidation(input.query, query, "query");
|
|
260
|
+
if (!result.ok) {
|
|
261
|
+
return {
|
|
262
|
+
ok: false,
|
|
263
|
+
response: createTypedResponse(
|
|
264
|
+
{ message: "Invalid query parameters.", issues: result.issues },
|
|
265
|
+
400,
|
|
266
|
+
"json"
|
|
267
|
+
)
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
validated.query = result.value;
|
|
271
|
+
}
|
|
272
|
+
if (input?.body) {
|
|
273
|
+
const contents = input.body.contents;
|
|
274
|
+
const declared = Object.keys(contents);
|
|
275
|
+
const requested = contentTypeFromHeader(request.headers.get("content-type"));
|
|
276
|
+
const chosen = requested && contents[requested] ? requested : contents.json ? "json" : void 0;
|
|
277
|
+
if (!chosen) {
|
|
278
|
+
return {
|
|
279
|
+
ok: false,
|
|
280
|
+
response: createTypedResponse(
|
|
281
|
+
{ message: "Unsupported media type.", issues: { accepted: declared } },
|
|
282
|
+
415,
|
|
283
|
+
"json"
|
|
284
|
+
)
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
let rawBody;
|
|
288
|
+
try {
|
|
289
|
+
rawBody = await readRawBody(request, chosen);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
return {
|
|
292
|
+
ok: false,
|
|
293
|
+
response: createTypedResponse(
|
|
294
|
+
{ message: "Invalid request body.", issues: error },
|
|
295
|
+
400,
|
|
296
|
+
"json"
|
|
297
|
+
)
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const result = await runValidation(contents[chosen], rawBody, "body");
|
|
301
|
+
if (!result.ok) {
|
|
302
|
+
return {
|
|
303
|
+
ok: false,
|
|
304
|
+
response: createTypedResponse(
|
|
305
|
+
{ message: "Invalid request body.", issues: result.issues },
|
|
306
|
+
400,
|
|
307
|
+
"json"
|
|
308
|
+
)
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
validated.body = declared.length > 1 ? { type: chosen, data: result.value } : result.value;
|
|
312
|
+
}
|
|
313
|
+
return { ok: true, validated };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/app.ts
|
|
317
|
+
var import_node_module = __toESM(require("module"));
|
|
318
|
+
var import_node_path3 = require("path");
|
|
319
|
+
|
|
320
|
+
// src/loader/loader.ts
|
|
321
|
+
var import_prompts = require("@clack/prompts");
|
|
322
|
+
var import_node_fs = require("fs");
|
|
323
|
+
var import_node_path = require("path");
|
|
324
|
+
var import_node_process = require("process");
|
|
325
|
+
|
|
326
|
+
// src/config/schema.ts
|
|
327
|
+
var import_typebox = require("@sinclair/typebox");
|
|
328
|
+
var configSchema = import_typebox.Type.Object({
|
|
329
|
+
adapter: import_typebox.Type.Any(),
|
|
330
|
+
alias: import_typebox.Type.Optional(import_typebox.Type.Record(
|
|
331
|
+
import_typebox.Type.String(),
|
|
332
|
+
import_typebox.Type.Union([import_typebox.Type.String(), import_typebox.Type.Array(import_typebox.Type.String())])
|
|
333
|
+
)),
|
|
334
|
+
outDir: import_typebox.Type.Optional(import_typebox.Type.String()),
|
|
335
|
+
server: import_typebox.Type.Optional(import_typebox.Type.Object({
|
|
336
|
+
port: import_typebox.Type.Optional(import_typebox.Type.Number()),
|
|
337
|
+
hostname: import_typebox.Type.Optional(import_typebox.Type.String())
|
|
338
|
+
}, { additionalProperties: false })),
|
|
339
|
+
errorSchema: import_typebox.Type.Optional(import_typebox.Type.Any())
|
|
340
|
+
}, { additionalProperties: false });
|
|
341
|
+
|
|
342
|
+
// src/loader/loader.ts
|
|
343
|
+
var import_value = require("@sinclair/typebox/value");
|
|
344
|
+
var assertES5 = async (unregister) => {
|
|
345
|
+
try {
|
|
346
|
+
init_es5();
|
|
347
|
+
} catch (e) {
|
|
348
|
+
if ("errors" in e && Array.isArray(e.errors) && e.errors.length > 0) {
|
|
349
|
+
const es5Error = e.errors.filter((it) => it.text?.includes(`("es5") is not supported yet`)).length > 0;
|
|
350
|
+
if (es5Error) {
|
|
351
|
+
import_prompts.log.error(
|
|
352
|
+
`Please change compilerOptions.target from 'es5' to 'es6' or above in your tsconfig.json`
|
|
353
|
+
);
|
|
354
|
+
(0, import_node_process.exit)(1);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
import_prompts.log.error(e);
|
|
358
|
+
(0, import_node_process.exit)(1);
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
var safeRegister = async () => {
|
|
362
|
+
const { register } = await import("esbuild-register/dist/node");
|
|
363
|
+
let res;
|
|
364
|
+
try {
|
|
365
|
+
res = register({
|
|
366
|
+
format: "cjs",
|
|
367
|
+
loader: "ts"
|
|
368
|
+
});
|
|
369
|
+
} catch {
|
|
370
|
+
res = {
|
|
371
|
+
unregister: () => {
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
await assertES5(res.unregister);
|
|
376
|
+
return res;
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
// src/routes.ts
|
|
380
|
+
var import_node_fs2 = require("fs");
|
|
381
|
+
var import_promises = require("fs/promises");
|
|
382
|
+
var import_node_path2 = require("path");
|
|
383
|
+
var import_tinyglobby = require("tinyglobby");
|
|
384
|
+
var METHOD_ORDER = ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS", "HEAD"];
|
|
385
|
+
var METHOD_FROM_FILE = new Map(
|
|
386
|
+
METHOD_ORDER.map((method) => [`+${method.toLowerCase()}`, method])
|
|
387
|
+
);
|
|
388
|
+
function normalizeSlashes(path) {
|
|
389
|
+
return path.split(import_node_path2.sep).join("/");
|
|
390
|
+
}
|
|
391
|
+
function isRouteSourceFile(fileName) {
|
|
392
|
+
return /\.(?:[cm]?[jt]s|[jt]sx)$/.test(fileName) && !fileName.endsWith(".d.ts");
|
|
393
|
+
}
|
|
394
|
+
function methodFromFile(fileName) {
|
|
395
|
+
if (!isRouteSourceFile(fileName)) {
|
|
396
|
+
return void 0;
|
|
397
|
+
}
|
|
398
|
+
const stem = fileName.replace(/\.(?:[cm]?[jt]s|[jt]sx)$/, "").toLowerCase();
|
|
399
|
+
return METHOD_FROM_FILE.get(stem);
|
|
400
|
+
}
|
|
401
|
+
function sharedFileIn(dir) {
|
|
402
|
+
for (const ext of ["ts", "tsx", "js", "jsx", "mjs", "cjs", "mts", "cts"]) {
|
|
403
|
+
const file = (0, import_node_path2.join)(dir, `+shared.${ext}`);
|
|
404
|
+
if ((0, import_node_fs2.existsSync)(file)) {
|
|
405
|
+
return file;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return void 0;
|
|
409
|
+
}
|
|
410
|
+
function physicalRouteSegments(routesDir, routeDir) {
|
|
411
|
+
const rel = (0, import_node_path2.relative)(routesDir, routeDir);
|
|
412
|
+
if (!rel) {
|
|
413
|
+
return [];
|
|
414
|
+
}
|
|
415
|
+
return normalizeSlashes(rel).split("/").filter(Boolean);
|
|
416
|
+
}
|
|
417
|
+
function urlSegment(segment) {
|
|
418
|
+
if (/^\(.+\)$/.test(segment)) {
|
|
419
|
+
return {};
|
|
420
|
+
}
|
|
421
|
+
const catchAll = /^\[\.\.\.(.+)\]$/.exec(segment);
|
|
422
|
+
if (catchAll) {
|
|
423
|
+
const name = catchAll[1];
|
|
424
|
+
return {
|
|
425
|
+
value: `:${name}{.*}`,
|
|
426
|
+
param: { name, catchAll: true }
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
const param = /^\[(.+)\]$/.exec(segment);
|
|
430
|
+
if (param) {
|
|
431
|
+
const name = param[1];
|
|
432
|
+
return {
|
|
433
|
+
value: `:${name}`,
|
|
434
|
+
param: { name, catchAll: false }
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
return { value: segment };
|
|
438
|
+
}
|
|
439
|
+
function pathFromSegments(segments) {
|
|
440
|
+
const pathSegments = [];
|
|
441
|
+
const params = [];
|
|
442
|
+
for (const segment of segments) {
|
|
443
|
+
const converted = urlSegment(segment);
|
|
444
|
+
if (converted.value) {
|
|
445
|
+
pathSegments.push(converted.value);
|
|
446
|
+
}
|
|
447
|
+
if (converted.param) {
|
|
448
|
+
params.push(converted.param);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return {
|
|
452
|
+
path: pathSegments.length > 0 ? `/${pathSegments.join("/")}` : "/",
|
|
453
|
+
params
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
async function scanRouteFolders(routesDir) {
|
|
457
|
+
if (!(0, import_node_fs2.existsSync)(routesDir)) {
|
|
458
|
+
return [];
|
|
459
|
+
}
|
|
460
|
+
const folders = [routesDir];
|
|
461
|
+
const walk = async (dir) => {
|
|
462
|
+
for (const entry of await (0, import_promises.readdir)(dir, { withFileTypes: true })) {
|
|
463
|
+
if (entry.isDirectory() && entry.name !== "node_modules") {
|
|
464
|
+
const full = (0, import_node_path2.join)(dir, entry.name);
|
|
465
|
+
folders.push(full);
|
|
466
|
+
await walk(full);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
await walk(routesDir);
|
|
471
|
+
return folders;
|
|
472
|
+
}
|
|
473
|
+
function routeParamsForDir(routesDir, dir) {
|
|
474
|
+
return pathFromSegments(physicalRouteSegments(routesDir, dir)).params;
|
|
475
|
+
}
|
|
476
|
+
function sharedFilesForDir(routesDir, dir) {
|
|
477
|
+
const segments = physicalRouteSegments(routesDir, dir);
|
|
478
|
+
const dirs = [routesDir];
|
|
479
|
+
let current = routesDir;
|
|
480
|
+
for (const segment of segments) {
|
|
481
|
+
current = (0, import_node_path2.join)(current, segment);
|
|
482
|
+
dirs.push(current);
|
|
483
|
+
}
|
|
484
|
+
return dirs.map(sharedFileIn).filter((file) => Boolean(file));
|
|
485
|
+
}
|
|
486
|
+
async function scanRoutes(routesDir) {
|
|
487
|
+
if (!(0, import_node_fs2.existsSync)(routesDir)) {
|
|
488
|
+
return [];
|
|
489
|
+
}
|
|
490
|
+
const files = await (0, import_tinyglobby.glob)("**/+*.{ts,tsx,js,jsx,mjs,cjs,mts,cts}", {
|
|
491
|
+
cwd: routesDir,
|
|
492
|
+
absolute: true,
|
|
493
|
+
onlyFiles: true
|
|
494
|
+
});
|
|
495
|
+
const routes = [];
|
|
496
|
+
for (const file of files) {
|
|
497
|
+
const method = methodFromFile((0, import_node_path2.basename)(file));
|
|
498
|
+
if (!method) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
const routeDir = (0, import_node_path2.dirname)(file);
|
|
502
|
+
const routeSegments = physicalRouteSegments(routesDir, routeDir);
|
|
503
|
+
const { path, params } = pathFromSegments(routeSegments);
|
|
504
|
+
routes.push({
|
|
505
|
+
method,
|
|
506
|
+
path,
|
|
507
|
+
file,
|
|
508
|
+
routeDir,
|
|
509
|
+
routeSegments,
|
|
510
|
+
params,
|
|
511
|
+
sharedFiles: sharedFilesForDir(routesDir, routeDir)
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
return routes.sort((left, right) => {
|
|
515
|
+
const pathOrder = left.path.localeCompare(right.path);
|
|
516
|
+
if (pathOrder !== 0) {
|
|
517
|
+
return pathOrder;
|
|
518
|
+
}
|
|
519
|
+
return METHOD_ORDER.indexOf(left.method) - METHOD_ORDER.indexOf(right.method);
|
|
520
|
+
});
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// src/app.ts
|
|
524
|
+
function loadModule(file) {
|
|
525
|
+
const resolved = require.resolve(file);
|
|
526
|
+
delete require.cache[resolved];
|
|
527
|
+
return require(resolved);
|
|
528
|
+
}
|
|
529
|
+
function interopDefault(value) {
|
|
530
|
+
if (value && typeof value === "object" && "default" in value) {
|
|
531
|
+
return value.default;
|
|
532
|
+
}
|
|
533
|
+
return value;
|
|
534
|
+
}
|
|
535
|
+
function normalizeMiddleware(value, file) {
|
|
536
|
+
const exported = interopDefault(value);
|
|
537
|
+
if (exported === void 0) {
|
|
538
|
+
return [];
|
|
539
|
+
}
|
|
540
|
+
if (typeof exported === "function") {
|
|
541
|
+
return [exported];
|
|
542
|
+
}
|
|
543
|
+
if (Array.isArray(exported)) {
|
|
544
|
+
for (const middleware of exported) {
|
|
545
|
+
if (typeof middleware !== "function") {
|
|
546
|
+
throw new Error(`Middleware export in ${file} must contain only functions.`);
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
return exported;
|
|
550
|
+
}
|
|
551
|
+
throw new Error(`Middleware export in ${file} must be a function or an array of functions.`);
|
|
552
|
+
}
|
|
553
|
+
function assertBodySchema(value, file) {
|
|
554
|
+
if (!isGiriBodySchema(value)) {
|
|
555
|
+
throw new Error(
|
|
556
|
+
`${file}: "body" must be wrapped with a validator, e.g. \`export const body = zod.body({ json: ... })\` from @boon4681/giri/validators/zod.`
|
|
557
|
+
);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
function assertQuerySchema(value, file) {
|
|
561
|
+
if (!isGiriInputSchema(value)) {
|
|
562
|
+
throw new Error(
|
|
563
|
+
`${file}: "query" must be wrapped with a validator, e.g. \`export const query = zod.query(...)\` from @boon4681/giri/validators/zod.`
|
|
564
|
+
);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
function routeInput(routeModule, file) {
|
|
568
|
+
const input = {};
|
|
569
|
+
if (routeModule.body !== void 0) {
|
|
570
|
+
assertBodySchema(routeModule.body, file);
|
|
571
|
+
input.body = routeModule.body;
|
|
572
|
+
}
|
|
573
|
+
if (routeModule.query !== void 0) {
|
|
574
|
+
assertQuerySchema(routeModule.query, file);
|
|
575
|
+
input.query = routeModule.query;
|
|
576
|
+
}
|
|
577
|
+
return input.body || input.query ? input : void 0;
|
|
578
|
+
}
|
|
579
|
+
function aliasValues(value) {
|
|
580
|
+
return Array.isArray(value) ? value : [value];
|
|
581
|
+
}
|
|
582
|
+
function resolveAliasTarget(cwd, target, capture = "") {
|
|
583
|
+
const replaced = target.includes("*") ? target.replaceAll("*", capture) : target;
|
|
584
|
+
return (0, import_node_path3.isAbsolute)(replaced) ? replaced : (0, import_node_path3.resolve)(cwd, replaced);
|
|
585
|
+
}
|
|
586
|
+
function matchAlias(request, key) {
|
|
587
|
+
if (key.includes("*")) {
|
|
588
|
+
const [prefix2, suffix = ""] = key.split("*");
|
|
589
|
+
if (request.startsWith(prefix2) && request.endsWith(suffix)) {
|
|
590
|
+
return request.slice(prefix2.length, request.length - suffix.length);
|
|
591
|
+
}
|
|
592
|
+
return void 0;
|
|
593
|
+
}
|
|
594
|
+
if (request === key) {
|
|
595
|
+
return "";
|
|
596
|
+
}
|
|
597
|
+
const prefix = `${key}/`;
|
|
598
|
+
if (request.startsWith(prefix)) {
|
|
599
|
+
return request.slice(prefix.length);
|
|
600
|
+
}
|
|
601
|
+
return void 0;
|
|
602
|
+
}
|
|
603
|
+
function resolveAliasRequest(request, alias, cwd) {
|
|
604
|
+
for (const [key, value] of Object.entries(alias ?? {})) {
|
|
605
|
+
const capture = matchAlias(request, key);
|
|
606
|
+
if (capture === void 0) {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
const [target] = aliasValues(value);
|
|
610
|
+
if (!target) {
|
|
611
|
+
continue;
|
|
612
|
+
}
|
|
613
|
+
return resolveAliasTarget(cwd, target, capture);
|
|
614
|
+
}
|
|
615
|
+
return void 0;
|
|
616
|
+
}
|
|
617
|
+
function registerAliasResolver(alias, cwd) {
|
|
618
|
+
if (!alias || Object.keys(alias).length === 0) {
|
|
619
|
+
return () => {
|
|
620
|
+
};
|
|
621
|
+
}
|
|
622
|
+
const moduleWithResolver = import_node_module.default;
|
|
623
|
+
const originalResolveFilename = moduleWithResolver._resolveFilename;
|
|
624
|
+
moduleWithResolver._resolveFilename = function resolveWithGiriAlias(request, parent, isMain, options) {
|
|
625
|
+
return originalResolveFilename.call(
|
|
626
|
+
this,
|
|
627
|
+
resolveAliasRequest(request, alias, cwd) ?? request,
|
|
628
|
+
parent,
|
|
629
|
+
isMain,
|
|
630
|
+
options
|
|
631
|
+
);
|
|
632
|
+
};
|
|
633
|
+
return () => {
|
|
634
|
+
moduleWithResolver._resolveFilename = originalResolveFilename;
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
var GIRI_ALIAS_PREFIX = "$giri/";
|
|
638
|
+
var giriOutDir;
|
|
639
|
+
var giriResolverInstalled = false;
|
|
640
|
+
function ensureGiriAliasResolver(outDir) {
|
|
641
|
+
giriOutDir = outDir;
|
|
642
|
+
if (giriResolverInstalled) {
|
|
643
|
+
return;
|
|
644
|
+
}
|
|
645
|
+
giriResolverInstalled = true;
|
|
646
|
+
const moduleWithResolver = import_node_module.default;
|
|
647
|
+
const originalResolveFilename = moduleWithResolver._resolveFilename;
|
|
648
|
+
moduleWithResolver._resolveFilename = function resolveWithGiriInternalAlias(request, parent, isMain, options) {
|
|
649
|
+
const mapped = typeof request === "string" && request.startsWith(GIRI_ALIAS_PREFIX) && giriOutDir ? (0, import_node_path3.join)(giriOutDir, request.slice(GIRI_ALIAS_PREFIX.length)) : request;
|
|
650
|
+
return originalResolveFilename.call(this, mapped, parent, isMain, options);
|
|
651
|
+
};
|
|
652
|
+
}
|
|
653
|
+
function resolveGiriPaths(config, cwd = process.cwd()) {
|
|
654
|
+
return {
|
|
655
|
+
cwd: (0, import_node_path3.resolve)(cwd),
|
|
656
|
+
routesDir: (0, import_node_path3.resolve)(cwd, "src/routes"),
|
|
657
|
+
outDir: (0, import_node_path3.resolve)(cwd, config.outDir ?? ".giri")
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
async function buildGiriApp(config, options = {}) {
|
|
661
|
+
const paths = resolveGiriPaths(config, options.cwd);
|
|
662
|
+
const routes = await scanRoutes(paths.routesDir);
|
|
663
|
+
const app = config.adapter.createApp();
|
|
664
|
+
ensureGiriAliasResolver(paths.outDir);
|
|
665
|
+
const { unregister } = await safeRegister();
|
|
666
|
+
const unregisterAliasResolver = registerAliasResolver(config.alias, paths.cwd);
|
|
667
|
+
try {
|
|
668
|
+
for (const route of routes) {
|
|
669
|
+
const routeModule = loadModule(route.file);
|
|
670
|
+
if (typeof routeModule.handle !== "function") {
|
|
671
|
+
throw new Error(`${route.file} must export a named handle function.`);
|
|
672
|
+
}
|
|
673
|
+
const folderMiddleware = routeModule.config?.skipInherited ? [] : route.sharedFiles.flatMap(
|
|
674
|
+
(file) => normalizeMiddleware(loadModule(file).middleware, file)
|
|
675
|
+
);
|
|
676
|
+
const verbMiddleware = normalizeMiddleware(routeModule.middleware, route.file);
|
|
677
|
+
config.adapter.register(app, {
|
|
678
|
+
method: route.method,
|
|
679
|
+
path: route.path,
|
|
680
|
+
handle: routeModule.handle,
|
|
681
|
+
middleware: [...folderMiddleware, ...verbMiddleware],
|
|
682
|
+
input: routeInput(routeModule, route.file),
|
|
683
|
+
services: options.services
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
} finally {
|
|
687
|
+
unregisterAliasResolver();
|
|
688
|
+
unregister();
|
|
689
|
+
}
|
|
690
|
+
return { app, routes, paths };
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
// src/generator/sync.ts
|
|
694
|
+
var import_node_fs6 = require("fs");
|
|
695
|
+
var import_promises3 = require("fs/promises");
|
|
696
|
+
var import_node_path11 = require("path");
|
|
697
|
+
|
|
698
|
+
// src/generator/app-types.ts
|
|
699
|
+
var import_node_fs4 = require("fs");
|
|
700
|
+
var import_node_path5 = require("path");
|
|
701
|
+
|
|
702
|
+
// src/generator/util.ts
|
|
703
|
+
var import_node_fs3 = require("fs");
|
|
704
|
+
var import_promises2 = require("fs/promises");
|
|
705
|
+
var import_node_path4 = require("path");
|
|
706
|
+
var GENERATED_HEADER = "// Generated by giri sync. Do not edit.";
|
|
707
|
+
function slash(path) {
|
|
708
|
+
return path.split(import_node_path4.sep).join("/");
|
|
709
|
+
}
|
|
710
|
+
function importPath(fromFile, toFile) {
|
|
711
|
+
let path = slash((0, import_node_path4.relative)((0, import_node_path4.dirname)(fromFile), toFile)).replace(/\.d\.ts$/, "");
|
|
712
|
+
if (!path.startsWith(".")) {
|
|
713
|
+
path = `./${path}`;
|
|
714
|
+
}
|
|
715
|
+
return path;
|
|
716
|
+
}
|
|
717
|
+
function relativeConfigPath(fromDir, toPath) {
|
|
718
|
+
let path = slash((0, import_node_path4.relative)(fromDir, toPath));
|
|
719
|
+
if (!path.startsWith(".")) {
|
|
720
|
+
path = `./${path}`;
|
|
721
|
+
}
|
|
722
|
+
return path;
|
|
723
|
+
}
|
|
724
|
+
function moduleSpecifier(fromDir, target) {
|
|
725
|
+
let path = slash((0, import_node_path4.relative)(fromDir, target)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
726
|
+
if (!path.startsWith(".")) {
|
|
727
|
+
path = `./${path}`;
|
|
728
|
+
}
|
|
729
|
+
return path;
|
|
730
|
+
}
|
|
731
|
+
function typeFilePath(paths, routeDir) {
|
|
732
|
+
const sourceDir = (0, import_node_path4.relative)(paths.cwd, routeDir);
|
|
733
|
+
return (0, import_node_path4.join)(paths.outDir, "types", sourceDir, "$types.d.ts");
|
|
734
|
+
}
|
|
735
|
+
function assertSafeOutDir(paths) {
|
|
736
|
+
const rel = (0, import_node_path4.relative)(paths.cwd, paths.outDir);
|
|
737
|
+
if (!rel || rel.startsWith("..") || rel.includes(`..${import_node_path4.sep}`)) {
|
|
738
|
+
throw new Error(`Refusing to sync outside the project root: ${paths.outDir}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
var writeCache = /* @__PURE__ */ new Map();
|
|
742
|
+
async function writeGenerated(path, content) {
|
|
743
|
+
if (writeCache.get(path) === content && (0, import_node_fs3.existsSync)(path)) {
|
|
744
|
+
return;
|
|
745
|
+
}
|
|
746
|
+
await (0, import_promises2.mkdir)((0, import_node_path4.dirname)(path), { recursive: true });
|
|
747
|
+
await (0, import_promises2.writeFile)(path, content);
|
|
748
|
+
writeCache.set(path, content);
|
|
749
|
+
}
|
|
750
|
+
async function writeJson(path, value) {
|
|
751
|
+
await writeGenerated(path, `${JSON.stringify(value, null, 2)}
|
|
752
|
+
`);
|
|
753
|
+
}
|
|
754
|
+
async function pruneDir(dir, keep) {
|
|
755
|
+
let entries;
|
|
756
|
+
try {
|
|
757
|
+
entries = await (0, import_promises2.readdir)(dir, { withFileTypes: true });
|
|
758
|
+
} catch {
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
for (const entry of entries) {
|
|
762
|
+
const full = (0, import_node_path4.join)(dir, entry.name);
|
|
763
|
+
if (entry.isDirectory()) {
|
|
764
|
+
await pruneDir(full, keep);
|
|
765
|
+
await (0, import_promises2.rmdir)(full).catch(() => {
|
|
766
|
+
});
|
|
767
|
+
} else if (!keep.has(full)) {
|
|
768
|
+
await (0, import_promises2.rm)(full, { force: true });
|
|
769
|
+
writeCache.delete(full);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
// src/generator/app-types.ts
|
|
775
|
+
var MAIN_EXTENSIONS = ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"];
|
|
776
|
+
function findMainFile(cwd) {
|
|
777
|
+
for (const ext of MAIN_EXTENSIONS) {
|
|
778
|
+
const file = (0, import_node_path5.join)(cwd, "src", `main.${ext}`);
|
|
779
|
+
if ((0, import_node_fs4.existsSync)(file)) {
|
|
780
|
+
return file;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
return void 0;
|
|
784
|
+
}
|
|
785
|
+
function moduleSpecifier2(fromDir, target) {
|
|
786
|
+
let path = slash((0, import_node_path5.relative)(fromDir, target)).replace(/\.(?:[cm]?[jt]sx?)$/, "");
|
|
787
|
+
if (!path.startsWith(".")) {
|
|
788
|
+
path = `./${path}`;
|
|
789
|
+
}
|
|
790
|
+
return path;
|
|
791
|
+
}
|
|
792
|
+
async function writeAppTypes(paths) {
|
|
793
|
+
const file = (0, import_node_path5.join)(paths.outDir, "types", "app.d.ts");
|
|
794
|
+
const mainFile = findMainFile(paths.cwd);
|
|
795
|
+
if (!mainFile) {
|
|
796
|
+
await writeGenerated(file, [GENERATED_HEADER, "export {};", ""].join("\n"));
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
const spec = moduleSpecifier2((0, import_node_path5.join)(paths.outDir, "types"), mainFile);
|
|
800
|
+
await writeGenerated(
|
|
801
|
+
file,
|
|
802
|
+
[
|
|
803
|
+
GENERATED_HEADER,
|
|
804
|
+
"declare global {",
|
|
805
|
+
" namespace Giri {",
|
|
806
|
+
" interface Register {",
|
|
807
|
+
` app: typeof import(${JSON.stringify(spec)}) extends {`,
|
|
808
|
+
" init: (...args: any[]) => infer R;",
|
|
809
|
+
" }",
|
|
810
|
+
" ? Awaited<R>",
|
|
811
|
+
" : Record<string, unknown>;",
|
|
812
|
+
" }",
|
|
813
|
+
" }",
|
|
814
|
+
"}",
|
|
815
|
+
"export {};",
|
|
816
|
+
""
|
|
817
|
+
].join("\n")
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
// src/generator/manifest.ts
|
|
822
|
+
var import_node_path6 = require("path");
|
|
823
|
+
async function writeManifest(paths, routes, data = {}) {
|
|
824
|
+
const manifest = {
|
|
825
|
+
version: 1,
|
|
826
|
+
routes: routes.map((route) => {
|
|
827
|
+
const responses = data.responsesByFile?.get(route.file);
|
|
828
|
+
const input = data.inputsByFile?.get(route.file);
|
|
829
|
+
const security = data.securityByFile?.get(route.file);
|
|
830
|
+
return {
|
|
831
|
+
method: route.method,
|
|
832
|
+
path: route.path,
|
|
833
|
+
file: slash((0, import_node_path6.relative)(paths.cwd, route.file)),
|
|
834
|
+
params: route.params,
|
|
835
|
+
shared: route.sharedFiles.map((file) => slash((0, import_node_path6.relative)(paths.cwd, file))),
|
|
836
|
+
types: slash((0, import_node_path6.relative)(paths.cwd, typeFilePath(paths, route.routeDir))),
|
|
837
|
+
...data.hiddenFiles?.has(route.file) ? { hidden: true } : {},
|
|
838
|
+
...input ? { input } : {},
|
|
839
|
+
...security && security.security.length > 0 ? { security: security.security } : {},
|
|
840
|
+
responses: responses?.responses ?? [],
|
|
841
|
+
...responses && Object.keys(responses.$defs).length > 0 ? { $defs: responses.$defs } : {}
|
|
842
|
+
};
|
|
843
|
+
})
|
|
844
|
+
};
|
|
845
|
+
await writeJson((0, import_node_path6.join)(paths.outDir, "manifest.json"), manifest);
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// src/generator/openapi.ts
|
|
849
|
+
var import_node_fs5 = require("fs");
|
|
850
|
+
var import_node_path7 = require("path");
|
|
851
|
+
var REASON = {
|
|
852
|
+
200: "OK",
|
|
853
|
+
201: "Created",
|
|
854
|
+
202: "Accepted",
|
|
855
|
+
204: "No Content",
|
|
856
|
+
400: "Bad Request",
|
|
857
|
+
401: "Unauthorized",
|
|
858
|
+
403: "Forbidden",
|
|
859
|
+
404: "Not Found",
|
|
860
|
+
409: "Conflict",
|
|
861
|
+
422: "Unprocessable Entity",
|
|
862
|
+
500: "Internal Server Error"
|
|
863
|
+
};
|
|
864
|
+
function toOpenApiPath(path) {
|
|
865
|
+
return path.replace(/:([A-Za-z0-9_]+)(?:\{[^}]*\})?/g, "{$1}");
|
|
866
|
+
}
|
|
867
|
+
function rewriteRefs(value) {
|
|
868
|
+
if (Array.isArray(value)) {
|
|
869
|
+
return value.map(rewriteRefs);
|
|
870
|
+
}
|
|
871
|
+
if (value && typeof value === "object") {
|
|
872
|
+
const out = {};
|
|
873
|
+
for (const [key, child] of Object.entries(value)) {
|
|
874
|
+
if (key === "$ref" && typeof child === "string" && child.startsWith("#/$defs/")) {
|
|
875
|
+
out.$ref = child.replace("#/$defs/", "#/components/schemas/");
|
|
876
|
+
} else {
|
|
877
|
+
out[key] = rewriteRefs(child);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return out;
|
|
881
|
+
}
|
|
882
|
+
return value;
|
|
883
|
+
}
|
|
884
|
+
function mediaTypeFor(format) {
|
|
885
|
+
return format === "text" ? "text/plain" : "application/json";
|
|
886
|
+
}
|
|
887
|
+
var BODY_MEDIA_TYPE = {
|
|
888
|
+
json: "application/json",
|
|
889
|
+
form: "multipart/form-data",
|
|
890
|
+
urlencoded: "application/x-www-form-urlencoded",
|
|
891
|
+
text: "text/plain"
|
|
892
|
+
};
|
|
893
|
+
function buildResponses(responses) {
|
|
894
|
+
if (responses.length === 0) {
|
|
895
|
+
return { default: { description: "Response" } };
|
|
896
|
+
}
|
|
897
|
+
const out = {};
|
|
898
|
+
for (const response of responses) {
|
|
899
|
+
const key = response.status === "default" ? "default" : String(response.status);
|
|
900
|
+
const description = typeof response.status === "number" && REASON[response.status] || "Response";
|
|
901
|
+
out[key] = {
|
|
902
|
+
description,
|
|
903
|
+
content: { [mediaTypeFor(response.format)]: { schema: rewriteRefs(response.schema) } }
|
|
904
|
+
};
|
|
905
|
+
}
|
|
906
|
+
return out;
|
|
907
|
+
}
|
|
908
|
+
function pathParameters(route) {
|
|
909
|
+
const seen = /* @__PURE__ */ new Set();
|
|
910
|
+
const params = [];
|
|
911
|
+
for (const param of route.params) {
|
|
912
|
+
if (seen.has(param.name)) {
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
seen.add(param.name);
|
|
916
|
+
params.push({ name: param.name, in: "path", required: true, schema: { type: "string" } });
|
|
917
|
+
}
|
|
918
|
+
return params;
|
|
919
|
+
}
|
|
920
|
+
function queryParameters(query) {
|
|
921
|
+
if (!query || query.type !== "object" || typeof query.properties !== "object") {
|
|
922
|
+
return [];
|
|
923
|
+
}
|
|
924
|
+
const properties = query.properties;
|
|
925
|
+
const required = Array.isArray(query.required) ? query.required : [];
|
|
926
|
+
return Object.entries(properties).map(([name, schema]) => ({
|
|
927
|
+
name,
|
|
928
|
+
in: "query",
|
|
929
|
+
required: required.includes(name),
|
|
930
|
+
schema: rewriteRefs(schema)
|
|
931
|
+
}));
|
|
932
|
+
}
|
|
933
|
+
function readProjectInfo(cwd) {
|
|
934
|
+
const file = (0, import_node_path7.join)(cwd, "package.json");
|
|
935
|
+
if ((0, import_node_fs5.existsSync)(file)) {
|
|
936
|
+
try {
|
|
937
|
+
const pkg = JSON.parse((0, import_node_fs5.readFileSync)(file, "utf8"));
|
|
938
|
+
return { title: pkg.name ?? "giri API", version: pkg.version ?? "0.0.0" };
|
|
939
|
+
} catch {
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
return { title: "giri API", version: "0.0.0" };
|
|
943
|
+
}
|
|
944
|
+
function buildOpenApiDocument(paths, routes, data = {}) {
|
|
945
|
+
const documentPaths = {};
|
|
946
|
+
const schemas = {};
|
|
947
|
+
const securitySchemes = {};
|
|
948
|
+
for (const route of routes) {
|
|
949
|
+
if (data.hiddenFiles?.has(route.file)) {
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
const responses = data.responsesByFile?.get(route.file);
|
|
953
|
+
const input = data.inputsByFile?.get(route.file);
|
|
954
|
+
const security = data.securityByFile?.get(route.file);
|
|
955
|
+
for (const [name, schema] of Object.entries(responses?.$defs ?? {})) {
|
|
956
|
+
schemas[name] = rewriteRefs(schema);
|
|
957
|
+
}
|
|
958
|
+
const operation = { responses: buildResponses(responses?.responses ?? []) };
|
|
959
|
+
const parameters = [...pathParameters(route), ...queryParameters(input?.query)];
|
|
960
|
+
if (parameters.length > 0) {
|
|
961
|
+
operation.parameters = parameters;
|
|
962
|
+
}
|
|
963
|
+
if (input?.body) {
|
|
964
|
+
const content = {};
|
|
965
|
+
for (const [contentType, schema] of Object.entries(input.body)) {
|
|
966
|
+
content[BODY_MEDIA_TYPE[contentType] ?? contentType] = {
|
|
967
|
+
schema: rewriteRefs(schema)
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
if (Object.keys(content).length > 0) {
|
|
971
|
+
operation.requestBody = { required: true, content };
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
if (security && security.security.length > 0) {
|
|
975
|
+
operation.security = security.security;
|
|
976
|
+
}
|
|
977
|
+
if (security) {
|
|
978
|
+
Object.assign(securitySchemes, security.securitySchemes);
|
|
979
|
+
}
|
|
980
|
+
const openApiPath = toOpenApiPath(route.path);
|
|
981
|
+
const pathItem = documentPaths[openApiPath] ?? {};
|
|
982
|
+
pathItem[route.method.toLowerCase()] = operation;
|
|
983
|
+
documentPaths[openApiPath] = pathItem;
|
|
984
|
+
}
|
|
985
|
+
const document = {
|
|
986
|
+
openapi: "3.1.0",
|
|
987
|
+
info: readProjectInfo(paths.cwd),
|
|
988
|
+
paths: documentPaths
|
|
989
|
+
};
|
|
990
|
+
const components = {};
|
|
991
|
+
if (Object.keys(schemas).length > 0) {
|
|
992
|
+
components.schemas = schemas;
|
|
993
|
+
}
|
|
994
|
+
if (Object.keys(securitySchemes).length > 0) {
|
|
995
|
+
components.securitySchemes = securitySchemes;
|
|
996
|
+
}
|
|
997
|
+
if (Object.keys(components).length > 0) {
|
|
998
|
+
document.components = components;
|
|
999
|
+
}
|
|
1000
|
+
return document;
|
|
1001
|
+
}
|
|
1002
|
+
async function writeOpenApi(paths, routes, data = {}) {
|
|
1003
|
+
await writeJson((0, import_node_path7.join)(paths.outDir, "openapi.json"), buildOpenApiDocument(paths, routes, data));
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
// src/generator/param-types.ts
|
|
1007
|
+
var import_node_path8 = require("path");
|
|
1008
|
+
function paramsType(params) {
|
|
1009
|
+
if (params.length === 0) {
|
|
1010
|
+
return "{}";
|
|
1011
|
+
}
|
|
1012
|
+
const unique = /* @__PURE__ */ new Map();
|
|
1013
|
+
for (const param of params) {
|
|
1014
|
+
unique.set(param.name, param);
|
|
1015
|
+
}
|
|
1016
|
+
const fields = [...unique.values()].map((param) => ` ${JSON.stringify(param.name)}: string;`).join("\n");
|
|
1017
|
+
return `{
|
|
1018
|
+
${fields}
|
|
1019
|
+
}`;
|
|
1020
|
+
}
|
|
1021
|
+
function varsType(typesDir, sharedFiles) {
|
|
1022
|
+
if (sharedFiles.length === 0) {
|
|
1023
|
+
return "{}";
|
|
1024
|
+
}
|
|
1025
|
+
return sharedFiles.map((file) => {
|
|
1026
|
+
const spec = JSON.stringify(moduleSpecifier(typesDir, file));
|
|
1027
|
+
return `(typeof import(${spec}) extends { middleware: infer M } ? import("@boon4681/giri").InferStackVars<M> : {})`;
|
|
1028
|
+
}).join("\n & ");
|
|
1029
|
+
}
|
|
1030
|
+
function methodExports(typesDir, verbs) {
|
|
1031
|
+
return verbs.map(({ method, file }) => {
|
|
1032
|
+
const spec = JSON.stringify(moduleSpecifier(typesDir, file));
|
|
1033
|
+
const input = `import("@boon4681/giri").RouteInputOf<typeof import(${spec})>`;
|
|
1034
|
+
const vars = `Vars & import("@boon4681/giri").MiddlewareVarsOf<typeof import(${spec})>`;
|
|
1035
|
+
return `export type ${method} = import("@boon4681/giri").Handle<Params, ${input}, ${vars}>;`;
|
|
1036
|
+
});
|
|
1037
|
+
}
|
|
1038
|
+
async function writeParamTypes(paths, folders) {
|
|
1039
|
+
for (const { dir, params, sharedFiles, verbs } of folders) {
|
|
1040
|
+
const file = typeFilePath(paths, dir);
|
|
1041
|
+
const typesDir = (0, import_node_path8.dirname)(file);
|
|
1042
|
+
const lines = [
|
|
1043
|
+
GENERATED_HEADER,
|
|
1044
|
+
`export type Params = ${paramsType(params)};`,
|
|
1045
|
+
"export type RouteParams = Params;",
|
|
1046
|
+
`type Vars = ${varsType(typesDir, sharedFiles)};`,
|
|
1047
|
+
"export type Middleware<Injects extends Record<string, unknown> = {}> =",
|
|
1048
|
+
' import("@boon4681/giri").Middleware<Params, import("@boon4681/giri").ValidatedInput, Injects>;',
|
|
1049
|
+
'export type Handle<Input extends import("@boon4681/giri").ValidatedInput = import("@boon4681/giri").ValidatedInput> =',
|
|
1050
|
+
' import("@boon4681/giri").Handle<Params, Input, Vars>;'
|
|
1051
|
+
];
|
|
1052
|
+
if (verbs.length > 0) {
|
|
1053
|
+
lines.push(...methodExports(typesDir, verbs));
|
|
1054
|
+
}
|
|
1055
|
+
lines.push("");
|
|
1056
|
+
await writeGenerated(file, lines.join("\n"));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
// src/generator/inputs.ts
|
|
1061
|
+
function sanitize(schema) {
|
|
1062
|
+
const { $schema, ...rest } = schema;
|
|
1063
|
+
void $schema;
|
|
1064
|
+
return rest;
|
|
1065
|
+
}
|
|
1066
|
+
function inputToJsonSchema(schema) {
|
|
1067
|
+
if (!isGiriInputSchema(schema)) {
|
|
1068
|
+
return void 0;
|
|
1069
|
+
}
|
|
1070
|
+
return sanitize(schema.toJsonSchema());
|
|
1071
|
+
}
|
|
1072
|
+
function bodyToJsonSchemas(value) {
|
|
1073
|
+
if (!isGiriBodySchema(value)) {
|
|
1074
|
+
return void 0;
|
|
1075
|
+
}
|
|
1076
|
+
const out = {};
|
|
1077
|
+
for (const [contentType, schema] of Object.entries(value.contents)) {
|
|
1078
|
+
const json = inputToJsonSchema(schema);
|
|
1079
|
+
if (json) {
|
|
1080
|
+
out[contentType] = json;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// src/generator/route-meta.ts
|
|
1087
|
+
function loadModule2(file) {
|
|
1088
|
+
const resolved = require.resolve(file);
|
|
1089
|
+
delete require.cache[resolved];
|
|
1090
|
+
return require(resolved);
|
|
1091
|
+
}
|
|
1092
|
+
function interopDefault2(value) {
|
|
1093
|
+
if (value && typeof value === "object" && "default" in value) {
|
|
1094
|
+
return value.default;
|
|
1095
|
+
}
|
|
1096
|
+
return value;
|
|
1097
|
+
}
|
|
1098
|
+
function middlewareFunctions(value) {
|
|
1099
|
+
const exported = interopDefault2(value);
|
|
1100
|
+
if (typeof exported === "function") {
|
|
1101
|
+
return [exported];
|
|
1102
|
+
}
|
|
1103
|
+
if (Array.isArray(exported)) {
|
|
1104
|
+
return exported.filter((fn) => typeof fn === "function");
|
|
1105
|
+
}
|
|
1106
|
+
return [];
|
|
1107
|
+
}
|
|
1108
|
+
function readInput(routeModule) {
|
|
1109
|
+
const input = {};
|
|
1110
|
+
const body = bodyToJsonSchemas(routeModule.body);
|
|
1111
|
+
const query = inputToJsonSchema(routeModule.query);
|
|
1112
|
+
if (body) {
|
|
1113
|
+
input.body = body;
|
|
1114
|
+
}
|
|
1115
|
+
if (query) {
|
|
1116
|
+
input.query = query;
|
|
1117
|
+
}
|
|
1118
|
+
return input.body || input.query ? input : void 0;
|
|
1119
|
+
}
|
|
1120
|
+
function hiddenFrom(value) {
|
|
1121
|
+
if (value === false) {
|
|
1122
|
+
return true;
|
|
1123
|
+
}
|
|
1124
|
+
if (value === true) {
|
|
1125
|
+
return false;
|
|
1126
|
+
}
|
|
1127
|
+
if (value && typeof value === "object" && "hidden" in value) {
|
|
1128
|
+
return Boolean(value.hidden);
|
|
1129
|
+
}
|
|
1130
|
+
return void 0;
|
|
1131
|
+
}
|
|
1132
|
+
function collectHidden(route, routeModule, loadShared) {
|
|
1133
|
+
let hidden = false;
|
|
1134
|
+
for (const file of route.sharedFiles) {
|
|
1135
|
+
const opinion = hiddenFrom(loadShared(file).openapi);
|
|
1136
|
+
if (opinion !== void 0) {
|
|
1137
|
+
hidden = opinion;
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
const verb = hiddenFrom(routeModule.openapi);
|
|
1141
|
+
return verb ?? hidden;
|
|
1142
|
+
}
|
|
1143
|
+
function collectSecurity(route, routeModule, loadShared) {
|
|
1144
|
+
const skipInherited = Boolean(
|
|
1145
|
+
routeModule.config?.skipInherited
|
|
1146
|
+
);
|
|
1147
|
+
const middleware = [];
|
|
1148
|
+
if (!skipInherited) {
|
|
1149
|
+
for (const file of route.sharedFiles) {
|
|
1150
|
+
middleware.push(...middlewareFunctions(loadShared(file).middleware));
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
middleware.push(...middlewareFunctions(routeModule.middleware));
|
|
1154
|
+
const security = [];
|
|
1155
|
+
const securitySchemes = {};
|
|
1156
|
+
for (const fn of middleware) {
|
|
1157
|
+
const openapi = fn.openapi;
|
|
1158
|
+
if (openapi?.security) {
|
|
1159
|
+
for (const requirement of openapi.security) {
|
|
1160
|
+
if (!security.some((seen) => JSON.stringify(seen) === JSON.stringify(requirement))) {
|
|
1161
|
+
security.push(requirement);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
if (openapi?.securitySchemes) {
|
|
1166
|
+
Object.assign(securitySchemes, openapi.securitySchemes);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
return security.length > 0 || Object.keys(securitySchemes).length > 0 ? { security, securitySchemes } : void 0;
|
|
1170
|
+
}
|
|
1171
|
+
async function extractRouteMeta(config, paths, routes) {
|
|
1172
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
1173
|
+
const { unregister } = await safeRegister();
|
|
1174
|
+
const unregisterAlias = registerAliasResolver(config.alias, paths.cwd);
|
|
1175
|
+
const sharedCache = /* @__PURE__ */ new Map();
|
|
1176
|
+
const loadShared = (file) => {
|
|
1177
|
+
if (!sharedCache.has(file)) {
|
|
1178
|
+
try {
|
|
1179
|
+
sharedCache.set(file, loadModule2(file));
|
|
1180
|
+
} catch {
|
|
1181
|
+
sharedCache.set(file, {});
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
return sharedCache.get(file);
|
|
1185
|
+
};
|
|
1186
|
+
try {
|
|
1187
|
+
for (const route of routes) {
|
|
1188
|
+
try {
|
|
1189
|
+
const routeModule = loadModule2(route.file);
|
|
1190
|
+
const meta = {};
|
|
1191
|
+
const input = readInput(routeModule);
|
|
1192
|
+
const security = collectSecurity(route, routeModule, loadShared);
|
|
1193
|
+
const hidden = collectHidden(route, routeModule, loadShared);
|
|
1194
|
+
if (input) {
|
|
1195
|
+
meta.input = input;
|
|
1196
|
+
}
|
|
1197
|
+
if (security) {
|
|
1198
|
+
meta.security = security;
|
|
1199
|
+
}
|
|
1200
|
+
if (hidden) {
|
|
1201
|
+
meta.hidden = true;
|
|
1202
|
+
}
|
|
1203
|
+
if (meta.input || meta.security || meta.hidden) {
|
|
1204
|
+
byFile.set(route.file, meta);
|
|
1205
|
+
}
|
|
1206
|
+
} catch {
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
} finally {
|
|
1210
|
+
unregisterAlias();
|
|
1211
|
+
unregister();
|
|
1212
|
+
}
|
|
1213
|
+
return byFile;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
// src/generator/route-types.ts
|
|
1217
|
+
var import_node_path9 = require("path");
|
|
1218
|
+
async function writeRouteTypes(paths, routes) {
|
|
1219
|
+
const file = (0, import_node_path9.join)(paths.outDir, "routes.d.ts");
|
|
1220
|
+
const lines = [
|
|
1221
|
+
GENERATED_HEADER,
|
|
1222
|
+
"export interface RouteParams {"
|
|
1223
|
+
];
|
|
1224
|
+
for (const route of routes) {
|
|
1225
|
+
const typeFile = typeFilePath(paths, route.routeDir);
|
|
1226
|
+
lines.push(
|
|
1227
|
+
` ${JSON.stringify(`${route.method} ${route.path}`)}: import(${JSON.stringify(
|
|
1228
|
+
importPath(file, typeFile)
|
|
1229
|
+
)}).Params;`
|
|
1230
|
+
);
|
|
1231
|
+
}
|
|
1232
|
+
lines.push("}", "");
|
|
1233
|
+
await writeGenerated(file, lines.join("\n"));
|
|
1234
|
+
}
|
|
1235
|
+
|
|
1236
|
+
// src/generator/schema/program.ts
|
|
1237
|
+
var import_typescript = __toESM(require("typescript"));
|
|
1238
|
+
var DEFAULT_OPTIONS = {
|
|
1239
|
+
target: import_typescript.default.ScriptTarget.ES2022,
|
|
1240
|
+
module: import_typescript.default.ModuleKind.NodeNext,
|
|
1241
|
+
moduleResolution: import_typescript.default.ModuleResolutionKind.NodeNext,
|
|
1242
|
+
strict: true,
|
|
1243
|
+
skipLibCheck: true,
|
|
1244
|
+
noEmit: true
|
|
1245
|
+
};
|
|
1246
|
+
function createSchemaProgram(paths, routeFiles) {
|
|
1247
|
+
let options = { ...DEFAULT_OPTIONS };
|
|
1248
|
+
const configPath = import_typescript.default.findConfigFile(paths.cwd, import_typescript.default.sys.fileExists, "tsconfig.json");
|
|
1249
|
+
if (configPath) {
|
|
1250
|
+
const parsed = import_typescript.default.getParsedCommandLineOfConfigFile(configPath, {}, {
|
|
1251
|
+
...import_typescript.default.sys,
|
|
1252
|
+
onUnRecoverableConfigFileDiagnostic: () => {
|
|
1253
|
+
}
|
|
1254
|
+
});
|
|
1255
|
+
if (parsed) {
|
|
1256
|
+
options = { ...parsed.options, noEmit: true };
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
return import_typescript.default.createProgram(routeFiles, options);
|
|
1260
|
+
}
|
|
1261
|
+
|
|
1262
|
+
// src/generator/schema/responses.ts
|
|
1263
|
+
var import_typescript3 = __toESM(require("typescript"));
|
|
1264
|
+
|
|
1265
|
+
// src/generator/schema/json-schema.ts
|
|
1266
|
+
var import_typescript2 = __toESM(require("typescript"));
|
|
1267
|
+
function createWalkContext(checker, location) {
|
|
1268
|
+
return {
|
|
1269
|
+
checker,
|
|
1270
|
+
location,
|
|
1271
|
+
defs: {},
|
|
1272
|
+
inProgress: /* @__PURE__ */ new Map(),
|
|
1273
|
+
usedDefs: /* @__PURE__ */ new Set(),
|
|
1274
|
+
warnings: []
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
function typeId(type) {
|
|
1278
|
+
return type.id;
|
|
1279
|
+
}
|
|
1280
|
+
function intrinsicName(type) {
|
|
1281
|
+
return type.intrinsicName;
|
|
1282
|
+
}
|
|
1283
|
+
function isDateType(type) {
|
|
1284
|
+
const symbol = type.getSymbol() ?? type.aliasSymbol;
|
|
1285
|
+
return symbol?.getName() === "Date";
|
|
1286
|
+
}
|
|
1287
|
+
function literalValuesOf(types) {
|
|
1288
|
+
const values = [];
|
|
1289
|
+
for (const member of types) {
|
|
1290
|
+
if (member.isStringLiteral() || member.isNumberLiteral()) {
|
|
1291
|
+
values.push(member.value);
|
|
1292
|
+
} else if (member.flags & import_typescript2.default.TypeFlags.BooleanLiteral) {
|
|
1293
|
+
values.push(intrinsicName(member) === "true");
|
|
1294
|
+
} else {
|
|
1295
|
+
return void 0;
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
return values;
|
|
1299
|
+
}
|
|
1300
|
+
function walkUnion(type, ctx) {
|
|
1301
|
+
const flag = import_typescript2.default.TypeFlags.Undefined | import_typescript2.default.TypeFlags.Void | import_typescript2.default.TypeFlags.Never;
|
|
1302
|
+
const members = type.types.filter((member) => !(member.flags & flag));
|
|
1303
|
+
if (members.length === 1) {
|
|
1304
|
+
return walkType(members[0], ctx);
|
|
1305
|
+
}
|
|
1306
|
+
const enumValues = literalValuesOf(members);
|
|
1307
|
+
if (enumValues) {
|
|
1308
|
+
return { enum: enumValues };
|
|
1309
|
+
}
|
|
1310
|
+
return { anyOf: members.map((member) => walkType(member, ctx)) };
|
|
1311
|
+
}
|
|
1312
|
+
function buildObjectSchema(type, ctx) {
|
|
1313
|
+
const { checker } = ctx;
|
|
1314
|
+
const indexInfo = checker.getIndexInfoOfType(type, import_typescript2.default.IndexKind.String) ?? checker.getIndexInfoOfType(type, import_typescript2.default.IndexKind.Number);
|
|
1315
|
+
const properties = {};
|
|
1316
|
+
const required = [];
|
|
1317
|
+
for (const symbol of checker.getPropertiesOfType(type)) {
|
|
1318
|
+
const name = symbol.getName();
|
|
1319
|
+
const propType = checker.getTypeOfSymbolAtLocation(symbol, ctx.location);
|
|
1320
|
+
const optional = Boolean(symbol.getFlags() & import_typescript2.default.SymbolFlags.Optional) || Boolean(propType.flags & import_typescript2.default.TypeFlags.Union && propType.types.some((t) => t.flags & import_typescript2.default.TypeFlags.Undefined));
|
|
1321
|
+
properties[name] = walkType(propType, ctx);
|
|
1322
|
+
if (!optional) {
|
|
1323
|
+
required.push(name);
|
|
1324
|
+
}
|
|
1325
|
+
}
|
|
1326
|
+
const schema = { type: "object" };
|
|
1327
|
+
if (Object.keys(properties).length > 0) {
|
|
1328
|
+
schema.properties = properties;
|
|
1329
|
+
}
|
|
1330
|
+
if (required.length > 0) {
|
|
1331
|
+
schema.required = required;
|
|
1332
|
+
}
|
|
1333
|
+
if (indexInfo) {
|
|
1334
|
+
schema.additionalProperties = walkType(indexInfo.type, ctx);
|
|
1335
|
+
} else if (Object.keys(properties).length > 0) {
|
|
1336
|
+
schema.additionalProperties = false;
|
|
1337
|
+
}
|
|
1338
|
+
return schema;
|
|
1339
|
+
}
|
|
1340
|
+
function defName(type) {
|
|
1341
|
+
const symbol = type.getSymbol() ?? type.aliasSymbol;
|
|
1342
|
+
const name = symbol?.getName();
|
|
1343
|
+
if (name && name !== "__type" && name !== "__object") {
|
|
1344
|
+
return name;
|
|
1345
|
+
}
|
|
1346
|
+
return `Anonymous${typeId(type)}`;
|
|
1347
|
+
}
|
|
1348
|
+
function walkObject(type, ctx) {
|
|
1349
|
+
const { checker } = ctx;
|
|
1350
|
+
if (isDateType(type)) {
|
|
1351
|
+
return { type: "string", format: "date-time" };
|
|
1352
|
+
}
|
|
1353
|
+
if (checker.isArrayType(type)) {
|
|
1354
|
+
const [element] = checker.getTypeArguments(type);
|
|
1355
|
+
return { type: "array", items: element ? walkType(element, ctx) : {} };
|
|
1356
|
+
}
|
|
1357
|
+
if (checker.isTupleType(type)) {
|
|
1358
|
+
const elements = checker.getTypeArguments(type);
|
|
1359
|
+
return { type: "array", items: elements.map((element) => walkType(element, ctx)) };
|
|
1360
|
+
}
|
|
1361
|
+
const id = typeId(type);
|
|
1362
|
+
const existing = ctx.inProgress.get(id);
|
|
1363
|
+
if (existing) {
|
|
1364
|
+
ctx.usedDefs.add(existing);
|
|
1365
|
+
return { $ref: `#/$defs/${existing}` };
|
|
1366
|
+
}
|
|
1367
|
+
const name = defName(type);
|
|
1368
|
+
ctx.inProgress.set(id, name);
|
|
1369
|
+
const schema = buildObjectSchema(type, ctx);
|
|
1370
|
+
ctx.inProgress.delete(id);
|
|
1371
|
+
if (ctx.usedDefs.has(name)) {
|
|
1372
|
+
ctx.defs[name] = schema;
|
|
1373
|
+
return { $ref: `#/$defs/${name}` };
|
|
1374
|
+
}
|
|
1375
|
+
return schema;
|
|
1376
|
+
}
|
|
1377
|
+
function walkType(type, ctx) {
|
|
1378
|
+
const flags = type.flags;
|
|
1379
|
+
if (flags & (import_typescript2.default.TypeFlags.Any | import_typescript2.default.TypeFlags.Unknown)) {
|
|
1380
|
+
return {};
|
|
1381
|
+
}
|
|
1382
|
+
if (flags & import_typescript2.default.TypeFlags.Null) {
|
|
1383
|
+
return { type: "null" };
|
|
1384
|
+
}
|
|
1385
|
+
if (flags & (import_typescript2.default.TypeFlags.Undefined | import_typescript2.default.TypeFlags.Void)) {
|
|
1386
|
+
return {};
|
|
1387
|
+
}
|
|
1388
|
+
if (flags & (import_typescript2.default.TypeFlags.BigInt | import_typescript2.default.TypeFlags.BigIntLiteral)) {
|
|
1389
|
+
ctx.warnings.push("bigint is not JSON-serializable (JSON.stringify throws); documented as string.");
|
|
1390
|
+
return { type: "string" };
|
|
1391
|
+
}
|
|
1392
|
+
if (type.isStringLiteral()) {
|
|
1393
|
+
return { type: "string", const: type.value };
|
|
1394
|
+
}
|
|
1395
|
+
if (type.isNumberLiteral()) {
|
|
1396
|
+
return { type: "number", const: type.value };
|
|
1397
|
+
}
|
|
1398
|
+
if (flags & import_typescript2.default.TypeFlags.BooleanLiteral) {
|
|
1399
|
+
return { type: "boolean", const: intrinsicName(type) === "true" };
|
|
1400
|
+
}
|
|
1401
|
+
if (flags & import_typescript2.default.TypeFlags.String) {
|
|
1402
|
+
return { type: "string" };
|
|
1403
|
+
}
|
|
1404
|
+
if (flags & import_typescript2.default.TypeFlags.Number) {
|
|
1405
|
+
return { type: "number" };
|
|
1406
|
+
}
|
|
1407
|
+
if (flags & import_typescript2.default.TypeFlags.Boolean) {
|
|
1408
|
+
return { type: "boolean" };
|
|
1409
|
+
}
|
|
1410
|
+
if (type.isUnion()) {
|
|
1411
|
+
return walkUnion(type, ctx);
|
|
1412
|
+
}
|
|
1413
|
+
if (flags & import_typescript2.default.TypeFlags.Object || type.isIntersection()) {
|
|
1414
|
+
return walkObject(type, ctx);
|
|
1415
|
+
}
|
|
1416
|
+
return {};
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
// src/generator/schema/responses.ts
|
|
1420
|
+
function findHandleFunction(source) {
|
|
1421
|
+
let found;
|
|
1422
|
+
const isExported = (node) => import_typescript3.default.canHaveModifiers(node) && (import_typescript3.default.getModifiers(node)?.some((m) => m.kind === import_typescript3.default.SyntaxKind.ExportKeyword) ?? false);
|
|
1423
|
+
for (const statement of source.statements) {
|
|
1424
|
+
if (import_typescript3.default.isFunctionDeclaration(statement) && statement.name?.text === "handle" && isExported(statement)) {
|
|
1425
|
+
found = statement;
|
|
1426
|
+
}
|
|
1427
|
+
if (import_typescript3.default.isVariableStatement(statement) && isExported(statement)) {
|
|
1428
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
1429
|
+
if (import_typescript3.default.isIdentifier(declaration.name) && declaration.name.text === "handle" && declaration.initializer && (import_typescript3.default.isArrowFunction(declaration.initializer) || import_typescript3.default.isFunctionExpression(declaration.initializer))) {
|
|
1430
|
+
found = declaration.initializer;
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
return found;
|
|
1436
|
+
}
|
|
1437
|
+
function collectReturnExpressions(fn) {
|
|
1438
|
+
if (import_typescript3.default.isArrowFunction(fn) && !import_typescript3.default.isBlock(fn.body)) {
|
|
1439
|
+
return [fn.body];
|
|
1440
|
+
}
|
|
1441
|
+
if (!fn.body) {
|
|
1442
|
+
return [];
|
|
1443
|
+
}
|
|
1444
|
+
const expressions = [];
|
|
1445
|
+
const visit = (node) => {
|
|
1446
|
+
if (import_typescript3.default.isFunctionDeclaration(node) || import_typescript3.default.isFunctionExpression(node) || import_typescript3.default.isArrowFunction(node)) {
|
|
1447
|
+
return;
|
|
1448
|
+
}
|
|
1449
|
+
if (import_typescript3.default.isReturnStatement(node) && node.expression) {
|
|
1450
|
+
expressions.push(node.expression);
|
|
1451
|
+
}
|
|
1452
|
+
import_typescript3.default.forEachChild(node, visit);
|
|
1453
|
+
};
|
|
1454
|
+
import_typescript3.default.forEachChild(fn.body, visit);
|
|
1455
|
+
return expressions;
|
|
1456
|
+
}
|
|
1457
|
+
function propertyType(checker, type, name, location) {
|
|
1458
|
+
const symbol = checker.getPropertyOfType(type, name);
|
|
1459
|
+
return symbol ? checker.getTypeOfSymbolAtLocation(symbol, location) : void 0;
|
|
1460
|
+
}
|
|
1461
|
+
function isTypedResponse2(checker, type) {
|
|
1462
|
+
return Boolean(
|
|
1463
|
+
checker.getPropertyOfType(type, "data") && checker.getPropertyOfType(type, "status") && checker.getPropertyOfType(type, "format")
|
|
1464
|
+
);
|
|
1465
|
+
}
|
|
1466
|
+
function readFromCall(checker, expression) {
|
|
1467
|
+
if (!import_typescript3.default.isCallExpression(expression) || !import_typescript3.default.isPropertyAccessExpression(expression.expression)) {
|
|
1468
|
+
return void 0;
|
|
1469
|
+
}
|
|
1470
|
+
const method = expression.expression.name.text;
|
|
1471
|
+
if (method !== "json" && method !== "text") {
|
|
1472
|
+
return void 0;
|
|
1473
|
+
}
|
|
1474
|
+
if (!isTypedResponse2(checker, checker.getTypeAtLocation(expression))) {
|
|
1475
|
+
return void 0;
|
|
1476
|
+
}
|
|
1477
|
+
const [dataArg, statusArg] = expression.arguments;
|
|
1478
|
+
if (!dataArg) {
|
|
1479
|
+
return void 0;
|
|
1480
|
+
}
|
|
1481
|
+
let status = 200;
|
|
1482
|
+
if (statusArg) {
|
|
1483
|
+
const statusType = checker.getTypeAtLocation(statusArg);
|
|
1484
|
+
status = statusType.isNumberLiteral() ? statusType.value : "default";
|
|
1485
|
+
}
|
|
1486
|
+
return { status, format: method === "text" ? "text" : "json", data: checker.getTypeAtLocation(dataArg) };
|
|
1487
|
+
}
|
|
1488
|
+
function readFromType(checker, type, location) {
|
|
1489
|
+
const dataType = propertyType(checker, type, "data", location);
|
|
1490
|
+
const statusType = propertyType(checker, type, "status", location);
|
|
1491
|
+
const formatType = propertyType(checker, type, "format", location);
|
|
1492
|
+
if (!dataType || !statusType || !formatType) {
|
|
1493
|
+
return void 0;
|
|
1494
|
+
}
|
|
1495
|
+
const status = statusType.isNumberLiteral() ? statusType.value : "default";
|
|
1496
|
+
const format = formatType.isStringLiteral() && formatType.value === "text" ? "text" : "json";
|
|
1497
|
+
return { status, format, data: dataType };
|
|
1498
|
+
}
|
|
1499
|
+
function constituents(type) {
|
|
1500
|
+
return type.isUnion() ? type.types : [type];
|
|
1501
|
+
}
|
|
1502
|
+
function extractRouteResponses(program, file) {
|
|
1503
|
+
const result = { responses: [], opaque: false, warnings: [], $defs: {} };
|
|
1504
|
+
const source = program.getSourceFile(file);
|
|
1505
|
+
if (!source) {
|
|
1506
|
+
return result;
|
|
1507
|
+
}
|
|
1508
|
+
const checker = program.getTypeChecker();
|
|
1509
|
+
const fn = findHandleFunction(source);
|
|
1510
|
+
if (!fn) {
|
|
1511
|
+
return result;
|
|
1512
|
+
}
|
|
1513
|
+
const ctx = createWalkContext(checker, fn);
|
|
1514
|
+
const byStatus = /* @__PURE__ */ new Map();
|
|
1515
|
+
const record = (hit) => {
|
|
1516
|
+
const schema = walkType(hit.data, ctx);
|
|
1517
|
+
const bucket = byStatus.get(hit.status) ?? { format: hit.format, schemas: [] };
|
|
1518
|
+
bucket.schemas.push(schema);
|
|
1519
|
+
byStatus.set(hit.status, bucket);
|
|
1520
|
+
};
|
|
1521
|
+
for (const expression of collectReturnExpressions(fn)) {
|
|
1522
|
+
const fromCall = readFromCall(checker, expression);
|
|
1523
|
+
if (fromCall) {
|
|
1524
|
+
record(fromCall);
|
|
1525
|
+
continue;
|
|
1526
|
+
}
|
|
1527
|
+
let matched = false;
|
|
1528
|
+
for (const member of constituents(checker.getTypeAtLocation(expression))) {
|
|
1529
|
+
const hit = readFromType(checker, member, expression);
|
|
1530
|
+
if (hit) {
|
|
1531
|
+
record(hit);
|
|
1532
|
+
matched = true;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
if (!matched) {
|
|
1536
|
+
result.opaque = true;
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
for (const [status, { format, schemas }] of byStatus) {
|
|
1540
|
+
const schema = schemas.length === 1 ? schemas[0] : { anyOf: schemas };
|
|
1541
|
+
result.responses.push({ status, format, schema });
|
|
1542
|
+
}
|
|
1543
|
+
result.responses.sort((a, b) => Number(a.status) - Number(b.status));
|
|
1544
|
+
result.warnings = ctx.warnings;
|
|
1545
|
+
result.$defs = ctx.defs;
|
|
1546
|
+
return result;
|
|
1547
|
+
}
|
|
1548
|
+
|
|
1549
|
+
// src/generator/tsconfig.ts
|
|
1550
|
+
var import_node_path10 = require("path");
|
|
1551
|
+
function normalizeAlias(alias, paths) {
|
|
1552
|
+
const result = {};
|
|
1553
|
+
for (const [key, value] of Object.entries(alias ?? {})) {
|
|
1554
|
+
const targets = Array.isArray(value) ? value : [value];
|
|
1555
|
+
result[key] = targets.map(
|
|
1556
|
+
(target) => relativeConfigPath(paths.outDir, (0, import_node_path10.resolve)(paths.cwd, target))
|
|
1557
|
+
);
|
|
1558
|
+
}
|
|
1559
|
+
return result;
|
|
1560
|
+
}
|
|
1561
|
+
async function writeTsConfig(paths, config) {
|
|
1562
|
+
const file = (0, import_node_path10.join)(paths.outDir, "tsconfig.json");
|
|
1563
|
+
await writeJson(file, {
|
|
1564
|
+
compilerOptions: {
|
|
1565
|
+
rootDirs: [
|
|
1566
|
+
"..",
|
|
1567
|
+
"./types"
|
|
1568
|
+
],
|
|
1569
|
+
paths: {
|
|
1570
|
+
// The tsconfig lives in outDir, so `$giri/*` maps to its own folder.
|
|
1571
|
+
"$giri/*": ["./*"],
|
|
1572
|
+
...normalizeAlias(config.alias, paths)
|
|
1573
|
+
},
|
|
1574
|
+
plugins: [
|
|
1575
|
+
{
|
|
1576
|
+
name: "@boon4681/giri/tsc"
|
|
1577
|
+
}
|
|
1578
|
+
]
|
|
1579
|
+
},
|
|
1580
|
+
include: [
|
|
1581
|
+
relativeConfigPath(paths.outDir, (0, import_node_path10.join)(paths.cwd, "src")),
|
|
1582
|
+
relativeConfigPath(paths.outDir, (0, import_node_path10.join)(paths.cwd, "giri.config.ts")),
|
|
1583
|
+
"./types/**/*.d.ts"
|
|
1584
|
+
]
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
// src/generator/sync.ts
|
|
1589
|
+
async function typeFolders(paths, routes) {
|
|
1590
|
+
const verbsByDir = /* @__PURE__ */ new Map();
|
|
1591
|
+
for (const route of routes) {
|
|
1592
|
+
const key = slash(route.routeDir);
|
|
1593
|
+
const list = verbsByDir.get(key) ?? [];
|
|
1594
|
+
list.push({ method: route.method, file: route.file });
|
|
1595
|
+
verbsByDir.set(key, list);
|
|
1596
|
+
}
|
|
1597
|
+
const dirs = await scanRouteFolders(paths.routesDir);
|
|
1598
|
+
return dirs.map((dir) => ({
|
|
1599
|
+
dir,
|
|
1600
|
+
params: routeParamsForDir(paths.routesDir, dir),
|
|
1601
|
+
sharedFiles: sharedFilesForDir(paths.routesDir, dir),
|
|
1602
|
+
verbs: verbsByDir.get(slash(dir)) ?? []
|
|
1603
|
+
}));
|
|
1604
|
+
}
|
|
1605
|
+
function extractResponses(paths, routes) {
|
|
1606
|
+
const byFile = /* @__PURE__ */ new Map();
|
|
1607
|
+
if (routes.length === 0) {
|
|
1608
|
+
return byFile;
|
|
1609
|
+
}
|
|
1610
|
+
try {
|
|
1611
|
+
const files = [...new Set(routes.map((route) => route.file))];
|
|
1612
|
+
const appTypes = (0, import_node_path11.join)(paths.outDir, "types", "app.d.ts");
|
|
1613
|
+
const program = createSchemaProgram(
|
|
1614
|
+
paths,
|
|
1615
|
+
(0, import_node_fs6.existsSync)(appTypes) ? [...files, appTypes] : files
|
|
1616
|
+
);
|
|
1617
|
+
for (const file of files) {
|
|
1618
|
+
byFile.set(file, extractRouteResponses(program, file));
|
|
1619
|
+
}
|
|
1620
|
+
} catch (error) {
|
|
1621
|
+
console.warn(`giri: skipped response schema generation (${error.message}).`);
|
|
1622
|
+
}
|
|
1623
|
+
return byFile;
|
|
1624
|
+
}
|
|
1625
|
+
async function extractMeta(config, paths, routes) {
|
|
1626
|
+
const inputsByFile = /* @__PURE__ */ new Map();
|
|
1627
|
+
const securityByFile = /* @__PURE__ */ new Map();
|
|
1628
|
+
const hiddenFiles = /* @__PURE__ */ new Set();
|
|
1629
|
+
if (routes.length === 0) {
|
|
1630
|
+
return { inputsByFile, securityByFile, hiddenFiles };
|
|
1631
|
+
}
|
|
1632
|
+
try {
|
|
1633
|
+
const meta = await extractRouteMeta(config, paths, routes);
|
|
1634
|
+
for (const [file, entry] of meta) {
|
|
1635
|
+
if (entry.input) {
|
|
1636
|
+
inputsByFile.set(file, entry.input);
|
|
1637
|
+
}
|
|
1638
|
+
if (entry.security) {
|
|
1639
|
+
securityByFile.set(file, entry.security);
|
|
1640
|
+
}
|
|
1641
|
+
if (entry.hidden) {
|
|
1642
|
+
hiddenFiles.add(file);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
} catch (error) {
|
|
1646
|
+
console.warn(`giri: skipped input/security generation (${error.message}).`);
|
|
1647
|
+
}
|
|
1648
|
+
return { inputsByFile, securityByFile, hiddenFiles };
|
|
1649
|
+
}
|
|
1650
|
+
async function syncProject(config, options = {}) {
|
|
1651
|
+
const paths = resolveGiriPaths(config, options.cwd);
|
|
1652
|
+
assertSafeOutDir(paths);
|
|
1653
|
+
const routes = await scanRoutes(paths.routesDir);
|
|
1654
|
+
const folders = await typeFolders(paths, routes);
|
|
1655
|
+
await (0, import_promises3.mkdir)(paths.outDir, { recursive: true });
|
|
1656
|
+
await writeParamTypes(paths, folders);
|
|
1657
|
+
await writeRouteTypes(paths, routes);
|
|
1658
|
+
await writeAppTypes(paths);
|
|
1659
|
+
await writeTsConfig(paths, config);
|
|
1660
|
+
const responsesByFile = extractResponses(paths, routes);
|
|
1661
|
+
const { inputsByFile, securityByFile, hiddenFiles } = await extractMeta(config, paths, routes);
|
|
1662
|
+
const data = { responsesByFile, inputsByFile, securityByFile, hiddenFiles };
|
|
1663
|
+
await writeManifest(paths, routes, data);
|
|
1664
|
+
await writeOpenApi(paths, routes, data);
|
|
1665
|
+
await pruneDir(
|
|
1666
|
+
paths.outDir,
|
|
1667
|
+
/* @__PURE__ */ new Set([
|
|
1668
|
+
(0, import_node_path11.join)(paths.outDir, "tsconfig.json"),
|
|
1669
|
+
(0, import_node_path11.join)(paths.outDir, "manifest.json"),
|
|
1670
|
+
(0, import_node_path11.join)(paths.outDir, "openapi.json"),
|
|
1671
|
+
(0, import_node_path11.join)(paths.outDir, "routes.d.ts"),
|
|
1672
|
+
(0, import_node_path11.join)(paths.outDir, "types", "app.d.ts"),
|
|
1673
|
+
...folders.map((folder) => typeFilePath(paths, folder.dir))
|
|
1674
|
+
])
|
|
1675
|
+
);
|
|
1676
|
+
return { paths, routes, folders, data };
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
// src/generator/watch.ts
|
|
1680
|
+
var import_node_fs7 = require("fs");
|
|
1681
|
+
var import_node_path13 = require("path");
|
|
1682
|
+
|
|
1683
|
+
// src/loader/module-loader.ts
|
|
1684
|
+
var import_node_path12 = require("path");
|
|
1685
|
+
|
|
1686
|
+
// src/lifecycle.ts
|
|
1687
|
+
var import_node_fs8 = require("fs");
|
|
1688
|
+
var import_node_path14 = require("path");
|
|
1689
|
+
var MAIN_EXTENSIONS2 = ["ts", "tsx", "mts", "cts", "js", "jsx", "mjs", "cjs"];
|
|
1690
|
+
function resolveMainFile(cwd) {
|
|
1691
|
+
for (const ext of MAIN_EXTENSIONS2) {
|
|
1692
|
+
const file = (0, import_node_path14.join)(cwd, "src", `main.${ext}`);
|
|
1693
|
+
if ((0, import_node_fs8.existsSync)(file)) {
|
|
1694
|
+
return file;
|
|
1695
|
+
}
|
|
1696
|
+
}
|
|
1697
|
+
return void 0;
|
|
1698
|
+
}
|
|
1699
|
+
async function loadLifecycle(cwd = process.cwd()) {
|
|
1700
|
+
const file = resolveMainFile((0, import_node_path14.resolve)(cwd));
|
|
1701
|
+
if (!file) {
|
|
1702
|
+
return {};
|
|
1703
|
+
}
|
|
1704
|
+
const { unregister } = await safeRegister();
|
|
1705
|
+
try {
|
|
1706
|
+
const resolved = require.resolve(file);
|
|
1707
|
+
delete require.cache[resolved];
|
|
1708
|
+
const loaded = require(resolved);
|
|
1709
|
+
const lifecycle = { file };
|
|
1710
|
+
if (loaded.init !== void 0) {
|
|
1711
|
+
if (typeof loaded.init !== "function") {
|
|
1712
|
+
throw new Error(`${file}: "init" must be a function.`);
|
|
1713
|
+
}
|
|
1714
|
+
lifecycle.init = loaded.init;
|
|
1715
|
+
}
|
|
1716
|
+
if (loaded.teardown !== void 0) {
|
|
1717
|
+
if (typeof loaded.teardown !== "function") {
|
|
1718
|
+
throw new Error(`${file}: "teardown" must be a function.`);
|
|
1719
|
+
}
|
|
1720
|
+
lifecycle.teardown = loaded.teardown;
|
|
1721
|
+
}
|
|
1722
|
+
return lifecycle;
|
|
1723
|
+
} finally {
|
|
1724
|
+
unregister();
|
|
1725
|
+
}
|
|
1726
|
+
}
|
|
1727
|
+
async function runInit(lifecycle) {
|
|
1728
|
+
if (!lifecycle.init) {
|
|
1729
|
+
return {};
|
|
1730
|
+
}
|
|
1731
|
+
const services = await lifecycle.init();
|
|
1732
|
+
return services ?? {};
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
// src/index.ts
|
|
1736
|
+
function defineConfig(config) {
|
|
1737
|
+
return config;
|
|
1738
|
+
}
|
|
1739
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1740
|
+
0 && (module.exports = {
|
|
1741
|
+
buildGiriApp,
|
|
1742
|
+
composeMiddleware,
|
|
1743
|
+
createContext,
|
|
1744
|
+
createTypedResponse,
|
|
1745
|
+
defineBodySchema,
|
|
1746
|
+
defineConfig,
|
|
1747
|
+
defineInputSchema,
|
|
1748
|
+
defineMiddleware,
|
|
1749
|
+
isGiriBodySchema,
|
|
1750
|
+
isGiriInputSchema,
|
|
1751
|
+
isTypedResponse,
|
|
1752
|
+
loadLifecycle,
|
|
1753
|
+
prepareRequestInput,
|
|
1754
|
+
resolveGiriPaths,
|
|
1755
|
+
runInit,
|
|
1756
|
+
scanRoutes,
|
|
1757
|
+
stack,
|
|
1758
|
+
syncProject,
|
|
1759
|
+
toResponse,
|
|
1760
|
+
typedResponseToResponse
|
|
1761
|
+
});
|
|
1762
|
+
//# sourceMappingURL=index.js.map
|