@nathapp/nax 0.73.0-canary.1 → 0.73.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/dist/nax.js +93 -59
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -36839,8 +36839,7 @@ function resolvePytestBin(packageDir) {
|
|
|
36839
36839
|
}
|
|
36840
36840
|
function buildAcceptanceRunCommand(testPath, testFramework, commandOverride, packageDir) {
|
|
36841
36841
|
if (commandOverride) {
|
|
36842
|
-
|
|
36843
|
-
return resolved.trim().split(/\s+/);
|
|
36842
|
+
return commandOverride.trim().split(/\s+/).map((part) => part.replace(/\{\{files\}\}/g, testPath).replace(/\{\{file\}\}/g, testPath).replace(/\{\{FILE\}\}/g, testPath));
|
|
36844
36843
|
}
|
|
36845
36844
|
switch (testFramework?.toLowerCase()) {
|
|
36846
36845
|
case "vitest":
|
|
@@ -40291,14 +40290,21 @@ var init_autofix_test_writer_strategy = __esm(() => {
|
|
|
40291
40290
|
});
|
|
40292
40291
|
|
|
40293
40292
|
// src/operations/apply-test-edit-declarations.ts
|
|
40294
|
-
function applyTestEditDeclarations(findings, declarations, story,
|
|
40293
|
+
function applyTestEditDeclarations(findings, declarations, story, opts = {}) {
|
|
40294
|
+
const { invalidMockStructure, allowTestRetag = true } = opts;
|
|
40295
40295
|
let result = [...findings];
|
|
40296
|
-
const
|
|
40296
|
+
const diagnostics = [];
|
|
40297
40297
|
for (const d of declarations) {
|
|
40298
40298
|
if (d.reason === "prd_contract") {
|
|
40299
40299
|
const prdQuote = d.prdQuote ?? "";
|
|
40300
40300
|
const valid = validatePrdQuote(prdQuote, story);
|
|
40301
|
-
if (valid) {
|
|
40301
|
+
if (!valid) {
|
|
40302
|
+
diagnostics.push({
|
|
40303
|
+
reason: "prd_quote_mismatch",
|
|
40304
|
+
file: d.file,
|
|
40305
|
+
detail: `PRD quote not found verbatim in story text for file: ${d.file}`
|
|
40306
|
+
});
|
|
40307
|
+
} else if (allowTestRetag) {
|
|
40302
40308
|
result = result.map((f) => {
|
|
40303
40309
|
const eligible = f.file === d.file && (f.fixTarget === "source" || f.fixTarget == null && f.source === "test-runner");
|
|
40304
40310
|
if (eligible) {
|
|
@@ -40313,31 +40319,18 @@ function applyTestEditDeclarations(findings, declarations, story, invalidMockStr
|
|
|
40313
40319
|
}
|
|
40314
40320
|
return f;
|
|
40315
40321
|
});
|
|
40316
|
-
} else {
|
|
40317
|
-
advisories.push({
|
|
40318
|
-
source: "autofix",
|
|
40319
|
-
severity: "warning",
|
|
40320
|
-
category: "prd_quote_mismatch",
|
|
40321
|
-
message: `PRD quote not found verbatim in story text for file: ${d.file}`,
|
|
40322
|
-
file: d.file,
|
|
40323
|
-
fixTarget: "source"
|
|
40324
|
-
});
|
|
40325
40322
|
}
|
|
40326
40323
|
}
|
|
40327
40324
|
}
|
|
40328
|
-
|
|
40329
|
-
|
|
40330
|
-
|
|
40331
|
-
|
|
40332
|
-
|
|
40333
|
-
|
|
40334
|
-
|
|
40335
|
-
message: `Mock structure handoff references file that does not exist or is not a test file: ${fileList}`,
|
|
40336
|
-
fixTarget: "source"
|
|
40337
|
-
});
|
|
40338
|
-
}
|
|
40325
|
+
for (const d of invalidMockStructure ?? []) {
|
|
40326
|
+
const fileList = (d.files ?? [d.file]).join(", ");
|
|
40327
|
+
diagnostics.push({
|
|
40328
|
+
reason: "mock_structure_invalid_files",
|
|
40329
|
+
file: d.file,
|
|
40330
|
+
detail: `Mock structure handoff references file that does not exist or is not a test file: ${fileList}`
|
|
40331
|
+
});
|
|
40339
40332
|
}
|
|
40340
|
-
return
|
|
40333
|
+
return { findings: result, diagnostics };
|
|
40341
40334
|
}
|
|
40342
40335
|
var init_apply_test_edit_declarations = __esm(() => {
|
|
40343
40336
|
init_test_edit_declaration();
|
|
@@ -41637,12 +41630,14 @@ async function runFixCycle(cycle, ctx, cycleName, _deps = {}) {
|
|
|
41637
41630
|
}
|
|
41638
41631
|
const active = selectActiveStrategies(cycle.strategies, cycle.findings, cycle.verdict);
|
|
41639
41632
|
if (active.length === 0) {
|
|
41640
|
-
|
|
41633
|
+
const orphanSources = [...new Set(cycle.findings.map((f) => f.source))];
|
|
41634
|
+
logger?.warn("findings.cycle", "cycle exited \u2014 no matching strategy (orphaned findings)", {
|
|
41641
41635
|
storyId,
|
|
41642
41636
|
packageDir,
|
|
41643
41637
|
cycleName,
|
|
41644
41638
|
reason: "no-strategy",
|
|
41645
|
-
findingsCount: cycle.findings.length
|
|
41639
|
+
findingsCount: cycle.findings.length,
|
|
41640
|
+
orphanSources
|
|
41646
41641
|
});
|
|
41647
41642
|
return {
|
|
41648
41643
|
iterations: cycle.iterations,
|
|
@@ -58427,6 +58422,20 @@ import { join as join50 } from "path";
|
|
|
58427
58422
|
function requiresInitialRefCapture(strategy) {
|
|
58428
58423
|
return isThreeSessionStrategy(strategy);
|
|
58429
58424
|
}
|
|
58425
|
+
function logRejectedDeclarations(diagnostics, ctx) {
|
|
58426
|
+
if (diagnostics.length === 0)
|
|
58427
|
+
return;
|
|
58428
|
+
const logger = getSafeLogger();
|
|
58429
|
+
for (const d of diagnostics) {
|
|
58430
|
+
logger?.warn("findings.cycle", "test-edit declaration rejected \u2014 ignoring declaration", {
|
|
58431
|
+
storyId: ctx.storyId,
|
|
58432
|
+
packageDir: ctx.packageDir,
|
|
58433
|
+
reason: d.reason,
|
|
58434
|
+
file: d.file,
|
|
58435
|
+
detail: d.detail
|
|
58436
|
+
});
|
|
58437
|
+
}
|
|
58438
|
+
}
|
|
58430
58439
|
function isFreshRun(story) {
|
|
58431
58440
|
const hasAttempts = (story.attempts ?? 0) > 0;
|
|
58432
58441
|
const hasReviewEscalation = (story.priorFailures ?? []).some((f) => f.stage === "review");
|
|
@@ -58498,7 +58507,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
58498
58507
|
strategies.push(makeAutofixTestWriterStrategy(story, config2, sink));
|
|
58499
58508
|
}
|
|
58500
58509
|
}
|
|
58501
|
-
const postValidate = async (findings,
|
|
58510
|
+
const postValidate = async (findings, validateCtx) => {
|
|
58502
58511
|
if (sink.testEdits.length === 0 && sink.mockHandoffs.length === 0)
|
|
58503
58512
|
return findings;
|
|
58504
58513
|
const pendingMock = sink.mockHandoffs.map((h) => ({
|
|
@@ -58511,7 +58520,12 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
58511
58520
|
sink.mockHandoffs = valid.map((d) => ({ files: d.files ?? [], reasonDetail: d.reasonDetail ?? "" }));
|
|
58512
58521
|
const allDeclarations = [...sink.testEdits, ...valid];
|
|
58513
58522
|
sink.testEdits = [];
|
|
58514
|
-
|
|
58523
|
+
const applied = applyTestEditDeclarations(findings, allDeclarations, story, {
|
|
58524
|
+
invalidMockStructure: invalid,
|
|
58525
|
+
allowTestRetag: isThreeSession
|
|
58526
|
+
});
|
|
58527
|
+
logRejectedDeclarations(applied.diagnostics, validateCtx);
|
|
58528
|
+
return applied.findings;
|
|
58515
58529
|
};
|
|
58516
58530
|
const rectOpts = {
|
|
58517
58531
|
...inputs.rectification,
|
|
@@ -58551,7 +58565,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
58551
58565
|
}));
|
|
58552
58566
|
}
|
|
58553
58567
|
nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2, nbSink));
|
|
58554
|
-
const nbPostValidate = async (findings,
|
|
58568
|
+
const nbPostValidate = async (findings, validateCtx) => {
|
|
58555
58569
|
if (nbSink.testEdits.length === 0 && nbSink.mockHandoffs.length === 0)
|
|
58556
58570
|
return findings;
|
|
58557
58571
|
const pendingMock = nbSink.mockHandoffs.map((h) => ({
|
|
@@ -58564,7 +58578,12 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
58564
58578
|
nbSink.mockHandoffs = valid.map((d) => ({ files: d.files ?? [], reasonDetail: d.reasonDetail ?? "" }));
|
|
58565
58579
|
const allDeclarations = [...nbSink.testEdits, ...valid];
|
|
58566
58580
|
nbSink.testEdits = [];
|
|
58567
|
-
|
|
58581
|
+
const applied = applyTestEditDeclarations(findings, allDeclarations, story, {
|
|
58582
|
+
invalidMockStructure: invalid,
|
|
58583
|
+
allowTestRetag: isThreeSession
|
|
58584
|
+
});
|
|
58585
|
+
logRejectedDeclarations(applied.diagnostics, validateCtx);
|
|
58586
|
+
return applied.findings;
|
|
58568
58587
|
};
|
|
58569
58588
|
builder.addNonBlockingFix(nbf, nbStrategies, nbPostValidate);
|
|
58570
58589
|
}
|
|
@@ -58572,6 +58591,7 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
58572
58591
|
}
|
|
58573
58592
|
var init_build_plan_for_strategy = __esm(() => {
|
|
58574
58593
|
init_config();
|
|
58594
|
+
init_logger2();
|
|
58575
58595
|
init_operations();
|
|
58576
58596
|
init_execution_gates();
|
|
58577
58597
|
init_full_suite_rectify();
|
|
@@ -59183,6 +59203,17 @@ async function decideStageAction(ctx, planResult, inspection, opts) {
|
|
|
59183
59203
|
ctx.tddFailureCategory = failureCategory;
|
|
59184
59204
|
}
|
|
59185
59205
|
if (planResult.rectificationExhausted && planResult.unfixedFindings && planResult.unfixedFindings.length > 0) {
|
|
59206
|
+
const blockingThreshold = ctx.config?.review?.blockingThreshold ?? "error";
|
|
59207
|
+
const blockingUnfixed = planResult.unfixedFindings.filter((f) => isBlockingSeverity(f.severity ?? "error", blockingThreshold));
|
|
59208
|
+
if (blockingUnfixed.length === 0) {
|
|
59209
|
+
logger.warn("execution", "Rectification exhausted but all unfixed findings are advisory (below blocking threshold) \u2014 proceeding", {
|
|
59210
|
+
storyId: ctx.story.id,
|
|
59211
|
+
blockingThreshold,
|
|
59212
|
+
unfixedCount: planResult.unfixedFindings.length,
|
|
59213
|
+
unfixedSources: [...new Set(planResult.unfixedFindings.map((f) => f.source))]
|
|
59214
|
+
});
|
|
59215
|
+
return { action: "continue" };
|
|
59216
|
+
}
|
|
59186
59217
|
const sources = new Set(planResult.unfixedFindings.map((f) => f.source));
|
|
59187
59218
|
const allMechanical = [...sources].every((s) => s === "lint" || s === "typecheck");
|
|
59188
59219
|
if (allMechanical) {
|
|
@@ -59344,6 +59375,7 @@ var init_post_run = __esm(() => {
|
|
|
59344
59375
|
init_operations();
|
|
59345
59376
|
init_execution_helpers();
|
|
59346
59377
|
init_quality();
|
|
59378
|
+
init_severity();
|
|
59347
59379
|
init_scratch_writer();
|
|
59348
59380
|
init_rollback();
|
|
59349
59381
|
init_git();
|
|
@@ -59925,6 +59957,8 @@ var init_queue_handler = __esm(() => {
|
|
|
59925
59957
|
import path15 from "path";
|
|
59926
59958
|
var queueCheckStage;
|
|
59927
59959
|
var init_queue_check = __esm(() => {
|
|
59960
|
+
init_config();
|
|
59961
|
+
init_errors();
|
|
59928
59962
|
init_queue_handler();
|
|
59929
59963
|
init_logger2();
|
|
59930
59964
|
init_prd();
|
|
@@ -59973,8 +60007,11 @@ var init_queue_check = __esm(() => {
|
|
|
59973
60007
|
continue;
|
|
59974
60008
|
}
|
|
59975
60009
|
if (cmd.type === "INJECT") {
|
|
59976
|
-
const storyFilePath = path15.isAbsolute(cmd.storyFile) ? cmd.storyFile : path15.join(ctx.workdir, cmd.storyFile);
|
|
59977
60010
|
try {
|
|
60011
|
+
if (path15.isAbsolute(cmd.storyFile)) {
|
|
60012
|
+
throw new NaxError(`INJECT storyFile must be a relative path within the workspace: ${cmd.storyFile}`, "INJECT_PATH_ABSOLUTE", { stage: "queue-check", storyId: ctx.story?.id ?? "unknown", storyFile: cmd.storyFile });
|
|
60013
|
+
}
|
|
60014
|
+
const storyFilePath = validateFilePath(path15.join(ctx.workdir, cmd.storyFile), ctx.workdir);
|
|
59978
60015
|
const raw = await Bun.file(storyFilePath).json();
|
|
59979
60016
|
const existingIds = new Set(ctx.prd.userStories.map((s) => s.id));
|
|
59980
60017
|
const story = validateInjectedStory(raw, existingIds);
|
|
@@ -63447,13 +63484,9 @@ var init_loader4 = __esm(() => {
|
|
|
63447
63484
|
});
|
|
63448
63485
|
|
|
63449
63486
|
// src/execution/checkpoint/writer.ts
|
|
63450
|
-
import {
|
|
63487
|
+
import { appendFile as appendFile4 } from "fs/promises";
|
|
63451
63488
|
async function defaultAppend(filePath, line) {
|
|
63452
|
-
|
|
63453
|
-
const existing = await file3.exists() ? await file3.text() : "";
|
|
63454
|
-
const tmpPath = `${filePath}.tmp`;
|
|
63455
|
-
await Bun.write(tmpPath, existing + line);
|
|
63456
|
-
await rename(tmpPath, filePath);
|
|
63489
|
+
await appendFile4(filePath, line, "utf8");
|
|
63457
63490
|
}
|
|
63458
63491
|
function createCheckpointWriter(filePath, runId) {
|
|
63459
63492
|
return new CheckpointWriter({ filePath, runId, _deps: { append: defaultAppend } });
|
|
@@ -63554,15 +63587,8 @@ async function loadCheckpoints(featureDir, options = { _deps: { read: defaultRea
|
|
|
63554
63587
|
break;
|
|
63555
63588
|
}
|
|
63556
63589
|
}
|
|
63557
|
-
let latestRunId;
|
|
63558
|
-
for (const r of validRecords) {
|
|
63559
|
-
latestRunId = maxRunId(latestRunId, r.runId);
|
|
63560
|
-
}
|
|
63561
|
-
if (latestRunId === undefined)
|
|
63562
|
-
return new Map;
|
|
63563
|
-
const latest = validRecords.filter((r) => r.runId === latestRunId);
|
|
63564
63590
|
const grouped = new Map;
|
|
63565
|
-
for (const r of
|
|
63591
|
+
for (const r of validRecords) {
|
|
63566
63592
|
const list = grouped.get(r.storyId);
|
|
63567
63593
|
if (list) {
|
|
63568
63594
|
list.push(r);
|
|
@@ -63570,6 +63596,13 @@ async function loadCheckpoints(featureDir, options = { _deps: { read: defaultRea
|
|
|
63570
63596
|
grouped.set(r.storyId, [r]);
|
|
63571
63597
|
}
|
|
63572
63598
|
}
|
|
63599
|
+
for (const [storyId, records] of grouped) {
|
|
63600
|
+
let latestRunId;
|
|
63601
|
+
for (const r of records) {
|
|
63602
|
+
latestRunId = maxRunId(latestRunId, r.runId);
|
|
63603
|
+
}
|
|
63604
|
+
grouped.set(storyId, records.filter((r) => r.runId === latestRunId));
|
|
63605
|
+
}
|
|
63573
63606
|
const result = new Map;
|
|
63574
63607
|
for (const [storyId, records] of grouped) {
|
|
63575
63608
|
const seen = new Set;
|
|
@@ -63842,7 +63875,7 @@ var package_default;
|
|
|
63842
63875
|
var init_package = __esm(() => {
|
|
63843
63876
|
package_default = {
|
|
63844
63877
|
name: "@nathapp/nax",
|
|
63845
|
-
version: "0.73.
|
|
63878
|
+
version: "0.73.1",
|
|
63846
63879
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
63847
63880
|
type: "module",
|
|
63848
63881
|
bin: {
|
|
@@ -63943,8 +63976,8 @@ var init_version = __esm(() => {
|
|
|
63943
63976
|
NAX_VERSION = package_default.version;
|
|
63944
63977
|
NAX_COMMIT = (() => {
|
|
63945
63978
|
try {
|
|
63946
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
63947
|
-
return "
|
|
63979
|
+
if (/^[0-9a-f]{6,10}$/.test("73c95c44"))
|
|
63980
|
+
return "73c95c44";
|
|
63948
63981
|
} catch {}
|
|
63949
63982
|
try {
|
|
63950
63983
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -64850,7 +64883,7 @@ var init_acceptance_loop = __esm(() => {
|
|
|
64850
64883
|
});
|
|
64851
64884
|
|
|
64852
64885
|
// src/session/scratch-purge.ts
|
|
64853
|
-
import { mkdir as mkdir12, rename
|
|
64886
|
+
import { mkdir as mkdir12, rename, rm } from "fs/promises";
|
|
64854
64887
|
import { dirname as dirname13, join as join84 } from "path";
|
|
64855
64888
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
64856
64889
|
const sessionsDir = join84(projectDir, ".nax", "features", featureName, "sessions");
|
|
@@ -64903,7 +64936,7 @@ var init_scratch_purge = __esm(() => {
|
|
|
64903
64936
|
remove: (path24) => rm(path24, { recursive: true, force: true }),
|
|
64904
64937
|
move: async (src, dest) => {
|
|
64905
64938
|
await mkdir12(dirname13(dest), { recursive: true });
|
|
64906
|
-
await
|
|
64939
|
+
await rename(src, dest);
|
|
64907
64940
|
},
|
|
64908
64941
|
now: () => Date.now()
|
|
64909
64942
|
};
|
|
@@ -66053,7 +66086,7 @@ var init_ensure_package_dirs = __esm(() => {
|
|
|
66053
66086
|
});
|
|
66054
66087
|
|
|
66055
66088
|
// src/pipeline/subscribers/events-writer.ts
|
|
66056
|
-
import { appendFile as
|
|
66089
|
+
import { appendFile as appendFile5, mkdir as mkdir13 } from "fs/promises";
|
|
66057
66090
|
import { basename as basename15, join as join85 } from "path";
|
|
66058
66091
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
66059
66092
|
const logger = getSafeLogger();
|
|
@@ -66068,7 +66101,7 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
66068
66101
|
await mkdir13(eventsDir, { recursive: true });
|
|
66069
66102
|
dirReady = true;
|
|
66070
66103
|
}
|
|
66071
|
-
await
|
|
66104
|
+
await appendFile5(eventsFile, `${JSON.stringify(line)}
|
|
66072
66105
|
`);
|
|
66073
66106
|
} catch (err) {
|
|
66074
66107
|
logger?.warn("events-writer", "Failed to write event line (non-fatal)", {
|
|
@@ -69031,7 +69064,7 @@ var init_runner_execution = __esm(() => {
|
|
|
69031
69064
|
});
|
|
69032
69065
|
|
|
69033
69066
|
// src/execution/status-file.ts
|
|
69034
|
-
import { rename as
|
|
69067
|
+
import { rename as rename2, unlink as unlink3 } from "fs/promises";
|
|
69035
69068
|
import { resolve as resolve16 } from "path";
|
|
69036
69069
|
function countProgress(prd) {
|
|
69037
69070
|
const stories = prd.userStories;
|
|
@@ -69086,7 +69119,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
69086
69119
|
await unlink3(tmpPath);
|
|
69087
69120
|
} catch {}
|
|
69088
69121
|
await Bun.write(tmpPath, JSON.stringify(status, null, 2));
|
|
69089
|
-
await
|
|
69122
|
+
await rename2(tmpPath, resolvedPath);
|
|
69090
69123
|
}
|
|
69091
69124
|
var init_status_file = () => {};
|
|
69092
69125
|
|
|
@@ -69242,7 +69275,7 @@ __export(exports_migrate, {
|
|
|
69242
69275
|
detectGeneratedContent: () => detectGeneratedContent
|
|
69243
69276
|
});
|
|
69244
69277
|
import { existsSync as existsSync36 } from "fs";
|
|
69245
|
-
import { mkdir as mkdir16, readdir as readdir5, rename as
|
|
69278
|
+
import { mkdir as mkdir16, readdir as readdir5, rename as rename3 } from "fs/promises";
|
|
69246
69279
|
import path28 from "path";
|
|
69247
69280
|
async function detectGeneratedContent(naxDir) {
|
|
69248
69281
|
if (!existsSync36(naxDir))
|
|
@@ -69331,7 +69364,7 @@ async function migrateCommand(options) {
|
|
|
69331
69364
|
const archiveBase = path28.join(globalConfigDir(), "_archive");
|
|
69332
69365
|
const archiveDest = path28.join(archiveBase, `${options.reclaim}-${Date.now()}`);
|
|
69333
69366
|
await mkdir16(archiveBase, { recursive: true });
|
|
69334
|
-
await
|
|
69367
|
+
await rename3(src, archiveDest);
|
|
69335
69368
|
logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
|
|
69336
69369
|
return;
|
|
69337
69370
|
}
|
|
@@ -69410,7 +69443,7 @@ async function migrateCommand(options) {
|
|
|
69410
69443
|
Remove the destination or run nax migrate --dry-run to inspect.`, "MIGRATE_CONFLICT", { stage: "migrate", src: candidate.srcPath, dest });
|
|
69411
69444
|
}
|
|
69412
69445
|
try {
|
|
69413
|
-
await
|
|
69446
|
+
await rename3(candidate.srcPath, dest);
|
|
69414
69447
|
} catch (err) {
|
|
69415
69448
|
const isXdev = err instanceof Error && "code" in err && err.code === "EXDEV";
|
|
69416
69449
|
if (isXdev) {
|
|
@@ -102467,6 +102500,7 @@ async function resolveFeatureAcceptance(featureName, workdir) {
|
|
|
102467
102500
|
testPath: relative15(repoRoot, g.testPath),
|
|
102468
102501
|
exists: await Bun.file(g.testPath).exists(),
|
|
102469
102502
|
command,
|
|
102503
|
+
cwd: packageDir,
|
|
102470
102504
|
language: g.language
|
|
102471
102505
|
};
|
|
102472
102506
|
}));
|