@lokascript/compilation-service 2.0.0 → 2.1.0

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.
@@ -5119,31 +5119,7 @@ var Parser = class _Parser {
5119
5119
  }
5120
5120
  }
5121
5121
  }
5122
- const handlerCommands = [];
5123
- while (!this.isAtEnd() && !this.check("end")) {
5124
- if (this.checkIsCommand() || this.isCommand(this.peek().value)) {
5125
- this.advance();
5126
- const savedError = this.error;
5127
- try {
5128
- const cmd = this.parseCommand();
5129
- if (this.error && this.error !== savedError) {
5130
- this.error = savedError;
5131
- }
5132
- handlerCommands.push(cmd);
5133
- while (!this.isAtEnd() && !this.check("end") && !this.checkIsCommand() && !this.isCommand(this.peek().value)) {
5134
- this.advance();
5135
- }
5136
- } catch (error) {
5137
- this.error = savedError;
5138
- break;
5139
- }
5140
- } else {
5141
- if (!this.check("end")) {
5142
- this.addError(`Unexpected token in event handler: ${this.peek().value}`);
5143
- }
5144
- break;
5145
- }
5146
- }
5122
+ const handlerCommands = this.parseCommandListUntilEnd();
5147
5123
  const handlerNode = {
5148
5124
  type: "eventHandler",
5149
5125
  event: eventName,
@@ -5156,7 +5132,6 @@ var Parser = class _Parser {
5156
5132
  column: handlerPos.column
5157
5133
  };
5158
5134
  eventHandlers.push(handlerNode);
5159
- this.consume("end", "Expected 'end' after event handler body");
5160
5135
  } else if (this.match("init")) {
5161
5136
  const initCommands = this.parseCommandBlock(["end"]);
5162
5137
  initBlock = {
@@ -6946,8 +6921,9 @@ var SPECIAL_DOM_PROPERTIES = {
6946
6921
  outerhtml: (el) => el.outerHTML,
6947
6922
  value: (el) => isFormElement(el) ? el.value : void 0,
6948
6923
  checked: (el) => isInputElement$1(el) ? el.checked : void 0,
6949
- disabled: (el) => isFormElement(el) ? el.disabled : void 0,
6924
+ disabled: (el) => "disabled" in el ? el.disabled : void 0,
6950
6925
  selected: (el) => isOptionElement$1(el) ? el.selected : void 0,
6926
+ tabindex: (el) => isHTMLElement$2(el) ? el.tabIndex : void 0,
6951
6927
  hidden: (el) => isHTMLElement$2(el) ? el.hidden : void 0,
6952
6928
  style: (el) => getComputedStyle(el),
6953
6929
  children: (el) => Array.from(el.children),
@@ -13846,14 +13822,31 @@ var RuntimeBase = class _RuntimeBase {
13846
13822
  }
13847
13823
  async executeBehaviorDefinition(node, _context) {
13848
13824
  const { name, parameters, eventHandlers, initBlock } = node;
13849
- this.behaviorRegistry.set(name, { name, parameters, eventHandlers, initBlock });
13850
- debug.runtime(`RUNTIME BASE: Registered behavior '${name}'`);
13825
+ const imperativeInstaller = node.imperativeInstaller;
13826
+ if (typeof imperativeInstaller === "function") {
13827
+ this.behaviorRegistry.set(name, {
13828
+ name,
13829
+ parameters,
13830
+ type: "imperative",
13831
+ install: imperativeInstaller
13832
+ });
13833
+ debug.runtime(`RUNTIME BASE: Registered imperative behavior '${name}'`);
13834
+ } else {
13835
+ this.behaviorRegistry.set(name, { name, parameters, eventHandlers, initBlock });
13836
+ debug.runtime(`RUNTIME BASE: Registered behavior '${name}'`);
13837
+ }
13851
13838
  }
13852
13839
  async installBehaviorOnElement(behaviorName, element, parameters) {
13853
13840
  debug.runtime(`BEHAVIOR: installBehaviorOnElement called: ${behaviorName}`);
13854
13841
  const behavior = this.behaviorRegistry.get(behaviorName);
13855
13842
  if (!behavior)
13856
13843
  throw new Error(`Behavior "${behaviorName}" not found`);
13844
+ if (behavior.type === "imperative" && typeof behavior.install === "function") {
13845
+ debug.runtime(`BEHAVIOR: Installing imperative behavior '${behaviorName}'`);
13846
+ behavior.install(element, parameters);
13847
+ debug.runtime(`BEHAVIOR: Finished installing imperative behavior '${behaviorName}'`);
13848
+ return;
13849
+ }
13857
13850
  debug.runtime(`BEHAVIOR: Found behavior, eventHandlers count: ${behavior.eventHandlers?.length || 0}`);
13858
13851
  const baseBehaviorContext = {
13859
13852
  me: element,
@@ -16584,6 +16577,7 @@ var Operation = {
16584
16577
  };
16585
16578
  var candidateNodes = /* @__PURE__ */ new Set();
16586
16579
  var candidateElements = /* @__PURE__ */ new Set();
16580
+ var candidateElementsWithIds = /* @__PURE__ */ new Set();
16587
16581
  var unmatchedNodes = /* @__PURE__ */ new Set();
16588
16582
  var unmatchedElements = /* @__PURE__ */ new Set();
16589
16583
  var whitespaceNodes = /* @__PURE__ */ new Set();
@@ -16675,6 +16669,7 @@ var Morph = class {
16675
16669
  const toChildNodes = [...to.childNodes];
16676
16670
  candidateNodes.clear();
16677
16671
  candidateElements.clear();
16672
+ candidateElementsWithIds.clear();
16678
16673
  unmatchedNodes.clear();
16679
16674
  unmatchedElements.clear();
16680
16675
  whitespaceNodes.clear();
@@ -16690,7 +16685,11 @@ var Morph = class {
16690
16685
  candidateNodeTypeMap[i] = nodeType;
16691
16686
  if (nodeType === ELEMENT_NODE_TYPE) {
16692
16687
  candidateLocalNameMap[i] = candidate.localName;
16693
- candidateElements.add(i);
16688
+ if (candidate.id !== "") {
16689
+ candidateElementsWithIds.add(i);
16690
+ } else {
16691
+ candidateElements.add(i);
16692
+ }
16694
16693
  } else if (nodeType === TEXT_NODE_TYPE && candidate.textContent?.trim() === "") {
16695
16694
  whitespaceNodes.add(i);
16696
16695
  } else {
@@ -16733,13 +16732,13 @@ var Morph = class {
16733
16732
  if (id === "" && !idArray)
16734
16733
  continue;
16735
16734
  candidateLoop:
16736
- for (const candidateIndex of candidateElements) {
16735
+ for (const candidateIndex of candidateElementsWithIds) {
16737
16736
  const candidate = fromChildNodes[candidateIndex];
16738
16737
  if (localNameMap[unmatchedIndex] === candidateLocalNameMap[candidateIndex]) {
16739
16738
  if (id !== "" && id === candidate.id) {
16740
16739
  matches[unmatchedIndex] = candidateIndex;
16741
16740
  op[unmatchedIndex] = Operation.SameElement;
16742
- candidateElements.delete(candidateIndex);
16741
+ candidateElementsWithIds.delete(candidateIndex);
16743
16742
  unmatchedElements.delete(unmatchedIndex);
16744
16743
  break candidateLoop;
16745
16744
  }
@@ -16751,7 +16750,7 @@ var Morph = class {
16751
16750
  if (candidateIdSet.has(arrayId)) {
16752
16751
  matches[unmatchedIndex] = candidateIndex;
16753
16752
  op[unmatchedIndex] = Operation.SameElement;
16754
- candidateElements.delete(candidateIndex);
16753
+ candidateElementsWithIds.delete(candidateIndex);
16755
16754
  unmatchedElements.delete(unmatchedIndex);
16756
16755
  break candidateLoop;
16757
16756
  }
@@ -16826,6 +16825,8 @@ var Morph = class {
16826
16825
  __privateMethod(this, _Morph_instances, removeNode_fn).call(this, fromChildNodes[i]);
16827
16826
  for (const i of candidateElements)
16828
16827
  __privateMethod(this, _Morph_instances, removeNode_fn).call(this, fromChildNodes[i]);
16828
+ for (const i of candidateElementsWithIds)
16829
+ __privateMethod(this, _Morph_instances, removeNode_fn).call(this, fromChildNodes[i]);
16829
16830
  const lisIndices = longestIncreasingSubsequence(matches);
16830
16831
  const shouldNotMove = new Array(fromChildNodes.length);
16831
16832
  for (let i = 0; i < lisIndices.length; i++) {
@@ -22545,6 +22546,65 @@ function createReference$1(value) {
22545
22546
  function createPropertyPath$1(object, property) {
22546
22547
  return { type: "property-path", object, property };
22547
22548
  }
22549
+ function createFlag(name, enabled = true) {
22550
+ return { type: "flag", name, enabled };
22551
+ }
22552
+ function createCommandNode$1(action, roles, metadata) {
22553
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
22554
+ const node = {
22555
+ kind: "command",
22556
+ action,
22557
+ roles: rolesMap
22558
+ };
22559
+ if (metadata) {
22560
+ return { ...node, metadata };
22561
+ }
22562
+ return node;
22563
+ }
22564
+ function createEventHandlerNode(action, roles, body, metadata, eventModifiers) {
22565
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
22566
+ const base = {
22567
+ kind: "event-handler",
22568
+ action,
22569
+ roles: rolesMap,
22570
+ body
22571
+ };
22572
+ return {
22573
+ ...base,
22574
+ ...eventModifiers,
22575
+ ...metadata && { metadata }
22576
+ };
22577
+ }
22578
+ function createConditionalNode$1(action, roles, thenBranch, elseBranch, metadata) {
22579
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
22580
+ const base = {
22581
+ kind: "conditional",
22582
+ action,
22583
+ roles: rolesMap,
22584
+ thenBranch
22585
+ };
22586
+ return {
22587
+ ...base,
22588
+ ...elseBranch && { elseBranch },
22589
+ ...metadata && { metadata }
22590
+ };
22591
+ }
22592
+ function createLoopNode$1(action, roles, loopVariant, body, loopVariable, indexVariable, metadata) {
22593
+ const rolesMap = roles instanceof Map ? roles : new Map(Object.entries(roles));
22594
+ const base = {
22595
+ kind: "loop",
22596
+ action,
22597
+ roles: rolesMap,
22598
+ loopVariant,
22599
+ body
22600
+ };
22601
+ return {
22602
+ ...base,
22603
+ ...loopVariable && { loopVariable },
22604
+ ...indexVariable && { indexVariable },
22605
+ ...metadata && { metadata }
22606
+ };
22607
+ }
22548
22608
  var DebugLogger = class {
22549
22609
  constructor(namespace) {
22550
22610
  this.namespace = namespace;
@@ -22742,7 +22802,7 @@ var _PatternMatcher$1 = class _PatternMatcher {
22742
22802
  * a token and the immediately following pattern token fails, the matcher
22743
22803
  * resets to before the optional role and retries the failed token.
22744
22804
  */
22745
- matchTokenSequence(tokens, patternTokens, captured) {
22805
+ matchTokenSequence(tokens, patternTokens, captured, parentStopMarkers) {
22746
22806
  const firstPatternToken = patternTokens[0];
22747
22807
  const patternExpectsConjunction = firstPatternToken?.type === "literal" && (firstPatternToken.value === "and" || firstPatternToken.value === "then" || firstPatternToken.alternatives?.includes("and") || firstPatternToken.alternatives?.includes("then"));
22748
22808
  if (this.currentProfile?.code === "ar" && !patternExpectsConjunction) {
@@ -22766,6 +22826,9 @@ var _PatternMatcher$1 = class _PatternMatcher {
22766
22826
  );
22767
22827
  if (patternToken.type === "role" && patternToken.greedy) {
22768
22828
  const stopMarkers = this.collectStopMarkers(patternTokens, i + 1);
22829
+ if (parentStopMarkers) {
22830
+ for (const m of parentStopMarkers) stopMarkers.add(m);
22831
+ }
22769
22832
  const values = [];
22770
22833
  while (!tokens.isAtEnd()) {
22771
22834
  const nextToken = tokens.peek();
@@ -22785,8 +22848,58 @@ var _PatternMatcher$1 = class _PatternMatcher {
22785
22848
  return false;
22786
22849
  }
22787
22850
  }
22851
+ if (patternToken.type === "group") {
22852
+ const siblingStopMarkers = this.collectStopMarkers(patternTokens, i + 1);
22853
+ if (parentStopMarkers) {
22854
+ for (const m of parentStopMarkers) siblingStopMarkers.add(m);
22855
+ }
22856
+ const groupMatched = this.matchGroupToken(
22857
+ tokens,
22858
+ patternToken,
22859
+ captured,
22860
+ siblingStopMarkers
22861
+ );
22862
+ if (groupMatched) {
22863
+ prevOptionalMark = null;
22864
+ prevOptionalRole = null;
22865
+ continue;
22866
+ }
22867
+ if (this.isOptional(patternToken)) {
22868
+ continue;
22869
+ }
22870
+ return false;
22871
+ }
22788
22872
  const isOptionalRole = patternToken.type === "role" && patternToken.optional === true;
22789
22873
  const markBefore = isOptionalRole ? tokens.mark() : null;
22874
+ const isRoleToken = patternToken.type === "role";
22875
+ if (isRoleToken && !isOptionalRole) {
22876
+ const nextPT = i + 1 < patternTokens.length ? patternTokens[i + 1] : null;
22877
+ if (nextPT?.type === "literal") {
22878
+ const currentToken = tokens.peek();
22879
+ if (currentToken && this.getMatchType(currentToken, nextPT.value) !== "none") {
22880
+ this.logger.debug(
22881
+ " >> MARKER-STEAL GUARD: current token matches next literal, skipping role"
22882
+ );
22883
+ this.logger.debug(" >> Token match FAILED");
22884
+ if (this.isOptional(patternToken)) {
22885
+ continue;
22886
+ }
22887
+ if (prevOptionalMark && prevOptionalRole) {
22888
+ this.logger.debug(" >> BACKTRACKING: undoing optional role", prevOptionalRole);
22889
+ tokens.reset(prevOptionalMark);
22890
+ captured.delete(prevOptionalRole);
22891
+ prevOptionalMark = null;
22892
+ prevOptionalRole = null;
22893
+ const retryMatched = this.matchPatternToken(tokens, patternToken, captured);
22894
+ this.logger.debug(" >> Backtrack retry result:", retryMatched);
22895
+ if (retryMatched) {
22896
+ continue;
22897
+ }
22898
+ }
22899
+ return false;
22900
+ }
22901
+ }
22902
+ }
22790
22903
  const matched = this.matchPatternToken(tokens, patternToken, captured);
22791
22904
  this.logger.debug(" >> Match result:", matched);
22792
22905
  if (matched) {
@@ -22907,7 +23020,7 @@ var _PatternMatcher$1 = class _PatternMatcher {
22907
23020
  const possessiveValue = this.tryMatchPossessiveExpression(tokens);
22908
23021
  if (possessiveValue) {
22909
23022
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
22910
- if (!patternToken.expectedTypes.includes(possessiveValue.type) && !patternToken.expectedTypes.includes("expression")) {
23023
+ if (!isTypeCompatible$1(possessiveValue.type, patternToken.expectedTypes)) {
22911
23024
  return patternToken.optional || false;
22912
23025
  }
22913
23026
  }
@@ -22917,7 +23030,7 @@ var _PatternMatcher$1 = class _PatternMatcher {
22917
23030
  const methodCallValue = this.tryMatchMethodCallExpression(tokens);
22918
23031
  if (methodCallValue) {
22919
23032
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
22920
- if (!patternToken.expectedTypes.includes(methodCallValue.type) && !patternToken.expectedTypes.includes("expression")) {
23033
+ if (!isTypeCompatible$1(methodCallValue.type, patternToken.expectedTypes)) {
22921
23034
  return patternToken.optional || false;
22922
23035
  }
22923
23036
  }
@@ -22937,7 +23050,7 @@ var _PatternMatcher$1 = class _PatternMatcher {
22937
23050
  const propertyAccessValue = this.tryMatchPropertyAccessExpression(tokens);
22938
23051
  if (propertyAccessValue) {
22939
23052
  if (patternToken.expectedTypes && patternToken.expectedTypes.length > 0) {
22940
- if (!patternToken.expectedTypes.includes(propertyAccessValue.type) && !patternToken.expectedTypes.includes("expression")) {
23053
+ if (!isTypeCompatible$1(propertyAccessValue.type, patternToken.expectedTypes)) {
22941
23054
  return patternToken.optional || false;
22942
23055
  }
22943
23056
  }
@@ -23256,10 +23369,15 @@ var _PatternMatcher$1 = class _PatternMatcher {
23256
23369
  * (e.g., a value like 'hello') to sit between groups without blocking later
23257
23370
  * marker matches.
23258
23371
  */
23259
- matchGroupToken(tokens, patternToken, captured) {
23372
+ matchGroupToken(tokens, patternToken, captured, parentStopMarkers) {
23260
23373
  const mark = tokens.mark();
23261
23374
  const capturedBefore = new Set(captured.keys());
23262
- const success2 = this.matchTokenSequence(tokens, patternToken.tokens, captured);
23375
+ const success2 = this.matchTokenSequence(
23376
+ tokens,
23377
+ patternToken.tokens,
23378
+ captured,
23379
+ parentStopMarkers
23380
+ );
23263
23381
  if (success2) return true;
23264
23382
  tokens.reset(mark);
23265
23383
  for (const role of captured.keys()) {
@@ -23281,7 +23399,12 @@ var _PatternMatcher$1 = class _PatternMatcher {
23281
23399
  "- skipping intervening tokens"
23282
23400
  );
23283
23401
  for (let s = 0; s < offset; s++) tokens.advance();
23284
- const retrySuccess = this.matchTokenSequence(tokens, patternToken.tokens, captured);
23402
+ const retrySuccess = this.matchTokenSequence(
23403
+ tokens,
23404
+ patternToken.tokens,
23405
+ captured,
23406
+ parentStopMarkers
23407
+ );
23285
23408
  if (retrySuccess) return true;
23286
23409
  tokens.reset(mark);
23287
23410
  for (const role of captured.keys()) {
@@ -23348,7 +23471,7 @@ var _PatternMatcher$1 = class _PatternMatcher {
23348
23471
  break;
23349
23472
  }
23350
23473
  }
23351
- break;
23474
+ if (!pt.optional) break;
23352
23475
  }
23353
23476
  }
23354
23477
  return markers;
@@ -23358,8 +23481,9 @@ var _PatternMatcher$1 = class _PatternMatcher {
23358
23481
  */
23359
23482
  isStopMarker(token, stopMarkers) {
23360
23483
  if (stopMarkers.size === 0) return false;
23361
- const value = (token.normalized || token.value).toLowerCase();
23362
- return stopMarkers.has(value);
23484
+ if (stopMarkers.has(token.value.toLowerCase())) return true;
23485
+ if (token.normalized && stopMarkers.has(token.normalized.toLowerCase())) return true;
23486
+ return false;
23363
23487
  }
23364
23488
  /**
23365
23489
  * Convert a language token to a semantic value.
@@ -23844,6 +23968,362 @@ function createTokenizerContext(tokenizer) {
23844
23968
  }
23845
23969
  return ctx;
23846
23970
  }
23971
+ var DEFAULT_REFERENCES = /* @__PURE__ */ new Set([
23972
+ "me",
23973
+ "you",
23974
+ "it",
23975
+ "result",
23976
+ "event",
23977
+ "target",
23978
+ "body"
23979
+ ]);
23980
+ function isValidReference2(value, referenceSet = DEFAULT_REFERENCES) {
23981
+ return referenceSet.has(value);
23982
+ }
23983
+ var STRUCTURAL_ROLES = /* @__PURE__ */ new Set([
23984
+ "body",
23985
+ "then",
23986
+ "else",
23987
+ "condition",
23988
+ "loop-body",
23989
+ "variable",
23990
+ "catch",
23991
+ // v1.2: try/catch/finally
23992
+ "finally"
23993
+ // v1.2: try/catch/finally
23994
+ ]);
23995
+ function isNestedCommand(value) {
23996
+ const inner = value.slice(1, -1);
23997
+ let depth = 0;
23998
+ for (const ch of inner) {
23999
+ if (ch === "[") depth++;
24000
+ else if (ch === "]") depth--;
24001
+ else if (depth === 0 && (ch === " " || ch === ":")) return true;
24002
+ }
24003
+ return false;
24004
+ }
24005
+ function parseExplicit$1(input, options = {}) {
24006
+ const trimmed = input.trim();
24007
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
24008
+ throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
24009
+ }
24010
+ const content = trimmed.slice(1, -1).trim();
24011
+ if (!content) {
24012
+ throw new Error("Empty explicit statement");
24013
+ }
24014
+ const tokens = tokenizeExplicit(content);
24015
+ if (tokens.length === 0) {
24016
+ throw new Error("No command specified in explicit statement");
24017
+ }
24018
+ const command2 = tokens[0].toLowerCase();
24019
+ const roles = /* @__PURE__ */ new Map();
24020
+ const refSet = options.referenceSet ?? DEFAULT_REFERENCES;
24021
+ const schema = options.schemaLookup?.getSchema(command2);
24022
+ const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
24023
+ for (let i = 1; i < tokens.length; i++) {
24024
+ const token = tokens[i];
24025
+ if (token.startsWith("+") || token.startsWith("~")) {
24026
+ const enabled = token.startsWith("+");
24027
+ const flagName = token.slice(1);
24028
+ if (!flagName) {
24029
+ throw new Error(`Empty flag name: "${token}"`);
24030
+ }
24031
+ if (validRoleNames && !validRoleNames.has(flagName)) {
24032
+ const roleList = [...validRoleNames].join(", ");
24033
+ throw new Error(
24034
+ `Unknown flag "${flagName}" for command "${command2}". Valid roles: ${roleList}`
24035
+ );
24036
+ }
24037
+ roles.set(flagName, createFlag(flagName, enabled));
24038
+ continue;
24039
+ }
24040
+ const colonIndex = token.indexOf(":");
24041
+ if (colonIndex === -1) {
24042
+ throw new Error(`Invalid role format: "${token}". Expected role:value or +flag`);
24043
+ }
24044
+ const roleName = token.slice(0, colonIndex);
24045
+ if (validRoleNames && !STRUCTURAL_ROLES.has(roleName) && !validRoleNames.has(roleName)) {
24046
+ const roleList = [...validRoleNames].join(", ");
24047
+ throw new Error(
24048
+ `Unknown role "${roleName}" for command "${command2}". Valid roles: ${roleList}`
24049
+ );
24050
+ }
24051
+ const role = roleName;
24052
+ const valueStr = token.slice(colonIndex + 1);
24053
+ if (STRUCTURAL_ROLES.has(roleName) && valueStr.startsWith("[") && isNestedCommand(valueStr)) {
24054
+ const nestedEnd = findMatchingBracket(token, colonIndex + 1);
24055
+ const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
24056
+ roles.set(role, { type: "expression", raw: nestedSyntax });
24057
+ continue;
24058
+ }
24059
+ const value = parseExplicitValue(valueStr, refSet);
24060
+ roles.set(role, value);
24061
+ }
24062
+ if (schema && command2 !== "on") {
24063
+ for (const roleSpec of schema.roles) {
24064
+ if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
24065
+ throw new Error(
24066
+ `Missing required role "${roleSpec.role}" for command "${command2}": ${roleSpec.description}`
24067
+ );
24068
+ }
24069
+ }
24070
+ }
24071
+ switch (command2) {
24072
+ case "on": {
24073
+ const eventValue = roles.get("event");
24074
+ if (!eventValue) {
24075
+ throw new Error("Event handler requires event role: [on event:click ...]");
24076
+ }
24077
+ const body = extractStructuralBody(roles, "body", options);
24078
+ roles.delete("body");
24079
+ return createEventHandlerNode("on", roles, body, {
24080
+ sourceLanguage: "explicit"
24081
+ });
24082
+ }
24083
+ case "if": {
24084
+ const thenBranch = extractStructuralBody(roles, "then", options);
24085
+ const elseBranch = extractStructuralBody(roles, "else", options);
24086
+ roles.delete("then");
24087
+ roles.delete("else");
24088
+ return createConditionalNode$1(
24089
+ command2,
24090
+ roles,
24091
+ thenBranch,
24092
+ elseBranch.length > 0 ? elseBranch : void 0,
24093
+ {
24094
+ sourceLanguage: "explicit"
24095
+ }
24096
+ );
24097
+ }
24098
+ case "repeat": {
24099
+ const loopBody = extractStructuralBody(roles, "loop-body", options);
24100
+ const loopVariantValue = roles.get("loop-variant");
24101
+ const variableValue = roles.get("variable");
24102
+ const indexVariableValue = roles.get("index-variable");
24103
+ roles.delete("loop-body");
24104
+ roles.delete("loop-variant");
24105
+ roles.delete("variable");
24106
+ roles.delete("index-variable");
24107
+ let variant;
24108
+ if (loopVariantValue && loopVariantValue.type === "literal" && typeof loopVariantValue.value === "string") {
24109
+ variant = loopVariantValue.value;
24110
+ } else if (roles.has("quantity")) {
24111
+ variant = "times";
24112
+ } else if (roles.has("source")) {
24113
+ variant = "for";
24114
+ } else if (roles.has("condition")) {
24115
+ variant = "while";
24116
+ } else {
24117
+ variant = "forever";
24118
+ }
24119
+ const loopVariable = variableValue?.type === "literal" && typeof variableValue.value === "string" ? variableValue.value : variableValue?.type === "expression" ? variableValue.raw : void 0;
24120
+ const indexVariable = indexVariableValue?.type === "literal" && typeof indexVariableValue.value === "string" ? indexVariableValue.value : void 0;
24121
+ return createLoopNode$1(command2, roles, variant, loopBody, loopVariable, indexVariable, {
24122
+ sourceLanguage: "explicit"
24123
+ });
24124
+ }
24125
+ default:
24126
+ return createCommandNode$1(command2, roles, {
24127
+ sourceLanguage: "explicit"
24128
+ });
24129
+ }
24130
+ }
24131
+ function isExplicitSyntax(input) {
24132
+ const trimmed = input.trim();
24133
+ return trimmed.startsWith("[") && trimmed.endsWith("]");
24134
+ }
24135
+ function extractStructuralBody(roles, roleName, options) {
24136
+ const value = roles.get(roleName);
24137
+ if (!value) return [];
24138
+ if (value.type === "expression") {
24139
+ return [parseExplicit$1(value.raw, options)];
24140
+ }
24141
+ return [];
24142
+ }
24143
+ function inferSelectorKind(value) {
24144
+ if (!value) return void 0;
24145
+ if (value.startsWith("#")) return "id";
24146
+ if (value.startsWith(".")) return "class";
24147
+ if (value.startsWith("[")) return "attribute";
24148
+ if (value.startsWith("<") || value.startsWith("*")) return "element";
24149
+ if (/[>+~ ]/.test(value) && value.length > 1) return "complex";
24150
+ return void 0;
24151
+ }
24152
+ function tokenizeExplicit(content) {
24153
+ const tokens = [];
24154
+ let current = "";
24155
+ let inString = false;
24156
+ let stringChar = "";
24157
+ let bracketDepth = 0;
24158
+ for (let i = 0; i < content.length; i++) {
24159
+ const char = content[i];
24160
+ if (inString) {
24161
+ current += char;
24162
+ if (char === stringChar && content[i - 1] !== "\\") {
24163
+ inString = false;
24164
+ }
24165
+ continue;
24166
+ }
24167
+ if (char === '"' || char === "'") {
24168
+ inString = true;
24169
+ stringChar = char;
24170
+ current += char;
24171
+ continue;
24172
+ }
24173
+ if (char === "[") {
24174
+ bracketDepth++;
24175
+ current += char;
24176
+ continue;
24177
+ }
24178
+ if (char === "]") {
24179
+ bracketDepth--;
24180
+ current += char;
24181
+ continue;
24182
+ }
24183
+ if (char === " " && bracketDepth === 0) {
24184
+ if (current) {
24185
+ tokens.push(current);
24186
+ current = "";
24187
+ }
24188
+ continue;
24189
+ }
24190
+ current += char;
24191
+ }
24192
+ if (current) {
24193
+ tokens.push(current);
24194
+ }
24195
+ return tokens;
24196
+ }
24197
+ function parseExplicitValue(valueStr, referenceSet) {
24198
+ if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
24199
+ return createSelector$1(valueStr, inferSelectorKind(valueStr));
24200
+ }
24201
+ if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
24202
+ const inner = valueStr.slice(1, -1);
24203
+ return createLiteral$1(inner, "string");
24204
+ }
24205
+ if (valueStr === "true") return createLiteral$1(true, "boolean");
24206
+ if (valueStr === "false") return createLiteral$1(false, "boolean");
24207
+ const lowerRef = valueStr.toLowerCase();
24208
+ if (isValidReference2(lowerRef, referenceSet)) {
24209
+ return createReference$1(lowerRef);
24210
+ }
24211
+ const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
24212
+ if (numMatch) {
24213
+ const num = parseFloat(numMatch[1]);
24214
+ const suffix = numMatch[2];
24215
+ if (suffix) {
24216
+ return createLiteral$1(valueStr, "duration");
24217
+ }
24218
+ return createLiteral$1(num, "number");
24219
+ }
24220
+ return createLiteral$1(valueStr);
24221
+ }
24222
+ function findMatchingBracket(str, start) {
24223
+ let depth = 0;
24224
+ for (let i = start; i < str.length; i++) {
24225
+ if (str[i] === "[") depth++;
24226
+ else if (str[i] === "]") {
24227
+ depth--;
24228
+ if (depth === 0) return i;
24229
+ }
24230
+ }
24231
+ return str.length - 1;
24232
+ }
24233
+ function renderExplicit$1(node) {
24234
+ if (node.kind === "compound") {
24235
+ const compoundNode = node;
24236
+ const renderedStatements = compoundNode.statements.map((stmt) => renderExplicit$1(stmt));
24237
+ return renderedStatements.join(` ${compoundNode.chainType} `);
24238
+ }
24239
+ const parts = [node.action];
24240
+ for (const [role, value] of node.roles) {
24241
+ if (value.type === "flag") {
24242
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
24243
+ } else {
24244
+ parts.push(`${role}:${valueToString(value)}`);
24245
+ }
24246
+ }
24247
+ if (node.kind === "event-handler") {
24248
+ const eventNode = node;
24249
+ if (eventNode.body && eventNode.body.length > 0) {
24250
+ const bodyParts = eventNode.body.map((n) => renderExplicit$1(n));
24251
+ parts.push(`body:${bodyParts.join(" ")}`);
24252
+ }
24253
+ }
24254
+ if (node.kind === "conditional") {
24255
+ const condNode = node;
24256
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
24257
+ const thenParts = condNode.thenBranch.map((n) => renderExplicit$1(n));
24258
+ parts.push(`then:${thenParts.join(" ")}`);
24259
+ }
24260
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
24261
+ const elseParts = condNode.elseBranch.map((n) => renderExplicit$1(n));
24262
+ parts.push(`else:${elseParts.join(" ")}`);
24263
+ }
24264
+ }
24265
+ if (node.kind === "loop") {
24266
+ const loopNode = node;
24267
+ if (loopNode.loopVariant) {
24268
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
24269
+ }
24270
+ if (loopNode.loopVariable) {
24271
+ parts.push(`variable:${loopNode.loopVariable}`);
24272
+ }
24273
+ if (loopNode.indexVariable) {
24274
+ parts.push(`index-variable:${loopNode.indexVariable}`);
24275
+ }
24276
+ if (loopNode.body && loopNode.body.length > 0) {
24277
+ const bodyParts = loopNode.body.map((n) => renderExplicit$1(n));
24278
+ parts.push(`loop-body:${bodyParts.join(" ")}`);
24279
+ }
24280
+ }
24281
+ if (node.kind === "command") {
24282
+ const cmd = node;
24283
+ if (cmd.body && cmd.body.length > 0) {
24284
+ const bodyParts = cmd.body.map((n) => renderExplicit$1(n));
24285
+ parts.push(`body:${bodyParts.join(" ")}`);
24286
+ }
24287
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
24288
+ const catchParts = cmd.catchBranch.map((n) => renderExplicit$1(n));
24289
+ parts.push(`catch:${catchParts.join(" ")}`);
24290
+ }
24291
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
24292
+ const finallyParts = cmd.finallyBranch.map((n) => renderExplicit$1(n));
24293
+ parts.push(`finally:${finallyParts.join(" ")}`);
24294
+ }
24295
+ if (cmd.asyncVariant) {
24296
+ parts.push(`async-variant:${cmd.asyncVariant}`);
24297
+ }
24298
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
24299
+ const asyncParts = cmd.asyncBody.map((n) => renderExplicit$1(n));
24300
+ parts.push(`async-body:${asyncParts.join(" ")}`);
24301
+ }
24302
+ }
24303
+ return `[${parts.join(" ")}]`;
24304
+ }
24305
+ function valueToString(value) {
24306
+ switch (value.type) {
24307
+ case "literal":
24308
+ if (typeof value.value === "string") {
24309
+ if (value.dataType === "string" || /\s/.test(value.value)) {
24310
+ return `"${value.value}"`;
24311
+ }
24312
+ return value.value;
24313
+ }
24314
+ return String(value.value);
24315
+ case "selector":
24316
+ return value.value;
24317
+ case "reference":
24318
+ return value.value;
24319
+ case "property-path":
24320
+ return `${valueToString(value.object)}'s ${value.property}`;
24321
+ case "expression":
24322
+ return value.raw;
24323
+ case "flag":
24324
+ return value.name;
24325
+ }
24326
+ }
23847
24327
  var TokenStreamImpl = class {
23848
24328
  constructor(tokens, language) {
23849
24329
  this.pos = 0;
@@ -30183,6 +30663,10 @@ var init_schema_error_codes = __esm({
30183
30663
  // Loop commands
30184
30664
  FOR_LOOP_MISSING_SOURCE: "SCHEMA_FOR_LOOP_MISSING_SOURCE",
30185
30665
  WHILE_LOOP_MISSING_CONDITION: "SCHEMA_WHILE_LOOP_MISSING_CONDITION",
30666
+ // Type constraint issues (v1.2)
30667
+ VALUE_TYPE_MISMATCH: "SCHEMA_VALUE_TYPE_MISMATCH",
30668
+ SELECTOR_KIND_MISMATCH: "SCHEMA_SELECTOR_KIND_MISMATCH",
30669
+ SELECTOR_KINDS_WITHOUT_SELECTOR_TYPE: "SCHEMA_SELECTOR_KINDS_WITHOUT_SELECTOR_TYPE",
30186
30670
  // Keyword collision
30187
30671
  KEYWORD_COLLISION: "PROFILE_KEYWORD_COLLISION"
30188
30672
  };
@@ -30206,6 +30690,10 @@ var init_schema_error_codes = __esm({
30206
30690
  // Loops
30207
30691
  [SchemaErrorCodes.FOR_LOOP_MISSING_SOURCE]: "For-loop should have a 'source' role for the collection to iterate over.",
30208
30692
  [SchemaErrorCodes.WHILE_LOOP_MISSING_CONDITION]: "While-loop should have a 'condition' role for the loop condition.",
30693
+ // Type constraint (v1.2)
30694
+ [SchemaErrorCodes.VALUE_TYPE_MISMATCH]: "Role '{role}' of command '{command}' expects type [{expectedTypes}], got '{actualType}'.",
30695
+ [SchemaErrorCodes.SELECTOR_KIND_MISMATCH]: "Role '{role}' of command '{command}' expects selector kind [{selectorKinds}], got '{actualKind}'.",
30696
+ [SchemaErrorCodes.SELECTOR_KINDS_WITHOUT_SELECTOR_TYPE]: "Role '{role}' has selectorKinds but expectedTypes does not include 'selector'.",
30209
30697
  // Keyword collision
30210
30698
  [SchemaErrorCodes.KEYWORD_COLLISION]: "Keyword '{keyword}' is used by multiple commands: {commands}. Only the first-registered command will be reachable."
30211
30699
  };
@@ -30220,6 +30708,9 @@ var init_schema_error_codes = __esm({
30220
30708
  [SchemaErrorCodes.CONDITIONAL_CONDITION_NOT_REQUIRED]: "Set required: true on the 'condition' role.",
30221
30709
  [SchemaErrorCodes.FOR_LOOP_MISSING_SOURCE]: "Add a 'source' role for the collection to iterate over.",
30222
30710
  [SchemaErrorCodes.WHILE_LOOP_MISSING_CONDITION]: "Add a 'condition' role for the loop continuation condition.",
30711
+ [SchemaErrorCodes.VALUE_TYPE_MISMATCH]: "Ensure the value matches one of the expected types: [{expectedTypes}].",
30712
+ [SchemaErrorCodes.SELECTOR_KIND_MISMATCH]: "Use a selector of the correct kind: [{selectorKinds}].",
30713
+ [SchemaErrorCodes.SELECTOR_KINDS_WITHOUT_SELECTOR_TYPE]: "Either add 'selector' to expectedTypes or remove selectorKinds.",
30223
30714
  [SchemaErrorCodes.KEYWORD_COLLISION]: "Give each command a unique keyword. Move the shared keyword to alternatives on one command, or use a more specific translation."
30224
30715
  };
30225
30716
  }
@@ -30232,7 +30723,8 @@ __export(schema_validator_exports, {
30232
30723
  validateAllKeywordCollisions: () => validateAllKeywordCollisions,
30233
30724
  validateAllSchemas: () => validateAllSchemas,
30234
30725
  validateCommandSchema: () => validateCommandSchema,
30235
- validateKeywordCollisions: () => validateKeywordCollisions
30726
+ validateKeywordCollisions: () => validateKeywordCollisions,
30727
+ validateRoleValues: () => validateRoleValues
30236
30728
  });
30237
30729
  function createValidationResult(action, items) {
30238
30730
  return {
@@ -30284,6 +30776,20 @@ function validateCommandSchema(schema) {
30284
30776
  );
30285
30777
  }
30286
30778
  }
30779
+ for (const role of schema.roles) {
30780
+ if (role.selectorKinds && role.selectorKinds.length > 0) {
30781
+ if (!role.expectedTypes.includes("selector")) {
30782
+ items.push(
30783
+ createSchemaValidationItem(
30784
+ SchemaErrorCodes.SELECTOR_KINDS_WITHOUT_SELECTOR_TYPE,
30785
+ "warning",
30786
+ { role: role.role },
30787
+ role.role
30788
+ )
30789
+ );
30790
+ }
30791
+ }
30792
+ }
30287
30793
  switch (schema.action) {
30288
30794
  case "transition":
30289
30795
  validateTransitionSchema(schema, items);
@@ -30448,6 +30954,34 @@ function getValidationStats(validations) {
30448
30954
  byCode
30449
30955
  };
30450
30956
  }
30957
+ function validateRoleValues(schema, values) {
30958
+ const diagnostics = [];
30959
+ for (const value of values) {
30960
+ const roleSpec = schema.roles.find((r) => r.role === value.role);
30961
+ if (!roleSpec) continue;
30962
+ if (roleSpec.expectedTypes.length > 0 && !roleSpec.expectedTypes.includes(value.type)) {
30963
+ if (!roleSpec.expectedTypes.includes("expression")) {
30964
+ diagnostics.push({
30965
+ level: "error",
30966
+ role: value.role,
30967
+ message: `${schema.action}.${value.role} expects type [${roleSpec.expectedTypes.join(", ")}], got '${value.type}'`,
30968
+ code: SchemaErrorCodes.VALUE_TYPE_MISMATCH
30969
+ });
30970
+ }
30971
+ }
30972
+ if (value.type === "selector" && value.selectorKind && roleSpec.selectorKinds && roleSpec.selectorKinds.length > 0) {
30973
+ if (!roleSpec.selectorKinds.includes(value.selectorKind)) {
30974
+ diagnostics.push({
30975
+ level: "error",
30976
+ role: value.role,
30977
+ message: `${schema.action}.${value.role} expects selector kind [${roleSpec.selectorKinds.join(", ")}], got '${value.selectorKind}'`,
30978
+ code: SchemaErrorCodes.SELECTOR_KIND_MISMATCH
30979
+ });
30980
+ }
30981
+ }
30982
+ }
30983
+ return diagnostics;
30984
+ }
30451
30985
  function validateKeywordCollisions(profile) {
30452
30986
  const collisions = [];
30453
30987
  if (!profile.keywords) {
@@ -30624,6 +31158,7 @@ var init_command_schemas = __esm({
30624
31158
  description: "The class or attribute to toggle",
30625
31159
  required: true,
30626
31160
  expectedTypes: ["selector"],
31161
+ selectorKinds: ["class", "attribute"],
30627
31162
  svoPosition: 1,
30628
31163
  sovPosition: 2
30629
31164
  },
@@ -43633,7 +44168,7 @@ function createLiteral(value, dataType) {
43633
44168
  return result;
43634
44169
  }
43635
44170
  function isValidReference(value) {
43636
- return VALID_REFERENCES.has(value);
44171
+ return isValidReference2(value);
43637
44172
  }
43638
44173
  function createReference(value) {
43639
44174
  return { type: "reference", value };
@@ -43725,18 +44260,8 @@ function createLoopNode(action, loopVariant, roles, body, options) {
43725
44260
  }
43726
44261
  return node;
43727
44262
  }
43728
- var VALID_REFERENCES;
43729
44263
  var init_types2 = __esm({
43730
44264
  "src/types.ts"() {
43731
- VALID_REFERENCES = /* @__PURE__ */ new Set([
43732
- "me",
43733
- "you",
43734
- "it",
43735
- "result",
43736
- "event",
43737
- "target",
43738
- "body"
43739
- ]);
43740
44265
  }
43741
44266
  });
43742
44267
  function normalizeEventName(event, language) {
@@ -44957,25 +45482,10 @@ var init_renderer = __esm({
44957
45482
  }
44958
45483
  /**
44959
45484
  * Render a semantic node in explicit mode.
45485
+ * Delegates to @lokascript/framework/ir for the core logic.
44960
45486
  */
44961
45487
  renderExplicit(node) {
44962
- if (node.kind === "compound") {
44963
- const compoundNode = node;
44964
- const renderedStatements = compoundNode.statements.map((stmt) => this.renderExplicit(stmt));
44965
- return renderedStatements.join(` ${compoundNode.chainType} `);
44966
- }
44967
- const parts = [node.action];
44968
- for (const [role, value] of node.roles) {
44969
- parts.push(`${role}:${this.valueToString(value)}`);
44970
- }
44971
- if (node.kind === "event-handler") {
44972
- const eventNode = node;
44973
- if (eventNode.body && eventNode.body.length > 0) {
44974
- const bodyParts = eventNode.body.map((n) => this.renderExplicit(n));
44975
- parts.push(`body:${bodyParts.join(" ")}`);
44976
- }
44977
- }
44978
- return `[${parts.join(" ")}]`;
45488
+ return renderExplicit$1(node);
44979
45489
  }
44980
45490
  /**
44981
45491
  * Get all supported languages.
@@ -45068,41 +45578,21 @@ var init_renderer = __esm({
45068
45578
  }
45069
45579
  }
45070
45580
  const groupParts = [];
45581
+ let hasRoleValue = false;
45071
45582
  for (const subToken of token.tokens) {
45072
45583
  const rendered = this.renderPatternToken(subToken, node, language);
45073
45584
  if (rendered !== null) {
45074
45585
  groupParts.push(rendered);
45586
+ if (subToken.type === "role") hasRoleValue = true;
45075
45587
  }
45076
45588
  }
45589
+ if (token.optional && !hasRoleValue) return null;
45077
45590
  return groupParts.length > 0 ? groupParts.join(" ") : null;
45078
45591
  }
45079
45592
  default:
45080
45593
  return null;
45081
45594
  }
45082
45595
  }
45083
- /**
45084
- * Convert a semantic value to a string for explicit syntax.
45085
- */
45086
- valueToString(value) {
45087
- switch (value.type) {
45088
- case "literal":
45089
- if (typeof value.value === "string") {
45090
- if (value.dataType === "string" || /\s/.test(value.value)) {
45091
- return `"${value.value}"`;
45092
- }
45093
- return value.value;
45094
- }
45095
- return String(value.value);
45096
- case "selector":
45097
- return value.value;
45098
- case "reference":
45099
- return value.value;
45100
- case "property-path":
45101
- return `${this.valueToString(value.object)}'s ${value.property}`;
45102
- case "expression":
45103
- return value.raw;
45104
- }
45105
- }
45106
45596
  /**
45107
45597
  * Convert a semantic value to natural language string.
45108
45598
  * Uses language-specific possessive rendering when language is provided.
@@ -45122,6 +45612,8 @@ var init_renderer = __esm({
45122
45612
  return this.renderPropertyPath(value, language);
45123
45613
  case "expression":
45124
45614
  return value.raw;
45615
+ case "flag":
45616
+ return value.name;
45125
45617
  }
45126
45618
  }
45127
45619
  /**
@@ -45193,167 +45685,15 @@ var init_renderer = __esm({
45193
45685
  }
45194
45686
  });
45195
45687
  function parseExplicit(input) {
45196
- const trimmed = input.trim();
45197
- if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) {
45198
- throw new Error("Explicit syntax must be wrapped in brackets: [command role:value ...]");
45199
- }
45200
- const content = trimmed.slice(1, -1).trim();
45201
- if (!content) {
45202
- throw new Error("Empty explicit statement");
45203
- }
45204
- const tokens = tokenizeExplicit(content);
45205
- if (tokens.length === 0) {
45206
- throw new Error("No command specified in explicit statement");
45207
- }
45208
- const command2 = tokens[0].toLowerCase();
45209
- const roles = /* @__PURE__ */ new Map();
45210
- const schema = getSchema(command2);
45211
- const validRoleNames = schema ? new Set(schema.roles.map((r) => r.role)) : null;
45212
- for (let i = 1; i < tokens.length; i++) {
45213
- const token = tokens[i];
45214
- const colonIndex = token.indexOf(":");
45215
- if (colonIndex === -1) {
45216
- throw new Error(`Invalid role format: "${token}". Expected role:value`);
45217
- }
45218
- const roleName = token.slice(0, colonIndex);
45219
- if (validRoleNames && roleName !== "body" && !validRoleNames.has(roleName)) {
45220
- const roleList = [...validRoleNames].join(", ");
45221
- throw new Error(
45222
- `Unknown role "${roleName}" for command "${command2}". Valid roles: ${roleList}`
45223
- );
45224
- }
45225
- const role = roleName;
45226
- const valueStr = token.slice(colonIndex + 1);
45227
- if (role === "body" && valueStr.startsWith("[")) {
45228
- const nestedEnd = findMatchingBracket(token, colonIndex + 1);
45229
- const nestedSyntax = token.slice(colonIndex + 1, nestedEnd + 1);
45230
- roles.set(role, { type: "expression", raw: nestedSyntax });
45231
- continue;
45232
- }
45233
- const value = parseExplicitValue(valueStr);
45234
- roles.set(role, value);
45235
- }
45236
- if (schema && command2 !== "on") {
45237
- for (const roleSpec of schema.roles) {
45238
- if (roleSpec.required && !roles.has(roleSpec.role) && !roleSpec.default) {
45239
- throw new Error(
45240
- `Missing required role "${roleSpec.role}" for command "${command2}": ${roleSpec.description}`
45241
- );
45242
- }
45243
- }
45244
- }
45245
- if (command2 === "on") {
45246
- const eventValue = roles.get("event");
45247
- if (!eventValue) {
45248
- throw new Error("Event handler requires event role: [on event:click ...]");
45249
- }
45250
- const bodyValue = roles.get("body");
45251
- const body = [];
45252
- if (bodyValue && bodyValue.type === "expression") {
45253
- body.push(parseExplicit(bodyValue.raw));
45254
- }
45255
- roles.delete("body");
45256
- return createEventHandler(eventValue, body, void 0, {
45257
- sourceLanguage: "explicit"
45258
- });
45259
- }
45260
- const rolesObj = {};
45261
- for (const [role, value] of roles) {
45262
- rolesObj[role] = value;
45263
- }
45264
- return createCommandNode(command2, rolesObj, {
45265
- sourceLanguage: "explicit"
45688
+ return parseExplicit$1(input, {
45689
+ schemaLookup: hyperscriptSchemaLookup
45266
45690
  });
45267
45691
  }
45268
- function tokenizeExplicit(content) {
45269
- const tokens = [];
45270
- let current = "";
45271
- let inString = false;
45272
- let stringChar = "";
45273
- let bracketDepth = 0;
45274
- for (let i = 0; i < content.length; i++) {
45275
- const char = content[i];
45276
- if (inString) {
45277
- current += char;
45278
- if (char === stringChar && content[i - 1] !== "\\") {
45279
- inString = false;
45280
- }
45281
- continue;
45282
- }
45283
- if (char === '"' || char === "'") {
45284
- inString = true;
45285
- stringChar = char;
45286
- current += char;
45287
- continue;
45288
- }
45289
- if (char === "[") {
45290
- bracketDepth++;
45291
- current += char;
45292
- continue;
45293
- }
45294
- if (char === "]") {
45295
- bracketDepth--;
45296
- current += char;
45297
- continue;
45298
- }
45299
- if (char === " " && bracketDepth === 0) {
45300
- if (current) {
45301
- tokens.push(current);
45302
- current = "";
45303
- }
45304
- continue;
45305
- }
45306
- current += char;
45307
- }
45308
- if (current) {
45309
- tokens.push(current);
45310
- }
45311
- return tokens;
45312
- }
45313
- function parseExplicitValue(valueStr) {
45314
- if (valueStr.startsWith("#") || valueStr.startsWith(".") || valueStr.startsWith("[") || valueStr.startsWith("@") || valueStr.startsWith("*")) {
45315
- return createSelector(valueStr);
45316
- }
45317
- if (valueStr.startsWith('"') || valueStr.startsWith("'")) {
45318
- const inner = valueStr.slice(1, -1);
45319
- return createLiteral(inner, "string");
45320
- }
45321
- if (valueStr === "true") return createLiteral(true, "boolean");
45322
- if (valueStr === "false") return createLiteral(false, "boolean");
45323
- const lowerRef = valueStr.toLowerCase();
45324
- if (isValidReference(lowerRef)) {
45325
- return createReference(lowerRef);
45326
- }
45327
- const numMatch = valueStr.match(/^(-?\d+(?:\.\d+)?)(ms|s|m|h)?$/);
45328
- if (numMatch) {
45329
- const num = parseFloat(numMatch[1]);
45330
- const suffix = numMatch[2];
45331
- if (suffix) {
45332
- return createLiteral(valueStr, "duration");
45333
- }
45334
- return createLiteral(num, "number");
45335
- }
45336
- return createLiteral(valueStr, "string");
45337
- }
45338
- function findMatchingBracket(str, start) {
45339
- let depth = 0;
45340
- for (let i = start; i < str.length; i++) {
45341
- if (str[i] === "[") depth++;
45342
- else if (str[i] === "]") {
45343
- depth--;
45344
- if (depth === 0) return i;
45345
- }
45346
- }
45347
- return str.length - 1;
45348
- }
45349
- function isExplicitSyntax(input) {
45350
- const trimmed = input.trim();
45351
- return trimmed.startsWith("[") && trimmed.endsWith("]");
45352
- }
45692
+ var hyperscriptSchemaLookup;
45353
45693
  var init_parser = __esm({
45354
45694
  "src/explicit/parser.ts"() {
45355
- init_types2();
45356
45695
  init_command_schemas();
45696
+ hyperscriptSchemaLookup = { getSchema };
45357
45697
  }
45358
45698
  });
45359
45699
  var semantic_parser_exports = {};
@@ -54430,6 +54770,8 @@ function semanticValuesEqual(a, b) {
54430
54770
  return semanticValuesEqual(a.object, b.object) && a.property === b.property;
54431
54771
  case "expression":
54432
54772
  return a.raw === b.raw;
54773
+ case "flag":
54774
+ return a.name === b.name && a.enabled === b.enabled;
54433
54775
  default:
54434
54776
  return false;
54435
54777
  }
@@ -56144,6 +56486,8 @@ function convertValue(value, warnings) {
56144
56486
  return convertPropertyPath(value, warnings);
56145
56487
  case "expression":
56146
56488
  return convertExpression(value);
56489
+ case "flag":
56490
+ return { type: "literal", value: value.enabled };
56147
56491
  default:
56148
56492
  const _exhaustive = value;
56149
56493
  throw new Error(`Unknown semantic value type: ${_exhaustive.type}`);
@@ -58440,6 +58784,316 @@ function process$1(element) {
58440
58784
  console.error("Error processing hyperscript node:", error);
58441
58785
  }
58442
58786
  }
58787
+ var RingBuffer = class {
58788
+ constructor(capacity) {
58789
+ this.capacity = capacity;
58790
+ this.head = 0;
58791
+ this.count = 0;
58792
+ this.items = new Array(capacity);
58793
+ }
58794
+ push(item) {
58795
+ this.items[this.head] = item;
58796
+ this.head = (this.head + 1) % this.capacity;
58797
+ if (this.count < this.capacity)
58798
+ this.count++;
58799
+ }
58800
+ toArray() {
58801
+ if (this.count < this.capacity) {
58802
+ return this.items.slice(0, this.count);
58803
+ }
58804
+ return [...this.items.slice(this.head), ...this.items.slice(0, this.head)];
58805
+ }
58806
+ get length() {
58807
+ return this.count;
58808
+ }
58809
+ clear() {
58810
+ this.head = 0;
58811
+ this.count = 0;
58812
+ }
58813
+ last() {
58814
+ if (this.count === 0)
58815
+ return void 0;
58816
+ const idx = (this.head - 1 + this.capacity) % this.capacity;
58817
+ return this.items[idx];
58818
+ }
58819
+ };
58820
+ var HISTORY_CAPACITY = 200;
58821
+ var DebugController = class {
58822
+ constructor() {
58823
+ this.state = {
58824
+ enabled: false,
58825
+ paused: false,
58826
+ stepMode: "continue",
58827
+ callDepth: 0,
58828
+ pauseDepth: 0,
58829
+ currentSnapshot: null
58830
+ };
58831
+ this.history = new RingBuffer(HISTORY_CAPACITY);
58832
+ this.breakpoints = /* @__PURE__ */ new Map();
58833
+ this.snapshotIndex = 0;
58834
+ this._resumeResolver = null;
58835
+ this.listeners = /* @__PURE__ */ new Map();
58836
+ this.hooks = {
58837
+ beforeExecute: this.beforeExecute.bind(this),
58838
+ afterExecute: this.afterExecute.bind(this)
58839
+ };
58840
+ }
58841
+ on(event, listener) {
58842
+ let set = this.listeners.get(event);
58843
+ if (!set) {
58844
+ set = /* @__PURE__ */ new Set();
58845
+ this.listeners.set(event, set);
58846
+ }
58847
+ set.add(listener);
58848
+ return () => set.delete(listener);
58849
+ }
58850
+ emit(event, data) {
58851
+ const set = this.listeners.get(event);
58852
+ if (set) {
58853
+ for (const listener of set) {
58854
+ try {
58855
+ listener(data);
58856
+ } catch {
58857
+ }
58858
+ }
58859
+ }
58860
+ }
58861
+ enable() {
58862
+ this.state.enabled = true;
58863
+ this.emit("enabled");
58864
+ }
58865
+ disable() {
58866
+ if (this.state.paused && this._resumeResolver) {
58867
+ this._resumeResolver();
58868
+ this._resumeResolver = null;
58869
+ }
58870
+ this.state.enabled = false;
58871
+ this.state.paused = false;
58872
+ this.state.stepMode = "continue";
58873
+ this.state.callDepth = 0;
58874
+ this.emit("disabled");
58875
+ }
58876
+ get enabled() {
58877
+ return this.state.enabled;
58878
+ }
58879
+ continue() {
58880
+ this.state.stepMode = "continue";
58881
+ this.resume();
58882
+ }
58883
+ pause() {
58884
+ this.state.stepMode = "pause";
58885
+ }
58886
+ stepOver() {
58887
+ this.state.stepMode = "over";
58888
+ this.state.pauseDepth = this.state.callDepth;
58889
+ this.resume();
58890
+ }
58891
+ stepInto() {
58892
+ this.state.stepMode = "into";
58893
+ this.resume();
58894
+ }
58895
+ stepOut() {
58896
+ this.state.stepMode = "out";
58897
+ this.state.pauseDepth = this.state.callDepth;
58898
+ this.resume();
58899
+ }
58900
+ resume() {
58901
+ if (this._resumeResolver) {
58902
+ this.state.paused = false;
58903
+ this.emit("resumed");
58904
+ this._resumeResolver();
58905
+ this._resumeResolver = null;
58906
+ }
58907
+ }
58908
+ setBreakpoint(condition) {
58909
+ const id = `bp_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
58910
+ this.breakpoints.set(id, { ...condition, id, hitCount: 0 });
58911
+ return id;
58912
+ }
58913
+ removeBreakpoint(id) {
58914
+ return this.breakpoints.delete(id);
58915
+ }
58916
+ getBreakpoints() {
58917
+ return Array.from(this.breakpoints.values());
58918
+ }
58919
+ clearBreakpoints() {
58920
+ this.breakpoints.clear();
58921
+ }
58922
+ getState() {
58923
+ return { ...this.state };
58924
+ }
58925
+ getHistory() {
58926
+ return this.history.toArray();
58927
+ }
58928
+ clearHistory() {
58929
+ this.history.clear();
58930
+ this.snapshotIndex = 0;
58931
+ }
58932
+ async beforeExecute(ctx) {
58933
+ if (!this.state.enabled)
58934
+ return;
58935
+ this.state.callDepth++;
58936
+ const snapshot = this.captureSnapshot(ctx);
58937
+ this.history.push(snapshot);
58938
+ this.emit("snapshot", snapshot);
58939
+ if (this.shouldPause(ctx)) {
58940
+ this.state.paused = true;
58941
+ this.state.currentSnapshot = snapshot;
58942
+ this.emit("paused", snapshot);
58943
+ await new Promise((resolve) => {
58944
+ this._resumeResolver = resolve;
58945
+ });
58946
+ this.state.currentSnapshot = null;
58947
+ }
58948
+ }
58949
+ async afterExecute(ctx, result) {
58950
+ if (!this.state.enabled)
58951
+ return;
58952
+ const lastSnapshot = this.history.last();
58953
+ if (lastSnapshot) {
58954
+ lastSnapshot.variables["__result"] = serializeValue(result);
58955
+ }
58956
+ this.state.callDepth = Math.max(0, this.state.callDepth - 1);
58957
+ }
58958
+ shouldPause(ctx) {
58959
+ if (this.hitBreakpoint(ctx))
58960
+ return true;
58961
+ switch (this.state.stepMode) {
58962
+ case "pause":
58963
+ this.state.stepMode = "into";
58964
+ return true;
58965
+ case "into":
58966
+ return true;
58967
+ case "over":
58968
+ return this.state.callDepth <= this.state.pauseDepth;
58969
+ case "out":
58970
+ return this.state.callDepth < this.state.pauseDepth;
58971
+ case "continue":
58972
+ return false;
58973
+ }
58974
+ }
58975
+ hitBreakpoint(ctx) {
58976
+ for (const bp of this.breakpoints.values()) {
58977
+ if (!bp.enabled)
58978
+ continue;
58979
+ let hit = false;
58980
+ switch (bp.type) {
58981
+ case "command":
58982
+ hit = ctx.commandName === bp.value;
58983
+ break;
58984
+ case "element":
58985
+ if (ctx.element) {
58986
+ try {
58987
+ hit = ctx.element.matches(bp.value);
58988
+ } catch {
58989
+ }
58990
+ }
58991
+ break;
58992
+ case "expression":
58993
+ hit = this.evaluateCondition(bp.value, ctx);
58994
+ break;
58995
+ }
58996
+ if (hit) {
58997
+ bp.hitCount++;
58998
+ return true;
58999
+ }
59000
+ }
59001
+ return false;
59002
+ }
59003
+ evaluateCondition(expression, ctx) {
59004
+ try {
59005
+ const ec = ctx.executionContext;
59006
+ const vars = {
59007
+ commandName: ctx.commandName,
59008
+ it: ec.it,
59009
+ result: ec.result,
59010
+ me: ec.me
59011
+ };
59012
+ if (ec.locals) {
59013
+ for (const [k, v2] of ec.locals) {
59014
+ vars[k] = v2;
59015
+ }
59016
+ }
59017
+ const keys = Object.keys(vars);
59018
+ const values = keys.map((k) => vars[k]);
59019
+ const fn = new Function(...keys, `return (${expression});`);
59020
+ return !!fn(...values);
59021
+ } catch {
59022
+ return false;
59023
+ }
59024
+ }
59025
+ captureSnapshot(ctx) {
59026
+ const ec = ctx.executionContext;
59027
+ const variables = {};
59028
+ variables["it"] = serializeValue(ec.it);
59029
+ variables["result"] = serializeValue(ec.result);
59030
+ if (ec.me)
59031
+ variables["me"] = describeElement(ec.me);
59032
+ if (ec.you)
59033
+ variables["you"] = describeElement(ec.you);
59034
+ if (ctx.event) {
59035
+ variables["event.type"] = ctx.event.type;
59036
+ if (ctx.event.target) {
59037
+ variables["event.target"] = describeElement(ctx.event.target);
59038
+ }
59039
+ }
59040
+ if (ec.locals) {
59041
+ for (const [key, value] of ec.locals) {
59042
+ if (!key.startsWith("__")) {
59043
+ variables[`:${key}`] = serializeValue(value);
59044
+ }
59045
+ }
59046
+ }
59047
+ return {
59048
+ commandName: ctx.commandName,
59049
+ element: ctx.element,
59050
+ variables,
59051
+ timestamp: Date.now(),
59052
+ depth: this.state.callDepth,
59053
+ index: this.snapshotIndex++
59054
+ };
59055
+ }
59056
+ };
59057
+ function serializeValue(value) {
59058
+ if (value === null || value === void 0)
59059
+ return value;
59060
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
59061
+ return value;
59062
+ }
59063
+ if (value instanceof Element) {
59064
+ return describeElement(value);
59065
+ }
59066
+ if (Array.isArray(value)) {
59067
+ return `Array(${value.length})`;
59068
+ }
59069
+ if (value instanceof Map) {
59070
+ return `Map(${value.size})`;
59071
+ }
59072
+ if (value instanceof Set) {
59073
+ return `Set(${value.size})`;
59074
+ }
59075
+ if (typeof value === "function") {
59076
+ return `\u0192 ${value.name || "anonymous"}()`;
59077
+ }
59078
+ if (typeof value === "object") {
59079
+ const keys = Object.keys(value);
59080
+ if (keys.length <= 3) {
59081
+ return `{${keys.join(", ")}}`;
59082
+ }
59083
+ return `{${keys.slice(0, 3).join(", ")}, ...${keys.length - 3} more}`;
59084
+ }
59085
+ return String(value);
59086
+ }
59087
+ function describeElement(el) {
59088
+ let desc = `<${el.tagName.toLowerCase()}`;
59089
+ if (el.id)
59090
+ desc += `#${el.id}`;
59091
+ if (el.classList.length > 0) {
59092
+ desc += `.${Array.from(el.classList).join(".")}`;
59093
+ }
59094
+ desc += ">";
59095
+ return desc;
59096
+ }
58443
59097
  var DEFAULT_LANGUAGE = "en";
58444
59098
  var ASTCache = class {
58445
59099
  constructor(maxSize = 500) {
@@ -58527,6 +59181,14 @@ function getDefaultParserOptions() {
58527
59181
  };
58528
59182
  }
58529
59183
  var _defaultRuntime = null;
59184
+ var _debugController = null;
59185
+ function getDebugController() {
59186
+ if (!_debugController) {
59187
+ _debugController = new DebugController();
59188
+ getDefaultRuntime().registerHooks("__debugger", _debugController.hooks);
59189
+ }
59190
+ return _debugController;
59191
+ }
58530
59192
  function getDefaultRuntime() {
58531
59193
  if (!_defaultRuntime) {
58532
59194
  _defaultRuntime = new Runtime({
@@ -58737,7 +59399,10 @@ var hyperscript = {
58737
59399
  return getDefaultRuntime().getRegisteredHooks();
58738
59400
  },
58739
59401
  clearCache: () => astCache.clear(),
58740
- getCacheStats: () => astCache.getStats()
59402
+ getCacheStats: () => astCache.getStats(),
59403
+ get debug() {
59404
+ return getDebugController();
59405
+ }
58741
59406
  };
58742
59407
  var lokascript = hyperscript;
58743
59408
  var OP_TABLE = {
@@ -65770,6 +66435,9 @@ var CompletionItemKind;
65770
66435
  CompletionItemKind2[CompletionItemKind2["Operator"] = 24] = "Operator";
65771
66436
  CompletionItemKind2[CompletionItemKind2["TypeParameter"] = 25] = "TypeParameter";
65772
66437
  })(CompletionItemKind || (CompletionItemKind = {}));
66438
+ function createDebugController() {
66439
+ return new DebugController();
66440
+ }
65773
66441
  var _semanticModule = null;
65774
66442
  async function getSemanticModule() {
65775
66443
  if (!_semanticModule) {
@@ -65922,6 +66590,7 @@ var bridge = /* @__PURE__ */ Object.freeze({
65922
66590
  SemanticGrammarBridge
65923
66591
  });
65924
66592
  export {
66593
+ DebugController,
65925
66594
  Lexer,
65926
66595
  Runtime,
65927
66596
  RuntimeBase,
@@ -65937,6 +66606,7 @@ export {
65937
66606
  createBehaviorsFeature,
65938
66607
  createChildContext,
65939
66608
  createContext,
66609
+ createDebugController,
65940
66610
  createDef,
65941
66611
  createDefFeature,
65942
66612
  createEventSource,
@@ -65966,4 +66636,4 @@ export {
65966
66636
  toCoreAST,
65967
66637
  validatePartialContent
65968
66638
  };
65969
- //# sourceMappingURL=dist-JOIDNRL3.js.map
66639
+ //# sourceMappingURL=dist-KBC4YRQJ.js.map