@quatronic/sdk 0.6.1-rc.4 → 0.7.0-rc.6

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.
Files changed (40) hide show
  1. package/README.md +26 -0
  2. package/dist/cli/legacy/qdc-sourcemaps.js +237 -0
  3. package/dist/cli/legacy/qdc-test-report.js +200 -0
  4. package/dist/cli/skills-postinstall.js +603 -0
  5. package/dist/cli/skills.js +499 -0
  6. package/dist/cli/sourcemaps.js +9 -9
  7. package/dist/cli/test-report.js +2 -2
  8. package/dist/next/config/index.js +1 -1
  9. package/dist/next/config/index.js.map +1 -1
  10. package/package.json +10 -4
  11. package/postinstall.mjs +9 -0
  12. package/skills/.gitattributes +1 -0
  13. package/skills/catalog.json +24 -0
  14. package/skills/qore-add-api-route/SKILL.md +58 -0
  15. package/skills/qore-add-connection/SKILL.next.md +149 -0
  16. package/skills/qore-add-connection/SKILL.node.md +160 -0
  17. package/skills/qore-add-db-table/SKILL.md +88 -0
  18. package/skills/qore-add-db-table/references/cross-module-reference.md +74 -0
  19. package/skills/qore-add-feature/SKILL.md +77 -0
  20. package/skills/qore-add-integration/SKILL.next.md +217 -0
  21. package/skills/qore-add-integration/SKILL.node.md +216 -0
  22. package/skills/qore-add-page/SKILL.md +53 -0
  23. package/skills/qore-author-skill/SKILL.md +136 -0
  24. package/skills/qore-debug-issue/SKILL.md +48 -0
  25. package/skills/qore-enable-auth-method/SKILL.md +90 -0
  26. package/skills/qore-observability/SKILL.md +89 -0
  27. package/skills/qore-observability/references/errors.md +87 -0
  28. package/skills/qore-observability/references/logs.md +69 -0
  29. package/skills/qore-observability/references/tracing.md +58 -0
  30. package/skills/qore-recommended-tools/SKILL.md +58 -0
  31. package/skills/qore-secure-io/SKILL.md +174 -0
  32. package/skills/qore-ship-checklist/SKILL.md +80 -0
  33. package/skills/qore-translate-strings/SKILL.md +36 -0
  34. package/skills/qore-use-addons/SKILL.md +175 -0
  35. package/skills/qore-use-database/SKILL.md +222 -0
  36. package/skills/qore-use-design-system/SKILL.md +76 -0
  37. package/skills/qore-use-mail-domain/SKILL.next.md +67 -0
  38. package/skills/qore-use-mail-domain/SKILL.node.md +62 -0
  39. package/skills/qore-use-permissions/SKILL.md +116 -0
  40. package/skills/qore-verify-user/SKILL.md +56 -0
package/README.md CHANGED
@@ -23,6 +23,32 @@ app runs against its own proxy, and `@quatronic/sdk/eslint` for the fourteen
23
23
  `qdc/*` lint rules —
24
24
  [`docs/security-rules.md`](docs/security-rules.md).
25
25
 
26
+ ## Claude Code skills
27
+
28
+ The SDK ships the app's managed Claude Code skills (`skills/`, one entry per
29
+ skill in `skills/catalog.json`), and `qore-skills sync` writes them into the
30
+ app's `.claude/skills/qore-*`, with `.claude/qore/manifest.json` recording the
31
+ SDK version and a hash per file. The app commits the result.
32
+
33
+ Installing or bumping the SDK runs the sync by itself. The package's
34
+ `postinstall` syncs when the manifest records a different SDK version, and
35
+ prints what it changed so it lands in the same commit as the bump. It never
36
+ fails an install, and it skips CI, `QORE_SKILLS_SYNC=0`, a directory that is not
37
+ a git checkout, and any bump where a file it would overwrite holds an
38
+ uncommitted hand edit. pnpm 11 runs a dependency's script only when the app
39
+ allows it, so each app's `pnpm-workspace.yaml` needs:
40
+
41
+ ```yaml
42
+ allowBuilds:
43
+ "@quatronic/sdk": true
44
+ ```
45
+
46
+ Without it, pnpm 11 fails the install with `ERR_PNPM_IGNORED_BUILDS`. pnpm only
47
+ runs the script when the installed version changes, so after a skipped sync run
48
+ `pnpm exec qore-skills sync` yourself; `qore-skills check` exits 1 while the
49
+ skills are behind. How a team extends, replaces or turns off a managed skill is
50
+ in the app's `.claude/qore/README.md` and the `/qore-author-skill` skill.
51
+
26
52
  ## Permissions
27
53
 
28
54
  The SDK ships a hybrid role/permission layer. Roles come from the Keycloak JWT;
@@ -0,0 +1,237 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/legacy/notice.ts
4
+ import { basename } from "path";
5
+ function renamedNotice(oldName, newName) {
6
+ return `\u26A0 ${oldName} is renamed to ${newName}; the old name keeps working until the next major release.`;
7
+ }
8
+ var invoked = basename(process.argv[1] ?? "", ".js");
9
+ if (invoked.startsWith("qdc-")) console.warn(renamedNotice(invoked, `qore-${invoked.slice(4)}`));
10
+
11
+ // src/cli/sourcemaps.ts
12
+ import { execFile } from "child_process";
13
+ import { copyFileSync, existsSync, readdirSync, readFileSync, realpathSync, rmSync, writeFileSync } from "fs";
14
+ import { tmpdir } from "os";
15
+ import { join } from "path";
16
+ import { fileURLToPath } from "url";
17
+ import { promisify } from "util";
18
+
19
+ // src/cli/sourcemaps-core.ts
20
+ import { posix } from "path";
21
+ var SHA = /^[0-9a-f]{40}$/;
22
+ var BUNDLE = /^[a-z0-9][a-z0-9-]{0,31}$/;
23
+ var SCRIPT = /\.(js|mjs|cjs)$/;
24
+ var MAPPING_URL = /^\/\/# sourceMappingURL=(\S+)[ \t]*$/gm;
25
+ var STANDALONE_SERVER = "standalone/.next/server/";
26
+ function parseArgs(argv) {
27
+ const [cmd, dir, ...rest] = argv;
28
+ if (cmd !== "upload" || !dir) return { error: "usage: qore-sourcemaps upload <dir> [--bundle <name>]" };
29
+ let bundle = "app";
30
+ if (rest[0] === "--bundle") bundle = rest[1] ?? "";
31
+ if (!BUNDLE.test(bundle)) return { error: `invalid bundle name: ${bundle}` };
32
+ return { dir, bundle };
33
+ }
34
+ function nextRoots(has) {
35
+ if (!has("BUILD_ID") || !has("server")) return null;
36
+ return ["static", "server"].filter(has);
37
+ }
38
+ function underNodeModules(p) {
39
+ return p.split("/").includes("node_modules");
40
+ }
41
+ function inRoots(p, roots) {
42
+ if (underNodeModules(p)) return false;
43
+ if (roots) return roots.some((r) => p.startsWith(`${r}/`));
44
+ return !p.startsWith("standalone/");
45
+ }
46
+ function lastMappingUrl(src) {
47
+ return [...src.matchAll(MAPPING_URL)].at(-1);
48
+ }
49
+ function fixMappingUrl(src) {
50
+ const m = lastMappingUrl(src);
51
+ if (!m?.[1] || !/%(5b|5d|40)/i.test(m[1])) return null;
52
+ const fixed = m[1].replace(/%5b/gi, "[").replace(/%5d/gi, "]").replace(/%40/g, "@");
53
+ const at = m.index ?? 0;
54
+ return src.slice(0, at) + `//# sourceMappingURL=${fixed}` + src.slice(at + m[0].length);
55
+ }
56
+ function mapFor(js, src) {
57
+ const url = lastMappingUrl(src)?.[1];
58
+ if (!url || url.startsWith("data:") || /^[a-z]+:/i.test(url)) return null;
59
+ let decoded = url;
60
+ try {
61
+ decoded = decodeURIComponent(url);
62
+ } catch {
63
+ }
64
+ const target = posix.normalize(posix.join(posix.dirname(js), decoded));
65
+ return target.startsWith("../") || posix.isAbsolute(target) ? null : target;
66
+ }
67
+ function listMaps(paths) {
68
+ return paths.filter((p) => p.endsWith(".map") && !underNodeModules(p));
69
+ }
70
+ function hasDebugId(text) {
71
+ try {
72
+ const map = JSON.parse(text);
73
+ const id = map?.debugId ?? map?.debug_id;
74
+ return typeof id === "string" && id.length > 0;
75
+ } catch {
76
+ return false;
77
+ }
78
+ }
79
+ function listArtifacts(dir, walk, readText, roots) {
80
+ const all = walk(dir);
81
+ const set = new Set(all);
82
+ const used = /* @__PURE__ */ new Set();
83
+ const files = [];
84
+ let dropped = 0;
85
+ for (const js of all) {
86
+ if (!SCRIPT.test(js) || !inRoots(js, roots)) continue;
87
+ const map = mapFor(js, readText(js));
88
+ if (!map) continue;
89
+ if (!map.endsWith(".map") || !set.has(map) || !inRoots(map, roots) || used.has(map) || !hasDebugId(readText(map))) {
90
+ dropped++;
91
+ continue;
92
+ }
93
+ used.add(map);
94
+ files.push(js, map);
95
+ }
96
+ return { maps: listMaps(all), files, dropped };
97
+ }
98
+ function standaloneTwins(paths) {
99
+ const set = new Set(paths);
100
+ return paths.filter((p) => p.startsWith("server/") && SCRIPT.test(p)).map((p) => [p, `${STANDALONE_SERVER}${p.slice("server/".length)}`]).filter(([, twin]) => set.has(twin));
101
+ }
102
+ function uploadUrl(base, sha, bundle) {
103
+ return `${base.replace(/\/+$/, "")}/${sha}/${bundle}`;
104
+ }
105
+ function skipReason(env) {
106
+ for (const k of ["QORE_BUILD_UPLOAD_URL", "QORE_BUILD_UPLOAD_TOKEN", "NF_GIT_SHA"]) {
107
+ if (!env[k]) return `${k} is not set`;
108
+ }
109
+ if (!SHA.test(env.NF_GIT_SHA ?? "")) return "NF_GIT_SHA is not a commit sha";
110
+ return null;
111
+ }
112
+
113
+ // src/cli/sourcemaps.ts
114
+ var execFileP = promisify(execFile);
115
+ var UPLOAD_TIMEOUT_MS = 5 * 6e4;
116
+ var RUN_TIMEOUT_MS = 12e4;
117
+ var SCRIPT2 = /\.(js|mjs|cjs)$/;
118
+ async function runCommand(cmd, args) {
119
+ await execFileP(cmd, args, {
120
+ timeout: RUN_TIMEOUT_MS,
121
+ maxBuffer: 16 * 1024 * 1024,
122
+ env: { ...process.env, COPYFILE_DISABLE: "1" }
123
+ });
124
+ }
125
+ function walkDir(dir, prefix = "") {
126
+ const out = [];
127
+ for (const d of readdirSync(join(dir, prefix), { withFileTypes: true })) {
128
+ const rel = prefix ? `${prefix}/${d.name}` : d.name;
129
+ if (d.isDirectory() && d.name !== "node_modules") out.push(...walkDir(dir, rel));
130
+ else if (d.isFile()) out.push(rel);
131
+ }
132
+ return out;
133
+ }
134
+ var defaultIo = () => ({
135
+ env: process.env,
136
+ walk: (dir) => walkDir(dir),
137
+ exists: existsSync,
138
+ readText: (p) => readFileSync(p, "utf8"),
139
+ writeText: (p, t) => writeFileSync(p, t),
140
+ copyFile: copyFileSync,
141
+ run: runCommand,
142
+ readFile: (p) => readFileSync(p),
143
+ remove: (p) => rmSync(p, { force: true }),
144
+ fetch,
145
+ tmpFile: () => join(tmpdir(), `qdc-maps-${process.pid}.tgz`)
146
+ });
147
+ function describe(err, token) {
148
+ const msg = (err instanceof Error ? err.message : String(err)).slice(0, 500);
149
+ return token ? msg.split(token).join("***") : msg;
150
+ }
151
+ function tryRemove(io, path) {
152
+ try {
153
+ io.remove(path);
154
+ } catch {
155
+ }
156
+ }
157
+ async function main(argv = process.argv.slice(2), io = defaultIo()) {
158
+ const args = parseArgs(argv);
159
+ if ("error" in args) {
160
+ console.warn(`qore-sourcemaps: ${args.error}`);
161
+ const dir2 = argv[0] === "upload" ? argv[1] : void 0;
162
+ if (dir2 && !dir2.startsWith("-")) {
163
+ try {
164
+ for (const m of listMaps(io.walk(dir2))) tryRemove(io, join(dir2, m));
165
+ } catch (err) {
166
+ console.warn(`qore-sourcemaps: cleanup skipped: ${describe(err, io.env.QORE_BUILD_UPLOAD_TOKEN)}`);
167
+ }
168
+ }
169
+ return;
170
+ }
171
+ const { dir, bundle } = args;
172
+ const at = (rel) => join(dir, rel);
173
+ let maps = [];
174
+ const scratch = [];
175
+ try {
176
+ const paths = io.walk(dir);
177
+ maps = listMaps(paths);
178
+ const skip = skipReason(io.env);
179
+ if (skip) {
180
+ console.log(`qore-sourcemaps: skipped (${skip})`);
181
+ return;
182
+ }
183
+ const roots = nextRoots((rel) => io.exists(at(rel)));
184
+ if (roots) {
185
+ for (const p of paths) {
186
+ if (!SCRIPT2.test(p) || !roots.some((r) => p.startsWith(`${r}/`))) continue;
187
+ const fixed = fixMappingUrl(io.readText(at(p)));
188
+ if (fixed !== null) io.writeText(at(p), fixed);
189
+ }
190
+ await io.run("sentry-cli", ["sourcemaps", "inject", ...roots.map(at)]);
191
+ for (const [src, twin] of standaloneTwins(paths)) io.copyFile(at(src), at(twin));
192
+ } else {
193
+ await io.run("sentry-cli", ["sourcemaps", "inject", dir]);
194
+ }
195
+ const { files, dropped } = listArtifacts(dir, () => paths, (rel) => io.readText(at(rel)), roots);
196
+ if (dropped > 0) console.log(`qore-sourcemaps: dropped ${dropped} source maps the uploader would reject`);
197
+ if (files.length === 0) {
198
+ console.log("qore-sourcemaps: no source maps found, nothing to upload");
199
+ return;
200
+ }
201
+ const tgz = io.tmpFile();
202
+ const list = `${tgz}.list`;
203
+ scratch.push(list, tgz);
204
+ io.writeText(list, `${files.join("\n")}
205
+ `);
206
+ await io.run("tar", ["-czf", tgz, "-C", dir, "-T", list]);
207
+ const body = io.readFile(tgz);
208
+ const res = await io.fetch(uploadUrl(io.env.QORE_BUILD_UPLOAD_URL, io.env.NF_GIT_SHA, bundle), {
209
+ method: "PUT",
210
+ headers: {
211
+ Authorization: `Bearer ${io.env.QORE_BUILD_UPLOAD_TOKEN}`,
212
+ "Content-Type": "application/gzip",
213
+ "Content-Length": String(body.byteLength)
214
+ },
215
+ body: new Uint8Array(body),
216
+ signal: AbortSignal.timeout(UPLOAD_TIMEOUT_MS)
217
+ });
218
+ if (res.ok) console.log(`qore-sourcemaps: handed off ${files.length / 2} source maps (${body.byteLength} bytes)`);
219
+ else console.warn(`qore-sourcemaps: upload refused, broker answered ${res.status}`);
220
+ } catch (err) {
221
+ console.warn(`qore-sourcemaps: upload skipped: ${describe(err, io.env.QORE_BUILD_UPLOAD_TOKEN)}`);
222
+ } finally {
223
+ for (const m of maps) tryRemove(io, at(m));
224
+ for (const s of scratch) tryRemove(io, s);
225
+ }
226
+ }
227
+ function isMainModule() {
228
+ if (!process.argv[1]) return false;
229
+ try {
230
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
231
+ } catch {
232
+ return false;
233
+ }
234
+ }
235
+ if (isMainModule()) {
236
+ void main();
237
+ }
@@ -0,0 +1,200 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/cli/legacy/notice.ts
4
+ import { basename } from "path";
5
+ function renamedNotice(oldName, newName) {
6
+ return `\u26A0 ${oldName} is renamed to ${newName}; the old name keeps working until the next major release.`;
7
+ }
8
+ var invoked = basename(process.argv[1] ?? "", ".js");
9
+ if (invoked.startsWith("qdc-")) console.warn(renamedNotice(invoked, `qore-${invoked.slice(4)}`));
10
+
11
+ // src/cli/test-report.ts
12
+ import { existsSync, mkdirSync, readFileSync, realpathSync, writeFileSync } from "fs";
13
+ import { dirname, resolve } from "path";
14
+ import { fileURLToPath } from "url";
15
+
16
+ // src/cli/test-report-core.ts
17
+ var SCHEMA_VERSION = 1;
18
+ function asRecord(value) {
19
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
20
+ return value;
21
+ }
22
+ function finiteNumber(value) {
23
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
24
+ }
25
+ function nonNegative(value) {
26
+ return value !== null && value >= 0 ? value : 0;
27
+ }
28
+ function countAssertions(testResults) {
29
+ let passed = 0;
30
+ let failed = 0;
31
+ let skipped = 0;
32
+ for (const file of testResults) {
33
+ const rec = asRecord(file);
34
+ const assertions = rec?.assertionResults;
35
+ if (!Array.isArray(assertions)) continue;
36
+ for (const assertion of assertions) {
37
+ const status = asRecord(assertion)?.status;
38
+ if (status === "passed") passed += 1;
39
+ else if (status === "failed") failed += 1;
40
+ else if (status === "pending" || status === "todo" || status === "skipped") skipped += 1;
41
+ }
42
+ }
43
+ return { passed, failed, skipped };
44
+ }
45
+ function spanMs(testResults) {
46
+ let earliest = null;
47
+ let latest = null;
48
+ for (const file of testResults) {
49
+ const rec = asRecord(file);
50
+ if (!rec) continue;
51
+ const start = finiteNumber(rec.startTime);
52
+ const end = finiteNumber(rec.endTime);
53
+ if (start !== null && (earliest === null || start < earliest)) earliest = start;
54
+ if (end !== null && (latest === null || end > latest)) latest = end;
55
+ }
56
+ if (earliest === null || latest === null || latest < earliest) return 0;
57
+ return Math.round(latest - earliest);
58
+ }
59
+ function summarizeVitestJson(raw) {
60
+ const root = asRecord(raw);
61
+ if (!root) return null;
62
+ const testResults = Array.isArray(root.testResults) ? root.testResults : [];
63
+ const topPassed = finiteNumber(root.numPassedTests);
64
+ const topFailed = finiteNumber(root.numFailedTests);
65
+ const topPending = finiteNumber(root.numPendingTests);
66
+ const topTodo = finiteNumber(root.numTodoTests);
67
+ const hasTopLevelCounts = topPassed !== null || topFailed !== null;
68
+ if (!hasTopLevelCounts && testResults.length === 0) {
69
+ return null;
70
+ }
71
+ const counted = hasTopLevelCounts ? {
72
+ passed: nonNegative(topPassed),
73
+ failed: nonNegative(topFailed),
74
+ skipped: nonNegative(topPending) + nonNegative(topTodo)
75
+ } : countAssertions(testResults);
76
+ return { ...counted, durationMs: spanMs(testResults) };
77
+ }
78
+ function summarizeCoverage(raw) {
79
+ const total = asRecord(asRecord(raw)?.total);
80
+ if (!total) return null;
81
+ const lines = finiteNumber(asRecord(total.lines)?.pct);
82
+ const branches = finiteNumber(asRecord(total.branches)?.pct);
83
+ if (lines === null && branches === null) return null;
84
+ return { linesPct: lines, branchesPct: branches };
85
+ }
86
+ function buildReport(input) {
87
+ const suites = {};
88
+ if (input.unit.kind === "ok") suites.unit = input.unit.summary;
89
+ if (input.integration.kind === "ok") suites.integration = input.integration.summary;
90
+ const measured = Object.keys(suites).length > 0;
91
+ const anyParseFailure = input.unit.kind === "parse_failed" || input.integration.kind === "parse_failed";
92
+ return {
93
+ schemaVersion: SCHEMA_VERSION,
94
+ commitSha: input.commitSha,
95
+ ranAt: input.ranAt,
96
+ measured,
97
+ reason: measured ? null : anyParseFailure ? "parse_failed" : "no_results",
98
+ suites,
99
+ coverage: input.coverage
100
+ };
101
+ }
102
+ function unmeasuredReport(commitSha, ranAt, reason) {
103
+ return {
104
+ schemaVersion: SCHEMA_VERSION,
105
+ commitSha,
106
+ ranAt,
107
+ measured: false,
108
+ reason,
109
+ suites: {},
110
+ coverage: null
111
+ };
112
+ }
113
+
114
+ // src/cli/test-report.ts
115
+ var DEFAULT_OUT = ".qdc/test-report.json";
116
+ function parseArgs(argv) {
117
+ const args = argv.slice();
118
+ const opts = {
119
+ unit: null,
120
+ integration: null,
121
+ coverage: null,
122
+ commitSha: null,
123
+ out: DEFAULT_OUT
124
+ };
125
+ while (args.length) {
126
+ const arg = args.shift();
127
+ if (arg === "--unit") opts.unit = args.shift() ?? null;
128
+ else if (arg === "--integration") opts.integration = args.shift() ?? null;
129
+ else if (arg === "--coverage") opts.coverage = args.shift() ?? null;
130
+ else if (arg === "--commit-sha") opts.commitSha = args.shift() ?? null;
131
+ else if (arg === "--out") opts.out = args.shift() ?? opts.out;
132
+ }
133
+ return opts;
134
+ }
135
+ function readJson(path) {
136
+ if (!path) return { kind: "absent" };
137
+ const resolved = resolve(path);
138
+ if (!existsSync(resolved)) return { kind: "absent" };
139
+ try {
140
+ return { kind: "ok", value: JSON.parse(readFileSync(resolved, "utf8")) };
141
+ } catch {
142
+ return { kind: "bad" };
143
+ }
144
+ }
145
+ function readSuite(path) {
146
+ const read = readJson(path);
147
+ if (read.kind === "absent") return { kind: "absent" };
148
+ if (read.kind === "bad") return { kind: "parse_failed" };
149
+ const summary = summarizeVitestJson(read.value);
150
+ return summary ? { kind: "ok", summary } : { kind: "parse_failed" };
151
+ }
152
+ function readCoverage(path) {
153
+ const read = readJson(path);
154
+ return read.kind === "ok" ? summarizeCoverage(read.value) : null;
155
+ }
156
+ function writeReport(out, report) {
157
+ const resolved = resolve(out);
158
+ mkdirSync(dirname(resolved), { recursive: true });
159
+ writeFileSync(resolved, `${JSON.stringify(report, null, 2)}
160
+ `);
161
+ }
162
+ function main(argv = process.argv.slice(2)) {
163
+ let out = DEFAULT_OUT;
164
+ let commitSha = null;
165
+ const ranAt = (/* @__PURE__ */ new Date()).toISOString();
166
+ try {
167
+ const opts = parseArgs(argv);
168
+ out = opts.out;
169
+ commitSha = opts.commitSha;
170
+ const report = buildReport({
171
+ commitSha: opts.commitSha,
172
+ ranAt,
173
+ unit: readSuite(opts.unit),
174
+ integration: readSuite(opts.integration),
175
+ coverage: readCoverage(opts.coverage)
176
+ });
177
+ writeReport(out, report);
178
+ const summary = report.measured ? Object.entries(report.suites).map(([name, s]) => `${name}: ${s.passed} passed, ${s.failed} failed, ${s.skipped} skipped`).join("; ") : `unmeasured (${report.reason})`;
179
+ console.log(`qore-test-report \u2192 ${out} \u2014 ${summary}`);
180
+ } catch (err) {
181
+ console.warn(
182
+ `qore-test-report could not build a report: ${err instanceof Error ? err.message : String(err)}`
183
+ );
184
+ try {
185
+ writeReport(out, unmeasuredReport(commitSha, ranAt, "parse_failed"));
186
+ } catch {
187
+ }
188
+ }
189
+ }
190
+ function isMainModule() {
191
+ if (!process.argv[1]) return false;
192
+ try {
193
+ return realpathSync(fileURLToPath(import.meta.url)) === realpathSync(process.argv[1]);
194
+ } catch {
195
+ return false;
196
+ }
197
+ }
198
+ if (isMainModule()) {
199
+ main();
200
+ }