@neocompose/cli 0.50.7 → 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)}
@@ -82365,25 +82409,66 @@ function resolvedStoredInstanceRows(args) {
82365
82409
  const rows = new Map(document.values.map((row) => [row.id, row]));
82366
82410
  const materializedIndex = buildVirtualInstanceMaterializedIndex({ document });
82367
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
+ };
82368
82448
  const stored = indexExpansion({
82369
82449
  document,
82370
82450
  instanceRoot: args.instanceRoot,
82371
82451
  rootMember: args.rootMember,
82372
82452
  expandedRoot: rows.get(args.instanceRoot.id) ?? args.instanceRoot,
82373
- expandedRows: document.values
82453
+ expandedRows: document.values,
82454
+ additionalRoots: argumentRoots
82374
82455
  });
82375
- for (const node of stored.nodesByPath.values()) {
82376
- if (node.expandedRow.id === args.instanceRoot.id) continue;
82377
- if (!isMemberClassBase(node.member)) continue;
82378
- if (!isStoredClassResolutionRoot(node.expandedRow)) continue;
82379
- queue.push({ root: node.expandedRow, member: node.member });
82380
- }
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());
82381
82465
  const resolvedRootIds = /* @__PURE__ */ new Set();
82382
82466
  const resolvedFrameByValueId = /* @__PURE__ */ new Map();
82383
- while (queue.length > 0) {
82384
- const next = queue.shift();
82467
+ for (let index = 0; index < queue.length; index += 1) {
82468
+ const next = queue[index];
82385
82469
  if (next === void 0 || resolvedRootIds.has(next.root.id)) continue;
82386
82470
  resolvedRootIds.add(next.root.id);
82471
+ enqueueStoredRoots(stored.includeRoot(next.root, next.member));
82387
82472
  const expanded = expandStoredClassValue({
82388
82473
  evaluateInitializer: args.evaluateInitializer,
82389
82474
  document,
@@ -83263,6 +83348,7 @@ function indexExpansion(args) {
83263
83348
  if (paths === void 0) childPathsByParentPath.set(parentPath, [pathKey]);
83264
83349
  else paths.push(pathKey);
83265
83350
  };
83351
+ let discoveredNodes = null;
83266
83352
  const visit = (rowId, rawMember, pathKey, parentPath, parentSegment, genericEnv) => {
83267
83353
  const row = rowsByExpandedId.get(rowId);
83268
83354
  if (row === void 0 || nodesByExpandedId.has(rowId)) return;
@@ -83297,6 +83383,7 @@ function indexExpansion(args) {
83297
83383
  virtualId
83298
83384
  };
83299
83385
  nodesByExpandedId.set(rowId, node);
83386
+ discoveredNodes?.push(node);
83300
83387
  nodesByPath.set(pathKey, node);
83301
83388
  appendChildPath(parentPath, pathKey);
83302
83389
  const childEnv = row.genericBindings ? envFromStamp(row.genericBindings) : genericEnv;
@@ -83399,15 +83486,49 @@ function indexExpansion(args) {
83399
83486
  );
83400
83487
  }
83401
83488
  };
83402
- visit(
83403
- args.expandedRoot.id,
83404
- args.rootMember,
83405
- ROOT_PATH,
83406
- null,
83407
- null,
83408
- /* @__PURE__ */ new Map()
83409
- );
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
+ }
83410
83530
  return {
83531
+ includeRoot,
83411
83532
  nodesByExpandedId,
83412
83533
  nodesByPath,
83413
83534
  rowsByExpandedId,
@@ -109738,17 +109859,17 @@ function computeWorkspaceStatus(workspace, options) {
109738
109859
  useBuildCaches: options.useBuildCaches !== false
109739
109860
  })
109740
109861
  });
109862
+ const compilationSources = projectSources.map((source) => ({
109863
+ uri: source.uri,
109864
+ kind: source.kind,
109865
+ text: source.source
109866
+ }));
109741
109867
  try {
109742
109868
  if (!baseManifest) {
109743
109869
  throw new Error(
109744
109870
  "Format-4 status requires a pulled schema base; run `neo pull --reset`."
109745
109871
  );
109746
109872
  }
109747
- const compilationSources = projectSources.map((source) => ({
109748
- uri: source.uri,
109749
- kind: source.kind,
109750
- text: source.source
109751
- }));
109752
109873
  const analysis = options.readProjectAnalysisCache?.(workspace.root, compilationSources) ?? options.compileProjectAnalysis?.(workspace.root, compilationSources) ?? compileNeoProjectSources(compilationSources);
109753
109874
  projectAnalysisV4 = analysis;
109754
109875
  for (const diagnostic of analysis.diagnostics) {
@@ -110451,6 +110572,12 @@ function computeWorkspaceStatus(workspace, options) {
110451
110572
  }
110452
110573
  reportPhase("initializer-materialization");
110453
110574
  return {
110575
+ ...projectAnalysisV4 === null ? {} : {
110576
+ sourceSnapshot: {
110577
+ inputs: compilationSources,
110578
+ analysis: projectAnalysisV4
110579
+ }
110580
+ },
110454
110581
  localValues: valueLowerRegistry.localValues?.placements(),
110455
110582
  changes,
110456
110583
  conflictedFiles,
@@ -123313,6 +123440,7 @@ function computeWorkspaceStatus2(workspace, options = {}) {
123313
123440
  }));
123314
123441
  return computeWorkspaceStatus(workspace, {
123315
123442
  ...options,
123443
+ reportPhase: options.reportPhase ?? ((phase, milliseconds) => recordPerformance(`workspace-${phase}`, milliseconds)),
123316
123444
  virtualSourceFiles,
123317
123445
  writeProjectAnalysisCache: options.writeProjectAnalysisCache ?? writeProjectSourceAnalysisCacheV4,
123318
123446
  readProjectAnalysisCache: options.readProjectAnalysisCache ?? readProjectSourceAnalysisBuildCacheV4,
@@ -123329,6 +123457,7 @@ var init_workspace_status = __esm({
123329
123457
  "use strict";
123330
123458
  import_ignore = __toESM(require_ignore(), 1);
123331
123459
  init_src();
123460
+ init_performance();
123332
123461
  init_project_document_cache();
123333
123462
  init_project_files();
123334
123463
  init_supplemental_records_file_system();
@@ -123465,6 +123594,164 @@ var init_status_output = __esm({
123465
123594
  }
123466
123595
  });
123467
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
+
123468
123755
  // ../convex/_generated/api.js
123469
123756
  var api_exports = {};
123470
123757
  __export(api_exports, {
@@ -123487,35 +123774,62 @@ var init_api = __esm({
123487
123774
  var convex_exports = {};
123488
123775
  __export(convex_exports, {
123489
123776
  api: () => api2,
123777
+ convexHttpActionUrl: () => convexHttpActionUrl,
123490
123778
  createConvexClient: () => createConvexClient,
123491
123779
  createConvexClientForApi: () => createConvexClientForApi,
123780
+ createConvexPushConnection: () => createConvexPushConnection,
123492
123781
  mintConvexJwt: () => mintConvexJwt,
123493
123782
  resolveConvexUrl: () => resolveConvexUrl
123494
123783
  });
123495
- import { ConvexHttpClient } from "convex/browser";
123784
+ import { gzipSync } from "node:zlib";
123785
+ import { ConvexClient, ConvexHttpClient } from "convex/browser";
123496
123786
  import { Buffer as Buffer2 } from "node:buffer";
123497
- async function mintConvexJwt(apiBaseUrl) {
123498
- const sessionToken = loadToken(apiBaseUrl);
123499
- if (sessionToken === null) {
123500
- throw new Error(
123501
- `No credentials stored for "${apiBaseUrl}". Run "neo login".`
123502
- );
123503
- }
123504
- const response = await fetch(new URL("/api/auth/convex/token", apiBaseUrl), {
123505
- headers: { Authorization: `Bearer ${sessionToken}` }
123506
- });
123507
- const body = await response.json();
123508
- if (!response.ok) {
123509
- throw new Error(
123510
- `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
+ }
123511
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();
123512
123830
  }
123513
- if (typeof body !== "object" || body === null || typeof body.token !== "string") {
123514
- throw new Error('Convex token response is missing a string "token".');
123515
- }
123516
- return body.token;
123517
123831
  }
123518
- async function resolveConvexUrl(workspace) {
123832
+ async function resolveConvexUrl(workspace, signal) {
123519
123833
  const fromEnv = process.env.NEXT_PUBLIC_CONVEX_URL;
123520
123834
  if (fromEnv !== void 0 && fromEnv.length > 0) return fromEnv;
123521
123835
  const persisted = workspace.config.convexUrl;
@@ -123533,7 +123847,8 @@ async function resolveConvexUrl(workspace) {
123533
123847
  ),
123534
123848
  {
123535
123849
  method: "POST",
123536
- headers: { Authorization: `Bearer ${sessionToken}` }
123850
+ headers: { Authorization: `Bearer ${sessionToken}` },
123851
+ signal
123537
123852
  }
123538
123853
  );
123539
123854
  const convexUrl = response.headers.get("x-neo-convex-url");
@@ -123549,11 +123864,12 @@ async function resolveConvexUrl(workspace) {
123549
123864
  writeWorkspaceConfig(workspace.root, workspace.config);
123550
123865
  return convexUrl;
123551
123866
  }
123552
- async function createConvexClient(workspace) {
123553
- const convexUrl = await resolveConvexUrl(workspace);
123867
+ async function createConvexClient(workspace, options = {}) {
123868
+ const convexUrl = await resolveConvexUrl(workspace, options.signal);
123554
123869
  return await createAuthenticatedConvexClient(
123555
123870
  convexUrl,
123556
- workspace.config.apiBaseUrl
123871
+ workspace.config.apiBaseUrl,
123872
+ options.signal
123557
123873
  );
123558
123874
  }
123559
123875
  async function createConvexClientForApi(apiBaseUrl) {
@@ -123574,22 +123890,35 @@ async function createConvexClientForApi(apiBaseUrl) {
123574
123890
  }
123575
123891
  return await createAuthenticatedConvexClient(convexUrl, apiBaseUrl);
123576
123892
  }
123577
- async function createAuthenticatedConvexClient(convexUrl, apiBaseUrl) {
123578
- 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
+ );
123579
123899
  const initialToken = await tokenCache.getToken();
123580
123900
  const authenticatedFetch = async (input, init) => {
123581
123901
  const headers = new Headers(
123582
123902
  init?.headers ?? (input instanceof Request ? input.headers : void 0)
123583
123903
  );
123584
123904
  headers.set("Authorization", `Bearer ${await tokenCache.getToken()}`);
123585
- 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
+ }
123586
123915
  };
123587
123916
  return new ConvexHttpClient(convexUrl, {
123588
123917
  auth: initialToken,
123589
123918
  fetch: authenticatedFetch
123590
123919
  });
123591
123920
  }
123592
- function convexJwtExpiration(token) {
123921
+ function convexJwtPayload(token) {
123593
123922
  const segments = token.split(".");
123594
123923
  if (segments.length !== 3) {
123595
123924
  throw new Error("Convex token response is not a three-segment JWT.");
@@ -123608,9 +123937,16 @@ function convexJwtExpiration(token) {
123608
123937
  } catch {
123609
123938
  throw new Error("Convex token response has an invalid JWT payload.");
123610
123939
  }
123611
- if (typeof payload !== "object" || payload === null) {
123940
+ if (typeof payload !== "object" || payload === null || Array.isArray(payload)) {
123612
123941
  throw new Error("Convex token response has a non-object JWT payload.");
123613
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);
123614
123950
  if (!("exp" in payload)) {
123615
123951
  throw new Error('Convex token response JWT is missing an "exp" claim.');
123616
123952
  }
@@ -123642,10 +123978,93 @@ function missingProjectsConvexUrlError() {
123642
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)."
123643
123979
  );
123644
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
+ }
123645
124061
  var generatedApiModule, generatedApiValue, api2, CONVEX_JWT_REFRESH_LEEWAY_MS, ConvexJwtCache;
123646
124062
  var init_convex = __esm({
123647
124063
  "src/convex.ts"() {
123648
124064
  "use strict";
124065
+ init_http();
124066
+ init_schema_commit_transport();
124067
+ init_performance();
123649
124068
  init_api();
123650
124069
  init_token_store();
123651
124070
  init_workspace();
@@ -123657,18 +124076,22 @@ var init_convex = __esm({
123657
124076
  api2 = generatedApiValue;
123658
124077
  CONVEX_JWT_REFRESH_LEEWAY_MS = 6e4;
123659
124078
  ConvexJwtCache = class {
123660
- constructor(apiBaseUrl) {
124079
+ constructor(apiBaseUrl, signal, convexSiteUrl) {
123661
124080
  this.apiBaseUrl = apiBaseUrl;
124081
+ this.signal = signal;
124082
+ this.convexSiteUrl = convexSiteUrl;
123662
124083
  }
123663
124084
  apiBaseUrl;
124085
+ signal;
124086
+ convexSiteUrl;
123664
124087
  token = null;
123665
124088
  expiresAt = 0;
123666
124089
  refreshPromise = null;
123667
- async getToken() {
123668
- 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) {
123669
124092
  return this.token;
123670
124093
  }
123671
- const pending = this.refreshPromise ?? this.refresh();
124094
+ const pending = this.refreshPromise ?? this.refresh(signal);
123672
124095
  this.refreshPromise = pending;
123673
124096
  try {
123674
124097
  return await pending;
@@ -123676,8 +124099,12 @@ var init_convex = __esm({
123676
124099
  if (this.refreshPromise === pending) this.refreshPromise = null;
123677
124100
  }
123678
124101
  }
123679
- async refresh() {
123680
- const token = await mintConvexJwt(this.apiBaseUrl);
124102
+ async refresh(signal) {
124103
+ const token = await mintConvexJwt(
124104
+ this.apiBaseUrl,
124105
+ signal,
124106
+ this.convexSiteUrl
124107
+ );
123681
124108
  const expiresAt = convexJwtExpiration(token);
123682
124109
  if (expiresAt <= Date.now()) {
123683
124110
  throw new Error("Convex token response contains an expired JWT.");
@@ -124614,119 +125041,6 @@ var init_reset = __esm({
124614
125041
  }
124615
125042
  });
124616
125043
 
124617
- // src/http.ts
124618
- var NeoApiError, NeoApiClient;
124619
- var init_http = __esm({
124620
- "src/http.ts"() {
124621
- "use strict";
124622
- init_token_store();
124623
- NeoApiError = class extends Error {
124624
- constructor(message, status, body) {
124625
- super(message);
124626
- this.status = status;
124627
- this.body = body;
124628
- this.name = "NeoApiError";
124629
- }
124630
- status;
124631
- body;
124632
- };
124633
- NeoApiClient = class _NeoApiClient {
124634
- constructor(apiBaseUrl, token) {
124635
- this.apiBaseUrl = apiBaseUrl;
124636
- this.token = token;
124637
- }
124638
- apiBaseUrl;
124639
- token;
124640
- static forBaseUrl(apiBaseUrl) {
124641
- const token = loadToken(apiBaseUrl);
124642
- if (token === null) {
124643
- throw new Error(
124644
- `No credentials stored for "${apiBaseUrl}". Run "neo login" (or set NEO_COMPOSE_TOKEN).`
124645
- );
124646
- }
124647
- return new _NeoApiClient(apiBaseUrl, token);
124648
- }
124649
- async post(path, body, headers, options = {}) {
124650
- const url = new URL(path, this.apiBaseUrl).toString();
124651
- const response = await fetch(url, {
124652
- method: "POST",
124653
- headers: {
124654
- Authorization: `Bearer ${this.token}`,
124655
- "Content-Type": "application/json",
124656
- ...headers
124657
- },
124658
- body: body === void 0 ? void 0 : JSON.stringify(body),
124659
- signal: options.signal
124660
- });
124661
- const text = await response.text();
124662
- let parsed = null;
124663
- if (text.length > 0) {
124664
- try {
124665
- parsed = JSON.parse(text);
124666
- } catch {
124667
- parsed = text;
124668
- }
124669
- }
124670
- if (!response.ok) {
124671
- const detail = typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string" ? parsed.error : text.slice(0, 300);
124672
- throw new NeoApiError(
124673
- `POST ${path} failed with status ${response.status}: ${detail}`,
124674
- response.status,
124675
- parsed
124676
- );
124677
- }
124678
- return parsed;
124679
- }
124680
- async get(path, options = {}) {
124681
- const url = new URL(path, this.apiBaseUrl).toString();
124682
- const response = await fetch(url, {
124683
- method: "GET",
124684
- headers: { Authorization: `Bearer ${this.token}` },
124685
- signal: options.signal
124686
- });
124687
- const text = await response.text();
124688
- let parsed = null;
124689
- if (text.length > 0) {
124690
- try {
124691
- parsed = JSON.parse(text);
124692
- } catch {
124693
- parsed = text;
124694
- }
124695
- }
124696
- if (!response.ok) {
124697
- const detail = typeof parsed === "object" && parsed !== null && "error" in parsed && typeof parsed.error === "string" ? parsed.error : text.slice(0, 300);
124698
- throw new NeoApiError(
124699
- `GET ${path} failed with status ${response.status}: ${detail}`,
124700
- response.status,
124701
- parsed
124702
- );
124703
- }
124704
- return parsed;
124705
- }
124706
- async getBytes(path) {
124707
- const url = new URL(path, this.apiBaseUrl).toString();
124708
- const response = await fetch(url, {
124709
- method: "GET",
124710
- headers: { Authorization: `Bearer ${this.token}` }
124711
- });
124712
- if (!response.ok) {
124713
- const detail = (await response.text()).slice(0, 300);
124714
- throw new NeoApiError(
124715
- `GET ${path} failed with status ${response.status}: ${detail}`,
124716
- response.status,
124717
- detail
124718
- );
124719
- }
124720
- return {
124721
- bytes: new Uint8Array(await response.arrayBuffer()),
124722
- contentSha256: response.headers.get("x-neo-content-sha256"),
124723
- contentType: response.headers.get("content-type")
124724
- };
124725
- }
124726
- };
124727
- }
124728
- });
124729
-
124730
125044
  // src/project-source/project-file-pull.ts
124731
125045
  import { existsSync as existsSync7, rmSync as rmSync4 } from "node:fs";
124732
125046
  import { join as join9 } from "node:path";
@@ -129163,6 +129477,78 @@ var init_script = __esm({
129163
129477
  }
129164
129478
  });
129165
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
+
129166
129552
  // src/project-source/identity-rewriter.ts
129167
129553
  function createIdentityRewriter(assignments) {
129168
129554
  if (assignments.size === 0) return (value) => value;
@@ -130278,6 +130664,7 @@ function assignPendingIds(changes, authoredValueSeeds, reconstructed3, localInit
130278
130664
  }
130279
130665
  const rewriteString = createIdentityRewriter(assigned);
130280
130666
  const rewrite = (value) => {
130667
+ if (assigned.size === 0) return value;
130281
130668
  if (typeof value === "string") {
130282
130669
  return rewriteString(value);
130283
130670
  }
@@ -130348,7 +130735,7 @@ function assignPendingIds(changes, authoredValueSeeds, reconstructed3, localInit
130348
130735
  });
130349
130736
  }
130350
130737
  }
130351
- if (reconstructed3 !== void 0) {
130738
+ if (reconstructed3 !== void 0 && assigned.size > 0) {
130352
130739
  const rewrittenRecords = /* @__PURE__ */ new Map();
130353
130740
  for (const record4 of reconstructed3.values()) {
130354
130741
  const rewrittenRecordId = rewrite(record4.recordId);
@@ -130471,6 +130858,10 @@ function readProjectVersionTransactionStatus(value, transactionId) {
130471
130858
  return {
130472
130859
  transactionId,
130473
130860
  commitStatus: value.commitStatus,
130861
+ transactionHash: readNullableString(
130862
+ value.transactionHash,
130863
+ `Project transaction "${transactionId}" transactionHash`
130864
+ ),
130474
130865
  totalChangeCount: readNullableCount(
130475
130866
  value.totalChangeCount,
130476
130867
  `Project transaction "${transactionId}" totalChangeCount`
@@ -130680,66 +131071,164 @@ function progressEventFromStatus(status) {
130680
131071
  };
130681
131072
  }
130682
131073
  async function waitForAcceptedProjectVersionTransaction(args) {
130683
- const { client, projectId, versionId, accepted, reporter } = args;
130684
- const transactionPath = `/api/projects/${projectId}/versions/${versionId}/schema/transactions/${accepted.transactionId}`;
131074
+ const { workspace, accepted, reporter } = args;
131075
+ const { projectId, versionId } = workspace.config;
130685
131076
  const controller = new AbortController();
130686
- let interrupted = false;
130687
- const interrupt = () => {
130688
- interrupted = true;
130689
- controller.abort();
130690
- };
131077
+ const interrupt = () => controller.abort(
131078
+ new ProjectTransactionInterruptedError(accepted.transactionId)
131079
+ );
130691
131080
  process.once("SIGINT", interrupt);
130692
131081
  reporter.report(progressEventFromAccepted(accepted));
130693
- let pollIndex = 0;
131082
+ const finishWait = startPerformanceSpan("transaction-wait");
131083
+ let client;
131084
+ let unsubscribe;
131085
+ let progressTimer;
131086
+ let settled = false;
130694
131087
  try {
130695
- for (; ; ) {
130696
- const rawStatus = await client.get(transactionPath, {
130697
- signal: controller.signal
130698
- });
130699
- const status = readProjectVersionTransactionStatus(
130700
- rawStatus,
130701
- accepted.transactionId
130702
- );
130703
- reporter.report(progressEventFromStatus(status));
130704
- 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 {
130705
131188
  return await downloadProjectVersionTransactionResult({
130706
131189
  client,
130707
- transactionPath,
131190
+ projectId,
131191
+ versionId,
130708
131192
  transactionId: accepted.transactionId,
130709
131193
  status,
131194
+ firstResultPage,
130710
131195
  signal: controller.signal
130711
131196
  });
131197
+ } finally {
131198
+ finishDownload();
130712
131199
  }
130713
- if (status.commitStatus === "failed") {
130714
- throw new ProjectTransactionFailedError(status);
130715
- }
130716
- const delay = PROJECT_TRANSACTION_POLL_DELAYS_MS[Math.min(pollIndex, PROJECT_TRANSACTION_POLL_DELAYS_MS.length - 1)];
130717
- pollIndex += 1;
130718
- await waitForPollDelay(delay, controller.signal);
130719
- }
130720
- } catch (error) {
130721
- if (interrupted) {
130722
- throw new ProjectTransactionInterruptedError(accepted.transactionId);
131200
+ } finally {
131201
+ if (firstStatus) finishFirstStatus();
130723
131202
  }
130724
- throw error;
130725
131203
  } finally {
131204
+ settled = true;
131205
+ clearTimeout(progressTimer);
131206
+ unsubscribe?.();
131207
+ controller.abort();
131208
+ if (client !== void 0) await client.close();
131209
+ finishWait();
130726
131210
  process.removeListener("SIGINT", interrupt);
130727
131211
  }
130728
131212
  }
130729
- async function waitForPollDelay(delayMs, signal) {
130730
- if (signal.aborted) {
130731
- throw new Error("Project transaction polling was interrupted.");
130732
- }
130733
- await new Promise((resolve5, reject) => {
130734
- const aborted = () => {
130735
- clearTimeout(timer);
130736
- 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);
130737
131219
  };
130738
- const timer = setTimeout(() => {
130739
- signal.removeEventListener("abort", aborted);
130740
- resolve5();
130741
- }, delayMs);
130742
- 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
+ );
130743
131232
  });
130744
131233
  }
130745
131234
  async function downloadProjectVersionTransactionResult(args) {
@@ -130754,11 +131243,20 @@ async function downloadProjectVersionTransactionResult(args) {
130754
131243
  );
130755
131244
  }
130756
131245
  seenCursors.add(cursor);
130757
- const query = new URLSearchParams({ cursor });
130758
- const rawPage = await args.client.get(
130759
- `${args.transactionPath}/results?${query.toString()}`,
130760
- { 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
130761
131258
  );
131259
+ const rawPage = storedPage === null ? null : decodeProjectVersionTransactionResultPage(storedPage);
130762
131260
  const page = readProjectVersionTransactionResultPage(
130763
131261
  rawPage,
130764
131262
  args.transactionId
@@ -130784,8 +131282,13 @@ async function downloadProjectVersionTransactionResult(args) {
130784
131282
  `Project transaction "${args.transactionId}" committed ${args.status.totalChangeCount} changes but its result download returned ${changedRecords.length}.`
130785
131283
  );
130786
131284
  }
131285
+ if (args.status.transactionHash === null)
131286
+ throw new Error(
131287
+ `Committed project transaction "${args.transactionId}" has no transaction hash.`
131288
+ );
130787
131289
  return {
130788
131290
  kind: "committed",
131291
+ headTransactionHash: args.status.transactionHash,
130789
131292
  transactionId: args.transactionId,
130790
131293
  totalChangeCount: args.status.totalChangeCount,
130791
131294
  totalChunkCount: args.status.totalChunkCount ?? 0,
@@ -130900,6 +131403,7 @@ async function runPush(workspace, options, preparationOverride) {
130900
131403
  const verifiesConfiguredHook = options.noVerify !== true && configuredHook !== void 0;
130901
131404
  const preparedInputFingerprint = verifiesConfiguredHook ? fingerprintPushInputs(workspace) : void 0;
130902
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");
130903
131407
  let candidate;
130904
131408
  try {
130905
131409
  candidate = await prepareLocalCandidateV4(workspace, {
@@ -130922,6 +131426,8 @@ async function runPush(workspace, options, preparationOverride) {
130922
131426
  }
130923
131427
  preparation?.fail("Push preparation failed.");
130924
131428
  throw error;
131429
+ } finally {
131430
+ finishCandidate();
130925
131431
  }
130926
131432
  const status = candidate.status;
130927
131433
  const local = candidate.preparedLocal;
@@ -131102,7 +131608,12 @@ async function runPush(workspace, options, preparationOverride) {
131102
131608
  };
131103
131609
  let preparedPush;
131104
131610
  try {
131105
- preparedPush = await stagePreparedPush();
131611
+ const finishUploads = startPerformanceSpan("file-uploads");
131612
+ try {
131613
+ preparedPush = await stagePreparedPush();
131614
+ } finally {
131615
+ finishUploads();
131616
+ }
131106
131617
  } catch (error) {
131107
131618
  progress.stop();
131108
131619
  if (error instanceof ProjectFilePushCancelledError) {
@@ -131122,9 +131633,9 @@ async function runPush(workspace, options, preparationOverride) {
131122
131633
  stagedUploadSessionId
131123
131634
  } = preparedPush;
131124
131635
  const { stagedFiles } = preparedPush;
131125
- const commit = async (force) => await client.post(
131126
- `/api/projects/${workspace.config.projectId}/versions/${workspace.config.versionId}/schema/commit`,
131127
- {
131636
+ let pushConnection;
131637
+ const commit = async (force) => {
131638
+ const body = {
131128
131639
  operation: transactionOperation,
131129
131640
  changes: transportChanges,
131130
131641
  authoredValueSeeds: transportSeeds,
@@ -131139,9 +131650,20 @@ async function runPush(workspace, options, preparationOverride) {
131139
131650
  headTransactionHash: workspace.state.headTransactionHash ?? null,
131140
131651
  derivedEdgeExtractorVersion: PROJECT_SNAPSHOT_DERIVED_EDGE_EXTRACTOR_VERSION,
131141
131652
  forceRecompile: options.forceRecompile === true
131142
- },
131143
- force ? { "x-neo-force-project-version-write": "true" } : void 0
131144
- );
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
+ };
131145
131667
  const finishCommitResponse = async (response, reporter) => {
131146
131668
  let accepted;
131147
131669
  try {
@@ -131156,7 +131678,12 @@ async function runPush(workspace, options, preparationOverride) {
131156
131678
  if (!applyCommittedHeadTransactionHash(workspace, response)) {
131157
131679
  await recoverHeadTransactionHashFromSchemaSignal(workspace);
131158
131680
  }
131159
- applyPushResult(workspace, response, pendingAssignment.assigned);
131681
+ applyPushResult(
131682
+ workspace,
131683
+ response,
131684
+ pendingAssignment.assigned,
131685
+ preparedLocal.submittedStatus
131686
+ );
131160
131687
  } catch (error) {
131161
131688
  reporter.stop();
131162
131689
  throw error;
@@ -131184,11 +131711,10 @@ async function runPush(workspace, options, preparationOverride) {
131184
131711
  let completed;
131185
131712
  try {
131186
131713
  completed = await waitForAcceptedProjectVersionTransaction({
131187
- client,
131188
- projectId: workspace.config.projectId,
131189
- versionId: workspace.config.versionId,
131714
+ workspace,
131190
131715
  accepted,
131191
- reporter
131716
+ reporter,
131717
+ connection: pushConnection
131192
131718
  });
131193
131719
  } catch (error) {
131194
131720
  if (error instanceof ProjectTransactionInterruptedError) {
@@ -131234,15 +131760,19 @@ async function runPush(workspace, options, preparationOverride) {
131234
131760
  throw error;
131235
131761
  }
131236
131762
  try {
131237
- if (!applyCommittedHeadTransactionHash(workspace, completed)) {
131238
- 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();
131239
131775
  }
131240
- applyCommittedProjectTransactionResult({
131241
- workspace,
131242
- result: completed,
131243
- submittedChanges: status.changes,
131244
- candidateAssignments: pendingAssignment.assigned
131245
- });
131246
131776
  } catch (error) {
131247
131777
  reporter.stop();
131248
131778
  throw error;
@@ -131282,7 +131812,12 @@ async function runPush(workspace, options, preparationOverride) {
131282
131812
  }
131283
131813
  let result;
131284
131814
  try {
131285
- result = await commit(options.acceptBump);
131815
+ const finishSubmit = startPerformanceSpan("submit");
131816
+ try {
131817
+ result = await commit(options.acceptBump);
131818
+ } finally {
131819
+ finishSubmit();
131820
+ }
131286
131821
  } catch (error) {
131287
131822
  progress.stop();
131288
131823
  if (!(error instanceof NeoApiError)) throw error;
@@ -131362,12 +131897,19 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
131362
131897
  fullValidation: true,
131363
131898
  forceRecompile: false
131364
131899
  }) {
131365
- onPhase("Hashing canonical project source\u2026");
131366
- 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(
131367
131909
  workspace,
131368
131910
  status,
131369
131911
  status.authoredValueSeeds
131370
- );
131912
+ ) : null;
131371
131913
  const pendingTransportChanges = transportChangesForStatus(status);
131372
131914
  const pendingTransportSeeds = [...status.authoredValueSeeds].map(
131373
131915
  ([memberId, seed]) => ({ memberId, ...seed })
@@ -131401,20 +131943,20 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
131401
131943
  localizedTextCreateEnvelopeConfig2(workspace, status.changes)
131402
131944
  );
131403
131945
  let preparedChanges = null;
131404
- if (options.fullValidation) {
131946
+ if (verificationSource !== null) {
131405
131947
  onPhase("Verifying the assigned-id source rewrite\u2026");
131406
131948
  materializeAssignedSchemaIdsInAuthoredSource(
131407
131949
  workspace,
131408
131950
  pendingAssignment.assigned,
131409
131951
  status.pendingValueIdentitySites,
131410
- true
131952
+ { sourceSnapshot: status.sourceSnapshot, validateResult: true }
131411
131953
  );
131412
131954
  onPhase("Verifying the complete source round trip\u2026");
131413
131955
  verifyProjectSourceCommitAgainstStateV4({
131414
131956
  projectId: workspace.config.projectId,
131415
131957
  versionId: workspace.config.versionId,
131416
131958
  stateRecords: workspace.state.records,
131417
- files: source.files,
131959
+ files: verificationSource.files,
131418
131960
  changes: transportChanges,
131419
131961
  authoredValueSeeds: transportSeeds,
131420
131962
  initializerMaterialization: pendingAssignment.localInitializerMaterialization ?? { roots: [] },
@@ -131439,6 +131981,7 @@ async function prepareLocalPushArtifactsV4(workspace, status, onPhase = () => vo
131439
131981
  }
131440
131982
  return {
131441
131983
  source,
131984
+ submittedStatus,
131442
131985
  pendingAssignment,
131443
131986
  transactionOperation,
131444
131987
  transportChanges,
@@ -131468,7 +132011,7 @@ async function prepareLocalCandidateV4(workspace, options = {}) {
131468
132011
  let documentStatus = status;
131469
132012
  if (status.changes.length > 0 || status.authoredValueSeeds.size > 0) {
131470
132013
  try {
131471
- documentStatus = pushOptions.dryRun ? cloneStatusForDryRun(status) : status;
132014
+ documentStatus = pushOptions.dryRun ? cloneWorkspaceStatus(status) : status;
131472
132015
  if (options.refreshDerivedEdgeVersion === true) {
131473
132016
  const convex = await createConvexClient(workspace);
131474
132017
  const signal = await convex.query(api2.projectExportData.schemaSignal, {
@@ -131494,20 +132037,7 @@ async function prepareLocalCandidateV4(workspace, options = {}) {
131494
132037
  }
131495
132038
  sourceHash = preparedLocal.source.sourceHash;
131496
132039
  } else {
131497
- const files = listProjectSourceFilesV4(workspace.root).map(
131498
- (absolutePath) => {
131499
- const path = relative7(workspace.root, absolutePath).split(sep7).join("/");
131500
- const content = readFileSync16(absolutePath, "utf8");
131501
- const kind = neoProjectSourceKind(path, content);
131502
- if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
131503
- throw new Error(
131504
- `Listed workspace source ${JSON.stringify(path)} is not production Neo source.`
131505
- );
131506
- }
131507
- return { path, kind, content };
131508
- }
131509
- );
131510
- sourceHash = hashProjectSourceFiles(files);
132040
+ sourceHash = projectSourceIdentityForStatus(status).sourceHash;
131511
132041
  }
131512
132042
  if (options.includeDocument === false) {
131513
132043
  return { status, document: null, sourceHash, preparedLocal };
@@ -131648,7 +132178,7 @@ function describeChangeOrigin(change) {
131648
132178
  if (parts.length === 0) return "";
131649
132179
  return ` (${parts.join(" in ")})`;
131650
132180
  }
131651
- function cloneStatusForDryRun(status) {
132181
+ function cloneWorkspaceStatus(status) {
131652
132182
  return {
131653
132183
  ...status,
131654
132184
  changes: structuredClone(status.changes),
@@ -131700,6 +132230,19 @@ function reportDryRunFailure(error, json) {
131700
132230
  }
131701
132231
  console.error(`Dry run failed: ${message}`);
131702
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
+ }
131703
132246
  function createPendingProjectSourceIdentityV4(workspace, status, authoredValueSeeds) {
131704
132247
  const records2 = /* @__PURE__ */ new Map();
131705
132248
  for (const [key, state] of Object.entries(workspace.state.records)) {
@@ -131981,7 +132524,7 @@ function reportAdditionalServerChanges(totalChangeCount, authoredChangeCount, js
131981
132524
  }
131982
132525
  function applyCommittedProjectTransactionResult(args) {
131983
132526
  const { workspace, result, submittedChanges, candidateAssignments } = args;
131984
- const localStatus = computeWorkspaceStatus2(workspace);
132527
+ const localStatus = statusForPushResult(workspace, args.submittedStatus);
131985
132528
  const blockingErrors = localStatus.parseErrors.filter(
131986
132529
  isBlockingSchemaSourceError
131987
132530
  );
@@ -132003,7 +132546,8 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
132003
132546
  const preservedSourceFiles = materializeAssignedSchemaIdsInAuthoredSource(
132004
132547
  workspace,
132005
132548
  replacements.exact,
132006
- localStatus.pendingValueIdentitySites
132549
+ localStatus.pendingValueIdentitySites,
132550
+ { sourceSnapshot: localStatus.sourceSnapshot }
132007
132551
  );
132008
132552
  const localRecords = rewriteReconstructedRecords(
132009
132553
  localStatus.reconstructed,
@@ -132083,6 +132627,38 @@ ${blockingErrors.map((error) => ` ${error.message}`).join("\n")}`
132083
132627
  throw error;
132084
132628
  }
132085
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
+ }
132086
132662
  function projectTransactionResultReplacements(candidateAssignments, resultAssignments) {
132087
132663
  const replacements = /* @__PURE__ */ new Map();
132088
132664
  for (const [pendingId2, assignedId] of Object.entries(resultAssignments)) {
@@ -132096,6 +132672,7 @@ function projectTransactionResultReplacements(candidateAssignments, resultAssign
132096
132672
  };
132097
132673
  }
132098
132674
  function rewriteReconstructedRecords(records2, replacements) {
132675
+ if (replacements.exact.size === 0) return records2;
132099
132676
  const rewritten = /* @__PURE__ */ new Map();
132100
132677
  for (const record4 of records2.values()) {
132101
132678
  const recordId = rewriteAssignedString(record4.recordId, replacements);
@@ -132250,15 +132827,16 @@ function foldChangedRecords(previous, changedRecords) {
132250
132827
  }
132251
132828
  return records2;
132252
132829
  }
132253
- function applyPushResult(workspace, result, pendingIdAssignments = /* @__PURE__ */ new Map()) {
132830
+ function applyPushResult(workspace, result, pendingIdAssignments = /* @__PURE__ */ new Map(), submittedStatus) {
132254
132831
  if (!isObjectRecord2(result) || !Array.isArray(result.changedRecords)) {
132255
132832
  throw new Error("Transaction response is missing changedRecords.");
132256
132833
  }
132257
- const localStatus = computeWorkspaceStatus2(workspace);
132834
+ const localStatus = statusForPushResult(workspace, submittedStatus);
132258
132835
  const preservedSourceFiles = materializeAssignedSchemaIdsInAuthoredSource(
132259
132836
  workspace,
132260
132837
  pendingIdAssignments,
132261
- localStatus.pendingValueIdentitySites
132838
+ localStatus.pendingValueIdentitySites,
132839
+ { sourceSnapshot: localStatus.sourceSnapshot }
132262
132840
  );
132263
132841
  const replacements = {
132264
132842
  exact: pendingIdAssignments,
@@ -132378,19 +132956,20 @@ function rewriteFilesFromState(workspace, records2 = new Map(
132378
132956
  )
132379
132957
  };
132380
132958
  });
132381
- const finalAnalysis = compileNeoProjectSources(
132382
- files.map((file) => {
132383
- const kind = neoProjectSourceKind(file.path, file.content);
132384
- if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
132385
- throw new Error(
132386
- `Emitted project source ${JSON.stringify(file.path)} has an unsupported path.`
132387
- );
132388
- }
132389
- return { uri: file.path, kind, text: file.content };
132390
- })
132391
- );
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);
132392
132971
  assertAssignedIdSourceRewriteValid(finalAnalysis);
132393
- writeProjectSourceAnalysisCacheV4(workspace.root, finalAnalysis);
132972
+ writeProjectSourceAnalysisCacheV4(workspace.root, finalAnalysis, finalInputs);
132394
132973
  const resolveSourcePath = createWorkspaceSourcePathResolver(workspace.root);
132395
132974
  const emittedPaths = new Set(files.map((file) => file.path));
132396
132975
  for (const recordState of Object.values(workspace.state.records)) {
@@ -132438,7 +133017,7 @@ ${finalErrors.map(
132438
133017
  );
132439
133018
  }
132440
133019
  }
132441
- function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, pendingValueIdentitySites = /* @__PURE__ */ new Map(), validateResult = false) {
133020
+ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, pendingValueIdentitySites = /* @__PURE__ */ new Map(), options = {}) {
132442
133021
  const pendingIdsByUri = /* @__PURE__ */ new Map();
132443
133022
  for (const pendingId2 of replacements.keys()) {
132444
133023
  if (!isPendingId(pendingId2)) continue;
@@ -132449,25 +133028,23 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
132449
133028
  pendingIdsByUri.set(uri, pendingIds);
132450
133029
  }
132451
133030
  if (pendingIdsByUri.size === 0) return /* @__PURE__ */ new Map();
132452
- const inputs = listProjectSourceFilesV4(workspace.root).map(
132453
- (absolutePath) => {
132454
- const uri = relative7(workspace.root, absolutePath).split(sep7).join("/");
132455
- const text = readFileSync16(absolutePath, "utf8");
132456
- const kind = neoProjectSourceKind(uri, text);
132457
- if (kind === null || !isNeoProjectProductionSourceKind(kind)) {
132458
- throw new Error(
132459
- `Listed workspace source ${JSON.stringify(uri)} has an unsupported path.`
132460
- );
132461
- }
132462
- return {
132463
- uri,
132464
- kind,
132465
- text
132466
- };
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
+ );
132467
133039
  }
132468
- );
133040
+ return {
133041
+ uri,
133042
+ kind,
133043
+ text
133044
+ };
133045
+ });
132469
133046
  const inputByUri = new Map(inputs.map((input) => [input.uri, input.text]));
132470
- const analysis = compileNeoProjectSources(inputs);
133047
+ const analysis = options.sourceSnapshot?.analysis ?? compileNeoProjectSources(inputs);
132471
133048
  if (analysis.diagnostics.some((diagnostic) => diagnostic.severity === "error")) {
132472
133049
  return /* @__PURE__ */ new Map();
132473
133050
  }
@@ -132615,7 +133192,7 @@ function materializeAssignedSchemaIdsInAuthoredSource(workspace, replacements, p
132615
133192
  }
132616
133193
  preserved.set(uri, source);
132617
133194
  }
132618
- if (validateResult && preserved.size > 0) {
133195
+ if (options.validateResult === true && preserved.size > 0) {
132619
133196
  assertAssignedIdSourceRewriteValid(
132620
133197
  compileNeoProjectSources(
132621
133198
  inputs.map((input) => ({
@@ -133181,10 +133758,12 @@ function compileMigrationAction(context, migrationData, locator) {
133181
133758
  locator
133182
133759
  );
133183
133760
  }
133184
- 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;
133185
133762
  var init_push = __esm({
133186
133763
  "src/commands/push.ts"() {
133187
133764
  "use strict";
133765
+ init_project_version_transaction_result();
133766
+ init_performance();
133188
133767
  init_identity_rewriter();
133189
133768
  init_source_layout();
133190
133769
  init_src();
@@ -133200,6 +133779,7 @@ var init_push = __esm({
133200
133779
  init_push_hook();
133201
133780
  init_project_documents();
133202
133781
  init_project_document_cache();
133782
+ init_project_files();
133203
133783
  init_workspace_status();
133204
133784
  init_lower_support();
133205
133785
  init_init_conversion();
@@ -133251,14 +133831,6 @@ var init_push = __esm({
133251
133831
  }
133252
133832
  status;
133253
133833
  };
133254
- PROJECT_TRANSACTION_POLL_DELAYS_MS = [
133255
- 250,
133256
- 500,
133257
- 1e3,
133258
- 2e3,
133259
- 4e3,
133260
- 5e3
133261
- ];
133262
133834
  LocalCandidateArtifactError = class extends Error {
133263
133835
  constructor(cause) {
133264
133836
  super(cause instanceof Error ? cause.message : String(cause));
@@ -133279,7 +133851,7 @@ var init_registry2 = __esm({
133279
133851
  "schema-contract/registry.mjs"() {
133280
133852
  "use strict";
133281
133853
  PROJECT_SCHEMA_CONTRACT = Object.freeze({
133282
- cliVersion: "0.50.7",
133854
+ cliVersion: "0.50.8",
133283
133855
  projectFileUploadBatchSize: 32,
133284
133856
  documentRecords: {
133285
133857
  member: {
@@ -134724,7 +135296,7 @@ import {
134724
135296
  sep as sep8
134725
135297
  } from "node:path";
134726
135298
  import { isDeepStrictEqual } from "node:util";
134727
- function isRecord9(value) {
135299
+ function isRecord10(value) {
134728
135300
  return typeof value === "object" && value !== null && !Array.isArray(value);
134729
135301
  }
134730
135302
  function expectation(actual, negated = false) {
@@ -134735,13 +135307,13 @@ function expectation(actual, negated = false) {
134735
135307
  return value;
134736
135308
  }
134737
135309
  function readExpectation(value) {
134738
- if (!isRecord9(value) || value.__neoExpectation !== true) {
135310
+ if (!isRecord10(value) || value.__neoExpectation !== true) {
134739
135311
  throw new NeoTestAssertionError("Matcher receiver is not an expectation.");
134740
135312
  }
134741
135313
  return value;
134742
135314
  }
134743
135315
  function readMockHandle(value) {
134744
- if (!isRecord9(value) || value.__neoMockHandle !== true || typeof value.mockId !== "number") {
135316
+ if (!isRecord10(value) || value.__neoMockHandle !== true || typeof value.mockId !== "number") {
134745
135317
  throw new NeoTestAssertionError(
134746
135318
  "Mock configuration receiver is not a mock handle."
134747
135319
  );
@@ -134852,13 +135424,13 @@ function errorMessage2(error) {
134852
135424
  return error instanceof Error ? error.message : String(error);
134853
135425
  }
134854
135426
  function findDelegateTargetMemberId(value) {
134855
- return isRecord9(value) && typeof value.memberId === "string" ? value.memberId : null;
135427
+ return isRecord10(value) && typeof value.memberId === "string" ? value.memberId : null;
134856
135428
  }
134857
135429
  function sharedEvaluatorBase(rawDocument) {
134858
135430
  const cached = SHARED_EVALUATOR_BASES.get(rawDocument);
134859
135431
  if (cached !== void 0) return cached;
134860
135432
  const document = readDocumentArrays(rawDocument);
134861
- const constructors = Array.isArray(rawDocument.constructors) ? rawDocument.constructors.filter(isRecord9) : [];
135433
+ const constructors = Array.isArray(rawDocument.constructors) ? rawDocument.constructors.filter(isRecord10) : [];
134862
135434
  const runtime = cliEvaluatorRuntime(
134863
135435
  { ...document, constructors },
134864
135436
  { includeValueGraphIndexes: true }
@@ -134927,7 +135499,7 @@ function preparedHookCandidate(workspace) {
134927
135499
  const parsed = JSON.parse(
134928
135500
  readFileSync17(join15(directory, "candidate.json"), "utf8")
134929
135501
  );
134930
- if (!isRecord9(parsed)) {
135502
+ if (!isRecord10(parsed)) {
134931
135503
  throw new NeoTestPreparedCandidateError(
134932
135504
  "Configured push hook candidate manifest must be a JSON object."
134933
135505
  );
@@ -134947,7 +135519,7 @@ function preparedHookCandidate(workspace) {
134947
135519
  "Configured push hook candidate inputs no longer match the workspace."
134948
135520
  );
134949
135521
  }
134950
- if (!isRecord9(parsed.document)) {
135522
+ if (!isRecord10(parsed.document)) {
134951
135523
  throw new NeoTestPreparedCandidateError(
134952
135524
  "Configured push hook candidate is missing its project document."
134953
135525
  );
@@ -134996,7 +135568,7 @@ function cachedTestCandidate(workspace, inputFingerprint) {
134996
135568
  try {
134997
135569
  const candidatePath = testCandidateCachePath(workspace, inputFingerprint);
134998
135570
  const parsed = JSON.parse(readFileSync17(candidatePath, "utf8"));
134999
- if (!isRecord9(parsed)) return null;
135571
+ if (!isRecord10(parsed)) return null;
135000
135572
  if (parsed.version !== 1) return null;
135001
135573
  if (parsed.candidateRevision !== TEST_CANDIDATE_CACHE_REVISION) return null;
135002
135574
  if (parsed.cliVersion !== PROJECT_SCHEMA_CONTRACT.cliVersion) return null;
@@ -135014,7 +135586,7 @@ function cachedTestCandidate(workspace, inputFingerprint) {
135014
135586
  return null;
135015
135587
  }
135016
135588
  const document = JSON.parse(documentJson);
135017
- if (!isRecord9(document)) return null;
135589
+ if (!isRecord10(document)) return null;
135018
135590
  return {
135019
135591
  document,
135020
135592
  sourceHash: parsed.sourceHash,
@@ -135058,7 +135630,7 @@ function compileSpec(workspace, document, absolutePath, projectCompilationHash2)
135058
135630
  );
135059
135631
  try {
135060
135632
  const cached = JSON.parse(readFileSync17(artifactPath, "utf8"));
135061
- 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)) {
135062
135634
  return {
135063
135635
  path,
135064
135636
  source,
@@ -135222,7 +135794,7 @@ function portableDelegate(value) {
135222
135794
  }
135223
135795
  }
135224
135796
  function callMemberId(value) {
135225
- if (!isRecord9(value) || value.type !== "callFunction") return null;
135797
+ if (!isRecord10(value) || value.type !== "callFunction") return null;
135226
135798
  return typeof value.memberId === "string" ? value.memberId : null;
135227
135799
  }
135228
135800
  function assertNoNestedRegistration(value) {
@@ -135230,7 +135802,7 @@ function assertNoNestedRegistration(value) {
135230
135802
  for (const entry of value) assertNoNestedRegistration(entry);
135231
135803
  return;
135232
135804
  }
135233
- if (!isRecord9(value)) return;
135805
+ if (!isRecord10(value)) return;
135234
135806
  const memberId = callMemberId(value);
135235
135807
  if (memberId !== null && REGISTRATION_IDS.has(memberId)) {
135236
135808
  throw new NeoTestRegistrationError(
@@ -135246,8 +135818,8 @@ function compiledDelegateActions(value) {
135246
135818
  for (const child of entry) visit(child);
135247
135819
  return;
135248
135820
  }
135249
- if (!isRecord9(entry)) return;
135250
- 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)) {
135251
135823
  actions.push(entry.action);
135252
135824
  return;
135253
135825
  }
@@ -135258,7 +135830,7 @@ function compiledDelegateActions(value) {
135258
135830
  }
135259
135831
  function validateRegistrationAction(action, allowRegistration) {
135260
135832
  for (const instruction of action.instructions) {
135261
- const record4 = isRecord9(instruction) ? instruction : null;
135833
+ const record4 = isRecord10(instruction) ? instruction : null;
135262
135834
  const call = record4?.type === "functionCall" ? record4.call : null;
135263
135835
  const memberId = callMemberId(call);
135264
135836
  if (memberId !== null && REGISTRATION_IDS.has(memberId)) {
@@ -135391,8 +135963,8 @@ function mockResponse(environment, mock, selected, call) {
135391
135963
  };
135392
135964
  }
135393
135965
  function partialObjectMatch(actual, expected) {
135394
- if (!isRecord9(expected)) return isDeepStrictEqual(actual, expected);
135395
- if (!isRecord9(actual)) return false;
135966
+ if (!isRecord10(expected)) return isDeepStrictEqual(actual, expected);
135967
+ if (!isRecord10(actual)) return false;
135396
135968
  return Object.entries(expected).every(
135397
135969
  ([key, value]) => partialObjectMatch(actual[key], value)
135398
135970
  );
@@ -135584,7 +136156,7 @@ function interceptTestCallCore(environment, call) {
135584
136156
  const contains2 = typeof actual === "string" ? actual.includes(String(call.args[0])) : Array.isArray(actual) && actual.some((entry) => isDeepStrictEqual(entry, call.args[0]));
135585
136157
  assertMatcher(contains2, expectationValue, "toContain", call.args[0]);
135586
136158
  } else if (call.memberId === NEO_TEST_IDS.toHaveLength) {
135587
- 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;
135588
136160
  assertMatcher(
135589
136161
  length === call.args[0],
135590
136162
  expectationValue,
@@ -135675,11 +136247,11 @@ function snapshotEnvironmentDocument(environment) {
135675
136247
  const values = [...baseValues];
135676
136248
  const byId = /* @__PURE__ */ new Map();
135677
136249
  values.forEach((value, index) => {
135678
- if (isRecord9(value) && typeof value.id === "string")
136250
+ if (isRecord10(value) && typeof value.id === "string")
135679
136251
  byId.set(value.id, index);
135680
136252
  });
135681
136253
  const mergeRow = (row) => {
135682
- if (!isRecord9(row) || typeof row.id !== "string") return;
136254
+ if (!isRecord10(row) || typeof row.id !== "string") return;
135683
136255
  const cloned = structuredClone(row);
135684
136256
  const index = byId.get(row.id);
135685
136257
  if (index === void 0) {
@@ -135695,7 +136267,7 @@ function snapshotEnvironmentDocument(environment) {
135695
136267
  if (bindings.size > 0) {
135696
136268
  const baseMembers = Array.isArray(snapshot.members) ? snapshot.members : [];
135697
136269
  snapshot.members = baseMembers.map((member) => {
135698
- if (!isRecord9(member) || typeof member.id !== "string" || !bindings.has(member.id)) {
136270
+ if (!isRecord10(member) || typeof member.id !== "string" || !bindings.has(member.id)) {
135699
136271
  return member;
135700
136272
  }
135701
136273
  return { ...member, valueId: bindings.get(member.id) ?? null };
@@ -139980,7 +140552,7 @@ __export(dev_exports, {
139980
140552
  import { watch } from "node:fs";
139981
140553
  import { join as join20 } from "node:path";
139982
140554
  import { emitKeypressEvents } from "node:readline";
139983
- import { ConvexClient } from "convex/browser";
140555
+ import { ConvexClient as ConvexClient2 } from "convex/browser";
139984
140556
  function isSchemaSignal(value) {
139985
140557
  if (typeof value !== "object" || value === null) return false;
139986
140558
  return typeof value.transactionHash === "string";
@@ -140055,8 +140627,14 @@ async function runDev(workspace, options) {
140055
140627
  busy = false;
140056
140628
  }
140057
140629
  };
140058
- const convex = new ConvexClient(convexUrl);
140059
- 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
+ );
140060
140638
  convex.onUpdate(
140061
140639
  api2.projectExportData.schemaSignal,
140062
140640
  {
@@ -140495,7 +141073,7 @@ There is no --keep-current: preserving current semantic state defines flatten.
140495
141073
  async function main() {
140496
141074
  const args = parseArgs(process.argv.slice(2));
140497
141075
  if (args.command === "--version") {
140498
- console.log("0.50.7");
141076
+ console.log("0.50.8");
140499
141077
  return;
140500
141078
  }
140501
141079
  if (args.command === null || args.command === "help" || args.command === "--help" || args.command === "-h") {
@@ -140899,12 +141477,15 @@ async function main() {
140899
141477
  return;
140900
141478
  }
140901
141479
  case "push": {
141480
+ const finishPush = startPerformanceSpan("push");
141481
+ const finishWorkspace = startPerformanceSpan("workspace");
140902
141482
  const json = boolFlag(args, "json");
140903
141483
  const preparation = process.stdout.isTTY === true && json !== true ? spinner("Preparing push\u2026") : null;
140904
141484
  let handedOffPreparation = false;
140905
141485
  try {
140906
141486
  const workspace = loadWorkspaceForCommand(args);
140907
141487
  const { runPush: runPush2 } = await Promise.resolve().then(() => (init_push(), push_exports));
141488
+ finishWorkspace();
140908
141489
  handedOffPreparation = true;
140909
141490
  await runPush2(
140910
141491
  workspace,
@@ -140923,6 +141504,8 @@ async function main() {
140923
141504
  preparation?.fail("Push preparation failed.");
140924
141505
  }
140925
141506
  throw error;
141507
+ } finally {
141508
+ finishPush();
140926
141509
  }
140927
141510
  return;
140928
141511
  }
@@ -141022,6 +141605,7 @@ var DEFAULT_API_BASE_URL;
141022
141605
  var init_main = __esm({
141023
141606
  "src/main.ts"() {
141024
141607
  "use strict";
141608
+ init_performance();
141025
141609
  init_auth_api_base_url();
141026
141610
  init_login();
141027
141611
  init_args();