@hyperfixi/core 2.6.0 → 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/api/hyperscript-api.d.ts +6 -0
  2. package/dist/ast-utils/index.js +8 -8
  3. package/dist/ast-utils/index.mjs +8 -8
  4. package/dist/bundle-generator/index.js +12 -4
  5. package/dist/bundle-generator/index.mjs +12 -4
  6. package/dist/chunks/{bridge-fFLEvCQs.js → bridge-lZbOVRDD.js} +2 -2
  7. package/dist/chunks/browser-modular-DegkWQ8d.js +2 -0
  8. package/dist/chunks/{index-BRgVy8nY.js → index-i_j9Z-1e.js} +2 -2
  9. package/dist/commands/data/set.d.ts +1 -0
  10. package/dist/commands/index.js +156 -47
  11. package/dist/commands/index.mjs +156 -47
  12. package/dist/core/context.d.ts +4 -0
  13. package/dist/dom/attribute-processor.d.ts +1 -0
  14. package/dist/hyperfixi-browser-classic-i18n.js +1 -1
  15. package/dist/hyperfixi-browser-minimal.js +1 -1
  16. package/dist/hyperfixi-browser-standard.js +1 -1
  17. package/dist/hyperfixi-browser.js +1 -1
  18. package/dist/hyperfixi-classic-i18n.js +1 -1
  19. package/dist/hyperfixi-hx-v4.js +1 -1
  20. package/dist/hyperfixi-hx.js +1 -1
  21. package/dist/hyperfixi-hybrid-complete.js +1 -1
  22. package/dist/hyperfixi-hybrid-hx.js +1 -1
  23. package/dist/hyperfixi-lite-plus.js +1 -1
  24. package/dist/hyperfixi-lite.js +1 -1
  25. package/dist/hyperfixi-minimal.js +1 -1
  26. package/dist/hyperfixi-multilingual.js +1 -1
  27. package/dist/hyperfixi-standard.js +1 -1
  28. package/dist/hyperfixi.js +1 -1
  29. package/dist/hyperfixi.mjs +1 -1
  30. package/dist/index.d.ts +1 -1
  31. package/dist/index.js +694 -459
  32. package/dist/index.min.js +1 -1
  33. package/dist/index.mjs +691 -460
  34. package/dist/lokascript-browser-classic-i18n.js +1 -1
  35. package/dist/lokascript-browser-minimal.js +1 -1
  36. package/dist/lokascript-browser-standard.js +1 -1
  37. package/dist/lokascript-browser.js +1 -1
  38. package/dist/lokascript-hybrid-complete.js +1 -1
  39. package/dist/lokascript-hybrid-hx.js +1 -1
  40. package/dist/lokascript-lite-plus.js +1 -1
  41. package/dist/lokascript-lite.js +1 -1
  42. package/dist/lokascript-multilingual.js +1 -1
  43. package/dist/parser/full-parser.js +70 -35
  44. package/dist/parser/full-parser.mjs +70 -35
  45. package/dist/parser/parser-types.d.ts +1 -1
  46. package/dist/parser/parser.d.ts +2 -0
  47. package/dist/parser/regex-parser.js +23 -14
  48. package/dist/parser/regex-parser.mjs +23 -14
  49. package/dist/registry/index.js +178 -45
  50. package/dist/registry/index.mjs +178 -45
  51. package/dist/types/core-context.d.ts +1 -0
  52. package/package.json +6 -6
  53. package/dist/chunks/browser-modular-BK3IOo93.js +0 -2
  54. package/dist/compatibility/browser-bundle.d.ts +0 -164
@@ -1222,6 +1222,7 @@ const FEATURE_REGISTRY = new Map();
1222
1222
  const NODE_EVALUATORS = new Map();
1223
1223
  const GLOBAL_WRITE_HOOKS = new Set();
1224
1224
  const GLOBAL_READ_HOOKS = new Set();
1225
+ const LOCAL_WRITE_HOOKS = new Set();
1225
1226
  const LOCAL_READ_HOOKS = new Set();
1226
1227
  function getRegisteredFeature(name) {
1227
1228
  return FEATURE_REGISTRY.get(name.toLowerCase());
@@ -1243,6 +1244,20 @@ function notifyGlobalRead(name, context) {
1243
1244
  }
1244
1245
  }
1245
1246
  }
1247
+ function notifyLocalWrite(name, value, context) {
1248
+ if (LOCAL_WRITE_HOOKS.size === 0)
1249
+ return;
1250
+ for (const hook of LOCAL_WRITE_HOOKS) {
1251
+ try {
1252
+ hook(name, value, context);
1253
+ }
1254
+ catch (err) {
1255
+ if (typeof console !== 'undefined') {
1256
+ console.error('[hyperfixi] localWriteHook threw:', err);
1257
+ }
1258
+ }
1259
+ }
1260
+ }
1246
1261
  function notifyLocalRead(name, context) {
1247
1262
  if (LOCAL_READ_HOOKS.size === 0)
1248
1263
  return;
@@ -1273,6 +1288,68 @@ function setGlobal(context, name, value) {
1273
1288
  }
1274
1289
  }
1275
1290
 
1291
+ const elementScopes = new WeakMap();
1292
+ function getElementScopeMap(element) {
1293
+ let map = elementScopes.get(element);
1294
+ if (!map) {
1295
+ map = new Map();
1296
+ elementScopes.set(element, map);
1297
+ }
1298
+ return map;
1299
+ }
1300
+ function resolveScopeOwner(context) {
1301
+ const owner = context.owner;
1302
+ if (owner)
1303
+ return owner;
1304
+ return context.me instanceof Element ? context.me : null;
1305
+ }
1306
+ function getElementVar(context, name) {
1307
+ const owner = resolveScopeOwner(context);
1308
+ if (!owner)
1309
+ return undefined;
1310
+ notifyLocalRead(name, context);
1311
+ return getElementScopeMap(owner).get(name);
1312
+ }
1313
+ function setElementVar(context, name, value) {
1314
+ const owner = resolveScopeOwner(context);
1315
+ if (!owner)
1316
+ return;
1317
+ getElementScopeMap(owner).set(name, value);
1318
+ notifyLocalWrite(name, value, context);
1319
+ }
1320
+
1321
+ function convertToNumber(value) {
1322
+ if (value === null || value === undefined) {
1323
+ return 0;
1324
+ }
1325
+ if (typeof value === 'number') {
1326
+ return isFinite(value) ? value : 0;
1327
+ }
1328
+ if (typeof value === 'string') {
1329
+ const parsed = parseFloat(value);
1330
+ return parsed;
1331
+ }
1332
+ if (typeof value === 'boolean') {
1333
+ return value ? 1 : 0;
1334
+ }
1335
+ if (Array.isArray(value)) {
1336
+ return value.length;
1337
+ }
1338
+ if (typeof value === 'object') {
1339
+ if ('length' in value && typeof value.length === 'number') {
1340
+ return value.length;
1341
+ }
1342
+ if (typeof value.valueOf === 'function') {
1343
+ const result = value.valueOf();
1344
+ if (typeof result === 'number') {
1345
+ return result;
1346
+ }
1347
+ }
1348
+ return NaN;
1349
+ }
1350
+ return 0;
1351
+ }
1352
+
1276
1353
  class ExpressionTypeRegistry {
1277
1354
  constructor(config = {}) {
1278
1355
  this.types = new Map();
@@ -6976,7 +7053,7 @@ function parseColonVariable(ctx) {
6976
7053
  return {
6977
7054
  type: 'identifier',
6978
7055
  name: varToken.value,
6979
- scope: 'local',
7056
+ scope: 'element',
6980
7057
  start: varToken.start - 1,
6981
7058
  end: varToken.end,
6982
7059
  };
@@ -7028,7 +7105,7 @@ function parseTargetFallback(ctx) {
7028
7105
  expression: {
7029
7106
  type: 'identifier',
7030
7107
  name: varToken.value,
7031
- scope: 'local',
7108
+ scope: 'element',
7032
7109
  start: varToken.start,
7033
7110
  end: varToken.end,
7034
7111
  },
@@ -7109,6 +7186,11 @@ function parseSetCommand(ctx, identifierNode) {
7109
7186
  }
7110
7187
  ctx.advance();
7111
7188
  const value = ctx.parseExpression() ?? ctx.parsePrimary();
7189
+ let scopeTarget;
7190
+ if (ctx.check(KEYWORDS.ON) && tokenLooksLikeElementTarget(ctx.peekAt(1))) {
7191
+ ctx.advance();
7192
+ scopeTarget = parseOneArgument(ctx);
7193
+ }
7112
7194
  const finalArgs = [];
7113
7195
  if (targetExpression) {
7114
7196
  finalArgs.push(targetExpression);
@@ -7120,11 +7202,22 @@ function parseSetCommand(ctx, identifierNode) {
7120
7202
  if (value) {
7121
7203
  finalArgs.push(value);
7122
7204
  }
7123
- return CommandNodeBuilder.fromIdentifier(identifierNode)
7124
- .withArgs(...finalArgs)
7125
- .endingAt(ctx.getPosition())
7126
- .build();
7205
+ const builder = CommandNodeBuilder.fromIdentifier(identifierNode).withArgs(...finalArgs);
7206
+ if (scopeTarget) {
7207
+ builder.withModifier('on', scopeTarget);
7208
+ }
7209
+ return builder.endingAt(ctx.getPosition()).build();
7210
+ }
7211
+ function tokenLooksLikeElementTarget(token) {
7212
+ if (!token)
7213
+ return false;
7214
+ if (token.kind === 'selector')
7215
+ return true;
7216
+ if (token.value.startsWith('<'))
7217
+ return true;
7218
+ return ELEMENT_PRONOUNS.has(token.value);
7127
7219
  }
7220
+ const ELEMENT_PRONOUNS = new Set(['me', 'my', 'it', 'its', 'you', 'your', 'yourself']);
7128
7221
  function parseIncrementDecrementCommand(ctx, commandToken) {
7129
7222
  const commandName = commandToken.value;
7130
7223
  const isIncrement = commandName === 'increment';
@@ -7170,6 +7263,7 @@ function parseIncrementDecrementCommand(ctx, commandToken) {
7170
7263
  column: commandToken.column,
7171
7264
  };
7172
7265
  const binaryExpr = createBinaryExpression(operator, target, amount, pos);
7266
+ binaryExpr.coerceNumeric = true;
7173
7267
  const toIdentifier = createIdentifier(KEYWORDS.TO, pos);
7174
7268
  const args = [targetWithScope, toIdentifier, binaryExpr];
7175
7269
  return CommandNodeBuilder.from(commandToken)
@@ -7788,6 +7882,18 @@ class Parser {
7788
7882
  this.advance();
7789
7883
  continue;
7790
7884
  }
7885
+ if (this.check('then')) {
7886
+ const next = this.peekAt(1);
7887
+ const startsFeature = next !== null &&
7888
+ (next.value === 'init' ||
7889
+ next.value === 'on' ||
7890
+ next.value === 'def' ||
7891
+ getRegisteredFeature(next.value) !== undefined);
7892
+ if (startsFeature) {
7893
+ this.advance();
7894
+ continue;
7895
+ }
7896
+ }
7791
7897
  if (this.check('init')) {
7792
7898
  this.advance();
7793
7899
  const initBlock = this.parseTopLevelInitBlock();
@@ -8588,7 +8694,7 @@ class Parser {
8588
8694
  return {
8589
8695
  type: 'identifier',
8590
8696
  name: varToken.value,
8591
- scope: 'local',
8697
+ scope: 'element',
8592
8698
  start: varToken.start - 1,
8593
8699
  end: varToken.end,
8594
8700
  line: varToken.line,
@@ -9215,6 +9321,33 @@ class Parser {
9215
9321
  return null;
9216
9322
  }
9217
9323
  }
9324
+ parseTemporalKeywordModifiers(modifiers) {
9325
+ if (!this.check('debounced') && !this.check('throttled')) {
9326
+ return;
9327
+ }
9328
+ const modToken = this.advance();
9329
+ const modName = modToken.value.toLowerCase();
9330
+ const modifierKey = modName === 'debounced' ? 'debounce' : 'throttle';
9331
+ if (this.match('at')) {
9332
+ if (this.checkTimeExpression()) {
9333
+ const timeToken = this.advance();
9334
+ const delayMs = parseTimeToMs(timeToken.value);
9335
+ modifiers[modifierKey] = delayMs;
9336
+ debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
9337
+ }
9338
+ else if (this.matchNumber()) {
9339
+ const delayMs = parseInt(this.previous().value, 10);
9340
+ modifiers[modifierKey] = delayMs;
9341
+ debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
9342
+ }
9343
+ else {
9344
+ this.addError(`Expected time expression after '${modName} at', got: ${this.peek().value}`);
9345
+ }
9346
+ }
9347
+ else {
9348
+ this.addError(`Expected 'at' after '${modName}'`);
9349
+ }
9350
+ }
9218
9351
  parseEventHandler() {
9219
9352
  debug.parse(`🔧 parseEventHandler: ENTRY - parsing event handler`);
9220
9353
  const eventNames = [];
@@ -9302,30 +9435,7 @@ class Parser {
9302
9435
  debug.parse(`🔧 parseEventHandler: Warning - unknown modifier '.${modName}'`);
9303
9436
  }
9304
9437
  }
9305
- if (this.check('debounced') || this.check('throttled')) {
9306
- const modToken = this.advance();
9307
- const modName = modToken.value.toLowerCase();
9308
- const modifierKey = modName === 'debounced' ? 'debounce' : 'throttle';
9309
- if (this.match('at')) {
9310
- if (this.checkTimeExpression()) {
9311
- const timeToken = this.advance();
9312
- const delayMs = parseTimeToMs(timeToken.value);
9313
- modifiers[modifierKey] = delayMs;
9314
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${timeToken.value}' (${delayMs}ms)`);
9315
- }
9316
- else if (this.matchNumber()) {
9317
- const delayMs = parseInt(this.previous().value, 10);
9318
- modifiers[modifierKey] = delayMs;
9319
- debug.parse(`🔧 parseEventHandler: Parsed '${modName} at ${delayMs}'`);
9320
- }
9321
- else {
9322
- this.addError(`Expected time expression after '${modName} at', got: ${this.peek().value}`);
9323
- }
9324
- }
9325
- else {
9326
- this.addError(`Expected 'at' after '${modName}'`);
9327
- }
9328
- }
9438
+ this.parseTemporalKeywordModifiers(modifiers);
9329
9439
  if (firstOnceAlias) {
9330
9440
  modifiers.once = true;
9331
9441
  }
@@ -9342,6 +9452,7 @@ class Parser {
9342
9452
  const targetToken = this.advance();
9343
9453
  target = targetToken.value;
9344
9454
  debug.parse(`🔧 parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
9455
+ this.parseTemporalKeywordModifiers(modifiers);
9345
9456
  }
9346
9457
  let attributeName;
9347
9458
  if (this.match('of')) {
@@ -10312,6 +10423,10 @@ class Parser {
10312
10423
  }
10313
10424
  return this.tokens[this.current];
10314
10425
  }
10426
+ peekAt(offset) {
10427
+ const index = this.current + offset;
10428
+ return index >= 0 && index < this.tokens.length ? this.tokens[index] : null;
10429
+ }
10315
10430
  previous() {
10316
10431
  return this.tokens[this.current - 1];
10317
10432
  }
@@ -10516,10 +10631,7 @@ class Parser {
10516
10631
  restorePosition: (pos) => {
10517
10632
  parser.current = pos;
10518
10633
  },
10519
- peekAt: (offset) => {
10520
- const index = parser.current + offset;
10521
- return index >= 0 && index < parser.tokens.length ? parser.tokens[index] : null;
10522
- },
10634
+ peekAt: (offset) => parser.peekAt(offset),
10523
10635
  getInputSlice: (start, end) => {
10524
10636
  if (!this.originalInput)
10525
10637
  return '';
@@ -10663,7 +10775,7 @@ function evaluateExpressionSync(node, context) {
10663
10775
  case 'selector':
10664
10776
  return evaluateSelectorSync(n, context);
10665
10777
  case 'identifier':
10666
- return resolveIdentifierSync(n.name, context);
10778
+ return resolveIdentifierSync(n.name, context, n.scope);
10667
10779
  case 'contextReference':
10668
10780
  return resolveContextReferenceSync(n.contextType, context);
10669
10781
  case 'arrayLiteral':
@@ -10699,7 +10811,7 @@ function evaluateExpressionSync(node, context) {
10699
10811
  throw new NotSyncEvaluable(n?.type ?? 'unknown');
10700
10812
  }
10701
10813
  }
10702
- function resolveIdentifierSync(name, context) {
10814
+ function resolveIdentifierSync(name, context, scope) {
10703
10815
  if (name === 'me' || name === 'my' || name === 'I')
10704
10816
  return context.me;
10705
10817
  if (name === 'you' || name === 'your' || name === 'yourself')
@@ -10710,6 +10822,8 @@ function resolveIdentifierSync(name, context) {
10710
10822
  return typeof window !== 'undefined' ? window : globalThis;
10711
10823
  if (name === 'document')
10712
10824
  return typeof document !== 'undefined' ? document : undefined;
10825
+ if (scope === 'element')
10826
+ return getElementVar(context, name);
10713
10827
  if (context.locals?.has(name))
10714
10828
  return context.locals.get(name);
10715
10829
  if (context.globals?.has(name))
@@ -10839,6 +10953,9 @@ async function evaluateIdentifier(node, context) {
10839
10953
  notifyGlobalRead(name, context);
10840
10954
  return context.globals.get(name);
10841
10955
  }
10956
+ if (node.scope === 'element') {
10957
+ return getElementVar(context, name);
10958
+ }
10842
10959
  if (context.locals && context.locals.has(name)) {
10843
10960
  notifyLocalRead(name, context);
10844
10961
  return context.locals.get(name);
@@ -10874,6 +10991,9 @@ async function evaluateBinaryExpression(node, context) {
10874
10991
  else if (leftNode.scope === 'global') {
10875
10992
  setGlobal(context, name, value);
10876
10993
  }
10994
+ else if (leftNode.scope === 'element') {
10995
+ setElementVar(context, name, value);
10996
+ }
10877
10997
  else {
10878
10998
  if (!context.locals)
10879
10999
  context.locals = new Map();
@@ -10955,14 +11075,26 @@ async function evaluateBinaryExpression(node, context) {
10955
11075
  const L = node.ignoringCase ? applyCI(left) : left;
10956
11076
  const R = node.ignoringCase ? applyCI(right) : right;
10957
11077
  switch (operator) {
10958
- case '+':
10959
- if (typeof left === 'string' || typeof right === 'string') {
10960
- return String(left ?? '') + String(right ?? '');
10961
- }
10962
- if (Array.isArray(left)) {
10963
- return Array.isArray(right) ? [...left, ...right] : [...left, right];
10964
- }
10965
- return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, { left, right }));
11078
+ case '+': {
11079
+ let addLeft = left;
11080
+ let addRight = right;
11081
+ if (node.coerceNumeric) {
11082
+ if (typeof addLeft === 'string')
11083
+ addLeft = convertToNumber(addLeft);
11084
+ if (typeof addRight === 'string')
11085
+ addRight = convertToNumber(addRight);
11086
+ }
11087
+ if (typeof addLeft === 'string' || typeof addRight === 'string') {
11088
+ return String(addLeft ?? '') + String(addRight ?? '');
11089
+ }
11090
+ if (Array.isArray(addLeft)) {
11091
+ return Array.isArray(addRight) ? [...addLeft, ...addRight] : [...addLeft, addRight];
11092
+ }
11093
+ return unwrapTypedResult(await getExpr(context, 'addition').evaluate(context, {
11094
+ left: addLeft,
11095
+ right: addRight,
11096
+ }));
11097
+ }
10966
11098
  case '-':
10967
11099
  return unwrapTypedResult(await getExpr(context, 'subtraction').evaluate(context, { left, right }));
10968
11100
  case '*':
@@ -11744,6 +11876,7 @@ class ContextBridge {
11744
11876
  static toTyped(context) {
11745
11877
  return {
11746
11878
  me: context.me,
11879
+ ...(context.owner !== undefined && { owner: context.owner }),
11747
11880
  it: context.it,
11748
11881
  you: context.you,
11749
11882
  result: context.result,