@miyaoka/fsss 0.1.0

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,4 @@
1
+ declare function deriveEnvName(prefix: string, commandPath: string[], argName: string): string;
2
+ declare function deriveConfigPath(commandPath: string[], argName: string): string;
3
+ export { deriveConfigPath, deriveEnvName };
4
+ //# sourceMappingURL=auto-mapping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-mapping.d.ts","sourceRoot":"","sources":["../src/auto-mapping.ts"],"names":[],"mappings":"AAIA,iBAAS,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGrF;AAMD,iBAAS,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAGxE;AAED,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,15 @@
1
+ interface AutoEnvConfig {
2
+ prefix: string;
3
+ }
4
+ interface CLIOptions {
5
+ name: string;
6
+ commandsDir?: string;
7
+ autoEnv?: AutoEnvConfig;
8
+ }
9
+ interface CLI {
10
+ run: () => Promise<void>;
11
+ }
12
+ declare function createCLI(options: CLIOptions): CLI;
13
+ export { createCLI };
14
+ export type { CLI, CLIOptions };
15
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAYA,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,UAAU,UAAU;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,aAAa,CAAC;CACzB;AAED,UAAU,GAAG;IACX,GAAG,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAgFD,iBAAS,SAAS,CAAC,OAAO,EAAE,UAAU,GAAG,GAAG,CA2F3C;AAED,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ declare function getByDotPath(obj: Record<string, unknown>, path: string): unknown;
2
+ declare function loadConfig(filePath: string): Promise<Record<string, unknown> | undefined>;
3
+ declare function deepMerge(base: Record<string, unknown>, override: Record<string, unknown>): Record<string, unknown>;
4
+ declare function resolveConfigPaths(appName: string, cliConfigPath: string | undefined): string[];
5
+ declare function loadMergedConfig(appName: string, cliConfigPath: string | undefined): Promise<Record<string, unknown> | undefined>;
6
+ export { deepMerge, getByDotPath, loadConfig, loadMergedConfig, resolveConfigPaths };
7
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AASA,iBAAS,YAAY,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAYzE;AAGD,iBAAe,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAOxF;AAGD,iBAAS,SAAS,CAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAazB;AAGD,iBAAS,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,EAAE,CAexF;AAGD,iBAAe,gBAAgB,CAC7B,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC,CAiB9C;AAED,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,CAAC"}
package/dist/help.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { AvailableEntry } from './router';
2
+ import { ArgsDefs } from './types';
3
+ interface HelpConfig {
4
+ programName: string;
5
+ commandPath: string[];
6
+ description?: string;
7
+ argsDefs?: ArgsDefs;
8
+ envPrefix?: string;
9
+ }
10
+ declare function generateHelp(config: HelpConfig): string;
11
+ interface SubcommandHelpConfig {
12
+ programName: string;
13
+ commandPath: string[];
14
+ availableEntries: AvailableEntry[];
15
+ }
16
+ declare function generateSubcommandHelp(config: SubcommandHelpConfig): string;
17
+ declare function generateValidationErrorHelp(helpText: string, errors: string[]): string;
18
+ export { generateHelp, generateSubcommandHelp, generateValidationErrorHelp };
19
+ export type { HelpConfig, SubcommandHelpConfig };
20
+ //# sourceMappingURL=help.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../src/help.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAU,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGhD,UAAU,UAAU;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAkED,iBAAS,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA8DhD;AAGD,UAAU,oBAAoB;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,gBAAgB,EAAE,cAAc,EAAE,CAAC;CACpC;AAED,iBAAS,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAuBpE;AAID,iBAAS,2BAA2B,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAG/E;AAED,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,2BAA2B,EAAE,CAAC;AAC7E,YAAY,EAAE,UAAU,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createCLI } from './cli';
2
+ export { defineCommand } from './types';
3
+ export type { CLI, CLIOptions } from './cli';
4
+ export type { ArgDef, ArgDefBase, ArgsDefs, CommandConfig, InferArgs, Params, RunContext, } from './types';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,YAAY,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAC7C,YAAY,EACV,MAAM,EACN,UAAU,EACV,QAAQ,EACR,aAAa,EACb,SAAS,EACT,MAAM,EACN,UAAU,GACX,MAAM,SAAS,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,437 @@
1
+ import { join, relative, resolve } from "node:path";
2
+ import { ZodBoolean, ZodError, ZodNumber, z } from "zod";
3
+ import { readFile, readdir, stat } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ function isRecord(e) {
6
+ return typeof e == "object" && !!e;
7
+ }
8
+ function getByDotPath(e, t) {
9
+ let n = t.split("."), r = e;
10
+ for (let e of n) {
11
+ if (!isRecord(r)) return;
12
+ r = r[e];
13
+ }
14
+ return r;
15
+ }
16
+ async function loadConfig(e) {
17
+ try {
18
+ let t = await readFile(e, "utf-8");
19
+ return JSON.parse(t);
20
+ } catch {
21
+ return;
22
+ }
23
+ }
24
+ function deepMerge(e, t) {
25
+ let n = { ...e };
26
+ for (let [e, r] of Object.entries(t)) {
27
+ let t = n[e];
28
+ if (isRecord(t) && isRecord(r)) {
29
+ n[e] = deepMerge(t, r);
30
+ continue;
31
+ }
32
+ n[e] = r;
33
+ }
34
+ return n;
35
+ }
36
+ function resolveConfigPaths(t, n) {
37
+ let r = [];
38
+ return r.push(join(homedir(), ".config", t, "config.json")), r.push(join(process.cwd(), `${t}.config.json`)), n !== void 0 && r.push(n), r;
39
+ }
40
+ async function loadMergedConfig(e, t) {
41
+ let n = resolveConfigPaths(e, t), r;
42
+ for (let e of n) {
43
+ let t = await loadConfig(e);
44
+ if (t !== void 0) {
45
+ if (r === void 0) {
46
+ r = t;
47
+ continue;
48
+ }
49
+ r = deepMerge(r, t);
50
+ }
51
+ }
52
+ return r;
53
+ }
54
+ function deriveEnvName(e, t, n) {
55
+ return [
56
+ e,
57
+ ...t,
58
+ n
59
+ ].join("_").toUpperCase();
60
+ }
61
+ function deriveConfigPath(e, t) {
62
+ return [...e, t].join(".");
63
+ }
64
+ function isBooleanSchema(e) {
65
+ return e instanceof ZodBoolean;
66
+ }
67
+ function isNumberSchema(e) {
68
+ return e instanceof ZodNumber;
69
+ }
70
+ var INDENT = " ", COLUMN_GAP = 2;
71
+ function resolveEnvNameForHelp(e, t, n, r) {
72
+ if (e.env !== void 0) return e.env;
73
+ if (t !== void 0) return deriveEnvName(t, n, r);
74
+ }
75
+ function formatOptionMeta(e, t) {
76
+ let n = [];
77
+ return t !== void 0 && n.push(`env: ${t}`), "default" in e && e.default !== void 0 && n.push(`default: ${String(e.default)}`), n.length === 0 ? "" : ` (${n.join(", ")})`;
78
+ }
79
+ function formatOptionLine(e, t, n, r) {
80
+ let i = isBooleanSchema(t.type) ? "" : ` <${e}>`, a = `${t.alias === void 0 ? " " : `-${t.alias}, `}--${e}${i}`, o = resolveEnvNameForHelp(t, n, r, e);
81
+ return {
82
+ left: a,
83
+ right: `${t.description}${formatOptionMeta(t, o)}`
84
+ };
85
+ }
86
+ function formatHelpLine() {
87
+ return {
88
+ left: "-h, --help",
89
+ right: "ヘルプを表示する"
90
+ };
91
+ }
92
+ function generateHelp(e) {
93
+ let { programName: t, commandPath: n, description: r, argsDefs: i, envPrefix: a } = e, o = [];
94
+ r !== void 0 && (o.push(r), o.push(""));
95
+ let s = [t, ...n];
96
+ if (i !== void 0) {
97
+ let e = Object.entries(i), t = e.some(([, e]) => e.positional === !0), n = e.some(([, e]) => e.positional !== !0);
98
+ if (t) {
99
+ let t = e.filter(([, e]) => e.positional === !0).map(([e]) => `<${e}>`);
100
+ s.push(...t);
101
+ }
102
+ n && s.push("[options]");
103
+ }
104
+ if (o.push(`Usage: ${s.join(" ")}`), o.push(""), i === void 0) return o.join("\n");
105
+ let c = Object.entries(i).filter(([, e]) => e.positional !== !0);
106
+ if (c.length === 0) {
107
+ o.push("Options:");
108
+ let e = formatHelpLine();
109
+ return o.push(`${INDENT}${e.left} ${e.right}`), o.join("\n");
110
+ }
111
+ o.push("Options:");
112
+ let l = c.map(([e, t]) => formatOptionLine(e, t, a, n));
113
+ l.push(formatHelpLine());
114
+ let u = Math.max(...l.map((e) => e.left.length));
115
+ for (let e of l) {
116
+ let t = " ".repeat(u - e.left.length + COLUMN_GAP);
117
+ o.push(`${INDENT}${e.left}${t}${e.right}`);
118
+ }
119
+ return o.join("\n");
120
+ }
121
+ function generateSubcommandHelp(e) {
122
+ let { programName: t, commandPath: n, availableEntries: r } = e, i = [], a = [t, ...n].join(" ");
123
+ if (i.push(`Usage: ${a} <command>`), i.push(""), r.length === 0) return i.push("No available commands."), i.join("\n");
124
+ i.push("Available commands:");
125
+ for (let e of r) {
126
+ if (e.isDynamic) {
127
+ i.push(`${INDENT}<${e.paramName}>`);
128
+ continue;
129
+ }
130
+ i.push(`${INDENT}${e.name}`);
131
+ }
132
+ return i.join("\n");
133
+ }
134
+ function generateValidationErrorHelp(e, t) {
135
+ return [
136
+ `Error: ${t.join(", ")}`,
137
+ "",
138
+ e
139
+ ].join("\n");
140
+ }
141
+ var LONG_FLAG_PREFIX = "--", SHORT_FLAG_PREFIX = "-", DOUBLE_DASH = "--", NEGATION_PREFIX = "--no-", BOOLEAN_TRUE = "true", BOOLEAN_FALSE = "false";
142
+ function addFlag(e, t, n) {
143
+ let r = e.get(t);
144
+ if (r) {
145
+ r.push(n);
146
+ return;
147
+ }
148
+ e.set(t, [n]);
149
+ }
150
+ function isNextTokenAvailable(e, t) {
151
+ return t + 1 >= e.length ? !1 : !e[t + 1].startsWith(SHORT_FLAG_PREFIX);
152
+ }
153
+ function parseTokens(e, t) {
154
+ let n = /* @__PURE__ */ new Map(), r = [], i = [], a = 0;
155
+ for (; a < e.length;) {
156
+ let o = e[a];
157
+ if (o === DOUBLE_DASH) {
158
+ i = e.slice(a + 1);
159
+ break;
160
+ }
161
+ if (o.startsWith(NEGATION_PREFIX)) {
162
+ addFlag(n, o.slice(5), BOOLEAN_FALSE), a++;
163
+ continue;
164
+ }
165
+ if (o.startsWith(LONG_FLAG_PREFIX) && o.includes("=")) {
166
+ let e = o.indexOf("=");
167
+ addFlag(n, o.slice(2, e), o.slice(e + 1)), a++;
168
+ continue;
169
+ }
170
+ if (o.startsWith(LONG_FLAG_PREFIX)) {
171
+ let r = o.slice(2);
172
+ if (t.booleanFlags.has(r)) {
173
+ addFlag(n, r, BOOLEAN_TRUE), a++;
174
+ continue;
175
+ }
176
+ if (isNextTokenAvailable(e, a)) {
177
+ addFlag(n, r, e[a + 1]), a += 2;
178
+ continue;
179
+ }
180
+ throw Error(`Flag --${r} requires a value`);
181
+ }
182
+ if (o.startsWith(SHORT_FLAG_PREFIX) && !o.startsWith(LONG_FLAG_PREFIX) && o.includes("=")) {
183
+ let e = o.indexOf("="), r = o.slice(1, e), i = o.slice(e + 1);
184
+ addFlag(n, t.aliases.get(r) ?? r, i), a++;
185
+ continue;
186
+ }
187
+ if (o.startsWith(SHORT_FLAG_PREFIX) && !o.startsWith(LONG_FLAG_PREFIX)) {
188
+ let r = o.slice(1), i = t.aliases.get(r) ?? r;
189
+ if (t.booleanFlags.has(i)) {
190
+ addFlag(n, i, BOOLEAN_TRUE), a++;
191
+ continue;
192
+ }
193
+ if (isNextTokenAvailable(e, a)) {
194
+ addFlag(n, i, e[a + 1]), a += 2;
195
+ continue;
196
+ }
197
+ throw Error(`Flag -${r} requires a value`);
198
+ }
199
+ r.push(o), a++;
200
+ }
201
+ return {
202
+ flags: n,
203
+ positionals: r,
204
+ doubleDashArgs: i
205
+ };
206
+ }
207
+ function resolveEnvName(e, t, n, r) {
208
+ if (e !== void 0) return e;
209
+ if (t !== void 0) return deriveEnvName(t, n, r);
210
+ }
211
+ function resolveValues(e) {
212
+ let { argsDefs: t, parsedTokens: n, env: r, config: i, commandPath: a, envPrefix: o } = e, s = {}, c = 0;
213
+ for (let [e, l] of Object.entries(t)) {
214
+ let t = n.flags.get(e);
215
+ if (t !== void 0) {
216
+ if (l.multiple === !0) {
217
+ s[e] = t;
218
+ continue;
219
+ }
220
+ s[e] = t[t.length - 1];
221
+ continue;
222
+ }
223
+ if (l.positional === !0 && c < n.positionals.length) {
224
+ s[e] = n.positionals[c], c++;
225
+ continue;
226
+ }
227
+ let u = resolveEnvName(l.env, o, a, e);
228
+ if (u !== void 0) {
229
+ let t = r[u];
230
+ if (t !== void 0) {
231
+ s[e] = t;
232
+ continue;
233
+ }
234
+ }
235
+ if (i !== void 0) {
236
+ let t = getByDotPath(i, l.config ?? deriveConfigPath(a, e));
237
+ if (t !== void 0) {
238
+ s[e] = t;
239
+ continue;
240
+ }
241
+ }
242
+ if ("default" in l) {
243
+ s[e] = l.default;
244
+ continue;
245
+ }
246
+ }
247
+ return s;
248
+ }
249
+ var DYNAMIC_SEGMENT_PATTERN = /^\[(.+)]$/, COMMAND_FILE_EXTENSION = ".ts", INDEX_FILE_NAME = "index";
250
+ async function fileExists(e) {
251
+ try {
252
+ return (await stat(e)).isFile();
253
+ } catch {
254
+ return !1;
255
+ }
256
+ }
257
+ async function listAvailableEntries(e) {
258
+ try {
259
+ let t = await readdir(e, { withFileTypes: !0 }), n = [];
260
+ for (let e of t) {
261
+ if (e.isFile() && e.name.endsWith(COMMAND_FILE_EXTENSION)) {
262
+ let t = e.name.slice(0, -3);
263
+ if (t === INDEX_FILE_NAME) continue;
264
+ n.push({
265
+ name: t,
266
+ isDynamic: !1
267
+ });
268
+ continue;
269
+ }
270
+ if (e.isDirectory()) {
271
+ let t = e.name.match(DYNAMIC_SEGMENT_PATTERN);
272
+ if (t !== null) {
273
+ n.push({
274
+ name: e.name,
275
+ isDynamic: !0,
276
+ paramName: t[1]
277
+ });
278
+ continue;
279
+ }
280
+ n.push({
281
+ name: e.name,
282
+ isDynamic: !1
283
+ });
284
+ }
285
+ }
286
+ return n;
287
+ } catch {
288
+ return [];
289
+ }
290
+ }
291
+ async function resolveRoute(t, r) {
292
+ let i = resolve(t), a = {}, o = 0;
293
+ for (let t = 0; t < r.length; t++) {
294
+ let n = r[t];
295
+ if (n.startsWith("-")) break;
296
+ let s = await readdir(i, { withFileTypes: !0 }), l = n + COMMAND_FILE_EXTENSION, u = s.find((e) => e.isFile() && e.name === l);
297
+ if (u) return o = t + 1, {
298
+ kind: "resolved",
299
+ filePath: join(i, u.name),
300
+ params: a,
301
+ remainingTokens: r.slice(o)
302
+ };
303
+ let d = s.find((e) => e.isDirectory() && e.name === n);
304
+ if (d) {
305
+ i = join(i, d.name), o = t + 1;
306
+ continue;
307
+ }
308
+ let f, p = s.find((e) => {
309
+ if (!e.isDirectory()) return !1;
310
+ let t = e.name.match(DYNAMIC_SEGMENT_PATTERN);
311
+ return t === null ? !1 : (f = t[1], !0);
312
+ });
313
+ if (p && f !== void 0) {
314
+ a[f] = n, i = join(i, p.name), o = t + 1;
315
+ continue;
316
+ }
317
+ break;
318
+ }
319
+ let s = join(i, INDEX_FILE_NAME + COMMAND_FILE_EXTENSION);
320
+ if (await fileExists(s)) return {
321
+ kind: "resolved",
322
+ filePath: s,
323
+ params: a,
324
+ remainingTokens: r.slice(o)
325
+ };
326
+ let l = await listAvailableEntries(i);
327
+ return {
328
+ kind: "unresolved",
329
+ stoppedDir: i,
330
+ availableEntries: l
331
+ };
332
+ }
333
+ function wrapWithStringPreprocess(e) {
334
+ return isBooleanSchema(e) ? z.preprocess((e) => typeof e == "string" ? e === "true" || e === "1" : e, e) : isNumberSchema(e) ? z.preprocess((e) => typeof e == "string" ? Number(e) : e, e) : e;
335
+ }
336
+ function validateArgs(e, t) {
337
+ let n = {};
338
+ for (let [t, r] of Object.entries(e)) {
339
+ let e = wrapWithStringPreprocess(r.type);
340
+ if (r.multiple === !0) {
341
+ n[t] = z.array(e);
342
+ continue;
343
+ }
344
+ n[t] = e;
345
+ }
346
+ return z.object(n).parse(t);
347
+ }
348
+ var DEFAULT_COMMANDS_DIR = "commands", ARGV_SKIP = 2, EXIT_CODE_ERROR = 1;
349
+ function buildParserConfig(e) {
350
+ let t = /* @__PURE__ */ new Set(), n = /* @__PURE__ */ new Map();
351
+ for (let [r, i] of Object.entries(e)) isBooleanSchema(i.type) && t.add(r), i.alias !== void 0 && n.set(i.alias, r);
352
+ return {
353
+ booleanFlags: t,
354
+ aliases: n
355
+ };
356
+ }
357
+ function extractCommandPath(e, r) {
358
+ return relative(resolve(e), r).replace(/\.ts$/, "").split("/").filter((e) => e !== "index" && !e.startsWith("["));
359
+ }
360
+ function extractPartialCommandPath(e, r) {
361
+ let i = relative(resolve(e), r);
362
+ return i === "" || i === "." ? [] : i.split("/").filter((e) => !e.startsWith("["));
363
+ }
364
+ function extractFrameworkFlags(e) {
365
+ let t, n = [];
366
+ for (let r = 0; r < e.length; r++) {
367
+ let i = e[r];
368
+ if (i.startsWith("--config=")) {
369
+ t = i.slice(9);
370
+ continue;
371
+ }
372
+ if (i === "--config" || i === "-c") {
373
+ let n = e[r + 1];
374
+ if (n === void 0) throw Error("--config requires a file path");
375
+ t = n, r++;
376
+ continue;
377
+ }
378
+ n.push(i);
379
+ }
380
+ return {
381
+ configPath: t,
382
+ remainingTokens: n
383
+ };
384
+ }
385
+ function createCLI(e) {
386
+ let t = e.commandsDir ?? DEFAULT_COMMANDS_DIR, n = e.name, r = e.autoEnv?.prefix;
387
+ async function a() {
388
+ let { configPath: e, remainingTokens: a } = extractFrameworkFlags(process.argv.slice(ARGV_SKIP)), o = await resolveRoute(t, a);
389
+ if (o.kind === "unresolved") {
390
+ let e = generateSubcommandHelp({
391
+ programName: n,
392
+ commandPath: extractPartialCommandPath(t, o.stoppedDir),
393
+ availableEntries: o.availableEntries
394
+ });
395
+ console.log(e);
396
+ return;
397
+ }
398
+ let s = (await import(o.filePath)).default, c = s.args ?? {}, l = extractCommandPath(t, o.filePath), u = generateHelp({
399
+ programName: n,
400
+ commandPath: l,
401
+ description: s.description,
402
+ argsDefs: Object.keys(c).length > 0 ? c : void 0,
403
+ envPrefix: r
404
+ });
405
+ if (o.remainingTokens.includes("--help") || o.remainingTokens.includes("-h")) {
406
+ console.log(u);
407
+ return;
408
+ }
409
+ let d = buildParserConfig(c), f;
410
+ try {
411
+ let t = parseTokens(o.remainingTokens, d), i = await loadMergedConfig(n, e);
412
+ f = validateArgs(c, resolveValues({
413
+ argsDefs: c,
414
+ parsedTokens: t,
415
+ env: process.env,
416
+ config: i,
417
+ commandPath: l,
418
+ envPrefix: r
419
+ }));
420
+ } catch (e) {
421
+ if (e instanceof ZodError) {
422
+ let t = e.issues.map((e) => `${e.path.join(".")}: ${e.message}`);
423
+ console.error(generateValidationErrorHelp(u, t)), process.exit(EXIT_CODE_ERROR);
424
+ }
425
+ throw e instanceof Error && (console.error(generateValidationErrorHelp(u, [e.message])), process.exit(EXIT_CODE_ERROR)), e;
426
+ }
427
+ await s.run({
428
+ params: o.params,
429
+ args: f
430
+ });
431
+ }
432
+ return { run: a };
433
+ }
434
+ function defineCommand(e) {
435
+ return e;
436
+ }
437
+ export { createCLI, defineCommand };
@@ -0,0 +1,13 @@
1
+ interface ParsedTokens {
2
+ flags: Map<string, string[]>;
3
+ positionals: string[];
4
+ doubleDashArgs: string[];
5
+ }
6
+ interface ParserConfig {
7
+ booleanFlags: Set<string>;
8
+ aliases: Map<string, string>;
9
+ }
10
+ declare function parseTokens(tokens: string[], config: ParserConfig): ParsedTokens;
11
+ export { parseTokens };
12
+ export type { ParsedTokens, ParserConfig };
13
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,UAAU,YAAY;IACpB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7B,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AAED,UAAU,YAAY;IACpB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC1B,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AA0BD,iBAAS,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,YAAY,GAAG,YAAY,CA6FzE;AAED,OAAO,EAAE,WAAW,EAAE,CAAC;AACvB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,14 @@
1
+ import { ParsedTokens } from './parser';
2
+ import { ArgsDefs } from './types';
3
+ interface ResolverInput {
4
+ argsDefs: ArgsDefs;
5
+ parsedTokens: ParsedTokens;
6
+ env: Record<string, string | undefined>;
7
+ config: Record<string, unknown> | undefined;
8
+ commandPath: string[];
9
+ envPrefix: string | undefined;
10
+ }
11
+ declare function resolveValues(input: ResolverInput): Record<string, unknown>;
12
+ export { resolveValues };
13
+ export type { ResolverInput };
14
+ //# sourceMappingURL=resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../src/resolver.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,UAAU,aAAa;IACrB,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC5C,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B;AAmBD,iBAAS,aAAa,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CA0DpE;AAED,OAAO,EAAE,aAAa,EAAE,CAAC;AACzB,YAAY,EAAE,aAAa,EAAE,CAAC"}
@@ -0,0 +1,21 @@
1
+ interface RouteResolved {
2
+ kind: "resolved";
3
+ filePath: string;
4
+ params: Record<string, string>;
5
+ remainingTokens: string[];
6
+ }
7
+ interface RouteUnresolved {
8
+ kind: "unresolved";
9
+ stoppedDir: string;
10
+ availableEntries: AvailableEntry[];
11
+ }
12
+ interface AvailableEntry {
13
+ name: string;
14
+ isDynamic: boolean;
15
+ paramName?: string;
16
+ }
17
+ type RouteResult = RouteResolved | RouteUnresolved;
18
+ declare function resolveRoute(commandsDir: string, tokens: string[]): Promise<RouteResult>;
19
+ export { resolveRoute };
20
+ export type { AvailableEntry, RouteResolved, RouteResult, RouteUnresolved };
21
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAOA,UAAU,aAAa;IACrB,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,YAAY,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,cAAc,EAAE,CAAC;CACpC;AAED,UAAU,cAAc;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,KAAK,WAAW,GAAG,aAAa,GAAG,eAAe,CAAC;AAgDnD,iBAAe,YAAY,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CA8EvF;AAED,OAAO,EAAE,YAAY,EAAE,CAAC;AACxB,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,eAAe,EAAE,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { z, ZodType } from 'zod';
2
+ interface ArgDefBase<T extends ZodType> {
3
+ type: T;
4
+ description: string;
5
+ alias?: string;
6
+ positional?: boolean;
7
+ default?: z.output<T>;
8
+ env?: string;
9
+ config?: string;
10
+ multiple?: boolean;
11
+ }
12
+ type ArgDef = ArgDefBase<ZodType>;
13
+ type ArgsDefs = Record<string, ArgDef>;
14
+ type InferArgs<T extends ArgsDefs> = {
15
+ [K in keyof T]: T[K]["multiple"] extends true ? z.output<T[K]["type"]>[] : z.output<T[K]["type"]>;
16
+ };
17
+ type Params = Record<string, string>;
18
+ interface RunContext<T extends ArgsDefs> {
19
+ params: Params;
20
+ args: InferArgs<T>;
21
+ }
22
+ interface CommandConfig<T extends ArgsDefs = ArgsDefs> {
23
+ description?: string;
24
+ args?: T;
25
+ run: (context: RunContext<T>) => void | Promise<void>;
26
+ }
27
+ declare function defineCommand<T extends ArgsDefs>(config: CommandConfig<T>): CommandConfig<T>;
28
+ export { defineCommand };
29
+ export type { ArgDef, ArgDefBase, ArgsDefs, CommandConfig, InferArgs, Params, RunContext };
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAItC,UAAU,UAAU,CAAC,CAAC,SAAS,OAAO;IACpC,IAAI,EAAE,CAAC,CAAC;IACR,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAGD,KAAK,MAAM,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;AAGlC,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAIvC,KAAK,SAAS,CAAC,CAAC,SAAS,QAAQ,IAAI;KAClC,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,SAAS,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;CAClG,CAAC;AAMF,KAAK,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;AAIrC,UAAU,UAAU,CAAC,CAAC,SAAS,QAAQ;IACrC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;CACpB;AAED,UAAU,aAAa,CAAC,CAAC,SAAS,QAAQ,GAAG,QAAQ;IACnD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,GAAG,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvD;AAOD,iBAAS,aAAa,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAErF;AAED,OAAO,EAAE,aAAa,EAAE,CAAC;AACzB,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { ArgsDefs } from './types';
2
+ declare function validateArgs(argsDefs: ArgsDefs, rawValues: Record<string, unknown>): Record<string, unknown>;
3
+ export { validateArgs };
4
+ //# sourceMappingURL=validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../src/validator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AA+BxC,iBAAS,YAAY,CACnB,QAAQ,EAAE,QAAQ,EAClB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAgBzB;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { ZodType } from 'zod';
2
+ declare function isBooleanSchema(schema: ZodType): boolean;
3
+ declare function isNumberSchema(schema: ZodType): boolean;
4
+ export { isBooleanSchema, isNumberSchema };
5
+ //# sourceMappingURL=zod-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"zod-utils.d.ts","sourceRoot":"","sources":["../src/zod-utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,KAAK,CAAC;AAM1D,iBAAS,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEjD;AAMD,iBAAS,cAAc,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAEhD;AAED,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,CAAC"}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@miyaoka/fsss",
3
+ "version": "0.1.0",
4
+ "description": "A CLI framework where your file structure becomes your command structure, and a single schema gives you typed values whether they come from flags, env vars, or config files.",
5
+ "type": "module",
6
+ "author": "miyaoka",
7
+ "license": "MIT",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/miyaoka/fsss.git"
11
+ },
12
+ "bugs": "https://github.com/miyaoka/fsss/issues",
13
+ "keywords": [
14
+ "cli",
15
+ "framework",
16
+ "file-based-routing",
17
+ "zod",
18
+ "bun",
19
+ "typescript"
20
+ ],
21
+ "main": "dist/index.js",
22
+ "types": "dist/index.d.ts",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/index.d.ts",
26
+ "import": "./dist/index.js"
27
+ }
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "scripts": {
33
+ "prepublishOnly": "vite build",
34
+ "build": "vite build",
35
+ "dev": "vite build --watch",
36
+ "test": "bun test",
37
+ "typecheck": "tsgo --noEmit",
38
+ "lint": "oxlint --type-aware .",
39
+ "fix": "oxlint --type-aware --fix . && oxfmt ."
40
+ },
41
+ "dependencies": {
42
+ "zod": "catalog:"
43
+ },
44
+ "devDependencies": {
45
+ "@tsconfig/node24": "catalog:",
46
+ "@types/bun": "catalog:",
47
+ "oxfmt": "catalog:",
48
+ "oxlint": "catalog:",
49
+ "oxlint-tsgolint": "catalog:",
50
+ "typescript": "catalog:",
51
+ "unplugin-dts": "catalog:",
52
+ "vite": "catalog:"
53
+ }
54
+ }