claude-code-jsonl-generator 0.1.3-beta.9 → 0.1.4-beta.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/dist/cli.cjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
- const require_emit_guards = require("./emit-guards-DWNOOQCB.cjs");
2
+ const require_emit_guards = require("./emit-guards-CwEZ3nRU.cjs");
3
3
  let node_fs_promises = require("node:fs/promises");
4
4
  let node_path = require("node:path");
5
5
  node_path = require_emit_guards.__toESM(node_path, 1);
6
- //#region src/cli.ts
6
+ // src/cli.ts
7
7
  /**
8
8
  * 引数を解釈する。`--corpus <dir> --out <parser/src/generated>`。
9
9
  *
@@ -30,7 +30,8 @@ async function main() {
30
30
  const typeBlocks = [];
31
31
  const guardBlocks = [];
32
32
  const registry = [];
33
- for (const [type, samples] of [...scan.samplesByType].toSorted((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)) {
33
+ const sortedSamples = [...scan.samplesByType].toSorted((a, b) => a[0].localeCompare(b[0]));
34
+ for (const [type, samples] of sortedSamples) {
34
35
  const shape = require_emit_guards.inferShape(samples);
35
36
  const name = `${require_emit_guards.pascalCase(type)}Entry`;
36
37
  const fn = `is${require_emit_guards.pascalCase(type)}`;
@@ -52,4 +53,3 @@ main().catch((error) => {
52
53
  console.error(error);
53
54
  process.exit(1);
54
55
  });
55
- //#endregion
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import { a as inferShape, n as emitInterface, r as pascalCase, s as scanCorpus, t as emitGuardFunction } from "./emit-guards-CSDZc-iy.js";
2
+ import { a as inferShape, n as emitInterface, r as pascalCase, s as scanCorpus, t as emitGuardFunction } from "./emit-guards-DPY2-6nD.js";
3
3
  import { mkdir, writeFile } from "node:fs/promises";
4
4
  import path from "node:path";
5
- //#region src/cli.ts
5
+ // src/cli.ts
6
6
  /**
7
7
  * 引数を解釈する。`--corpus <dir> --out <parser/src/generated>`。
8
8
  *
@@ -29,7 +29,8 @@ async function main() {
29
29
  const typeBlocks = [];
30
30
  const guardBlocks = [];
31
31
  const registry = [];
32
- for (const [type, samples] of [...scan.samplesByType].toSorted((a, b) => a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0)) {
32
+ const sortedSamples = [...scan.samplesByType].toSorted((a, b) => a[0].localeCompare(b[0]));
33
+ for (const [type, samples] of sortedSamples) {
33
34
  const shape = inferShape(samples);
34
35
  const name = `${pascalCase(type)}Entry`;
35
36
  const fn = `is${pascalCase(type)}`;
@@ -51,7 +52,6 @@ main().catch((error) => {
51
52
  console.error(error);
52
53
  process.exit(1);
53
54
  });
54
- //#endregion
55
55
  export {};
56
56
 
57
57
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { writeFile, mkdir } from 'node:fs/promises'\nimport path from 'node:path'\nimport { scanCorpus } from './scanner'\nimport { inferShape } from './infer'\nimport { emitInterface, pascalCase } from './emit-types'\nimport { emitGuardFunction } from './emit-guards'\n\n/**\n * 引数を解釈する。`--corpus <dir> --out <parser/src/generated>`。\n *\n * @param argv - process.argv.slice(2)\n */\nfunction parseArgs(argv: string[]): { corpus: string; out: string } {\n let corpus = ''\n let out = ''\n for (let i = 0; i < argv.length; i += 1) {\n if (argv[i] === '--corpus') corpus = argv[i + 1] ?? ''\n else if (argv[i] === '--out') out = argv[i + 1] ?? ''\n }\n if (!corpus || !out) {\n console.error('Usage: claude-code-jsonl-generator --corpus <dir> --out <parser/src/generated>')\n process.exit(2)\n }\n return { corpus, out }\n}\n\nasync function main(): Promise<void> {\n const { corpus, out } = parseArgs(process.argv.slice(2))\n const scan = await scanCorpus(corpus)\n console.error(`scanned: ${scan.totalLines} lines, ${scan.samplesByType.size} types, ${scan.malformedLines} malformed`)\n\n const typeBlocks: string[] = []\n const guardBlocks: string[] = []\n const registry: string[] = []\n for (const [type, samples] of [...scan.samplesByType].toSorted((a, b) =>\n a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0\n )) {\n const shape = inferShape(samples)\n const name = `${pascalCase(type)}Entry`\n const fn = `is${pascalCase(type)}`\n typeBlocks.push(emitInterface(name, type, shape))\n guardBlocks.push(emitGuardFunction(fn, type, shape))\n registry.push(` '${type}': ${fn},`)\n }\n\n const typesHeader = `/** GENERATED by claude-code-jsonl-generator. Do not edit by hand. */\\n\\n`\n const typesOut = typesHeader + typeBlocks.join('\\n\\n') + '\\n'\n\n const guardsHeader =\n `/** GENERATED by claude-code-jsonl-generator. Do not edit by hand. */\\n\\n` +\n `function isObject(v: unknown): v is Record<string, unknown> {\\n` +\n ` return typeof v === 'object' && v !== null && !Array.isArray(v)\\n}\\n` +\n `function isString(v: unknown): v is string { return typeof v === 'string' }\\n` +\n `function isNumber(v: unknown): v is number { return typeof v === 'number' }\\n` +\n `function isBoolean(v: unknown): v is boolean { return typeof v === 'boolean' }\\n\\n`\n const guardsRegistry =\n `\\n\\nexport const guards = {\\n${registry.join('\\n')}\\n} as const satisfies Record<string, (v: unknown) => boolean>\\n` +\n `export const KNOWN_TYPES = Object.keys(guards) as (keyof typeof guards)[]\\n` +\n `export function guardByType(type: string, v: unknown): boolean | undefined {\\n` +\n ` const g = (guards as Record<string, (x: unknown) => boolean>)[type]\\n` +\n ` return g === undefined ? undefined : g(v)\\n}\\n`\n const guardsOut = guardsHeader + guardBlocks.join('\\n\\n') + guardsRegistry\n\n await mkdir(out, { recursive: true })\n await writeFile(path.join(out,'types.generated.ts'), typesOut, 'utf8')\n await writeFile(path.join(out,'guards.generated.ts'), guardsOut, 'utf8')\n console.error(`written: ${path.join(out,'types.generated.ts')}, ${path.join(out,'guards.generated.ts')}`)\n console.error('NOTE: review the diff before committing. content-block guards / message shapes are curated manually and are not overwritten by this generator.')\n}\n\nmain().catch((error: unknown) => {\n console.error(error)\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;;AAaA,SAAS,UAAU,MAAiD;CAClE,IAAI,SAAS;CACb,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GACpC,IAAI,KAAK,OAAO,YAAY,SAAS,KAAK,IAAI,MAAM;MAC/C,IAAI,KAAK,OAAO,SAAS,MAAM,KAAK,IAAI,MAAM;CAErD,IAAI,CAAC,UAAU,CAAC,KAAK;EACnB,QAAQ,MAAM,gFAAgF;EAC9F,QAAQ,KAAK,CAAC;CAChB;CACA,OAAO;EAAE;EAAQ;CAAI;AACvB;AAEA,eAAe,OAAsB;CACnC,MAAM,EAAE,QAAQ,QAAQ,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;CACvD,MAAM,OAAO,MAAM,WAAW,MAAM;CACpC,QAAQ,MAAM,YAAY,KAAK,WAAW,UAAU,KAAK,cAAc,KAAK,UAAU,KAAK,eAAe,WAAW;CAErH,MAAM,aAAuB,CAAC;CAC9B,MAAM,cAAwB,CAAC;CAC/B,MAAM,WAAqB,CAAC;CAC5B,KAAK,MAAM,CAAC,MAAM,YAAY,CAAC,GAAG,KAAK,aAAa,CAAC,CAAC,UAAU,GAAG,MACjE,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,EAAE,KAAK,IAAI,CACvC,GAAG;EACD,MAAM,QAAQ,WAAW,OAAO;EAChC,MAAM,OAAO,GAAG,WAAW,IAAI,EAAE;EACjC,MAAM,KAAK,KAAK,WAAW,IAAI;EAC/B,WAAW,KAAK,cAAc,MAAM,MAAM,KAAK,CAAC;EAChD,YAAY,KAAK,kBAAkB,IAAI,MAAM,KAAK,CAAC;EACnD,SAAS,KAAK,MAAM,KAAK,KAAK,GAAG,EAAE;CACrC;CAGA,MAAM,WAAW,8EAAc,WAAW,KAAK,MAAM,IAAI;CAEzD,MAAM,eACJ;CAMF,MAAM,iBACJ,gCAAgC,SAAS,KAAK,IAAI,EAAE;CAKtD,MAAM,YAAY,eAAe,YAAY,KAAK,MAAM,IAAI;CAE5D,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;CACpC,MAAM,UAAU,KAAK,KAAK,KAAI,oBAAoB,GAAG,UAAU,MAAM;CACrE,MAAM,UAAU,KAAK,KAAK,KAAI,qBAAqB,GAAG,WAAW,MAAM;CACvE,QAAQ,MAAM,YAAY,KAAK,KAAK,KAAI,oBAAoB,EAAE,IAAI,KAAK,KAAK,KAAI,qBAAqB,GAAG;CACxG,QAAQ,MAAM,gJAAgJ;AAChK;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,MAAM,KAAK;CACnB,QAAQ,KAAK,CAAC;AAChB,CAAC"}
1
+ {"version":3,"file":"cli.js","names":[],"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { writeFile, mkdir } from 'node:fs/promises'\nimport path from 'node:path'\nimport { scanCorpus } from './scanner'\nimport { inferShape } from './infer'\nimport { emitInterface, pascalCase } from './emit-types'\nimport { emitGuardFunction } from './emit-guards'\n\n/**\n * 引数を解釈する。`--corpus <dir> --out <parser/src/generated>`。\n *\n * @param argv - process.argv.slice(2)\n */\nfunction parseArgs(argv: string[]): { corpus: string; out: string } {\n let corpus = ''\n let out = ''\n for (let i = 0; i < argv.length; i += 1) {\n if (argv[i] === '--corpus') corpus = argv[i + 1] ?? ''\n else if (argv[i] === '--out') out = argv[i + 1] ?? ''\n }\n if (!corpus || !out) {\n console.error('Usage: claude-code-jsonl-generator --corpus <dir> --out <parser/src/generated>')\n process.exit(2)\n }\n return { corpus, out }\n}\n\nasync function main(): Promise<void> {\n const { corpus, out } = parseArgs(process.argv.slice(2))\n const scan = await scanCorpus(corpus)\n console.error(`scanned: ${scan.totalLines} lines, ${scan.samplesByType.size} types, ${scan.malformedLines} malformed`)\n\n const typeBlocks: string[] = []\n const guardBlocks: string[] = []\n const registry: string[] = []\n const sortedSamples = [...scan.samplesByType].toSorted((a, b) =>\n a[0].localeCompare(b[0])\n )\n for (const [type, samples] of sortedSamples) {\n const shape = inferShape(samples)\n const name = `${pascalCase(type)}Entry`\n const fn = `is${pascalCase(type)}`\n typeBlocks.push(emitInterface(name, type, shape))\n guardBlocks.push(emitGuardFunction(fn, type, shape))\n registry.push(` '${type}': ${fn},`)\n }\n\n const typesHeader = `/** GENERATED by claude-code-jsonl-generator. Do not edit by hand. */\\n\\n`\n const typesOut = typesHeader + typeBlocks.join('\\n\\n') + '\\n'\n\n const guardsHeader =\n `/** GENERATED by claude-code-jsonl-generator. Do not edit by hand. */\\n\\n` +\n `function isObject(v: unknown): v is Record<string, unknown> {\\n` +\n ` return typeof v === 'object' && v !== null && !Array.isArray(v)\\n}\\n` +\n `function isString(v: unknown): v is string { return typeof v === 'string' }\\n` +\n `function isNumber(v: unknown): v is number { return typeof v === 'number' }\\n` +\n `function isBoolean(v: unknown): v is boolean { return typeof v === 'boolean' }\\n\\n`\n const guardsRegistry =\n `\\n\\nexport const guards = {\\n${registry.join('\\n')}\\n} as const satisfies Record<string, (v: unknown) => boolean>\\n` +\n `export const KNOWN_TYPES = Object.keys(guards) as (keyof typeof guards)[]\\n` +\n `export function guardByType(type: string, v: unknown): boolean | undefined {\\n` +\n ` const g = (guards as Record<string, (x: unknown) => boolean>)[type]\\n` +\n ` return g === undefined ? undefined : g(v)\\n}\\n`\n const guardsOut = guardsHeader + guardBlocks.join('\\n\\n') + guardsRegistry\n\n await mkdir(out, { recursive: true })\n await writeFile(path.join(out,'types.generated.ts'), typesOut, 'utf8')\n await writeFile(path.join(out,'guards.generated.ts'), guardsOut, 'utf8')\n console.error(`written: ${path.join(out,'types.generated.ts')}, ${path.join(out,'guards.generated.ts')}`)\n console.error('NOTE: review the diff before committing. content-block guards / message shapes are curated manually and are not overwritten by this generator.')\n}\n\nmain().catch((error: unknown) => {\n console.error(error)\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;;AAaA,SAAS,UAAU,MAAiD;CAClE,IAAI,SAAS;CACb,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GACpC,IAAI,KAAK,OAAO,YAAY,SAAS,KAAK,IAAI,MAAM;MAC/C,IAAI,KAAK,OAAO,SAAS,MAAM,KAAK,IAAI,MAAM;CAErD,IAAI,CAAC,UAAU,CAAC,KAAK;EACnB,QAAQ,MAAM,gFAAgF;EAC9F,QAAQ,KAAK,CAAC;CAChB;CACA,OAAO;EAAE;EAAQ;CAAI;AACvB;AAEA,eAAe,OAAsB;CACnC,MAAM,EAAE,QAAQ,QAAQ,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;CACvD,MAAM,OAAO,MAAM,WAAW,MAAM;CACpC,QAAQ,MAAM,YAAY,KAAK,WAAW,UAAU,KAAK,cAAc,KAAK,UAAU,KAAK,eAAe,WAAW;CAErH,MAAM,aAAuB,CAAC;CAC9B,MAAM,cAAwB,CAAC;CAC/B,MAAM,WAAqB,CAAC;CAC5B,MAAM,gBAAgB,CAAC,GAAG,KAAK,aAAa,CAAC,CAAC,UAAU,GAAG,MACzD,EAAE,EAAE,CAAC,cAAc,EAAE,EAAE,CACzB;CACA,KAAK,MAAM,CAAC,MAAM,YAAY,eAAe;EAC3C,MAAM,QAAQ,WAAW,OAAO;EAChC,MAAM,OAAO,GAAG,WAAW,IAAI,EAAE;EACjC,MAAM,KAAK,KAAK,WAAW,IAAI;EAC/B,WAAW,KAAK,cAAc,MAAM,MAAM,KAAK,CAAC;EAChD,YAAY,KAAK,kBAAkB,IAAI,MAAM,KAAK,CAAC;EACnD,SAAS,KAAK,MAAM,KAAK,KAAK,GAAG,EAAE;CACrC;CAGA,MAAM,WAAW,8EAAc,WAAW,KAAK,MAAM,IAAI;CAEzD,MAAM,eACJ;CAMF,MAAM,iBACJ,gCAAgC,SAAS,KAAK,IAAI,EAAE;CAKtD,MAAM,YAAY,eAAe,YAAY,KAAK,MAAM,IAAI;CAE5D,MAAM,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;CACpC,MAAM,UAAU,KAAK,KAAK,KAAI,oBAAoB,GAAG,UAAU,MAAM;CACrE,MAAM,UAAU,KAAK,KAAK,KAAI,qBAAqB,GAAG,WAAW,MAAM;CACvE,QAAQ,MAAM,YAAY,KAAK,KAAK,KAAI,oBAAoB,EAAE,IAAI,KAAK,KAAK,KAAI,qBAAqB,GAAG;CACxG,QAAQ,MAAM,gJAAgJ;AAChK;AAEA,KAAK,CAAC,CAAC,OAAO,UAAmB;CAC/B,QAAQ,MAAM,KAAK;CACnB,QAAQ,KAAK,CAAC;AAChB,CAAC"}
@@ -1,4 +1,4 @@
1
- //#region \0rolldown/runtime.js
1
+ // \0rolldown/runtime.js
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -19,13 +19,12 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
19
19
  value: mod,
20
20
  enumerable: true
21
21
  }) : target, mod));
22
- //#endregion
23
22
  let node_fs_promises = require("node:fs/promises");
24
23
  let node_fs = require("node:fs");
25
24
  let node_readline = require("node:readline");
26
25
  let node_path = require("node:path");
27
26
  node_path = __toESM(node_path, 1);
28
- //#region src/scanner.ts
27
+ // src/scanner.ts
29
28
  /**
30
29
  * `.jsonl` ファイルを再帰収集する。
31
30
  *
@@ -34,7 +33,8 @@ node_path = __toESM(node_path, 1);
34
33
  */
35
34
  async function collectJsonlFiles(dir) {
36
35
  const out = [];
37
- for (const entry of await (0, node_fs_promises.readdir)(dir)) {
36
+ const entries = await (0, node_fs_promises.readdir)(dir);
37
+ for (const entry of entries) {
38
38
  const full = node_path.default.join(dir, entry);
39
39
  if ((await (0, node_fs_promises.stat)(full)).isDirectory()) out.push(...await collectJsonlFiles(full));
40
40
  else if (entry.endsWith(".jsonl")) out.push(full);
@@ -52,7 +52,8 @@ async function scanCorpus(corpusDir, maxSamplesPerType = 5e3) {
52
52
  let totalLines = 0;
53
53
  let malformedLines = 0;
54
54
  let typelessLines = 0;
55
- for (const file of await collectJsonlFiles(corpusDir)) {
55
+ const files = await collectJsonlFiles(corpusDir);
56
+ for (const file of files) {
56
57
  const rl = (0, node_readline.createInterface)({
57
58
  input: (0, node_fs.createReadStream)(file, { encoding: "utf8" }),
58
59
  crlfDelay: Infinity
@@ -74,10 +75,9 @@ async function scanCorpus(corpusDir, maxSamplesPerType = 5e3) {
74
75
  const record = obj;
75
76
  const type = record.type;
76
77
  const bucket = samplesByType.get(type) ?? [];
77
- if (bucket.length < maxSamplesPerType) {
78
- bucket.push(record);
79
- samplesByType.set(type, bucket);
80
- }
78
+ if (!(bucket.length < maxSamplesPerType)) continue;
79
+ bucket.push(record);
80
+ samplesByType.set(type, bucket);
81
81
  }
82
82
  }
83
83
  return {
@@ -87,8 +87,7 @@ async function scanCorpus(corpusDir, maxSamplesPerType = 5e3) {
87
87
  typelessLines
88
88
  };
89
89
  }
90
- //#endregion
91
- //#region src/infer.ts
90
+ // src/infer.ts
92
91
  /**
93
92
  * JSON 値の種別を返す。
94
93
  *
@@ -98,12 +97,11 @@ function jsonKind(v) {
98
97
  if (v === null) return "null";
99
98
  if (Array.isArray(v)) return "array";
100
99
  const t = typeof v;
101
- if ([
100
+ return [
102
101
  "string",
103
102
  "number",
104
103
  "boolean"
105
- ].includes(t)) return t;
106
- return "object";
104
+ ].includes(t) ? t : "object";
107
105
  }
108
106
  /** `InferOptions.maxLiterals` 省略時の既定値。 */
109
107
  const DEFAULT_MAX_LITERALS = 20;
@@ -129,8 +127,7 @@ function inferShape(samples, options = {}) {
129
127
  if (cur === null) {} else if (kind === "string") {
130
128
  const set = cur ?? /* @__PURE__ */ new Set();
131
129
  set.add(value);
132
- if (set.size > maxLiterals) literals.set(key, null);
133
- else literals.set(key, set);
130
+ literals.set(key, set.size > maxLiterals ? null : set);
134
131
  }
135
132
  }
136
133
  const fields = /* @__PURE__ */ new Map();
@@ -149,8 +146,7 @@ function inferShape(samples, options = {}) {
149
146
  fields
150
147
  };
151
148
  }
152
- //#endregion
153
- //#region src/emit-types.ts
149
+ // src/emit-types.ts
154
150
  /** JS 識別子として安全な名前かどうかを判定する正規表現。 */
155
151
  const IDENTIFIER_RE$1 = /^[A-Za-z_$][\w$]*$/;
156
152
  /** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */
@@ -174,7 +170,7 @@ function tsTypeOf(kinds, literals) {
174
170
  object: "Record<string, unknown>"
175
171
  };
176
172
  if (literals && literals.size > 0 && kinds.has("string")) {
177
- const lits = [...literals].toSorted().map((s) => `'${escapeSingleQuote$1(s)}'`);
173
+ const lits = [...literals].toSorted((a, b) => a.localeCompare(b)).map((s) => `'${escapeSingleQuote$1(s)}'`);
178
174
  parts.push(`${lits.join(" | ")} | (string & {})`);
179
175
  } else if (kinds.has("string")) parts.push("string");
180
176
  for (const k of [
@@ -216,8 +212,7 @@ function emitInterface(interfaceName, type, shape) {
216
212
  lines.push("}");
217
213
  return lines.join("\n");
218
214
  }
219
- //#endregion
220
- //#region src/emit-guards.ts
215
+ // src/emit-guards.ts
221
216
  /** JS 識別子として安全な名前かどうかを判定する正規表現。 */
222
217
  const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/;
223
218
  /** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */
@@ -261,7 +256,6 @@ function emitGuardFunction(fnName, type, shape) {
261
256
  `}`
262
257
  ].join("\n");
263
258
  }
264
- //#endregion
265
259
  Object.defineProperty(exports, "__toESM", {
266
260
  enumerable: true,
267
261
  get: function() {
@@ -2,7 +2,7 @@ import { readdir, stat } from "node:fs/promises";
2
2
  import { createReadStream } from "node:fs";
3
3
  import { createInterface } from "node:readline";
4
4
  import path from "node:path";
5
- //#region src/scanner.ts
5
+ // src/scanner.ts
6
6
  /**
7
7
  * `.jsonl` ファイルを再帰収集する。
8
8
  *
@@ -11,7 +11,8 @@ import path from "node:path";
11
11
  */
12
12
  async function collectJsonlFiles(dir) {
13
13
  const out = [];
14
- for (const entry of await readdir(dir)) {
14
+ const entries = await readdir(dir);
15
+ for (const entry of entries) {
15
16
  const full = path.join(dir, entry);
16
17
  if ((await stat(full)).isDirectory()) out.push(...await collectJsonlFiles(full));
17
18
  else if (entry.endsWith(".jsonl")) out.push(full);
@@ -29,7 +30,8 @@ async function scanCorpus(corpusDir, maxSamplesPerType = 5e3) {
29
30
  let totalLines = 0;
30
31
  let malformedLines = 0;
31
32
  let typelessLines = 0;
32
- for (const file of await collectJsonlFiles(corpusDir)) {
33
+ const files = await collectJsonlFiles(corpusDir);
34
+ for (const file of files) {
33
35
  const rl = createInterface({
34
36
  input: createReadStream(file, { encoding: "utf8" }),
35
37
  crlfDelay: Infinity
@@ -51,10 +53,9 @@ async function scanCorpus(corpusDir, maxSamplesPerType = 5e3) {
51
53
  const record = obj;
52
54
  const type = record.type;
53
55
  const bucket = samplesByType.get(type) ?? [];
54
- if (bucket.length < maxSamplesPerType) {
55
- bucket.push(record);
56
- samplesByType.set(type, bucket);
57
- }
56
+ if (!(bucket.length < maxSamplesPerType)) continue;
57
+ bucket.push(record);
58
+ samplesByType.set(type, bucket);
58
59
  }
59
60
  }
60
61
  return {
@@ -64,8 +65,7 @@ async function scanCorpus(corpusDir, maxSamplesPerType = 5e3) {
64
65
  typelessLines
65
66
  };
66
67
  }
67
- //#endregion
68
- //#region src/infer.ts
68
+ // src/infer.ts
69
69
  /**
70
70
  * JSON 値の種別を返す。
71
71
  *
@@ -75,12 +75,11 @@ function jsonKind(v) {
75
75
  if (v === null) return "null";
76
76
  if (Array.isArray(v)) return "array";
77
77
  const t = typeof v;
78
- if ([
78
+ return [
79
79
  "string",
80
80
  "number",
81
81
  "boolean"
82
- ].includes(t)) return t;
83
- return "object";
82
+ ].includes(t) ? t : "object";
84
83
  }
85
84
  /** `InferOptions.maxLiterals` 省略時の既定値。 */
86
85
  const DEFAULT_MAX_LITERALS = 20;
@@ -106,8 +105,7 @@ function inferShape(samples, options = {}) {
106
105
  if (cur === null) {} else if (kind === "string") {
107
106
  const set = cur ?? /* @__PURE__ */ new Set();
108
107
  set.add(value);
109
- if (set.size > maxLiterals) literals.set(key, null);
110
- else literals.set(key, set);
108
+ literals.set(key, set.size > maxLiterals ? null : set);
111
109
  }
112
110
  }
113
111
  const fields = /* @__PURE__ */ new Map();
@@ -126,8 +124,7 @@ function inferShape(samples, options = {}) {
126
124
  fields
127
125
  };
128
126
  }
129
- //#endregion
130
- //#region src/emit-types.ts
127
+ // src/emit-types.ts
131
128
  /** JS 識別子として安全な名前かどうかを判定する正規表現。 */
132
129
  const IDENTIFIER_RE$1 = /^[A-Za-z_$][\w$]*$/;
133
130
  /** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */
@@ -151,7 +148,7 @@ function tsTypeOf(kinds, literals) {
151
148
  object: "Record<string, unknown>"
152
149
  };
153
150
  if (literals && literals.size > 0 && kinds.has("string")) {
154
- const lits = [...literals].toSorted().map((s) => `'${escapeSingleQuote$1(s)}'`);
151
+ const lits = [...literals].toSorted((a, b) => a.localeCompare(b)).map((s) => `'${escapeSingleQuote$1(s)}'`);
155
152
  parts.push(`${lits.join(" | ")} | (string & {})`);
156
153
  } else if (kinds.has("string")) parts.push("string");
157
154
  for (const k of [
@@ -193,8 +190,7 @@ function emitInterface(interfaceName, type, shape) {
193
190
  lines.push("}");
194
191
  return lines.join("\n");
195
192
  }
196
- //#endregion
197
- //#region src/emit-guards.ts
193
+ // src/emit-guards.ts
198
194
  /** JS 識別子として安全な名前かどうかを判定する正規表現。 */
199
195
  const IDENTIFIER_RE = /^[A-Za-z_$][\w$]*$/;
200
196
  /** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */
@@ -238,7 +234,6 @@ function emitGuardFunction(fnName, type, shape) {
238
234
  `}`
239
235
  ].join("\n");
240
236
  }
241
- //#endregion
242
237
  export { inferShape as a, tsTypeOf as i, emitInterface as n, jsonKind as o, pascalCase as r, scanCorpus as s, emitGuardFunction as t };
243
238
 
244
- //# sourceMappingURL=emit-guards-CSDZc-iy.js.map
239
+ //# sourceMappingURL=emit-guards-DPY2-6nD.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emit-guards-DPY2-6nD.js","names":["IDENTIFIER_RE","escapeSingleQuote"],"sources":["../src/scanner.ts","../src/infer.ts","../src/emit-types.ts","../src/emit-guards.ts"],"sourcesContent":["import { readdir, stat } from 'node:fs/promises'\nimport { createReadStream } from 'node:fs'\nimport { createInterface } from 'node:readline'\nimport path from 'node:path'\n\n/** スキャン結果。 */\nexport interface ScanResult {\n /** type 文字列 → その type の生 JSON オブジェクト配列。 */\n samplesByType: Map<string, Record<string, unknown>[]>\n /** 走査した有効行数 (パース成功)。 */\n totalLines: number\n /** JSON 構文エラー行数。 */\n malformedLines: number\n /** type フィールドを持たない/文字列でない行数。 */\n typelessLines: number\n}\n\n/**\n * `.jsonl` ファイルを再帰収集する。\n *\n * @param dir - 起点ディレクトリ\n * @returns 絶対パス配列\n */\nasync function collectJsonlFiles(dir: string): Promise<string[]> {\n const out: string[] = []\n const entries = await readdir(dir)\n for (const entry of entries) {\n const full = path.join(dir, entry)\n const st = await stat(full)\n if (st.isDirectory()) {\n out.push(...(await collectJsonlFiles(full)))\n } else if (entry.endsWith('.jsonl')) {\n out.push(full)\n }\n }\n return out\n}\n\n/**\n * コーパスを走査し type ごとに生サンプルを集約する。\n *\n * @param corpusDir - コーパスのルートディレクトリ\n * @param maxSamplesPerType - type ごとの保持上限 (メモリ保護、既定 5000)\n */\nexport async function scanCorpus(\n corpusDir: string,\n maxSamplesPerType = 5000\n): Promise<ScanResult> {\n const samplesByType = new Map<string, Record<string, unknown>[]>()\n let totalLines = 0\n let malformedLines = 0\n let typelessLines = 0\n\n const files = await collectJsonlFiles(corpusDir)\n for (const file of files) {\n const rl = createInterface({\n input: createReadStream(file, { encoding: 'utf8' }),\n crlfDelay: Infinity,\n })\n for await (const raw of rl) {\n if (raw.trim() === '') continue\n let obj: unknown\n try {\n obj = JSON.parse(raw)\n } catch {\n malformedLines += 1\n continue\n }\n totalLines += 1\n if (\n typeof obj !== 'object' || obj === null || Array.isArray(obj) ||\n typeof (obj as Record<string, unknown>).type !== 'string'\n ) {\n typelessLines += 1\n continue\n }\n const record = obj as Record<string, unknown>\n const type = record.type as string\n const bucket = samplesByType.get(type) ?? []\n if (!(bucket.length < maxSamplesPerType)) {\n continue\n }\n\n bucket.push(record)\n samplesByType.set(type, bucket)\n }\n }\n\n return { samplesByType, totalLines, malformedLines, typelessLines }\n}\n","/** JSON 値の種別。 */\nexport type JsonKind = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object'\n\n/**\n * JSON 値の種別を返す。\n *\n * @param v - 対象値\n */\nexport function jsonKind(v: unknown): JsonKind {\n if (v === null) return 'null'\n if (Array.isArray(v)) return 'array'\n const t = typeof v\n return (['string', 'number', 'boolean'] as string[]).includes(t) ? (t as 'string' | 'number' | 'boolean') : 'object'\n}\n\n/** 1 フィールドの推論結果。 */\nexport interface FieldShape {\n /** 全サンプルに存在したか。 */\n required: boolean\n /** 観測された JSON 種別集合。 */\n kinds: Set<JsonKind>\n /** 観測された文字列リテラル集合 (閾値以下のときのみ)。 */\n literals?: Set<string>\n}\n\n/** 1 type (または content ブロック) の推論結果。 */\nexport interface Shape {\n /** サンプル総数。 */\n count: number\n /** フィールド名 → 推論結果。 */\n fields: Map<string, FieldShape>\n}\n\n/** `InferOptions.maxLiterals` 省略時の既定値。 */\nconst DEFAULT_MAX_LITERALS = 20\n\n/** 推論オプション。 */\nexport interface InferOptions {\n /** 文字列リテラルを列挙として保持する上限。超えたら literals を破棄。既定は `DEFAULT_MAX_LITERALS`。 */\n maxLiterals?: number\n}\n\n/**\n * 同一 type のサンプル群からフィールド形状を推論する。\n *\n * @param samples - 生 JSON オブジェクト配列\n * @param options - 推論オプション\n */\nexport function inferShape(\n samples: Record<string, unknown>[],\n options: InferOptions = {}\n): Shape {\n const maxLiterals = options.maxLiterals ?? DEFAULT_MAX_LITERALS\n const count = samples.length\n const presence = new Map<string, number>()\n const kinds = new Map<string, Set<JsonKind>>()\n const literals = new Map<string, Set<string> | null>()\n\n for (const sample of samples) {\n for (const [key, value] of Object.entries(sample)) {\n presence.set(key, (presence.get(key) ?? 0) + 1)\n\n const kset = kinds.get(key) ?? new Set<JsonKind>()\n const kind = jsonKind(value)\n kset.add(kind)\n kinds.set(key, kset)\n\n // 文字列リテラル収集 (null は「破棄済み」マーカー)\n const cur = literals.get(key)\n if (cur === null) {\n // すでに破棄済み\n } else if (kind === 'string') {\n const set = cur ?? new Set<string>()\n set.add(value as string)\n literals.set(key, set.size > maxLiterals ? null : set)\n }\n }\n }\n\n const fields = new Map<string, FieldShape>()\n for (const [key, seen] of presence) {\n const lit = literals.get(key)\n const kindSet = kinds.get(key) ?? new Set<JsonKind>()\n // 全て文字列だった場合のみ literals を採用する\n const allString = kindSet.size === 1 && kindSet.has('string')\n fields.set(key, {\n required: seen === count,\n kinds: kindSet,\n literals: allString && lit ? lit : undefined,\n })\n }\n\n return { count, fields }\n}\n","import type { JsonKind, Shape } from './infer'\n\n/** JS 識別子として安全な名前かどうかを判定する正規表現。 */\nconst IDENTIFIER_RE = /^[A-Za-z_$][\\w$]*$/\n\n/** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */\nfunction escapeSingleQuote(s: string): string {\n return s.replaceAll('\\'', String.raw`\\'`)\n}\n\n/**\n * 観測種別集合 (と任意のリテラル集合) を TypeScript 型文字列に写像する。\n *\n * @param kinds - 観測された JSON 種別\n * @param literals - 文字列リテラル集合 (あれば)\n */\nexport function tsTypeOf(kinds: Set<JsonKind>, literals?: Set<string>): string {\n const parts: string[] = []\n const mapped: Record<JsonKind, string> = {\n string: 'string',\n number: 'number',\n boolean: 'boolean',\n null: 'null',\n array: 'unknown[]',\n object: 'Record<string, unknown>',\n }\n // 文字列リテラルが与えられていれば開いた union にする\n if (literals && literals.size > 0 && kinds.has('string')) {\n const lits = [...literals].toSorted((a, b) => a.localeCompare(b)).map((s) => `'${escapeSingleQuote(s)}'`)\n parts.push(`${lits.join(' | ')} | (string & {})`)\n } else if (kinds.has('string')) {\n parts.push('string')\n }\n for (const k of ['number', 'boolean', 'array', 'object', 'null'] as JsonKind[]) {\n if (kinds.has(k)) parts.push(mapped[k])\n }\n // 重複除去して結合\n return [...new Set(parts)].join(' | ')\n}\n\n/** JS 識別子として安全ならそのまま、そうでなければクォートする。 */\nfunction propKey(name: string): string {\n return IDENTIFIER_RE.test(name) ? name : `'${escapeSingleQuote(name)}'`\n}\n\n/** interface 名として使うため type 文字列を PascalCase 化する (エミッタ内部用)。 */\nexport function pascalCase(type: string): string {\n return type\n .split(/[-_]/)\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join('')\n}\n\n/**\n * 1 type の interface 文字列を生成する。\n *\n * @param interfaceName - 出力する interface 名 (例 LastPromptEntry)\n * @param type - 判別子文字列 (例 last-prompt)\n * @param shape - 推論結果\n */\nexport function emitInterface(\n interfaceName: string,\n type: string,\n shape: Shape\n): string {\n const lines: string[] = [\n `export interface ${interfaceName} {`,\n ` _kind: 'known'`,\n ` type: '${escapeSingleQuote(type)}'`,\n ]\n for (const [name, field] of shape.fields) {\n if (name === 'type') continue\n const opt = field.required ? '' : '?'\n const tsType = tsTypeOf(field.kinds, field.literals)\n lines.push(` ${propKey(name)}${opt}: ${tsType}`)\n }\n lines.push('}')\n return lines.join('\\n')\n}\n","import type { JsonKind, Shape } from './infer'\n\n/** JS 識別子として安全な名前かどうかを判定する正規表現。 */\nconst IDENTIFIER_RE = /^[A-Za-z_$][\\w$]*$/\n\n/** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */\nfunction escapeSingleQuote(s: string): string {\n return s.replaceAll('\\'', String.raw`\\'`)\n}\n\n/** 単一種別の required フィールドに対する検査式を返す。 */\nfunction checkExpr(name: string, kinds: Set<JsonKind>): string | undefined {\n const access = `v.${IDENTIFIER_RE.test(name) ? name : `['${escapeSingleQuote(name)}']`}`\n // object|null → isObject or null 許容\n const hasNull = kinds.has('null')\n const nonNull = [...kinds].filter((k) => k !== 'null')\n if (nonNull.length !== 1) {\n // 複数種別が混在する場合は存在確認のみ (過剰厳格を避ける)\n return `'${escapeSingleQuote(name)}' in v`\n }\n const kind = nonNull[0]\n const base: Partial<Record<JsonKind, string>> = {\n string: `isString(${access})`,\n number: `isNumber(${access})`,\n boolean: `isBoolean(${access})`,\n object: `isObject(${access})`,\n array: `Array.isArray(${access})`,\n }\n const expr = base[kind]\n if (expr === undefined) return `'${escapeSingleQuote(name)}' in v`\n return hasNull ? `(${access} === null || ${expr})` : expr\n}\n\n/**\n * 1 type のガード関数文字列を生成する。required フィールドのみ検査する。\n *\n * @param fnName - 関数名 (例 isMode)\n * @param type - 判別子\n * @param shape - 推論結果\n */\nexport function emitGuardFunction(\n fnName: string,\n type: string,\n shape: Shape\n): string {\n const checks: string[] = [\n `isObject(v)`,\n `v.type === '${escapeSingleQuote(type)}'`,\n ]\n for (const [name, field] of shape.fields) {\n if (name === 'type' || !field.required) continue\n const expr = checkExpr(name, field.kinds)\n if (expr) checks.push(expr)\n }\n return [\n `function ${fnName}(v: unknown): boolean {`,\n ` return ${checks.join(' &&\\n ')}`,\n `}`,\n ].join('\\n')\n}\n"],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_emit_guards = require("./emit-guards-DWNOOQCB.cjs");
2
+ const require_emit_guards = require("./emit-guards-CwEZ3nRU.cjs");
3
3
  let zod = require("zod");
4
- //#region src/schemas.ts
4
+ // src/schemas.ts
5
5
  const entryBase = {
6
6
  cwd: zod.z.string(),
7
7
  entrypoint: zod.z.string(),
@@ -207,7 +207,6 @@ const KNOWN_TYPES = Object.keys(entrySchemas);
207
207
  function knownTypeSchema(type) {
208
208
  return entrySchemas[type];
209
209
  }
210
- //#endregion
211
210
  exports.KNOWN_TYPES = KNOWN_TYPES;
212
211
  exports.emitGuardFunction = require_emit_guards.emitGuardFunction;
213
212
  exports.emitInterface = require_emit_guards.emitInterface;
package/dist/index.d.cts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod";
2
- //#region src/scanner.d.ts
3
2
  /** スキャン結果。 */
4
3
  interface ScanResult {
5
4
  /** type 文字列 → その type の生 JSON オブジェクト配列。 */
@@ -18,8 +17,6 @@ interface ScanResult {
18
17
  * @param maxSamplesPerType - type ごとの保持上限 (メモリ保護、既定 5000)
19
18
  */
20
19
  export declare function scanCorpus(corpusDir: string, maxSamplesPerType?: number): Promise<ScanResult>;
21
- //#endregion
22
- //#region src/infer.d.ts
23
20
  /** JSON 値の種別。 */
24
21
  type JsonKind = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object';
25
22
  /**
@@ -56,8 +53,6 @@ interface InferOptions {
56
53
  * @param options - 推論オプション
57
54
  */
58
55
  export declare function inferShape(samples: Record<string, unknown>[], options?: InferOptions): Shape;
59
- //#endregion
60
- //#region src/schemas.d.ts
61
56
  /** type 文字列 → 公開 zod スキーマ。 */
62
57
  export declare const entrySchemas: {
63
58
  readonly assistant: z.ZodObject<{
@@ -285,8 +280,6 @@ export declare const KNOWN_TYPES: string[];
285
280
  * @param type - 判別子
286
281
  */
287
282
  export declare function knownTypeSchema(type: string): z.ZodType | undefined;
288
- //#endregion
289
- //#region src/emit-types.d.ts
290
283
  /**
291
284
  * 観測種別集合 (と任意のリテラル集合) を TypeScript 型文字列に写像する。
292
285
  *
@@ -304,8 +297,6 @@ export declare function pascalCase(type: string): string;
304
297
  * @param shape - 推論結果
305
298
  */
306
299
  export declare function emitInterface(interfaceName: string, type: string, shape: Shape): string;
307
- //#endregion
308
- //#region src/emit-guards.d.ts
309
300
  /**
310
301
  * 1 type のガード関数文字列を生成する。required フィールドのみ検査する。
311
302
  *
@@ -314,6 +305,5 @@ export declare function emitInterface(interfaceName: string, type: string, shape
314
305
  * @param shape - 推論結果
315
306
  */
316
307
  export declare function emitGuardFunction(fnName: string, type: string, shape: Shape): string;
317
- //#endregion
318
308
  export type { FieldShape, InferOptions, JsonKind, ScanResult, Shape };
319
309
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/scanner.ts","../src/infer.ts","../src/schemas.ts","../src/emit-types.ts","../src/emit-guards.ts"],"mappings":";;;UAMiB;;EAEf,eAAe,YAAY;;EAE3B;;EAEA;;EAEA;;;;;;;;wBA6BoB,WACpB,mBACA,6BACC,QAAQ;;;;KC7CC;;;;;;wBAOI,SAAS,aAAa;;UAWrB;;EAEf;;EAEA,OAAO,IAAI;;EAEX,WAAW;;;UAII;;EAEf;;EAEA,QAAQ,YAAY;;;UAOL;;EAEf;;;;;;;;wBASc,WACd,SAAS,2BACT,UAAS,eACR;;;;qBCuFU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkBA;;;;;;wBAOG,gBAAgB,eAAe,EAAE;;;;;;;;;wBCtJjC,SAAS,OAAO,IAAI,WAAW,WAAW;;wBA8B1C,WAAW;;;;;;;;wBAcX,cACd,uBACA,cACA,OAAO;;;;;;;;;;wBCvBO,kBACd,gBACA,cACA,OAAO"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/scanner.ts","../src/infer.ts","../src/schemas.ts","../src/emit-types.ts","../src/emit-guards.ts"],"mappings":";;UAMiB;;EAEf,eAAe,YAAY;;EAE3B;;EAEA;;EAEA;;;;;;;;wBA8BoB,WACpB,mBACA,6BACC,QAAQ;;KC9CC;;;;;;wBAOI,SAAS,aAAa;;UAQrB;;EAEf;;EAEA,OAAO,IAAI;;EAEX,WAAW;;;UAII;;EAEf;;EAEA,QAAQ,YAAY;;;UAOL;;EAEf;;;;;;;;wBASc,WACd,SAAS,2BACT,UAAS,eACR;;qBC0FU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkBA;;;;;;wBAOG,gBAAgB,eAAe,EAAE;;;;;;;wBCtJjC,SAAS,OAAO,IAAI,WAAW,WAAW;;wBA8B1C,WAAW;;;;;;;;wBAcX,cACd,uBACA,cACA,OAAO;;;;;;;;wBCvBO,kBACd,gBACA,cACA,OAAO"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { z } from "zod";
2
- //#region src/scanner.d.ts
3
2
  /** スキャン結果。 */
4
3
  interface ScanResult {
5
4
  /** type 文字列 → その type の生 JSON オブジェクト配列。 */
@@ -18,8 +17,6 @@ interface ScanResult {
18
17
  * @param maxSamplesPerType - type ごとの保持上限 (メモリ保護、既定 5000)
19
18
  */
20
19
  export declare function scanCorpus(corpusDir: string, maxSamplesPerType?: number): Promise<ScanResult>;
21
- //#endregion
22
- //#region src/infer.d.ts
23
20
  /** JSON 値の種別。 */
24
21
  type JsonKind = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object';
25
22
  /**
@@ -56,8 +53,6 @@ interface InferOptions {
56
53
  * @param options - 推論オプション
57
54
  */
58
55
  export declare function inferShape(samples: Record<string, unknown>[], options?: InferOptions): Shape;
59
- //#endregion
60
- //#region src/schemas.d.ts
61
56
  /** type 文字列 → 公開 zod スキーマ。 */
62
57
  export declare const entrySchemas: {
63
58
  readonly assistant: z.ZodObject<{
@@ -285,8 +280,6 @@ export declare const KNOWN_TYPES: string[];
285
280
  * @param type - 判別子
286
281
  */
287
282
  export declare function knownTypeSchema(type: string): z.ZodType | undefined;
288
- //#endregion
289
- //#region src/emit-types.d.ts
290
283
  /**
291
284
  * 観測種別集合 (と任意のリテラル集合) を TypeScript 型文字列に写像する。
292
285
  *
@@ -304,8 +297,6 @@ export declare function pascalCase(type: string): string;
304
297
  * @param shape - 推論結果
305
298
  */
306
299
  export declare function emitInterface(interfaceName: string, type: string, shape: Shape): string;
307
- //#endregion
308
- //#region src/emit-guards.d.ts
309
300
  /**
310
301
  * 1 type のガード関数文字列を生成する。required フィールドのみ検査する。
311
302
  *
@@ -314,6 +305,5 @@ export declare function emitInterface(interfaceName: string, type: string, shape
314
305
  * @param shape - 推論結果
315
306
  */
316
307
  export declare function emitGuardFunction(fnName: string, type: string, shape: Shape): string;
317
- //#endregion
318
308
  export type { FieldShape, InferOptions, JsonKind, ScanResult, Shape };
319
309
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/scanner.ts","../src/infer.ts","../src/schemas.ts","../src/emit-types.ts","../src/emit-guards.ts"],"mappings":";;;UAMiB;;EAEf,eAAe,YAAY;;EAE3B;;EAEA;;EAEA;;;;;;;;wBA6BoB,WACpB,mBACA,6BACC,QAAQ;;;;KC7CC;;;;;;wBAOI,SAAS,aAAa;;UAWrB;;EAEf;;EAEA,OAAO,IAAI;;EAEX,WAAW;;;UAII;;EAEf;;EAEA,QAAQ,YAAY;;;UAOL;;EAEf;;;;;;;;wBASc,WACd,SAAS,2BACT,UAAS,eACR;;;;qBCuFU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkBA;;;;;;wBAOG,gBAAgB,eAAe,EAAE;;;;;;;;;wBCtJjC,SAAS,OAAO,IAAI,WAAW,WAAW;;wBA8B1C,WAAW;;;;;;;;wBAcX,cACd,uBACA,cACA,OAAO;;;;;;;;;;wBCvBO,kBACd,gBACA,cACA,OAAO"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/scanner.ts","../src/infer.ts","../src/schemas.ts","../src/emit-types.ts","../src/emit-guards.ts"],"mappings":";;UAMiB;;EAEf,eAAe,YAAY;;EAE3B;;EAEA;;EAEA;;;;;;;;wBA8BoB,WACpB,mBACA,6BACC,QAAQ;;KC9CC;;;;;;wBAOI,SAAS,aAAa;;UAQrB;;EAEf;;EAEA,OAAO,IAAI;;EAEX,WAAW;;;UAII;;EAEf;;EAEA,QAAQ,YAAY;;;UAOL;;EAEf;;;;;;;;wBASc,WACd,SAAS,2BACT,UAAS,eACR;;qBC0FU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkBA;;;;;;wBAOG,gBAAgB,eAAe,EAAE;;;;;;;wBCtJjC,SAAS,OAAO,IAAI,WAAW,WAAW;;wBA8B1C,WAAW;;;;;;;;wBAcX,cACd,uBACA,cACA,OAAO;;;;;;;;wBCvBO,kBACd,gBACA,cACA,OAAO"}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import { a as inferShape, i as tsTypeOf, n as emitInterface, o as jsonKind, r as pascalCase, s as scanCorpus, t as emitGuardFunction } from "./emit-guards-CSDZc-iy.js";
1
+ import { a as inferShape, i as tsTypeOf, n as emitInterface, o as jsonKind, r as pascalCase, s as scanCorpus, t as emitGuardFunction } from "./emit-guards-DPY2-6nD.js";
2
2
  import { z } from "zod";
3
- //#region src/schemas.ts
3
+ // src/schemas.ts
4
4
  const entryBase = {
5
5
  cwd: z.string(),
6
6
  entrypoint: z.string(),
@@ -206,7 +206,6 @@ const KNOWN_TYPES = Object.keys(entrySchemas);
206
206
  function knownTypeSchema(type) {
207
207
  return entrySchemas[type];
208
208
  }
209
- //#endregion
210
209
  export { KNOWN_TYPES, emitGuardFunction, emitInterface, entrySchemas, inferShape, jsonKind, knownTypeSchema, pascalCase, scanCorpus, tsTypeOf };
211
210
 
212
211
  //# sourceMappingURL=index.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-code-jsonl-generator",
3
- "version": "0.1.3-beta.9",
3
+ "version": "0.1.4-beta.1",
4
4
  "description": "Infer and generate types/schemas/guards from real Claude Code JSONL corpora",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -1 +0,0 @@
1
- {"version":3,"file":"emit-guards-CSDZc-iy.js","names":["IDENTIFIER_RE","escapeSingleQuote"],"sources":["../src/scanner.ts","../src/infer.ts","../src/emit-types.ts","../src/emit-guards.ts"],"sourcesContent":["import { readdir, stat } from 'node:fs/promises'\nimport { createReadStream } from 'node:fs'\nimport { createInterface } from 'node:readline'\nimport path from 'node:path'\n\n/** スキャン結果。 */\nexport interface ScanResult {\n /** type 文字列 → その type の生 JSON オブジェクト配列。 */\n samplesByType: Map<string, Record<string, unknown>[]>\n /** 走査した有効行数 (パース成功)。 */\n totalLines: number\n /** JSON 構文エラー行数。 */\n malformedLines: number\n /** type フィールドを持たない/文字列でない行数。 */\n typelessLines: number\n}\n\n/**\n * `.jsonl` ファイルを再帰収集する。\n *\n * @param dir - 起点ディレクトリ\n * @returns 絶対パス配列\n */\nasync function collectJsonlFiles(dir: string): Promise<string[]> {\n const out: string[] = []\n for (const entry of await readdir(dir)) {\n const full = path.join(dir, entry)\n const st = await stat(full)\n if (st.isDirectory()) {\n out.push(...(await collectJsonlFiles(full)))\n } else if (entry.endsWith('.jsonl')) {\n out.push(full)\n }\n }\n return out\n}\n\n/**\n * コーパスを走査し type ごとに生サンプルを集約する。\n *\n * @param corpusDir - コーパスのルートディレクトリ\n * @param maxSamplesPerType - type ごとの保持上限 (メモリ保護、既定 5000)\n */\nexport async function scanCorpus(\n corpusDir: string,\n maxSamplesPerType = 5000\n): Promise<ScanResult> {\n const samplesByType = new Map<string, Record<string, unknown>[]>()\n let totalLines = 0\n let malformedLines = 0\n let typelessLines = 0\n\n for (const file of await collectJsonlFiles(corpusDir)) {\n const rl = createInterface({\n input: createReadStream(file, { encoding: 'utf8' }),\n crlfDelay: Infinity,\n })\n for await (const raw of rl) {\n if (raw.trim() === '') continue\n let obj: unknown\n try {\n obj = JSON.parse(raw)\n } catch {\n malformedLines += 1\n continue\n }\n totalLines += 1\n if (\n typeof obj !== 'object' || obj === null || Array.isArray(obj) ||\n typeof (obj as Record<string, unknown>).type !== 'string'\n ) {\n typelessLines += 1\n continue\n }\n const record = obj as Record<string, unknown>\n const type = record.type as string\n const bucket = samplesByType.get(type) ?? []\n if (bucket.length < maxSamplesPerType) {\n bucket.push(record)\n samplesByType.set(type, bucket)\n }\n }\n }\n\n return { samplesByType, totalLines, malformedLines, typelessLines }\n}\n","/** JSON 値の種別。 */\nexport type JsonKind = 'string' | 'number' | 'boolean' | 'null' | 'array' | 'object'\n\n/**\n * JSON 値の種別を返す。\n *\n * @param v - 対象値\n */\nexport function jsonKind(v: unknown): JsonKind {\n if (v === null) return 'null'\n if (Array.isArray(v)) return 'array'\n const t = typeof v\n if ((['string', 'number', 'boolean'] as string[]).includes(t)) {\n return t as 'string' | 'number' | 'boolean'\n }\n return 'object'\n}\n\n/** 1 フィールドの推論結果。 */\nexport interface FieldShape {\n /** 全サンプルに存在したか。 */\n required: boolean\n /** 観測された JSON 種別集合。 */\n kinds: Set<JsonKind>\n /** 観測された文字列リテラル集合 (閾値以下のときのみ)。 */\n literals?: Set<string>\n}\n\n/** 1 type (または content ブロック) の推論結果。 */\nexport interface Shape {\n /** サンプル総数。 */\n count: number\n /** フィールド名 → 推論結果。 */\n fields: Map<string, FieldShape>\n}\n\n/** `InferOptions.maxLiterals` 省略時の既定値。 */\nconst DEFAULT_MAX_LITERALS = 20\n\n/** 推論オプション。 */\nexport interface InferOptions {\n /** 文字列リテラルを列挙として保持する上限。超えたら literals を破棄。既定は `DEFAULT_MAX_LITERALS`。 */\n maxLiterals?: number\n}\n\n/**\n * 同一 type のサンプル群からフィールド形状を推論する。\n *\n * @param samples - 生 JSON オブジェクト配列\n * @param options - 推論オプション\n */\nexport function inferShape(\n samples: Record<string, unknown>[],\n options: InferOptions = {}\n): Shape {\n const maxLiterals = options.maxLiterals ?? DEFAULT_MAX_LITERALS\n const count = samples.length\n const presence = new Map<string, number>()\n const kinds = new Map<string, Set<JsonKind>>()\n const literals = new Map<string, Set<string> | null>()\n\n for (const sample of samples) {\n for (const [key, value] of Object.entries(sample)) {\n presence.set(key, (presence.get(key) ?? 0) + 1)\n\n const kset = kinds.get(key) ?? new Set<JsonKind>()\n const kind = jsonKind(value)\n kset.add(kind)\n kinds.set(key, kset)\n\n // 文字列リテラル収集 (null は「破棄済み」マーカー)\n const cur = literals.get(key)\n if (cur === null) {\n // すでに破棄済み\n } else if (kind === 'string') {\n const set = cur ?? new Set<string>()\n set.add(value as string)\n if (set.size > maxLiterals) {\n literals.set(key, null)\n } else {\n literals.set(key, set)\n }\n }\n }\n }\n\n const fields = new Map<string, FieldShape>()\n for (const [key, seen] of presence) {\n const lit = literals.get(key)\n const kindSet = kinds.get(key) ?? new Set<JsonKind>()\n // 全て文字列だった場合のみ literals を採用する\n const allString = kindSet.size === 1 && kindSet.has('string')\n fields.set(key, {\n required: seen === count,\n kinds: kindSet,\n literals: allString && lit ? lit : undefined,\n })\n }\n\n return { count, fields }\n}\n","import type { JsonKind, Shape } from './infer'\n\n/** JS 識別子として安全な名前かどうかを判定する正規表現。 */\nconst IDENTIFIER_RE = /^[A-Za-z_$][\\w$]*$/\n\n/** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */\nfunction escapeSingleQuote(s: string): string {\n return s.replaceAll('\\'', String.raw`\\'`)\n}\n\n/**\n * 観測種別集合 (と任意のリテラル集合) を TypeScript 型文字列に写像する。\n *\n * @param kinds - 観測された JSON 種別\n * @param literals - 文字列リテラル集合 (あれば)\n */\nexport function tsTypeOf(kinds: Set<JsonKind>, literals?: Set<string>): string {\n const parts: string[] = []\n const mapped: Record<JsonKind, string> = {\n string: 'string',\n number: 'number',\n boolean: 'boolean',\n null: 'null',\n array: 'unknown[]',\n object: 'Record<string, unknown>',\n }\n // 文字列リテラルが与えられていれば開いた union にする\n if (literals && literals.size > 0 && kinds.has('string')) {\n const lits = [...literals].toSorted().map((s) => `'${escapeSingleQuote(s)}'`)\n parts.push(`${lits.join(' | ')} | (string & {})`)\n } else if (kinds.has('string')) {\n parts.push('string')\n }\n for (const k of ['number', 'boolean', 'array', 'object', 'null'] as JsonKind[]) {\n if (kinds.has(k)) parts.push(mapped[k])\n }\n // 重複除去して結合\n return [...new Set(parts)].join(' | ')\n}\n\n/** JS 識別子として安全ならそのまま、そうでなければクォートする。 */\nfunction propKey(name: string): string {\n return IDENTIFIER_RE.test(name) ? name : `'${escapeSingleQuote(name)}'`\n}\n\n/** interface 名として使うため type 文字列を PascalCase 化する (エミッタ内部用)。 */\nexport function pascalCase(type: string): string {\n return type\n .split(/[-_]/)\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join('')\n}\n\n/**\n * 1 type の interface 文字列を生成する。\n *\n * @param interfaceName - 出力する interface 名 (例 LastPromptEntry)\n * @param type - 判別子文字列 (例 last-prompt)\n * @param shape - 推論結果\n */\nexport function emitInterface(\n interfaceName: string,\n type: string,\n shape: Shape\n): string {\n const lines: string[] = [\n `export interface ${interfaceName} {`,\n ` _kind: 'known'`,\n ` type: '${escapeSingleQuote(type)}'`,\n ]\n for (const [name, field] of shape.fields) {\n if (name === 'type') continue\n const opt = field.required ? '' : '?'\n const tsType = tsTypeOf(field.kinds, field.literals)\n lines.push(` ${propKey(name)}${opt}: ${tsType}`)\n }\n lines.push('}')\n return lines.join('\\n')\n}\n","import type { JsonKind, Shape } from './infer'\n\n/** JS 識別子として安全な名前かどうかを判定する正規表現。 */\nconst IDENTIFIER_RE = /^[A-Za-z_$][\\w$]*$/\n\n/** シングルクォート文字列リテラルとして安全になるよう `'` をエスケープする。 */\nfunction escapeSingleQuote(s: string): string {\n return s.replaceAll('\\'', String.raw`\\'`)\n}\n\n/** 単一種別の required フィールドに対する検査式を返す。 */\nfunction checkExpr(name: string, kinds: Set<JsonKind>): string | undefined {\n const access = `v.${IDENTIFIER_RE.test(name) ? name : `['${escapeSingleQuote(name)}']`}`\n // object|null → isObject or null 許容\n const hasNull = kinds.has('null')\n const nonNull = [...kinds].filter((k) => k !== 'null')\n if (nonNull.length !== 1) {\n // 複数種別が混在する場合は存在確認のみ (過剰厳格を避ける)\n return `'${escapeSingleQuote(name)}' in v`\n }\n const kind = nonNull[0]\n const base: Partial<Record<JsonKind, string>> = {\n string: `isString(${access})`,\n number: `isNumber(${access})`,\n boolean: `isBoolean(${access})`,\n object: `isObject(${access})`,\n array: `Array.isArray(${access})`,\n }\n const expr = base[kind]\n if (expr === undefined) return `'${escapeSingleQuote(name)}' in v`\n return hasNull ? `(${access} === null || ${expr})` : expr\n}\n\n/**\n * 1 type のガード関数文字列を生成する。required フィールドのみ検査する。\n *\n * @param fnName - 関数名 (例 isMode)\n * @param type - 判別子\n * @param shape - 推論結果\n */\nexport function emitGuardFunction(\n fnName: string,\n type: string,\n shape: Shape\n): string {\n const checks: string[] = [\n `isObject(v)`,\n `v.type === '${escapeSingleQuote(type)}'`,\n ]\n for (const [name, field] of shape.fields) {\n if (name === 'type' || !field.required) continue\n const expr = checkExpr(name, field.kinds)\n if (expr) checks.push(expr)\n }\n return [\n `function ${fnName}(v: unknown): boolean {`,\n ` return ${checks.join(' &&\\n ')}`,\n `}`,\n ].join('\\n')\n}\n"],"mappings":"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"}