@wrongstack/tools 0.299.0 → 0.300.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/dist/audit.js +6 -2
- package/dist/bash.js +6 -2
- package/dist/builtin.js +1473 -295
- package/dist/codebase-index/import-extractor.d.ts +39 -0
- package/dist/codebase-index/index.js +1396 -255
- package/dist/codebase-index/languages.d.ts +24 -0
- package/dist/codebase-index/module-resolver.d.ts +78 -0
- package/dist/codebase-index/module-roots.d.ts +81 -0
- package/dist/codebase-index/parser-output.d.ts +29 -0
- package/dist/codebase-index/project-server.js +1379 -236
- package/dist/codebase-index/rs-parser.d.ts +22 -0
- package/dist/codebase-index/schema.d.ts +24 -1
- package/dist/codebase-index/worker.js +1381 -238
- package/dist/codebase-index/writer-graph-helpers.d.ts +17 -5
- package/dist/codebase-index/writer-ref-mapper.d.ts +3 -0
- package/dist/codebase-index/writer-schema.d.ts +15 -3
- package/dist/codebase-index/writer.d.ts +76 -3
- package/dist/exec.js +35 -2
- package/dist/format.js +6 -2
- package/dist/index.js +1473 -295
- package/dist/install.js +6 -2
- package/dist/json.js +51 -2
- package/dist/languages/index.js +6 -2
- package/dist/lint.js +6 -2
- package/dist/outdated.js +6 -2
- package/dist/pack.js +1473 -295
- package/dist/process-registry.d.ts +6 -0
- package/dist/process-registry.js +6 -2
- package/dist/ps-slash.js +6 -2
- package/dist/read.js +1387 -244
- package/dist/test.js +6 -2
- package/dist/tool-tier.js +1473 -295
- package/dist/typecheck.js +6 -2
- package/package.json +3 -3
- package/dist/codebase-index/refs-extractor.d.ts +0 -11
|
@@ -2,15 +2,28 @@ import type { GraphEdge, GraphNode, SymbolLang } from './schema.js';
|
|
|
2
2
|
/**
|
|
3
3
|
* Derive a monorepo package name from an absolute file path.
|
|
4
4
|
* Handles both `packages/<name>/...` and `apps/<name>/...` layouts.
|
|
5
|
+
*
|
|
6
|
+
* This is the fallback only. The authoritative grouping is computed at index
|
|
7
|
+
* time from each ecosystem's own manifests and stored on `files.package` —
|
|
8
|
+
* see {@link createPackageLabeller}. A path-shape guess is all that is left
|
|
9
|
+
* for a repo with no manifest at all.
|
|
5
10
|
*/
|
|
6
11
|
export declare function derivePackage(filePath: string): string | undefined;
|
|
7
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Build the `file → package` lookup the graph readers group by.
|
|
14
|
+
*
|
|
15
|
+
* `stored` comes from `files.package`, which the indexer filled from `go.mod`,
|
|
16
|
+
* `Cargo.toml`, `package.json`, `pom.xml` and friends. Files missing a stored
|
|
17
|
+
* label (indexed by an older run, or outside any manifest) fall back to the
|
|
18
|
+
* path-shape heuristic and finally to `(root)`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createPackageLabeller(stored: ReadonlyMap<string, string>): (file: string) => string;
|
|
8
21
|
export declare function buildPackageGraphNodes(fileCounts: Array<{
|
|
9
22
|
file: string;
|
|
10
23
|
n: number;
|
|
11
24
|
}>, files: Array<{
|
|
12
25
|
file: string;
|
|
13
|
-
}
|
|
26
|
+
}>, packageOf: (file: string) => string): {
|
|
14
27
|
pkgNodes: Map<string, GraphNode>;
|
|
15
28
|
fileToPkg: Map<string, string>;
|
|
16
29
|
};
|
|
@@ -22,7 +35,7 @@ export type WriterFileGraphSymbolRow = {
|
|
|
22
35
|
lang: string;
|
|
23
36
|
line: number;
|
|
24
37
|
};
|
|
25
|
-
export declare function buildFileGraphNodeState(pkgSyms: WriterFileGraphSymbolRow[], localFiles: Set<string
|
|
38
|
+
export declare function buildFileGraphNodeState(pkgSyms: WriterFileGraphSymbolRow[], localFiles: Set<string>, packageOf: (file: string) => string): {
|
|
26
39
|
fileNodes: Map<string, GraphNode>;
|
|
27
40
|
symToFile: Map<number, string>;
|
|
28
41
|
fileStats: Map<string, {
|
|
@@ -41,8 +54,7 @@ export type WriterSymbolGraphRow = {
|
|
|
41
54
|
signature: string;
|
|
42
55
|
scope: string;
|
|
43
56
|
};
|
|
44
|
-
export declare function buildSymbolGraphNodes(symById: Map<number, WriterSymbolGraphRow>, relatedIds: Set<number>, fileFilter: string): GraphNode[];
|
|
45
|
-
export declare function resolveRelativeImport(fromFile: string, moduleName: string, indexedFiles: Set<string>): string | undefined;
|
|
57
|
+
export declare function buildSymbolGraphNodes(symById: Map<number, WriterSymbolGraphRow>, relatedIds: Set<number>, fileFilter: string, packageOf: (file: string) => string): GraphNode[];
|
|
46
58
|
export type WeightedEdgeAccumulator = {
|
|
47
59
|
weight: number;
|
|
48
60
|
types: Map<string, number>;
|
|
@@ -6,6 +6,9 @@ export type WriterRefRow = {
|
|
|
6
6
|
to_id: number | null;
|
|
7
7
|
call_type: string;
|
|
8
8
|
line: number;
|
|
9
|
+
lang?: string | null;
|
|
10
|
+
module?: string | null;
|
|
11
|
+
to_file?: string | null;
|
|
9
12
|
};
|
|
10
13
|
export declare function mapWriterRefRow(row: WriterRefRow): Ref;
|
|
11
14
|
//# sourceMappingURL=writer-ref-mapper.d.ts.map
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
export declare const METADATA_TABLE_SQL = "\n CREATE TABLE IF NOT EXISTS metadata (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n );\n";
|
|
2
|
-
export declare const CORE_TABLES_SQL = "\n CREATE TABLE IF NOT EXISTS files (\n file TEXT PRIMARY KEY,\n lang TEXT NOT NULL,\n mtime_ms INTEGER NOT NULL,\n symbol_count INTEGER NOT NULL DEFAULT 0,\n last_indexed INTEGER NOT NULL\n );\n CREATE TABLE IF NOT EXISTS symbols (\n id INTEGER PRIMARY KEY,\n lang TEXT NOT NULL,\n kind TEXT NOT NULL,\n name TEXT NOT NULL,\n file TEXT NOT NULL,\n line INTEGER NOT NULL,\n col INTEGER NOT NULL,\n signature TEXT NOT NULL DEFAULT '',\n doc_comment TEXT NOT NULL DEFAULT '',\n scope TEXT NOT NULL DEFAULT '',\n text TEXT NOT NULL DEFAULT '',\n file_fk TEXT NOT NULL\n );\n";
|
|
2
|
+
export declare const CORE_TABLES_SQL = "\n CREATE TABLE IF NOT EXISTS files (\n file TEXT PRIMARY KEY,\n lang TEXT NOT NULL,\n mtime_ms INTEGER NOT NULL,\n symbol_count INTEGER NOT NULL DEFAULT 0,\n last_indexed INTEGER NOT NULL,\n -- Code Atlas grouping label, computed at index time from the ecosystem's\n -- own manifests (package.json, go.mod, Cargo.toml, \u2026). Stored rather than\n -- re-derived per query because the evidence lives on disk, not in the DB.\n package TEXT NOT NULL DEFAULT ''\n );\n CREATE TABLE IF NOT EXISTS symbols (\n id INTEGER PRIMARY KEY,\n lang TEXT NOT NULL,\n kind TEXT NOT NULL,\n name TEXT NOT NULL,\n file TEXT NOT NULL,\n line INTEGER NOT NULL,\n col INTEGER NOT NULL,\n signature TEXT NOT NULL DEFAULT '',\n doc_comment TEXT NOT NULL DEFAULT '',\n scope TEXT NOT NULL DEFAULT '',\n text TEXT NOT NULL DEFAULT '',\n file_fk TEXT NOT NULL\n );\n";
|
|
3
|
+
export declare const FILE_INDEX_SQL: readonly ['CREATE INDEX IF NOT EXISTS idx_f_package ON files(package)'];
|
|
3
4
|
export declare const SYMBOL_INDEX_SQL: readonly ['CREATE INDEX IF NOT EXISTS idx_s_name ON symbols(name)', 'CREATE INDEX IF NOT EXISTS idx_s_kind ON symbols(kind)', 'CREATE INDEX IF NOT EXISTS idx_s_lang ON symbols(lang)', 'CREATE INDEX IF NOT EXISTS idx_s_file ON symbols(file)', 'CREATE INDEX IF NOT EXISTS idx_s_lang_kind ON symbols(lang, kind)', 'CREATE INDEX IF NOT EXISTS idx_s_file_fk ON symbols(file_fk)', 'CREATE INDEX IF NOT EXISTS idx_s_name_id ON symbols(name, id)'];
|
|
4
|
-
export declare const REFS_TABLE_SQL = "\n CREATE TABLE IF NOT EXISTS refs (\n id INTEGER PRIMARY KEY,\n from_id INTEGER NOT NULL,\n to_name TEXT NOT NULL,\n to_id INTEGER,\n call_type TEXT NOT NULL,\n line INTEGER NOT NULL\n );\n";
|
|
5
|
-
export declare const REFS_INDEX_SQL: readonly ['CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)', 'CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)', 'CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)', 'CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)'];
|
|
5
|
+
export declare const REFS_TABLE_SQL = "\n CREATE TABLE IF NOT EXISTS refs (\n id INTEGER PRIMARY KEY,\n from_id INTEGER NOT NULL,\n to_name TEXT NOT NULL,\n to_id INTEGER,\n call_type TEXT NOT NULL,\n line INTEGER NOT NULL,\n lang TEXT NOT NULL DEFAULT '',\n module TEXT,\n to_file TEXT\n );\n";
|
|
6
|
+
export declare const REFS_INDEX_SQL: readonly ['CREATE INDEX IF NOT EXISTS idx_r_from ON refs(from_id)', 'CREATE INDEX IF NOT EXISTS idx_r_to_id ON refs(to_id)', 'CREATE INDEX IF NOT EXISTS idx_r_to_name ON refs(to_name)', 'CREATE INDEX IF NOT EXISTS idx_r_call_type ON refs(call_type)', 'CREATE INDEX IF NOT EXISTS idx_r_to_name_lang ON refs(to_name, lang)', 'CREATE INDEX IF NOT EXISTS idx_r_module ON refs(module)', 'CREATE INDEX IF NOT EXISTS idx_r_to_file ON refs(to_file)'];
|
|
7
|
+
/**
|
|
8
|
+
* Static `lang → family` mirror of {@link LANG_FAMILY_ENTRIES}, so ref
|
|
9
|
+
* resolution can scope a match to one language family with a join rather than
|
|
10
|
+
* binding a per-family IN-list into every statement.
|
|
11
|
+
*
|
|
12
|
+
* Row `('', '*')` is the wildcard for refs written without a language (older
|
|
13
|
+
* rows, and tests that construct refs by hand): they keep resolving globally.
|
|
14
|
+
*/
|
|
15
|
+
export declare const LANG_FAMILY_TABLE_SQL = "\n CREATE TABLE IF NOT EXISTS lang_family (\n lang TEXT PRIMARY KEY,\n family TEXT NOT NULL\n );\n";
|
|
16
|
+
/** Family value that matches every symbol family, used by language-less refs. */
|
|
17
|
+
export declare const LANG_FAMILY_WILDCARD = "*";
|
|
6
18
|
export declare const SYMBOLS_FTS_SQL = "CREATE VIRTUAL TABLE IF NOT EXISTS symbols_fts USING fts5(text, tokenize = 'unicode61')";
|
|
7
19
|
//# sourceMappingURL=writer-schema.d.ts.map
|
|
@@ -56,10 +56,44 @@ export declare class IndexStore {
|
|
|
56
56
|
indexDir?: string | undefined;
|
|
57
57
|
});
|
|
58
58
|
runWithRetry<T>(fn: () => T): T;
|
|
59
|
+
/**
|
|
60
|
+
* Mirror the in-process language→family map into SQLite.
|
|
61
|
+
*
|
|
62
|
+
* Rewritten on every open rather than only on schema bumps: the mapping is
|
|
63
|
+
* static lookup data, so a code-side change (a new language, a language
|
|
64
|
+
* moving families) must take effect without forcing a full reindex.
|
|
65
|
+
*/
|
|
66
|
+
private seedLangFamilies;
|
|
67
|
+
/**
|
|
68
|
+
* Add any column the current schema expects but the on-disk table lacks.
|
|
69
|
+
*
|
|
70
|
+
* `CREATE TABLE IF NOT EXISTS` silently keeps an existing table's old shape,
|
|
71
|
+
* and the version check above only rebuilds on a version *mismatch*. That
|
|
72
|
+
* leaves a real gap: several wstack processes share this database, and while
|
|
73
|
+
* a version upgrade is rolling out one of them may still be running the
|
|
74
|
+
* previous build. That older process sees the newer version number, drops the
|
|
75
|
+
* tables, and recreates them from *its* DDL — without the newer columns —
|
|
76
|
+
* while the metadata row still reads the new version. Every later query for
|
|
77
|
+
* one of those columns then fails with `no such column`, and no amount of
|
|
78
|
+
* reindexing fixes it, because the version numbers already agree.
|
|
79
|
+
*
|
|
80
|
+
* Repairing column-by-column makes the schema self-healing from any of those
|
|
81
|
+
* states. Table and column names are compile-time literals from this module,
|
|
82
|
+
* never user input.
|
|
83
|
+
*/
|
|
84
|
+
private repairMissingColumns;
|
|
59
85
|
private initSchema;
|
|
60
86
|
private static readonly NEXT_SYMBOL_ID_KEY;
|
|
61
87
|
/** Stay under typical SQLite SQLITE_MAX_VARIABLE_NUMBER (often 999). */
|
|
62
88
|
private static readonly MAX_SQL_VARS;
|
|
89
|
+
/**
|
|
90
|
+
* Correlated predicate: the ref in `refs` and the candidate symbol aliased
|
|
91
|
+
* `sym` belong to the same language family — or the ref carries no language,
|
|
92
|
+
* in which case the wildcard bind matches everything.
|
|
93
|
+
*
|
|
94
|
+
* Each textual occurrence consumes one `?` bind of {@link LANG_FAMILY_WILDCARD}.
|
|
95
|
+
*/
|
|
96
|
+
private static readonly FAMILY_MATCH_SQL;
|
|
63
97
|
/**
|
|
64
98
|
* Ensure `metadata.next_symbol_id` exists. Safe to call outside a write
|
|
65
99
|
* transaction on open; the first concurrent writer under BEGIN IMMEDIATE
|
|
@@ -101,6 +135,42 @@ export declare class IndexStore {
|
|
|
101
135
|
upsertFile(meta: FileMeta): void;
|
|
102
136
|
getFileMeta(file: string): FileMeta | null;
|
|
103
137
|
getAllFileMetas(): FileMeta[];
|
|
138
|
+
/** Store the Code Atlas grouping label for each indexed file. */
|
|
139
|
+
setFilePackages(entries: ReadonlyMap<string, string>): void;
|
|
140
|
+
/**
|
|
141
|
+
* Every indexed `namespace`/`module` declaration, for ecosystems whose import
|
|
142
|
+
* specifiers name a namespace rather than a path (C#, PHP, Elixir, Haskell).
|
|
143
|
+
* Ordered so the resolver's choice among duplicate declarations is stable.
|
|
144
|
+
*/
|
|
145
|
+
getNamespaceDeclarations(): Array<{
|
|
146
|
+
name: string;
|
|
147
|
+
file: string;
|
|
148
|
+
}>;
|
|
149
|
+
/** `file → package` for every indexed file that has a label. */
|
|
150
|
+
getFilePackages(): Map<string, string>;
|
|
151
|
+
/**
|
|
152
|
+
* Distinct `(fromFile, lang, module)` triples needing module resolution.
|
|
153
|
+
*
|
|
154
|
+
* Distinct rather than per-ref because resolution depends only on these three
|
|
155
|
+
* values: a file importing the same module twenty times resolves it once.
|
|
156
|
+
*/
|
|
157
|
+
getUnresolvedImports(onlyFiles?: readonly string[]): Array<{
|
|
158
|
+
fromFile: string;
|
|
159
|
+
lang: string;
|
|
160
|
+
module: string;
|
|
161
|
+
}>;
|
|
162
|
+
/**
|
|
163
|
+
* Write resolved import targets back onto `refs.to_file`.
|
|
164
|
+
*
|
|
165
|
+
* Applied through a temp table and a single UPDATE: one statement per
|
|
166
|
+
* resolution would mean thousands of round-trips on a first index.
|
|
167
|
+
*/
|
|
168
|
+
applyImportResolutions(resolutions: ReadonlyArray<{
|
|
169
|
+
fromFile: string;
|
|
170
|
+
lang: string;
|
|
171
|
+
module: string;
|
|
172
|
+
toFile: string;
|
|
173
|
+
}>): number;
|
|
104
174
|
search(query: string, filter?: WriterSearchFilter, opts?: {
|
|
105
175
|
limit?: number | undefined;
|
|
106
176
|
}): SearchResult[];
|
|
@@ -228,9 +298,12 @@ export declare class IndexStore {
|
|
|
228
298
|
* Resolve `to_name` → `to_id` for all refs that have a name but no id.
|
|
229
299
|
* Call this after all symbols have been inserted to fill in cross-references.
|
|
230
300
|
*
|
|
231
|
-
*
|
|
232
|
-
* the
|
|
233
|
-
*
|
|
301
|
+
* A match additionally requires the referencing ref and the target symbol to
|
|
302
|
+
* be in the same {@link LangFamily}. Without that guard a name match is a
|
|
303
|
+
* cross-language accident waiting to happen — `main`, `New`, `Parse` and
|
|
304
|
+
* `Config` are declared in most languages at once, and each collision draws a
|
|
305
|
+
* Code Atlas edge between files that never reference each other. Refs stored
|
|
306
|
+
* without a language keep the old global behaviour via the `'*'` wildcard row.
|
|
234
307
|
*/
|
|
235
308
|
resolveRefs(): number;
|
|
236
309
|
resolveRefsForNames(names: Iterable<string>): number;
|
package/dist/exec.js
CHANGED
|
@@ -1131,7 +1131,7 @@ var ProcessRegistryImpl = class {
|
|
|
1131
1131
|
const p = this.processes.get(pid);
|
|
1132
1132
|
if (!p) return false;
|
|
1133
1133
|
if (p.killed) return true;
|
|
1134
|
-
if (p.protected) return false;
|
|
1134
|
+
if (p.protected && opts.includeProtected !== true) return false;
|
|
1135
1135
|
if (opts.preserveBackground && p.background) return false;
|
|
1136
1136
|
const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
|
|
1137
1137
|
const isWin3 = os.platform() === "win32";
|
|
@@ -1182,9 +1182,13 @@ var ProcessRegistryImpl = class {
|
|
|
1182
1182
|
killAll(opts = {}) {
|
|
1183
1183
|
const pids = Array.from(this.processes.keys());
|
|
1184
1184
|
const killed = [];
|
|
1185
|
+
const includeProtected = opts.includeProtected === true;
|
|
1185
1186
|
for (const pid of pids) {
|
|
1186
1187
|
const p = this.processes.get(pid);
|
|
1187
|
-
if (
|
|
1188
|
+
if (!p) continue;
|
|
1189
|
+
if (p.protected && !includeProtected) continue;
|
|
1190
|
+
if (opts.preserveBackground && p.background) continue;
|
|
1191
|
+
if (this.kill(pid, opts)) killed.push(pid);
|
|
1188
1192
|
}
|
|
1189
1193
|
return killed;
|
|
1190
1194
|
}
|
|
@@ -2642,6 +2646,26 @@ var BLOCKED_ARG_PATTERNS = {
|
|
|
2642
2646
|
pnpm: [],
|
|
2643
2647
|
npx: []
|
|
2644
2648
|
};
|
|
2649
|
+
var BLOCKED_OPTION_NAMES = {
|
|
2650
|
+
git: /* @__PURE__ */ new Set([
|
|
2651
|
+
"--exec",
|
|
2652
|
+
"--upload-pack",
|
|
2653
|
+
"--receive-pack",
|
|
2654
|
+
"--exec-path",
|
|
2655
|
+
"--git-dir",
|
|
2656
|
+
"--work-tree",
|
|
2657
|
+
"--namespace",
|
|
2658
|
+
"-c",
|
|
2659
|
+
"--config",
|
|
2660
|
+
"--config-env",
|
|
2661
|
+
"-C"
|
|
2662
|
+
]),
|
|
2663
|
+
find: /* @__PURE__ */ new Set(["-exec", "-ok", "-execdir"])
|
|
2664
|
+
};
|
|
2665
|
+
function optionName(arg) {
|
|
2666
|
+
const eq = arg.indexOf("=");
|
|
2667
|
+
return eq > 0 ? arg.slice(0, eq) : arg;
|
|
2668
|
+
}
|
|
2645
2669
|
var BLOCKED_SUBCOMMANDS = {
|
|
2646
2670
|
docker: /* @__PURE__ */ new Set(["push"]),
|
|
2647
2671
|
podman: /* @__PURE__ */ new Set(["push"]),
|
|
@@ -2679,6 +2703,15 @@ function validateArgs(cmd, args) {
|
|
|
2679
2703
|
const blocked2 = blockedSequences.find((seq) => seq.every((part, idx) => actual[idx] === part));
|
|
2680
2704
|
if (blocked2) return `Blocked subcommand "${blocked2.join(" ")}" for command "${cmd}"`;
|
|
2681
2705
|
}
|
|
2706
|
+
const blockedOptions = BLOCKED_OPTION_NAMES[cmd];
|
|
2707
|
+
if (blockedOptions) {
|
|
2708
|
+
for (const arg of args) {
|
|
2709
|
+
if (arg === "--") break;
|
|
2710
|
+
if (blockedOptions.has(optionName(arg))) {
|
|
2711
|
+
return `Blocked option "${optionName(arg)}" for command "${cmd}"`;
|
|
2712
|
+
}
|
|
2713
|
+
}
|
|
2714
|
+
}
|
|
2682
2715
|
const blocked = BLOCKED_ARG_PATTERNS[cmd];
|
|
2683
2716
|
if (!blocked) return null;
|
|
2684
2717
|
for (const arg of args) {
|
package/dist/format.js
CHANGED
|
@@ -666,7 +666,7 @@ var ProcessRegistryImpl = class {
|
|
|
666
666
|
const p = this.processes.get(pid);
|
|
667
667
|
if (!p) return false;
|
|
668
668
|
if (p.killed) return true;
|
|
669
|
-
if (p.protected) return false;
|
|
669
|
+
if (p.protected && opts.includeProtected !== true) return false;
|
|
670
670
|
if (opts.preserveBackground && p.background) return false;
|
|
671
671
|
const { force = false, graceMs = DEFAULT_GRACE_MS } = opts;
|
|
672
672
|
const isWin2 = os.platform() === "win32";
|
|
@@ -717,9 +717,13 @@ var ProcessRegistryImpl = class {
|
|
|
717
717
|
killAll(opts = {}) {
|
|
718
718
|
const pids = Array.from(this.processes.keys());
|
|
719
719
|
const killed = [];
|
|
720
|
+
const includeProtected = opts.includeProtected === true;
|
|
720
721
|
for (const pid of pids) {
|
|
721
722
|
const p = this.processes.get(pid);
|
|
722
|
-
if (
|
|
723
|
+
if (!p) continue;
|
|
724
|
+
if (p.protected && !includeProtected) continue;
|
|
725
|
+
if (opts.preserveBackground && p.background) continue;
|
|
726
|
+
if (this.kill(pid, opts)) killed.push(pid);
|
|
723
727
|
}
|
|
724
728
|
return killed;
|
|
725
729
|
}
|