@fairfox/polly 0.23.0 → 0.24.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.
- package/README.md +55 -1
- package/dist/src/elysia/index.js +2 -2
- package/dist/src/elysia/index.js.map +2 -2
- package/dist/src/elysia/peer-repo-plugin.d.ts +1 -1
- package/dist/src/mesh-node.d.ts +89 -0
- package/dist/src/mesh-node.js +594 -0
- package/dist/src/mesh-node.js.map +14 -0
- package/dist/src/mesh.d.ts +10 -0
- package/dist/src/mesh.js +926 -24
- package/dist/src/mesh.js.map +17 -9
- package/dist/src/peer.d.ts +1 -0
- package/dist/src/peer.js +105 -84
- package/dist/src/peer.js.map +11 -10
- package/dist/src/shared/lib/blob-cache.d.ts +58 -0
- package/dist/src/shared/lib/blob-store-impl.d.ts +33 -0
- package/dist/src/shared/lib/blob-store.d.ts +87 -0
- package/dist/src/shared/lib/blob-transfer.d.ts +58 -0
- package/dist/src/shared/lib/crdt-specialised.d.ts +1 -1
- package/dist/src/shared/lib/crdt-state.d.ts +1 -1
- package/dist/src/shared/lib/keyring-storage.d.ts +57 -0
- package/dist/src/shared/lib/mesh-client.d.ts +91 -0
- package/dist/src/shared/lib/mesh-network-adapter.d.ts +1 -1
- package/dist/src/shared/lib/mesh-signaling-client.d.ts +6 -0
- package/dist/src/shared/lib/mesh-state.d.ts +1 -1
- package/dist/src/shared/lib/mesh-webrtc-adapter.d.ts +20 -1
- package/dist/src/shared/lib/peer-relay-adapter.d.ts +1 -1
- package/dist/src/shared/lib/peer-repo-server.d.ts +1 -1
- package/dist/src/shared/lib/peer-state.d.ts +1 -1
- package/dist/src/shared/lib/wasm-init.d.ts +17 -0
- package/dist/tools/quality/src/cli.js +8 -1
- package/dist/tools/quality/src/cli.js.map +3 -3
- package/dist/tools/quality/src/index.d.ts +25 -0
- package/dist/tools/quality/src/index.js +196 -0
- package/dist/tools/quality/src/index.js.map +10 -0
- package/dist/tools/quality/src/no-as-casting.d.ts +44 -0
- package/package.json +22 -2
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
18
|
+
|
|
19
|
+
// tools/quality/src/no-as-casting.ts
|
|
20
|
+
import { readFileSync } from "node:fs";
|
|
21
|
+
import { Glob } from "bun";
|
|
22
|
+
function isLineClean(line) {
|
|
23
|
+
if (!line.includes(" as "))
|
|
24
|
+
return true;
|
|
25
|
+
const trimmed = line.trim();
|
|
26
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("*") || trimmed.startsWith("/*")) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (line.match(/\bas const\b/)) {
|
|
30
|
+
const withoutAsConst = line.replace(/\bas const\b/g, "");
|
|
31
|
+
if (!withoutAsConst.includes(" as "))
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
if (line.includes(" as unknown as ") || line.trimEnd().endsWith("as unknown as")) {
|
|
35
|
+
const withoutEscapeHatch = line.replace(/\bas unknown as\b/g, "");
|
|
36
|
+
if (!withoutEscapeHatch.includes(" as "))
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
if (line.match(/\b(import|export)\s+.*\s+as\s+\w+/) || line.match(/\b(import|export)\s+\*\s+as\s+\w+/) || line.match(/\b(import|export)\s+type\s+.*\s+as\s+\w+/) || line.match(/^\s*\w+\s+as\s+\w+,?\s*$/) || line.match(/^\s*type\s+\w+\s+as\s+\w+,?\s*$/)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if (line.match(/\bas\s*[=:,]/))
|
|
43
|
+
return true;
|
|
44
|
+
if (everyAsInsideString(line))
|
|
45
|
+
return true;
|
|
46
|
+
if (isJsxText(trimmed))
|
|
47
|
+
return true;
|
|
48
|
+
if (isPlainText(trimmed))
|
|
49
|
+
return true;
|
|
50
|
+
const commentIdx = line.indexOf("//");
|
|
51
|
+
if (commentIdx >= 0 && line.indexOf(" as ", commentIdx) >= 0) {
|
|
52
|
+
const beforeComment = line.substring(0, commentIdx);
|
|
53
|
+
if (!beforeComment.includes(" as "))
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
if (line.match(/"\)\s+as\s+\w+"/))
|
|
57
|
+
return true;
|
|
58
|
+
if (line.includes(" satisfies "))
|
|
59
|
+
return true;
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
function everyAsInsideString(line) {
|
|
63
|
+
let searchFrom = 0;
|
|
64
|
+
while (true) {
|
|
65
|
+
const idx = line.indexOf(" as ", searchFrom);
|
|
66
|
+
if (idx < 0)
|
|
67
|
+
return true;
|
|
68
|
+
const before = line.substring(0, idx);
|
|
69
|
+
const singleQuotes = (before.match(/'/g) ?? []).length;
|
|
70
|
+
const doubleQuotes = (before.match(/"/g) ?? []).length;
|
|
71
|
+
const backticks = (before.match(/`/g) ?? []).length;
|
|
72
|
+
if (singleQuotes % 2 === 0 && doubleQuotes % 2 === 0 && backticks % 2 === 0) {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
searchFrom = idx + 4;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
function isJsxText(trimmed) {
|
|
79
|
+
if (trimmed.match(/^[^{};=()]*\bas\b[^{};=()]*$/)) {
|
|
80
|
+
if (!trimmed.match(/\bas\s+[A-Z]\w*/) && !trimmed.match(/\bas\s+(string|number|boolean|any|unknown|never)\b/)) {
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
function isPlainText(trimmed) {
|
|
87
|
+
const idx = trimmed.indexOf(" as ");
|
|
88
|
+
if (idx < 0)
|
|
89
|
+
return false;
|
|
90
|
+
const before = trimmed.substring(0, idx);
|
|
91
|
+
return !before.match(/[={}:;(]/) && !before.match(/\b(const|let|var|type|interface|function|return|await)\b/);
|
|
92
|
+
}
|
|
93
|
+
function suggestFix(line) {
|
|
94
|
+
if (line.includes("JSON.parse")) {
|
|
95
|
+
return "Use a validation function or type guard to parse and validate the result.";
|
|
96
|
+
}
|
|
97
|
+
if (line.includes("as HTMLInputElement") || line.includes("as HTMLTextAreaElement") || line.includes("as HTMLButtonElement")) {
|
|
98
|
+
return "Use instanceof: if (el instanceof HTMLInputElement) { el.value ... }";
|
|
99
|
+
}
|
|
100
|
+
if (line.includes("as HTMLElement") || line.includes("as Element")) {
|
|
101
|
+
return "Use instanceof: if (el instanceof HTMLElement) { ... }";
|
|
102
|
+
}
|
|
103
|
+
if (line.includes(".doc()") && line.includes("as ")) {
|
|
104
|
+
return "Type the DocHandle generic: repo.find<MyType>(id) returns DocHandle<MyType>.";
|
|
105
|
+
}
|
|
106
|
+
if (line.includes("Record<string, unknown>") && (line.includes("window") || line.includes("globalThis"))) {
|
|
107
|
+
return "Extract a type guard: function getGlobalProp(name: string): unknown { ... }";
|
|
108
|
+
}
|
|
109
|
+
if (line.includes("Record<string, unknown>")) {
|
|
110
|
+
return "Use a type guard function that narrows the unknown value to the target shape.";
|
|
111
|
+
}
|
|
112
|
+
if (line.includes("as PeerId") || line.includes("as DocumentId")) {
|
|
113
|
+
return "Use the library's branded-type constructor if available, or centralise the cast in a factory.";
|
|
114
|
+
}
|
|
115
|
+
if (line.includes("as string") || line.includes("as number") || line.includes("as boolean")) {
|
|
116
|
+
return "Narrow with typeof: if (typeof x === 'string') { ... }";
|
|
117
|
+
}
|
|
118
|
+
if (line.includes("as any")) {
|
|
119
|
+
return "Replace 'any' with 'unknown' and add a type guard or validation at the boundary.";
|
|
120
|
+
}
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
function isFileExcluded(relative, excludeDirs, excludePackages, excludeFiles) {
|
|
124
|
+
const segments = relative.split("/");
|
|
125
|
+
if (segments.some((s) => excludeDirs.has(s)))
|
|
126
|
+
return true;
|
|
127
|
+
if (excludePackages.size > 0 && segments.some((s) => excludePackages.has(s)))
|
|
128
|
+
return true;
|
|
129
|
+
const basename = segments[segments.length - 1] ?? "";
|
|
130
|
+
return excludeFiles.has(basename) || excludeFiles.has(relative);
|
|
131
|
+
}
|
|
132
|
+
function findViolations(relative, content) {
|
|
133
|
+
const results = [];
|
|
134
|
+
const lines = content.split(`
|
|
135
|
+
`);
|
|
136
|
+
let insideTemplate = false;
|
|
137
|
+
for (let i = 0;i < lines.length; i++) {
|
|
138
|
+
const line = lines[i] ?? "";
|
|
139
|
+
const backticks = (line.match(/`/g) ?? []).length;
|
|
140
|
+
const startedInTemplate = insideTemplate;
|
|
141
|
+
if (backticks % 2 === 1)
|
|
142
|
+
insideTemplate = !insideTemplate;
|
|
143
|
+
if (startedInTemplate && backticks === 0 && !line.includes("${"))
|
|
144
|
+
continue;
|
|
145
|
+
if (!isLineClean(line)) {
|
|
146
|
+
results.push({
|
|
147
|
+
file: relative,
|
|
148
|
+
line: i + 1,
|
|
149
|
+
content: line.trim(),
|
|
150
|
+
advice: suggestFix(line.trim())
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return results;
|
|
155
|
+
}
|
|
156
|
+
function printViolations(violations) {
|
|
157
|
+
if (violations.length === 0) {
|
|
158
|
+
console.log("[no-as-casting] ✅ No violations found.");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
console.log(`[no-as-casting] ❌ ${violations.length} violation(s) found:
|
|
162
|
+
`);
|
|
163
|
+
for (const v of violations) {
|
|
164
|
+
console.log(` ${v.file}:${v.line}`);
|
|
165
|
+
console.log(` ${v.content}`);
|
|
166
|
+
if (v.advice)
|
|
167
|
+
console.log(` \uD83D\uDCA1 ${v.advice}`);
|
|
168
|
+
console.log();
|
|
169
|
+
}
|
|
170
|
+
console.log("[no-as-casting] Use type guards, validation, or fix the types at the source.");
|
|
171
|
+
console.log('[no-as-casting] Only "as const" and "as unknown as" are allowed.');
|
|
172
|
+
}
|
|
173
|
+
async function checkNoAsCasting(options) {
|
|
174
|
+
const rootDir = options.rootDir;
|
|
175
|
+
const excludeDirs = new Set(options.exclude ?? ["node_modules", "dist", ".git", ".bun"]);
|
|
176
|
+
const excludePackages = new Set(options.excludePackages ?? []);
|
|
177
|
+
const excludeFiles = new Set(options.excludeFiles ?? []);
|
|
178
|
+
const pattern = options.filePatterns ?? "**/*.{ts,tsx}";
|
|
179
|
+
const glob = new Glob(pattern);
|
|
180
|
+
const violations = [];
|
|
181
|
+
for await (const file of glob.scan({ cwd: rootDir, absolute: true })) {
|
|
182
|
+
const relative = file.replace(`${rootDir}/`, "");
|
|
183
|
+
if (isFileExcluded(relative, excludeDirs, excludePackages, excludeFiles))
|
|
184
|
+
continue;
|
|
185
|
+
const content = readFileSync(file, "utf-8");
|
|
186
|
+
violations.push(...findViolations(relative, content));
|
|
187
|
+
}
|
|
188
|
+
return { violations, print: () => printViolations(violations) };
|
|
189
|
+
}
|
|
190
|
+
export {
|
|
191
|
+
suggestFix,
|
|
192
|
+
isLineClean,
|
|
193
|
+
checkNoAsCasting
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
//# debugId=8394B2630EA383EC64756E2164756E21
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../tools/quality/src/no-as-casting.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * No-as-casting conformance check.\n *\n * Bans all TypeScript type assertions (`as Type`) except the allowed\n * patterns: `as const` (literal narrowing), `as unknown as` (explicit\n * escape hatch), import/export renames, and `as` inside strings or\n * comments. Violations include pattern-specific fix advice.\n *\n * This module exports the check logic as a library so consuming\n * applications can import it from `@fairfox/polly/quality` and run it\n * programmatically. Polly's own `scripts/check-no-as-casting.ts` is a\n * thin CLI wrapper around these exports.\n */\n\nimport { readFileSync } from \"node:fs\";\nimport { Glob } from \"bun\";\n\nexport interface Violation {\n file: string;\n line: number;\n content: string;\n advice?: string;\n}\n\nexport interface CheckResult {\n violations: Violation[];\n print: () => void;\n}\n\nexport interface CheckOptions {\n rootDir: string;\n exclude?: string[];\n excludePackages?: string[];\n excludeFiles?: string[];\n filePatterns?: string;\n}\n\n/**\n * Check whether a line contains a forbidden `as` type assertion.\n * Returns true if the line is clean (no violation), false if it violates.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Source-text scanning with many skip rules is inherently branchy.\nexport function isLineClean(line: string): boolean {\n if (!line.includes(\" as \")) return true;\n\n const trimmed = line.trim();\n\n // Full-line comments\n if (trimmed.startsWith(\"//\") || trimmed.startsWith(\"*\") || trimmed.startsWith(\"/*\")) {\n return true;\n }\n\n // as const (literal narrowing)\n if (line.match(/\\bas const\\b/)) {\n const withoutAsConst = line.replace(/\\bas const\\b/g, \"\");\n if (!withoutAsConst.includes(\" as \")) return true;\n }\n\n // as unknown as (explicit escape hatch)\n if (line.includes(\" as unknown as \") || line.trimEnd().endsWith(\"as unknown as\")) {\n const withoutEscapeHatch = line.replace(/\\bas unknown as\\b/g, \"\");\n if (!withoutEscapeHatch.includes(\" as \")) return true;\n }\n\n // Import/export renames\n if (\n line.match(/\\b(import|export)\\s+.*\\s+as\\s+\\w+/) ||\n line.match(/\\b(import|export)\\s+\\*\\s+as\\s+\\w+/) ||\n line.match(/\\b(import|export)\\s+type\\s+.*\\s+as\\s+\\w+/) ||\n line.match(/^\\s*\\w+\\s+as\\s+\\w+,?\\s*$/) ||\n line.match(/^\\s*type\\s+\\w+\\s+as\\s+\\w+,?\\s*$/)\n ) {\n return true;\n }\n\n // Property declarations: as= or as: or as,\n if (line.match(/\\bas\\s*[=:,]/)) return true;\n\n // String literal detection: count quotes before each ` as ` occurrence.\n // If any quote type has an odd count, the ` as ` is inside a string.\n if (everyAsInsideString(line)) return true;\n\n // JSX text: ` as ` between > and < with no code syntax around it\n if (isJsxText(trimmed)) return true;\n\n // Plain text heuristic: indented line with no code syntax characters\n // before ` as ` — catches multiline JSX text and template literal bodies.\n if (isPlainText(trimmed)) return true;\n\n // Inline comment: ` as ` appears only after //\n const commentIdx = line.indexOf(\"//\");\n if (commentIdx >= 0 && line.indexOf(\" as \", commentIdx) >= 0) {\n const beforeComment = line.substring(0, commentIdx);\n if (!beforeComment.includes(\" as \")) return true;\n }\n\n // SQL alias: `) as column_name`\n if (line.match(/\"\\)\\s+as\\s+\\w+\"/)) return true;\n\n if (line.includes(\" satisfies \")) return true;\n\n return false;\n}\n\n/**\n * Returns true when every ` as ` occurrence in the line falls inside a\n * string literal (single-quoted, double-quoted, or backtick).\n */\nfunction everyAsInsideString(line: string): boolean {\n let searchFrom = 0;\n while (true) {\n const idx = line.indexOf(\" as \", searchFrom);\n if (idx < 0) return true; // no more ` as ` to check\n const before = line.substring(0, idx);\n const singleQuotes = (before.match(/'/g) ?? []).length;\n const doubleQuotes = (before.match(/\"/g) ?? []).length;\n const backticks = (before.match(/`/g) ?? []).length;\n if (singleQuotes % 2 === 0 && doubleQuotes % 2 === 0 && backticks % 2 === 0) {\n return false; // this ` as ` is outside any string\n }\n searchFrom = idx + 4;\n }\n}\n\n/**\n * Detects JSX text content — ` as ` appearing between > and < with no\n * code-like syntax around it (no braces, semicolons, equals signs).\n */\nfunction isJsxText(trimmed: string): boolean {\n // Classic JSX text: starts after > or is plain text ending before <\n if (trimmed.match(/^[^{};=()]*\\bas\\b[^{};=()]*$/)) {\n // No code syntax at all — could be JSX text or template literal body.\n // Reject if it looks like a type assertion (word ` as ` TypeName pattern\n // where TypeName starts with uppercase, or is a known TS type).\n if (\n !trimmed.match(/\\bas\\s+[A-Z]\\w*/) &&\n !trimmed.match(/\\bas\\s+(string|number|boolean|any|unknown|never)\\b/)\n ) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Heuristic for plain text in template literals or JSX: the line has no\n * code-like characters before ` as ` — no `=`, `{`, `}`, `:`, `;`, `(`.\n */\nfunction isPlainText(trimmed: string): boolean {\n const idx = trimmed.indexOf(\" as \");\n if (idx < 0) return false;\n const before = trimmed.substring(0, idx);\n // If nothing before ` as ` looks like code, it's probably prose.\n return (\n !before.match(/[={}:;(]/) &&\n !before.match(/\\b(const|let|var|type|interface|function|return|await)\\b/)\n );\n}\n\n/**\n * Suggest a concrete fix for a specific violation pattern.\n */\n// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: Pattern-matching advice is a linear chain of if-returns.\nexport function suggestFix(line: string): string | undefined {\n if (line.includes(\"JSON.parse\")) {\n return \"Use a validation function or type guard to parse and validate the result.\";\n }\n if (\n line.includes(\"as HTMLInputElement\") ||\n line.includes(\"as HTMLTextAreaElement\") ||\n line.includes(\"as HTMLButtonElement\")\n ) {\n return \"Use instanceof: if (el instanceof HTMLInputElement) { el.value ... }\";\n }\n if (line.includes(\"as HTMLElement\") || line.includes(\"as Element\")) {\n return \"Use instanceof: if (el instanceof HTMLElement) { ... }\";\n }\n if (line.includes(\".doc()\") && line.includes(\"as \")) {\n return \"Type the DocHandle generic: repo.find<MyType>(id) returns DocHandle<MyType>.\";\n }\n if (\n line.includes(\"Record<string, unknown>\") &&\n (line.includes(\"window\") || line.includes(\"globalThis\"))\n ) {\n return \"Extract a type guard: function getGlobalProp(name: string): unknown { ... }\";\n }\n if (line.includes(\"Record<string, unknown>\")) {\n return \"Use a type guard function that narrows the unknown value to the target shape.\";\n }\n if (line.includes(\"as PeerId\") || line.includes(\"as DocumentId\")) {\n return \"Use the library's branded-type constructor if available, or centralise the cast in a factory.\";\n }\n if (line.includes(\"as string\") || line.includes(\"as number\") || line.includes(\"as boolean\")) {\n return \"Narrow with typeof: if (typeof x === 'string') { ... }\";\n }\n if (line.includes(\"as any\")) {\n return \"Replace 'any' with 'unknown' and add a type guard or validation at the boundary.\";\n }\n return undefined;\n}\n\nfunction isFileExcluded(\n relative: string,\n excludeDirs: Set<string>,\n excludePackages: Set<string>,\n excludeFiles: Set<string>\n): boolean {\n const segments = relative.split(\"/\");\n if (segments.some((s) => excludeDirs.has(s))) return true;\n if (excludePackages.size > 0 && segments.some((s) => excludePackages.has(s))) return true;\n const basename = segments[segments.length - 1] ?? \"\";\n return excludeFiles.has(basename) || excludeFiles.has(relative);\n}\n\nfunction findViolations(relative: string, content: string): Violation[] {\n const results: Violation[] = [];\n const lines = content.split(\"\\n\");\n let insideTemplate = false;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i] ?? \"\";\n const backticks = (line.match(/`/g) ?? []).length;\n const startedInTemplate = insideTemplate;\n if (backticks % 2 === 1) insideTemplate = !insideTemplate;\n\n // Line is entirely inside a multi-line template literal and has no\n // interpolation — treat as string content (e.g. SQL column aliases).\n if (startedInTemplate && backticks === 0 && !line.includes(\"${\")) continue;\n\n if (!isLineClean(line)) {\n results.push({\n file: relative,\n line: i + 1,\n content: line.trim(),\n advice: suggestFix(line.trim()),\n });\n }\n }\n return results;\n}\n\nfunction printViolations(violations: Violation[]): void {\n if (violations.length === 0) {\n console.log(\"[no-as-casting] ✅ No violations found.\");\n return;\n }\n console.log(`[no-as-casting] ❌ ${violations.length} violation(s) found:\\n`);\n for (const v of violations) {\n console.log(` ${v.file}:${v.line}`);\n console.log(` ${v.content}`);\n if (v.advice) console.log(` 💡 ${v.advice}`);\n console.log();\n }\n console.log(\"[no-as-casting] Use type guards, validation, or fix the types at the source.\");\n console.log('[no-as-casting] Only \"as const\" and \"as unknown as\" are allowed.');\n}\n\n/**\n * Run the no-as-casting check against a directory. Returns a result\n * object with the violations and a print function for CLI output.\n */\nexport async function checkNoAsCasting(options: CheckOptions): Promise<CheckResult> {\n const rootDir = options.rootDir;\n const excludeDirs = new Set(options.exclude ?? [\"node_modules\", \"dist\", \".git\", \".bun\"]);\n const excludePackages = new Set(options.excludePackages ?? []);\n const excludeFiles = new Set(options.excludeFiles ?? []);\n const pattern = options.filePatterns ?? \"**/*.{ts,tsx}\";\n const glob = new Glob(pattern);\n const violations: Violation[] = [];\n\n for await (const file of glob.scan({ cwd: rootDir, absolute: true })) {\n const relative = file.replace(`${rootDir}/`, \"\");\n if (isFileExcluded(relative, excludeDirs, excludePackages, excludeFiles)) continue;\n const content = readFileSync(file, \"utf-8\");\n violations.push(...findViolations(relative, content));\n }\n\n return { violations, print: () => printViolations(violations) };\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "8394B2630EA383EC64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* No-as-casting conformance check.
|
|
3
|
+
*
|
|
4
|
+
* Bans all TypeScript type assertions (`as Type`) except the allowed
|
|
5
|
+
* patterns: `as const` (literal narrowing), `as unknown as` (explicit
|
|
6
|
+
* escape hatch), import/export renames, and `as` inside strings or
|
|
7
|
+
* comments. Violations include pattern-specific fix advice.
|
|
8
|
+
*
|
|
9
|
+
* This module exports the check logic as a library so consuming
|
|
10
|
+
* applications can import it from `@fairfox/polly/quality` and run it
|
|
11
|
+
* programmatically. Polly's own `scripts/check-no-as-casting.ts` is a
|
|
12
|
+
* thin CLI wrapper around these exports.
|
|
13
|
+
*/
|
|
14
|
+
export interface Violation {
|
|
15
|
+
file: string;
|
|
16
|
+
line: number;
|
|
17
|
+
content: string;
|
|
18
|
+
advice?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface CheckResult {
|
|
21
|
+
violations: Violation[];
|
|
22
|
+
print: () => void;
|
|
23
|
+
}
|
|
24
|
+
export interface CheckOptions {
|
|
25
|
+
rootDir: string;
|
|
26
|
+
exclude?: string[];
|
|
27
|
+
excludePackages?: string[];
|
|
28
|
+
excludeFiles?: string[];
|
|
29
|
+
filePatterns?: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Check whether a line contains a forbidden `as` type assertion.
|
|
33
|
+
* Returns true if the line is clean (no violation), false if it violates.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isLineClean(line: string): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Suggest a concrete fix for a specific violation pattern.
|
|
38
|
+
*/
|
|
39
|
+
export declare function suggestFix(line: string): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Run the no-as-casting check against a directory. Returns a result
|
|
42
|
+
* object with the violations and a print function for CLI output.
|
|
43
|
+
*/
|
|
44
|
+
export declare function checkNoAsCasting(options: CheckOptions): Promise<CheckResult>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fairfox/polly",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Multi-execution-context framework with reactive state and cross-context messaging for Chrome extensions, PWAs, and worker-based applications",
|
|
@@ -70,6 +70,10 @@
|
|
|
70
70
|
"import": "./dist/src/mesh.js",
|
|
71
71
|
"types": "./dist/src/mesh.d.ts"
|
|
72
72
|
},
|
|
73
|
+
"./mesh/node": {
|
|
74
|
+
"import": "./dist/src/mesh-node.js",
|
|
75
|
+
"types": "./dist/src/mesh-node.d.ts"
|
|
76
|
+
},
|
|
73
77
|
"./elysia": {
|
|
74
78
|
"import": "./dist/src/elysia/index.js",
|
|
75
79
|
"types": "./dist/src/elysia/index.d.ts"
|
|
@@ -77,6 +81,10 @@
|
|
|
77
81
|
"./client": {
|
|
78
82
|
"import": "./dist/src/client/index.js",
|
|
79
83
|
"types": "./dist/src/client/index.d.ts"
|
|
84
|
+
},
|
|
85
|
+
"./quality": {
|
|
86
|
+
"import": "./dist/tools/quality/src/index.js",
|
|
87
|
+
"types": "./dist/tools/quality/src/index.d.ts"
|
|
80
88
|
}
|
|
81
89
|
},
|
|
82
90
|
"files": [
|
|
@@ -138,12 +146,15 @@
|
|
|
138
146
|
"@preact/signals": "^2.5.1",
|
|
139
147
|
"elysia": ">= 1.4.0",
|
|
140
148
|
"@elysiajs/eden": ">= 1.2.0",
|
|
149
|
+
"@automerge/automerge": ">= 3.2.0",
|
|
141
150
|
"@automerge/automerge-repo": ">= 2.5.0",
|
|
142
151
|
"@automerge/automerge-repo-network-websocket": ">= 2.5.0",
|
|
143
152
|
"@automerge/automerge-repo-storage-indexeddb": ">= 2.5.0",
|
|
144
153
|
"@automerge/automerge-repo-storage-nodefs": ">= 2.5.0",
|
|
145
154
|
"ws": ">= 8.0.0",
|
|
146
|
-
"tweetnacl": ">= 1.0.0"
|
|
155
|
+
"tweetnacl": ">= 1.0.0",
|
|
156
|
+
"werift": ">= 0.19.0",
|
|
157
|
+
"@roamhq/wrtc": ">= 0.8.0"
|
|
147
158
|
},
|
|
148
159
|
"peerDependenciesMeta": {
|
|
149
160
|
"elysia": {
|
|
@@ -152,6 +163,9 @@
|
|
|
152
163
|
"@elysiajs/eden": {
|
|
153
164
|
"optional": true
|
|
154
165
|
},
|
|
166
|
+
"@automerge/automerge": {
|
|
167
|
+
"optional": true
|
|
168
|
+
},
|
|
155
169
|
"@automerge/automerge-repo": {
|
|
156
170
|
"optional": true
|
|
157
171
|
},
|
|
@@ -169,6 +183,12 @@
|
|
|
169
183
|
},
|
|
170
184
|
"tweetnacl": {
|
|
171
185
|
"optional": true
|
|
186
|
+
},
|
|
187
|
+
"werift": {
|
|
188
|
+
"optional": true
|
|
189
|
+
},
|
|
190
|
+
"@roamhq/wrtc": {
|
|
191
|
+
"optional": true
|
|
172
192
|
}
|
|
173
193
|
},
|
|
174
194
|
"keywords": [
|