@happyvertical/smrt-scanner 0.40.27 → 0.40.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -67,3 +67,21 @@ executes the source.
67
67
  unwrapped from their `UnaryExpression` before the check.
68
68
  - **Static property capture**: captures `uiSlots` and `adminRoutes` for agent
69
69
  manifest generation.
70
+ - **`@smrt()` config spreads resolve only against unescaped same-file `const`s** (issue
71
+ #2100). `@smrt({ ...INTERNAL_SURFACE })` works when `INTERNAL_SURFACE` is a
72
+ module-scope `const` object literal in the same file (`as const` and
73
+ `export const` included); a constant may spread an earlier constant. Anything
74
+ else — an imported constant, a `let`, a function call, a computed non-literal
75
+ key, shorthand or non-literal value, mutation/alias/escape, or a spread inside
76
+ an include array — is reported as a `severity: 'error'` scan diagnostic,
77
+ never silently dropped. `const` prevents rebinding but not property mutation,
78
+ and object spread is shallow, so only binding-aware spread references whose
79
+ shared nested values remain safe through the decorator use are trusted.
80
+ Silence would be unsafe: a dropped spread can remove `api`/`mcp`/`cli`, and an
81
+ ABSENT surface key means default-open full CRUD, so a quiet drop turns a
82
+ deliberate lockdown into a published surface.
83
+ A constant whose own initializer holds an unresolvable spread
84
+ (`const CFG = { ...IMPORTED }`) is tracked as **tainted**: it still resolves,
85
+ but its taint replays into the diagnostics of every decorator that spreads
86
+ it, transitively through constant chains. Without that the silent drop simply
87
+ moves one level up. An unused tainted constant reports nothing.
@@ -374,10 +374,15 @@ function parseFile(filePath) {
374
374
  const importAliases = extractImportAliases(program.body);
375
375
  typeAliases = extractTypeAliases(program.body);
376
376
  smrtImports = extractSmrtImports(program.body);
377
+ const ctx = {
378
+ constants: extractModuleObjectConstants(program.body, sourceText),
379
+ unresolved: []
380
+ };
377
381
  for (const node of program.body) {
378
- const extracted = extractClassFromNode(node, filePath, sourceText, importAliases);
382
+ const extracted = extractClassFromNode(node, filePath, sourceText, importAliases, ctx);
379
383
  if (extracted) classes.push(extracted);
380
384
  }
385
+ reportUnresolvedSpreads(ctx.unresolved, filePath, sourceText, errors);
381
386
  }
382
387
  } catch (error) {
383
388
  errors.push({
@@ -422,10 +427,15 @@ function parseSource(sourceText, filename = "test.ts") {
422
427
  const importAliases = extractImportAliases(program.body);
423
428
  typeAliases = extractTypeAliases(program.body);
424
429
  smrtImports = extractSmrtImports(program.body);
430
+ const ctx = {
431
+ constants: extractModuleObjectConstants(program.body, sourceText),
432
+ unresolved: []
433
+ };
425
434
  for (const node of program.body) {
426
- const extracted = extractClassFromNode(node, filename, sourceText, importAliases);
435
+ const extracted = extractClassFromNode(node, filename, sourceText, importAliases, ctx);
427
436
  if (extracted) classes.push(extracted);
428
437
  }
438
+ reportUnresolvedSpreads(ctx.unresolved, filename, sourceText, errors);
429
439
  }
430
440
  } catch (error) {
431
441
  errors.push({
@@ -526,24 +536,253 @@ function extractTypeAliases(body) {
526
536
  }
527
537
  return aliases;
528
538
  }
529
- function extractClassFromNode(node, filePath, sourceText, importAliases) {
530
- if (node.type === "ExportNamedDeclaration" && node.declaration) return extractClassFromNode(node.declaration, filePath, sourceText, importAliases);
531
- if (node.type === "ExportDefaultDeclaration" && node.declaration) return extractClassFromNode(node.declaration, filePath, sourceText, importAliases);
532
- if (node.type === "ClassDeclaration") return extractClassDeclaration(node, filePath, sourceText, importAliases);
539
+ function unwrapTypeAssertion(node) {
540
+ let current = node;
541
+ for (let depth = 0; current && depth < 8; depth++) {
542
+ const type = current.type;
543
+ if (type === "TSAsExpression" || type === "TSSatisfiesExpression" || type === "TSNonNullExpression" || type === "TSTypeAssertion" || type === "ParenthesizedExpression") {
544
+ current = current.expression;
545
+ continue;
546
+ }
547
+ return current;
548
+ }
549
+ return current;
550
+ }
551
+ function extractModuleObjectConstants(body, sourceText) {
552
+ const constants = /* @__PURE__ */ new Map();
553
+ for (const node of body) {
554
+ const decl = node.type === "VariableDeclaration" ? node : node.type === "ExportNamedDeclaration" && node.declaration?.type === "VariableDeclaration" ? node.declaration : null;
555
+ if (decl?.kind !== "const") continue;
556
+ for (const declarator of decl.declarations) {
557
+ if (declarator.id?.type !== "Identifier") continue;
558
+ const name = declarator.id.name;
559
+ if (!name || !isSafeObjectKey(name)) continue;
560
+ const init = unwrapTypeAssertion(declarator.init);
561
+ if (init?.type !== "ObjectExpression") continue;
562
+ const unresolved = [];
563
+ const dependencies = [];
564
+ const value = extractObjectLiteral(init, sourceText, {
565
+ constants,
566
+ unresolved,
567
+ dependencies,
568
+ requireLiteralValues: true
569
+ });
570
+ constants.set(name, {
571
+ value,
572
+ unresolved,
573
+ unsafeReferences: [],
574
+ dependencies
575
+ });
576
+ }
577
+ }
578
+ taintUnsafeModuleConstantReferences(body, constants, sourceText);
579
+ propagateModuleConstantTaint(constants);
580
+ return constants;
581
+ }
582
+ function taintUnsafeModuleConstantReferences(body, constants, sourceText) {
583
+ if (constants.size === 0) return;
584
+ const tainted = /* @__PURE__ */ new Set();
585
+ const visit = (value, ancestors, shadowed) => {
586
+ if (Array.isArray(value)) {
587
+ for (const entry of value) visit(entry, ancestors, shadowed);
588
+ return;
589
+ }
590
+ if (!value || typeof value !== "object") return;
591
+ const node = value;
592
+ if (typeof node.type !== "string") {
593
+ for (const child of Object.values(node)) visit(child, ancestors, shadowed);
594
+ return;
595
+ }
596
+ if (node.type === "Identifier" && typeof node.name === "string" && constants.has(node.name) && !shadowed.has(node.name) && !isTypeOnlyReference(node, ancestors) && !isSafeModuleConstantReference(node, ancestors, constants)) {
597
+ const name = node.name;
598
+ if (!tainted.has(name)) {
599
+ tainted.add(name);
600
+ const unresolved = {
601
+ expression: sliceSource(node, sourceText) ?? name,
602
+ start: typeof node.start === "number" ? node.start : void 0
603
+ };
604
+ const constant = constants.get(name);
605
+ constant?.unsafeReferences.push(unresolved);
606
+ constant?.unresolved.push(unresolved);
607
+ }
608
+ }
609
+ const nextShadowed = createsLexicalScope(node) ? /* @__PURE__ */ new Set([...shadowed, ...collectScopeBindings(node)]) : shadowed;
610
+ const nextAncestors = [...ancestors, node];
611
+ for (const [key, child] of Object.entries(node)) {
612
+ if (key === "loc" || key === "range" || key === "start" || key === "end") continue;
613
+ visit(child, nextAncestors, nextShadowed);
614
+ }
615
+ };
616
+ visit(body, [], /* @__PURE__ */ new Set());
617
+ }
618
+ function createsLexicalScope(node) {
619
+ return node.type === "BlockStatement" || node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "CatchClause" || node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement" || node.type === "ClassDeclaration" || node.type === "ClassExpression";
620
+ }
621
+ function collectScopeBindings(node) {
622
+ const bindings = /* @__PURE__ */ new Set();
623
+ const addPattern = (value) => {
624
+ if (!value || typeof value !== "object") return;
625
+ const pattern = value;
626
+ if (pattern.type === "Identifier" && typeof pattern.name === "string") {
627
+ bindings.add(pattern.name);
628
+ return;
629
+ }
630
+ if (pattern.type === "RestElement") {
631
+ addPattern(pattern.argument);
632
+ return;
633
+ }
634
+ if (pattern.type === "AssignmentPattern") {
635
+ addPattern(pattern.left);
636
+ return;
637
+ }
638
+ if (pattern.type === "ArrayPattern" && Array.isArray(pattern.elements)) {
639
+ for (const element of pattern.elements) addPattern(element);
640
+ return;
641
+ }
642
+ if (pattern.type === "ObjectPattern" && Array.isArray(pattern.properties)) for (const property of pattern.properties) {
643
+ if (!property || typeof property !== "object") continue;
644
+ const propertyNode = property;
645
+ addPattern(propertyNode.type === "RestElement" ? propertyNode.argument : propertyNode.value);
646
+ }
647
+ };
648
+ if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") {
649
+ if (node.type !== "ArrowFunctionExpression") addPattern(node.id);
650
+ if (Array.isArray(node.params)) for (const parameter of node.params) addPattern(parameter);
651
+ collectFunctionVarBindings(node.body, bindings);
652
+ } else if (node.type === "CatchClause") addPattern(node.param);
653
+ else if (node.type === "ForStatement" || node.type === "ForInStatement" || node.type === "ForOfStatement") {
654
+ const declaration = node.type === "ForStatement" ? node.init : node.left;
655
+ if (declaration && typeof declaration === "object" && declaration.type === "VariableDeclaration") for (const declarator of declaration.declarations) addPattern(declarator.id);
656
+ } else if ((node.type === "ClassDeclaration" || node.type === "ClassExpression") && node.id) addPattern(node.id);
657
+ if (node.type === "BlockStatement" && Array.isArray(node.body)) for (const statement of node.body) {
658
+ const declaration = statement.type === "ExportNamedDeclaration" ? statement.declaration : statement;
659
+ if (declaration?.type === "VariableDeclaration") for (const declarator of declaration.declarations) addPattern(declarator.id);
660
+ else if (declaration?.type === "FunctionDeclaration" || declaration?.type === "ClassDeclaration") addPattern(declaration.id);
661
+ }
662
+ return bindings;
663
+ }
664
+ function collectFunctionVarBindings(value, bindings) {
665
+ if (Array.isArray(value)) {
666
+ for (const entry of value) collectFunctionVarBindings(entry, bindings);
667
+ return;
668
+ }
669
+ if (!value || typeof value !== "object") return;
670
+ const node = value;
671
+ if (node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") return;
672
+ if (node.type === "VariableDeclaration" && node.kind === "var") for (const declarator of node.declarations) {
673
+ const id = declarator.id;
674
+ if (id?.type === "Identifier" && typeof id.name === "string") bindings.add(id.name);
675
+ }
676
+ for (const [key, child] of Object.entries(node)) {
677
+ if (key === "loc" || key === "range" || key === "start" || key === "end") continue;
678
+ collectFunctionVarBindings(child, bindings);
679
+ }
680
+ }
681
+ function isTypeOnlyReference(identifier, ancestors) {
682
+ let child = identifier;
683
+ for (let index = ancestors.length - 1; index >= 0; index--) {
684
+ const parent = ancestors[index];
685
+ if ((parent.type === "TSAsExpression" || parent.type === "TSSatisfiesExpression" || parent.type === "TSNonNullExpression" || parent.type === "TSTypeAssertion") && parent.expression === child) {
686
+ child = parent;
687
+ continue;
688
+ }
689
+ if (typeof parent.type === "string" && parent.type.startsWith("TS")) return true;
690
+ break;
691
+ }
692
+ return false;
693
+ }
694
+ function propagateModuleConstantTaint(constants) {
695
+ let changed = true;
696
+ while (changed) {
697
+ changed = false;
698
+ for (const constant of constants.values()) for (const dependency of constant.dependencies) {
699
+ const source = constants.get(dependency.name);
700
+ if (!source) continue;
701
+ for (const unresolved of source.unresolved) {
702
+ if (dependency.start !== void 0 && unresolved.start !== void 0 && unresolved.start > dependency.start && !hasNestedReferenceValue(source.value)) continue;
703
+ if (!constant.unresolved.some((existing) => existing.expression === unresolved.expression && existing.start === unresolved.start)) {
704
+ constant.unresolved.push(unresolved);
705
+ changed = true;
706
+ }
707
+ }
708
+ if (hasNestedReferenceValue(source.value)) for (const unsafeReference of constant.unsafeReferences) {
709
+ const matchesUnsafeReference = (existing) => existing.expression === unsafeReference.expression && existing.start === unsafeReference.start;
710
+ if (!source.unresolved.some(matchesUnsafeReference)) {
711
+ source.unresolved.push(unsafeReference);
712
+ changed = true;
713
+ }
714
+ if (!source.unsafeReferences.some(matchesUnsafeReference)) {
715
+ source.unsafeReferences.push(unsafeReference);
716
+ changed = true;
717
+ }
718
+ }
719
+ }
720
+ }
721
+ }
722
+ function hasNestedReferenceValue(value) {
723
+ return Object.values(value).some((entry) => entry !== null && typeof entry === "object");
724
+ }
725
+ function isSafeModuleConstantReference(identifier, ancestors, constants) {
726
+ let child = identifier;
727
+ for (let index = ancestors.length - 1; index >= 0; index--) {
728
+ const parent = ancestors[index];
729
+ if (parent.type === "VariableDeclarator" && parent.id === child) return true;
730
+ if (parent.type === "Property" && parent.key === child && parent.computed === false && parent.shorthand === false) return true;
731
+ if (parent.type === "MemberExpression" && parent.property === child && parent.computed === false) return true;
732
+ if ((parent.type === "TSAsExpression" || parent.type === "TSSatisfiesExpression" || parent.type === "TSNonNullExpression" || parent.type === "TSTypeAssertion" || parent.type === "ParenthesizedExpression") && parent.expression === child) {
733
+ child = parent;
734
+ continue;
735
+ }
736
+ if (parent.type === "SpreadElement" && parent.argument === child) {
737
+ const name = typeof identifier.name === "string" ? identifier.name : void 0;
738
+ const constant = name ? constants.get(name) : void 0;
739
+ if (!constant || !hasNestedReferenceValue(constant.value)) return true;
740
+ if ([...constants.values()].some((candidate) => candidate.dependencies.some((dependency) => dependency.name === name && dependency.start === parent.start))) return true;
741
+ return ancestors.some((ancestor) => {
742
+ if (ancestor.type !== "Decorator" || !ancestor.expression || typeof ancestor.expression !== "object") return false;
743
+ const expression = ancestor.expression;
744
+ if (expression.type !== "CallExpression" || !expression.callee || typeof expression.callee !== "object") return false;
745
+ const callee = expression.callee;
746
+ return callee.type === "Identifier" && callee.name === "smrt";
747
+ });
748
+ }
749
+ return false;
750
+ }
751
+ return false;
752
+ }
753
+ function reportUnresolvedSpreads(unresolved, filePath, sourceText, errors) {
754
+ for (const spread of unresolved) {
755
+ const loc = spread.start === void 0 ? void 0 : getLineColumn(sourceText, spread.start);
756
+ errors.push({
757
+ message: `Cannot statically resolve \`${spread.expression}\` while expanding a @smrt() config. Only literal keys/values and unescaped module-scope \`const\` object literals in the same file are supported. Inline the keys or remove the mutation/alias \u2014 an unresolved expression would drop api/mcp/cli from the manifest, and an absent surface defaults to open.`,
758
+ filePath,
759
+ line: loc?.line,
760
+ column: loc?.column,
761
+ severity: "error"
762
+ });
763
+ }
764
+ }
765
+ function extractClassFromNode(node, filePath, sourceText, importAliases, ctx) {
766
+ if (node.type === "ExportNamedDeclaration" && node.declaration) return extractClassFromNode(node.declaration, filePath, sourceText, importAliases, ctx);
767
+ if (node.type === "ExportDefaultDeclaration" && node.declaration) return extractClassFromNode(node.declaration, filePath, sourceText, importAliases, ctx);
768
+ if (node.type === "ClassDeclaration") return extractClassDeclaration(node, filePath, sourceText, importAliases, ctx);
533
769
  return null;
534
770
  }
535
- function extractClassDeclaration(node, filePath, sourceText, importAliases) {
771
+ function extractClassDeclaration(node, filePath, sourceText, importAliases, ctx) {
536
772
  const className = node.id?.name || "AnonymousClass";
537
773
  const decorators = node.decorators || [];
538
774
  const smrtDecorator = decorators.find((d) => isSmrtDecorator(d));
539
775
  const reportDecorator = decorators.find((d) => isNamedDecorator(d, "report"));
540
776
  const tenantScopedDecorator = decorators.find((d) => isNamedDecorator(d, "TenantScoped"));
541
777
  const hasSmartDecorator = !!smrtDecorator;
542
- const smrtConfig = smrtDecorator ? extractDecoratorConfig(smrtDecorator, sourceText) : null;
778
+ const smrtConfig = smrtDecorator ? extractDecoratorConfig(smrtDecorator, sourceText, ctx ? {
779
+ ...ctx,
780
+ requireLiteralValues: true
781
+ } : ctx) : null;
543
782
  const decoratorConfig = tenantScopedDecorator || reportDecorator ? {
544
783
  ...smrtConfig ?? {},
545
- ...reportDecorator ? { report: extractDecoratorConfig(reportDecorator, sourceText) } : {},
546
- ...tenantScopedDecorator ? { tenantScoped: extractDecoratorConfig(tenantScopedDecorator, sourceText) } : {}
784
+ ...reportDecorator ? { report: extractDecoratorConfig(reportDecorator, sourceText, ctx) } : {},
785
+ ...tenantScopedDecorator ? { tenantScoped: extractDecoratorConfig(tenantScopedDecorator, sourceText, ctx) } : {}
547
786
  } : smrtConfig;
548
787
  const { extendsClause, extendsTypeArg } = extractExtendsClause(node, importAliases);
549
788
  const fields = [];
@@ -580,19 +819,45 @@ function isNamedDecorator(decorator, name) {
580
819
  if (expr.type === "Identifier" && expr.name === name) return true;
581
820
  return false;
582
821
  }
583
- function extractDecoratorConfig(decorator, sourceText) {
822
+ function extractDecoratorConfig(decorator, sourceText, ctx) {
584
823
  const expr = decorator.expression;
585
824
  if (expr.type === "CallExpression" && expr.arguments.length > 0) {
586
- const arg = expr.arguments[0];
587
- if (arg.type === "ObjectExpression") return extractObjectLiteral(arg, sourceText);
825
+ const arg = unwrapTypeAssertion(expr.arguments[0]);
826
+ if (arg?.type === "ObjectExpression") return extractObjectLiteral(arg, sourceText, ctx);
588
827
  }
589
828
  return {};
590
829
  }
591
- function extractObjectLiteral(node, sourceText) {
830
+ function extractObjectLiteral(node, sourceText, ctx) {
592
831
  const result = {};
593
- for (const prop of node.properties) if (prop.type === "Property" && !prop.computed) {
594
- const key = getPropertyKey(prop.key);
595
- if (key && isSafeObjectKey(key)) result[key] = extractValue(prop.value, sourceText);
832
+ for (const prop of node.properties) {
833
+ if (prop.type === "SpreadElement") {
834
+ const argument = unwrapTypeAssertion(prop.argument);
835
+ const constant = argument?.type === "Identifier" ? ctx?.constants.get(argument.name) : void 0;
836
+ const resolved = constant?.value ?? (argument?.type === "ObjectExpression" ? extractObjectLiteral(argument, sourceText, ctx) : void 0);
837
+ if (resolved) {
838
+ if (constant && ctx?.dependencies && argument?.type === "Identifier") ctx.dependencies.push({
839
+ name: argument.name,
840
+ start: prop.start
841
+ });
842
+ for (const [key, value] of Object.entries(resolved)) if (isSafeObjectKey(key)) result[key] = value;
843
+ if (constant?.unresolved.length && ctx && !ctx.dependencies) ctx.unresolved.push(...constant.unresolved.filter((unresolved) => hasNestedReferenceValue(constant.value) || prop.start === void 0 || unresolved.start === void 0 || unresolved.start <= prop.start));
844
+ } else if (ctx) ctx.unresolved.push({
845
+ expression: sliceSource(prop, sourceText) ?? "...<unknown>",
846
+ start: prop.start
847
+ });
848
+ continue;
849
+ }
850
+ if (prop.type === "Property") {
851
+ const key = getPropertyKey(prop.key);
852
+ if (prop.shorthand || prop.computed && !(prop.key.type === "Literal" && typeof prop.key.value === "string")) {
853
+ ctx?.unresolved.push({
854
+ expression: sliceSource(prop, sourceText) ?? "<unknown property>",
855
+ start: prop.start
856
+ });
857
+ continue;
858
+ }
859
+ if (key && isSafeObjectKey(key)) result[key] = extractValue(prop.value, sourceText, ctx);
860
+ }
596
861
  }
597
862
  return result;
598
863
  }
@@ -601,7 +866,9 @@ function getPropertyKey(node) {
601
866
  if (node.type === "Literal" && typeof node.value === "string") return node.value;
602
867
  return null;
603
868
  }
604
- function extractValue(node, sourceText) {
869
+ function extractValue(node, sourceText, ctx) {
870
+ const unwrapped = unwrapTypeAssertion(node);
871
+ if (unwrapped && unwrapped !== node) return extractValue(unwrapped, sourceText, ctx);
605
872
  switch (node.type) {
606
873
  case "Literal": return node.value;
607
874
  case "Identifier":
@@ -609,9 +876,18 @@ function extractValue(node, sourceText) {
609
876
  if (node.name === "null") return null;
610
877
  if (node.name === "true") return true;
611
878
  if (node.name === "false") return false;
879
+ if (ctx?.requireLiteralValues) ctx.unresolved.push({
880
+ expression: sliceSource(node, sourceText) ?? node.name,
881
+ start: node.start
882
+ });
612
883
  return node.name;
613
- case "ArrayExpression": return node.elements.filter((el) => el !== null && typeof el === "object" && "type" in el && el.type !== "SpreadElement").map((el) => extractValue(el, sourceText));
614
- case "ObjectExpression": return extractObjectLiteral(node, sourceText);
884
+ case "ArrayExpression":
885
+ for (const el of node.elements) if (el && typeof el === "object" && el.type === "SpreadElement" && ctx) ctx.unresolved.push({
886
+ expression: sliceSource(el, sourceText) ?? "...<unknown>",
887
+ start: el.start
888
+ });
889
+ return node.elements.filter((el) => el !== null && typeof el === "object" && "type" in el && el.type !== "SpreadElement").map((el) => extractValue(el, sourceText, ctx));
890
+ case "ObjectExpression": return extractObjectLiteral(node, sourceText, ctx);
615
891
  case "UnaryExpression":
616
892
  if (node.operator === "-" && node.argument?.type === "Literal") {
617
893
  const value = node.argument.value;
@@ -621,12 +897,24 @@ function extractValue(node, sourceText) {
621
897
  case "CallExpression":
622
898
  case "NewExpression": {
623
899
  const src = sliceSource(node, sourceText);
624
- if (src) return src;
900
+ if (src) {
901
+ if (ctx?.requireLiteralValues) ctx.unresolved.push({
902
+ expression: src,
903
+ start: node.start
904
+ });
905
+ return src;
906
+ }
625
907
  break;
626
908
  }
627
909
  }
628
910
  const rawSrc = sliceSource(node, sourceText);
629
- if (rawSrc) return rawSrc;
911
+ if (rawSrc) {
912
+ if (ctx?.requireLiteralValues) ctx.unresolved.push({
913
+ expression: rawSrc,
914
+ start: node.start
915
+ });
916
+ return rawSrc;
917
+ }
630
918
  }
631
919
  function extractExtendsClause(node, importAliases) {
632
920
  if (!node.superClass) return {
@@ -1817,4 +2105,4 @@ var OxcScanner = class {
1817
2105
  //#endregion
1818
2106
  export { parseSource as a, parseFile as i, ManifestAdapter as n, InheritanceResolver as o, extractSmrtImports as r, OxcScanner as t };
1819
2107
 
1820
- //# sourceMappingURL=scanner-Di4QAFXG.js.map
2108
+ //# sourceMappingURL=scanner-_R1muI_s.js.map