@nudojs/service 4.0.0 → 5.0.0-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/README.md +17 -7
- package/dist/analysis-_DlXLC-L.d.ts +281 -0
- package/dist/analysis.d.ts +8 -0
- package/dist/analysis.js +61 -0
- package/dist/analyzer-types-CfBrPK9L.d.ts +158 -0
- package/dist/call-record-CkyCFkT9.d.ts +33 -0
- package/dist/chunk-EG6FYQLI.js +89 -0
- package/dist/chunk-J2PUE4NV.js +235 -0
- package/dist/chunk-NQXAF4LJ.js +3739 -0
- package/dist/{chunk-ADFCZP72.js → chunk-ZXVAX7DE.js} +274 -15
- package/dist/{config-Cqj8zeZH.d.ts → config-DDB588oA.d.ts} +45 -48
- package/dist/emit/index.d.ts +607 -0
- package/dist/emit/index.js +3747 -0
- package/dist/env-loader-fj0TSCcA.d.ts +20 -0
- package/dist/evaluator/evaluator-api.d.ts +3 -1
- package/dist/evaluator/evaluator-api.js +7 -1
- package/dist/index.d.ts +291 -1197
- package/dist/index.js +232 -7554
- package/dist/what-if-mIo7I5Ii.d.ts +21 -0
- package/package.json +17 -9
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// src/what-if.ts
|
|
2
|
+
import { parse } from "@nudojs/parser";
|
|
3
|
+
function splitTopLevelUnion(expr) {
|
|
4
|
+
const members = [];
|
|
5
|
+
let start = 0;
|
|
6
|
+
let depth = 0;
|
|
7
|
+
for (let i = 0; i < expr.length; i++) {
|
|
8
|
+
const c = expr[i];
|
|
9
|
+
if (c === "(" || c === "[" || c === "{") depth++;
|
|
10
|
+
else if (c === ")" || c === "]" || c === "}") depth--;
|
|
11
|
+
else if (c === "|" && depth === 0) {
|
|
12
|
+
members.push(expr.slice(start, i));
|
|
13
|
+
start = i + 1;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
members.push(expr.slice(start));
|
|
17
|
+
return members.map((m) => m.trim()).filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
function typeExprToDirective(expr) {
|
|
20
|
+
const members = splitTopLevelUnion(expr);
|
|
21
|
+
if (members.length === 0) return "any()";
|
|
22
|
+
const mapped = members.map((m) => {
|
|
23
|
+
if (m.startsWith("T.")) return "any()";
|
|
24
|
+
if (m === "number" || m === "string" || m === "boolean") return `${m}()`;
|
|
25
|
+
if (m === "unknown" || m === "any") return "any()";
|
|
26
|
+
if (m === "null" || m === "undefined" || m === "true" || m === "false") return m;
|
|
27
|
+
if (/^-?\d+(\.\d+)?$/.test(m)) return m;
|
|
28
|
+
if (/^["']/.test(m)) return m;
|
|
29
|
+
if (/[([{]|=>/.test(m) && !m.startsWith("T.")) return m;
|
|
30
|
+
if (/^(number|string|boolean|any|array|shape|lit|union|fn)\s*\(/.test(m)) return m;
|
|
31
|
+
return "any()";
|
|
32
|
+
});
|
|
33
|
+
return mapped.length === 1 ? mapped[0] : `union(${mapped.join(", ")})`;
|
|
34
|
+
}
|
|
35
|
+
function declaredNames(stmt, out) {
|
|
36
|
+
if (stmt.type === "FunctionDeclaration" && stmt.id) out.add(stmt.id.name);
|
|
37
|
+
else if (stmt.type === "ClassDeclaration" && stmt.id) out.add(stmt.id.name);
|
|
38
|
+
else if (stmt.type === "VariableDeclaration") {
|
|
39
|
+
for (const decl of stmt.declarations) {
|
|
40
|
+
if (decl.id?.type === "Identifier") out.add(decl.id.name);
|
|
41
|
+
}
|
|
42
|
+
} else if (stmt.type === "ExportNamedDeclaration" && stmt.declaration) {
|
|
43
|
+
declaredNames(stmt.declaration, out);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function injectBindings(source, bindings) {
|
|
47
|
+
const applied = [];
|
|
48
|
+
const unapplied = [];
|
|
49
|
+
if (bindings.length === 0) return { source, applied, unapplied };
|
|
50
|
+
const ast = parse(source);
|
|
51
|
+
const declLines = /* @__PURE__ */ new Map();
|
|
52
|
+
for (const stmt of ast.program.body) {
|
|
53
|
+
const names = /* @__PURE__ */ new Set();
|
|
54
|
+
declaredNames(stmt, names);
|
|
55
|
+
if (names.size === 0 || !stmt.loc) continue;
|
|
56
|
+
const anchorLine = stmt.leadingComments?.[0]?.loc?.start.line ?? stmt.loc.start.line;
|
|
57
|
+
for (const name of names) {
|
|
58
|
+
if (!declLines.has(name)) declLines.set(name, anchorLine);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const byLine = /* @__PURE__ */ new Map();
|
|
62
|
+
for (const binding of bindings) {
|
|
63
|
+
const line = declLines.get(binding.name);
|
|
64
|
+
if (line === void 0) {
|
|
65
|
+
unapplied.push(binding.name);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
const group = byLine.get(line) ?? [];
|
|
69
|
+
group.push(binding);
|
|
70
|
+
byLine.set(line, group);
|
|
71
|
+
applied.push(binding.name);
|
|
72
|
+
}
|
|
73
|
+
const insertions = [];
|
|
74
|
+
for (const [line, group] of byLine) {
|
|
75
|
+
insertions.push({
|
|
76
|
+
index: line - 1,
|
|
77
|
+
text: `// @nudo:as ${typeExprToDirective(group[0].type)}`
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
insertions.sort((a, b) => b.index - a.index);
|
|
81
|
+
const lines = source.split("\n");
|
|
82
|
+
for (const ins of insertions) lines.splice(ins.index, 0, ins.text);
|
|
83
|
+
return { source: lines.join("\n"), applied, unapplied };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// src/dep-contents.ts
|
|
87
|
+
import { loadModuleDepsFingerprint } from "@nudojs/core/internal";
|
|
88
|
+
function collectLoadDepContents(filePath, source, loadModule) {
|
|
89
|
+
const fp = loadModuleDepsFingerprint(source, loadModule, filePath);
|
|
90
|
+
const depContents = fp.contents.length > 0 ? fp.contents : fp.paths.map((path) => ({ path, content: null }));
|
|
91
|
+
return { depContents, truncated: fp.truncated };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// src/disk-cache.ts
|
|
95
|
+
import { createHash } from "crypto";
|
|
96
|
+
import { createRequire } from "module";
|
|
97
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync, rmSync } from "fs";
|
|
98
|
+
import { join, dirname, relative, sep, isAbsolute } from "path";
|
|
99
|
+
function readServiceVersion() {
|
|
100
|
+
try {
|
|
101
|
+
const require2 = createRequire(import.meta.url);
|
|
102
|
+
for (const p of ["../package.json", "./package.json", "../../package.json"]) {
|
|
103
|
+
try {
|
|
104
|
+
const pkg = require2(p);
|
|
105
|
+
if (pkg?.name === "@nudojs/service" && pkg.version) return pkg.version;
|
|
106
|
+
if (pkg?.version && p.includes("service")) return pkg.version;
|
|
107
|
+
} catch {
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
} catch {
|
|
111
|
+
}
|
|
112
|
+
return "0";
|
|
113
|
+
}
|
|
114
|
+
var ANALYSIS_ABI = `nudo-check-cache-v3+${readServiceVersion()}`;
|
|
115
|
+
function sha256Hex(data) {
|
|
116
|
+
return createHash("sha256").update(data).digest("hex");
|
|
117
|
+
}
|
|
118
|
+
function relativizePath(p, root) {
|
|
119
|
+
const norm = p.split(sep).join("/");
|
|
120
|
+
if (!root) return norm;
|
|
121
|
+
const r = relative(root, p).split(sep).join("/");
|
|
122
|
+
if (!r.startsWith("..") && !isAbsolute(r)) return r;
|
|
123
|
+
return `ext:${createHash("sha256").update(norm).digest("hex").slice(0, 16)}`;
|
|
124
|
+
}
|
|
125
|
+
function sanitizeCacheNamespace(ns) {
|
|
126
|
+
return ns.replace(/[^a-z0-9_-]/gi, "_").replace(/^_+|_+$/g, "") || "cache";
|
|
127
|
+
}
|
|
128
|
+
var DiskCache = class {
|
|
129
|
+
root;
|
|
130
|
+
ns;
|
|
131
|
+
enabled = false;
|
|
132
|
+
constructor(opts) {
|
|
133
|
+
this.root = opts.root;
|
|
134
|
+
this.ns = sanitizeCacheNamespace(opts.namespace);
|
|
135
|
+
this.enabled = !!opts.root;
|
|
136
|
+
}
|
|
137
|
+
pathFor(key) {
|
|
138
|
+
if (!/^[a-f0-9]{16,128}$/i.test(key)) {
|
|
139
|
+
throw new Error("DiskCache key must be a hex digest");
|
|
140
|
+
}
|
|
141
|
+
return join(this.root, this.ns, key.slice(0, 2), `${key}.json`);
|
|
142
|
+
}
|
|
143
|
+
get(key) {
|
|
144
|
+
if (!this.enabled || !this.root) return void 0;
|
|
145
|
+
try {
|
|
146
|
+
const p = this.pathFor(key);
|
|
147
|
+
if (!existsSync(p)) return void 0;
|
|
148
|
+
const raw = readFileSync(p, "utf8");
|
|
149
|
+
const parsed = JSON.parse(raw);
|
|
150
|
+
if (parsed?.abi !== ANALYSIS_ABI) return void 0;
|
|
151
|
+
return parsed.value;
|
|
152
|
+
} catch {
|
|
153
|
+
return void 0;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
set(key, value) {
|
|
157
|
+
if (!this.enabled || !this.root) return;
|
|
158
|
+
try {
|
|
159
|
+
const p = this.pathFor(key);
|
|
160
|
+
mkdirSync(dirname(p), { recursive: true });
|
|
161
|
+
writeFileSync(p, JSON.stringify({ abi: ANALYSIS_ABI, value }), "utf8");
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
clearNamespace() {
|
|
166
|
+
if (!this.enabled || !this.root) return;
|
|
167
|
+
try {
|
|
168
|
+
rmSync(join(this.root, this.ns), { recursive: true, force: true });
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
function checkCacheKey(filePath, source, opts) {
|
|
174
|
+
const rel = relativizePath(filePath, opts.projectDir);
|
|
175
|
+
const sidecarSha = opts.sidecarContent != null ? sha256Hex(opts.sidecarContent) : "nosidecar";
|
|
176
|
+
const depSeg = (opts.depContents ?? []).map(
|
|
177
|
+
(d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
|
|
178
|
+
).join("\n");
|
|
179
|
+
const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
|
|
180
|
+
const cfgSeg = opts.analysisCfg ? `${opts.analysisCfg.mode ?? "-"}|${opts.analysisCfg.evalMissingSlot ?? "-"}|${opts.analysisCfg.callSiteBudget ?? "-"}|${opts.analysisCfg.entryThrows ?? "-"}|${opts.analysisCfg.ignoreThrows ?? "-"}` : "-";
|
|
181
|
+
return sha256Hex(
|
|
182
|
+
[
|
|
183
|
+
ANALYSIS_ABI,
|
|
184
|
+
rel,
|
|
185
|
+
opts.autoBind ? "ab1" : "ab0",
|
|
186
|
+
sha256Hex(source),
|
|
187
|
+
sidecarSha,
|
|
188
|
+
depSeg,
|
|
189
|
+
envSeg,
|
|
190
|
+
cfgSeg
|
|
191
|
+
].join("\0")
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
function ifaceCacheKey(filePath, source, opts) {
|
|
195
|
+
const rel = relativizePath(filePath, opts.projectDir);
|
|
196
|
+
const sidecarSeg = opts.autoBind && opts.sidecarSource !== void 0 ? `sc:${sha256Hex(opts.sidecarSource)}` : "sc0";
|
|
197
|
+
const depSeg = (opts.depContents ?? []).map(
|
|
198
|
+
(d) => `${relativizePath(d.path, opts.projectDir)}\0${d.content != null ? sha256Hex(d.content) : "miss"}`
|
|
199
|
+
).join("\n");
|
|
200
|
+
const envSeg = (opts.projectEnvNames ?? []).length > 0 ? [...opts.projectEnvNames ?? []].sort().join(",") : "-";
|
|
201
|
+
return sha256Hex(
|
|
202
|
+
[
|
|
203
|
+
ANALYSIS_ABI,
|
|
204
|
+
"iface",
|
|
205
|
+
rel,
|
|
206
|
+
opts.autoBind ? "ab1" : "ab0",
|
|
207
|
+
sha256Hex(source),
|
|
208
|
+
sidecarSeg,
|
|
209
|
+
depSeg,
|
|
210
|
+
envSeg
|
|
211
|
+
].join("\0")
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
function extractNudoImportSpecs(source) {
|
|
215
|
+
const specs = /* @__PURE__ */ new Set();
|
|
216
|
+
const named = /@nudo:import\s*\{[^}]*\}\s*from\s*["']([^"']+)["']/g;
|
|
217
|
+
const ns = /@nudo:import\s+\*\s+as\s+\w+\s+from\s*["']([^"']+)["']/g;
|
|
218
|
+
let m;
|
|
219
|
+
while (m = named.exec(source)) specs.add(m[1]);
|
|
220
|
+
while (m = ns.exec(source)) specs.add(m[1]);
|
|
221
|
+
return [...specs];
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export {
|
|
225
|
+
typeExprToDirective,
|
|
226
|
+
injectBindings,
|
|
227
|
+
collectLoadDepContents,
|
|
228
|
+
ANALYSIS_ABI,
|
|
229
|
+
sha256Hex,
|
|
230
|
+
relativizePath,
|
|
231
|
+
DiskCache,
|
|
232
|
+
checkCacheKey,
|
|
233
|
+
ifaceCacheKey,
|
|
234
|
+
extractNudoImportSpecs
|
|
235
|
+
};
|