statelyai 0.12.0 → 0.12.3

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.
@@ -6908,14 +6908,15 @@ function containsCodeExpression(value, seen = /* @__PURE__ */ new WeakSet()) {
6908
6908
  */
6909
6909
  const VALID_IDENT = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/;
6910
6910
  var RawCode = class {
6911
- constructor(code, sourceValue, sourceSlice) {
6911
+ constructor(code, sourceValue, sourceSlice, preserveWhitespace = false) {
6912
6912
  this.code = code;
6913
6913
  this.sourceValue = sourceValue;
6914
6914
  this.sourceSlice = sourceSlice;
6915
+ this.preserveWhitespace = preserveWhitespace;
6915
6916
  }
6916
6917
  };
6917
- function raw(code, sourceValue, sourceSlice) {
6918
- return new RawCode(code, sourceValue, sourceSlice);
6918
+ function raw(code, sourceValue, sourceSlice, preserveWhitespace = false) {
6919
+ return new RawCode(code, sourceValue, sourceSlice, preserveWhitespace);
6919
6920
  }
6920
6921
  const SERIALIZED_SOURCE_ID = "__stately_source_id__";
6921
6922
  function serializeJS(value, indent = 0, step = 2) {
@@ -6963,6 +6964,7 @@ function serializeObject(obj, indent, step) {
6963
6964
  }
6964
6965
  function preservesAuthoredWhitespace(value) {
6965
6966
  if (isCodeExpression(value)) return true;
6967
+ if (value instanceof RawCode && value.preserveWhitespace) return true;
6966
6968
  const sourceValue = value instanceof RawCode ? value.sourceValue : value;
6967
6969
  return Boolean(sourceValue && typeof sourceValue === "object" && SERIALIZED_SOURCE_ID in sourceValue);
6968
6970
  }
@@ -7142,13 +7144,13 @@ function deepSimplify(config) {
7142
7144
  if (config.states && typeof config.states === "object") for (const stateConfig of Object.values(config.states)) deepSimplify(stateConfig);
7143
7145
  return config;
7144
7146
  }
7145
- function buildMeta(meta, color) {
7147
+ function buildMeta(meta, color, showStatelyMeta = true) {
7146
7148
  if (isCodeExpression(meta)) return meta;
7147
7149
  const result = {};
7148
7150
  if (meta) {
7149
- for (const [k, v] of Object.entries(meta)) if (v !== void 0 && v !== null) result[k] = v;
7151
+ for (const [k, v] of Object.entries(meta)) if (v !== void 0 && v !== null && (showStatelyMeta || !k.startsWith("@statelyai"))) result[k] = v;
7150
7152
  }
7151
- if (color) result["@statelyai.color"] = color;
7153
+ if (color && showStatelyMeta) result["@statelyai.color"] = color;
7152
7154
  return Object.keys(result).length > 0 ? result : void 0;
7153
7155
  }
7154
7156
  /** Format event param — supports { type: string } (new) and plain string (legacy) */
@@ -7444,7 +7446,7 @@ function tagToString(tag) {
7444
7446
  }
7445
7447
  function graphToMachineConfig(graph, options = {}) {
7446
7448
  const v6 = isXStateV6Profile(graph.data.profile);
7447
- const { showDescriptions = true, showMeta = true, inlineGuardsAsTypeRef = false, keepChooseStructured = false } = options;
7449
+ const { showDescriptions = true, showMeta = true, showStatelyMeta = true, inlineGuardsAsTypeRef = false, keepChooseStructured = false } = options;
7448
7450
  const serializeAction = (action) => serializeActionItem(action, keepChooseStructured);
7449
7451
  const config = {};
7450
7452
  function getNodeConfig(node) {
@@ -7458,7 +7460,7 @@ function graphToMachineConfig(graph, options = {}) {
7458
7460
  "choice"
7459
7461
  ].includes(node.data.type) ? node.data.type : void 0;
7460
7462
  const tags = node.data.tags;
7461
- const stateMeta = showMeta ? buildMeta(node.data.meta, node.data.color) : void 0;
7463
+ const stateMeta = showMeta ? buildMeta(node.data.meta, node.data.color, showStatelyMeta) : void 0;
7462
7464
  Object.assign(nodeConfig, {
7463
7465
  id: resolvedNodeId,
7464
7466
  type: nodeType === "normal" ? void 0 : nodeType ?? void 0,
@@ -7505,7 +7507,7 @@ function graphToMachineConfig(graph, options = {}) {
7505
7507
  return additionalTarget ? [resolveTransitionTarget(graph, node, additionalTarget)] : [];
7506
7508
  })].filter(Boolean);
7507
7509
  const type = edge.data.eventType ?? "";
7508
- const transitionMeta = showMeta ? buildMeta(edge.data.meta, edge.data.color) : void 0;
7510
+ const transitionMeta = showMeta ? buildMeta(edge.data.meta, edge.data.color, showStatelyMeta) : void 0;
7509
7511
  const transitionObject = {
7510
7512
  ...edge.data.matches ? { matches: edge.data.matches } : void 0,
7511
7513
  target: edge.data.transitionType === "targetless" ? void 0 : resolvedTargets.length > 1 ? resolvedTargets : `${resolvedTarget}`,
@@ -7514,7 +7516,7 @@ function graphToMachineConfig(graph, options = {}) {
7514
7516
  ...edge.data.context !== void 0 ? { context: edge.data.context } : void 0,
7515
7517
  ...edge.data.input !== void 0 ? { input: edge.data.input } : void 0,
7516
7518
  actions: edge.data.actions?.length ? edge.data.actions.map(serializeAction) : void 0,
7517
- description: edge.data.description ?? void 0,
7519
+ description: showDescriptions ? edge.data.description ?? void 0 : void 0,
7518
7520
  ...transitionMeta ? { meta: transitionMeta } : void 0
7519
7521
  };
7520
7522
  const pushOn = (key) => {
@@ -8864,7 +8866,7 @@ function omitIdentityTransitionContext(transition, identity) {
8864
8866
  function normalizeFunctionTransition(fn, bindings, depth) {
8865
8867
  if (fn.body && ts.isBlock(fn.body) && hasUnsafeVariableInitializer(fn.body)) return fallbackFunctionTransition(fn);
8866
8868
  const branches = ts.isArrowFunction(fn) && !ts.isBlock(fn.body) ? expressionBranches(fn.body, bindings, depth + 1) : fn.body && ts.isBlock(fn.body) ? statementBranches(fn.body.statements, bindings, enqueueParamName(fn)) : [];
8867
- if (branches.some((branch) => branch.unknown)) return fallbackFunctionTransition(fn);
8869
+ if (branches.some((branch) => branch.unknown || isCodeExpression(branch.result))) return fallbackFunctionTransition(fn);
8868
8870
  const contextIdentity = transitionContextIdentity(fn);
8869
8871
  const transitions = branches.map((branch) => {
8870
8872
  let result = branch.result;
@@ -9300,6 +9302,7 @@ function expressionToValue(expression, bindings, context = {}, depth = 0) {
9300
9302
  if (builtIn) return builtIn;
9301
9303
  return inlineAction(node);
9302
9304
  }
9305
+ if (context.v6 && context.transitionValue) return inlineCodeExpression(resolvedExpressionText(node, bindings));
9303
9306
  const callee = node.expression.getText();
9304
9307
  const type = callee.includes(".") ? callee.split(".").pop() : callee;
9305
9308
  return { type: type === "assign" ? "xstate.assign" : type };
@@ -10107,8 +10110,9 @@ function mapV6MachineConfig(value, context = {}) {
10107
10110
  result[key] = mapV6StatesMap(item);
10108
10111
  continue;
10109
10112
  }
10110
- if (key === "choice") {
10111
- result[key] = buildV6TransitionFunction(Array.isArray(item) ? item : [item]) ?? mapV6MachineConfig(item, { transitionValue: true });
10113
+ if (key === "choice" && value.type === "choice") {
10114
+ const choices = Array.isArray(item) ? item : [item];
10115
+ result[key] = choices.length === 1 && opaqueTransitionSource(choices[0]) !== void 0 ? toV6TransitionValue(item) : buildV6TransitionFunction(choices.map(withoutOpaqueTransitionMeta), true) ?? toV6TransitionValue(item);
10112
10116
  continue;
10113
10117
  }
10114
10118
  if (key === "entry" || key === "exit" || key === "actions") {
@@ -10140,10 +10144,44 @@ function mapV6TransitionMap(value) {
10140
10144
  for (const [key, item] of Object.entries(value)) result[key] = mapV6MachineConfig(item, { transitionValue: true });
10141
10145
  return result;
10142
10146
  }
10147
+ /**
10148
+ * The authored source of a transition function the importer could not
10149
+ * analyze. It projects to one opaque edge, so the source is still exactly what
10150
+ * the edge represents.
10151
+ */
10152
+ function opaqueTransitionSource(value) {
10153
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
10154
+ const transition = value;
10155
+ const meta = transition.meta;
10156
+ const fnSource = meta?.[XSTATE_V6_FN_SOURCE_META];
10157
+ const hasGraphEdits = Object.entries(transition).some(([key, field]) => key !== "meta" && field !== void 0);
10158
+ return meta?.[XSTATE_V6_FN_TRANSITION_META] === true && typeof fnSource === "string" && !hasGraphEdits ? fnSource : void 0;
10159
+ }
10160
+ function withoutOpaqueTransitionMeta(value) {
10161
+ if (!value || typeof value !== "object" || Array.isArray(value)) return value;
10162
+ const transition = value;
10163
+ const meta = transition.meta;
10164
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) return value;
10165
+ const { [XSTATE_V6_FN_TRANSITION_META]: _fnTransition, [XSTATE_V6_FN_SOURCE_META]: _fnSource, ...publicMeta } = meta;
10166
+ if (_fnTransition === void 0 && _fnSource === void 0) return value;
10167
+ return {
10168
+ ...transition,
10169
+ meta: Object.keys(publicMeta).length > 0 ? publicMeta : void 0
10170
+ };
10171
+ }
10143
10172
  function toV6TransitionValue(value) {
10144
- if (Array.isArray(value)) return buildV6TransitionFunction(value) ?? value.map((item) => toV6TransitionValue(item));
10173
+ const opaqueSource = opaqueTransitionSource(value);
10174
+ if (opaqueSource !== void 0) return raw(opaqueSource, value, void 0, true);
10175
+ if (Array.isArray(value)) {
10176
+ if (value.length === 1) {
10177
+ const onlyOpaqueSource = opaqueTransitionSource(value[0]);
10178
+ if (onlyOpaqueSource !== void 0) return raw(onlyOpaqueSource, value[0], void 0, true);
10179
+ }
10180
+ const transitions = value.map(withoutOpaqueTransitionMeta);
10181
+ return buildV6TransitionFunction(transitions) ?? transitions.map((item) => toV6TransitionValue(item));
10182
+ }
10145
10183
  if (!value || typeof value !== "object") return value;
10146
- const transition = value;
10184
+ const transition = withoutOpaqueTransitionMeta(value);
10147
10185
  if (transition.matches !== void 0) return buildV6TransitionFunction([transition]) ?? mapV6MachineConfig(transition);
10148
10186
  if (transition.guard) return buildV6TransitionFunction([transition]) ?? mapV6MachineConfig(transition);
10149
10187
  if (transition.description || transition.input !== void 0 && !containsCodeExpression(transition.input)) return mapV6MachineConfig(transition);
@@ -10294,11 +10332,11 @@ function mergeV6FirstParam(authoredFirstParam, inferred) {
10294
10332
  prelude: []
10295
10333
  };
10296
10334
  }
10297
- function buildV6TransitionFunction(transitions) {
10335
+ function buildV6TransitionFunction(transitions, choice = false) {
10298
10336
  const transitionObjects = transitions.every((item) => Boolean(item) && typeof item === "object" && !Array.isArray(item)) ? transitions : void 0;
10299
10337
  if (!transitionObjects?.length) return;
10300
10338
  const descriptions = transitionObjects.map((transition) => typeof transition.description === "string" ? transition.description : void 0);
10301
- const hasBranchDescriptions = transitionObjects.some((transition) => Object.prototype.hasOwnProperty.call(transition.meta ?? {}, XSTATE_V6_BRANCH_DESCRIPTION_META)) || descriptions.some((description) => description !== descriptions[0]);
10339
+ const hasBranchDescriptions = choice && descriptions.some((description) => description !== void 0) || transitionObjects.some((transition) => Object.prototype.hasOwnProperty.call(transition.meta ?? {}, XSTATE_V6_BRANCH_DESCRIPTION_META)) || descriptions.some((description) => description !== descriptions[0]);
10302
10340
  const branches = [];
10303
10341
  let authoredFirstParam;
10304
10342
  const commonDescription = descriptions[0];
@@ -10334,7 +10372,7 @@ function buildV6TransitionFunction(transitions) {
10334
10372
  });
10335
10373
  }
10336
10374
  if (branches.length === 0) return;
10337
- if (branches.slice(0, -1).some((branch) => branch.condition === "true")) return;
10375
+ if (!choice && branches.slice(0, -1).some((branch) => branch.condition === "true")) return;
10338
10376
  const generated = branches.flatMap((branch) => [branch.condition, ...branch.body]).join("\n");
10339
10377
  let renderedBranches = branches;
10340
10378
  let merged = mergeV6FirstParam(authoredFirstParam, inferV6ArgBindings(generated));
@@ -10347,7 +10385,7 @@ function buildV6TransitionFunction(transitions) {
10347
10385
  }
10348
10386
  if (!merged) return;
10349
10387
  const lines = merged.prelude.map((statement) => indentV6Code(statement, 2));
10350
- for (const branch of renderedBranches) if (branch.condition === "true") lines.push(...branch.body.map((line) => indentV6Code(line, 2)));
10388
+ for (const [index, branch] of renderedBranches.entries()) if (branch.condition === "true" && index === renderedBranches.length - 1) lines.push(...branch.body.map((line) => indentV6Code(line, 2)));
10351
10389
  else {
10352
10390
  lines.push(indentV6Code(`if (${branch.condition}) {`, 2));
10353
10391
  lines.push(...branch.body.map((line) => indentV6Code(line, 4)));
@@ -13051,7 +13089,7 @@ function applyEdits(text, edits) {
13051
13089
  }
13052
13090
 
13053
13091
  //#endregion
13054
- //#region ../editor-sync/dist/adapters-gXgLug6Z.mjs
13092
+ //#region ../editor-sync/dist/adapters-CL5NOglI.mjs
13055
13093
  const sourceFileUris = /* @__PURE__ */ new WeakMap();
13056
13094
  function collectXStateSourceLocations(sourceText, document, options = {}) {
13057
13095
  const sourceFile = ts$1.createSourceFile(document.fileName, sourceText, ts$1.ScriptTarget.Latest, true, getScriptKind$2$1(document.fileName));
@@ -13237,11 +13275,21 @@ function addImportedStaticValueBindings(bindings, sourceFile, uri, fileName, opt
13237
13275
  }, statement.moduleSpecifier.text);
13238
13276
  if (!importedDocument) continue;
13239
13277
  for (const element of namedBindings.elements) {
13240
- const binding = resolveExportedStaticValue(importedDocument, element.propertyName?.text ?? element.name.text, resolver, /* @__PURE__ */ new Set());
13241
- if (binding) bindings.set(element.name.text, {
13242
- ...binding,
13243
- symbol: element.name.text
13244
- });
13278
+ const importedName = element.propertyName?.text ?? element.name.text;
13279
+ const members = new Set([""]);
13280
+ const visit = (node) => {
13281
+ if (ts$1.isPropertyAccessExpression(node) && ts$1.isIdentifier(node.expression) && node.expression.text === element.name.text) members.add(`.${node.name.text}`);
13282
+ ts$1.forEachChild(node, visit);
13283
+ };
13284
+ visit(sourceFile);
13285
+ for (const member of members) {
13286
+ const binding = resolveExportedStaticValue(importedDocument, `${importedName}${member}`, resolver, /* @__PURE__ */ new Set());
13287
+ const symbol = `${element.name.text}${member}`;
13288
+ if (binding) bindings.set(symbol, {
13289
+ ...binding,
13290
+ symbol
13291
+ });
13292
+ }
13245
13293
  }
13246
13294
  }
13247
13295
  }
@@ -13254,15 +13302,21 @@ function resolveExportedStaticValue(document, exportName, resolver, visited) {
13254
13302
  sourceFileUris.set(sourceFile, document.uri);
13255
13303
  const localBindings = collectStaticValueBindings(sourceFile);
13256
13304
  const localBinding = localBindings.get(exportName);
13257
- if (localBinding && sourceFile.statements.some((statement) => ts$1.isVariableStatement(statement) && statement.modifiers?.some((modifier) => modifier.kind === ts$1.SyntaxKind.ExportKeyword) && statement.declarationList.declarations.some((declaration) => ts$1.isIdentifier(declaration.name) && declaration.name.text === exportName))) return localBinding;
13305
+ const [exportedSymbol] = exportName.split(".");
13306
+ const memberSuffix = exportName.slice(exportedSymbol.length);
13307
+ if (localBinding && sourceFile.statements.some((statement) => {
13308
+ if (!ts$1.isVariableStatement(statement) && !ts$1.isEnumDeclaration(statement)) return false;
13309
+ if (!statement.modifiers?.some((modifier) => modifier.kind === ts$1.SyntaxKind.ExportKeyword)) return false;
13310
+ return ts$1.isEnumDeclaration(statement) ? statement.name.text === exportedSymbol : statement.declarationList.declarations.some((declaration) => ts$1.isIdentifier(declaration.name) && declaration.name.text === exportName);
13311
+ })) return localBinding;
13258
13312
  const exports = sourceFile.statements.filter(ts$1.isExportDeclaration);
13259
13313
  for (const statement of exports) {
13260
13314
  if (!statement.exportClause || !ts$1.isNamedExports(statement.exportClause)) continue;
13261
13315
  const specifier = statement.moduleSpecifier && ts$1.isStringLiteral(statement.moduleSpecifier) ? statement.moduleSpecifier.text : null;
13262
13316
  const exportedDocument = specifier?.startsWith(".") ? resolver.resolveImport(document, specifier) : null;
13263
13317
  for (const element of statement.exportClause.elements) {
13264
- if (element.name.text !== exportName) continue;
13265
- const localName = element.propertyName?.text ?? element.name.text;
13318
+ if (element.name.text !== exportedSymbol) continue;
13319
+ const localName = `${element.propertyName?.text ?? element.name.text}${memberSuffix}`;
13266
13320
  if (exportedDocument) {
13267
13321
  const binding = resolveExportedStaticValue(exportedDocument, localName, resolver, visited);
13268
13322
  return binding && {
@@ -13298,15 +13352,17 @@ function resolveExportedStaticValue(document, exportName, resolver, visited) {
13298
13352
  }
13299
13353
  }
13300
13354
  function resolveImportedStaticValue(sourceFile, document, localName, resolver, visited) {
13355
+ const [localSymbol] = localName.split(".");
13356
+ const memberSuffix = localName.slice(localSymbol.length);
13301
13357
  for (const statement of sourceFile.statements) {
13302
13358
  if (!ts$1.isImportDeclaration(statement) || !ts$1.isStringLiteral(statement.moduleSpecifier) || !statement.moduleSpecifier.text.startsWith(".")) continue;
13303
13359
  const namedBindings = statement.importClause?.namedBindings;
13304
13360
  if (!namedBindings || !ts$1.isNamedImports(namedBindings)) continue;
13305
- const imported = namedBindings.elements.find((element) => element.name.text === localName);
13361
+ const imported = namedBindings.elements.find((element) => element.name.text === localSymbol);
13306
13362
  if (!imported) continue;
13307
13363
  const importedDocument = resolver.resolveImport(document, statement.moduleSpecifier.text);
13308
13364
  if (!importedDocument) return null;
13309
- return resolveExportedStaticValue(importedDocument, imported.propertyName?.text ?? imported.name.text, resolver, visited);
13365
+ return resolveExportedStaticValue(importedDocument, `${imported.propertyName?.text ?? imported.name.text}${memberSuffix}`, resolver, visited);
13310
13366
  }
13311
13367
  return null;
13312
13368
  }
@@ -14195,8 +14251,9 @@ async function applyMachineToSourceDocuments(machineConfig, document, options =
14195
14251
  let semanticReplacements = semanticSplit?.replacements ?? [];
14196
14252
  if (activePatches.length === 0) return finalize(semanticReplacements);
14197
14253
  const transitionSplit = await tryCreateTransitionPatchSplitForDocuments(document, sourceFile, machineConfigNode, machineConfig, activePatches, prevGraph, nextGraph, activeSourceLocations, options.openTextDocument);
14198
- if (transitionSplit && transitionSplit.remainingPatches.length === 0) return finalize([...semanticReplacements, ...transitionSplit.replacements]);
14199
- if (transitionSplit) semanticReplacements = [...semanticReplacements, ...transitionSplit.replacements];
14254
+ const transitionReplacements = transitionSplit ? combineSemanticAndTransitionReplacements(semanticReplacements, transitionSplit.replacements) : semanticReplacements;
14255
+ if (transitionSplit && transitionSplit.remainingPatches.length === 0) return finalize(transitionReplacements);
14256
+ if (transitionSplit) semanticReplacements = transitionReplacements;
14200
14257
  const implementationSplit = tryCreateImplementationPatchSplit(document, sourceFile, transitionSplit?.remainingPatches ?? activePatches, nextGraph, options.machineIndex);
14201
14258
  if (implementationSplit && implementationSplit.remainingPatches.length === 0) return finalize([...semanticReplacements, ...implementationSplit.replacements]);
14202
14259
  if (implementationSplit) semanticReplacements = [...semanticReplacements, ...implementationSplit.replacements];
@@ -14673,7 +14730,17 @@ async function createTransitionReplacementForDocuments(document, sourceFile, mac
14673
14730
  if (transitionSource && transitionTarget.previousEventType === transitionTarget.eventType) return [createPropertyValueReplacement(targetDocument, targetSourceFile, transitionSource.eventProperty, nextTransitionValue)];
14674
14731
  if (transitionSource) return [createPropertyAssignmentReplacement(targetDocument, targetSourceFile, transitionSource.eventProperty, transitionTarget.eventType, nextTransitionValue)];
14675
14732
  const onObject = getObjectLiteralProperty(stateObject, "on");
14676
- if (onObject) return createObjectLiteralPropertyInsertion(targetDocument, targetSourceFile, onObject, transitionTarget.eventType, nextTransitionValue);
14733
+ if (onObject) {
14734
+ const nextOn = getStateConfigByPath(machineConfig, transitionTarget.sourcePath)?.on;
14735
+ if (isMachineConfig(nextOn) && !onObject.properties.some((property) => {
14736
+ const key = property.name && getPropertyNameText$2(property.name);
14737
+ return !key || nextOn[key] !== void 0;
14738
+ })) return [createObjectLiteralPropertiesInsertion(targetDocument, targetSourceFile, onObject, [{
14739
+ key: transitionTarget.eventType,
14740
+ config: nextTransitionValue
14741
+ }], false)];
14742
+ return createObjectLiteralPropertyInsertion(targetDocument, targetSourceFile, onObject, transitionTarget.eventType, nextTransitionValue);
14743
+ }
14677
14744
  return createObjectLiteralPropertyInsertion(targetDocument, targetSourceFile, stateObject, "on", { [transitionTarget.eventType]: nextTransitionValue });
14678
14745
  }
14679
14746
  async function createV6BranchActionNameReplacementForDocuments(document, sourceFile, machineConfigNode, patch, transitionTarget, prevGraph, nextGraph, sourceLocations, openTextDocument) {
@@ -14820,7 +14887,10 @@ async function createStatePropertiesReplacementForDocuments(document, sourceFile
14820
14887
  config: nextSlotValue
14821
14888
  });
14822
14889
  }
14823
- if (insertedSlots.length > 0) replacements.push(createObjectLiteralPropertiesInsertion(targetDocument, targetSourceFile, existingMap, insertedSlots));
14890
+ if (insertedSlots.length > 0) replacements.push(createObjectLiteralPropertiesInsertion(targetDocument, targetSourceFile, existingMap, insertedSlots, existingMap.properties.some((property) => {
14891
+ const key = property.name && getPropertyNameText$2(property.name);
14892
+ return !key || !affectedSlots.has(key) || nextValue[key] !== void 0;
14893
+ })));
14824
14894
  continue;
14825
14895
  }
14826
14896
  }
@@ -15499,7 +15569,7 @@ function createStateMembershipInitialReplacements(document, sourceFile, parentOb
15499
15569
  config: nextInitial
15500
15570
  }])];
15501
15571
  }
15502
- function createObjectLiteralPropertiesInsertion(document, sourceFile, objectLiteral, entries) {
15572
+ function createObjectLiteralPropertiesInsertion(document, sourceFile, objectLiteral, entries, hasRetainedProperties = objectLiteral.properties.length > 0) {
15503
15573
  const formatting = getFormattingContext(document, sourceFile, objectLiteral);
15504
15574
  const lineSeparator = formatting.newLineKind === ts$1.NewLineKind.CarriageReturnLineFeed ? "\r\n" : "\n";
15505
15575
  const propertyTexts = entries.map((entry) => printObjectLiteralProperty(entry.key, entry.config, sourceFile, formatting));
@@ -15508,7 +15578,7 @@ function createObjectLiteralPropertiesInsertion(document, sourceFile, objectLite
15508
15578
  const startOffset = existingProperties.length === 0 ? closeBraceOffset : existingProperties[existingProperties.length - 1].getEnd();
15509
15579
  const start = document.positionAt(startOffset);
15510
15580
  const end = document.positionAt(closeBraceOffset);
15511
- const prefix = existingProperties.length === 0 ? lineSeparator : `,${lineSeparator}`;
15581
+ const prefix = hasRetainedProperties ? `,${lineSeparator}` : lineSeparator;
15512
15582
  const suffix = `${lineSeparator}${formatting.baseIndent}`;
15513
15583
  return {
15514
15584
  uri: getDocumentUri(document),
@@ -16219,8 +16289,8 @@ function createStateKeyRenameReplacementsForTarget(document, sourceFile, parentO
16219
16289
  }
16220
16290
  const supportedScope = new Set([rename.parentPath.join("."), ...getDirectChildStatePaths(nodeKind === "statesObject" ? initialOwnerObject : parentObject, rename.parentPath).map((path) => path.join("."))]);
16221
16291
  for (const target of targetLiterals) {
16222
- if (!referencesRenamedState(target.node.text, rename)) continue;
16223
- if (!referencesRenamedAbsoluteState(target.node.text, rename)) {
16292
+ if (!referencesRenamedState(target.value, rename)) continue;
16293
+ if (!referencesRenamedAbsoluteState(target.value, rename)) {
16224
16294
  const initialSourcePaths = target.initialSourcePaths ?? [];
16225
16295
  if (initialSourcePaths.length > 0) {
16226
16296
  if (!target.sourcePaths.every((sourcePath) => arePathsEqual(sourcePath, rename.parentPath))) {
@@ -16232,7 +16302,7 @@ function createStateKeyRenameReplacementsForTarget(document, sourceFile, parentO
16232
16302
  if (!(sourcePath.length === rename.parentPath.length || arePathsEqual(sourcePath.slice(0, -1), rename.parentPath)) || !supportedScope.has(sourceKey)) return null;
16233
16303
  }
16234
16304
  }
16235
- const replacement = createStringLiteralReplacement(target.document, target.sourceFile, target.node, renameStateReference(target.node.text, rename));
16305
+ const replacement = createStringLiteralReplacement(target.document, target.sourceFile, target.node, renameStateReference(target.value, rename));
16236
16306
  replacements.push(replacement);
16237
16307
  }
16238
16308
  return replacements;
@@ -16367,21 +16437,25 @@ function getDirectChildStatePaths(stateObject, parentPath) {
16367
16437
  return [[...parentPath, key]];
16368
16438
  });
16369
16439
  }
16370
- function collectTargetStringLiterals(machineConfigNode) {
16440
+ function collectTargetStringLiterals(machineConfigNode, sourceLocations, documentUri) {
16371
16441
  const literals = [];
16372
- const bindings = collectTopLevelExpressionBindings(machineConfigNode.getSourceFile());
16442
+ const bindings = collectTopLevelExpressionBindings(machineConfigNode.getSourceFile(), sourceLocations, documentUri);
16373
16443
  const visitStateObject = (stateObject, sourcePath) => {
16374
16444
  collectInitialTargetStringLiterals(stateObject, sourcePath, literals, bindings);
16375
16445
  for (const propertyName of [
16376
16446
  "always",
16377
16447
  "after",
16378
16448
  "on",
16379
- "onDone"
16449
+ "onDone",
16450
+ "onError",
16451
+ "onTimeout",
16452
+ "choice"
16380
16453
  ]) {
16381
16454
  const property = getPropertyAssignment(stateObject, propertyName);
16382
16455
  if (!property) continue;
16383
16456
  collectTargetStringLiteralsFromExpression(property.initializer, sourcePath, literals, bindings);
16384
16457
  }
16458
+ collectInvokeTargetStringLiterals(stateObject, sourcePath, literals, bindings);
16385
16459
  const statesObject = getObjectLiteralProperty(stateObject, "states");
16386
16460
  if (!statesObject) return;
16387
16461
  for (const property of statesObject.properties) {
@@ -16402,11 +16476,11 @@ function collectInitialTargetStringLiterals(stateObject, sourcePath, literals, b
16402
16476
  if (ts$1.isObjectLiteralExpression(initializer)) {
16403
16477
  const initialLiterals = [];
16404
16478
  collectTargetStringLiteralsFromExpression(initializer, sourcePath, initialLiterals, bindings);
16405
- for (const literal of initialLiterals) for (const literalSourcePath of literal.sourcePaths) addTargetLiteral(literals, literal.node, literalSourcePath, true);
16479
+ for (const literal of initialLiterals) for (const literalSourcePath of literal.sourcePaths) addTargetLiteral(literals, literal.node, literalSourcePath, true, literal.value);
16406
16480
  }
16407
16481
  }
16408
16482
  async function collectMappedTargetStringLiterals(rootDocument, rootSourceFile, machineConfigNode, sourceLocations, openTextDocument) {
16409
- const literals = collectTargetStringLiterals(machineConfigNode).map((target) => ({
16483
+ const literals = collectTargetStringLiterals(machineConfigNode, sourceLocations, getDocumentUri(rootDocument)).map((target) => ({
16410
16484
  ...target,
16411
16485
  document: rootDocument,
16412
16486
  sourceFile: rootSourceFile
@@ -16421,7 +16495,7 @@ async function collectMappedTargetStringLiterals(rootDocument, rootSourceFile, m
16421
16495
  const stateObject = findObjectLiteralByRange(targetSourceFile, location.range);
16422
16496
  if (!stateObject) continue;
16423
16497
  const stateLiterals = [];
16424
- collectTargetStringLiteralsFromStateObject(stateObject, location.path, stateLiterals, collectTopLevelExpressionBindings(targetSourceFile));
16498
+ collectTargetStringLiteralsFromStateObject(stateObject, location.path, stateLiterals, collectTopLevelExpressionBindings(targetSourceFile, sourceLocations, getDocumentUri(targetDocument)));
16425
16499
  for (const target of stateLiterals) {
16426
16500
  const key = getTargetLiteralReplacementKey(targetDocument, targetSourceFile, target.node);
16427
16501
  if (seen.has(key)) {
@@ -16472,7 +16546,7 @@ async function collectMappedTargetStringLiterals(rootDocument, rootSourceFile, m
16472
16546
  }
16473
16547
  }
16474
16548
  const functionLiterals = [];
16475
- const bindings = collectTopLevelExpressionBindings(targetSourceFile);
16549
+ const bindings = collectTopLevelExpressionBindings(targetSourceFile, sourceLocations, getDocumentUri(targetDocument));
16476
16550
  for (const sourcePath of location.sourcePaths) collectTargetStringLiteralsFromFunction(fn, sourcePath, functionLiterals, bindings, new Set([location.symbol]));
16477
16551
  for (const target of functionLiterals) {
16478
16552
  const key = getTargetLiteralReplacementKey(targetDocument, targetSourceFile, target.node);
@@ -16558,7 +16632,10 @@ function collectTransitionCallArgumentLiterals(machineConfigNode, symbol, target
16558
16632
  "always",
16559
16633
  "after",
16560
16634
  "on",
16561
- "onDone"
16635
+ "onDone",
16636
+ "onError",
16637
+ "onTimeout",
16638
+ "choice"
16562
16639
  ]) {
16563
16640
  const property = getPropertyAssignment(stateObject, propertyName);
16564
16641
  if (property) visitExpression(property.initializer, sourcePath);
@@ -16584,12 +16661,16 @@ function collectTargetStringLiteralsFromStateObject(stateObject, sourcePath, lit
16584
16661
  "always",
16585
16662
  "after",
16586
16663
  "on",
16587
- "onDone"
16664
+ "onDone",
16665
+ "onError",
16666
+ "onTimeout",
16667
+ "choice"
16588
16668
  ]) {
16589
16669
  const property = getPropertyAssignment(stateObject, propertyName);
16590
16670
  if (!property) continue;
16591
16671
  collectTargetStringLiteralsFromExpression(property.initializer, sourcePath, literals, bindings);
16592
16672
  }
16673
+ collectInvokeTargetStringLiterals(stateObject, sourcePath, literals, bindings);
16593
16674
  const statesObject = getObjectLiteralProperty(stateObject, "states");
16594
16675
  if (!statesObject) return;
16595
16676
  for (const property of statesObject.properties) {
@@ -16600,8 +16681,35 @@ function collectTargetStringLiteralsFromStateObject(stateObject, sourcePath, lit
16600
16681
  collectTargetStringLiteralsFromStateObject(childState, [...sourcePath, key], literals, bindings);
16601
16682
  }
16602
16683
  }
16684
+ function collectInvokeTargetStringLiterals(stateObject, sourcePath, literals, bindings) {
16685
+ const invoke = getPropertyAssignment(stateObject, "invoke");
16686
+ if (!invoke) return;
16687
+ const visit = (expression, seen = /* @__PURE__ */ new Set()) => {
16688
+ const current = unwrapExpression$2(expression);
16689
+ if (ts$1.isIdentifier(current)) {
16690
+ const binding = bindings.get(current.text);
16691
+ if (binding && ts$1.isExpression(binding) && !seen.has(current.text)) visit(binding, new Set([...seen, current.text]));
16692
+ } else if (ts$1.isArrayLiteralExpression(current)) {
16693
+ for (const item of current.elements) if (ts$1.isExpression(item)) visit(item, seen);
16694
+ } else if (ts$1.isObjectLiteralExpression(current)) for (const name of [
16695
+ "onDone",
16696
+ "onError",
16697
+ "onSnapshot",
16698
+ "onTimeout"
16699
+ ]) {
16700
+ const property = getPropertyAssignment(current, name);
16701
+ if (property) collectTargetStringLiteralsFromExpression(property.initializer, sourcePath, literals, bindings);
16702
+ }
16703
+ };
16704
+ visit(invoke.initializer);
16705
+ }
16603
16706
  function collectTargetStringLiteralsFromExpression(expression, sourcePath, literals, bindings, seenBindings = /* @__PURE__ */ new Set()) {
16604
16707
  const current = unwrapExpression$2(expression);
16708
+ const staticTarget = bindings.staticTargets?.get(`${current.getStart()}:${current.getEnd()}`);
16709
+ if (staticTarget !== void 0 && (ts$1.isIdentifier(current) || ts$1.isPropertyAccessExpression(current))) {
16710
+ addTargetLiteral(literals, current, sourcePath, false, staticTarget);
16711
+ return;
16712
+ }
16605
16713
  if (ts$1.isStringLiteralLike(current)) {
16606
16714
  addTargetLiteral(literals, current, sourcePath);
16607
16715
  return;
@@ -16624,6 +16732,7 @@ function collectTargetStringLiteralsFromExpression(expression, sourcePath, liter
16624
16732
  const binding = bindings.get(current.expression.text);
16625
16733
  if (binding && isTransitionFunctionBinding(binding)) {
16626
16734
  const scopedBindings = new Map(bindings);
16735
+ scopedBindings.staticTargets = bindings.staticTargets;
16627
16736
  for (const [index, parameter] of binding.parameters.entries()) {
16628
16737
  if (!ts$1.isIdentifier(parameter.name)) continue;
16629
16738
  const argument = current.arguments[index];
@@ -16686,8 +16795,9 @@ function collectTargetStringLiteralsFromFunction(fn, sourcePath, literals, bindi
16686
16795
  }
16687
16796
  collectTargetStringLiteralsFromExpression(fn.body, sourcePath, literals, bindings, seenBindings);
16688
16797
  }
16689
- function collectTopLevelExpressionBindings(sourceFile) {
16798
+ function collectTopLevelExpressionBindings(sourceFile, sourceLocations, documentUri) {
16690
16799
  const bindings = /* @__PURE__ */ new Map();
16800
+ bindings.staticTargets = new Map((sourceLocations?.staticValues ?? []).filter((location) => location.definitionUri && location.uri === documentUri).map((location) => [`${offsetAtRangePosition(sourceFile.text, location.range.startLine, location.range.startChar)}:${offsetAtRangePosition(sourceFile.text, location.range.endLine, location.range.endChar)}`, location.value]));
16691
16801
  for (const statement of sourceFile.statements) {
16692
16802
  if (ts$1.isFunctionDeclaration(statement) && statement.name) {
16693
16803
  bindings.set(statement.name.text, statement);
@@ -16698,7 +16808,7 @@ function collectTopLevelExpressionBindings(sourceFile) {
16698
16808
  }
16699
16809
  return bindings;
16700
16810
  }
16701
- function addTargetLiteral(literals, node, sourcePath, isInitialTarget = false) {
16811
+ function addTargetLiteral(literals, node, sourcePath, isInitialTarget = false, value = node.text) {
16702
16812
  const existing = literals.find((literal) => literal.node.getStart() === node.getStart() && literal.node.getEnd() === node.getEnd());
16703
16813
  if (existing) {
16704
16814
  mergeTargetSourcePaths(existing, [sourcePath]);
@@ -16707,6 +16817,7 @@ function addTargetLiteral(literals, node, sourcePath, isInitialTarget = false) {
16707
16817
  }
16708
16818
  literals.push({
16709
16819
  node,
16820
+ value,
16710
16821
  sourcePaths: [sourcePath],
16711
16822
  ...isInitialTarget ? { initialSourcePaths: [sourcePath] } : {}
16712
16823
  });
@@ -16877,6 +16988,10 @@ function compareReplacementRangesDescending(a, b) {
16877
16988
  function normalizeOverlappingReplacements(replacements) {
16878
16989
  return planTextReplacements(replacements);
16879
16990
  }
16991
+ function combineSemanticAndTransitionReplacements(semantic, transitions) {
16992
+ const compare = (leftLine, leftChar, rightLine, rightChar) => leftLine - rightLine || leftChar - rightChar;
16993
+ return [...semantic.filter((focused) => !transitions.some((owner) => owner.uri === focused.uri && compare(focused.range.startLine, focused.range.startChar, owner.range.startLine, owner.range.startChar) > 0 && compare(focused.range.endLine, focused.range.endChar, owner.range.endLine, owner.range.endChar) <= 0)), ...transitions];
16994
+ }
16880
16995
  function appendWholeStateReplacement(replacements, wholeState) {
16881
16996
  const compare = (leftLine, leftChar, rightLine, rightChar) => leftLine - rightLine || leftChar - rightChar;
16882
16997
  for (let index = replacements.length - 1; index >= 0; index--) {
@@ -17821,7 +17936,7 @@ function diagnosticKey$1(diagnostic) {
17821
17936
  }
17822
17937
 
17823
17938
  //#endregion
17824
- //#region ../editor-sync/dist/localBackend-M-YkjWse.mjs
17939
+ //#region ../editor-sync/dist/localBackend-rcokEw-A.mjs
17825
17940
  var LocalEditorSyncBackend = class {
17826
17941
  constructor(access) {
17827
17942
  this.access = access;
@@ -17960,7 +18075,7 @@ function toFileName(uriOrPath) {
17960
18075
  }
17961
18076
 
17962
18077
  //#endregion
17963
- //#region ../editor-sync/dist/pushMerge-BTyVbmbW.mjs
18078
+ //#region ../editor-sync/dist/pushMerge-piN5M1Ej.mjs
17964
18079
  const GEOMETRY_KEYS = [
17965
18080
  "x",
17966
18081
  "y",
@@ -17977,6 +18092,36 @@ const DEFAULT_ANNOTATION_SIZE = {
17977
18092
  };
17978
18093
  const NEW_NODE_GAP = 80;
17979
18094
  const MAX_CONCURRENT_MERGE_ATTEMPTS = 3;
18095
+ /** Match presentation events independently of converter-specific entity IDs. */
18096
+ function presentationMatchingGraph(graph) {
18097
+ const nodes = new Map(graph.nodes.map((node) => [node.id, node]));
18098
+ return {
18099
+ ...graph,
18100
+ edges: graph.edges.map((edge) => {
18101
+ let eventType = edge.data.eventType;
18102
+ if (typeof eventType !== "string") return edge;
18103
+ const sourceSuffix = `.${edge.sourceId}`;
18104
+ if (eventType.startsWith("xstate.after.") && eventType.endsWith(sourceSuffix)) eventType = eventType.slice(0, -sourceSuffix.length);
18105
+ else if (/^(?:@statelyai?\.state\.done\.|xstate\.done\.state(?:\.|$))/.test(eventType)) eventType = "xstate.done.state";
18106
+ else {
18107
+ const ref = /^@statelyai?\.invoke\.(done|error|snapshot|timeout)\.(.+)$/.exec(eventType);
18108
+ const literal = /^xstate\.(done|error|snapshot|timeout)\.actor\.(.+)$/.exec(eventType);
18109
+ const match = ref ?? literal;
18110
+ if (match) {
18111
+ const index = (nodes.get(edge.sourceId)?.data?.invokes ?? []).findIndex((invoke) => invoke.id === match[2] || "invocationId" in invoke && invoke.invocationId === match[2]);
18112
+ if (index >= 0) eventType = `@presentation.invoke.${match[1]}.${index}`;
18113
+ }
18114
+ }
18115
+ return {
18116
+ ...edge,
18117
+ data: {
18118
+ ...edge.data,
18119
+ eventType
18120
+ }
18121
+ };
18122
+ })
18123
+ };
18124
+ }
17980
18125
  function indexBySemanticId(normalized, original) {
17981
18126
  const bySemanticId = /* @__PURE__ */ new Map();
17982
18127
  normalized.forEach((entity, index) => {
@@ -18014,6 +18159,7 @@ function restoreEdgeFields(edges, remoteEdgesById, remoteEdgeIdByLocalId) {
18014
18159
  const remote = remoteId ? remoteEdgesById.get(remoteId) : void 0;
18015
18160
  if (remote) {
18016
18161
  if (remote.uniqueId !== void 0) edge.uniqueId = remote.uniqueId;
18162
+ edge.data.eventTypeData = structuredClone(remote.data.eventTypeData);
18017
18163
  if (remote.data.assets !== void 0) edge.data.assets = structuredClone(remote.data.assets);
18018
18164
  }
18019
18165
  }
@@ -18058,7 +18204,7 @@ function mergePresentationIntoRemoteDefinition(editedDefinition, remoteDefinitio
18058
18204
  remoteGraph.nodes = remoteGraph.nodes.filter((node) => !remoteAnnotationIds.has(node.id));
18059
18205
  editedGraph.edges = editedGraph.edges.filter((edge) => !editedAnnotationIds.has(edge.sourceId) && !editedAnnotationIds.has(edge.targetId));
18060
18206
  remoteGraph.edges = remoteGraph.edges.filter((edge) => !remoteAnnotationIds.has(edge.sourceId) && !remoteAnnotationIds.has(edge.targetId));
18061
- const { base, compare } = diffGraphsSemantically(remoteGraph, editedGraph);
18207
+ const { base, compare } = diffGraphsSemantically(presentationMatchingGraph(remoteGraph), presentationMatchingGraph(editedGraph));
18062
18208
  const remoteNodesBySemanticId = indexBySemanticId(base.nodes, remoteGraph.nodes);
18063
18209
  const editedNodesBySemanticId = indexBySemanticId(compare.nodes, editedGraph.nodes);
18064
18210
  const remoteEdgesBySemanticId = indexBySemanticId(base.edges, remoteGraph.edges);
@@ -18067,6 +18213,7 @@ function mergePresentationIntoRemoteDefinition(editedDefinition, remoteDefinitio
18067
18213
  const mergedNodesById = collectDigraphNodes(merged.rootNode, /* @__PURE__ */ new Map());
18068
18214
  const editedNodesById = collectDigraphNodes(editedDefinition.rootNode, /* @__PURE__ */ new Map());
18069
18215
  const editedNodeIdToRemoteNodeId = /* @__PURE__ */ new Map();
18216
+ const editedEdgeIdToRemoteEdgeId = /* @__PURE__ */ new Map();
18070
18217
  for (const [semanticId, remoteNode] of remoteNodesBySemanticId) {
18071
18218
  const editedNode = editedNodesBySemanticId.get(semanticId);
18072
18219
  const target = mergedNodesById.get(remoteNode.id);
@@ -18089,6 +18236,7 @@ function mergePresentationIntoRemoteDefinition(editedDefinition, remoteDefinitio
18089
18236
  const target = mergedEdgesById.get(remoteEdge.id);
18090
18237
  const source = editedEdge ? editedEdgesById.get(editedEdge.id) : void 0;
18091
18238
  if (!editedEdge || !target || !source) continue;
18239
+ editedEdgeIdToRemoteEdgeId.set(editedEdge.id, remoteEdge.id);
18092
18240
  for (const field of ["position", "size"]) replacePresentationField(target, source, field);
18093
18241
  for (const field of ["color", "assets"]) replacePresentationField(target.data, source.data, field);
18094
18242
  }
@@ -18097,7 +18245,10 @@ function mergePresentationIntoRemoteDefinition(editedDefinition, remoteDefinitio
18097
18245
  collectAnnotations(editedDefinition.rootNode, null, editedAnnotations);
18098
18246
  for (const annotation of editedAnnotations) {
18099
18247
  const remoteParentId = annotation.parentId ? editedNodeIdToRemoteNodeId.get(annotation.parentId) : merged.rootNode.id;
18100
- (remoteParentId ? mergedNodesById.get(remoteParentId) : void 0)?.nodes.push(structuredClone(annotation.node));
18248
+ const parent = remoteParentId ? mergedNodesById.get(remoteParentId) : void 0;
18249
+ const note = structuredClone(annotation.node);
18250
+ if (Array.isArray(note.data.data?.referenceIds)) note.data.data.referenceIds = note.data.data.referenceIds.map((id) => editedNodeIdToRemoteNodeId.get(id) ?? editedEdgeIdToRemoteEdgeId.get(id) ?? id);
18251
+ parent?.nodes.push(note);
18101
18252
  }
18102
18253
  return merged;
18103
18254
  }
@@ -18141,7 +18292,7 @@ function mergeLocalGraphIntoRemoteDefinition(localGraph, remoteDefinition) {
18141
18292
  const remoteGraph = fromStudioMachine(remoteDefinition);
18142
18293
  remoteGraph.nodes = remoteGraph.nodes.filter((node) => !annotationIds.has(node.id));
18143
18294
  remoteGraph.edges = remoteGraph.edges.filter((edge) => !annotationIds.has(edge.sourceId) && !annotationIds.has(edge.targetId));
18144
- const { base, compare, summary } = diffGraphsSemantically(remoteGraph, localGraph);
18295
+ const { base, compare, summary } = diffGraphsSemantically(presentationMatchingGraph(remoteGraph), presentationMatchingGraph(localGraph));
18145
18296
  const remoteNodesBySemanticId = indexBySemanticId(base.nodes, remoteGraph.nodes);
18146
18297
  const remoteEdgesBySemanticId = indexBySemanticId(base.edges, remoteGraph.edges);
18147
18298
  const merged = structuredClone(localGraph);
@@ -18165,7 +18316,10 @@ function mergeLocalGraphIntoRemoteDefinition(localGraph, remoteDefinition) {
18165
18316
  });
18166
18317
  const definition = toStudioMachine(merged);
18167
18318
  restoreNodeFields(definition.rootNode, remoteNodesById, remoteNodeIdByLocalId);
18168
- restoreEdgeFields(definition.edges, new Map(remoteDefinition.edges.map((edge) => [edge.id, edge])), remoteEdgeIdByLocalId);
18319
+ restoreEdgeFields(definition.edges, new Map(remoteDefinition.edges.map((edge) => [edge.id, edge])), new Map(definition.edges.flatMap((edge, index) => {
18320
+ const remoteId = remoteEdgeIdByLocalId.get(merged.edges[index].id);
18321
+ return remoteId ? [[edge.id, remoteId]] : [];
18322
+ })));
18169
18323
  ensureCompleteNodeGeometry(definition.rootNode);
18170
18324
  return {
18171
18325
  definition: {
@@ -18230,7 +18384,7 @@ async function updateMachineDefinition(client, machineId, localGraph) {
18230
18384
  }
18231
18385
 
18232
18386
  //#endregion
18233
- //#region ../editor-sync/dist/presentationMetadata-BL3Oixz9.mjs
18387
+ //#region ../editor-sync/dist/presentationMetadata-YX6iRP4T.mjs
18234
18388
  function createStudioPresentationMetadataStore(client, machineId) {
18235
18389
  const load = async () => {
18236
18390
  return {
@@ -18241,6 +18395,7 @@ function createStudioPresentationMetadataStore(client, machineId) {
18241
18395
  return {
18242
18396
  load,
18243
18397
  async hydrate(graph) {
18398
+ if (!isGraph(graph)) return graph;
18244
18399
  return {
18245
18400
  presentationMachine: mergeLocalGraphIntoRemoteDefinition(graph, (await load()).definition).definition,
18246
18401
  presentationFormat: "digraph"
@@ -18248,7 +18403,7 @@ function createStudioPresentationMetadataStore(client, machineId) {
18248
18403
  },
18249
18404
  async persist(_graph, context) {
18250
18405
  const serialized = context?.serialized?.digraph;
18251
- if (!serialized) return;
18406
+ if (!serialized) throw new Error("Studio presentation requires a serialized digraph. Presentation was not saved.");
18252
18407
  const editedDefinition = JSON.parse(serialized);
18253
18408
  const latest = await client.machines.get(machineId);
18254
18409
  if (!latest.updatedAt) throw new Error(`Studio machine "${machineId}" did not provide an updatedAt guard. Presentation was not saved.`);
@@ -18309,10 +18464,29 @@ function createLocalPresentationMetadataRepository(file) {
18309
18464
  };
18310
18465
  } };
18311
18466
  }
18312
- function applyPresentationInitializer(initializer, current) {
18467
+ function applyPresentationInitializer(initializer, current, convertStudioDefinition = fromStudioMachine) {
18313
18468
  if (initializer.type === "overlay") return applyPresentationMetadata(initializer.overlay, current);
18314
18469
  if (!isGraph(current)) return current;
18315
- return fromStudioMachine(mergeLocalGraphIntoRemoteDefinition(current, initializer.definition).definition);
18470
+ const presentationGraph = convertStudioDefinition(initializer.definition);
18471
+ if (!isGraph(presentationGraph)) return current;
18472
+ const overlay = createPresentationSnapshot(presentationGraph);
18473
+ const sourceIds = presentationIds(current);
18474
+ const studioIds = presentationIds(presentationGraph);
18475
+ const liveIds = /* @__PURE__ */ new Map();
18476
+ for (const kind of ["nodes", "edges"]) {
18477
+ const bySemanticId = new Map(current[kind].map((entity, index) => [sourceIds[kind][index], entity.id]));
18478
+ presentationGraph[kind].forEach((entity, index) => {
18479
+ const liveId = bySemanticId.get(studioIds[kind][index]);
18480
+ if (typeof liveId === "string") liveIds.set(entity.id, liveId);
18481
+ });
18482
+ }
18483
+ const sourceEntityIds = new Set([...current.nodes, ...current.edges].map((entity) => entity.id));
18484
+ overlay.annotations = (overlay.annotations ?? []).filter((annotation) => annotation.parentId == null || liveIds.has(annotation.parentId));
18485
+ for (const annotation of overlay.annotations) {
18486
+ annotation.parentId = liveIds.get(annotation.parentId) ?? annotation.parentId;
18487
+ if (Array.isArray(annotation.data?.referenceIds)) annotation.data.referenceIds = annotation.data.referenceIds.map((id) => liveIds.get(id) ?? id).filter((id) => sourceEntityIds.has(id));
18488
+ }
18489
+ return applyPresentationMetadata(overlay, current);
18316
18490
  }
18317
18491
  function applyPresentationMetadata(overlay, current) {
18318
18492
  if (!isPresentationOverlay(overlay) || !isGraph(current)) return current;
@@ -18320,6 +18494,7 @@ function applyPresentationMetadata(overlay, current) {
18320
18494
  const addedNodes = [];
18321
18495
  const addedEdges = [];
18322
18496
  const semanticIds = overlay.identity === "semantic-v1" ? presentationIds(result) : void 0;
18497
+ const legacyIds = semanticIds ? presentationIds(result, false) : void 0;
18323
18498
  const renamedGuardPresentation = semanticIds ? matchUniqueGuardRenames(semanticIds.edges, overlay.edges ?? {}) : /* @__PURE__ */ new Map();
18324
18499
  for (const [index, node] of result.nodes.entries()) {
18325
18500
  normalizeCanonicalGeometry(node);
@@ -18330,7 +18505,7 @@ function applyPresentationMetadata(overlay, current) {
18330
18505
  for (const [index, edge] of result.edges.entries()) {
18331
18506
  normalizeCanonicalGeometry(edge);
18332
18507
  const semanticId = semanticIds?.edges[index] ?? edge.id;
18333
- const presentation = overlay.edges?.[semanticId] ?? renamedGuardPresentation.get(semanticId);
18508
+ const presentation = overlay.edges?.[semanticId] ?? overlay.edges?.[legacyIds?.edges[index] ?? edge.id] ?? renamedGuardPresentation.get(semanticId);
18334
18509
  if (presentation) applyEntityPresentation(presentation, edge);
18335
18510
  else addedEdges.push(edge);
18336
18511
  }
@@ -18410,6 +18585,7 @@ function entityPresentationMap(entities, includeSourceSide, ids) {
18410
18585
  const presentation = compactDefined({
18411
18586
  ...pick(canonicalGeometry(entity), GEOMETRY_FIELDS),
18412
18587
  color: clone(entity.data?.color),
18588
+ layout: clone(entity.data?.layout),
18413
18589
  assets: clone(entity.data?.assets),
18414
18590
  sourceSide: includeSourceSide ? clone(entity.data?.sourceSide) : void 0
18415
18591
  });
@@ -18417,7 +18593,8 @@ function entityPresentationMap(entities, includeSourceSide, ids) {
18417
18593
  });
18418
18594
  return entries.length > 0 ? Object.fromEntries(entries) : void 0;
18419
18595
  }
18420
- function presentationIds(graph) {
18596
+ function presentationIds(graph, canonicalEvents = true) {
18597
+ const matchingGraph = canonicalEvents ? presentationMatchingGraph(graph) : graph;
18421
18598
  const normalized = normalizeGraphForSemanticDiff({
18422
18599
  id: graph.id ?? "presentation",
18423
18600
  nodes: graph.nodes.map((node) => ({
@@ -18425,7 +18602,7 @@ function presentationIds(graph) {
18425
18602
  parentId: node.parentId,
18426
18603
  data: { key: node.data?.key }
18427
18604
  })),
18428
- edges: graph.edges.map((edge) => ({
18605
+ edges: matchingGraph.edges.map((edge) => ({
18429
18606
  id: edge.id,
18430
18607
  sourceId: edge.sourceId,
18431
18608
  targetId: edge.targetId,
@@ -18458,10 +18635,12 @@ function createAnnotationSnapshot(annotations) {
18458
18635
  parentId: annotation.parentId,
18459
18636
  content: clone(annotation.content),
18460
18637
  ...pick(geometry, GEOMETRY_FIELDS),
18638
+ ...pick(annotation, ["width", "height"]),
18461
18639
  data: compactDefined({
18462
18640
  referenceIds: clone(annotation.data?.referenceIds),
18463
18641
  color: clone(annotation.data?.color),
18464
- textAlign: clone(annotation.data?.textAlign)
18642
+ textAlign: clone(annotation.data?.textAlign),
18643
+ placement: clone(annotation.data?.placement)
18465
18644
  })
18466
18645
  });
18467
18646
  });
@@ -18480,7 +18659,8 @@ function applyEntityPresentation(presentation, entity) {
18480
18659
  for (const field of [
18481
18660
  "color",
18482
18661
  "assets",
18483
- "sourceSide"
18662
+ "sourceSide",
18663
+ "layout"
18484
18664
  ]) copyField(presentation, entity.data, field);
18485
18665
  }
18486
18666
  function placeAddedNodes(added, allNodes) {
@@ -19003,7 +19183,7 @@ const editorSyncApplyResponseSchema = z$1.object({ replacements: z$1.array(edito
19003
19183
  const editorSyncAccessResponseSchema = z$1.object({ access: editorSyncAccessSchema });
19004
19184
 
19005
19185
  //#endregion
19006
- //#region ../editor-sync/dist/host-DGJOurlX.mjs
19186
+ //#region ../editor-sync/dist/host-Dvy75KWk.mjs
19007
19187
  let diagnosticSequence = 0;
19008
19188
  function presentationsEqual(first, second) {
19009
19189
  return JSON.stringify(createPresentationSnapshot(first)) === JSON.stringify(createPresentationSnapshot(second));
@@ -19030,6 +19210,12 @@ var EditorSyncError = class extends Error {
19030
19210
  this.code = code;
19031
19211
  }
19032
19212
  };
19213
+ function isEditorSyncAuthenticationError(error) {
19214
+ return error instanceof Error && "status" in error && error.status === 401;
19215
+ }
19216
+ function authenticationRecoveryInstructions(recovery) {
19217
+ return recovery.host === "cli" ? "Run `npx statelyai login` in your terminal, then reload the editor." : "In the VS Code Command Palette, run “Stately: Sign In”, then reopen the visual editor.";
19218
+ }
19033
19219
  async function fetchEditorSyncAccess(options) {
19034
19220
  const fetchImpl = options.fetch ?? globalThis.fetch;
19035
19221
  const url = new URL("/api/editor-sync/access", `${options.editorUrl.replace(/\/+$/, "")}/`);
@@ -19123,12 +19309,14 @@ var EditorHostSession = class {
19123
19309
  await this.initialize();
19124
19310
  } catch (error) {
19125
19311
  const message = formatError(error, "Failed to initialize the visual editor.");
19312
+ const recovery = isEditorSyncAuthenticationError(error) ? this.options.authenticationRecovery : void 0;
19126
19313
  this.options.send({
19127
19314
  type: "@statelyai.error",
19128
19315
  code: "initialization_failed",
19129
- message
19316
+ message,
19317
+ ...recovery ? { authenticationRecovery: recovery } : {}
19130
19318
  });
19131
- this.options.showError?.(message);
19319
+ this.options.showError?.(recovery ? `${message}\n${authenticationRecoveryInstructions(recovery)}` : message);
19132
19320
  }
19133
19321
  break;
19134
19322
  case "@statelyai.loaded":
@@ -19329,7 +19517,7 @@ var EditorHostSession = class {
19329
19517
  return;
19330
19518
  }
19331
19519
  const documents = await this.options.source.collectDocuments(sourceLocationUris(this.currentSourceLocations));
19332
- const serialized = await this.getSerializedPayload();
19520
+ const serialized = await this.getSerializedPayload(message.serialized);
19333
19521
  const response = await this.options.sync.apply({
19334
19522
  rootUri: this.options.source.rootUri,
19335
19523
  rootFileName: this.options.source.rootFileName,
@@ -19378,7 +19566,7 @@ var EditorHostSession = class {
19378
19566
  });
19379
19567
  }
19380
19568
  async acknowledgeWithoutSourceChanges(message, serialized) {
19381
- await this.persistPresentation(message.graph, serialized ?? await this.getSerializedPayload());
19569
+ await this.persistPresentation(message.graph, serialized ?? await this.getSerializedPayload(message.serialized));
19382
19570
  this.currentGraph = message.graph;
19383
19571
  this.lastPersistedGraph = message.graph;
19384
19572
  this.options.send({
@@ -19476,11 +19664,12 @@ var EditorHostSession = class {
19476
19664
  this.watchedSourceUris.clear();
19477
19665
  for (const uri of sourceLocationUris(sourceLocations)) if (uri !== this.options.source.rootUri) this.watchedSourceUris.add(uri);
19478
19666
  }
19479
- async getSerializedPayload() {
19667
+ async getSerializedPayload(provided) {
19480
19668
  const format = this.currentFormat;
19481
- if (!format || format === "xstate") return void 0;
19669
+ const formats = [...new Set([...format && format !== "xstate" ? [format] : [], ...this.initialPresentation?.type === "studio" ? ["digraph"] : []])];
19670
+ if (formats.length === 0) return void 0;
19482
19671
  try {
19483
- return { [format]: await this.requestSerialized(format) };
19672
+ return Object.fromEntries(await Promise.all(formats.map(async (format) => [format, provided?.[format] ?? await this.requestSerialized(format)])));
19484
19673
  } catch {
19485
19674
  return;
19486
19675
  }
@@ -19855,14 +20044,31 @@ async function assertSourceSaveMatchesGraph(input) {
19855
20044
  const graph = convertMachineToGraph({
19856
20045
  machine: parsed.machine,
19857
20046
  format,
19858
- ...input.profile ? { profile: input.profile } : {}
20047
+ profile: input.profile ?? (format === "json" ? input.graph.data?.profile : void 0)
19859
20048
  }).graph;
20049
+ if (format === "json" && graph.data.schemas && input.machineConfig?.schemas !== void 0) {
20050
+ const root = graph.nodes.find((node) => !node.parentId);
20051
+ if (root) root.data.schemas = graph.data.schemas;
20052
+ }
19860
20053
  return graphToMachineConfig(graph);
19861
20054
  };
19862
20055
  const expectedConfig = comparableMachineConfig(stripStatelyMetadata(input.machineConfig));
19863
20056
  const actualConfig = comparableMachineConfig(stripStatelyMetadata(projectConfig(documents)));
20057
+ const baselineConfig = comparableMachineConfig(stripStatelyMetadata(projectConfig(input.documents)));
20058
+ const schemaEvents = (config) => config?.schemas?.events;
20059
+ const expectedEvents = schemaEvents(expectedConfig);
20060
+ const eventOrigins = input.graph.data?.schemaMeta?.events;
20061
+ let removedAutoEvent = false;
20062
+ for (const [event, origin] of Object.entries(eventOrigins ?? {})) if (origin.origin === "auto" && expectedEvents && JSON.stringify(expectedEvents[event]) === JSON.stringify(comparableMachineConfig({
20063
+ type: "object",
20064
+ properties: {},
20065
+ required: []
20066
+ })) && !Object.hasOwn(schemaEvents(baselineConfig) ?? {}, event) && !Object.hasOwn(schemaEvents(actualConfig) ?? {}, event)) {
20067
+ delete expectedEvents[event];
20068
+ removedAutoEvent = true;
20069
+ }
20070
+ if (removedAutoEvent && expectedEvents && Object.keys(expectedEvents).length === 0) delete expectedConfig.schemas.events;
19864
20071
  if (expectedConfig && typeof expectedConfig === "object" && !Array.isArray(expectedConfig) && actualConfig && typeof actualConfig === "object" && !Array.isArray(actualConfig) && !("id" in expectedConfig) && "id" in actualConfig) {
19865
- const baselineConfig = comparableMachineConfig(stripStatelyMetadata(projectConfig(input.documents)));
19866
20072
  if (baselineConfig && typeof baselineConfig === "object" && "id" in baselineConfig && actualConfig.id === baselineConfig.id) delete actualConfig.id;
19867
20073
  }
19868
20074
  if (JSON.stringify(expectedConfig) !== JSON.stringify(actualConfig)) throw new Error("The updated source does not match the requested machine.");
@@ -19944,7 +20150,7 @@ function sourceLocationUris(sourceLocations) {
19944
20150
 
19945
20151
  //#endregion
19946
20152
  //#region src/version.ts
19947
- const CLI_VERSION = "0.12.0";
20153
+ const CLI_VERSION = "0.12.3";
19948
20154
 
19949
20155
  //#endregion
19950
20156
  //#region src/cliHost.ts
@@ -19985,11 +20191,21 @@ function rawDataToBuffer(data) {
19985
20191
  var CliEditorSession = class {
19986
20192
  session;
19987
20193
  workspaceWatcher;
20194
+ readyMessage;
20195
+ loginPromise;
20196
+ loginController;
20197
+ disposed = false;
19988
20198
  constructor(options, sendMessage) {
19989
20199
  this.options = options;
20200
+ this.sendMessage = sendMessage;
20201
+ this.workspaceWatcher = createSharedWorkspaceWatcher(options.rootDir);
20202
+ this.session = this.createSession();
20203
+ }
20204
+ createSession() {
20205
+ const options = this.options;
19990
20206
  const send = (message) => {
19991
20207
  this.log("send", message.type, message);
19992
- sendMessage(message);
20208
+ this.sendMessage(message);
19993
20209
  };
19994
20210
  const machines = options.machines ?? [{
19995
20211
  id: `${options.rootUri}#0`,
@@ -20003,8 +20219,7 @@ var CliEditorSession = class {
20003
20219
  baseUrl: options.studioUrl,
20004
20220
  apiKey: options.apiKey
20005
20221
  });
20006
- this.workspaceWatcher = createSharedWorkspaceWatcher(options.rootDir);
20007
- let access;
20222
+ let access = options.access ? Promise.resolve(options.access) : void 0;
20008
20223
  const getAccess = () => {
20009
20224
  if (!access) {
20010
20225
  const request = fetchEditorSyncAccess({
@@ -20019,7 +20234,11 @@ var CliEditorSession = class {
20019
20234
  }
20020
20235
  return access;
20021
20236
  };
20022
- this.session = new EditorHostWorkspaceSession({
20237
+ return new EditorHostWorkspaceSession({
20238
+ authenticationRecovery: {
20239
+ host: "cli",
20240
+ canLogin: Boolean(options.onLogin)
20241
+ },
20023
20242
  machines: machines.map((machine) => ({
20024
20243
  id: machine.id,
20025
20244
  name: machine.name,
@@ -20055,14 +20274,75 @@ var CliEditorSession = class {
20055
20274
  showError: (message) => console.error(`Stately Editor: ${message}`)
20056
20275
  });
20057
20276
  }
20058
- receiveMessage(message) {
20277
+ async receiveMessage(message) {
20059
20278
  this.log("recv", message.type, message);
20279
+ if (this.disposed) return;
20280
+ if (message.type === "@statelyai.login") {
20281
+ if (!this.readyMessage || !this.options.onLogin) return;
20282
+ this.loginPromise ??= this.login().finally(() => {
20283
+ this.loginPromise = void 0;
20284
+ });
20285
+ return this.loginPromise;
20286
+ }
20287
+ if (this.loginPromise) {
20288
+ if (message.type !== "@statelyai.ready") return this.loginPromise;
20289
+ this.loginController?.abort();
20290
+ await this.loginPromise;
20291
+ if (this.disposed) return;
20292
+ }
20293
+ if (message.type === "@statelyai.ready") {
20294
+ this.readyMessage = message;
20295
+ try {
20296
+ const apiKey = this.options.getApiKey ? await this.options.getApiKey() : this.options.apiKey;
20297
+ if (this.disposed) return;
20298
+ if (apiKey !== this.options.apiKey) this.replaceCredential(apiKey);
20299
+ } catch (error) {
20300
+ this.reportLoginError(error);
20301
+ return;
20302
+ }
20303
+ }
20060
20304
  return this.session.receive(message);
20061
20305
  }
20062
20306
  dispose() {
20307
+ this.disposed = true;
20308
+ this.loginController?.abort();
20063
20309
  this.session.dispose();
20064
20310
  this.workspaceWatcher.dispose();
20065
20311
  }
20312
+ replaceCredential(apiKey) {
20313
+ this.session.dispose();
20314
+ this.options.apiKey = apiKey;
20315
+ this.options.access = void 0;
20316
+ this.session = this.createSession();
20317
+ }
20318
+ async login() {
20319
+ const controller = new AbortController();
20320
+ this.loginController = controller;
20321
+ try {
20322
+ const apiKey = await this.options.onLogin(controller.signal);
20323
+ if (this.disposed || controller.signal.aborted) return;
20324
+ this.replaceCredential(apiKey);
20325
+ await this.session.receive(this.readyMessage);
20326
+ } catch (error) {
20327
+ if (!this.disposed) this.reportLoginError(error);
20328
+ } finally {
20329
+ this.loginController = void 0;
20330
+ }
20331
+ }
20332
+ reportLoginError(error) {
20333
+ const message = error instanceof Error ? error.message : "Stately login failed. Try again.";
20334
+ const authenticationRecovery = {
20335
+ host: "cli",
20336
+ canLogin: Boolean(this.options.onLogin)
20337
+ };
20338
+ this.sendMessage({
20339
+ type: "@statelyai.error",
20340
+ code: "initialization_failed",
20341
+ message,
20342
+ authenticationRecovery
20343
+ });
20344
+ console.error(`Stately Editor: ${message}\n${authenticationRecoveryInstructions(authenticationRecovery)}`);
20345
+ }
20066
20346
  log(direction, type, payload) {
20067
20347
  if (this.options.debug) console.error(`[${direction}] ${type}`, redactDebugPayload(payload) ?? "");
20068
20348
  }
@@ -20164,6 +20444,31 @@ function redactDebugPayload(payload) {
20164
20444
  if (!payload || typeof payload !== "object") return payload;
20165
20445
  return Object.fromEntries(Object.entries(payload).map(([key, value]) => [key, /^(?:apiKey|accessToken|refreshToken|token|authorization)$/i.test(key) ? "[REDACTED]" : redactDebugPayload(value)]));
20166
20446
  }
20447
+ async function prepareEditorAuthentication(options) {
20448
+ const getAccess = (apiKey) => fetchEditorSyncAccess({
20449
+ editorUrl: options.editorUrl,
20450
+ apiKey,
20451
+ fetch: (input, init) => fetchEditorHost(input instanceof URL ? input : new URL(String(input)), init)
20452
+ });
20453
+ try {
20454
+ return {
20455
+ apiKey: options.apiKey,
20456
+ access: await getAccess(options.apiKey)
20457
+ };
20458
+ } catch (error) {
20459
+ if (!isEditorSyncAuthenticationError(error)) return { apiKey: options.apiKey };
20460
+ console.log("Log in to Stately to open this machine. Complete sign-in in your browser; the editor will open automatically.");
20461
+ const apiKey = await options.onLogin(new AbortController().signal);
20462
+ try {
20463
+ return {
20464
+ apiKey,
20465
+ access: await getAccess(apiKey)
20466
+ };
20467
+ } catch {
20468
+ return { apiKey };
20469
+ }
20470
+ }
20471
+ }
20167
20472
  async function openEditor(options) {
20168
20473
  const fileName = path$1.resolve(options.fileName);
20169
20474
  const machines = (options.machines ?? [{
@@ -20176,6 +20481,11 @@ async function openEditor(options) {
20176
20481
  }));
20177
20482
  await Promise.all([...new Set(machines.map((machine) => machine.fileName))].map((target) => fsPromises.access(target)));
20178
20483
  await assertEditorHostAvailable(options.editorUrl);
20484
+ if (options.shouldOpen && options.onLogin && process.stdin.isTTY && process.stdout.isTTY) Object.assign(options, await prepareEditorAuthentication({
20485
+ editorUrl: options.editorUrl,
20486
+ apiKey: options.apiKey,
20487
+ onLogin: options.onLogin
20488
+ }));
20179
20489
  const rootUri = fileNameToUri(fileName);
20180
20490
  const rootDir = path$1.resolve(options.rootDir ?? path$1.dirname(fileName));
20181
20491
  let activeClient;
@@ -20195,6 +20505,10 @@ async function openEditor(options) {
20195
20505
  socket.end("HTTP/1.1 404 Not Found\r\n\r\n");
20196
20506
  return;
20197
20507
  }
20508
+ if (request.headers.origin !== editorPageUrl || request.headers.host !== new URL(editorPageUrl).host) {
20509
+ socket.end("HTTP/1.1 403 Forbidden\r\n\r\n");
20510
+ return;
20511
+ }
20198
20512
  webSocketServer.handleUpgrade(request, socket, head, (webSocket) => {
20199
20513
  activeClient?.close();
20200
20514
  session?.dispose();
@@ -20217,7 +20531,11 @@ async function openEditor(options) {
20217
20531
  const editorPageUrl = `http://${options.host}:${port}`;
20218
20532
  console.log(`Stately visual editor: ${editorPageUrl}`);
20219
20533
  console.log(`Editing ${machines.length} machine${machines.length === 1 ? "" : "s"} in ${rootDir}`);
20220
- if (options.shouldOpen) openBrowser(editorPageUrl);
20534
+ if (options.shouldOpen) try {
20535
+ await openBrowser(editorPageUrl);
20536
+ } catch {
20537
+ console.warn("Could not open a browser automatically. Open the editor URL above.");
20538
+ }
20221
20539
  const shutdown = () => {
20222
20540
  session?.dispose();
20223
20541
  activeClient?.close();
@@ -20398,6 +20716,7 @@ function getCliWebviewContent(options) {
20398
20716
  ></iframe>
20399
20717
  <script>
20400
20718
  const iframe = document.getElementById('stately-editor');
20719
+ const editorOrigin = new URL(iframe.src).origin;
20401
20720
  const socket = new WebSocket(
20402
20721
  (location.protocol === 'https:' ? 'wss:' : 'ws:') + '//' + location.host + '/ws'
20403
20722
  );
@@ -20418,10 +20737,13 @@ function getCliWebviewContent(options) {
20418
20737
  });
20419
20738
 
20420
20739
  socket.addEventListener('message', (event) => {
20421
- iframe.contentWindow?.postMessage(JSON.parse(event.data), '*');
20740
+ iframe.contentWindow?.postMessage(JSON.parse(event.data), editorOrigin);
20422
20741
  });
20423
20742
 
20424
20743
  window.addEventListener('message', (event) => {
20744
+ if (event.source !== iframe.contentWindow || event.origin !== editorOrigin) {
20745
+ return;
20746
+ }
20425
20747
  if (!event.data?.type?.startsWith?.('@statelyai.')) {
20426
20748
  return;
20427
20749
  }
@@ -20454,8 +20776,8 @@ async function assertEditorHostAvailable(editorUrl) {
20454
20776
  function formatEditorHostError(baseUrl, status, cause) {
20455
20777
  return `Cannot reach a usable editor host at ${baseUrl}.${status ? ` HTTP ${status} from /embed.` : ""}${cause instanceof Error && cause.message ? ` ${cause.message}` : ""} The CLI loads editor URL defaults from your environment, including \`.env.local\`. Start the editor host, set \`STATELY_EDITOR_URL\`, or pass \`--editor-url <url>\`.`;
20456
20778
  }
20457
- function openBrowser(url) {
20458
- spawn(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open", process.platform === "win32" ? [
20779
+ async function openBrowser(url) {
20780
+ const child = spawn(process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open", process.platform === "win32" ? [
20459
20781
  "/c",
20460
20782
  "start",
20461
20783
  "",
@@ -20463,7 +20785,12 @@ function openBrowser(url) {
20463
20785
  ] : [url], {
20464
20786
  detached: true,
20465
20787
  stdio: "ignore"
20466
- }).unref();
20788
+ });
20789
+ child.unref();
20790
+ await new Promise((resolve, reject) => {
20791
+ child.once("spawn", resolve);
20792
+ child.once("error", reject);
20793
+ });
20467
20794
  }
20468
20795
  function fileNameToUri(fileName) {
20469
20796
  return pathToFileURL(path$1.resolve(fileName)).toString();
@@ -20489,6 +20816,8 @@ async function createLocalOAuthCallbackServer(options) {
20489
20816
  resolveCode = resolve;
20490
20817
  rejectCode = reject;
20491
20818
  });
20819
+ codePromise.catch(() => {});
20820
+ const cancel = () => rejectCode(/* @__PURE__ */ new Error("Stately sign-in was canceled."));
20492
20821
  const server = http.createServer((req, res) => {
20493
20822
  const requestUrl = new URL(req.url ?? "/", "http://127.0.0.1");
20494
20823
  const code = requestUrl.searchParams.get("code");
@@ -20516,12 +20845,17 @@ async function createLocalOAuthCallbackServer(options) {
20516
20845
  });
20517
20846
  const address = server.address();
20518
20847
  if (!address || typeof address === "string") throw new Error("Could not start OAuth callback server.");
20848
+ options.signal?.addEventListener("abort", cancel, { once: true });
20849
+ if (options.signal?.aborted) cancel();
20519
20850
  return {
20520
20851
  redirectUri: `http://127.0.0.1:${address.port}/callback`,
20521
20852
  waitForCode: () => codePromise,
20522
- close: () => new Promise((resolve) => {
20523
- server.close(() => resolve());
20524
- })
20853
+ close: () => {
20854
+ options.signal?.removeEventListener("abort", cancel);
20855
+ return new Promise((resolve) => {
20856
+ server.close(() => resolve());
20857
+ });
20858
+ }
20525
20859
  };
20526
20860
  }
20527
20861
 
@@ -21780,9 +22114,10 @@ function getOAuthExpiresAt(credential) {
21780
22114
  function isTerminalOAuthRefreshError(error) {
21781
22115
  return error instanceof Error && /Token refresh failed with HTTP (?:400|401|403)\b/.test(error.message);
21782
22116
  }
22117
+ var ExpiredStatelyCredentialError = class extends Error {};
21783
22118
  async function invalidateExpiredOAuthCredential() {
21784
22119
  await deleteStoredApiKey();
21785
- throw new Error("Your Stately session expired. Run `npx statelyai login` to continue.");
22120
+ throw new ExpiredStatelyCredentialError("Your Stately session expired. Run `npx statelyai login` to continue.");
21786
22121
  }
21787
22122
  async function refreshStoredOAuthCredential(credential, options) {
21788
22123
  if (!credential.refreshToken) return invalidateExpiredOAuthCredential();
@@ -21816,7 +22151,7 @@ async function refreshStoredOAuthCredential(credential, options) {
21816
22151
  }
21817
22152
  }
21818
22153
  async function resolveApiKey(options = {}) {
21819
- const envApiKey = getEnvApiKey();
22154
+ const envApiKey = options.ignoreEnvironment ? void 0 : getEnvApiKey();
21820
22155
  if (envApiKey) return {
21821
22156
  apiKey: envApiKey.apiKey,
21822
22157
  source: "env",
@@ -22042,22 +22377,32 @@ function formatStoredCredentialSource(storedCredential) {
22042
22377
  return `Credential source: stored unknown (${describeCredentialBackend(storedCredential.backend, storedCredential.location)}).`;
22043
22378
  }
22044
22379
  async function runOAuthBrowserLogin(options) {
22380
+ options.signal?.throwIfAborted();
22045
22381
  const state = crypto.randomBytes(16).toString("base64url");
22046
- const callbackServer = await createLocalOAuthCallbackServer({ state });
22382
+ const callbackServer = await createLocalOAuthCallbackServer({
22383
+ state,
22384
+ signal: options.signal
22385
+ });
22386
+ const loginFetch = (input, init) => fetch(input, {
22387
+ ...init,
22388
+ signal: options.signal
22389
+ });
22047
22390
  try {
22048
22391
  const { authorizeUrl, codeVerifier, discovery } = await buildOAuthLoginStart({
22392
+ fetch: loginFetch,
22049
22393
  baseUrl: options.baseUrl,
22050
22394
  redirectUri: callbackServer.redirectUri,
22051
22395
  state
22052
22396
  });
22397
+ options.signal?.throwIfAborted();
22053
22398
  options.log(`Opening browser for ${discovery.resourceUrl}`);
22054
- options.log(authorizeUrl);
22055
22399
  let openedBrowser = false;
22056
22400
  try {
22057
- openBrowser(authorizeUrl);
22401
+ await openBrowser(authorizeUrl);
22058
22402
  openedBrowser = true;
22059
22403
  } catch {
22060
22404
  options.log("Could not open a browser automatically.");
22405
+ options.log(authorizeUrl);
22061
22406
  }
22062
22407
  const codePromise = callbackServer.waitForCode();
22063
22408
  const pastedCodePromise = !openedBrowser && process.stdin.isTTY && process.stdout.isTTY ? promptForOAuthCallback().then((value) => {
@@ -22066,13 +22411,16 @@ async function runOAuthBrowserLogin(options) {
22066
22411
  return callback.code;
22067
22412
  }) : void 0;
22068
22413
  const code = await (pastedCodePromise ? Promise.race([codePromise, pastedCodePromise]) : codePromise);
22069
- return setStoredCredential(createOAuthCredential(await exchangeCodeForToken({
22414
+ const tokenResponse = await exchangeCodeForToken({
22415
+ fetch: loginFetch,
22070
22416
  tokenEndpoint: discovery.tokenEndpoint,
22071
22417
  code,
22072
22418
  codeVerifier,
22073
22419
  redirectUri: callbackServer.redirectUri,
22074
22420
  clientId: discovery.clientId
22075
- }), {
22421
+ });
22422
+ options.signal?.throwIfAborted();
22423
+ return setStoredCredential(createOAuthCredential(tokenResponse, {
22076
22424
  clientId: discovery.clientId,
22077
22425
  tokenEndpoint: discovery.tokenEndpoint
22078
22426
  }));
@@ -22985,7 +23333,16 @@ var OpenCommand = class OpenCommand extends Command {
22985
23333
  };
22986
23334
  async run() {
22987
23335
  const { args, flags } = await this.parse(OpenCommand);
22988
- const apiKey = (await resolveApiKey()).apiKey;
23336
+ let hasLoggedIn = false;
23337
+ const getApiKey = async () => {
23338
+ try {
23339
+ return (await resolveApiKey({ ignoreEnvironment: hasLoggedIn })).apiKey;
23340
+ } catch (error) {
23341
+ if (error instanceof ExpiredStatelyCredentialError) return void 0;
23342
+ throw error;
23343
+ }
23344
+ };
23345
+ const apiKey = await getApiKey();
22989
23346
  const cwd = process.cwd();
22990
23347
  const discovered = args.file ? await discoverMachinesInFile(path.resolve(args.file)) : await scanProjectMachines({ cwd });
22991
23348
  if (discovered.length === 0) throw new Error(args.file ? `No machines found in ${path.resolve(args.file)}.` : `No machines found in ${cwd}.`);
@@ -23018,7 +23375,19 @@ var OpenCommand = class OpenCommand extends Command {
23018
23375
  sourceUpdateStrategy: flags["source-update"] ?? configuredSourceUpdateStrategy ?? "preserve",
23019
23376
  apiKey,
23020
23377
  studioUrl: resolveApiBaseUrl(configuredStudioUrl),
23021
- debug: flags.debug
23378
+ debug: flags.debug,
23379
+ getApiKey,
23380
+ onLogin: async (signal) => {
23381
+ const stored = await runOAuthBrowserLogin({
23382
+ baseUrl: flags["editor-url"] ?? getDefaultEditorUrl(),
23383
+ log: (message) => this.log(message),
23384
+ signal
23385
+ });
23386
+ if (stored.credential?.type !== "oauth") throw new Error("Stately login did not return an OAuth credential.");
23387
+ hasLoggedIn = true;
23388
+ this.log("Logged in to Stately. Loading your machine…");
23389
+ return stored.credential.accessToken;
23390
+ }
23022
23391
  });
23023
23392
  }
23024
23393
  };