@ts-kizuna/cli 1.49.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,735 @@
1
+ //#region \0rolldown/runtime.js
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 __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ let node_fs = require("node:fs");
24
+ node_fs = __toESM(node_fs, 1);
25
+ let node_path = require("node:path");
26
+ node_path = __toESM(node_path, 1);
27
+ let typescript = require("typescript");
28
+ typescript = __toESM(typescript, 1);
29
+ let _ts_kizuna_core_authoring_names = require("@ts-kizuna/core/authoring-names");
30
+ let _ts_kizuna_core_generator = require("@ts-kizuna/core/generator");
31
+ let jiti = require("jiti");
32
+ //#region src/schema-exports.ts
33
+ /**
34
+ * Maps every exported schema reachable from `entryPath` to the verbatim JSDoc
35
+ * block on each of its fields, keyed by the exported const name (and, for
36
+ * aliased re-exports, by the original declared name too).
37
+ *
38
+ * Walks the whole reachable graph, both the entry's re-exports and every file
39
+ * it imports, so a schema defined in its own module and used via `import`
40
+ * (rather than re-exported from the contract entry) is still collected.
41
+ *
42
+ * Used to patch emitted `.d.ts` files, where a `z.ZodObject<{...}>` shape is
43
+ * keyed by the `declare const` name rather than a route or schema `meta.id`.
44
+ */
45
+ const collectExportedSchemaDocs = (entryPath) => {
46
+ const { resolve, cache } = makeResolverWithCache(entryPath);
47
+ const result = /* @__PURE__ */ new Map();
48
+ const visited = /* @__PURE__ */ new Set();
49
+ const record = (name, expression) => {
50
+ const fields = /* @__PURE__ */ new Map();
51
+ collectFieldDocs(expression, "", fields, resolve);
52
+ if (fields.size > 0) result.set(name, fields);
53
+ };
54
+ const collectFromFile = (filePath) => {
55
+ if (visited.has(filePath)) return;
56
+ visited.add(filePath);
57
+ const sourceFile = typescript.default.createSourceFile(filePath, node_fs.readFileSync(filePath, "utf8"), typescript.default.ScriptTarget.Latest, true);
58
+ for (const statement of sourceFile.statements) {
59
+ if (typescript.default.isVariableStatement(statement)) {
60
+ if (!(statement.modifiers ?? []).some((modifier) => modifier.kind === typescript.default.SyntaxKind.ExportKeyword)) continue;
61
+ for (const declaration of statement.declarationList.declarations) if (typescript.default.isIdentifier(declaration.name) && declaration.initializer) record(declaration.name.text, declaration.initializer);
62
+ continue;
63
+ }
64
+ if (!typescript.default.isExportDeclaration(statement) || !statement.moduleSpecifier || !typescript.default.isStringLiteral(statement.moduleSpecifier)) continue;
65
+ const specifier = statement.moduleSpecifier.text;
66
+ if (!specifier.startsWith(".")) continue;
67
+ const target = resolveImportPath(node_path.dirname(filePath), specifier);
68
+ if (!target) continue;
69
+ const exportClause = statement.exportClause;
70
+ if (!exportClause) {
71
+ collectFromFile(target);
72
+ continue;
73
+ }
74
+ if (!typescript.default.isNamedExports(exportClause)) continue;
75
+ const targetScope = buildFileScope(target, cache);
76
+ for (const element of exportClause.elements) {
77
+ const localName = (element.propertyName ?? element.name).text;
78
+ const exposedName = element.name.text;
79
+ const expression = targetScope.get(localName);
80
+ if (!expression) continue;
81
+ record(exposedName, expression);
82
+ if (localName !== exposedName) record(localName, expression);
83
+ }
84
+ }
85
+ };
86
+ collectFromFile(entryPath);
87
+ for (const filePath of [...cache.keys()]) collectFromFile(filePath);
88
+ return result;
89
+ };
90
+ //#endregion
91
+ //#region src/dts-jsdoc.ts
92
+ /**
93
+ * Zod wrapper types whose inner type is the first type argument. Navigating a
94
+ * field path unwraps these until it reaches a `ZodObject`. Mirrors the runtime
95
+ * wrapper set in `zod-internals.ts`.
96
+ */
97
+ const ELEMENT_WRAPPERS = new Set([
98
+ "ZodArray",
99
+ "ZodOptional",
100
+ "ZodNullable",
101
+ "ZodDefault",
102
+ "ZodReadonly",
103
+ "ZodNonOptional",
104
+ "ZodCatch",
105
+ "ZodPrefault"
106
+ ]);
107
+ const entityNameRight = (name) => typescript.default.isQualifiedName(name) ? name.right.text : name.text;
108
+ /**
109
+ * The right-most type name, ignoring the qualifier, so `ZodObject`,
110
+ * `z.ZodObject`, and `import("zod").ZodObject` all read as `ZodObject`.
111
+ */
112
+ const rightmostTypeName = (node) => {
113
+ if (typescript.default.isTypeReferenceNode(node)) return entityNameRight(node.typeName);
114
+ if (typescript.default.isImportTypeNode(node)) return node.qualifier ? entityNameRight(node.qualifier) : void 0;
115
+ };
116
+ const typeArgumentsOf = (node) => {
117
+ if (typescript.default.isTypeReferenceNode(node)) return node.typeArguments ?? [];
118
+ if (typescript.default.isImportTypeNode(node)) return node.typeArguments ?? [];
119
+ return [];
120
+ };
121
+ const propertySignatureName = (node) => {
122
+ if (typescript.default.isIdentifier(node)) return node.text;
123
+ if (typescript.default.isStringLiteral(node)) return node.text;
124
+ if (typescript.default.isNumericLiteral(node)) return node.text;
125
+ };
126
+ /**
127
+ * Walks a dotted field path through a Zod schema type to the leaf property
128
+ * signature. Unwraps element wrappers (`ZodArray`, `ZodOptional`, …) and reads
129
+ * each `ZodObject`'s shape from its first type argument. Returns undefined and
130
+ * never throws when the path can't be navigated (e.g. the shape was emitted as
131
+ * a named reference rather than a literal).
132
+ */
133
+ const findPropertySignature = (typeNode, segments) => {
134
+ let current = typeNode;
135
+ for (let index = 0; index < segments.length; index += 1) {
136
+ let guard = 0;
137
+ let name = rightmostTypeName(current);
138
+ while (name !== void 0 && ELEMENT_WRAPPERS.has(name)) {
139
+ const args = typeArgumentsOf(current);
140
+ if (args.length === 0) return void 0;
141
+ current = args[0];
142
+ name = rightmostTypeName(current);
143
+ guard += 1;
144
+ if (guard > 32) return void 0;
145
+ }
146
+ if (name !== "ZodObject") return void 0;
147
+ const shape = typeArgumentsOf(current)[0];
148
+ if (!shape || !typescript.default.isTypeLiteralNode(shape)) return void 0;
149
+ const member = shape.members.find((candidate) => typescript.default.isPropertySignature(candidate) && propertySignatureName(candidate.name) === segments[index]);
150
+ if (!member) return void 0;
151
+ if (index === segments.length - 1) return member;
152
+ if (!member.type) return void 0;
153
+ current = member.type;
154
+ }
155
+ };
156
+ const hasLeadingJsDoc = (member, fullText) => {
157
+ return (typescript.default.getLeadingCommentRanges(fullText, member.getFullStart()) ?? []).some((range) => fullText.slice(range.pos, range.pos + 3) === "/**");
158
+ };
159
+ /**
160
+ * Re-indents a captured JSDoc block to `indent`: the opening `/**` keeps the
161
+ * property's own indentation (already present before the splice point), and each
162
+ * continuation line is re-prefixed so the `*` column lines up.
163
+ */
164
+ const reindentJsDoc = (block, indent) => block.split("\n").map((line, index) => index === 0 ? line.trim() : `${indent} ${line.trim()}`).join("\n");
165
+ const indentAt = (text, offset) => {
166
+ const lineStart = text.lastIndexOf("\n", offset - 1) + 1;
167
+ return text.slice(lineStart, offset).match(/^[ \t]*/)?.[0] ?? "";
168
+ };
169
+ /**
170
+ * Maps each locally declared name to the names it's re-exported under in the
171
+ * same file, e.g. `export { i as UserSchema }` from a bundled chunk where the
172
+ * `declare const` is named `i`.
173
+ */
174
+ const localExportAliases = (sourceFile) => {
175
+ const aliases = /* @__PURE__ */ new Map();
176
+ for (const statement of sourceFile.statements) {
177
+ if (!typescript.default.isExportDeclaration(statement) || statement.moduleSpecifier) continue;
178
+ if (!statement.exportClause || !typescript.default.isNamedExports(statement.exportClause)) continue;
179
+ for (const element of statement.exportClause.elements) {
180
+ const localName = (element.propertyName ?? element.name).text;
181
+ const list = aliases.get(localName) ?? [];
182
+ list.push(element.name.text);
183
+ aliases.set(localName, list);
184
+ }
185
+ }
186
+ return aliases;
187
+ };
188
+ const patchFile = (filePath, exportFieldMap) => {
189
+ const text = node_fs.readFileSync(filePath, "utf8");
190
+ const sourceFile = typescript.default.createSourceFile(filePath, text, typescript.default.ScriptTarget.Latest, true);
191
+ const aliases = localExportAliases(sourceFile);
192
+ const insertions = [];
193
+ for (const statement of sourceFile.statements) {
194
+ if (!typescript.default.isVariableStatement(statement)) continue;
195
+ for (const declaration of statement.declarationList.declarations) {
196
+ if (!typescript.default.isIdentifier(declaration.name) || !declaration.type) continue;
197
+ const fields = [declaration.name.text, ...aliases.get(declaration.name.text) ?? []].map((name) => exportFieldMap.get(name)).find(Boolean);
198
+ if (!fields) continue;
199
+ for (const [fieldPath, block] of fields) {
200
+ const member = findPropertySignature(declaration.type, fieldPath.split("."));
201
+ if (!member || hasLeadingJsDoc(member, text)) continue;
202
+ const offset = member.getStart(sourceFile);
203
+ const indent = indentAt(text, offset);
204
+ insertions.push({
205
+ offset,
206
+ text: `${reindentJsDoc(block, indent)}\n${indent}`
207
+ });
208
+ }
209
+ }
210
+ }
211
+ if (insertions.length === 0) return 0;
212
+ insertions.sort((left, right) => right.offset - left.offset);
213
+ let output = text;
214
+ for (const insertion of insertions) output = output.slice(0, insertion.offset) + insertion.text + output.slice(insertion.offset);
215
+ node_fs.writeFileSync(filePath, output, "utf8");
216
+ return insertions.length;
217
+ };
218
+ const DECLARATION_SUFFIXES = [
219
+ ".d.ts",
220
+ ".d.mts",
221
+ ".d.cts"
222
+ ];
223
+ const walkDeclarationFiles = (dir, into) => {
224
+ for (const entry of node_fs.readdirSync(dir, { withFileTypes: true })) {
225
+ const full = node_path.join(dir, entry.name);
226
+ if (entry.isDirectory()) walkDeclarationFiles(full, into);
227
+ else if (entry.isFile() && DECLARATION_SUFFIXES.some((suffix) => entry.name.endsWith(suffix))) into.push(full);
228
+ }
229
+ };
230
+ /**
231
+ * Re-injects the JSDoc blocks from {@link collectExportedSchemaDocs} onto Zod
232
+ * schema shape properties in the `.d.ts` files under `distDir`. Declaration emit
233
+ * drops the comments an author wrote on schema fields; this restores them, full
234
+ * descriptions, `@deprecated`, `@example`, etc., so they reach `z.infer`
235
+ * consumers in other repos. Skips properties that already have JSDoc. Idempotent.
236
+ */
237
+ const patchDeclarationDocs = (distDir, exportFieldMap) => {
238
+ const files = [];
239
+ walkDeclarationFiles(distDir, files);
240
+ let filesChanged = 0;
241
+ let injections = 0;
242
+ for (const file of files) {
243
+ const count = patchFile(file, exportFieldMap);
244
+ if (count > 0) {
245
+ filesChanged += 1;
246
+ injections += count;
247
+ }
248
+ }
249
+ return {
250
+ filesScanned: files.length,
251
+ filesChanged,
252
+ injections
253
+ };
254
+ };
255
+ //#endregion
256
+ //#region src/deprecation-parser.ts
257
+ const SCHEMA_KEYS = new Set([
258
+ "body",
259
+ "query",
260
+ "headers"
261
+ ]);
262
+ const readDeprecatedMessage = (node) => {
263
+ const tag = typescript.default.getJSDocTags(node).find((candidate) => candidate.tagName.text === "deprecated");
264
+ if (!tag) return void 0;
265
+ return typescript.default.getTextOfJSDocComment(tag.comment) ?? "";
266
+ };
267
+ const propertyName = (node) => {
268
+ if (typescript.default.isIdentifier(node)) return node.text;
269
+ if (typescript.default.isStringLiteral(node)) return node.text;
270
+ if (typescript.default.isNumericLiteral(node)) return node.text;
271
+ };
272
+ const resolveImportPath = (dir, specifier) => {
273
+ const base = specifier.replace(/\.js$/, "");
274
+ for (const candidate of [
275
+ `${base}.ts`,
276
+ `${base}/index.ts`,
277
+ base
278
+ ]) {
279
+ const full = node_path.resolve(dir, candidate);
280
+ if (node_fs.existsSync(full)) return full;
281
+ }
282
+ };
283
+ const buildFileScope = (filePath, cache) => {
284
+ if (cache.has(filePath)) return cache.get(filePath);
285
+ const scope = /* @__PURE__ */ new Map();
286
+ cache.set(filePath, scope);
287
+ const source = node_fs.readFileSync(filePath, "utf8");
288
+ const sourceFile = typescript.default.createSourceFile(filePath, source, typescript.default.ScriptTarget.Latest, true);
289
+ for (const statement of sourceFile.statements) {
290
+ if (!typescript.default.isVariableStatement(statement)) continue;
291
+ for (const decl of statement.declarationList.declarations) if (typescript.default.isIdentifier(decl.name) && decl.initializer) scope.set(decl.name.text, decl.initializer);
292
+ }
293
+ for (const statement of sourceFile.statements) {
294
+ if (!typescript.default.isImportDeclaration(statement)) continue;
295
+ if (!typescript.default.isStringLiteral(statement.moduleSpecifier)) continue;
296
+ const specifier = statement.moduleSpecifier.text;
297
+ if (!specifier.startsWith(".")) continue;
298
+ const importedPath = resolveImportPath(node_path.dirname(filePath), specifier);
299
+ if (!importedPath) continue;
300
+ const importedScope = buildFileScope(importedPath, cache);
301
+ const namedBindings = statement.importClause?.namedBindings;
302
+ if (!namedBindings || !typescript.default.isNamedImports(namedBindings)) continue;
303
+ for (const element of namedBindings.elements) {
304
+ const exportedName = (element.propertyName ?? element.name).text;
305
+ const localName = element.name.text;
306
+ const expr = importedScope.get(exportedName);
307
+ if (expr) scope.set(localName, expr);
308
+ }
309
+ }
310
+ return scope;
311
+ };
312
+ const makeResolverWithCache = (contractPath) => {
313
+ const cache = /* @__PURE__ */ new Map();
314
+ buildFileScope(contractPath, cache);
315
+ const resolve = (node) => {
316
+ const filePath = node.getSourceFile().fileName;
317
+ return (cache.get(filePath) ?? buildFileScope(filePath, cache)).get(node.text);
318
+ };
319
+ return {
320
+ resolve,
321
+ cache
322
+ };
323
+ };
324
+ const readObjectStringProperty = (object, name) => {
325
+ for (const property of object.properties) {
326
+ if (!typescript.default.isPropertyAssignment(property)) continue;
327
+ if (!typescript.default.isIdentifier(property.name) || property.name.text !== name) continue;
328
+ if (typescript.default.isStringLiteral(property.initializer)) return property.initializer.text;
329
+ }
330
+ };
331
+ const readAstMetaId = (expr) => {
332
+ if (!typescript.default.isCallExpression(expr)) return void 0;
333
+ const firstArg = expr.arguments[0];
334
+ if (isModelCall(expr)) {
335
+ if (!firstArg || !typescript.default.isObjectLiteralExpression(firstArg)) return void 0;
336
+ return readObjectStringProperty(firstArg, "title");
337
+ }
338
+ if (typescript.default.isPropertyAccessExpression(expr.expression) && expr.expression.name.text === "meta") {
339
+ if (!firstArg || !typescript.default.isObjectLiteralExpression(firstArg)) return void 0;
340
+ return readObjectStringProperty(firstArg, "id") ?? readAstMetaId(expr.expression.expression);
341
+ }
342
+ };
343
+ const firstObjectLiteralIn = (node, resolve, visited) => {
344
+ if (typescript.default.isObjectLiteralExpression(node)) return node;
345
+ if (typescript.default.isIdentifier(node)) {
346
+ const name = node.text;
347
+ if (visited.has(name)) return void 0;
348
+ const referenced = resolve(node);
349
+ if (!referenced) return void 0;
350
+ if (typescript.default.isArrowFunction(referenced) || typescript.default.isFunctionExpression(referenced)) return void 0;
351
+ visited.add(name);
352
+ return firstObjectLiteralIn(referenced, resolve, visited);
353
+ }
354
+ if (typescript.default.isCallExpression(node)) {
355
+ if (isContractChainCall(node) || isRoutesChainCall(node)) {
356
+ const routesArg = routesArgFrom(node);
357
+ if (routesArg) return firstObjectLiteralIn(routesArg, resolve, visited);
358
+ return;
359
+ }
360
+ if (isModelCall(node)) {
361
+ const modelSchema = extractCreateModelSchema(node);
362
+ return modelSchema ? firstObjectLiteralIn(modelSchema, resolve, visited) : void 0;
363
+ }
364
+ }
365
+ let found;
366
+ typescript.default.forEachChild(node, (child) => {
367
+ if (found) return;
368
+ found = firstObjectLiteralIn(child, resolve, visited);
369
+ });
370
+ return found;
371
+ };
372
+ const findExtendBase = (node) => {
373
+ if (!typescript.default.isCallExpression(node) || !typescript.default.isPropertyAccessExpression(node.expression)) return void 0;
374
+ if (node.expression.name.text === "extend") return node.expression.expression;
375
+ return findExtendBase(node.expression.expression);
376
+ };
377
+ const getFunctionBody = (func) => {
378
+ const { body } = func;
379
+ if (!typescript.default.isBlock(body)) return body;
380
+ let result;
381
+ typescript.default.forEachChild(body, (stmt) => {
382
+ if (!result && typescript.default.isReturnStatement(stmt) && stmt.expression) result = stmt.expression;
383
+ });
384
+ return result ?? body;
385
+ };
386
+ const makeScopedResolver = (func, args, parent) => {
387
+ const substitutions = /* @__PURE__ */ new Map();
388
+ func.parameters.forEach((param, index) => {
389
+ const arg = args[index];
390
+ if (arg && typescript.default.isIdentifier(param.name)) substitutions.set(param.name.text, arg);
391
+ });
392
+ return (node) => substitutions.get(node.text) ?? parent(node);
393
+ };
394
+ const extractCreateModelSchema = (node) => {
395
+ if (!typescript.default.isCallExpression(node) || !isModelCall(node)) return void 0;
396
+ const firstArg = node.arguments[0];
397
+ if (!firstArg || !typescript.default.isObjectLiteralExpression(firstArg)) return void 0;
398
+ for (const property of firstArg.properties) {
399
+ if (!typescript.default.isPropertyAssignment(property)) continue;
400
+ if (propertyName(property.name) === "schema") return property.initializer;
401
+ }
402
+ };
403
+ /**
404
+ * Walks a schema expression's fields, calling `visit` with each field's dot-path
405
+ * and its property node. Resolves identifiers, `Kizuna.model`, generic wrapper
406
+ * functions (e.g. `Pagination(Item)`), and `.extend()`; recurses into nested
407
+ * objects.
408
+ */
409
+ const walkSchemaFields = (schemaNode, prefix, resolve, visit) => {
410
+ const resolved = typescript.default.isIdentifier(schemaNode) ? resolve(schemaNode) ?? schemaNode : schemaNode;
411
+ const modelSchema = extractCreateModelSchema(resolved);
412
+ if (modelSchema) {
413
+ walkSchemaFields(modelSchema, prefix, resolve, visit);
414
+ return;
415
+ }
416
+ if (typescript.default.isCallExpression(resolved) && typescript.default.isIdentifier(resolved.expression)) {
417
+ const funcExpr = resolve(resolved.expression);
418
+ if (funcExpr && (typescript.default.isArrowFunction(funcExpr) || typescript.default.isFunctionExpression(funcExpr))) {
419
+ walkSchemaFields(getFunctionBody(funcExpr), prefix, makeScopedResolver(funcExpr, resolved.arguments, resolve), visit);
420
+ return;
421
+ }
422
+ }
423
+ const extendBase = findExtendBase(resolved);
424
+ if (extendBase) walkSchemaFields(extendBase, prefix, resolve, visit);
425
+ const objectLiteral = firstObjectLiteralIn(schemaNode, resolve, /* @__PURE__ */ new Set());
426
+ if (!objectLiteral) return;
427
+ for (const property of objectLiteral.properties) {
428
+ if (typescript.default.isShorthandPropertyAssignment(property)) {
429
+ visit(prefix === "" ? property.name.text : `${prefix}.${property.name.text}`, property);
430
+ continue;
431
+ }
432
+ if (!typescript.default.isPropertyAssignment(property)) continue;
433
+ const fieldName = propertyName(property.name);
434
+ if (fieldName === void 0) continue;
435
+ const fieldPath = prefix === "" ? fieldName : `${prefix}.${fieldName}`;
436
+ visit(fieldPath, property);
437
+ walkSchemaFields(property.initializer, fieldPath, resolve, visit);
438
+ }
439
+ };
440
+ const collectFieldDeprecations = (schemaNode, prefix, into, resolve) => {
441
+ walkSchemaFields(schemaNode, prefix, resolve, (fieldPath, property) => {
442
+ const message = readDeprecatedMessage(property);
443
+ if (message !== void 0) into.set(fieldPath, message);
444
+ });
445
+ };
446
+ /**
447
+ * Returns the verbatim leading JSDoc block (`/** … *\/`) on a node, or undefined
448
+ * when it has none.
449
+ */
450
+ const readJsDocBlock = (node) => {
451
+ const sourceFile = node.getSourceFile();
452
+ const jsDoc = (typescript.default.getLeadingCommentRanges(sourceFile.text, node.getFullStart()) ?? []).filter((range) => sourceFile.text.slice(range.pos, range.pos + 3) === "/**").at(-1);
453
+ return jsDoc ? sourceFile.text.slice(jsDoc.pos, jsDoc.end) : void 0;
454
+ };
455
+ const collectFieldDocs = (schemaNode, prefix, into, resolve) => {
456
+ walkSchemaFields(schemaNode, prefix, resolve, (fieldPath, property) => {
457
+ const block = readJsDocBlock(property);
458
+ if (block !== void 0) into.set(fieldPath, block);
459
+ });
460
+ };
461
+ const isRouteLike = (obj) => obj.properties.some((prop) => {
462
+ const name = typescript.default.isPropertyAssignment(prop) ? propertyName(prop.name) : typescript.default.isShorthandPropertyAssignment(prop) ? prop.name.text : void 0;
463
+ return name === "method" || name === "path" || name === "responses";
464
+ });
465
+ const routePropertyName = (prop) => {
466
+ if (typescript.default.isPropertyAssignment(prop)) return propertyName(prop.name);
467
+ if (typescript.default.isShorthandPropertyAssignment(prop)) return prop.name.text;
468
+ };
469
+ const routePropertyInitializer = (prop) => typescript.default.isPropertyAssignment(prop) ? prop.initializer : prop.name;
470
+ const buildMapFromRoutesLiteral = (routesLiteral, resolve, prefix = "") => {
471
+ const routes = /* @__PURE__ */ new Map();
472
+ const fields = /* @__PURE__ */ new Map();
473
+ for (const routeProperty of routesLiteral.properties) {
474
+ if (!typescript.default.isPropertyAssignment(routeProperty) && !typescript.default.isShorthandPropertyAssignment(routeProperty)) continue;
475
+ const routeName = routePropertyName(routeProperty);
476
+ if (routeName === void 0) continue;
477
+ const fullKey = prefix === "" ? routeName : `${prefix}.${routeName}`;
478
+ const routeMessage = readDeprecatedMessage(routeProperty);
479
+ if (routeMessage !== void 0) routes.set(fullKey, routeMessage);
480
+ const initializer = routePropertyInitializer(routeProperty);
481
+ const resolvedLiteral = typescript.default.isObjectLiteralExpression(initializer) ? initializer : firstObjectLiteralIn(initializer, resolve, /* @__PURE__ */ new Set());
482
+ if (!resolvedLiteral) continue;
483
+ if (!isRouteLike(resolvedLiteral)) {
484
+ const sub = buildMapFromRoutesLiteral(resolvedLiteral, resolve, fullKey);
485
+ for (const [key, value] of sub.routes) routes.set(key, value);
486
+ for (const [key, value] of sub.fields) fields.set(key, value);
487
+ continue;
488
+ }
489
+ const deprecated = /* @__PURE__ */ new Map();
490
+ for (const subProperty of resolvedLiteral.properties) {
491
+ if (!typescript.default.isPropertyAssignment(subProperty)) continue;
492
+ const subKey = propertyName(subProperty.name);
493
+ if (subKey === void 0) continue;
494
+ if (SCHEMA_KEYS.has(subKey)) {
495
+ collectFieldDeprecations(subProperty.initializer, subKey, deprecated, resolve);
496
+ continue;
497
+ }
498
+ if (subKey === "responses" && typescript.default.isObjectLiteralExpression(subProperty.initializer)) for (const responseEntry of subProperty.initializer.properties) {
499
+ if (!typescript.default.isPropertyAssignment(responseEntry)) continue;
500
+ const status = propertyName(responseEntry.name);
501
+ if (status === void 0) continue;
502
+ collectFieldDeprecations(responseEntry.initializer, `responses.${status}`, deprecated, resolve);
503
+ }
504
+ }
505
+ if (deprecated.size > 0) fields.set(fullKey, deprecated);
506
+ }
507
+ return {
508
+ routes,
509
+ fields
510
+ };
511
+ };
512
+ const findRouterCallInNode = (node) => {
513
+ if (typescript.default.isCallExpression(node)) {
514
+ if (isMemberCall(node, _ts_kizuna_core_authoring_names.AUTHORING_NAMES.router)) return node;
515
+ if (isContractChainCall(node) || isRoutesChainCall(node)) return node;
516
+ }
517
+ let found;
518
+ typescript.default.forEachChild(node, (child) => {
519
+ if (found) return;
520
+ found = findRouterCallInNode(child);
521
+ });
522
+ return found;
523
+ };
524
+ /**
525
+ * True when `node` calls `member` on any receiver, e.g. `k.routes(...)`.
526
+ */
527
+ const isMemberCall = (node, member) => typescript.default.isPropertyAccessExpression(node.expression) && typescript.default.isIdentifier(node.expression.name) && node.expression.name.text === member;
528
+ const isRoutesChainCall = (node) => isMemberCall(node, _ts_kizuna_core_authoring_names.AUTHORING_NAMES.routes);
529
+ const isContractChainCall = (node) => isMemberCall(node, _ts_kizuna_core_authoring_names.AUTHORING_NAMES.contract);
530
+ const isModelCall = (node) => isMemberCall(node, _ts_kizuna_core_authoring_names.AUTHORING_NAMES.model);
531
+ const isContractExportName = (name) => _ts_kizuna_core_authoring_names.CONTRACT_EXPORT_NAMES.includes(name);
532
+ const routesArgFrom = (call) => {
533
+ if (isRoutesChainCall(call)) return call.arguments[call.arguments.length - 1];
534
+ if (isContractChainCall(call)) {
535
+ const arg = call.arguments[0];
536
+ if (arg && typescript.default.isObjectLiteralExpression(arg)) for (const prop of arg.properties) {
537
+ if (typescript.default.isPropertyAssignment(prop) && propertyName(prop.name) === "routes") return prop.initializer;
538
+ if (typescript.default.isShorthandPropertyAssignment(prop) && prop.name.text === "routes") return prop.name;
539
+ }
540
+ return arg;
541
+ }
542
+ const firstArg = call.arguments[0];
543
+ if (!firstArg) return void 0;
544
+ if (call.arguments.length === 2) return call.arguments[1];
545
+ return firstArg;
546
+ };
547
+ const collectExportedRoutesLiterals = (sourceFile, resolve, into) => {
548
+ for (const statement of sourceFile.statements) {
549
+ if (!typescript.default.isVariableStatement(statement)) continue;
550
+ if (!(statement.modifiers ?? []).some((modifier) => modifier.kind === typescript.default.SyntaxKind.ExportKeyword)) continue;
551
+ for (const declaration of statement.declarationList.declarations) {
552
+ if (!typescript.default.isIdentifier(declaration.name)) continue;
553
+ const exportName = declaration.name.text;
554
+ if (!isContractExportName(exportName)) continue;
555
+ if (!declaration.initializer) continue;
556
+ let initializer = declaration.initializer;
557
+ if (typescript.default.isIdentifier(initializer)) {
558
+ const resolved = resolve(initializer);
559
+ if (resolved) initializer = resolved;
560
+ }
561
+ if (!typescript.default.isCallExpression(initializer)) continue;
562
+ initializer.expression;
563
+ if (isContractChainCall(initializer)) {
564
+ const routesArg = routesArgFrom(initializer);
565
+ const lit = routesArg ? firstObjectLiteralIn(routesArg, resolve, /* @__PURE__ */ new Set()) : void 0;
566
+ if (lit) into.push(lit);
567
+ }
568
+ }
569
+ }
570
+ for (const statement of sourceFile.statements) {
571
+ if (!typescript.default.isExportDeclaration(statement)) continue;
572
+ if (!statement.moduleSpecifier || !typescript.default.isStringLiteral(statement.moduleSpecifier)) continue;
573
+ const specifier = statement.moduleSpecifier.text;
574
+ if (!specifier.startsWith(".")) continue;
575
+ const exportClause = statement.exportClause;
576
+ if (!exportClause || !typescript.default.isNamedExports(exportClause)) continue;
577
+ if (!exportClause.elements.some((element) => {
578
+ const exportedName = element.name.text;
579
+ return isContractExportName(exportedName);
580
+ })) continue;
581
+ const importedPath = resolveImportPath(node_path.dirname(sourceFile.fileName), specifier);
582
+ if (!importedPath) continue;
583
+ collectExportedRoutesLiterals(typescript.default.createSourceFile(importedPath, node_fs.readFileSync(importedPath, "utf8"), typescript.default.ScriptTarget.Latest, true), resolve, into);
584
+ }
585
+ };
586
+ const findAllRouteObjectLiterals = (sourceFile, resolve) => {
587
+ const results = [];
588
+ collectExportedRoutesLiterals(sourceFile, resolve, results);
589
+ if (results.length > 0) return results;
590
+ const routerCall = findRouterCallInNode(sourceFile);
591
+ if (!routerCall) return [];
592
+ const routesArg = routesArgFrom(routerCall);
593
+ const lit = routesArg ? firstObjectLiteralIn(routesArg, resolve, /* @__PURE__ */ new Set()) : void 0;
594
+ return lit ? [lit] : [];
595
+ };
596
+ const parseFromSource = (contractPath) => {
597
+ const source = node_fs.readFileSync(contractPath, "utf8");
598
+ const sourceFile = typescript.default.createSourceFile(contractPath, source, typescript.default.ScriptTarget.Latest, true);
599
+ const { resolve, cache } = makeResolverWithCache(contractPath);
600
+ const routeLiterals = findAllRouteObjectLiterals(sourceFile, resolve);
601
+ const routes = /* @__PURE__ */ new Map();
602
+ const fields = /* @__PURE__ */ new Map();
603
+ for (const literal of routeLiterals) {
604
+ const partial = buildMapFromRoutesLiteral(literal, resolve);
605
+ for (const [key, value] of partial.routes) routes.set(key, value);
606
+ for (const [key, value] of partial.fields) fields.set(key, value);
607
+ }
608
+ const schemas = /* @__PURE__ */ new Map();
609
+ for (const fileScope of cache.values()) for (const expr of fileScope.values()) {
610
+ const id = readAstMetaId(expr);
611
+ if (!id) continue;
612
+ const fieldDeprecations = /* @__PURE__ */ new Map();
613
+ collectFieldDeprecations(expr, "", fieldDeprecations, resolve);
614
+ if (fieldDeprecations.size > 0) schemas.set(id, fieldDeprecations);
615
+ }
616
+ return {
617
+ routes,
618
+ fields,
619
+ schemas
620
+ };
621
+ };
622
+ /**
623
+ * Parses a contract's `@deprecated` JSDoc tags into a {@link DeprecationMap}.
624
+ */
625
+ const createDeprecationMap = (contractPath) => parseFromSource(contractPath);
626
+ /**
627
+ * Parses each contract's `@deprecated` tags and writes them to
628
+ * `<outDir>/deprecations.json`, keyed by contract fingerprint. Generators read
629
+ * the entry matching the contract they generate. Returns the written path.
630
+ */
631
+ const writeKizunaDeprecations = (contracts, outDir) => {
632
+ const entries = {};
633
+ for (const { contract, contractPath } of contracts) entries[(0, _ts_kizuna_core_generator.contractFingerprint)(contract)] = (0, _ts_kizuna_core_generator.serializeDeprecationMap)(createDeprecationMap(contractPath));
634
+ const outputPath = node_path.join(outDir, "deprecations.json");
635
+ node_fs.mkdirSync(outDir, { recursive: true });
636
+ node_fs.writeFileSync(outputPath, JSON.stringify(entries, null, 2), "utf8");
637
+ return outputPath;
638
+ };
639
+ //#endregion
640
+ //#region src/load-contract.ts
641
+ /**
642
+ * Imports a contract module with jiti (so a `.ts` entry works without a build
643
+ * step) and returns the named export (default `contract`) or the default export.
644
+ * Returns undefined when neither is present.
645
+ */
646
+ const loadContract = async (contractPath, exportName = "contract") => {
647
+ const loaded = await (0, jiti.createJiti)(require("url").pathToFileURL(__filename).href, { interopDefault: true }).import(contractPath);
648
+ return loaded[exportName] ?? loaded.default;
649
+ };
650
+ //#endregion
651
+ //#region src/lint-deprecations.ts
652
+ const editDistance = (a, b) => {
653
+ const rows = Array.from({ length: a.length + 1 }, (_, index) => [index, ...new Array(b.length).fill(0)]);
654
+ for (let column = 0; column <= b.length; column += 1) rows[0][column] = column;
655
+ for (let row = 1; row <= a.length; row += 1) for (let column = 1; column <= b.length; column += 1) {
656
+ const cost = a[row - 1] === b[column - 1] ? 0 : 1;
657
+ rows[row][column] = Math.min(rows[row - 1][column] + 1, rows[row][column - 1] + 1, rows[row - 1][column - 1] + cost);
658
+ }
659
+ return rows[a.length][b.length];
660
+ };
661
+ const lineOf = (text, index) => text.slice(0, index).split("\n").length;
662
+ /**
663
+ * Warns about JSDoc that would silently disable a deprecation, a misspelled
664
+ * `@deprecated` tag, or more than one on the same comment. Scans the contract
665
+ * source and the files it imports.
666
+ */
667
+ const lintDeprecations = (entryPath) => {
668
+ const { cache } = makeResolverWithCache(entryPath);
669
+ const warnings = [];
670
+ for (const filePath of cache.keys()) {
671
+ const text = node_fs.readFileSync(filePath, "utf8");
672
+ for (const block of text.matchAll(/\/\*\*[\s\S]*?\*\//g)) {
673
+ const blockStart = block.index;
674
+ const tags = [...block[0].matchAll(/@(\w+)/g)];
675
+ const deprecatedTags = tags.filter((tag) => tag[1] === "deprecated");
676
+ if (deprecatedTags.length > 1) warnings.push({
677
+ file: filePath,
678
+ line: lineOf(text, blockStart + deprecatedTags[1].index),
679
+ message: "Duplicate `@deprecated` tag in one comment, only the first message is used."
680
+ });
681
+ for (const tag of tags) {
682
+ const name = tag[1].toLowerCase();
683
+ if (name !== "deprecated" && name.startsWith("dep") && editDistance(name, "deprecated") <= 2) warnings.push({
684
+ file: filePath,
685
+ line: lineOf(text, blockStart + tag.index),
686
+ message: `\`@${tag[1]}\` looks like a typo of \`@deprecated\`, it will be ignored.`
687
+ });
688
+ }
689
+ }
690
+ }
691
+ return warnings;
692
+ };
693
+ //#endregion
694
+ Object.defineProperty(exports, "__toESM", {
695
+ enumerable: true,
696
+ get: function() {
697
+ return __toESM;
698
+ }
699
+ });
700
+ Object.defineProperty(exports, "collectExportedSchemaDocs", {
701
+ enumerable: true,
702
+ get: function() {
703
+ return collectExportedSchemaDocs;
704
+ }
705
+ });
706
+ Object.defineProperty(exports, "createDeprecationMap", {
707
+ enumerable: true,
708
+ get: function() {
709
+ return createDeprecationMap;
710
+ }
711
+ });
712
+ Object.defineProperty(exports, "lintDeprecations", {
713
+ enumerable: true,
714
+ get: function() {
715
+ return lintDeprecations;
716
+ }
717
+ });
718
+ Object.defineProperty(exports, "loadContract", {
719
+ enumerable: true,
720
+ get: function() {
721
+ return loadContract;
722
+ }
723
+ });
724
+ Object.defineProperty(exports, "patchDeclarationDocs", {
725
+ enumerable: true,
726
+ get: function() {
727
+ return patchDeclarationDocs;
728
+ }
729
+ });
730
+ Object.defineProperty(exports, "writeKizunaDeprecations", {
731
+ enumerable: true,
732
+ get: function() {
733
+ return writeKizunaDeprecations;
734
+ }
735
+ });