@zhuixin/engine 0.2.4 → 0.2.5

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 CHANGED
@@ -5,6 +5,16 @@ Private source repository for the AppTrace analysis engine.
5
5
  The published npm package contains only the compiled bundle (`dist/engine-main.js`).
6
6
  Source, tests, and build configuration stay in this repository.
7
7
 
8
+ ## Status
9
+
10
+ - macOS arm64: bundle inspection and Electron/JavaScript analysis are verified.
11
+ Reports include the code-signature authority chain, team identifier and entitlement
12
+ keys. Ghidra headless import and automatic analysis work with a thin Mach-O, but
13
+ macOS native analysis is not implemented. `.dmg` acquisition is unsupported.
14
+ - Windows x64: local application inspection, PE metadata and Authenticode signature
15
+ inspection are implemented. Native Windows analysis and MSIX/MSI packages are not
16
+ supported.
17
+
8
18
  ## Release
9
19
 
10
20
  ```bash
@@ -19950,6 +19950,12 @@ function archName2(cpu, subtype) {
19950
19950
  if (cpu === 16777228) return (subtype & 16777215) === 2 ? "arm64e" : "arm64";
19951
19951
  return `cpu-${cpu.toString(16)}`;
19952
19952
  }
19953
+ function readEntitlements(stdout) {
19954
+ const trimmed = stdout.trim();
19955
+ if (!trimmed) return void 0;
19956
+ const xmlStart = trimmed.search(/<\?xml|<plist/i);
19957
+ return (xmlStart === -1 ? trimmed : trimmed.slice(xmlStart)).trim() || void 0;
19958
+ }
19953
19959
  function machOArchitectures(filePath) {
19954
19960
  const fd = import_node_fs4.default.openSync(filePath, "r");
19955
19961
  try {
@@ -19989,7 +19995,9 @@ async function readSignature(bundlePath, warnings) {
19989
19995
  try {
19990
19996
  const { stdout, stderr } = await execFileAsync(
19991
19997
  "codesign",
19992
- ["-dv", "--entitlements", ":-", bundlePath],
19998
+ // Single `-v` omits the Authority chain on macOS 14.x; the certificate
19999
+ // chain is only printed at triple verbosity.
20000
+ ["-dvvv", "--entitlements", ":-", bundlePath],
19993
20001
  { timeout: 3e4, maxBuffer: 4 * 1024 * 1024 }
19994
20002
  );
19995
20003
  const details = `${stderr}`;
@@ -19998,11 +20006,14 @@ async function readSignature(bundlePath, warnings) {
19998
20006
  );
19999
20007
  const team = /^TeamIdentifier=(.+)$/m.exec(details)?.[1]?.trim();
20000
20008
  const signed = /^CodeDirectory|^Signature=/m.test(details) || authority.length > 0;
20009
+ if (/invalid entitlements blob/i.test(details)) {
20010
+ warnings.push("Code signature reports an invalid entitlements blob; the OS ignores these entitlements.");
20011
+ }
20001
20012
  return {
20002
20013
  signed,
20003
20014
  authority: authority.length ? authority : void 0,
20004
20015
  teamIdentifier: team,
20005
- entitlements: stdout.trim() || void 0
20016
+ entitlements: readEntitlements(stdout)
20006
20017
  };
20007
20018
  } catch (error) {
20008
20019
  const stderr = error.stderr ?? "";
@@ -20950,6 +20961,11 @@ function inspectionValue(inspection, key) {
20950
20961
  function record(value) {
20951
20962
  return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
20952
20963
  }
20964
+ function entitlementKeys(value) {
20965
+ if (typeof value !== "string" || !value.includes("<key>")) return void 0;
20966
+ const keys = [...value.matchAll(/<key>([^<]+)<\/key>/g)].map((match) => match[1]);
20967
+ return keys.length ? keys : void 0;
20968
+ }
20953
20969
  function collectEvidencePaths(jobs) {
20954
20970
  const paths = /* @__PURE__ */ new Set();
20955
20971
  for (const job of jobs) {
@@ -20986,6 +21002,13 @@ function renderMacAppInspection(lines, inspection, warnings) {
20986
21002
  `| Authority | ${cell(
20987
21003
  Array.isArray(signature?.authority) ? signature?.authority.join(", ") : void 0
20988
21004
  )} |`,
21005
+ `| Team identifier | ${cell(signature?.teamIdentifier)} |`,
21006
+ `| Entitlements | ${cell(
21007
+ (() => {
21008
+ const keys = entitlementKeys(signature?.entitlements);
21009
+ return keys ? keys.join(", ") : void 0;
21010
+ })()
21011
+ )} |`,
20989
21012
  ""
20990
21013
  );
20991
21014
  if (warnings.length > 0) {
@@ -21063,6 +21086,210 @@ function renderIpaInspection(lines, inspection, warnings) {
21063
21086
  lines.push("");
21064
21087
  }
21065
21088
  }
21089
+ function numberValue(value) {
21090
+ return typeof value === "number" && Number.isFinite(value) ? value : void 0;
21091
+ }
21092
+ var IRREGULAR_PLURALS = {
21093
+ "storage entry": "storage entries",
21094
+ slice: "slices"
21095
+ };
21096
+ function countLabel(count, noun) {
21097
+ const many = IRREGULAR_PLURALS[noun] ?? `${noun}s`;
21098
+ return `${count.toLocaleString("en-US")} ${count === 1 ? noun : many}`;
21099
+ }
21100
+ var PLATFORM_LABEL = {
21101
+ ios: "iOS",
21102
+ macos: "macOS",
21103
+ windows: "Windows",
21104
+ linux: "Linux"
21105
+ };
21106
+ var FORMAT_LABEL = {
21107
+ ipa: "IPA",
21108
+ app: "macOS app bundle",
21109
+ exe: "Windows executable"
21110
+ };
21111
+ function formatBytes(bytes) {
21112
+ if (typeof bytes !== "number" || bytes <= 0) return "unknown size";
21113
+ const units = ["B", "KB", "MB", "GB"];
21114
+ let size = bytes;
21115
+ let unit = 0;
21116
+ while (size >= 1024 && unit < units.length - 1) {
21117
+ size /= 1024;
21118
+ unit += 1;
21119
+ }
21120
+ return `${size.toFixed(unit === 0 ? 0 : 1)} ${units[unit]}`;
21121
+ }
21122
+ function routeSegments(value) {
21123
+ return value.replace(/^(?:\.{1,2}\/)+/, "").replace(/^\/+/, "").split(/[/?#]/).filter((segment) => segment.length > 0);
21124
+ }
21125
+ function routeGroup(value, depth = 1) {
21126
+ const segments = routeSegments(value).slice(0, depth);
21127
+ return segments.length > 0 ? segments.join("/") : "(root)";
21128
+ }
21129
+ function groupRoutes(values2) {
21130
+ if (values2.length === 0) return [];
21131
+ const buckets = /* @__PURE__ */ new Map();
21132
+ for (const value of values2) {
21133
+ const key = routeGroup(value, 1);
21134
+ const bucket = buckets.get(key);
21135
+ if (bucket) bucket.values.push(value);
21136
+ else buckets.set(key, { depth: 1, values: [value] });
21137
+ }
21138
+ for (let guard = 0; guard < 8; guard += 1) {
21139
+ const [key, bucket] = [...buckets.entries()].sort(
21140
+ (a, b) => b[1].values.length - a[1].values.length
21141
+ )[0];
21142
+ if (bucket.depth >= 3 || bucket.values.length <= values2.length / 2) break;
21143
+ const split = /* @__PURE__ */ new Map();
21144
+ for (const value of bucket.values) {
21145
+ const next = routeGroup(value, bucket.depth + 1);
21146
+ const list = split.get(next);
21147
+ if (list) list.push(value);
21148
+ else split.set(next, [value]);
21149
+ }
21150
+ if (split.size * 2 > bucket.values.length) break;
21151
+ if (split.size === 1 && split.has(key)) break;
21152
+ buckets.delete(key);
21153
+ for (const [next, list] of split) {
21154
+ buckets.set(next, { depth: bucket.depth + 1, values: list });
21155
+ }
21156
+ }
21157
+ return [...buckets.entries()].map(([group, bucket]) => ({
21158
+ group,
21159
+ count: bucket.values.length,
21160
+ example: bucket.values[0]
21161
+ })).sort((a, b) => b.count - a.count || a.group.localeCompare(b.group));
21162
+ }
21163
+ function selectReportJobs(jobs, limit = 20) {
21164
+ const window = jobs.slice(0, limit);
21165
+ const present = new Set(window.map((job) => job.id));
21166
+ const carried = [];
21167
+ for (const capability of ["native:inspect"]) {
21168
+ const latest = jobs.find(
21169
+ (job) => job.capabilityId === capability && String(job.status) === "analyzed"
21170
+ );
21171
+ if (latest && !present.has(latest.id)) carried.push(latest);
21172
+ }
21173
+ return [...window, ...carried];
21174
+ }
21175
+ function nativeFacts(jobs) {
21176
+ const job = [...jobs].reverse().find(
21177
+ (item) => item.capabilityId === "native:inspect" && String(item.status) === "analyzed"
21178
+ );
21179
+ if (!job?.result) return { inspected: false };
21180
+ const counts = record(job.result.counts) ?? {};
21181
+ const encryption = record(job.result.encryption) ?? {};
21182
+ return {
21183
+ inspected: true,
21184
+ procedureCount: numberValue(
21185
+ counts.procedure_count ?? counts.procedureCount
21186
+ ),
21187
+ stringCount: numberValue(counts.string_count ?? counts.stringCount),
21188
+ declaredSlices: numberValue(encryption.declaredSlices),
21189
+ encryptedSlices: numberValue(encryption.encryptedSlices),
21190
+ allDeclaredEncrypted: typeof encryption.allDeclaredEncrypted === "boolean" ? encryption.allDeclaredEncrypted : void 0
21191
+ };
21192
+ }
21193
+ function encryptionStatement(native, packageFormat) {
21194
+ if (packageFormat !== "ipa") {
21195
+ return "Not applicable: declared encryption only applies to iOS Mach-O slices.";
21196
+ }
21197
+ if (!native.inspected) {
21198
+ return "Not determined \u2014 native inspection has not run for this artifact, so the encryption state is unknown.";
21199
+ }
21200
+ const declared = native.declaredSlices ?? 0;
21201
+ const encrypted = native.encryptedSlices ?? 0;
21202
+ if (declared === 0) {
21203
+ return "The main executable declares no slices, so encryption state is unknown.";
21204
+ }
21205
+ if (encrypted === 0) {
21206
+ return `No slice declares encryption (0 of ${declared}). Native code is readable, subject to normal static-analysis limits.`;
21207
+ }
21208
+ const subject = native.allDeclaredEncrypted ? "Every main-executable slice declares" : `${encrypted} of ${countLabel(declared, "slice")} declare`;
21209
+ return `${subject} encryption (\`cryptid != 0\`, FairPlay). Symbol and string tables remain readable; decompiling those regions does not yield trustworthy original code.`;
21210
+ }
21211
+ function renderSummary(input2) {
21212
+ const { target, artifact, counts, routeGroups, native, technology } = input2;
21213
+ const countOf = (type) => counts.get(type) ?? 0;
21214
+ const lines = ["## Summary", ""];
21215
+ const format = artifact ? FORMAT_LABEL[artifact.packageFormat] ?? artifact.packageFormat.toUpperCase() : void 0;
21216
+ const platform = PLATFORM_LABEL[target.targetRef.platform] ?? target.targetRef.platform;
21217
+ const identity = [
21218
+ `**${target.name}**`,
21219
+ `${platform} application`,
21220
+ target.version ? `version ${target.version}` : void 0,
21221
+ target.publisher ? `published by ${target.publisher}` : void 0
21222
+ ].filter(Boolean).join(", ");
21223
+ const article = format && /^[AEIOU]/i.test(format) ? "an" : "a";
21224
+ lines.push(
21225
+ `${identity}${/[.!?]$/.test(identity) ? "" : "."}${artifact ? ` The artifact is ${article} ${format} of ${formatBytes(artifact.sizeBytes)}, SHA-256 \`${artifact.sha256.slice(0, 12)}\u2026\`.` : " No artifact has been registered yet."}`,
21226
+ ""
21227
+ );
21228
+ const hasScriptSurface = countOf("route") > 0 || countOf("module") > 0 || countOf("asset") > 0;
21229
+ if (hasScriptSurface) {
21230
+ const surface = [
21231
+ countOf("route") > 0 ? `${countLabel(countOf("route"), "route")} in ${countLabel(routeGroups.length, "path group")}` : void 0,
21232
+ countOf("module") > 0 ? countLabel(countOf("module"), "JavaScript module") : void 0,
21233
+ countOf("asset") > 0 ? countLabel(countOf("asset"), "asset") : void 0,
21234
+ countLabel(countOf("network_endpoint"), "network endpoint candidate"),
21235
+ countOf("storage") > 0 ? countLabel(countOf("storage"), "storage entry") : void 0
21236
+ ].filter(Boolean);
21237
+ lines.push(`**Static surface.** ${surface.join(", ")}.`, "");
21238
+ if (countOf("network_endpoint") === 0) {
21239
+ lines.push(
21240
+ "No endpoint was classified. The analyzer reports absolute URLs found in inert JavaScript; a base URL assembled at runtime is not visible this way, so this is **not** evidence that the app makes no network calls.",
21241
+ ""
21242
+ );
21243
+ }
21244
+ } else {
21245
+ lines.push(
21246
+ "**Static surface.** No bundle script, route or module findings were recorded; the bundle inspection above is the complete recorded surface.",
21247
+ ""
21248
+ );
21249
+ }
21250
+ lines.push(
21251
+ `**Technology signals.** ${technology.length > 0 ? technology.join(" / ") : "none detected"} (static hints, not a build-tool claim).`,
21252
+ "",
21253
+ `**Encryption.** ${encryptionStatement(native, artifact?.packageFormat)}`,
21254
+ ""
21255
+ );
21256
+ if (native.inspected) {
21257
+ const indexed = [
21258
+ native.procedureCount !== void 0 ? countLabel(native.procedureCount, "procedure") : void 0,
21259
+ native.stringCount !== void 0 ? countLabel(native.stringCount, "string") : void 0
21260
+ ].filter(Boolean);
21261
+ if (indexed.length > 0) {
21262
+ lines.push(
21263
+ `**Native surface.** ${indexed.join(" and ")} indexed; \`search_native\` and \`decompile_function\` answer from this index.`,
21264
+ ""
21265
+ );
21266
+ }
21267
+ }
21268
+ const questions = [];
21269
+ if (countOf("route") > 0 || countOf("module") > 0) {
21270
+ questions.push(
21271
+ "Which routes and modules make up a feature, and where each one is defined in the bundle."
21272
+ );
21273
+ }
21274
+ if (native.inspected) {
21275
+ questions.push(
21276
+ "Which native symbols and strings match a name, and what one exact symbol decompiles to."
21277
+ );
21278
+ }
21279
+ if (technology.length > 0) {
21280
+ questions.push("Which framework produced the package and how it is laid out.");
21281
+ }
21282
+ if (questions.length > 0) {
21283
+ lines.push("**Questions this evidence can answer.**", "");
21284
+ for (const question of questions) lines.push(`- ${question}`);
21285
+ lines.push("");
21286
+ }
21287
+ lines.push(
21288
+ "**What it cannot establish.** Runtime behaviour, server-side logic, and code paths outside the recorded evidence; the app was not executed and no backend was contacted.",
21289
+ ""
21290
+ );
21291
+ return lines;
21292
+ }
21066
21293
  function renderReportMarkdown(input2) {
21067
21294
  const {
21068
21295
  target,
@@ -21076,6 +21303,20 @@ function renderReportMarkdown(input2) {
21076
21303
  const inspection = artifact?.inspection;
21077
21304
  const warnings = Array.isArray(inspection?.warnings) ? inspection.warnings.map(String) : [];
21078
21305
  const evidencePaths = collectEvidencePaths(jobs);
21306
+ const counts = /* @__PURE__ */ new Map();
21307
+ for (const [type, total] of Object.entries(input2.typeTotals ?? {})) {
21308
+ counts.set(type, total);
21309
+ }
21310
+ if (counts.size === 0) {
21311
+ for (const finding of findings) {
21312
+ counts.set(finding.type, (counts.get(finding.type) ?? 0) + 1);
21313
+ }
21314
+ }
21315
+ const technology = findings.filter((finding) => finding.type === "technology").map((finding) => finding.value);
21316
+ const routeGroups = groupRoutes(
21317
+ findings.filter((finding) => finding.type === "route").map((finding) => finding.value)
21318
+ );
21319
+ const native = nativeFacts(jobs);
21079
21320
  const lines = [
21080
21321
  `# ${target.name} \xB7 Analysis report`,
21081
21322
  "",
@@ -21085,6 +21326,14 @@ function renderReportMarkdown(input2) {
21085
21326
  `- Source: ${target.targetRef.sourceId} / ${target.targetRef.sourceRef}`,
21086
21327
  "- Scope: this report summarizes the local artifact, static inspection and job records. It does not describe runtime behavior.",
21087
21328
  "",
21329
+ ...renderSummary({
21330
+ target,
21331
+ artifact,
21332
+ counts,
21333
+ routeGroups,
21334
+ native,
21335
+ technology
21336
+ }),
21088
21337
  "## Target",
21089
21338
  "",
21090
21339
  "| Field | Value |",
@@ -21135,34 +21384,71 @@ function renderReportMarkdown(input2) {
21135
21384
  "| Type | Count |",
21136
21385
  "| --- | --- |"
21137
21386
  );
21138
- const byType = /* @__PURE__ */ new Map();
21139
- for (const finding of findings) {
21140
- byType.set(finding.type, (byType.get(finding.type) ?? 0) + 1);
21141
- }
21142
- if (byType.size === 0) {
21387
+ if (counts.size === 0) {
21143
21388
  lines.push("| \u2014 | 0 |");
21144
21389
  } else {
21145
- for (const [type, count] of [...byType.entries()].sort(
21390
+ for (const [type, count] of [...counts.entries()].sort(
21146
21391
  (a, b) => a[0].localeCompare(b[0])
21147
21392
  )) {
21148
21393
  lines.push(`| ${cell(type)} | ${count} |`);
21149
21394
  }
21150
21395
  }
21151
21396
  lines.push("");
21397
+ const itemLine = (item) => {
21398
+ const location = item.sourcePath ? ` \u2014 \`${item.sourcePath}\`${item.start ? `:${item.start.line}:${item.start.column}` : ""}` : "";
21399
+ return `- ${item.value}${location} (Evidence: \`${item.evidenceId}\`)`;
21400
+ };
21401
+ const routeItems = findings.filter((finding) => finding.type === "route");
21402
+ const routeTotal = counts.get("route") ?? routeItems.length;
21403
+ if (routeItems.length > 0) {
21404
+ lines.push(
21405
+ `### Routes (${routeTotal}, ${routeGroups.length} path groups)`,
21406
+ "",
21407
+ "Grouped by path prefix. A group holding more than half of the routes is split further, so one row cannot hide the rest.",
21408
+ "",
21409
+ "| Group | Routes | Example |",
21410
+ "| --- | --- | --- |"
21411
+ );
21412
+ const shown = routeGroups.slice(0, 25);
21413
+ for (const group of shown) {
21414
+ lines.push(
21415
+ `| ${cell(group.group)} | ${group.count} | \`${cell(group.example)}\` |`
21416
+ );
21417
+ }
21418
+ if (routeGroups.length > shown.length) {
21419
+ const rest = routeGroups.slice(shown.length).reduce((total, group) => total + group.count, 0);
21420
+ lines.push(
21421
+ `| (other ${routeGroups.length - shown.length} groups) | ${rest} | \u2014 |`
21422
+ );
21423
+ }
21424
+ lines.push(
21425
+ "",
21426
+ routeItems.length < routeTotal ? `The inventory below covers the ${routeItems.length} routed findings included in this report; the full set stays queryable with \`search_findings --types route\`.` : "The inventory below is complete for this artifact.",
21427
+ "",
21428
+ `<details><summary>All ${routeItems.length} routed findings</summary>`,
21429
+ ""
21430
+ );
21431
+ for (const item of routeItems) lines.push(itemLine(item));
21432
+ lines.push("", "</details>", "");
21433
+ }
21434
+ const endpointItems = findings.filter(
21435
+ (finding) => finding.type === "network_endpoint"
21436
+ );
21437
+ if (endpointItems.length > 0) {
21438
+ lines.push("### Network endpoint candidates", "");
21439
+ for (const item of endpointItems) lines.push(itemLine(item));
21440
+ lines.push("");
21441
+ }
21152
21442
  for (const [type, title] of [
21153
- ["route", "Routes"],
21154
- ["network_endpoint", "Network endpoint candidates"],
21155
21443
  ["module", "JavaScript modules"],
21444
+ ["storage", "Storage entries"],
21156
21445
  ["native_procedure", "Native symbols"],
21157
21446
  ["risk", "Risks and limitations"]
21158
21447
  ]) {
21159
- const items = findings.filter((finding) => finding.type === type).slice(0, 20);
21448
+ const items = findings.filter((finding) => finding.type === type).slice(0, 40);
21160
21449
  if (items.length === 0) continue;
21161
21450
  lines.push(`### ${title}`, "");
21162
- for (const item of items) {
21163
- const location = item.sourcePath ? ` \u2014 \`${item.sourcePath}\`${item.start ? `:${item.start.line}:${item.start.column}` : ""}` : "";
21164
- lines.push(`- ${item.value}${location} (Evidence: \`${item.evidenceId}\`)`);
21165
- }
21451
+ for (const item of items) lines.push(itemLine(item));
21166
21452
  lines.push("");
21167
21453
  }
21168
21454
  const analysisGaps = findings.filter((finding) => finding.type === "limitation").slice(0, 20);
@@ -22199,13 +22485,22 @@ var CapabilityJobRunner = class {
22199
22485
  "risk",
22200
22486
  "limitation"
22201
22487
  ];
22488
+ const limits = { route: 200, module: 200 };
22202
22489
  const findings = findingTypes.flatMap(
22203
22490
  (type) => this.store.searchFindings({
22204
22491
  targetId: target.id,
22205
22492
  types: [type],
22206
- limit: 20
22493
+ limit: limits[type] ?? 40
22207
22494
  }).items
22208
22495
  );
22496
+ const typeTotals = {};
22497
+ for (const type of findingTypes) {
22498
+ typeTotals[type] = this.store.searchFindings({
22499
+ targetId: target.id,
22500
+ types: [type],
22501
+ limit: 1
22502
+ }).total;
22503
+ }
22209
22504
  const findingTotal = this.store.searchFindings({
22210
22505
  targetId: target.id,
22211
22506
  limit: 1
@@ -22213,9 +22508,13 @@ var CapabilityJobRunner = class {
22213
22508
  const markdown = renderReportMarkdown({
22214
22509
  target,
22215
22510
  artifact,
22216
- jobs: this.store.listJobs(target.id).filter((item) => item.id !== job.id).slice(0, 20),
22511
+ jobs: selectReportJobs(
22512
+ this.store.listJobs(target.id).filter((item) => item.id !== job.id),
22513
+ 20
22514
+ ),
22217
22515
  findings,
22218
22516
  findingTotal,
22517
+ typeTotals,
22219
22518
  hostPlatform: this.config.hostPlatform,
22220
22519
  generatedAt
22221
22520
  });
@@ -22437,7 +22736,9 @@ var FREE_FEATURES = /* @__PURE__ */ new Set([
22437
22736
  "inspect:pe",
22438
22737
  "extract:app",
22439
22738
  "javascript:analyze",
22440
- "generate:report"
22739
+ "generate:report",
22740
+ "native",
22741
+ "compare"
22441
22742
  ]);
22442
22743
  function decode(value) {
22443
22744
  return Buffer.from(value, "base64url");
@@ -22723,11 +23024,11 @@ function evidenceFromRow(row) {
22723
23024
  };
22724
23025
  }
22725
23026
  function findingFromRow(row) {
22726
- const numberValue = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
22727
- const startLine = numberValue(row.start_line);
22728
- const startColumn = numberValue(row.start_column);
22729
- const endLine = numberValue(row.end_line);
22730
- const endColumn = numberValue(row.end_column);
23027
+ const numberValue2 = (value) => typeof value === "number" && Number.isFinite(value) ? value : void 0;
23028
+ const startLine = numberValue2(row.start_line);
23029
+ const startColumn = numberValue2(row.start_column);
23030
+ const endLine = numberValue2(row.end_line);
23031
+ const endColumn = numberValue2(row.end_column);
22731
23032
  return {
22732
23033
  id: String(row.id),
22733
23034
  targetId: String(row.target_id),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zhuixin/engine",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "private": false,
5
5
  "license": "UNLICENSED",
6
6
  "description": "AppTrace analysis engine (compiled)",