@neocompose/cli 0.50.6 → 0.50.8

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/neo.mjs CHANGED
@@ -41,6 +41,31 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
41
41
  mod
42
42
  ));
43
43
 
44
+ // src/performance.ts
45
+ function startPerformanceSpan(phase, fields = {}) {
46
+ if (process.env.NEO_COMPOSE_TRACE_PERFORMANCE !== "1") return () => {
47
+ };
48
+ const started = performance.now();
49
+ return () => recordPerformance(phase, performance.now() - started, fields);
50
+ }
51
+ function recordPerformance(phase, durationMs, fields = {}) {
52
+ if (process.env.NEO_COMPOSE_TRACE_PERFORMANCE !== "1") return;
53
+ console.error(
54
+ JSON.stringify({
55
+ type: "neo-performance",
56
+ phase,
57
+ ...fields,
58
+ startedMs: Math.round(performance.now() - durationMs),
59
+ durationMs: Math.round(durationMs)
60
+ })
61
+ );
62
+ }
63
+ var init_performance = __esm({
64
+ "src/performance.ts"() {
65
+ "use strict";
66
+ }
67
+ });
68
+
44
69
  // src/project-source/workspace-source-path.ts
45
70
  import { existsSync, lstatSync, realpathSync } from "node:fs";
46
71
  import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
@@ -34757,7 +34782,9 @@ __export(token_store_exports, {
34757
34782
  keychainAccount: () => keychainAccount,
34758
34783
  loadCredential: () => loadCredential,
34759
34784
  loadToken: () => loadToken,
34760
- saveCredential: () => saveCredential
34785
+ loadTrustedConvexSite: () => loadTrustedConvexSite,
34786
+ saveCredential: () => saveCredential,
34787
+ saveTrustedConvexSite: () => saveTrustedConvexSite
34761
34788
  });
34762
34789
  import {
34763
34790
  chmodSync,
@@ -34884,6 +34911,22 @@ function loadCredential(apiBaseUrl) {
34884
34911
  const file = readCredentialsFile();
34885
34912
  return file.credentials[apiBaseUrl] ?? null;
34886
34913
  }
34914
+ function loadTrustedConvexSite(apiBaseUrl) {
34915
+ return readCredentialsFile().convexSites?.[apiBaseUrl] ?? null;
34916
+ }
34917
+ function saveTrustedConvexSite(apiBaseUrl, siteUrl) {
34918
+ const file = readCredentialsFile();
34919
+ file.convexSites ??= {};
34920
+ file.convexSites[apiBaseUrl] = siteUrl;
34921
+ mkdirSync2(credentialsDir(), { recursive: true, mode: 448 });
34922
+ const path = credentialsPath();
34923
+ writeFileSync2(path, `${JSON.stringify(file, null, 2)}
34924
+ `, {
34925
+ encoding: "utf8",
34926
+ mode: 384
34927
+ });
34928
+ chmodSync(path, 384);
34929
+ }
34887
34930
  function deleteCredential(apiBaseUrl) {
34888
34931
  const account = keychainAccount(apiBaseUrl);
34889
34932
  const fromKeychain = keychainGet(account);
@@ -34892,11 +34935,12 @@ function deleteCredential(apiBaseUrl) {
34892
34935
  const token = fromKeychain ?? (fileEntry !== void 0 && fileEntry.token !== "" ? fileEntry.token : null);
34893
34936
  const deletedKeychainEntry = keychainDelete(account);
34894
34937
  let deletedFileEntry = false;
34895
- if (fileEntry !== void 0) {
34938
+ if (fileEntry !== void 0 || file.convexSites?.[apiBaseUrl] !== void 0) {
34896
34939
  delete file.credentials[apiBaseUrl];
34897
- deletedFileEntry = true;
34940
+ if (file.convexSites !== void 0) delete file.convexSites[apiBaseUrl];
34941
+ deletedFileEntry = fileEntry !== void 0;
34898
34942
  const path = credentialsPath();
34899
- if (Object.keys(file.credentials).length === 0) {
34943
+ if (Object.keys(file.credentials).length === 0 && Object.keys(file.convexSites ?? {}).length === 0) {
34900
34944
  rmSync(path, { force: true });
34901
34945
  } else {
34902
34946
  writeFileSync2(path, `${JSON.stringify(file, null, 2)}
@@ -76470,7 +76514,13 @@ function virtualExpandedRecordForReceiver(receiver, ctx) {
76470
76514
  if (rowId === null) return null;
76471
76515
  const rows = ctx.vm.databaseVM?.virtualInstanceRowsForValue(rowId);
76472
76516
  if (rows === void 0 || rows.length === 0) return null;
76473
- const expanded = rows.find((row) => row.id === rowId);
76517
+ ctx.__virtualRowsById ??= /* @__PURE__ */ new WeakMap();
76518
+ let rowsById = ctx.__virtualRowsById.get(rows);
76519
+ if (rowsById === void 0) {
76520
+ rowsById = new Map(rows.map((row) => [row.id, row]));
76521
+ ctx.__virtualRowsById.set(rows, rowsById);
76522
+ }
76523
+ const expanded = rowsById.get(rowId);
76474
76524
  if (expanded === void 0) return null;
76475
76525
  const record4 = expanded.value;
76476
76526
  if (typeof record4 !== "object" || record4 === null || Array.isArray(record4)) {
@@ -79022,7 +79072,7 @@ function evaluateInitializerInContext(init, member, ctx, createdValues, argument
79022
79072
  if (isMemberLookupBase(member)) {
79023
79073
  return encodeLookupInitializerResult(member, result.value, created, ctx);
79024
79074
  }
79025
- const rootRow = initializerRootRow(result.value, created);
79075
+ let rootRow = initializerRootRow(result.value, created);
79026
79076
  for (const row of created) {
79027
79077
  if (row === rootRow) continue;
79028
79078
  createdValues.push(row);
@@ -79038,11 +79088,36 @@ function evaluateInitializerInContext(init, member, ctx, createdValues, argument
79038
79088
  existingValueRow
79039
79089
  };
79040
79090
  }
79041
- return { value: result.value, classId: existingRuntimeClassId };
79091
+ if (existingValueRow !== null && existingRuntimeClassId !== null && isMemberClassBase(member)) {
79092
+ const storage = constructorArgumentStorage(existingValueRow.id, ctx);
79093
+ if (storage !== 3 /* Session */) {
79094
+ const checkpoint = sessionCreationCheckpoint(ctx);
79095
+ const value = cloneClassValueGraph(
79096
+ result.value,
79097
+ existingRuntimeClassId,
79098
+ ctx
79099
+ );
79100
+ const importedRows = createdSessionValuesSince(ctx, checkpoint);
79101
+ const imported = initializerRootRow(value, importedRows);
79102
+ if (imported === null) {
79103
+ throw new NSGetterRuntimeError(
79104
+ `Initializer '${member.name}' imported a Class without a backing row.`
79105
+ );
79106
+ }
79107
+ rootRow = imported;
79108
+ result = { ...result, value };
79109
+ for (const row of importedRows) {
79110
+ if (row !== rootRow) createdValues.push(row);
79111
+ }
79112
+ }
79113
+ }
79114
+ if (rootRow === null) {
79115
+ return { value: result.value, classId: existingRuntimeClassId };
79116
+ }
79042
79117
  }
79043
79118
  deleteRuntimeSessionValue(ctx, rootRow.id);
79044
79119
  ctx.__executionState?.constructorGroups.delete(rootRow.id);
79045
- const constructorArgs = typeof result.value === "object" && result.value !== null ? ctx.__constructedArgumentsByValue.get(result.value) : void 0;
79120
+ const constructorArgs = typeof result.value === "object" && result.value !== null ? ctx.__constructedArgumentsByValue.get(result.value) ?? rootRow.constructorArgs ?? void 0 : void 0;
79046
79121
  const provenance = pickInstanceProvenance({
79047
79122
  constructorArgs,
79048
79123
  instanceConstructorId: rootRow.instanceConstructorId,
@@ -80420,7 +80495,11 @@ function cloneConstructorArgumentGraph(args) {
80420
80495
  );
80421
80496
  const record4 = {};
80422
80497
  clone.value = record4;
80423
- for (const [schemaKey, childId] of Object.entries(source.value)) {
80498
+ const sourceRecord = {
80499
+ ...virtualExpandedRecordForReceiver(source.value, args.ctx),
80500
+ ...source.value
80501
+ };
80502
+ for (const [schemaKey, childId] of Object.entries(sourceRecord)) {
80424
80503
  if (typeof childId !== "string") {
80425
80504
  throw new NSGetterRuntimeError(
80426
80505
  `Constructor import found a non-id Class field '${schemaKey}' on value '${source.id}'.`
@@ -80979,10 +81058,12 @@ function cloneClassValueGraph(receiver, expectedClassId, ctx) {
80979
81058
  registerRuntimeSessionValue(ctx, clone);
80980
81059
  clonedBySourceId.set(sourceRow.id, clone);
80981
81060
  if (typeof sourceRow.value === "object" && sourceRow.value !== null && !Array.isArray(sourceRow.value)) {
80982
- const sourceRecord = sourceRow.value;
80983
- const clonedRecord = { ...sourceRecord };
81061
+ const clonedRecord = {
81062
+ ...virtualExpandedRecordForReceiver(sourceRow.value, ctx),
81063
+ ...sourceRow.value
81064
+ };
80984
81065
  clone.value = clonedRecord;
80985
- for (const [key, childId] of Object.entries(sourceRecord)) {
81066
+ for (const [key, childId] of Object.entries(clonedRecord)) {
80986
81067
  if (typeof childId !== "string") continue;
80987
81068
  const childMember = sourceTypeInfo?.type === 5 /* Dictionary */ ? null : ownedObjectChildMember(sourceRow, sourceMember, key, ctx);
80988
81069
  const childTypeInfo = sourceTypeInfo?.type === 5 /* Dictionary */ ? sourceTypeInfo.entryTypeInfo : void 0;
@@ -82328,25 +82409,66 @@ function resolvedStoredInstanceRows(args) {
82328
82409
  const rows = new Map(document.values.map((row) => [row.id, row]));
82329
82410
  const materializedIndex = buildVirtualInstanceMaterializedIndex({ document });
82330
82411
  const queue = [{ root: args.instanceRoot, member: args.rootMember }];
82412
+ const visitedArguments = /* @__PURE__ */ new Set();
82413
+ const argumentRoots = (row) => {
82414
+ const roots = [];
82415
+ const collectArgument = (valueId, type) => {
82416
+ if (visitedArguments.has(valueId)) return;
82417
+ const row2 = materializedIndex.rowsById.get(valueId);
82418
+ if (row2 === void 0) return;
82419
+ visitedArguments.add(valueId);
82420
+ if (type.type === 7 /* Class */ || type.type === 22 /* Interface */) {
82421
+ const classId = row2.classId ?? (type.type === 7 /* Class */ ? type.classId : void 0);
82422
+ if (classId === void 0) return;
82423
+ roots.push({
82424
+ root: row2,
82425
+ member: {
82426
+ name: "Constructor argument",
82427
+ kind: 7 /* Class */,
82428
+ classId,
82429
+ requirement: 1 /* Required */
82430
+ }
82431
+ });
82432
+ } else if (type.type === 6 /* List */) {
82433
+ const children = materializedIndex.unorderedEntryIdsByContainerId.get(valueId);
82434
+ for (const childId of children ?? (Array.isArray(row2.value) ? row2.value : [])) {
82435
+ if (typeof childId === "string")
82436
+ collectArgument(childId, type.entryTypeInfo);
82437
+ }
82438
+ } else if (type.type === 5 /* Dictionary */) {
82439
+ for (const childId of Object.values(stringRecord2(row2.value)))
82440
+ collectArgument(childId, type.entryTypeInfo);
82441
+ }
82442
+ };
82443
+ for (const edge of materializedIndex.graph.constructorEdges(row)) {
82444
+ collectArgument(edge.targetValueId, edge.typeInfo);
82445
+ }
82446
+ return roots;
82447
+ };
82331
82448
  const stored = indexExpansion({
82332
82449
  document,
82333
82450
  instanceRoot: args.instanceRoot,
82334
82451
  rootMember: args.rootMember,
82335
82452
  expandedRoot: rows.get(args.instanceRoot.id) ?? args.instanceRoot,
82336
- expandedRows: document.values
82453
+ expandedRows: document.values,
82454
+ additionalRoots: argumentRoots
82337
82455
  });
82338
- for (const node of stored.nodesByPath.values()) {
82339
- if (node.expandedRow.id === args.instanceRoot.id) continue;
82340
- if (!isMemberClassBase(node.member)) continue;
82341
- if (!isStoredClassResolutionRoot(node.expandedRow)) continue;
82342
- queue.push({ root: node.expandedRow, member: node.member });
82343
- }
82456
+ const enqueueStoredRoots = (nodes) => {
82457
+ for (const node of nodes) {
82458
+ if (node.expandedRow.id === args.instanceRoot.id) continue;
82459
+ if (!isMemberClassBase(node.member)) continue;
82460
+ if (!isStoredClassResolutionRoot(node.expandedRow)) continue;
82461
+ queue.push({ root: node.expandedRow, member: node.member });
82462
+ }
82463
+ };
82464
+ enqueueStoredRoots(stored.nodesByPath.values());
82344
82465
  const resolvedRootIds = /* @__PURE__ */ new Set();
82345
82466
  const resolvedFrameByValueId = /* @__PURE__ */ new Map();
82346
- while (queue.length > 0) {
82347
- const next = queue.shift();
82467
+ for (let index = 0; index < queue.length; index += 1) {
82468
+ const next = queue[index];
82348
82469
  if (next === void 0 || resolvedRootIds.has(next.root.id)) continue;
82349
82470
  resolvedRootIds.add(next.root.id);
82471
+ enqueueStoredRoots(stored.includeRoot(next.root, next.member));
82350
82472
  const expanded = expandStoredClassValue({
82351
82473
  evaluateInitializer: args.evaluateInitializer,
82352
82474
  document,
@@ -83226,6 +83348,7 @@ function indexExpansion(args) {
83226
83348
  if (paths === void 0) childPathsByParentPath.set(parentPath, [pathKey]);
83227
83349
  else paths.push(pathKey);
83228
83350
  };
83351
+ let discoveredNodes = null;
83229
83352
  const visit = (rowId, rawMember, pathKey, parentPath, parentSegment, genericEnv) => {
83230
83353
  const row = rowsByExpandedId.get(rowId);
83231
83354
  if (row === void 0 || nodesByExpandedId.has(rowId)) return;
@@ -83260,6 +83383,7 @@ function indexExpansion(args) {
83260
83383
  virtualId
83261
83384
  };
83262
83385
  nodesByExpandedId.set(rowId, node);
83386
+ discoveredNodes?.push(node);
83263
83387
  nodesByPath.set(pathKey, node);
83264
83388
  appendChildPath(parentPath, pathKey);
83265
83389
  const childEnv = row.genericBindings ? envFromStamp(row.genericBindings) : genericEnv;
@@ -83362,15 +83486,49 @@ function indexExpansion(args) {
83362
83486
  );
83363
83487
  }
83364
83488
  };
83365
- visit(
83366
- args.expandedRoot.id,
83367
- args.rootMember,
83368
- ROOT_PATH,
83369
- null,
83370
- null,
83371
- /* @__PURE__ */ new Map()
83372
- );
83489
+ const includeRoot = (root, member, path = appendPath(ROOT_PATH, {
83490
+ kind: "class",
83491
+ schemaKey: `@stored:${root.id}`
83492
+ })) => {
83493
+ const added = [];
83494
+ discoveredNodes = added;
83495
+ visit(root.id, member, path, null, null, /* @__PURE__ */ new Map());
83496
+ if (args.additionalRoots !== void 0) {
83497
+ for (let index = 0; index < added.length; index += 1) {
83498
+ for (const { root: root2, member: member2 } of args.additionalRoots(
83499
+ added[index].expandedRow
83500
+ )) {
83501
+ visit(
83502
+ root2.id,
83503
+ member2,
83504
+ appendPath(ROOT_PATH, {
83505
+ kind: "class",
83506
+ schemaKey: `@constructor:${root2.id}`
83507
+ }),
83508
+ null,
83509
+ null,
83510
+ /* @__PURE__ */ new Map()
83511
+ );
83512
+ }
83513
+ }
83514
+ }
83515
+ discoveredNodes = null;
83516
+ return added;
83517
+ };
83518
+ if (args.additionalRoots === void 0) {
83519
+ visit(
83520
+ args.expandedRoot.id,
83521
+ args.rootMember,
83522
+ ROOT_PATH,
83523
+ null,
83524
+ null,
83525
+ /* @__PURE__ */ new Map()
83526
+ );
83527
+ } else {
83528
+ includeRoot(args.expandedRoot, args.rootMember, ROOT_PATH);
83529
+ }
83373
83530
  return {
83531
+ includeRoot,
83374
83532
  nodesByExpandedId,
83375
83533
  nodesByPath,
83376
83534
  rowsByExpandedId,
@@ -109701,17 +109859,17 @@ function computeWorkspaceStatus(workspace, options) {
109701
109859
  useBuildCaches: options.useBuildCaches !== false
109702
109860
  })
109703
109861
  });
109862
+ const compilationSources = projectSources.map((source) => ({
109863
+ uri: source.uri,
109864
+ kind: source.kind,
109865
+ text: source.source
109866
+ }));
109704
109867
  try {
109705
109868
  if (!baseManifest) {
109706
109869
  throw new Error(
109707
109870
  "Format-4 status requires a pulled schema base; run `neo pull --reset`."
109708
109871
  );
109709
109872
  }
109710
- const compilationSources = projectSources.map((source) => ({
109711
- uri: source.uri,
109712
- kind: source.kind,
109713
- text: source.source
109714
- }));
109715
109873
  const analysis = options.readProjectAnalysisCache?.(workspace.root, compilationSources) ?? options.compileProjectAnalysis?.(workspace.root, compilationSources) ?? compileNeoProjectSources(compilationSources);
109716
109874
  projectAnalysisV4 = analysis;
109717
109875
  for (const diagnostic of analysis.diagnostics) {
@@ -110414,6 +110572,12 @@ function computeWorkspaceStatus(workspace, options) {
110414
110572
  }
110415
110573
  reportPhase("initializer-materialization");
110416
110574
  return {
110575
+ ...projectAnalysisV4 === null ? {} : {
110576
+ sourceSnapshot: {
110577
+ inputs: compilationSources,
110578
+ analysis: projectAnalysisV4
110579
+ }
110580
+ },
110417
110581
  localValues: valueLowerRegistry.localValues?.placements(),
110418
110582
  changes,
110419
110583
  conflictedFiles,
@@ -123276,6 +123440,7 @@ function computeWorkspaceStatus2(workspace, options = {}) {
123276
123440
  }));
123277
123441
  return computeWorkspaceStatus(workspace, {
123278
123442
  ...options,
123443
+ reportPhase: options.reportPhase ?? ((phase, milliseconds) => recordPerformance(`workspace-${phase}`, milliseconds)),
123279
123444
  virtualSourceFiles,
123280
123445
  writeProjectAnalysisCache: options.writeProjectAnalysisCache ?? writeProjectSourceAnalysisCacheV4,
123281
123446
  readProjectAnalysisCache: options.readProjectAnalysisCache ?? readProjectSourceAnalysisBuildCacheV4,
@@ -123292,6 +123457,7 @@ var init_workspace_status = __esm({
123292
123457
  "use strict";
123293
123458
  import_ignore = __toESM(require_ignore(), 1);
123294
123459
  init_src();
123460
+ init_performance();
123295
123461
  init_project_document_cache();
123296
123462
  init_project_files();
123297
123463
  init_supplemental_records_file_system();
@@ -123428,6 +123594,164 @@ var init_status_output = __esm({
123428
123594
  }
123429
123595
  });
123430
123596
 
123597
+ // src/http.ts
123598
+ var NeoApiError, NeoApiClient;
123599
+ var init_http = __esm({
123600
+ "src/http.ts"() {
123601
+ "use strict";
123602
+ init_performance();
123603
+ init_token_store();
123604
+ NeoApiError = class extends Error {
123605
+ constructor(message, status, body) {
123606
+ super(message);
123607
+ this.status = status;
123608
+ this.body = body;
123609
+ this.name = "NeoApiError";
123610
+ }
123611
+ status;
123612
+ body;
123613
+ };
123614
+ NeoApiClient = class _NeoApiClient {
123615
+ constructor(apiBaseUrl, token) {
123616
+ this.apiBaseUrl = apiBaseUrl;
123617
+ this.token = token;
123618
+ }
123619
+ apiBaseUrl;
123620
+ token;
123621
+ static forBaseUrl(apiBaseUrl) {
123622
+ const token = loadToken(apiBaseUrl);
123623
+ if (token === null) {
123624
+ throw new Error(
123625
+ `No credentials stored for "${apiBaseUrl}". Run "neo login" (or set NEO_COMPOSE_TOKEN).`
123626
+ );
123627
+ }
123628
+ return new _NeoApiClient(apiBaseUrl, token);
123629
+ }
123630
+ async post(path, body, headers, options = {}) {
123631
+ return await this.postBytes(
123632
+ path,
123633
+ body === void 0 ? void 0 : JSON.stringify(body),
123634
+ headers,
123635
+ options
123636
+ );
123637
+ }
123638
+ async postBytes(path, body, headers, options = {}) {
123639
+ const url = new URL(path, this.apiBaseUrl).toString();
123640
+ const finish = startPerformanceSpan("http", {
123641
+ method: "POST",
123642
+ path: new URL(url).pathname
123643
+ });
123644
+ try {
123645
+ const response = await fetch(url, {
123646
+ method: "POST",
123647
+ headers: {
123648
+ Authorization: `Bearer ${this.token}`,
123649
+ "Content-Type": "application/json",
123650
+ ...headers
123651
+ },
123652
+ body,
123653
+ signal: options.signal
123654
+ });
123655
+ const text = await response.text();
123656
+ let parsed = null;
123657
+ if (text.length > 0) {
123658
+ try {
123659
+ parsed = JSON.parse(text);
123660
+ } catch {
123661
+ parsed = text;
123662
+ }
123663
+ }
123664
+ if (!response.ok) {
123665
+ const detail = typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string" ? parsed.error : text.slice(0, 300);
123666
+ throw new NeoApiError(
123667
+ `POST ${path} failed with status ${response.status}: ${detail}`,
123668
+ response.status,
123669
+ parsed
123670
+ );
123671
+ }
123672
+ return parsed;
123673
+ } finally {
123674
+ finish();
123675
+ }
123676
+ }
123677
+ async get(path, options = {}) {
123678
+ const url = new URL(path, this.apiBaseUrl).toString();
123679
+ const finish = startPerformanceSpan("http", {
123680
+ method: "GET",
123681
+ path: new URL(url).pathname
123682
+ });
123683
+ try {
123684
+ const response = await fetch(url, {
123685
+ method: "GET",
123686
+ headers: { Authorization: `Bearer ${this.token}` },
123687
+ signal: options.signal
123688
+ });
123689
+ const text = await response.text();
123690
+ let parsed = null;
123691
+ if (text.length > 0) {
123692
+ try {
123693
+ parsed = JSON.parse(text);
123694
+ } catch {
123695
+ parsed = text;
123696
+ }
123697
+ }
123698
+ if (!response.ok) {
123699
+ const detail = typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string" ? parsed.error : text.slice(0, 300);
123700
+ throw new NeoApiError(
123701
+ `GET ${path} failed with status ${response.status}: ${detail}`,
123702
+ response.status,
123703
+ parsed
123704
+ );
123705
+ }
123706
+ return parsed;
123707
+ } finally {
123708
+ finish();
123709
+ }
123710
+ }
123711
+ async getBytes(path) {
123712
+ const url = new URL(path, this.apiBaseUrl).toString();
123713
+ const finish = startPerformanceSpan("http", {
123714
+ method: "GET",
123715
+ path: new URL(url).pathname
123716
+ });
123717
+ try {
123718
+ const response = await fetch(url, {
123719
+ method: "GET",
123720
+ headers: { Authorization: `Bearer ${this.token}` }
123721
+ });
123722
+ if (!response.ok) {
123723
+ const detail = (await response.text()).slice(0, 300);
123724
+ throw new NeoApiError(
123725
+ `GET ${path} failed with status ${response.status}: ${detail}`,
123726
+ response.status,
123727
+ detail
123728
+ );
123729
+ }
123730
+ return {
123731
+ bytes: new Uint8Array(await response.arrayBuffer()),
123732
+ contentSha256: response.headers.get("x-neo-content-sha256"),
123733
+ contentType: response.headers.get("content-type")
123734
+ };
123735
+ } finally {
123736
+ finish();
123737
+ }
123738
+ }
123739
+ };
123740
+ }
123741
+ });
123742
+
123743
+ // ../src/common/schema-commit-transport.ts
123744
+ var SCHEMA_COMMIT_HTTP_MAX_BYTES, SCHEMA_COMMIT_HTTP_MAX_DECODED_BYTES, SCHEMA_COMMIT_HTTP_PATH, SCHEMA_COMMIT_V8_MAX_DECODED_BYTES;
123745
+ var init_schema_commit_transport = __esm({
123746
+ "../src/common/schema-commit-transport.ts"() {
123747
+ "use strict";
123748
+ SCHEMA_COMMIT_HTTP_MAX_BYTES = 18 * 1024 * 1024;
123749
+ SCHEMA_COMMIT_HTTP_MAX_DECODED_BYTES = 32 * 1024 * 1024;
123750
+ SCHEMA_COMMIT_HTTP_PATH = "/schema-commit";
123751
+ SCHEMA_COMMIT_V8_MAX_DECODED_BYTES = 1024 * 1024;
123752
+ }
123753
+ });
123754
+
123431
123755
  // ../convex/_generated/api.js
123432
123756
  var api_exports = {};
123433
123757
  __export(api_exports, {
@@ -123450,35 +123774,62 @@ var init_api = __esm({
123450
123774
  var convex_exports = {};
123451
123775
  __export(convex_exports, {
123452
123776
  api: () => api2,
123777
+ convexHttpActionUrl: () => convexHttpActionUrl,
123453
123778
  createConvexClient: () => createConvexClient,
123454
123779
  createConvexClientForApi: () => createConvexClientForApi,
123780
+ createConvexPushConnection: () => createConvexPushConnection,
123455
123781
  mintConvexJwt: () => mintConvexJwt,
123456
123782
  resolveConvexUrl: () => resolveConvexUrl
123457
123783
  });
123458
- import { ConvexHttpClient } from "convex/browser";
123784
+ import { gzipSync } from "node:zlib";
123785
+ import { ConvexClient, ConvexHttpClient } from "convex/browser";
123459
123786
  import { Buffer as Buffer2 } from "node:buffer";
123460
- async function mintConvexJwt(apiBaseUrl) {
123461
- const sessionToken = loadToken(apiBaseUrl);
123462
- if (sessionToken === null) {
123463
- throw new Error(
123464
- `No credentials stored for "${apiBaseUrl}". Run "neo login".`
123465
- );
123466
- }
123467
- const response = await fetch(new URL("/api/auth/convex/token", apiBaseUrl), {
123468
- headers: { Authorization: `Bearer ${sessionToken}` }
123469
- });
123470
- const body = await response.json();
123471
- if (!response.ok) {
123472
- throw new Error(
123473
- `Convex JWT mint failed with status ${response.status}: ${JSON.stringify(body).slice(0, 200)}`
123787
+ async function mintConvexJwt(apiBaseUrl, signal, convexSiteUrl) {
123788
+ const finish = startPerformanceSpan("authentication");
123789
+ try {
123790
+ const sessionToken = loadToken(apiBaseUrl);
123791
+ if (sessionToken === null) {
123792
+ throw new Error(
123793
+ `No credentials stored for "${apiBaseUrl}". Run "neo login".`
123794
+ );
123795
+ }
123796
+ const pinnedSite = loadTrustedConvexSite(apiBaseUrl);
123797
+ const direct = convexSiteUrl !== void 0 && pinnedSite === convexSiteUrl;
123798
+ const response = await fetch(
123799
+ new URL("/api/auth/convex/token", direct ? convexSiteUrl : apiBaseUrl),
123800
+ {
123801
+ headers: { Authorization: `Bearer ${sessionToken}` },
123802
+ redirect: "error",
123803
+ signal
123804
+ }
123474
123805
  );
123806
+ const body = await response.json();
123807
+ if (!response.ok) {
123808
+ throw new Error(
123809
+ `Convex JWT mint failed with status ${response.status}: ${JSON.stringify(body).slice(0, 200)}`
123810
+ );
123811
+ }
123812
+ if (typeof body !== "object" || body === null || !("token" in body) || typeof body.token !== "string")
123813
+ throw new Error('Convex token response is missing a string "token".');
123814
+ const payload = convexJwtPayload(body.token);
123815
+ if (convexSiteUrl !== void 0) {
123816
+ if (payload.iss !== convexSiteUrl)
123817
+ throw new Error(
123818
+ "The workspace Convex deployment does not match the authenticated API origin."
123819
+ );
123820
+ if (!direct) {
123821
+ try {
123822
+ saveTrustedConvexSite(apiBaseUrl, convexSiteUrl);
123823
+ } catch {
123824
+ }
123825
+ }
123826
+ }
123827
+ return body.token;
123828
+ } finally {
123829
+ finish();
123475
123830
  }
123476
- if (typeof body !== "object" || body === null || typeof body.token !== "string") {
123477
- throw new Error('Convex token response is missing a string "token".');
123478
- }
123479
- return body.token;
123480
123831
  }
123481
- async function resolveConvexUrl(workspace) {
123832
+ async function resolveConvexUrl(workspace, signal) {
123482
123833
  const fromEnv = process.env.NEXT_PUBLIC_CONVEX_URL;
123483
123834
  if (fromEnv !== void 0 && fromEnv.length > 0) return fromEnv;
123484
123835
  const persisted = workspace.config.convexUrl;
@@ -123496,7 +123847,8 @@ async function resolveConvexUrl(workspace) {
123496
123847
  ),
123497
123848
  {
123498
123849
  method: "POST",
123499
- headers: { Authorization: `Bearer ${sessionToken}` }
123850
+ headers: { Authorization: `Bearer ${sessionToken}` },
123851
+ signal
123500
123852
  }
123501
123853
  );
123502
123854
  const convexUrl = response.headers.get("x-neo-convex-url");
@@ -123512,11 +123864,12 @@ async function resolveConvexUrl(workspace) {
123512
123864
  writeWorkspaceConfig(workspace.root, workspace.config);
123513
123865
  return convexUrl;
123514
123866
  }
123515
- async function createConvexClient(workspace) {
123516
- const convexUrl = await resolveConvexUrl(workspace);
123867
+ async function createConvexClient(workspace, options = {}) {
123868
+ const convexUrl = await resolveConvexUrl(workspace, options.signal);
123517
123869
  return await createAuthenticatedConvexClient(
123518
123870
  convexUrl,
123519
- workspace.config.apiBaseUrl
123871
+ workspace.config.apiBaseUrl,
123872
+ options.signal
123520
123873
  );
123521
123874
  }
123522
123875
  async function createConvexClientForApi(apiBaseUrl) {
@@ -123537,22 +123890,35 @@ async function createConvexClientForApi(apiBaseUrl) {
123537
123890
  }
123538
123891
  return await createAuthenticatedConvexClient(convexUrl, apiBaseUrl);
123539
123892
  }
123540
- async function createAuthenticatedConvexClient(convexUrl, apiBaseUrl) {
123541
- const tokenCache = new ConvexJwtCache(apiBaseUrl);
123893
+ async function createAuthenticatedConvexClient(convexUrl, apiBaseUrl, signal) {
123894
+ const tokenCache = new ConvexJwtCache(
123895
+ apiBaseUrl,
123896
+ signal,
123897
+ convexHttpActionUrl(convexUrl)
123898
+ );
123542
123899
  const initialToken = await tokenCache.getToken();
123543
123900
  const authenticatedFetch = async (input, init) => {
123544
123901
  const headers = new Headers(
123545
123902
  init?.headers ?? (input instanceof Request ? input.headers : void 0)
123546
123903
  );
123547
123904
  headers.set("Authorization", `Bearer ${await tokenCache.getToken()}`);
123548
- return await fetch(input, { ...init, headers });
123905
+ const finish = startPerformanceSpan("convex-response-headers");
123906
+ try {
123907
+ return await fetch(input, {
123908
+ ...init,
123909
+ headers,
123910
+ ...signal === void 0 ? {} : { signal }
123911
+ });
123912
+ } finally {
123913
+ finish();
123914
+ }
123549
123915
  };
123550
123916
  return new ConvexHttpClient(convexUrl, {
123551
123917
  auth: initialToken,
123552
123918
  fetch: authenticatedFetch
123553
123919
  });
123554
123920
  }
123555
- function convexJwtExpiration(token) {
123921
+ function convexJwtPayload(token) {
123556
123922
  const segments = token.split(".");
123557
123923
  if (segments.length !== 3) {
123558
123924
  throw new Error("Convex token response is not a three-segment JWT.");
@@ -123571,9 +123937,16 @@ function convexJwtExpiration(token) {
123571
123937
  } catch {
123572
123938
  throw new Error("Convex token response has an invalid JWT payload.");
123573
123939
  }
123574
- if (typeof payload !== "object" || payload === null) {
123940
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
123575
123941
  throw new Error("Convex token response has a non-object JWT payload.");
123576
123942
  }
123943
+ return {
123944
+ exp: "exp" in payload ? payload.exp : void 0,
123945
+ iss: "iss" in payload ? payload.iss : void 0
123946
+ };
123947
+ }
123948
+ function convexJwtExpiration(token) {
123949
+ const payload = convexJwtPayload(token);
123577
123950
  if (!("exp" in payload)) {
123578
123951
  throw new Error('Convex token response JWT is missing an "exp" claim.');
123579
123952
  }
@@ -123605,10 +123978,93 @@ function missingProjectsConvexUrlError() {
123605
123978
  "Could not discover the Convex deployment URL (/api/projects supplied neither a non-empty x-neo-convex-url header nor a non-empty convexUrl JSON field)."
123606
123979
  );
123607
123980
  }
123981
+ function convexHttpActionUrl(convexUrl) {
123982
+ const url = new URL(convexUrl);
123983
+ if (url.protocol !== "https:" || url.port !== "" || url.username !== "" || url.password !== "" || !url.hostname.endsWith(".convex.cloud"))
123984
+ throw new Error(
123985
+ "Direct schema commits require a Convex cloud deployment URL."
123986
+ );
123987
+ url.hostname = url.hostname.slice(0, -".convex.cloud".length) + ".convex.site";
123988
+ return url.origin;
123989
+ }
123990
+ async function createConvexPushConnection(workspace) {
123991
+ const convexUrl = await resolveConvexUrl(workspace);
123992
+ const siteUrl = convexHttpActionUrl(convexUrl);
123993
+ const tokenCache = new ConvexJwtCache(
123994
+ workspace.config.apiBaseUrl,
123995
+ void 0,
123996
+ siteUrl
123997
+ );
123998
+ return {
123999
+ async submit(body, force) {
124000
+ const json = JSON.stringify(body);
124001
+ const bytes = Buffer2.from(json);
124002
+ if (bytes.byteLength > SCHEMA_COMMIT_HTTP_MAX_DECODED_BYTES)
124003
+ throw new NeoApiError(
124004
+ "Schema commit requires the large-payload route.",
124005
+ 413,
124006
+ { error: "schema-commit-route-required" }
124007
+ );
124008
+ const compressed = bytes.byteLength >= 1024 ? gzipSync(bytes) : bytes;
124009
+ const gzip = compressed.byteLength < bytes.byteLength;
124010
+ const payload = gzip ? compressed : bytes;
124011
+ if (payload.byteLength > SCHEMA_COMMIT_HTTP_MAX_BYTES)
124012
+ throw new NeoApiError(
124013
+ "Schema commit exceeds the HTTP body limit.",
124014
+ 413,
124015
+ { error: "schema-commit-route-required" }
124016
+ );
124017
+ const client = new NeoApiClient(siteUrl, await tokenCache.getToken());
124018
+ const path = `${SCHEMA_COMMIT_HTTP_PATH}?${new URLSearchParams({ projectId: workspace.config.projectId, versionId: workspace.config.versionId })}`;
124019
+ const finish = startPerformanceSpan("commit-transport", {
124020
+ decodedBytes: bytes.byteLength,
124021
+ encodedBytes: payload.byteLength,
124022
+ compression: gzip ? "gzip" : "identity"
124023
+ });
124024
+ try {
124025
+ return await client.postBytes(path, payload, {
124026
+ "x-neo-decoded-content-length": String(bytes.byteLength),
124027
+ ...process.env.NEO_COMPOSE_TRACE_PERFORMANCE === "1" ? { "x-neo-trace-performance": "1" } : {},
124028
+ ...typeof body === "object" && body !== null && "stagedFiles" in body && Array.isArray(body.stagedFiles) && body.stagedFiles.length > 0 ? { "x-neo-commit-runtime": "node" } : {},
124029
+ "Content-Type": gzip ? "application/gzip" : "application/json",
124030
+ ...force ? { "x-neo-force-project-version-write": "true" } : {}
124031
+ });
124032
+ } finally {
124033
+ finish();
124034
+ }
124035
+ },
124036
+ async subscribe(signal, onError) {
124037
+ await tokenCache.getToken(false, signal);
124038
+ signal.throwIfAborted();
124039
+ const client = new ConvexClient(convexUrl, {
124040
+ initialAuthTokenReuse: true,
124041
+ logger: false
124042
+ });
124043
+ client.setAuth(
124044
+ async ({ forceRefreshToken }) => {
124045
+ try {
124046
+ return await tokenCache.getToken(forceRefreshToken, signal);
124047
+ } catch (error) {
124048
+ onError(error instanceof Error ? error : new Error(String(error)));
124049
+ return null;
124050
+ }
124051
+ },
124052
+ (authenticated) => {
124053
+ if (!authenticated && !signal.aborted)
124054
+ onError(new Error("Convex rejected the session authentication."));
124055
+ }
124056
+ );
124057
+ return client;
124058
+ }
124059
+ };
124060
+ }
123608
124061
  var generatedApiModule, generatedApiValue, api2, CONVEX_JWT_REFRESH_LEEWAY_MS, ConvexJwtCache;
123609
124062
  var init_convex = __esm({
123610
124063
  "src/convex.ts"() {
123611
124064
  "use strict";
124065
+ init_http();
124066
+ init_schema_commit_transport();
124067
+ init_performance();
123612
124068
  init_api();
123613
124069
  init_token_store();
123614
124070
  init_workspace();
@@ -123620,18 +124076,22 @@ var init_convex = __esm({
123620
124076
  api2 = generatedApiValue;
123621
124077
  CONVEX_JWT_REFRESH_LEEWAY_MS = 6e4;
123622
124078
  ConvexJwtCache = class {
123623
- constructor(apiBaseUrl) {
124079
+ constructor(apiBaseUrl, signal, convexSiteUrl) {
123624
124080
  this.apiBaseUrl = apiBaseUrl;
124081
+ this.signal = signal;
124082
+ this.convexSiteUrl = convexSiteUrl;
123625
124083
  }
123626
124084
  apiBaseUrl;
124085
+ signal;
124086
+ convexSiteUrl;
123627
124087
  token = null;
123628
124088
  expiresAt = 0;
123629
124089
  refreshPromise = null;
123630
- async getToken() {
123631
- if (this.token !== null && Date.now() + CONVEX_JWT_REFRESH_LEEWAY_MS < this.expiresAt) {
124090
+ async getToken(forceRefresh = false, signal = this.signal) {
124091
+ if (!forceRefresh && this.token !== null && Date.now() + CONVEX_JWT_REFRESH_LEEWAY_MS < this.expiresAt) {
123632
124092
  return this.token;
123633
124093
  }
123634
- const pending = this.refreshPromise ?? this.refresh();
124094
+ const pending = this.refreshPromise ?? this.refresh(signal);
123635
124095
  this.refreshPromise = pending;
123636
124096
  try {
123637
124097
  return await pending;
@@ -123639,8 +124099,12 @@ var init_convex = __esm({
123639
124099
  if (this.refreshPromise === pending) this.refreshPromise = null;
123640
124100
  }
123641
124101
  }
123642
- async refresh() {
123643
- const token = await mintConvexJwt(this.apiBaseUrl);
124102
+ async refresh(signal) {
124103
+ const token = await mintConvexJwt(
124104
+ this.apiBaseUrl,
124105
+ signal,
124106
+ this.convexSiteUrl
124107
+ );
123644
124108
  const expiresAt = convexJwtExpiration(token);
123645
124109
  if (expiresAt <= Date.now()) {
123646
124110
  throw new Error("Convex token response contains an expired JWT.");
@@ -124577,119 +125041,6 @@ var init_reset = __esm({
124577
125041
  }
124578
125042
  });
124579
125043
 
124580
- // src/http.ts
124581
- var NeoApiError, NeoApiClient;
124582
- var init_http = __esm({
124583
- "src/http.ts"() {
124584
- "use strict";
124585
- init_token_store();
124586
- NeoApiError = class extends Error {
124587
- constructor(message, status, body) {
124588
- super(message);
124589
- this.status = status;
124590
- this.body = body;
124591
- this.name = "NeoApiError";
124592
- }
124593
- status;
124594
- body;
124595
- };
124596
- NeoApiClient = class _NeoApiClient {
124597
- constructor(apiBaseUrl, token) {
124598
- this.apiBaseUrl = apiBaseUrl;
124599
- this.token = token;
124600
- }
124601
- apiBaseUrl;
124602
- token;
124603
- static forBaseUrl(apiBaseUrl) {
124604
- const token = loadToken(apiBaseUrl);
124605
- if (token === null) {
124606
- throw new Error(
124607
- `No credentials stored for "${apiBaseUrl}". Run "neo login" (or set NEO_COMPOSE_TOKEN).`
124608
- );
124609
- }
124610
- return new _NeoApiClient(apiBaseUrl, token);
124611
- }
124612
- async post(path, body, headers, options = {}) {
124613
- const url = new URL(path, this.apiBaseUrl).toString();
124614
- const response = await fetch(url, {
124615
- method: "POST",
124616
- headers: {
124617
- Authorization: `Bearer ${this.token}`,
124618
- "Content-Type": "application/json",
124619
- ...headers
124620
- },
124621
- body: body === void 0 ? void 0 : JSON.stringify(body),
124622
- signal: options.signal
124623
- });
124624
- const text = await response.text();
124625
- let parsed = null;
124626
- if (text.length > 0) {
124627
- try {
124628
- parsed = JSON.parse(text);
124629
- } catch {
124630
- parsed = text;
124631
- }
124632
- }
124633
- if (!response.ok) {
124634
- const detail = typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string" ? parsed.error : text.slice(0, 300);
124635
- throw new NeoApiError(
124636
- `POST ${path} failed with status ${response.status}: ${detail}`,
124637
- response.status,
124638
- parsed
124639
- );
124640
- }
124641
- return parsed;
124642
- }
124643
- async get(path, options = {}) {
124644
- const url = new URL(path, this.apiBaseUrl).toString();
124645
- const response = await fetch(url, {
124646
- method: "GET",
124647
- headers: { Authorization: `Bearer ${this.token}` },
124648
- signal: options.signal
124649
- });
124650
- const text = await response.text();
124651
- let parsed = null;
124652
- if (text.length > 0) {
124653
- try {
124654
- parsed = JSON.parse(text);
124655
- } catch {
124656
- parsed = text;
124657
- }
124658
- }
124659
- if (!response.ok) {
124660
- const detail = typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string" ? parsed.error : text.slice(0, 300);
124661
- throw new NeoApiError(
124662
- `GET ${path} failed with status ${response.status}: ${detail}`,
124663
- response.status,
124664
- parsed
124665
- );
124666
- }
124667
- return parsed;
124668
- }
124669
- async getBytes(path) {
124670
- const url = new URL(path, this.apiBaseUrl).toString();
124671
- const response = await fetch(url, {
124672
- method: "GET",
124673
- headers: { Authorization: `Bearer ${this.token}` }
124674
- });
124675
- if (!response.ok) {
124676
- const detail = (await response.text()).slice(0, 300);
124677
- throw new NeoApiError(
124678
- `GET ${path} failed with status ${response.status}: ${detail}`,
124679
- response.status,
124680
- detail
124681
- );
124682
- }
124683
- return {
124684
- bytes: new Uint8Array(await response.arrayBuffer()),
124685
- contentSha256: response.headers.get("x-neo-content-sha256"),
124686
- contentType: response.headers.get("content-type")
124687
- };
124688
- }
124689
- };
124690
- }
124691
- });
124692
-
124693
125044
  // src/project-source/project-file-pull.ts
124694
125045
  import { existsSync as existsSync7, rmSync as rmSync4 } from "node:fs";
124695
125046
  import { join as join9 } from "node:path";
@@ -129126,6 +129477,78 @@ var init_script = __esm({
129126
129477
  }
129127
129478
  });
129128
129479
 
129480
+ // ../convex/convexValues.ts
129481
+ function decodeConvexOpaqueJson(value, context) {
129482
+ if (typeof value !== "string") {
129483
+ throw new Error(`${context} must be an encoded JSON string.`);
129484
+ }
129485
+ try {
129486
+ return JSON.parse(value);
129487
+ } catch (error) {
129488
+ const message = error instanceof Error ? error.message : String(error);
129489
+ throw new Error(`${context} could not be parsed as JSON: ${message}`);
129490
+ }
129491
+ }
129492
+ var CONVEX_DOCUMENT_WARNING_BYTES, CONVEX_DOCUMENT_MAX_BYTES;
129493
+ var init_convexValues = __esm({
129494
+ "../convex/convexValues.ts"() {
129495
+ "use strict";
129496
+ CONVEX_DOCUMENT_WARNING_BYTES = 750 * 1024;
129497
+ CONVEX_DOCUMENT_MAX_BYTES = 1024 * 1024;
129498
+ }
129499
+ });
129500
+
129501
+ // ../src/database/project-version-transaction-result.ts
129502
+ function decodeProjectVersionTransactionResultPage(page) {
129503
+ const assignments = {};
129504
+ const changedRecords = [];
129505
+ for (const chunk of page.chunks) {
129506
+ const chunkAssignments = decodeConvexOpaqueJson(
129507
+ chunk.assignmentsJson,
129508
+ `Project transaction "${page.transactionId}" result chunk ${chunk.chunkIndex} assignments`
129509
+ );
129510
+ if (!isRecord9(chunkAssignments)) {
129511
+ throw new Error(
129512
+ `Project transaction "${page.transactionId}" result chunk ${chunk.chunkIndex} assignments are not an object.`
129513
+ );
129514
+ }
129515
+ for (const [pendingId2, assignedId] of Object.entries(chunkAssignments)) {
129516
+ if (typeof assignedId !== "string") {
129517
+ throw new Error(
129518
+ `Project transaction "${page.transactionId}" result chunk ${chunk.chunkIndex} assignment ${JSON.stringify(pendingId2)} is not a string.`
129519
+ );
129520
+ }
129521
+ assignments[pendingId2] = assignedId;
129522
+ }
129523
+ const chunkRecords = decodeConvexOpaqueJson(
129524
+ chunk.changedRecordsJson,
129525
+ `Project transaction "${page.transactionId}" result chunk ${chunk.chunkIndex} changed records`
129526
+ );
129527
+ if (!Array.isArray(chunkRecords)) {
129528
+ throw new Error(
129529
+ `Project transaction "${page.transactionId}" result chunk ${chunk.chunkIndex} changed records are not an array.`
129530
+ );
129531
+ }
129532
+ for (const record4 of chunkRecords) changedRecords.push(record4);
129533
+ }
129534
+ return {
129535
+ transactionId: page.transactionId,
129536
+ assignments,
129537
+ changedRecords,
129538
+ continueCursor: page.continueCursor,
129539
+ isDone: page.isDone
129540
+ };
129541
+ }
129542
+ function isRecord9(value) {
129543
+ return typeof value === "object" && value !== null && !Array.isArray(value);
129544
+ }
129545
+ var init_project_version_transaction_result = __esm({
129546
+ "../src/database/project-version-transaction-result.ts"() {
129547
+ "use strict";
129548
+ init_convexValues();
129549
+ }
129550
+ });
129551
+
129129
129552
  // src/project-source/identity-rewriter.ts
129130
129553
  function createIdentityRewriter(assignments) {
129131
129554
  if (assignments.size === 0) return (value) => value;
@@ -130241,6 +130664,7 @@ function assignPendingIds(changes, authoredValueSeeds, reconstructed3, localInit
130241
130664
  }
130242
130665
  const rewriteString = createIdentityRewriter(assigned);
130243
130666
  const rewrite = (value) => {
130667
+ if (assigned.size === 0) return value;
130244
130668
  if (typeof value === "string") {
130245
130669
  return rewriteString(value);
130246
130670
  }
@@ -130311,7 +130735,7 @@ function assignPendingIds(changes, authoredValueSeeds, reconstructed3, localInit
130311
130735
  });
130312
130736
  }
130313
130737
  }
130314
- if (reconstructed3 !== void 0) {
130738
+ if (reconstructed3 !== void 0 && assigned.size > 0) {
130315
130739
  const rewrittenRecords = /* @__PURE__ */ new Map();
130316
130740
  for (const record4 of reconstructed3.values()) {
130317
130741
  const rewrittenRecordId = rewrite(record4.recordId);
@@ -130434,6 +130858,10 @@ function readProjectVersionTransactionStatus(value, transactionId) {
130434
130858
  return {
130435
130859
  transactionId,
130436
130860
  commitStatus: value.commitStatus,
130861
+ transactionHash: readNullableString(
130862
+ value.transactionHash,
130863
+ `Project transaction "${transactionId}" transactionHash`
130864
+ ),
130437
130865
  totalChangeCount: readNullableCount(
130438
130866
  value.totalChangeCount,
130439
130867
  `Project transaction "${transactionId}" totalChangeCount`
@@ -130643,66 +131071,164 @@ function progressEventFromStatus(status) {
130643
131071
  };
130644
131072
  }
130645
131073
  async function waitForAcceptedProjectVersionTransaction(args) {
130646
- const { client, projectId, versionId, accepted, reporter } = args;
130647
- const transactionPath = `/api/projects/${projectId}/versions/${versionId}/schema/transactions/${accepted.transactionId}`;
131074
+ const { workspace, accepted, reporter } = args;
131075
+ const { projectId, versionId } = workspace.config;
130648
131076
  const controller = new AbortController();
130649
- let interrupted = false;
130650
- const interrupt = () => {
130651
- interrupted = true;
130652
- controller.abort();
130653
- };
131077
+ const interrupt = () => controller.abort(
131078
+ new ProjectTransactionInterruptedError(accepted.transactionId)
131079
+ );
130654
131080
  process.once("SIGINT", interrupt);
130655
131081
  reporter.report(progressEventFromAccepted(accepted));
130656
- let pollIndex = 0;
131082
+ const finishWait = startPerformanceSpan("transaction-wait");
131083
+ let client;
131084
+ let unsubscribe;
131085
+ let progressTimer;
131086
+ let settled = false;
130657
131087
  try {
130658
- for (; ; ) {
130659
- const rawStatus = await client.get(transactionPath, {
130660
- signal: controller.signal
130661
- });
130662
- const status = readProjectVersionTransactionStatus(
130663
- rawStatus,
130664
- accepted.transactionId
130665
- );
130666
- reporter.report(progressEventFromStatus(status));
130667
- if (status.commitStatus === "committed") {
131088
+ const connection = args.connection ?? await withTransactionAbort(
131089
+ createConvexPushConnection(workspace),
131090
+ controller.signal
131091
+ );
131092
+ let failed = () => void 0;
131093
+ let updated = () => void 0;
131094
+ let firstResultPage;
131095
+ const terminal2 = new Promise(
131096
+ (resolve5, reject) => {
131097
+ failed = (error) => {
131098
+ if (!settled) {
131099
+ settled = true;
131100
+ reject(error);
131101
+ }
131102
+ };
131103
+ updated = (response) => {
131104
+ if (settled) return;
131105
+ if (response === null) return;
131106
+ try {
131107
+ const status = readProjectVersionTransactionStatus(
131108
+ response,
131109
+ accepted.transactionId
131110
+ );
131111
+ reporter.report(progressEventFromStatus(status));
131112
+ if (status.commitStatus === "failed") {
131113
+ failed(new ProjectTransactionFailedError(status));
131114
+ return;
131115
+ }
131116
+ if (status.commitStatus === "committed") {
131117
+ settled = true;
131118
+ resolve5(status);
131119
+ }
131120
+ } catch (error) {
131121
+ failed(error instanceof Error ? error : new Error(String(error)));
131122
+ }
131123
+ };
131124
+ }
131125
+ );
131126
+ void terminal2.catch(() => void 0);
131127
+ client = await connection.subscribe(controller.signal, (error) => {
131128
+ controller.abort(error);
131129
+ failed(error);
131130
+ });
131131
+ const progressClient = client;
131132
+ const refreshProgress = async () => {
131133
+ if (settled || controller.signal.aborted) return;
131134
+ try {
131135
+ const response = await withTransactionAbort(
131136
+ progressClient.query(
131137
+ api2.projectVersionTransactionJobs.getTransactionStatus,
131138
+ {
131139
+ projectId,
131140
+ versionId,
131141
+ transactionId: accepted.transactionId
131142
+ }
131143
+ ),
131144
+ controller.signal
131145
+ );
131146
+ if (settled || controller.signal.aborted) return;
131147
+ const status = readProjectVersionTransactionStatus(
131148
+ response,
131149
+ accepted.transactionId
131150
+ );
131151
+ if (status.commitStatus === "committed") return;
131152
+ updated(status);
131153
+ if (!settled)
131154
+ progressTimer = setTimeout(() => void refreshProgress(), 5e3);
131155
+ } catch (error) {
131156
+ failed(error instanceof Error ? error : new Error(String(error)));
131157
+ }
131158
+ };
131159
+ const finishFirstStatus = startPerformanceSpan("transaction-first-status");
131160
+ let firstStatus = true;
131161
+ try {
131162
+ unsubscribe = client.onUpdate(
131163
+ api2.projectVersionTransactionJobs.getTransactionCompletion,
131164
+ {
131165
+ projectId,
131166
+ versionId,
131167
+ transactionId: accepted.transactionId,
131168
+ includeResultPage: true
131169
+ },
131170
+ (response) => {
131171
+ if (firstStatus) {
131172
+ firstStatus = false;
131173
+ finishFirstStatus();
131174
+ }
131175
+ if (settled) return;
131176
+ firstResultPage = response?.resultPage;
131177
+ updated(response);
131178
+ },
131179
+ failed
131180
+ );
131181
+ if (!settled)
131182
+ progressTimer = setTimeout(() => void refreshProgress(), 5e3);
131183
+ const status = await withTransactionAbort(terminal2, controller.signal);
131184
+ unsubscribe();
131185
+ unsubscribe = void 0;
131186
+ const finishDownload = startPerformanceSpan("result-download");
131187
+ try {
130668
131188
  return await downloadProjectVersionTransactionResult({
130669
131189
  client,
130670
- transactionPath,
131190
+ projectId,
131191
+ versionId,
130671
131192
  transactionId: accepted.transactionId,
130672
131193
  status,
131194
+ firstResultPage,
130673
131195
  signal: controller.signal
130674
131196
  });
131197
+ } finally {
131198
+ finishDownload();
130675
131199
  }
130676
- if (status.commitStatus === "failed") {
130677
- throw new ProjectTransactionFailedError(status);
130678
- }
130679
- const delay = PROJECT_TRANSACTION_POLL_DELAYS_MS[Math.min(pollIndex, PROJECT_TRANSACTION_POLL_DELAYS_MS.length - 1)];
130680
- pollIndex += 1;
130681
- await waitForPollDelay(delay, controller.signal);
130682
- }
130683
- } catch (error) {
130684
- if (interrupted) {
130685
- throw new ProjectTransactionInterruptedError(accepted.transactionId);
131200
+ } finally {
131201
+ if (firstStatus) finishFirstStatus();
130686
131202
  }
130687
- throw error;
130688
131203
  } finally {
131204
+ settled = true;
131205
+ clearTimeout(progressTimer);
131206
+ unsubscribe?.();
131207
+ controller.abort();
131208
+ if (client !== void 0) await client.close();
131209
+ finishWait();
130689
131210
  process.removeListener("SIGINT", interrupt);
130690
131211
  }
130691
131212
  }
130692
- async function waitForPollDelay(delayMs, signal) {
130693
- if (signal.aborted) {
130694
- throw new Error("Project transaction polling was interrupted.");
130695
- }
130696
- await new Promise((resolve5, reject) => {
130697
- const aborted = () => {
130698
- clearTimeout(timer);
130699
- reject(new Error("Project transaction polling was interrupted."));
131213
+ async function withTransactionAbort(pending, signal) {
131214
+ signal.throwIfAborted();
131215
+ return await new Promise((resolve5, reject) => {
131216
+ const abort = () => {
131217
+ cleanup();
131218
+ reject(signal.reason);
130700
131219
  };
130701
- const timer = setTimeout(() => {
130702
- signal.removeEventListener("abort", aborted);
130703
- resolve5();
130704
- }, delayMs);
130705
- signal.addEventListener("abort", aborted, { once: true });
131220
+ const cleanup = () => signal.removeEventListener("abort", abort);
131221
+ signal.addEventListener("abort", abort, { once: true });
131222
+ pending.then(
131223
+ (value) => {
131224
+ cleanup();
131225
+ resolve5(value);
131226
+ },
131227
+ (error) => {
131228
+ cleanup();
131229
+ reject(error);
131230
+ }
131231
+ );
130706
131232
  });
130707
131233
  }
130708
131234
  async function downloadProjectVersionTransactionResult(args) {
@@ -130717,11 +131243,20 @@ async function downloadProjectVersionTransactionResult(args) {
130717
131243
  );
130718
131244
  }
130719
131245
  seenCursors.add(cursor);
130720
- const query = new URLSearchParams({ cursor });
130721
- const rawPage = await args.client.get(
130722
- `${args.transactionPath}/results?${query.toString()}`,
130723
- { signal: args.signal }
131246
+ args.signal.throwIfAborted();
131247
+ const storedPage = cursor === "" && args.firstResultPage !== void 0 ? args.firstResultPage : await withTransactionAbort(
131248
+ args.client.query(
131249
+ api2.projectVersionTransactionJobs.getTransactionResultPage,
131250
+ {
131251
+ projectId: args.projectId,
131252
+ versionId: args.versionId,
131253
+ transactionId: args.transactionId,
131254
+ paginationOpts: { cursor, numItems: 16 }
131255
+ }
131256
+ ),
131257
+ args.signal
130724
131258
  );
131259
+ const rawPage = storedPage === null ? null : decodeProjectVersionTransactionResultPage(storedPage);
130725
131260
  const page = readProjectVersionTransactionResultPage(
130726
131261
  rawPage,
130727
131262
  args.transactionId
@@ -130747,8 +131282,13 @@ async function downloadProjectVersionTransactionResult(args) {
130747
131282
  `Project transaction "${args.transactionId}" committed ${args.status.totalChangeCount} changes but its result download returned ${changedRecords.length}.`
130748
131283
  );
130749
131284
  }
131285
+ if (args.status.transactionHash === null)
131286
+ throw new Error(
131287
+ `Committed project transaction "${args.transactionId}" has no transaction hash.`
131288
+ );
130750
131289
  return {
130751
131290
  kind: "committed",
131291
+ headTransactionHash: args.status.transactionHash,
130752
131292
  transactionId: args.transactionId,
130753
131293
  totalChangeCount: args.status.totalChangeCount,
130754
131294
  totalChunkCount: args.status.totalChunkCount ?? 0,
@@ -130863,6 +131403,7 @@ async function runPush(workspace, options, preparationOverride) {
130863
131403
  const verifiesConfiguredHook = options.noVerify !== true && configuredHook !== void 0;
130864
131404
  const preparedInputFingerprint = verifiesConfiguredHook ? fingerprintPushInputs(workspace) : void 0;
130865
131405
  const preparation = preparationOverride === void 0 ? process.stdout.isTTY === true && options.json !== true ? spinner("Preparing push\u2026") : null : preparationOverride;
131406
+ const finishCandidate = startPerformanceSpan("local-candidate");
130866
131407
  let candidate;
130867
131408
  try {
130868
131409
  candidate = await prepareLocalCandidateV4(workspace, {
@@ -130885,6 +131426,8 @@ async function runPush(workspace, options, preparationOverride) {
130885
131426
  }
130886
131427
  preparation?.fail("Push preparation failed.");
130887
131428
  throw error;
131429
+ } finally {
131430
+ finishCandidate();
130888
131431
  }
130889
131432
  const status = candidate.status;
130890
131433
  const local = candidate.preparedLocal;
@@ -131065,7 +131608,12 @@ async function runPush(workspace, options, preparationOverride) {
131065
131608
  };
131066
131609
  let preparedPush;
131067
131610
  try {
131068
- preparedPush = await stagePreparedPush();
131611
+ const finishUploads = startPerformanceSpan("file-uploads");
131612
+ try {
131613
+ preparedPush = await stagePreparedPush();
131614
+ } finally {
131615
+ finishUploads();
131616
+ }
131069
131617
  } catch (error) {
131070
131618
  progress.stop();
131071
131619
  if (error instanceof ProjectFilePushCancelledError) {
@@ -131085,9 +131633,9 @@ async function runPush(workspace, options, preparationOverride) {
131085
131633
  stagedUploadSessionId
131086
131634
  } = preparedPush;
131087
131635
  const { stagedFiles } = preparedPush;
131088
- const commit = async (force) => await client.post(
131089
- `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}/schema/commit`,
131090
- {
131636
+ let pushConnection;
131637
+ const commit = async (force) => {
131638
+ const body = {
131091
131639
  operation: transactionOperation,
131092
131640
  changes: transportChanges,
131093
131641
  authoredValueSeeds: transportSeeds,
@@ -131102,9 +131650,20 @@ async function runPush(workspace, options, preparationOverride) {
131102
131650
  headTransactionHash: workspace.state.headTransactionHash ?? null,
131103
131651
  derivedEdgeExtractorVersion: PROJECT_SNAPSHOT_DERIVED_EDGE_EXTRACTOR_VERSION,
131104
131652
  forceRecompile: options.forceRecompile === true
131105
- },
131106
- force ? { "x-neo-force-project-version-write": "true" } : void 0
131107
- );
131653
+ };
131654
+ pushConnection ??= await createConvexPushConnection(workspace);
131655
+ try {
131656
+ return await pushConnection.submit(body, force);
131657
+ } catch (error) {
131658
+ if (!(error instanceof NeoApiError) || error.status !== 413 || !isObjectRecord2(error.body) || error.body.error !== "schema-commit-route-required")
131659
+ throw error;
131660
+ }
131661
+ return await client.post(
131662
+ `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}/schema/commit`,
131663
+ body,
131664
+ force ? { "x-neo-force-project-version-write": "true" } : void 0
131665
+ );
131666
+ };
131108
131667
  const finishCommitResponse = async (response, reporter) => {
131109
131668
  let accepted;
131110
131669
  try {
@@ -131119,7 +131678,12 @@ async function runPush(workspace, options, preparationOverride) {
131119
131678
  if (!applyCommittedHeadTransactionHash(workspace, response)) {
131120
131679
  await recoverHeadTransactionHashFromSchemaSignal(workspace);
131121
131680
  }
131122
- applyPushResult(workspace, response, pendingAssignment.assigned);
131681
+ applyPushResult(
131682
+ workspace,
131683
+ response,
131684
+ pendingAssignment.assigned,
131685
+ preparedLocal.submittedStatus
131686
+ );
131123
131687
  } catch (error) {
131124
131688
  reporter.stop();
131125
131689
  throw error;
@@ -131147,11 +131711,10 @@ async function runPush(workspace, options, preparationOverride) {
131147
131711
  let completed;
131148
131712
  try {
131149
131713
  completed = await waitForAcceptedProjectVersionTransaction({
131150
- client,
131151
- projectId: workspace.config.projectId,
131152
- versionId: workspace.config.versionId,
131714
+ workspace,
131153
131715
  accepted,
131154
- reporter
131716
+ reporter,
131717
+ connection: pushConnection
131155
131718
  });
131156
131719
  } catch (error) {
131157
131720
  if (error instanceof ProjectTransactionInterruptedError) {
@@ -131197,15 +131760,19 @@ async function runPush(workspace, options, preparationOverride) {
131197
131760
  throw error;
131198
131761
  }
131199
131762
  try {
131200
- if (!applyCommittedHeadTransactionHash(workspace, completed)) {
131201
- await recoverHeadTransactionHashFromSchemaSignal(workspace);
131763
+ applyCommittedHeadTransactionHash(workspace, completed);
131764
+ const finishApply = startPerformanceSpan("apply-result");
131765
+ try {
131766
+ applyCommittedProjectTransactionResult({
131767
+ workspace,
131768
+ result: completed,
131769
+ submittedChanges: status.changes,
131770
+ candidateAssignments: pendingAssignment.assigned,
131771
+ submittedStatus: preparedLocal.submittedStatus
131772
+ });
131773
+ } finally {
131774
+ finishApply();
131202
131775
  }
131203
- applyCommittedProjectTransactionResult({
131204
- workspace,
131205
- result: completed,
131206
- submittedChanges: status.changes,
131207
- candidateAssignments: pendingAssignment.assigned
131208
- });
131209
131776
  } catch (error) {
131210
131777
  reporter.stop();
131211
131778
  throw error;
@@ -131245,7 +131812,12 @@ async function runPush(workspace, options, preparationOverride) {
131245
131812
  }
131246
131813
  let result;
131247
131814
  try {
131248
- result = await commit(options.acceptBump);
131815
+ const finishSubmit = startPerformanceSpan("submit");
131816
+ try {
131817
+ result = await commit(options.acceptBump);
131818
+ } finally {
131819
+ finishSubmit();
131820
+ }
131249
131821
  } catch (error) {
131250
131822
  progress.stop();
131251
131823
  if (!(error instanceof NeoApiError)) throw error;
@@ -131325,12 +131897,19 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
131325
131897
  fullValidation: true,
131326
131898
  forceRecompile: false
131327
131899
  }) {
131328
- onPhase("Hashing canonical project source\u2026");
131329
- const source = createPendingProjectSourceIdentityV4(
131900
+ const submittedStatus = {
131901
+ status: cloneWorkspaceStatus(status),
131902
+ records: workspace.state.records,
131903
+ config: structuredClone(workspace.config),
131904
+ root: workspace.root
131905
+ };
131906
+ onPhase("Hashing project source\u2026");
131907
+ const source = projectSourceIdentityForStatus(status);
131908
+ const verificationSource = options.fullValidation ? createPendingProjectSourceIdentityV4(
131330
131909
  workspace,
131331
131910
  status,
131332
131911
  status.authoredValueSeeds
131333
- );
131912
+ ) : null;
131334
131913
  const pendingTransportChanges = transportChangesForStatus(status);
131335
131914
  const pendingTransportSeeds = [...status.authoredValueSeeds].map(
131336
131915
  ([memberId, seed]) => ({ memberId, ...seed })
@@ -131364,20 +131943,20 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
131364
131943
  localizedTextCreateEnvelopeConfig2(workspace, status.changes)
131365
131944
  );
131366
131945
  let preparedChanges = null;
131367
- if (options.fullValidation) {
131946
+ if (verificationSource !== null) {
131368
131947
  onPhase("Verifying the assigned-id source rewrite\u2026");
131369
131948
  materializeAssignedSchemaIdsInAuthoredSource(
131370
131949
  workspace,
131371
131950
  pendingAssignment.assigned,
131372
131951
  status.pendingValueIdentitySites,
131373
- true
131952
+ { sourceSnapshot: status.sourceSnapshot, validateResult: true }
131374
131953
  );
131375
131954
  onPhase("Verifying the complete source round trip\u2026");
131376
131955
  verifyProjectSourceCommitAgainstStateV4({
131377
131956
  projectId: workspace.config.projectId,
131378
131957
  versionId: workspace.config.versionId,
131379
131958
  stateRecords: workspace.state.records,
131380
- files: source.files,
131959
+ files: verificationSource.files,
131381
131960
  changes: transportChanges,
131382
131961
  authoredValueSeeds: transportSeeds,
131383
131962
  initializerMaterialization: pendingAssignment.localInitializerMaterialization ?? { roots: [] },
@@ -131402,6 +131981,7 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
131402
131981
  }
131403
131982
  return {
131404
131983
  source,
131984
+ submittedStatus,
131405
131985
  pendingAssignment,
131406
131986
  transactionOperation,
131407
131987
  transportChanges,
@@ -131431,7 +132011,7 @@ async function prepareLocalCandidateV4(workspace, options = {}) {
131431
132011
  let documentStatus = status;
131432
132012
  if (status.changes.length > 0 || status.authoredValueSeeds.size > 0) {
131433
132013
  try {
131434
- documentStatus = pushOptions.dryRun ? cloneStatusForDryRun(status) : status;
132014
+ documentStatus = pushOptions.dryRun ? cloneWorkspaceStatus(status) : status;
131435
132015
  if (options.refreshDerivedEdgeVersion === true) {
131436
132016
  const convex = await createConvexClient(workspace);
131437
132017
  const signal = await convex.query(api2.projectExportData.schemaSignal, {
@@ -131457,20 +132037,7 @@ async function prepareLocalCandidateV4(workspace, options = {}) {
131457
132037
  }
131458
132038
  sourceHash = preparedLocal.source.sourceHash;
131459
132039
  } else {
131460
- const files = listProjectSourceFilesV4(workspace.root).map(
131461
- (absolutePath) => {
131462
- const path = relative7(workspace.root, absolutePath).split(sep7).join("/");
131463
- const content = readFileSync16(absolutePath, "utf8");
131464
- const kind = neoProjectSourceKind(path, content);
131465
- if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
131466
- throw new Error(
131467
- `Listed workspace source ${JSON.stringify(path)} is not production Neo source.`
131468
- );
131469
- }
131470
- return { path, kind, content };
131471
- }
131472
- );
131473
- sourceHash = hashProjectSourceFiles(files);
132040
+ sourceHash = projectSourceIdentityForStatus(status).sourceHash;
131474
132041
  }
131475
132042
  if (options.includeDocument === false) {
131476
132043
  return { status, document: null, sourceHash, preparedLocal };
@@ -131611,7 +132178,7 @@ function describeChangeOrigin(change) {
131611
132178
  if (parts.length === 0) return "";
131612
132179
  return ` (${parts.join(" in ")})`;
131613
132180
  }
131614
- function cloneStatusForDryRun(status) {
132181
+ function cloneWorkspaceStatus(status) {
131615
132182
  return {
131616
132183
  ...status,
131617
132184
  changes: structuredClone(status.changes),
@@ -131663,6 +132230,19 @@ function reportDryRunFailure(error, json) {
131663
132230
  }
131664
132231
  console.error(`Dry run failed: ${message}`);
131665
132232
  }
132233
+ function projectSourceIdentityForStatus(status) {
132234
+ if (status.sourceSnapshot === void 0) {
132235
+ throw new Error(
132236
+ "Push source identity requires the analyzed source snapshot."
132237
+ );
132238
+ }
132239
+ const files = status.sourceSnapshot.inputs.map((input) => ({
132240
+ path: input.uri,
132241
+ kind: input.kind,
132242
+ content: input.text
132243
+ }));
132244
+ return { files, sourceHash: hashProjectSourceFiles(files) };
132245
+ }
131666
132246
  function createPendingProjectSourceIdentityV4(workspace, status, authoredValueSeeds) {
131667
132247
  const records2 = /* @__PURE__ */ new Map();
131668
132248
  for (const [key, state] of Object.entries(workspace.state.records)) {
@@ -131944,7 +132524,7 @@ function reportAdditionalServerChanges(totalChangeCount, authoredChangeCount, js
131944
132524
  }
131945
132525
  function applyCommittedProjectTransactionResult(args) {
131946
132526
  const { workspace, result, submittedChanges, candidateAssignments } = args;
131947
- const localStatus = computeWorkspaceStatus2(workspace);
132527
+ const localStatus = statusForPushResult(workspace, args.submittedStatus);
131948
132528
  const blockingErrors = localStatus.parseErrors.filter(
131949
132529
  isBlockingSchemaSourceError
131950
132530
  );
@@ -131966,7 +132546,8 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
131966
132546
  const preservedSourceFiles = materializeAssignedSchemaIdsInAuthoredSource(
131967
132547
  workspace,
131968
132548
  replacements.exact,
131969
- localStatus.pendingValueIdentitySites
132549
+ localStatus.pendingValueIdentitySites,
132550
+ { sourceSnapshot: localStatus.sourceSnapshot }
131970
132551
  );
131971
132552
  const localRecords = rewriteReconstructedRecords(
131972
132553
  localStatus.reconstructed,
@@ -132046,6 +132627,38 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
132046
132627
  throw error;
132047
132628
  }
132048
132629
  }
132630
+ function statusForPushResult(workspace, submitted) {
132631
+ if (submitted === void 0) return computeWorkspaceStatus2(workspace);
132632
+ if (submitted.records !== workspace.state.records)
132633
+ return computeWorkspaceStatus2(workspace);
132634
+ if (submitted.root !== workspace.root)
132635
+ return computeWorkspaceStatus2(workspace);
132636
+ if (!canonicallyEqual(submitted.config, workspace.config))
132637
+ return computeWorkspaceStatus2(workspace);
132638
+ const submittedStatus = submitted.status;
132639
+ const snapshot = submittedStatus?.sourceSnapshot;
132640
+ if (snapshot === void 0 || submittedStatus?.binaryFiles === void 0)
132641
+ return computeWorkspaceStatus2(workspace);
132642
+ const paths = listProjectSourceFilesV4(workspace.root);
132643
+ if (paths.length !== snapshot.inputs.length)
132644
+ return computeWorkspaceStatus2(workspace);
132645
+ for (let index = 0; index < paths.length; index += 1) {
132646
+ const path = paths[index];
132647
+ const input = snapshot.inputs[index];
132648
+ if (input?.uri !== relative7(workspace.root, path).split(sep7).join("/"))
132649
+ return computeWorkspaceStatus2(workspace);
132650
+ if (input.text !== readFileSync16(path, "utf8"))
132651
+ return computeWorkspaceStatus2(workspace);
132652
+ }
132653
+ const binaries = inspectProjectBinaryStatusV4(
132654
+ workspace.root,
132655
+ workspace.state.records,
132656
+ snapshot.analysis
132657
+ );
132658
+ if (!canonicallyEqual(binaries, submittedStatus.binaryFiles))
132659
+ return computeWorkspaceStatus2(workspace);
132660
+ return submittedStatus;
132661
+ }
132049
132662
  function projectTransactionResultReplacements(candidateAssignments, resultAssignments) {
132050
132663
  const replacements = /* @__PURE__ */ new Map();
132051
132664
  for (const [pendingId2, assignedId] of Object.entries(resultAssignments)) {
@@ -132059,6 +132672,7 @@ function projectTransactionResultReplacements(candidateAssignments, resultAssign
132059
132672
  };
132060
132673
  }
132061
132674
  function rewriteReconstructedRecords(records2, replacements) {
132675
+ if (replacements.exact.size === 0) return records2;
132062
132676
  const rewritten = /* @__PURE__ */ new Map();
132063
132677
  for (const record4 of records2.values()) {
132064
132678
  const recordId = rewriteAssignedString(record4.recordId, replacements);
@@ -132213,15 +132827,16 @@ function foldChangedRecords(previous, changedRecords) {
132213
132827
  }
132214
132828
  return records2;
132215
132829
  }
132216
- function applyPushResult(workspace, result, pendingIdAssignments = /* @__PURE__ */ new Map()) {
132830
+ function applyPushResult(workspace, result, pendingIdAssignments = /* @__PURE__ */ new Map(), submittedStatus) {
132217
132831
  if (!isObjectRecord2(result) || !Array.isArray(result.changedRecords)) {
132218
132832
  throw new Error("Transaction response is missing changedRecords.");
132219
132833
  }
132220
- const localStatus = computeWorkspaceStatus2(workspace);
132834
+ const localStatus = statusForPushResult(workspace, submittedStatus);
132221
132835
  const preservedSourceFiles = materializeAssignedSchemaIdsInAuthoredSource(
132222
132836
  workspace,
132223
132837
  pendingIdAssignments,
132224
- localStatus.pendingValueIdentitySites
132838
+ localStatus.pendingValueIdentitySites,
132839
+ { sourceSnapshot: localStatus.sourceSnapshot }
132225
132840
  );
132226
132841
  const replacements = {
132227
132842
  exact: pendingIdAssignments,
@@ -132341,19 +132956,20 @@ function rewriteFilesFromState(workspace, records2 = new Map(
132341
132956
  )
132342
132957
  };
132343
132958
  });
132344
- const finalAnalysis = compileNeoProjectSources(
132345
- files.map((file) => {
132346
- const kind = neoProjectSourceKind(file.path, file.content);
132347
- if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
132348
- throw new Error(
132349
- `Emitted project source ${JSON.stringify(file.path)} has an unsupported path.`
132350
- );
132351
- }
132352
- return { uri: file.path, kind, text: file.content };
132353
- })
132354
- );
132959
+ const finalInputs = files.map((file) => {
132960
+ const kind = neoProjectSourceKind(file.path, file.content);
132961
+ if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
132962
+ throw new Error(
132963
+ `Emitted project source ${JSON.stringify(file.path)} has an unsupported path.`
132964
+ );
132965
+ }
132966
+ return { uri: file.path, kind, text: file.content };
132967
+ });
132968
+ const finalAnalysis = files.every(
132969
+ (file, index) => file.content === result.files[index]?.content
132970
+ ) ? result.analysis : compileNeoProjectSources(finalInputs);
132355
132971
  assertAssignedIdSourceRewriteValid(finalAnalysis);
132356
- writeProjectSourceAnalysisCacheV4(workspace.root, finalAnalysis);
132972
+ writeProjectSourceAnalysisCacheV4(workspace.root, finalAnalysis, finalInputs);
132357
132973
  const resolveSourcePath = createWorkspaceSourcePathResolver(workspace.root);
132358
132974
  const emittedPaths = new Set(files.map((file) => file.path));
132359
132975
  for (const recordState of Object.values(workspace.state.records)) {
@@ -132401,7 +133017,7 @@ ${finalErrors.map(
132401
133017
  );
132402
133018
  }
132403
133019
  }
132404
- function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, pendingValueIdentitySites = /* @__PURE__ */ new Map(), validateResult = false) {
133020
+ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, pendingValueIdentitySites = /* @__PURE__ */ new Map(), options = {}) {
132405
133021
  const pendingIdsByUri = /* @__PURE__ */ new Map();
132406
133022
  for (const pendingId2 of replacements.keys()) {
132407
133023
  if (!isPendingId(pendingId2)) continue;
@@ -132412,25 +133028,23 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
132412
133028
  pendingIdsByUri.set(uri, pendingIds);
132413
133029
  }
132414
133030
  if (pendingIdsByUri.size === 0) return /* @__PURE__ */ new Map();
132415
- const inputs = listProjectSourceFilesV4(workspace.root).map(
132416
- (absolutePath) => {
132417
- const uri = relative7(workspace.root, absolutePath).split(sep7).join("/");
132418
- const text = readFileSync16(absolutePath, "utf8");
132419
- const kind = neoProjectSourceKind(uri, text);
132420
- if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
132421
- throw new Error(
132422
- `Listed workspace source ${JSON.stringify(uri)} has an unsupported path.`
132423
- );
132424
- }
132425
- return {
132426
- uri,
132427
- kind,
132428
- text
132429
- };
133031
+ const inputs = options.sourceSnapshot?.inputs ?? listProjectSourceFilesV4(workspace.root).map((absolutePath) => {
133032
+ const uri = relative7(workspace.root, absolutePath).split(sep7).join("/");
133033
+ const text = readFileSync16(absolutePath, "utf8");
133034
+ const kind = neoProjectSourceKind(uri, text);
133035
+ if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
133036
+ throw new Error(
133037
+ `Listed workspace source ${JSON.stringify(uri)} has an unsupported path.`
133038
+ );
132430
133039
  }
132431
- );
133040
+ return {
133041
+ uri,
133042
+ kind,
133043
+ text
133044
+ };
133045
+ });
132432
133046
  const inputByUri = new Map(inputs.map((input) => [input.uri, input.text]));
132433
- const analysis = compileNeoProjectSources(inputs);
133047
+ const analysis = options.sourceSnapshot?.analysis ?? compileNeoProjectSources(inputs);
132434
133048
  if (analysis.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
132435
133049
  return /* @__PURE__ */ new Map();
132436
133050
  }
@@ -132578,7 +133192,7 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
132578
133192
  }
132579
133193
  preserved.set(uri, source);
132580
133194
  }
132581
- if (validateResult && preserved.size > 0) {
133195
+ if (options.validateResult === true && preserved.size > 0) {
132582
133196
  assertAssignedIdSourceRewriteValid(
132583
133197
  compileNeoProjectSources(
132584
133198
  inputs.map((input) => ({
@@ -133144,10 +133758,12 @@ function compileMigrationAction(context, migrationData, locator) {
133144
133758
  locator
133145
133759
  );
133146
133760
  }
133147
- var compileNSVoidBody2, compileNSFunction2, compileNSGetter2, compileNSSetter2, createNeoScriptCompilationProject2, ProjectTransactionInterruptedError, ProjectTransactionFailedError, PROJECT_TRANSACTION_POLL_DELAYS_MS, LocalCandidateArtifactError, SERVER_MINTED_PUSH_RECORD_KINDS;
133761
+ var compileNSVoidBody2, compileNSFunction2, compileNSGetter2, compileNSSetter2, createNeoScriptCompilationProject2, ProjectTransactionInterruptedError, ProjectTransactionFailedError, LocalCandidateArtifactError, SERVER_MINTED_PUSH_RECORD_KINDS;
133148
133762
  var init_push = __esm({
133149
133763
  "src/commands/push.ts"() {
133150
133764
  "use strict";
133765
+ init_project_version_transaction_result();
133766
+ init_performance();
133151
133767
  init_identity_rewriter();
133152
133768
  init_source_layout();
133153
133769
  init_src();
@@ -133163,6 +133779,7 @@ var init_push = __esm({
133163
133779
  init_push_hook();
133164
133780
  init_project_documents();
133165
133781
  init_project_document_cache();
133782
+ init_project_files();
133166
133783
  init_workspace_status();
133167
133784
  init_lower_support();
133168
133785
  init_init_conversion();
@@ -133214,14 +133831,6 @@ var init_push = __esm({
133214
133831
  }
133215
133832
  status;
133216
133833
  };
133217
- PROJECT_TRANSACTION_POLL_DELAYS_MS = [
133218
- 250,
133219
- 500,
133220
- 1e3,
133221
- 2e3,
133222
- 4e3,
133223
- 5e3
133224
- ];
133225
133834
  LocalCandidateArtifactError = class extends Error {
133226
133835
  constructor(cause) {
133227
133836
  super(cause instanceof Error ? cause.message : String(cause));
@@ -133242,7 +133851,7 @@ var init_registry2 = __esm({
133242
133851
  "schema-contract/registry.mjs"() {
133243
133852
  "use strict";
133244
133853
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
133245
- cliVersion: "0.50.6",
133854
+ cliVersion: "0.50.8",
133246
133855
  projectFileUploadBatchSize: 32,
133247
133856
  documentRecords: {
133248
133857
  member: {
@@ -134687,7 +135296,7 @@ import {
134687
135296
  sep as sep8
134688
135297
  } from "node:path";
134689
135298
  import { isDeepStrictEqual } from "node:util";
134690
- function isRecord9(value) {
135299
+ function isRecord10(value) {
134691
135300
  return typeof value === "object" && value !== null && !Array.isArray(value);
134692
135301
  }
134693
135302
  function expectation(actual, negated = false) {
@@ -134698,13 +135307,13 @@ function expectation(actual, negated = false) {
134698
135307
  return value;
134699
135308
  }
134700
135309
  function readExpectation(value) {
134701
- if (!isRecord9(value) || value.__neoExpectation !== true) {
135310
+ if (!isRecord10(value) || value.__neoExpectation !== true) {
134702
135311
  throw new NeoTestAssertionError("Matcher receiver is not an expectation.");
134703
135312
  }
134704
135313
  return value;
134705
135314
  }
134706
135315
  function readMockHandle(value) {
134707
- if (!isRecord9(value) || value.__neoMockHandle !== true || typeof value.mockId !== "number") {
135316
+ if (!isRecord10(value) || value.__neoMockHandle !== true || typeof value.mockId !== "number") {
134708
135317
  throw new NeoTestAssertionError(
134709
135318
  "Mock configuration receiver is not a mock handle."
134710
135319
  );
@@ -134815,13 +135424,13 @@ function errorMessage2(error) {
134815
135424
  return error instanceof Error ? error.message : String(error);
134816
135425
  }
134817
135426
  function findDelegateTargetMemberId(value) {
134818
- return isRecord9(value) && typeof value.memberId === "string" ? value.memberId : null;
135427
+ return isRecord10(value) && typeof value.memberId === "string" ? value.memberId : null;
134819
135428
  }
134820
135429
  function sharedEvaluatorBase(rawDocument) {
134821
135430
  const cached = SHARED_EVALUATOR_BASES.get(rawDocument);
134822
135431
  if (cached !== void 0) return cached;
134823
135432
  const document = readDocumentArrays(rawDocument);
134824
- const constructors = Array.isArray(rawDocument.constructors) ? rawDocument.constructors.filter(isRecord9) : [];
135433
+ const constructors = Array.isArray(rawDocument.constructors) ? rawDocument.constructors.filter(isRecord10) : [];
134825
135434
  const runtime = cliEvaluatorRuntime(
134826
135435
  { ...document, constructors },
134827
135436
  { includeValueGraphIndexes: true }
@@ -134890,7 +135499,7 @@ function preparedHookCandidate(workspace) {
134890
135499
  const parsed = JSON.parse(
134891
135500
  readFileSync17(join15(directory, "candidate.json"), "utf8")
134892
135501
  );
134893
- if (!isRecord9(parsed)) {
135502
+ if (!isRecord10(parsed)) {
134894
135503
  throw new NeoTestPreparedCandidateError(
134895
135504
  "Configured push hook candidate manifest must be a JSON object."
134896
135505
  );
@@ -134910,7 +135519,7 @@ function preparedHookCandidate(workspace) {
134910
135519
  "Configured push hook candidate inputs no longer match the workspace."
134911
135520
  );
134912
135521
  }
134913
- if (!isRecord9(parsed.document)) {
135522
+ if (!isRecord10(parsed.document)) {
134914
135523
  throw new NeoTestPreparedCandidateError(
134915
135524
  "Configured push hook candidate is missing its project document."
134916
135525
  );
@@ -134959,7 +135568,7 @@ function cachedTestCandidate(workspace, inputFingerprint) {
134959
135568
  try {
134960
135569
  const candidatePath = testCandidateCachePath(workspace, inputFingerprint);
134961
135570
  const parsed = JSON.parse(readFileSync17(candidatePath, "utf8"));
134962
- if (!isRecord9(parsed)) return null;
135571
+ if (!isRecord10(parsed)) return null;
134963
135572
  if (parsed.version !== 1) return null;
134964
135573
  if (parsed.candidateRevision !== TEST_CANDIDATE_CACHE_REVISION) return null;
134965
135574
  if (parsed.cliVersion !== PROJECT_SCHEMA_CONTRACT.cliVersion) return null;
@@ -134977,7 +135586,7 @@ function cachedTestCandidate(workspace, inputFingerprint) {
134977
135586
  return null;
134978
135587
  }
134979
135588
  const document = JSON.parse(documentJson);
134980
- if (!isRecord9(document)) return null;
135589
+ if (!isRecord10(document)) return null;
134981
135590
  return {
134982
135591
  document,
134983
135592
  sourceHash: parsed.sourceHash,
@@ -135021,7 +135630,7 @@ function compileSpec(workspace, document, absolutePath, projectCompilationHash2)
135021
135630
  );
135022
135631
  try {
135023
135632
  const cached = JSON.parse(readFileSync17(artifactPath, "utf8"));
135024
- if (isRecord9(cached) && cached.version === 1 && cached.compilerRevision === NEOSCRIPT_COMPILER_REVISION && cached.projectCompilationHash === projectCompilationHash2 && cached.sourceHash === sourceHash && isRecord9(cached.action) && typeof cached.artifactSha256 === "string" && createHash11("sha256").update(JSON.stringify(cached.action)).digest("hex") === cached.artifactSha256 && cached.action.compilerRevision === NEOSCRIPT_COMPILER_REVISION && Array.isArray(cached.action.parameters) && Array.isArray(cached.action.instructions) && isRecord9(cached.action.typeInfo)) {
135633
+ if (isRecord10(cached) && cached.version === 1 && cached.compilerRevision === NEOSCRIPT_COMPILER_REVISION && cached.projectCompilationHash === projectCompilationHash2 && cached.sourceHash === sourceHash && isRecord10(cached.action) && typeof cached.artifactSha256 === "string" && createHash11("sha256").update(JSON.stringify(cached.action)).digest("hex") === cached.artifactSha256 && cached.action.compilerRevision === NEOSCRIPT_COMPILER_REVISION && Array.isArray(cached.action.parameters) && Array.isArray(cached.action.instructions) && isRecord10(cached.action.typeInfo)) {
135025
135634
  return {
135026
135635
  path,
135027
135636
  source,
@@ -135185,7 +135794,7 @@ function portableDelegate(value) {
135185
135794
  }
135186
135795
  }
135187
135796
  function callMemberId(value) {
135188
- if (!isRecord9(value) || value.type !== "callFunction") return null;
135797
+ if (!isRecord10(value) || value.type !== "callFunction") return null;
135189
135798
  return typeof value.memberId === "string" ? value.memberId : null;
135190
135799
  }
135191
135800
  function assertNoNestedRegistration(value) {
@@ -135193,7 +135802,7 @@ function assertNoNestedRegistration(value) {
135193
135802
  for (const entry of value) assertNoNestedRegistration(entry);
135194
135803
  return;
135195
135804
  }
135196
- if (!isRecord9(value)) return;
135805
+ if (!isRecord10(value)) return;
135197
135806
  const memberId = callMemberId(value);
135198
135807
  if (memberId !== null && REGISTRATION_IDS.has(memberId)) {
135199
135808
  throw new NeoTestRegistrationError(
@@ -135209,8 +135818,8 @@ function compiledDelegateActions(value) {
135209
135818
  for (const child of entry) visit(child);
135210
135819
  return;
135211
135820
  }
135212
- if (!isRecord9(entry)) return;
135213
- if (isRecord9(entry.action) && Array.isArray(entry.action.instructions) && Array.isArray(entry.action.parameters) && isRecord9(entry.action.typeInfo)) {
135821
+ if (!isRecord10(entry)) return;
135822
+ if (isRecord10(entry.action) && Array.isArray(entry.action.instructions) && Array.isArray(entry.action.parameters) && isRecord10(entry.action.typeInfo)) {
135214
135823
  actions.push(entry.action);
135215
135824
  return;
135216
135825
  }
@@ -135221,7 +135830,7 @@ function compiledDelegateActions(value) {
135221
135830
  }
135222
135831
  function validateRegistrationAction(action, allowRegistration) {
135223
135832
  for (const instruction of action.instructions) {
135224
- const record4 = isRecord9(instruction) ? instruction : null;
135833
+ const record4 = isRecord10(instruction) ? instruction : null;
135225
135834
  const call = record4?.type === "functionCall" ? record4.call : null;
135226
135835
  const memberId = callMemberId(call);
135227
135836
  if (memberId !== null && REGISTRATION_IDS.has(memberId)) {
@@ -135354,8 +135963,8 @@ function mockResponse(environment, mock, selected, call) {
135354
135963
  };
135355
135964
  }
135356
135965
  function partialObjectMatch(actual, expected) {
135357
- if (!isRecord9(expected)) return isDeepStrictEqual(actual, expected);
135358
- if (!isRecord9(actual)) return false;
135966
+ if (!isRecord10(expected)) return isDeepStrictEqual(actual, expected);
135967
+ if (!isRecord10(actual)) return false;
135359
135968
  return Object.entries(expected).every(
135360
135969
  ([key, value]) => partialObjectMatch(actual[key], value)
135361
135970
  );
@@ -135547,7 +136156,7 @@ function interceptTestCallCore(environment, call) {
135547
136156
  const contains2 = typeof actual === "string" ? actual.includes(String(call.args[0])) : Array.isArray(actual) && actual.some((entry) => isDeepStrictEqual(entry, call.args[0]));
135548
136157
  assertMatcher(contains2, expectationValue, "toContain", call.args[0]);
135549
136158
  } else if (call.memberId === NEO_TEST_IDS.toHaveLength) {
135550
- const length = typeof actual === "string" || Array.isArray(actual) ? actual.length : isRecord9(actual) ? Object.keys(actual).length : -1;
136159
+ const length = typeof actual === "string" || Array.isArray(actual) ? actual.length : isRecord10(actual) ? Object.keys(actual).length : -1;
135551
136160
  assertMatcher(
135552
136161
  length === call.args[0],
135553
136162
  expectationValue,
@@ -135638,11 +136247,11 @@ function snapshotEnvironmentDocument(environment) {
135638
136247
  const values = [...baseValues];
135639
136248
  const byId = /* @__PURE__ */ new Map();
135640
136249
  values.forEach((value, index) => {
135641
- if (isRecord9(value) && typeof value.id === "string")
136250
+ if (isRecord10(value) && typeof value.id === "string")
135642
136251
  byId.set(value.id, index);
135643
136252
  });
135644
136253
  const mergeRow = (row) => {
135645
- if (!isRecord9(row) || typeof row.id !== "string") return;
136254
+ if (!isRecord10(row) || typeof row.id !== "string") return;
135646
136255
  const cloned = structuredClone(row);
135647
136256
  const index = byId.get(row.id);
135648
136257
  if (index === void 0) {
@@ -135658,7 +136267,7 @@ function snapshotEnvironmentDocument(environment) {
135658
136267
  if (bindings.size > 0) {
135659
136268
  const baseMembers = Array.isArray(snapshot.members) ? snapshot.members : [];
135660
136269
  snapshot.members = baseMembers.map((member) => {
135661
- if (!isRecord9(member) || typeof member.id !== "string" || !bindings.has(member.id)) {
136270
+ if (!isRecord10(member) || typeof member.id !== "string" || !bindings.has(member.id)) {
135662
136271
  return member;
135663
136272
  }
135664
136273
  return { ...member, valueId: bindings.get(member.id) ?? null };
@@ -139943,7 +140552,7 @@ __export(dev_exports, {
139943
140552
  import { watch } from "node:fs";
139944
140553
  import { join as join20 } from "node:path";
139945
140554
  import { emitKeypressEvents } from "node:readline";
139946
- import { ConvexClient } from "convex/browser";
140555
+ import { ConvexClient as ConvexClient2 } from "convex/browser";
139947
140556
  function isSchemaSignal(value) {
139948
140557
  if (typeof value !== "object" || value === null) return false;
139949
140558
  return typeof value.transactionHash === "string";
@@ -140018,8 +140627,14 @@ async function runDev(workspace, options) {
140018
140627
  busy = false;
140019
140628
  }
140020
140629
  };
140021
- const convex = new ConvexClient(convexUrl);
140022
- convex.setAuth(async () => await mintConvexJwt(workspace.config.apiBaseUrl));
140630
+ const convex = new ConvexClient2(convexUrl, { initialAuthTokenReuse: true });
140631
+ convex.setAuth(
140632
+ async () => await mintConvexJwt(
140633
+ workspace.config.apiBaseUrl,
140634
+ void 0,
140635
+ convexHttpActionUrl(convexUrl)
140636
+ )
140637
+ );
140023
140638
  convex.onUpdate(
140024
140639
  api2.projectExportData.schemaSignal,
140025
140640
  {
@@ -140458,7 +141073,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
140458
141073
  async function main() {
140459
141074
  const args = parseArgs(process.argv.slice(2));
140460
141075
  if (args.command === "--version") {
140461
- console.log("0.50.6");
141076
+ console.log("0.50.8");
140462
141077
  return;
140463
141078
  }
140464
141079
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -140862,12 +141477,15 @@ async function main() {
140862
141477
  return;
140863
141478
  }
140864
141479
  case "push": {
141480
+ const finishPush = startPerformanceSpan("push");
141481
+ const finishWorkspace = startPerformanceSpan("workspace");
140865
141482
  const json = boolFlag(args, "json");
140866
141483
  const preparation = process.stdout.isTTY === true && json !== true ? spinner("Preparing push\u2026") : null;
140867
141484
  let handedOffPreparation = false;
140868
141485
  try {
140869
141486
  const workspace = loadWorkspaceForCommand(args);
140870
141487
  const { runPush: runPush2 } = await Promise.resolve().then(() => (init_push(), push_exports));
141488
+ finishWorkspace();
140871
141489
  handedOffPreparation = true;
140872
141490
  await runPush2(
140873
141491
  workspace,
@@ -140886,6 +141504,8 @@ async function main() {
140886
141504
  preparation?.fail("Push preparation failed.");
140887
141505
  }
140888
141506
  throw error;
141507
+ } finally {
141508
+ finishPush();
140889
141509
  }
140890
141510
  return;
140891
141511
  }
@@ -140985,6 +141605,7 @@ var DEFAULT_API_BASE_URL;
140985
141605
  var init_main = __esm({
140986
141606
  "src/main.ts"() {
140987
141607
  "use strict";
141608
+ init_performance();
140988
141609
  init_auth_api_base_url();
140989
141610
  init_login();
140990
141611
  init_args();